:root {
  /* Type system */
  --font-sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'SF Mono', Menlo, Monaco, 'Cascadia Mono', 'Roboto Mono',
    Consolas, monospace;

  /* Neutral surfaces */
  --bg-app: #f1f3f4;    /* Google grey 100 */
  --bg-surface: #ffffff;
  --bg-canvas: #f1f3f4; /* page-body canvas — white cards/divs read as raised against it */
  --bg-muted: #f8f9fa;  /* grey 50 */
  --bg-hover: #f1f3f4;

  /* Borders */
  --border: #dadce0;        /* grey 300 */
  --border-strong: #bdc1c6; /* grey 400 */
  --border-muted: #e8eaed;  /* grey 200 */

  /* Layout zones — three distinct but LIGHT surfaces so the chrome separates
     cleanly from content: primary nav (tinted, darkest) · module sub-nav (white) ·
     content canvas (soft neutral gray, so white cards/panels lift). --zone-divider
     is a hair stronger than --border for the seams between zones. */
  /* The nav zone TRACKS the rail tone: the header pairs with the second rail, the second
     rail pairs with the first, so the three chrome surfaces are ONE tone by construction
     and cannot drift apart -- dividers, not tints, are what separate them. An alias
     resolves on this same element (the dark block redefines both there too), so each
     theme follows automatically. */
  --zone-nav: var(--zone-rail);
  /* A soft cool grey, NOT white (2026-08-18, UX pass): white rails on a white page gave the
     nav zero presence — only the hairline said where navigation ended and work began. #f6f8fa
     is quiet enough not to compete with content, tinted enough that the body's white reads as
     "the work area", and it completes a three-step chrome gradation: header (#f1f3f4, deepest)
     → rails (#f6f8fa) → body (white). Ink stays AA: --text-subtle #5f6368 on this is ~5.9:1. */
  --zone-rail: #f6f8fa;
  /* The org name's header ink (2026-08-20, user direction: bigger and colourful). Violet on
     purpose — colour that cannot be mistaken for the link blue, so the one inert label in the
     header keeps refusing to read as a control. AA against --zone-rail: 6.67:1 here, 5.92:1
     for the dark value on its band. Stated per theme like every ink. */
  --orgbrand-ink: #6d28d9;
  /* THE APP'S GROUND — the grey the three panels sit on (2026-09-19, user direction: "change app
     background default color to something close to greish color"). Google grey 200, one step down
     from the grey 100 it was: at #f1f3f4 the ground read as off-white next to white panels, so
     the panels barely separated from it and the curved corners had almost nothing to show
     against. This is also what the HEADER BAND paints (`--chrome-bg: var(--zone-content)`), so
     the whole surround moves together and the band cannot drift a shade off the page again.
     NOT `--bg-app`, which is the same value and a different job: 66 rules paint cards, banners
     and page bodies with it, and moving that would repaint half the app. This token has eight
     readers and every one of them is the shell's own ground. */
  --zone-content: #e8eaed;
  /* Softened to suit the tinted rails: with the zones carrying their own tone the seam only
     has to confirm the edge, and #dadce0 next to #f6f8fa read heavier than the division it
     marks. */
  --zone-divider: #e3e6ea;

  /* Standard responsive page gutter — the single horizontal inset used by the top header
     AND every module's content, so the header and page content share one edge alignment.
     Scales ~24px on narrow screens up to a 64px cap on wide ones. Was per-module before. */
  --page-gutter: clamp(12px, 4vw, 32px); /* responsive side gutter — chrome + content share one edge inset */

  /* Text */
  --text: #202124;        /* grey 900 — 16.1:1 on white */
  --text-muted: #5f6368;  /* grey 700 — 6.05:1 */
  --text-subtle: #5f6368;
  --text-faint: #5f6368;  /* grey 600 (#80868b) is only 3.4:1 and fails AA, so faint stops here */

  /* Brand / accents (kept restrained) */
  --accent: #202124;
  --accent-soft: #f1f3f4;
  --brand: #3c4043;
  --brand-hover: #202124;
  /* Primary action colour — Google blue 600. 4.51:1 on white, and white on it is
     the same 4.51:1, so the fill and the label both clear AA. */
  --primary: #1a73e8;
  --primary-hover: #1967d2; /* blue 700 */
  /* User-themeable page header + filter bar, applied app-wide. These are the DEFAULTS;
     controllers/preferences.js overrides them per-user with inline vars on <html> (Settings
     → Header & filter colours). Every module's page header uses --app-header-bg/-text and
     every filter/toolbar uses --app-filter-bg/-border, so one choice re-skins the whole app.
     --app-page-max caps the header/filter to the page body width (centred), matching the
     task-detail page. */
  --app-header-bg: #1a73e8;
  /* Toolbar is a plain white band across all modules (the header keeps the gradient). */
  --app-toolbar-bg: #ffffff;
  --app-header-text: #ffffff;
  --app-filter-bg: #f8f9fa;
  --app-filter-border: #e8eaed;
  --app-page-max: 1200px;
  /* Text/ink shown ON a --primary fill (buttons). White passes on the light cobalt;
     dark mode overrides this to dark ink because the dark --primary is a light blue
     where white text fails WCAG AA. */
  --primary-ink: #ffffff;
  --focus-ring: rgba(26, 115, 232, 0.35);

  /* ── Legacy alias tokens ────────────────────────────────────────────────────
     Names used across this sheet that were never DEFINED anywhere. Every one silently
     fell through to the light literal written as its fallback — the same defect that
     made the table stripes, the row hover and the +New dropdown white in dark. Aliasing
     them to the real tokens fixes all their uses at once and makes them theme-aware for
     free, because the target is.

     Each is mapped from the fallback its call sites declared, so light is unchanged by
     construction. Non-colour phantoms (--h, --bg, --pct, --depth, --wk-tbl-*,
     --page-rail-w) are deliberately left alone: those are set inline per element by JS,
     and a global default could move layout. */
  --surface: var(--bg-surface);
  --surface-muted: var(--bg-muted);
  --bg-input: var(--bg-surface);
  --bg-subtle: var(--bg-muted);
  --muted: var(--text-subtle);
  --text-color: var(--text);
  --rc: var(--text-subtle);
  --vc: var(--text-subtle);
  --track: var(--bg-hover);
  --hover: var(--bg-hover);
  --ok: var(--success);
  --err: var(--danger);
  --danger-strong: var(--danger);
  --warning: var(--warn);
  --warn-fg: var(--warn);
  --accent-strong: var(--primary-hover);
  --tile-accent: var(--primary);
  --ph: var(--primary);
  /* The soft status washes need a value per theme: a pale tint is a surface in light and
     has to become a DARK tint in dark, not merely a dimmer pale. */
  /* Names used only from JS-injected <style> blocks and module partials, never defined —
     the same defect as the block above, in the one place a stylesheet audit cannot see.
     `--card-bg` is why Kriya's dashboard widgets stayed white in dark: the widget shell in
     dashgrid.js paints `var(--card-bg, #fff)`. Each alias resolves to the fallback its call
     sites already declared, so light does not move. */
  --card-bg: var(--bg-surface);
  --hover-strong: var(--bg-hover);
  --border-faint: var(--border-muted);
  --track-strong: var(--bg-muted);
  --chip-bg: var(--bg-muted);
  --wk-muted: var(--text-subtle);
  --brand-primary: var(--primary);
  /* Two tints that need a value per theme rather than an alias. */
  --primary-50: #eef4ff;
  --unread: #f6f9ff;

  --danger-soft: #fee2e2;
  --danger-bg: #fee2e2;
  --warn-bg: #fef3c7;
  --ok-soft: #dcfce7;

  /* Table surfaces. Every one of these was REFERENCED but never defined, so tables
     fell through to a hardcoded light literal in both themes. The light values here
     are exactly those literals, so light mode does not move; the dark block supplies
     the counterparts that never existed. */
  --table-stripe: #eef1f8;
  --table-stripe-soft: #fafbfd;
  --table-divider: #e8eaed;
  --table-row-hover: #e7edfa;
  --table-head-bg: #1f2937;
  --table-head-fg: #f1f5f9;
  /* Module tables sit on a RAISED card, not the page surface, so their divider
     needs its own value — the same grey reads a full step fainter on a lighter
     background. Light keeps the literal these tables already had. */
  --table-divider-raised: #f1f5f9;
  /* Chrome washes. These are exactly what `color-mix(in srgb, var(--text) 8%, transparent)`
     resolves to, written out — because Safari only learned color-mix in 16.2, and an
     unsupported declaration is DROPPED, which hands the element back to whatever earlier
     rule was losing. In the rail that meant a light hover surviving into dark mode. Chrome
     is the one place that cannot afford a progressive-enhancement hole. */
  --wash-ink: rgba(32, 33, 36, 0.08);
  --wash-ink-strong: rgba(32, 33, 36, 0.12);
  --wash-primary: rgba(26, 115, 232, 0.12);
  --crumb-sep: #9aa0a6;
  --search-pill: #e8f0fd;
  --search-pill-hover: #dde9fb;

  /* Status — Google's semantic set. All AA on white (5.02 / 4.77 / 4.65). */
  --success: #188038;
  --danger: #d93025;
  --warn: #b06000;

  /* ── The PAGE's palette, aliased ─────────────────────────────────────────────────
     These existed as the way BACK OUT of the header: it used to run opposite the page
     (a dark bar on a light one) by re-declaring the palette on `.topbar`, and custom
     properties inherit — so the menus and panels that live in the header's DOM but open
     OVER the page (the launcher, the profile and notification menus, the search panel)
     inherited the inversion and had to ask for the page's palette by name to get back.

     The header follows the theme now (see "THE TOP HEADER FOLLOWS THE PAGE THEME" at the
     end of this sheet) and nothing reads these any more. They are kept rather than deleted
     because they are correct, cost nothing, and are the mechanism any future surface that
     re-skins itself would need — deleting them would only mean re-deriving the same trick.

     The property they demonstrate is still worth knowing: a `var()` alias is substituted
     where it is DECLARED, so `--bg-subtle: var(--bg-muted)` at `:root` does NOT follow a
     `--bg-muted` override made further down on some other element. Anything that re-skins
     a scope must re-declare by NAME, aliases included. */
  --page-bg-surface: var(--bg-surface);
  --page-bg-muted: var(--bg-muted);
  --page-bg-hover: var(--bg-hover);
  --page-border: var(--border);
  --page-border-strong: var(--border-strong);
  --page-border-muted: var(--border-muted);
  --page-text: var(--text);
  --page-text-muted: var(--text-muted);
  --page-text-subtle: var(--text-subtle);
  --page-text-faint: var(--text-faint);
  --page-accent: var(--accent);
  --page-accent-soft: var(--accent-soft);
  --page-primary: var(--primary);
  --page-primary-hover: var(--primary-hover);
  --page-primary-ink: var(--primary-ink);
  --page-focus-ring: var(--focus-ring);
  --page-wash-ink: var(--wash-ink);
  --page-wash-ink-strong: var(--wash-ink-strong);
  --page-wash-primary: var(--wash-primary);
  --page-success: var(--success);
  --page-danger: var(--danger);
  --page-warn: var(--warn);
  --page-warn-bg: var(--warn-bg);
  --page-danger-bg: var(--danger-bg);

  /* `--header-bg` is declared with the band's other tones near the end of this sheet — see
     the note there. It was ALSO declared here as `#292a2d`, with `#f1f3f4` as its dark
     mirror, until 2026-09-24: the same two selectors ~25,600 lines apart, so the later pair
     silently won and this one described the dark header that the 2026-09-19 direction
     ("make page header background color and whole app background color same") had removed. */

  /* ── Scale tokens (UX standardisation) ─────────────────────────────────
     Spacing on a 4/8 grid; a radius scale matching existing conventions
     (8px is the house default); elevation shadows; and a z-index ladder so
     the new chrome (rail / header / chat dock / palette / toasts) stacks
     predictably. Existing rules keep their literals; new chrome uses these. */
  /* ── THE TYPE RAMP (2026-09-18) ──────────────────────────────────────────
     Measured before it was written: this sheet holds **117 distinct font-size values**, thirty of
     them between 11px and 15.5px — steps of a third of a pixel, which nobody chose and nobody can
     see. They are what happens when each component is sized by eye against its neighbour.

     AND `rem` IS THE MECHANISM. `html` sets no `font-size`, so rem resolves against the browser's
     16px while `body` declares 14px: an author writing `1rem` to mean "normal" gets a heading, and
     one writing `0.85rem` to mean "slightly smaller" gets 13.6px — a third of a pixel from the
     `0.86rem` next door and from the `13px` two rules up. So the ramp is in PIXELS. Not because
     rem is wrong in general; because here its base disagrees with the body, and a unit whose
     meaning has to be remembered is one that will be got wrong.

     Four steps carry the whole UI band, and 11/12/13/14 ARE distinguishable where 13.6 and 13.76
     are not. THE RULE IS THE TOUCH RULE: a component you create or redesign reads these; the other
     4300 declarations stay as they are until something takes them. A sheet-wide conversion is a
     large untested diff over surfaces nobody reported. → `.claude/skills/design-system/SKILL.md` */
  --fs-xs: 11px;    /* icon captions, chips, table meta */
  --fs-sm: 12px;    /* secondary and muted text, dense row detail, hints */
  --fs-md: 13px;    /* CONTROL TEXT — buttons, inputs, selects, table cells */
  --fs-base: 14px;  /* body. The same number `body` already declares */
  --fs-lg: 16px;    /* a section heading inside a page */
  --fs-xl: 18px;    /* a page heading */
  --fs-2xl: 22px;   /* a page title, once */
  --lh-tight: 1.25;
  --lh: 1.5;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius: 8px;
  --radius-md: 10px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
  --shadow-md: 0 4px 12px rgba(15, 23, 42, 0.10);
  --shadow-lg: 0 12px 32px rgba(15, 23, 42, 0.16);

  --z-rail: 100;
  --z-header: 200;
  --z-dock: 300;
  --z-popover: 400;
  --z-modal: 500;
  --z-palette: 600;
  --z-toast: 700;

  font-family: var(--font-sans);
  font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
  background: var(--bg-app);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  margin: 0;
  min-height: 100vh;
  font-size: 14px;
  line-height: 1.5;
}

::selection {
  background: rgba(17, 24, 39, 0.12);
}

/* Grid so the first rail (sidebar) spans full height; the canvas fills the rest and now
   CONTAINS the app footer (as its last flex child). That lets the full-height second rail
   extend past the footer, so the footer starts after BOTH rails (see the --rail-w shift). */
.app-layout {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-rows: 1fr;
  grid-template-areas: "sidebar canvas";
  height: 100vh;
  /* THE GROUND THE THREE PANELS SIT ON (2026-09-19, user direction: "keep, left rail, right rail
     and center page body.. all of their border corners curved"). Curved corners are only visible
     against something, and until now there was nothing: `body`, `html` and this grid all declared
     no background, so the strip beside each fixed rail was the browser's default white — in dark
     mode a white seam down both edges of the screen. `.canvas` has painted `--zone-content` all
     along; this is the same ground, behind the rails as well as between them. */
  background: var(--zone-content);
}
/* The old body wrapper: dissolve it so the sidebar + canvas become direct grid items
   (its other children — hidden mounts / fixed overlays — don't occupy grid cells). */
.app-body { display: contents; }
.sidebar { grid-area: sidebar; }
.canvas { grid-area: canvas; min-height: 0; min-width: 0; }

/* The first rail (module switcher) moved into the header's app launcher (the 9-block
   "waffle"). The .sidebar element stays in the DOM as the launcher's data source but is not
   rendered; the canvas fills the full width, so each module's own second rail (.page-left-rail)
   is now the ONLY left menu. */
.app-layout { grid-template-columns: 1fr; grid-template-areas: "canvas"; }
.sidebar { display: none !important; }

/* ── Page-nav hamburger (opens the current module's left rail as a dropdown) ── */
.tb-hamburger { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: 8px; background: transparent; color: var(--text-muted); cursor: pointer; }
.tb-hamburger svg { width: 18px; height: 18px; }
/* Waffle enlarged to match the brand logo (25px). */
.tb-applauncher svg { width: 24px; height: 24px; }
/* Beta Agent avatar — full-colour SVG sized to the logo; sits next to it. */
.tb-smai { width: 30px; height: 30px; }
.tb-smai .tb-smai__ico { display: inline-flex; }
.tb-smai .tb-smai__ico svg { width: 25px; height: 25px; display: block; }
.tb-hamburger:hover { background: var(--bg-muted); color: var(--text); }
.tb-hamburger[aria-expanded="true"] { background: var(--bg-muted); color: var(--primary); }
.tb-hamburger[hidden] { display: none; }
/* The module left rail is MOVED into the hamburger and hidden by default — the page body AND
   the top header are full width, edge to edge (no leftover rail-width gap). Scoped away from
   the signed-out public shells (public-legal/public-blog have no header hamburger, so their
   rail must stay). `!important` beats per-module id rules like #legal-tab. */
body:not(.public-legal):not(.public-blog) .canvas .tab-content.active .page-left-rail { display: none !important; }/* Mobile: default hidden (JS); when opened it OVERLAYS (doesn't squeeze the narrow body) and
   stays expanded (undo the small-screen icon-only squeeze). */
@media (max-width: 760px) {}

/* ── Universal PAGE SECOND RAIL ──────────────────────────────────────────────────
   The top hamburger promotes the active module's own left nav bar (whatever its bespoke
   class) to a pinned full-height column immediately right of the first rail — a real second
   rail — and shifts the module body (header + content) right to clear it. Expanded by default;
   the hamburger toggles it. `!important` on the layout props reliably overrides each module's
   own rail positioning (flex width, sticky, id-based rules). */
:root { --rail2-w: 188px; }
/* The gap between a list toolbar and whatever follows it. ONE owner, because the expanded filter
   band cancels this exact number with a negative margin to put its accent rule on the toolbar's
   hairline — two literals that must agree drifted 14 -> 8 on one side only, and the band's rule
   became a second line 6px above the first. Defined at the root so the band can read it: the two
   are siblings, so a value set on the toolbar would not reach it. */
:root { --lk-bar-gap: 8px; }
/* --rail2-w survives THE ONE RAIL because two live rules still size a module's own in-flow rail
   with it (the injected `.smz-app-rail` idle look). It no longer sizes anything on screen — the
   navigation column is `--nav-w`, which `controllers/rails.js` drags and persists.
   `--rail2-cw`, the COLLAPSED width, is deleted with the collapsed rail it sized — nothing
   collapses a rail in place any more. `--page-rail-w` STAYS: three live rules still size
   `.workspace-rail` with it. It no longer reaches the navigation column, and the drag handle no
   longer writes it. A token is only ever deleted together with every consumer, because one
   surviving `var(…, fallback)` silently re-adds the thing the deletion was for — which is exactly
   what `--subbar-h` cost once. *//* ── Injected app-rail (mountSectionRail) — gives a no-rail module a rail to DECLARE ──
   A `<nav class="smz-app-rail">` injected as the tab's first child. It is no longer promoted or
   shown: `ui/navRail.js` reads it, clones it into the navigation column and parks the original.
   What is left here is its idle box, which nothing but the parked node uses — kept because the
   node still has to lay out for `activeModuleRail` to tell "the module hid this" (display:none)
   from "the module rendered nothing" (no clickable row). */
.canvas .tab-content.active > .smz-app-rail { display: flex; flex-direction: column; }/* (The narrow-viewport OVERLAY mode is deleted, 2026-08-20, user direction: a rail that
   floats over the body with a shadow reads as a popup, and it visibly overlapped content the
   moment the window narrowed. The rail is STANDARD chrome like the first rail now — it sits
   between the first rail and the body and pushes the body right at every width; the way to
   more room on a phone is the hamburger's collapse, not a hovering panel.) */

/* Each module's OWN in-body rail-toggle hamburger (before its breadcrumb) is now redundant —
   the top header hamburger is the single second-rail toggle. Hide them app-wide. */
[data-wk-rail-toggle], [data-orbit-rail-toggle], [data-hive-rail-toggle], [data-artha-rail-toggle] { display: none !important; }/* ═══════════════════════════════════════════════════════════════════════════════
   GLOBAL FIRST RAIL — a persistent, full-height icon rail on the far left that hosts
   every MODULE's header action icons (the .app-shell__actions / .wk-hdr-actions
   cluster). Each module's own left menu stays as the SECOND rail to its right; the
   app topbar and each module's breadcrumb are unchanged — only the header ICONS move.

   Technique: pure CSS, no DOM move. The action cluster stays in its module's own DOM
   (so every module's delegated click handlers AND its rect-anchored popups keep
   working) but is position:fixed into the rail. Only the ACTIVE tab is displayed, so
   exactly one module's icons show at a time. A ::before on the signed-in canvas paints
   the rail surface + its top "header" block, matched to the dark app topbar so the two
   read as one continuous header band across the top of the page.
   ═══════════════════════════════════════════════════════════════════════════════ */
:root {
  /* `--rail1-w` is DELETED (2026-09-14). It was the first rail's column width, and every consumer
     went with it in the same edit — the canvas gutter it reserved, the two background layers that
     painted the rail into it, the band's negative pull back over it, and the centre cluster's
     correction for that pull. A token removed while one `var(…, 58px)` fallback survives silently
     re-adds the thing the deletion was for, which is what `--subbar-h` cost once. */
  /* `--topbar-h` was declared HERE as 56px and again at the band's own block as 44px —
     the same selector ~25,600 lines apart, so the later one won and this one described
     nothing anybody could see. Deleted 2026-09-24, which makes the note there ("the number
     changes HERE and nowhere else") true. A second declaration now fails `style.css
     tokens` in `ui/sheetIntegrity.spec.js`. */
  /* The brand corner's box: just the mark, centred. The band reserves exactly this — reserve more
     than the corner occupies and there is dead space before the hamburger; reserve less and the
     hamburger sits underneath the logo. */
  --brand-w: 44px;
}

/* NO GUTTER, AND NO PAINTED COLUMN (2026-09-14, user report: "why that highlighted extra space,
   even when left bar collapsed too").

   This reserved 58px for the FIRST RAIL and painted the rail's column into it. The first rail was
   retired when the two rails became one, and the reservation was left behind — so the body sat at
   58 + `--nav-w` instead of `--nav-w`, and with the column COLLAPSED the 58px stayed on its own.
   That is the empty strip: a gutter held open for a surface that no longer exists, with the
   surface's own background still painted in it so it read as a deliberate band.

   Scoped to `.canvas:has(.topbar)` — the topbar only renders when authenticated — so the
   public/landing/legal shells are untouched. */
.canvas:has(.topbar) {
  padding-left: 0;
  /* The rail's white column, painted as .canvas's OWN background rather than by an
     overlay. An element's background always paints beneath its descendants' content —
     there is no z-index or compositing order that can invert that — so this can never
     bury the icons the way the ::before panel did in WebKit. It also keeps the rail
     white on any tab that renders no icon cluster at all. */
  /* Was `linear-gradient(#ffffff, #ffffff)`, from when the rail was white in the only
     theme there was. That literal is what shows BEFORE JS mounts the icon cluster: a
     white flash in dark mode on every load, and a rail that stays white anywhere the
     cluster doesn't end up covering it. The cluster's own background was hiding this,
     not fixing it — which is why the rail looked right only once the app had mounted. */
  /* Everything the rail is made of, as background layers on this element: the logo, the
     right-edge divider, and the column itself. It used to be split — the column here, the
     logo/brand block/divider on a `position: fixed` ::before — and that pseudo-element is
     what buried the icons in Safari. See the note below where it used to be. */
  /* The MARK layer is gone: the brand is real text in `.tb-corner-home` now (see the wordmark
     block at the end of this sheet). A 32px badge also stopped fitting the moment the header
     band came down to 28px — it overflowed into the rail below it. */
  /* (The rail column and its right-edge divider were painted here as background layers. Both are
     gone with the gutter: the navigation column paints its own surface and its own seam.) */
}/* ── App launcher (header waffle) — replaces the old first rail ─────────────── */
.tb-applauncher-wrap { position: relative; display: inline-flex; align-items: center; gap: 8px; }
.tb-applauncher { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; border: 0; border-radius: 8px; background: transparent; color: var(--text-muted); cursor: pointer; }
.tb-applauncher:hover { background: var(--bg-muted); color: var(--text); }
.tb-applauncher[aria-expanded="true"] { background: var(--bg-muted); color: var(--primary); }
.tb-brand { display: inline-flex; align-items: center; gap: 7px; text-decoration: none; color: var(--text); font-weight: 700; letter-spacing: -0.01em; }
.tb-brand .brand-logo { width: 25px; height: 25px; } /* +30% — the header shows the mark only */
.tb-brand__name { font-size: 0.82rem; }
.tb-brand .brand-ai { color: var(--primary); }
.app-launcher-panel { position: absolute; top: calc(100% + 8px); left: 0; z-index: 60; /* 560, down from 760: the width was set for five tiles a row, and at three it left a
     quarter-panel of empty grid track to the right of every row. */
  width: 560px; max-width: calc(100vw - 24px);
  max-height: calc(100vh - 72px); display: flex; flex-direction: column;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 16px 40px rgba(15, 23, 42, .18); padding: 14px 16px; }
.app-launcher-panel[hidden] { display: none !important; }
.app-launcher__head { flex: 0 0 auto; display: flex; align-items: baseline; justify-content: space-between; padding: 2px 4px 10px; }
.app-launcher__title { font-size: 0.95rem; font-weight: 700; color: var(--text); }
.app-launcher__hint { font-size: 0.7rem; color: var(--text-faint); }
.app-launcher__grid { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; min-height: 0; }
/* Category = a highlighted, separated section: a header row (accent bar + uppercase label) with a
   hairline divider above every group but the first, then a 5-across tile grid. */
.app-launcher__cat { display: flex; flex-direction: column; gap: 8px; padding-top: 12px; }
.app-launcher__cat + .app-launcher__cat { border-top: 1px solid var(--border-muted, var(--border)); }
.app-launcher__cat-title { display: flex; align-items: center; gap: 8px; margin: 0 2px; font-size: 0.7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; color: var(--primary, #2563eb); }
.app-launcher__cat-title::before { content: ''; width: 4px; height: 13px; border-radius: 2px; background: var(--primary, #2563eb); flex: 0 0 auto; }
/* THREE per row (was five): five 140px tiles packed the caption under the icon into two
   truncated lines; at three the tile is wide enough for the name and its description to read
   as one line each, which is what the descriptions are for. */
.app-launcher__cat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
/* Tile: icon → name → one-line description, all stacked under the icon (no hover tooltip). */
.app-launcher__tile { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 12px 8px 11px; border: 1px solid transparent; border-radius: 12px; background: transparent; color: var(--text); cursor: pointer; text-align: center; font: inherit; }
.app-launcher__tile:hover { background: var(--bg-muted); border-color: var(--border-muted, var(--border)); }
.app-launcher__tile.is-active { background: rgba(37, 99, 235, .10); border-color: rgba(37, 99, 235, .22); color: var(--primary); }
.app-launcher__tile.is-active .app-launcher__desc { color: var(--primary); opacity: .8; }
.app-launcher__tile.is-dragging { opacity: .45; }
.app-launcher__tile.is-disabled { opacity: .4; cursor: not-allowed; }
.app-launcher__ico { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 1px; }
.app-launcher__ico .hicon { width: 26px; height: 26px; }
.app-launcher__name { font-size: 0.76rem; font-weight: 600; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; }
.app-launcher__desc { font-size: 0.65rem; line-height: 1.3; color: var(--text-subtle, var(--text-faint)); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.app-launcher__foot { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 4px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.app-launcher__act { display: inline-flex; align-items: center; gap: 6px; padding: 6px 8px; border: 0; border-radius: 7px; background: transparent; color: var(--text-muted); font: inherit; font-size: 0.8rem; cursor: pointer; text-decoration: none; }
.app-launcher__act:hover { background: var(--bg-muted); color: var(--text); }

/* Global app footer — the last flex child of the flex-column .canvas, pinned at the bottom.
   It starts after the first rail (it's in the canvas column) and, when a second rail is
   present, is shifted right by --rail-w (below) so it starts after the second rail too. */
.app-footer {
  flex: 0 0 auto;
  position: relative;  /* keep z-index real where it meets the absolutely-positioned rail */
  z-index: 40;
  display: flex;
  align-items: center;
  box-sizing: border-box;
  min-height: 46px;
  padding: 0 10px;
  /* Neutral grayish surface (industry-standard footer tone), theme-aware. */
  background: var(--bg-muted);
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 0.8rem;
}
.app-footer__inner {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}
/* The chat bar (New chat · Chat history · open-chat tabs) moved into the footer:
   fill the width and drop its standalone-bar chrome (the footer provides bg + border). */
.app-footer .chat-footer {
  flex: 1 1 auto;
  height: auto;
  min-height: 0;
  padding: 0;
  border-top: 0;
  background: transparent;
  align-items: center;
}

/* AI + cloud credit bubbles on the left of the app footer. */
.footer-credits {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}
.footer-credits[hidden] {
  display: none;
}
/* Usage pills when shown inside the profile dropdown (under the plan info). */
.tb-profile-credits { margin-top: 8px; flex-wrap: wrap; gap: 6px; }
.footer-credit {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 0.72rem;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}
.footer-credit:hover {
  border-color: var(--accent, #6366f1);
  color: var(--text);
}
.footer-credit__label {
  font-weight: 600;
}
.footer-credit__pct {
  font-variant-numeric: tabular-nums;
}
.footer-credit.is-over {
  border-color: #c0392b;
  color: #c0392b;
}

.sidebar {
  width: 210px;
  background: var(--zone-nav);
  border-right: 1px solid var(--zone-divider);
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow-x: hidden; /* vertical-only: never a horizontal scrollbar on the first rail */
  transition: width 0.2s ease, transform 0.2s ease;
}

/* Workspace secondary rail — the Workspace sections shown as left tabs in the
   main area (a flex sibling of the main sidebar). Visible only on Workspace
   tabs; the main sidebar sits collapsed (icon rail) to its left. */
.workspace-rail {
  width: 210px;
  box-sizing: border-box; /* like .page-left-rail: the 210px MUST include the 10px side
                             padding, else the rail's real footprint (230px) exceeds the
                             body/footer margin-left (--rail-w:210px) and overlays them. */
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 12px 10px 28px;
  /* Right-edge divider starts below the 52px header (header band stays line-free). */
  background: linear-gradient(var(--zone-divider), var(--zone-divider)) right 0 top 52px / 1px 100% no-repeat, var(--zone-rail);
  border-right: 0;
  overflow-y: auto;
}
.workspace-rail[hidden] { display: none; }
/* One left bar at a time: when the main sidebar is expanded (labels shown) the
   secondary rail collapses; it returns when the main sidebar folds to icons. */
.app-layout:not(.left-collapsed) .page-left-rail,
.app-layout:not(.left-collapsed) .workspace-rail { display: none; }
/* Module-name header shown atop every secondary rail (Workspace, Hive, Orbit,
   Settings, Docs). It sits flush at the rail's top (see the padding-top:0 rule
   below) so there's no empty gap between it and the header above. */
.page-rail-title {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 46px;
  padding: 0 14px;
  margin-bottom: 6px;
  font-size: 0.77rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.page-rail-title span { font-size: 0.83rem; line-height: 1; }
/* Org-branded rail header (railOrgHeader.js) — the active org's logo mark + name. */
.page-rail-title.page-rail-title--org { gap: 9px; }
.page-rail-title .rail-org-mark { display: inline-flex; align-items: center; flex-shrink: 0; }
.page-rail-title .rail-org-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.77rem; font-weight: 600; }
/* Org logo in the rail header matches the SMAIVIZ brand logo (30px) in both states.
   !important overrides the 20px inline --ol-size from orgLogoMarkup so the box AND the
   initial-badge letter (font = --ol-size * 0.46) scale together. The name matches the
   SMAIVIZ wordmark (0.95rem / 600). */
.page-rail-title .org-logo { --ol-size: 30px !important; }
/* No uploaded logo → the initial-letter badge: use white text (the default accent blue is
   low-contrast on the coloured rail), with a translucent-white badge so it reads. */
.page-rail-title .org-logo--initial { color: #ffffff; background: #1d4ed8; }
/* Module name as a labelled divider between the org header and the first nav item (Home),
   injected by railOrgHeader.js. */
.rail-module-name {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 6px 7px;
  font-size: 0.85rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text);
  border-bottom: 1px solid var(--border);
  margin-bottom: 4px;
}
.rail-module-name__ico { display: inline-flex; align-items: center; flex-shrink: 0; }
.rail-module-name__ico .hicon { width: 22px; height: 22px; }
/* Collapsed rail → hide the module-name label (icon rail has no room). */
:is(.page-left-rail, .workspace-rail).is-collapsed .rail-module-name { display: none; }
/* Collapsed rail → keep just the org logo (hide the name), centred. */
:is(.page-left-rail, .workspace-rail).is-collapsed > .page-rail-title .rail-org-name { display: none; }
/* Rails drop their top padding so the title sits flush under the header above —
   removes the small empty space. !important overrides the modules' inline rail
   padding (hive-nav / orbit-nav / settings-subnav / doc-nav). */
.page-left-rail,
.workspace-rail { padding-top: 0 !important; }
/* The ONE common secondary-rail container — every module/section rail (Orbit, Hive, Flux,
   CDP, Artha, Echo, Kriya, App Admin, Settings, Docs, Central, Legal) carries
   `.page-left-rail`, so its width / box / scroll all come from here. No per-rail container
   settings. VERTICAL-ONLY: overflow-x is hidden (a rail that sets overflow-y without
   overflow-x makes overflow-x compute to `auto` → a stray horizontal scrollbar). */
.page-left-rail {
  flex: 0 0 var(--page-rail-w, 147px);
  width: var(--page-rail-w, 147px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0 10px 28px;
  /* Rail fill + a right-edge divider that STARTS below the 52px header (so the header band
     has no vertical line). It's a fixed background (doesn't scroll), not a full-height border. */
  background: linear-gradient(var(--zone-divider), var(--zone-divider)) right 0 top 52px / 1px 100% no-repeat, var(--zone-rail);
  border-right: 0;
}
.page-left-rail > * { flex-shrink: 0; }
/* The Workspace rail now sits below the topbar (inside .canvas-body), so its title
   is the standard 46px rail header — same as Hive/Orbit. Keep the scroll setup:
   flex-shrink:0 lets tall content overflow + scroll instead of compressing. */
.workspace-rail { min-height: 0; min-width: 0; overflow-x: hidden; }
.workspace-rail > * { flex-shrink: 0; }
/* Shared rail-label truncation: a long button label can never widen the rail (which would
   re-introduce a horizontal scrollbar) — it ellipsises instead. */
:is(.page-left-rail, .workspace-rail) button > span:not([aria-hidden="true"]):not([class*="ico"]):not([class*="chev"]) {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.workspace-rail__item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 6px 9px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.855rem;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.workspace-rail__item:hover { background: var(--bg-muted); color: var(--text); }
.workspace-rail__item.is-active {
  background: rgba(37, 99, 235, 0.10);
  color: var(--primary);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--primary);
}
.workspace-rail__icon { flex-shrink: 0; width: 18px; text-align: center; font-size: 0.9rem; line-height: 1; }
/* Expandable group (Members & Access) + its nested iam-admin children. */
.workspace-rail__group { width: 100%; }
/* The group header ("Members & Access") mirrors Hive's collapsible section labels
   (.hive-nav-summary): a slim uppercase summary, not a chunky nav item. */
.workspace-rail__summary {
  list-style: none;
  justify-content: space-between;
  margin: 12px 0 3px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-subtle);
}
.workspace-rail__summary::-webkit-details-marker { display: none; }
.workspace-rail__chev {
  margin-left: auto;
  flex-shrink: 0;
  font-size: 1.3rem;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}
.workspace-rail__group:not([open]) .workspace-rail__chev { transform: rotate(-90deg); }
.workspace-rail__children { display: flex; flex-direction: column; gap: 1px; margin: 1px 0 4px; }
.workspace-rail__child {
  display: block;
  width: 100%;
  text-align: left;
  padding: 6px 9px 6px 36px;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.855rem;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.workspace-rail__child:hover { background: var(--bg-muted); color: var(--text); }
.workspace-rail__child.is-active { background: rgba(37, 99, 235, 0.10); color: var(--primary); font-weight: 600; box-shadow: inset 2px 0 0 var(--primary); }
/* The iam-admin top sub-nav was moved into the Workspace rail — hide it in-page. */
#iam-admin-tab .settings-subnav { display: none; }

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 52px;
  flex-shrink: 0;           /* never compress — only the nav section scrolls */
  box-sizing: border-box;   /* include the 1px bottom border so it equals the topbar */
  padding: 0 14px 0 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}

.sidebar-header h2 {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.brand-logo {
  width: auto;
  height: 1.4em;
  max-width: 2.4em;
  object-fit: contain;
  display: inline-block;
  vertical-align: middle;
  flex: 0 0 auto;
}
/* App-mark ↔ org-logo swap in the sidebar brand: a slow coin-flip (rotateY); the
   image src is swapped at the edge-on midpoint by features/org/brandSwap.js. */
@keyframes brand-logo-flip {
  0%   { transform: perspective(320px) rotateY(0deg); }
  50%  { transform: perspective(320px) rotateY(90deg); }
  100% { transform: perspective(320px) rotateY(0deg); }
}
.brand-logo--flip { animation: brand-logo-flip 0.7s ease-in-out; }

.panel-toggle {
  width: 30px;
  height: 30px;
  min-width: 30px;
  min-height: 30px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  transition: all 0.15s ease;
}

.panel-toggle:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.section-tabs {
  display: flex;
  gap: 0;
  padding: 10px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}

.section-tab {
  flex: 1;
  padding: 6px 8px;
  border: 0;
  background: transparent;
  color: var(--text-subtle);
  border-radius: 6px;
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0;
  cursor: pointer;
  transition: color 0.15s ease, background 0.15s ease;
}

.section-tab:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.section-tab.active {
  background: var(--accent-soft);
  color: var(--text);
  font-weight: 600;
}

.sidebar-section {
  display: none;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.sidebar-section.active {
  display: flex;
  flex-direction: column;
  /* Roomier expanded nav (2026 spacing pass): a clear gap between items + more
     side padding gives bigger touch targets and a calmer, more readable rail. */
  gap: 3px;
  padding: 8px 10px;
}

/* Unified sidebar: one toolbar with the "+ New" button + the single global
   search box that searches every type. */
.sidebar-toolbar {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  padding: 10px 10px 6px;
}
.new-button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--accent-soft);
  color: var(--text);
  font: inherit;
  font-weight: 600;
  font-size: 0.82rem;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s ease, border-color 0.12s ease;
}
.new-button:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}
.new-button .plus {
  font-size: 1rem;
  font-weight: 600;
}
.global-search-wrap {
  flex: 1;
  min-width: 0;
}
.global-search {
  width: 100%;
}

/* "+ New" popup — pick what to start (Chat / Agent / Task plan / …). */
.new-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  padding: 16px;
}
.new-menu-overlay[hidden] {
  display: none;
}
.new-menu-card {
  width: 100%;
  max-width: 420px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
}
.new-menu-title {
  font-weight: 700;
  font-size: 1rem;
  margin-bottom: 14px;
  color: var(--text);
}
.new-menu-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
.new-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-muted);
  color: var(--text);
  font: inherit;
  font-weight: 500;
  font-size: 0.88rem;
  cursor: pointer;
  text-align: left;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
}
.new-menu-item:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}
.new-menu-item:active {
  transform: translateY(1px);
}
.new-menu-emoji {
  font-size: 1.2rem;
  line-height: 1;
}

.sidebar-action {
  padding: 12px 10px 6px;
}

.new-chat-button {
  display: flex !important;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  background: var(--bg-surface) !important;
  border: 1px solid var(--border) !important;
  color: var(--text) !important;
  font-weight: 500 !important;
}

.new-chat-button:hover {
  background: var(--bg-hover) !important;
  border-color: var(--border-strong) !important;
}

.new-chat-button .plus {
  font-size: 1rem;
  font-weight: 500;
  color: var(--text-muted);
}

.chat-history-search-wrap {
  padding: 4px 8px 0;
}

.chat-history-search {
  width: 100%;
  padding: 6px 10px;
  font-size: 0.82rem;
  line-height: 1.3;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.chat-history-search::placeholder {
  color: var(--text-faint);
}

.chat-history-search:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 2px var(--focus-ring);
}

.chat-history-heading {
  padding: 6px 12px 2px;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.chat-history-list {
  padding: 1px 8px 6px;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.chat-history-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  padding: 4px 5px;
  border: 0;
  background: transparent;
  border-radius: 6px;
  cursor: pointer;
  font: inherit;
  font-size: 0.8rem;
  line-height: 1.2;
  color: var(--text-muted);
  transition: background 0.12s ease, color 0.12s ease;
}

.chat-history-row-title {
  flex: 1;
  min-width: 0;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-history-row-delete {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  font-size: 1rem;
  line-height: 1;
  color: var(--text-faint);
  opacity: 0;
  transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
  cursor: pointer;
}

.chat-history-row:hover .chat-history-row-delete,
.chat-history-row:focus-within .chat-history-row-delete {
  opacity: 1;
}

.chat-history-row-delete:hover {
  background: var(--bg-hover, rgba(0, 0, 0, 0.06));
  color: var(--danger, #c0392b);
}

/* Recents rows: name + View / Edit / Delete actions */
.personal-row-name {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
}
.personal-row-actions {
  flex: 0 0 auto;
  display: flex;
  gap: 1px;
  opacity: 0;
  transition: opacity 0.12s ease;
}
.personal-row:hover .personal-row-actions,
.personal-row:focus-within .personal-row-actions {
  opacity: 1;
}
.personal-row-act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 4px;
  background: none;
  font-size: 0.82rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.75;
}
.personal-row-act:hover {
  background: var(--bg-hover, rgba(0, 0, 0, 0.06));
  opacity: 1;
}
.personal-row-act--danger:hover {
  color: var(--danger, #c0392b);
}

.chat-history-row:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.chat-history-row.active {
  background: var(--accent-soft);
  color: var(--text);
}

.chat-history-title {
  display: block;
  font-size: 0.82rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}

.chat-history-row small {
  display: none;
}

.empty-state.small {
  padding: 8px 10px;
  margin: 0;
  font-size: 0.78rem;
  color: var(--text-faint);
  line-height: 1.45;
}
/* Inline anchors inside empty-state nudges — let the call-to-action read
   as a link without redefining anchor styles globally for `.muted` text. */
.empty-state.small a {
  color: var(--accent, #38a);
  text-decoration: none;
}
.empty-state.small a:hover {
  text-decoration: underline;
}

.sidebar-footer {
  border-top: 1px solid var(--border);
  flex-shrink: 0;            /* stay pinned at the bottom — the nav section scrolls, not this */
  padding: 8px 8px 0;        /* flush bottom so the collapse bar lines up with the
                                chat + rail footers */
  /* No scroller here — the Learn list (the only tall thing) caps + scrolls
     itself, so the footer doesn't add a second scrollbar. */
}
/* The first sidebar's collapse control is its bottom "footer" — match the chat
   composer footer height + sit flush at the bottom so all three bottom bars
   (sidebar / secondary rail / chat) line up across the screen. */
.sidebar-pin {
  height: var(--chat-footer-h);
  box-sizing: border-box;
  margin-top: 4px;
}

.tab-footer {
  display: flex !important;
  align-items: center;
  gap: 8px;
  color: var(--text-muted) !important;
}

.tab-footer .cog {
  font-size: 0.95rem;
}

/* "Report an issue" bug icon — a warm amber-red highlight so it stands out on the rail
   (the hand-drawn SVG strokes with currentColor, so `color` tints it). */
.tab-footer[data-action="report-issue"] .tab-footer-ico { color: #f97316 !important; }
.tab-footer[data-action="report-issue"]:hover .tab-footer-ico { color: #fb923c !important; }


/* Learn accordion (above Settings) */

.learn-accordion {
  margin-bottom: 4px;
}

.learn-summary {
  list-style: none;
  cursor: pointer;
}

.learn-summary::-webkit-details-marker {
  display: none;
}

.learn-chevron {
  display: inline-block;
  font-size: 0.9rem;
  color: var(--text-faint);
  transition: transform 0.15s ease;
}

.learn-accordion[open] .learn-chevron {
  transform: rotate(90deg);
}

.learn-sublist {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px 0 4px;
  /* The Learn list has many entries; cap its height when expanded and keep one
     default vertical scrollbar always shown so it scrolls in place instead of
     pushing the Report/Settings items off-screen. This is the only scroller in
     the footer (the footer itself no longer scrolls), so there's just one bar. */
  max-height: 260px;
  overflow-y: scroll;
}

.tab.learn-sub {
  padding-left: 28px;
  font-size: 0.82rem;
}

.tabs {
  padding: 10px 8px;
}

.nav-group {
  margin-bottom: 4px;
}

.nav-group-title {
  padding: 14px 12px 6px;
  color: var(--text-faint);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.tab {
  display: block;
  width: 100%;
  padding: 7px 12px;
  margin-bottom: 1px;
  border: 0;
  background: transparent;
  color: var(--text-muted);
  text-align: left;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0;
  /* Some tabs are <a href="/x" data-tab="x"> for native right-click +
     middle-click support (industry-standard SPA nav); the others are
     <button>. Strip the default anchor underline so both render identically. */
  text-decoration: none;
  transition: background 0.12s ease, color 0.12s ease;
}

.tab:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.tab.active {
  background: var(--accent-soft);
  color: var(--text);
  font-weight: 600;
}

.canvas {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--zone-content);
  overflow: hidden;
  position: relative;
}
/* Content row below the topbar: the Workspace rail (when shown) sits to the left
   of the active tab, like the Hive/Orbit rails. The topbar spans full width above. */
.canvas-body {
  flex: 1;
  display: flex;
  min-height: 0;
  min-width: 0; /* Safari: let the content column shrink instead of being forced
                   wide by inner content (tables, long strings) and pushed sideways. */
}

/* Descendant (not child) combinator: the tabs sit inside #app-tabs (display:contents),
   so `.canvas > .tab-content` would not match. */
.canvas .tab-content.active {
  flex: 1;
  min-height: 0;   /* let the tab + its inner rails scroll instead of growing */
  min-width: 0;    /* Safari: same, on the horizontal axis — without this a wide
                      child forces the whole page wider and shoves items sideways. */
  overflow-y: auto;
  /* Every in-app page shares the Preferences page's base background (the app grey), so no page
     shows a different canvas colour. Module content areas still layer their own surfaces on top. */
  background: var(--bg-app);
}

/* Global breadcrumb — the path (Org › Module › Section) shown inline on the LEFT of
   the topbar. Hidden while the header search is expanded. */
.app-breadcrumb {
  flex: 0 1 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  max-width: 42%;
  padding: 0;
  font-size: 0.76rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-breadcrumb[hidden] { display: none; }
.app-breadcrumb__crumb {
  color: var(--text-muted);
  text-decoration: none;
}
a.app-breadcrumb__crumb:hover { color: var(--text); text-decoration: underline; }
.app-breadcrumb__crumb.is-current { color: var(--text); font-weight: 600; }
.app-breadcrumb__sep { color: var(--text-faint); }
body.search-expanded .app-breadcrumb { display: none; }

.canvas-header {
  padding: 6px 24px;
  min-height: 52px;        /* match the sidebar logo header + topbar */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* User-themeable header — one choice re-skins every module's header bar. Defaults to the
     teal→cobalt gradient. Kriya opts OUT below (.wk-header) because it carries its own
     gradient content headers (.wk-board-header / .wk-detail-header) and would double up. */
  background: var(--app-header-bg);
  color: var(--app-header-text);
  border-bottom: 1px solid rgba(0, 0, 0, .08);
}

.canvas-header h1 {
  margin: 0;
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--app-header-text);
}

/* Only the dynamic project labels remain (descriptions removed); keep them tight. */
.canvas-header p {
  margin: 2px 0 0;
  color: var(--app-header-text);
  opacity: .82;
  font-size: 0.8rem;
}

/* The "Help docs" pill sits on the themed header — use the header text colour. */
.canvas-header > .canvas-ref-link {
  color: var(--app-header-text);
  border-color: color-mix(in srgb, var(--app-header-text) 45%, transparent);
  opacity: .92;
}
.canvas-header > .canvas-ref-link:hover {
  color: var(--app-header-text);
  border-color: var(--app-header-text);
  background: color-mix(in srgb, var(--app-header-text) 16%, transparent);
  opacity: 1;
}

/* Kriya keeps a plain white module bar — its page header is the gradient .wk-board-header /
   .wk-detail-header inside the content, so the top bar must NOT also be a gradient. */
.canvas-header.wk-header {
  background: var(--bg-surface);
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.canvas-header.wk-header h1 { color: var(--text); }
.canvas-header.wk-header > .canvas-ref-link { color: var(--muted, #9aa0a6); border-color: var(--border); opacity: 1; }

.canvas-header + * {
  margin-top: 10px;
}
/* In the flex-column module tabs (Hive/Orbit/Docs/Flux) the canvas-header is a
   flush header, so the rail/content sits directly beneath it — no 10px gap. */
#hive-tab .canvas-header + *,
#orbit-tab .canvas-header + *,
#docs-tab .canvas-header + *,
#flux-tab .canvas-header + *,
#kriya-tab .canvas-header + *,
#artha-tab .canvas-header + *,
#cdp-tab .canvas-header + *,
#app-admin-tab .canvas-header + *,
#config-tab .canvas-header + *,
#central-tab .canvas-header + * {
  margin-top: 0;
}

/* ── Global app-shell flush contract ─────────────────────────────────────────────
   The one drop-in rule that makes the shared `.app-shell` chrome (see appShell.js +
   the `.app-shell__*` styles below) sit FLUSH under the app top bar in ANY module —
   no per-tab exception list to maintain. A tab that renders the shell:
     • drops the .tab-content top padding, so the shell's header is the first thing
       under the app top bar (mirrors Kriya's .wk-flush-top);
     • treats the shell AS its module header, so a legacy .canvas-header left in the
       same partial is hidden and its 10px sibling gap is cancelled.
   Migrating a module to renderShell() is therefore all it takes to get the exact
   Kriya-standard flush header/toolbar/filter — nothing to add here per module. */
.tab-content:has(.app-shell) { padding-top: 0; }
.tab-content:has(.app-shell) > .canvas-header { display: none; }
.tab-content:has(.app-shell) .canvas-header + * { margin-top: 0; }

/* Normalize Artha + CDP to the Orbit/Hive content pattern so the flush toolbar full-bleeds
   the same way everywhere: cancel the tab's 28px padding (which left the toolbar inset) and
   move the inset onto the scroll-area main, carrying the --page-pad-x/top bleed vars. */
#artha-tab,
#cdp-tab,
#workspace-tab { padding: 0; }
.artha-main,
.cdp-main { padding: 20px 24px 40px; --page-pad-x: 24px; --page-pad-top: 20px; }

/* ── Uniform module body ────────────────────────────────────────────────────────
   Every module's main content column matches Orbit's Home (.mod-home): a centered,
   max-width-capped, evenly-padded column instead of stretching edge-to-edge. The
   `.canvas-body` prefix lifts specificity (0,2,0) above the per-module inline
   `.X-main` rules (0,1,0) in the partials, so this governs them all regardless of
   load order. Flux is intentionally excluded — its `.flux-main` is the node-editor
   canvas column, not a document body, so capping/centering it would shrink the editor.
   Workspace is handled on `.workspace-view` below (its scroll lives on the tab, not
   the wrapper). ───────────────────────────────────────────────────────────────── */
/* WRITTEN OUT, NOT `:is()` (2026-09-15). The note above claims the `.canvas-body` prefix lifts
   this to (0,2,0) so it governs the per-module `.X-main` rules regardless of load order. With
   `:is()` that claim is FALSE IN WEBKIT, which prices `:is()` at specificity ZERO — so in Safari
   it resolved at (0,1,0), tied with each module's own rule, and document order decided. Those
   module rules are INJECTED AT RUNTIME from each controller, so the order is whichever module
   the reader opened first: the same page could show one gutter or two depending on their
   navigation. Eight selectors is the price of the rule actually winning. */
.canvas-body .orbit-main,
.canvas-body .hive-main,
.canvas-body .kriya-main,
.canvas-body .echo-main,
.canvas-body .cdp-main,
.canvas-body .artha-main,
.canvas-body .lens-main,
.canvas-body .appadmin-main {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
  /* Page body AFTER the second rail (the rail stays white, --zone-rail) so white
     cards/tiles read as raised. Uses the app base grey so every module body matches the
     Preferences page background exactly (no per-module shade). Dark override below. */
  background: var(--bg-app);
  /* Full-width scroll container so the vertical scrollbar sits at the PAGE's far-right edge
     (UI Polish standard), not at the edge of a centred body column. The roomy padding-inline
     gutter gives content breathing room and sits between the content and the scrollbar. The
     horizontal inset is the shared responsive --page-gutter, so it tracks the top header. */
  padding: 20px var(--page-gutter) 40px;
  --page-pad-x: var(--page-gutter);
  --page-pad-top: 20px;
}
html[data-theme='dark'] .canvas-body .orbit-main,
html[data-theme='dark'] .canvas-body .hive-main,
html[data-theme='dark'] .canvas-body .kriya-main,
html[data-theme='dark'] .canvas-body .echo-main,
html[data-theme='dark'] .canvas-body .cdp-main,
html[data-theme='dark'] .canvas-body .artha-main,
html[data-theme='dark'] .canvas-body .lens-main,
html[data-theme='dark'] .canvas-body .appadmin-main {
  background: var(--bg-app);
}

/* Promote the module-body scroll to the PAGE's far-right edge. Each `.X-main` above owns
   `overflow-y: auto` and carries its own gutter padding — but the tab wrapping it still has
   the default `.tab-content` horizontal padding, which insets the scroll container (and so its
   scrollbar) by --page-gutter from the window edge. Any tab hosting a full-width scroll-main
   drops that horizontal padding so the main reaches the window edge and its scrollbar runs
   flush against the page's far right. (kriya/artha/cdp/workspace already zero it explicitly.) */
/* WRITTEN OUT, NOT `:has(:is(…))`, AND THE ORDER IS WHY IT MATTERS (2026-09-15).
   This rule sits ABOVE `.tab-content { padding: 24px var(--page-gutter) }`, so it can only take
   effect by winning on SPECIFICITY. `:has()` takes the weight of its most specific argument —
   which is exactly (0,1,0) for these classes in Chrome and Firefox, giving (0,2,0) and the win.
   WebKit prices `:is()` at ZERO, so there it resolved to (0,1,0), tied with the later
   `.tab-content`, and LOST: Safari kept the tab's gutter while the module body kept its own, and
   every one of these pages had two edges in that browser and one everywhere else. */
.tab-content:has(.orbit-main),
.tab-content:has(.hive-main),
.tab-content:has(.kriya-main),
.tab-content:has(.echo-main),
.tab-content:has(.cdp-main),
.tab-content:has(.artha-main),
.tab-content:has(.lens-main),
.tab-content:has(.appadmin-main),
/* Party joined 2026-09-21, user report against its All-companies list: "left and right side
   margins and all, arrange the page". It was in NONE of the three rules below, so the tab kept
   its gutter AND `.pty-main` added `clamp(12px, 3vw, 28px)` of its own — two edges, which is why
   the table sat inset from the breadcrumb above it. Written out longhand like its neighbours: the
   `:is()` form is priced at specificity ZERO by WebKit, which is the defect this whole list was
   rewritten to escape. */
.tab-content:has(.pty-main) {
  padding-left: 0;
  padding-right: 0;
}

.tab-content {
  display: none;
  padding: 24px var(--page-gutter);
}

.tab-content.active {
  display: block;
}

/* Standalone signed-out legal page (/legal, /privacy, /terms, /cookies, /security):
   the console chrome is stripped for guests, so render #legal-tab as a full-page,
   two-pane doc viewer — the policy nav rail on the LEFT, the selected policy scrolling
   on the RIGHT (matching the Docs page). The body itself doesn't scroll; each pane does,
   so there's no big empty area below short policies. */
/* The standalone signed-out Legal page reuses the marketing chrome: the sticky
   top nav stays, the Legal viewer is slotted in (JS moves #legal-tab into
   #home-landing before the footer), and the footer sits below. `is-legal` hides
   the marketing body + dead in-page nav links so only the header/footer remain. */
.home-landing:is(.is-legal, .is-blog) .home-main,
.home-landing:is(.is-legal, .is-blog) .home-nav-links,
.home-landing:is(.is-legal, .is-blog) .home-footer,
.home-landing:is(.is-legal, .is-blog) .docs-chat-launcher { display: none; }

body.public-legal {
  overflow-x: hidden;
  overflow-y: auto;
}
body.public-legal #home-landing { min-height: 100vh; }
/* On the standalone legal page the top nav scrolls away with the page (not pinned) — the whole
   page, header included, moves with the scrollbar. */
body.public-legal .home-nav { position: static; }
body.public-legal #legal-tab.tab-content.active {
  /* Full-width: the nav rail sits flush at the far-left page edge; the policy content is a centered
     readable column in the space to its right (see .doc-content below). */
  max-width: none;
  margin: 0;
  padding: 0;
  /* Flow naturally so the PAGE (body) scrolls — the scrollbar sits at the viewport's right edge,
     not inside the content pane. min-height fills the viewport for short policies. */
  min-height: calc(100vh - 60px);
  height: auto;
  overflow: visible;
}
body.public-legal #legal-tab .doc-shell {
  height: auto;
  min-height: 0;
  align-items: flex-start;
  margin-top: 0; /* override `.canvas-header + *` (the header is hidden here) */
}
/* The content pane flows at its natural height — no internal scroll; the body scrolls instead. Its
   readable policy column is centered in the space to the right of the far-left rail. */
body.public-legal #legal-tab .doc-content {
  overflow: visible;
  height: auto;
  padding: 0;
}
body.public-legal #legal-tab .doc-content > .doc-page {
  max-width: 820px;
  margin-inline: auto;
  padding: 32px 32px 72px;
}
/* Re-show the legal nav rail (the console hides secondary rails). It flows at its natural height —
   no scrollbar of its own — and scrolls WITH the page (moving to the bottom as you scroll). The
   page is at least as tall as the rail, so the whole menu is always visible without clipping. */
body.public-legal #legal-tab .doc-nav.page-left-rail {
  display: block;
  position: static;
  align-self: flex-start;
  max-height: none;
  overflow: visible;
  height: auto;
}
/* The rail title ("Legal & policies") is sticky by the shared rail standard — but here the rail has
   no internal scroll, so that sticky would pin it to the viewport ("always visible"). Keep it in
   flow so it scrolls away with the page like everything else. */
body.public-legal #legal-tab .doc-nav .page-rail-title { position: static; }

/* Standalone signed-out BLOG page (/blogs, /blogs/<slug>) — mirrors public-legal: it KEEPS the
   marketing top-nav header (the blog is slotted into #home-landing), the nav scrolls away with the
   page, and the blog flows below it at page-level scroll. Single column; the blog's own wrappers
   center the content (.blog-index-body caps at 1140px, .blog-post at 720px). */
body.public-blog { overflow-x: hidden; overflow-y: auto; }
body.public-blog #home-landing { min-height: 100vh; }
/* The top nav scrolls away with the page (not pinned), like the legal page's header. */
body.public-blog .home-nav { position: static; }
body.public-blog #blogs-tab.tab-content.active {
  padding: 24px 28px;
  min-height: calc(100vh - 60px);
  height: auto;
  overflow: visible;
}

/* ── Legal Center: self-contained, responsive two-pane viewer ────────────────────
   The legal nav reuses .page-left-rail, but #legal-tab lives OUTSIDE .canvas-body, so
   the canvas absolute-rail hack mis-positions it (overlapping the content when narrow).
   Lay the nav + content out as a plain flex row that STACKS when narrow. #id beats the
   canvas :has() rail rules, so this governs both signed-in and signed-out. */
#legal-tab .doc-shell {
  display: flex;
  height: 100%;
  min-height: 0;
}
#legal-tab .doc-nav.page-left-rail {
  display: block;
  position: static;
  flex: 0 0 232px;
  width: 232px;
  bottom: auto;
  z-index: auto;
  align-self: stretch;
  overflow-y: auto;
}
#legal-tab .doc-content {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  padding-left: 24px;
  background: #fff;
}
/* The nav's own "⚖️ Legal & policies" title is the page heading — drop the redundant
   canvas-header above it. */
#legal-tab > .canvas-header { display: none; }
@media (max-width: 760px) {
  #legal-tab .doc-shell { flex-direction: column; }
  #legal-tab .doc-nav.page-left-rail {
    flex: 0 0 auto;
    width: auto;
    max-height: 40vh;
    border-right: 0;
    border-bottom: 1px solid var(--zone-divider);
  }
  #legal-tab .doc-content { padding-left: 0; }
}

/* ── Legal Center — polished public doc-site UX ──────────────────────────────────
   The legal nav/content classes (.doc-nav / .doc-content / .doc-page) are legal-only
   now (Docs moved to the app-shell), so these rules give the standalone public Legal
   page an industry-standard doc-site look: brand-left / auth-right sticky header, a
   clean grouped policy sidebar, and a readable prose column. Scoped to #legal-tab. */

/* Header: right-align Sign in / Get started. On the standalone page the middle nav
   links (which normally flex-push the actions right) are hidden by .is-legal, so add
   the spacer back here. */
.home-landing:is(.is-legal, .is-blog) .home-nav-actions { margin-left: auto; }

/* Left policy nav — sticky, grouped, subtle. */
#legal-tab .doc-nav.page-left-rail {
  padding: 26px 14px 44px;
  background: var(--bg-surface);
  border-right: 1px solid var(--border-muted, var(--border));
}
#legal-tab .doc-nav .page-rail-title {
  display: flex; align-items: center; gap: 8px;
  margin: 0 6px 16px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border-muted, var(--border));
  font-size: 1rem; font-weight: 700; letter-spacing: 0.01em;
  color: var(--text);
}
#legal-tab .doc-nav-group { margin: 0 0 8px; }
#legal-tab .doc-nav-group > summary {
  /* No expand/collapse: the group headings are plain labels — clicking doesn't toggle (the groups
     stay open via their `open` attribute). */
  list-style: none; cursor: default; pointer-events: none; user-select: none;
  margin: 10px 0 3px; padding: 4px 10px;
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-faint, #94a3b8);
}
#legal-tab .doc-nav-group > summary::-webkit-details-marker { display: none; }
#legal-tab .doc-nav-group > summary::marker { content: ""; }
#legal-tab .doc-nav a {
  display: block;
  margin: 1px 0; padding: 7px 12px;
  border-radius: 8px;
  color: var(--text-muted); text-decoration: none;
  font-size: 0.875rem; font-weight: 500; line-height: 1.35;
  transition: background 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}
#legal-tab .doc-nav a:hover { background: var(--bg-muted); color: var(--text); }
#legal-tab .doc-nav a.is-active {
  background: var(--accent-soft, rgba(37, 99, 235, 0.1));
  color: var(--primary); font-weight: 600;
  box-shadow: inset 2px 0 0 var(--primary);
}

/* Content — a comfortable, readable prose column with clear hierarchy. */
#legal-tab .doc-content { padding: 34px 44px 72px; }
/* One policy shown at a time: the controller toggles `.is-active` on the selected
   `.doc-page` (features/legal/controller.js). Without this pair every policy renders
   stacked, so clicking a left-rail link couldn't land on the right policy — on the
   standalone public page it just jumped to the top of the stack. (The doc-modal view
   re-shows every page via `.doc-modal-body .doc-content .doc-page { display:block }`.) */
#legal-tab .doc-page { display: none; }
#legal-tab .doc-page.is-active { display: block; }
#legal-tab .doc-page { max-width: 760px; }
#legal-tab .doc-crumb {
  margin: 0 0 6px;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--text-faint, #94a3b8);
}
#legal-tab .doc-page h1 {
  margin: 2px 0 14px;
  font-size: 1.95rem; font-weight: 760; line-height: 1.2; letter-spacing: -0.01em;
  color: var(--text);
}
#legal-tab .doc-page h2 {
  margin: 32px 0 10px;
  font-size: 1.22rem; font-weight: 700; color: var(--text);
}
#legal-tab .doc-page h3 { margin: 22px 0 8px; font-size: 1.02rem; font-weight: 700; color: var(--text); }
#legal-tab .doc-page p,
#legal-tab .doc-page li { font-size: 0.95rem; line-height: 1.72; color: var(--text-muted); }
#legal-tab .doc-page p { margin: 0 0 14px; }
#legal-tab .doc-page ul,
#legal-tab .doc-page ol { margin: 0 0 16px; padding-left: 22px; }
#legal-tab .doc-page li { margin: 0 0 7px; }
#legal-tab .doc-page li::marker { color: var(--text-faint, #94a3b8); }
#legal-tab .doc-page a { color: var(--primary); text-decoration: none; font-weight: 500; }
#legal-tab .doc-page a:hover { text-decoration: underline; }
#legal-tab .doc-page strong { color: var(--text); font-weight: 650; }
#legal-tab .doc-page hr { border: 0; border-top: 1px solid var(--border-muted, var(--border)); margin: 30px 0; }
#legal-tab .doc-page table { border-collapse: collapse; width: 100%; margin: 4px 0 18px; font-size: 0.9rem; }
#legal-tab .doc-page th,
#legal-tab .doc-page td { border: 1px solid var(--border-muted, var(--border)); padding: 8px 11px; text-align: left; vertical-align: top; }
#legal-tab .doc-page th { background: var(--bg-muted); font-weight: 650; color: var(--text); }
@media (max-width: 760px) {
  #legal-tab .doc-content { padding: 22px 18px 56px; }
  #legal-tab .doc-page h1 { font-size: 1.6rem; }
}

/* ── Public LEARN/legal/blog tab shown INSIDE the signed-in console ───────────────
   These tabs ship in #public-tabs (OUTSIDE .canvas-body), so when one is active the
   module work-area has no active tab — leaving a big empty block on top, and the tab
   itself trails the chat footer in the DOM. Collapse the empty work-area, cancel the
   secondary-rail shift, and order the active public tab to sit between the topbar and
   the footer. Scoped away from the signed-out standalone pages (they keep their own
   full-page overlay above). */
body:not(.public-legal):not(.public-blog) .canvas:has(#public-tabs > .tab-content.active) .canvas-body { display: none; }
body:not(.public-legal):not(.public-blog) .canvas:has(#public-tabs > .tab-content.active) .topbar { margin-left: 0; }
body:not(.public-legal):not(.public-blog) .canvas:has(#public-tabs > .tab-content.active) #public-tabs > .tab-content.active { order: 1; min-height: 0; }
body:not(.public-legal):not(.public-blog) .canvas:has(#public-tabs > .tab-content.active) .chat-footer { order: 2; }
/* Signed-in, the Legal Center fills the canvas as a flex column so its two panes scroll
   internally (not the whole tab). */
body:not(.public-legal) .canvas #legal-tab.tab-content.active {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}

.canvas-area {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  margin-top: 16px;
  padding: 20px;
}

.left-collapsed .sidebar {
  width: 0;
  overflow: hidden;
  border: 0;
}

.topbar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 0.95rem;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.topbar-toggle:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.left-collapsed .topbar-toggle {
  display: inline-flex;
}

/* Top bar */
.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 4px;
  min-height: var(--topbar-h);   /* Gmail-height header */
  box-sizing: border-box;
  padding: 0 32px 0 4px;         /* tight left (icons hug the rail); 32px gap after the profile icon */
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
}

/* (The Gmail-era light-skin .topbar rule stood here. Its surface and washes are owned by
   "THE CHROME RUNS OPPOSITE" at the end of this sheet now — a leftover wash here would be a
   second declarer of the same names, which is exactly what that block's spec forbids.) */
/* Profile avatar keeps a colourful fill (reads well on the white bar). */
.topbar .tb-avatar { background: linear-gradient(135deg, #0e7490, #1d4ed8); color: #ffffff; }
/* These popovers used to opt OUT of a DARK header skin by restoring light tokens.
   The header is light now, so the block only re-pinned values and stopped the theme
   reaching the launcher, the profile menu and the search suggestions. They inherit
   the palette like everything else. */

.topbar-spacer {
  flex: 1;
}

.tb-control {
  position: relative;
}

/* Header active-organization picker (global org selector). A single clean
   control — native chrome reset, fixed height to match the other topbar controls,
   box-sizing so padding never bloats it, ellipsis for long org names. */
/* Custom org picker — a button + menu (native <option> can't hold the org logo
   image), driven by features/org/orgPicker.js. Each item shows the org's uploaded
   logo, or the initial/DNA-swap fallback (features/workspace/orgLogo.js). */
.tb-org { position: relative; display: inline-flex; align-items: center; }
.tb-org-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  max-width: 196px;
  box-sizing: border-box;
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font-size: 0.8rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.15s ease;
}
.tb-org-btn:hover { border-color: var(--border-strong); }
.tb-org-btn:focus-visible { border-color: var(--border-strong); box-shadow: 0 0 0 2px var(--focus-ring); outline: none; }
.tb-org-mark { display: inline-flex; flex-shrink: 0; }
.tb-org-name { overflow: hidden; text-overflow: ellipsis; }
.tb-org-chev { flex-shrink: 0; margin-left: auto; font-size: 0.62rem; color: var(--text-faint); }
.tb-org-menu {
  position: absolute;
  top: calc(100% + 7px);
  right: 0;
  min-width: 224px;
  max-height: 60vh;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 70;
}
.tb-org-menu[hidden] { display: none; }
.tb-org-menu li {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.85rem;
  color: var(--text);
  cursor: pointer;
}
.tb-org-menu li:hover { background: var(--accent-soft); }
.tb-org-menu li.is-active { background: var(--accent-soft); color: var(--primary); font-weight: 600; }
.tb-org-menu .tb-org-name { white-space: nowrap; }

/* Org switcher relocated into the profile dropdown: a full-width row whose list
   expands inline (in place) below the button instead of floating over the menu. */
.tb-profile-org .tb-menu-label {
  padding: 2px 8px 4px;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
}
.tb-profile-menu .tb-org { display: block; width: 100%; }
.tb-profile-menu .tb-org-btn { width: 100%; justify-content: flex-start; }
.tb-profile-menu .tb-org-menu {
  position: static;
  inset: auto;
  min-width: 0;
  width: 100%;
  max-height: 40vh;
  margin-top: 4px;
  box-shadow: none;
}

/* Cloud & AI settings pane — the moved Provider/Model/Cloud selectors. */
.card-subtle { color: var(--text-subtle); font-size: 0.82rem; margin: 0 0 10px; }
.cloud-ai-control .tb-selector { width: 100%; justify-content: space-between; height: 38px; }

.tb-selector {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  height: 34px;
  padding: 0 10px;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.tb-selector:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.tb-label {
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.tb-value {
  font-weight: 500;
  color: var(--text);
  max-width: 200px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tb-caret {
  color: var(--text-faint);
  font-size: 0.7rem;
}

.tb-profile-button {
  /* Block-level flex: the button must NOT sit on a text baseline — the avatar's
     baseline differs between an initial letter and an uploaded photo <img>, which
     made the circle creep up in the header once a photo was set. */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-surface);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.tb-profile-button:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.tb-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg-surface);
  font-size: 1.15rem;
  font-weight: 600;
  /* Baseline = bottom edge whatever the content, so swapping the initial for an
     uploaded <img> doesn't nudge the circle up in the header line box. */
  overflow: hidden;
}

.tb-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 260px;
  max-height: 380px;
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
  z-index: 40;
}

.tb-menu-empty {
  padding: 12px;
  color: var(--text-faint);
  font-size: 0.84rem;
  text-align: center;
}

/* ── Appearance toggle (header) ───────────────────────────────────────────────
   One button, two states, no menu. The glyph shows the theme the click will GIVE
   you — moon while you are in light — which is the convention and the only version
   that answers "what does pressing this do?". Light is the default, so the moon is
   what paints before any JS has run. */
.tb-appear__glyph .tb-appear__sun { display: none; }
.tb-appear__glyph .tb-appear__moon { display: inline; }
#appearance-button[data-theme='dark'] .tb-appear__sun { display: inline; }
#appearance-button[data-theme='dark'] .tb-appear__moon { display: none; }

/* ── Notifications bell (topbar) ─────────────────────────────────────────── */
.tb-notif-button {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.85rem;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.tb-notif-button:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.tb-notif-badge {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: #e5484d;
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
}

.tb-notif-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 10px 6px;
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.tb-notif-readall {
  border: none;
  background: transparent;
  color: var(--accent);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  cursor: pointer;
  padding: 0;
}

.tb-notif-readall:hover { text-decoration: underline; }

.tb-notif-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease;
}

.tb-notif-item:hover {
  background: var(--bg-hover);
}

.tb-notif-item.is-unread {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.tb-notif-icon {
  flex: 0 0 auto;
  font-size: 1rem;
  line-height: 1.3;
}

.tb-notif-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.tb-notif-title {
  color: var(--text);
  font-size: 0.85rem;
  font-weight: 600;
}

.tb-notif-sub {
  color: var(--text-subtle);
  font-size: 0.78rem;
  line-height: 1.35;
}

.tb-menu .tb-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 34px;
  margin: 1px 0;
  padding: 7px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}

.tb-menu .tb-menu-item:hover {
  background: var(--bg-hover);
}

.tb-menu .tb-menu-item.active {
  background: var(--accent-soft);
  font-weight: 600;
}

.tb-menu .tb-menu-item.locked {
  /* Locked models stay selectable — clicking shows a friendly error on send.
     Visually they're dimmed so the user understands they need extra setup. */
  color: var(--text-muted);
}

.tb-menu .tb-menu-item.locked .tb-menu-bullet {
  opacity: 0.6;
}

.tb-menu-label {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tb-menu-hint {
  flex-shrink: 0;
  padding: 2px 8px;
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: 999px;
  white-space: nowrap;
  border: 1px solid var(--border);
  background: var(--bg-muted);
  color: var(--text-muted);
  cursor: default;
}

/* "Available" — user has a key for this provider. Green-tinted, calm. */
.tb-menu-hint-available {
  color: #14803c;
  background: rgba(20, 128, 60, 0.10);
  border-color: rgba(20, 128, 60, 0.22);
}

/* "Add API key" — actionable; clicking jumps to the API-keys page. */
.tb-menu-hint-add-key {
  color: #1e6fbf;
  background: rgba(30, 111, 191, 0.10);
  border-color: rgba(30, 111, 191, 0.28);
  cursor: pointer;
}

.tb-menu-hint-add-key:hover,
.tb-menu-hint-add-key:focus-visible {
  background: rgba(30, 111, 191, 0.18);
  color: #154e8f;
  outline: none;
}

/* "Upgrade to Pro" — actionable; clicking jumps to the Plans page. */
.tb-menu-hint-upgrade {
  color: #8a4b00;
  background: rgba(204, 102, 0, 0.10);
  border-color: rgba(204, 102, 0, 0.28);
  cursor: pointer;
}

.tb-menu-hint-upgrade:hover,
.tb-menu-hint-upgrade:focus-visible {
  background: rgba(204, 102, 0, 0.18);
  color: #6a3a00;
  outline: none;
}

.tb-menu-bullet {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 22px;
  border-radius: 5px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.66rem;
  font-weight: 600;
  flex-shrink: 0;
}

.tb-menu-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  color: var(--text-faint);
  font-size: 0.6rem;
}

.tb-profile-menu {
  min-width: 280px;
}

.tb-profile-header {
  padding: 12px 12px 10px;
  border-bottom: 1px solid var(--border-muted);
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 11px;
}
/* Bigger copy of the avatar inside the dropdown (photo or initial). */
.tb-profile-header__avatar { width: 64px; height: 64px; font-size: 1.7rem; flex: none; }
.tb-profile-header__info { flex: 1; min-width: 0; }

.tb-profile-name {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text);
}

.tb-profile-email {
  font-size: 0.8rem;
  color: var(--text-subtle);
  margin-top: 2px;
}

.tb-profile-plan {
  display: inline-block;
  margin-top: 8px;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.7rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Plan/trial info chip inside the Account dropdown (relocated from the header). */
.tb-profile-trial {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--border-strong, #cbd5e1);
  background: var(--bg-subtle, #f1f5f9);
  color: var(--text-muted, #475569);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.1;
  cursor: pointer;
  white-space: nowrap;
}
.tb-profile-trial[data-warn="1"] {
  border-color: var(--warn, #f59e0b);
  background: var(--warn-bg, #fef3c7);
  color: var(--warn-fg, #b45309);
}
.tb-profile-trial:hover { filter: brightness(0.98); }

/* Plan/trial pinned as the top-most notification item (owner/admin only). */
.tb-notif-plan { border-bottom: 1px solid var(--border-muted, #eef0f3); }
.tb-notif-plan.is-warn { background: var(--warn-bg, #fef3c7); }
.tb-notif-plan.is-warn .tb-notif-title { color: var(--warn-fg, #b45309); }
.tb-notif-plan .tb-notif-icon { color: var(--warn-fg, #b45309); }

.tb-menu-section {
  padding: 4px 0;
  border-top: 1px solid var(--border-muted);
}

.tb-menu-section:first-of-type {
  border-top: 0;
}

.tb-menu-item-danger {
  color: var(--danger) !important;
}

.card {
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 12px;
  padding: 18px 20px;
  margin-bottom: 16px;
  /* App page standard: generic cards wear the Kriya tile chrome (soft border +
     layered shadow) so Settings/Billing/Echo/CDP/Ordio/Pimly cards match. */
  box-shadow: 0 1px 2px rgba(16,24,40,.05), 0 3px 8px rgba(16,24,40,.05);
}

.card h3,
.card-title {
  margin: 0 0 14px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.empty-state {
  margin: 0;
  color: var(--text-subtle);
  font-size: 0.85rem;
}

.saved-chat-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  margin: 0 0 6px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.saved-chat-row:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.saved-chat-row span {
  font-weight: 500;
  font-size: 0.86rem;
}

.saved-chat-row small {
  margin-top: 3px;
  color: var(--text-subtle);
  font-size: 0.74rem;
}

.chat-shell {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 130px);
  min-height: 520px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}

.project-chat-shell {
  display: flex;
  flex-direction: column;
  min-height: calc(100vh - 40px);
}

.project-chat-shell.compact {
  min-height: 560px;
}

.project-header {
  margin: -20px -20px 20px;
}

.task-builder-grid {
  display: grid;
  grid-template-columns: minmax(320px, 0.9fr) minmax(460px, 1.4fr);
  gap: 20px;
  align-items: stretch;
}

.architecture-panel {
  min-height: 560px;
  padding: 18px;
  border: 1px solid #dedbd2;
  border-radius: 12px;
  background: var(--bg-surface);
  overflow: auto;
}

.architecture-diagram {
  min-width: 980px;
}

.flow-reference-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid #d8d5ca;
  border-radius: 10px;
  background: #f7f7f4;
  color: #29312c;
  font-size: 0.86rem;
}

.flow-reference-header div {
  display: flex;
  gap: 10px;
  align-items: center;
}

.flow-reference-header span {
  color: #5d655f;
}

.architecture-lane {
  display: flex;
  justify-content: center;
}

.architecture-arrow {
  padding: 10px 0;
  color: #68716b;
  text-align: center;
  font-size: 1.4rem;
}

.architecture-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(140px, 1fr));
  gap: 14px;
}

.architecture-flow {
  display: grid;
  gap: 10px;
}

.flow-layer {
  display: grid;
  grid-template-columns: 230px 1fr;
  gap: 12px;
  align-items: stretch;
  padding: 10px;
  border: 1px solid #dcd9ce;
  border-radius: 10px;
  background: var(--bg-surface);
}

.flow-layer-title {
  display: flex;
  align-items: center;
  min-height: 52px;
  padding: 12px;
  border-radius: 8px;
  color: #1f2e27;
  font-size: 0.88rem;
  font-weight: 800;
  outline: none;
}

.flow-components {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
}

.flow-node {
  min-height: 58px;
  margin-bottom: 0;
  font-size: 0.78rem;
}

.flow-input .flow-layer-title,
.flow-stakeholders .flow-layer-title {
  background: #eff6ff;
  border: 1px solid #cfe0f7;
}

.flow-governance .flow-layer-title {
  background: #fff7e8;
  border: 1px solid #ead8b8;
}

.flow-intelligence .flow-layer-title {
  background: #edf8ef;
  border: 1px solid #cddfcf;
}

.flow-orchestration .flow-layer-title {
  background: #eef5ff;
  border: 1px solid #cddcf5;
}

.flow-department .flow-layer-title {
  background: #fff1e8;
  border: 1px solid #efd0bd;
}

.flow-runtime .flow-layer-title,
.flow-tools .flow-layer-title {
  background: #f0f8eb;
  border: 1px solid #d5e5cc;
}

.flow-communication .flow-layer-title,
.flow-memory .flow-layer-title {
  background: #f4efff;
  border: 1px solid #dbd0ef;
}

.flow-learning .flow-layer-title {
  background: #fff0f6;
  border: 1px solid #efccdb;
}

.flow-infrastructure .flow-layer-title {
  background: #f2f5f8;
  border: 1px solid #d5dce3;
}

.architecture-grid section {
  padding: 12px;
  border: 1px solid #e2dfd5;
  border-radius: 10px;
  background: #fafaf7;
}

.architecture-grid h3 {
  margin: 0 0 10px;
  color: #26342d;
  font-size: 0.9rem;
}

.architecture-node {
  min-height: 44px;
  margin-bottom: 10px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font-size: 0.82rem;
  line-height: 1.4;
  outline: none;
  overflow-wrap: anywhere;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.architecture-node:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.input-node {
  width: min(520px, 100%);
  background: #edf6f0;
  border-color: #c9d8cf;
  text-align: center;
  font-weight: 700;
}

.step-node {
  border-color: #d8dfed;
  background: #f8faff;
}

.memory-node {
  border-color: #ded8ea;
  background: #fbf9ff;
}

.tool-node {
  border-color: #e6dccb;
  background: #fffbf4;
}

#custom-task-card {
  display: none;
}

.home-chat {
  /* Full canvas width so the inner `.chat-messages` scrollbar lands at the
     canvas's right edge (not at the inner 820px column boundary). The
     820px content column is enforced per-child (bubbles, composer,
     sticky question strip) via max-width + margin:auto below. */
  width: 100%;
  height: 100%;
  min-height: 0;
  margin: 0;
  padding: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-sizing: border-box;
}

.home-chat > .chat-active-question,
.home-chat > .chat-form {
  width: 100%;
  max-width: 820px;
  margin-left: auto;
  margin-right: auto;
}

.home-chat .chat-message {
  max-width: 820px;
  margin-left: auto;
  margin-right: auto;
}

.home-brand {
  margin-bottom: 18px;
  color: var(--text);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.chat-toolbar {
  display: grid;
  grid-template-columns: minmax(240px, 360px) 1fr;
  gap: 16px;
  align-items: end;
  padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0 24px;
  background: transparent;
}

/* Bubbles span the same column as the composer below — full width of the
   .home-chat container, no per-side max-width, no margin-auto. That keeps
   the user prompt, the assistant reply, and the input box all sharing the
   same left + right edge (matches ChatGPT / Claude layouts). */
.chat-message {
  width: 100%;
  margin-bottom: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  box-sizing: border-box;
}

.chat-message.user {
  background: var(--bg-muted);
  border-color: var(--border);
}

.message-role {
  margin-bottom: 4px;
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.chat-message.assistant .message-role {
  display: none;
}

.message-text {
  color: var(--text);
  font-size: 0.92rem;
  line-height: 1.55;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* Streaming cursor: small block-cursor character appended at the end of the
   assistant bubble while it's filling from the SSE stream. Blinks at a slow
   rate so it's clearly an "in progress" indicator, not part of the reply. */
.streaming-cursor {
  display: inline-block;
  width: 0.45em;
  margin-left: 1px;
  color: var(--text-subtle);
  animation: streaming-cursor-blink 1s steps(2, start) infinite;
  vertical-align: baseline;
  opacity: 0.6;
}

@keyframes streaming-cursor-blink {
  to { visibility: hidden; }
}

.markdown-body > :first-child {
  margin-top: 0;
}
.markdown-body > :last-child {
  margin-bottom: 0;
}
.markdown-body p {
  margin: 0 0 0.6em;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4 {
  margin: 1em 0 0.4em;
  font-weight: 600;
  line-height: 1.3;
}
.markdown-body h1 { font-size: 1.25rem; }
.markdown-body h2 { font-size: 1.12rem; }
.markdown-body h3 { font-size: 1rem; }
.markdown-body h4 { font-size: 0.95rem; }
.markdown-body ul,
.markdown-body ol {
  margin: 0 0 0.6em;
  padding-left: 1.5em;
}
.markdown-body li {
  margin-bottom: 0.25em;
}
.markdown-body li > p {
  margin: 0;
}
.markdown-body a {
  color: var(--accent, #2563eb);
  text-decoration: underline;
}
.markdown-body strong { font-weight: 600; }
.markdown-body em { font-style: italic; }
.markdown-body blockquote {
  margin: 0 0 0.6em;
  padding: 0.3em 0.9em;
  border-left: 3px solid var(--border);
  color: var(--text-subtle);
}
.markdown-body code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.86em;
  padding: 0.1em 0.35em;
  background: var(--bg-muted);
  border-radius: 4px;
}
.markdown-body pre {
  margin: 0 0 0.7em;
  padding: 0.7em 0.9em;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow-x: auto;
  font-size: 0.85rem;
  line-height: 1.45;
}
.markdown-body pre code {
  padding: 0;
  background: transparent;
  border-radius: 0;
  font-size: inherit;
}
.markdown-body table {
  border-collapse: collapse;
  margin: 0 0 0.7em;
  font-size: 0.88em;
}
.markdown-body th,
.markdown-body td {
  border: 1px solid var(--border);
  padding: 0.35em 0.6em;
  text-align: left;
}
.markdown-body th {
  background: var(--bg-muted);
  font-weight: 600;
}
.markdown-body hr {
  border: 0;
  border-top: 1px solid var(--border);
  margin: 1em 0;
}

.chat-form {
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-surface);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.chat-form:focus-within {
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.chat-form textarea {
  display: block;
  width: 100%;
  min-height: 76px;
  padding: 4px 2px 8px;
  border: 0;
  border-radius: 0;
  outline: none;
  resize: vertical;
  color: var(--text);
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.5;
  background: transparent;
}

/* ---- Modern chat composer (ChatGPT / Claude / Cursor style) ----
   Used by the main chat tab. The shared `.chat-form` above stays the
   default; this class tightens it into a single pill-shaped box with a
   single-line auto-growing textarea and a circular send button. */
.chat-form-compact {
  padding: 10px 12px;
  border-radius: 18px;
}

/* Top-right corner of the composer: persistent AI-generated disclaimer
   (left of the row, faint) and the active model · provider tag (right).
   Both stay visible at the same tiny size so the user always knows which
   model is answering and that the reply is AI-generated. */
/* Composer top row: agent (left) · AI-generated notice (center) · model (right),
   all the same size and on one line. Grid with equal side columns keeps the
   center notice truly centered regardless of the side widths. */
.composer-topbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 10px;
  margin: 0 2px 6px;
  min-height: 14px;
  font-size: 0.66rem;
  line-height: 1.2;
  color: var(--text-faint);
}

.composer-topbar > * {
  min-width: 0; /* allow the side cells to ellipsis instead of pushing center */
}

.composer-topbar > #chat-agent-label {
  justify-self: start;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.composer-topbar > #chat-ai-disclaimer {
  justify-self: center;
  text-align: center;
}

.composer-topbar > #chat-active-model {
  justify-self: end;
  text-align: right;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.composer-meta-notice {
  letter-spacing: 0.02em;
  font-style: italic;
  opacity: 0.75;
}

.chat-active-model {
  margin: 0;
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  color: var(--text-faint);
  line-height: 1.2;
  text-align: right;
}

.chat-active-model[hidden] {
  display: none;
}

.chat-form-compact textarea {
  min-height: 36px;
  max-height: 240px;     /* ~10 lines before scrolling */
  padding: 6px 4px;
  resize: none;          /* no visible drag handle — JS handles auto-grow */
  overflow-y: auto;
  /* Use modern field-sizing where supported so the textarea grows with
     content without JS. Falls back to the JS auto-grow we wire in chat.js. */
  field-sizing: content;
}

.composer-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  background: var(--bg-surface);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  flex: 0 0 auto;
}

.composer-icon-button:hover {
  background: var(--bg-hover);
  color: var(--text);
  border-color: var(--border-strong);
}

/* Send button — solid accent, white icon. Pushed to the far right via the
   image-preview's `flex: 1` (existing rule). */
.send-icon-button {
  margin-left: auto;
  background: var(--accent);
  color: var(--bg-surface);
  border-color: var(--accent);
}

.send-icon-button:hover:not(:disabled) {
  background: var(--brand-hover);
  border-color: var(--brand-hover);
}

.send-icon-button:disabled {
  background: var(--bg-muted);
  border-color: var(--border);
  color: var(--text-faint);
  cursor: not-allowed;
}

/* While a request is in-flight, drop the icon opacity slightly and disable
   pointer events. Same visual contract as the .ready / .needs-config classes
   updateSendButtonState already sets — both go through .disabled, so this
   only adds a busy-state visual without re-implementing the gating. */
.send-icon-button.is-sending {
  opacity: 0.6;
}

/* The legacy `.upload-control` square is overridden by `.composer-icon-button`
   geometry (border-radius: 50%) — re-state it here so the rule order matches
   the order in this file regardless of which class wins specificity-wise. */
.chat-form-compact .upload-control {
  width: 34px;
  height: 34px;
  font-size: 1rem;
  border-radius: 50%;
}

.chat-form textarea::placeholder {
  color: var(--text-faint);
}

.chat-form textarea:focus,
.chat-form textarea:focus-visible {
  border: 0;
  outline: none;
  box-shadow: none;
}

.composer-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 42px;
}

.upload-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  min-height: 36px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 1.2rem;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease;
}

.upload-control:hover {
  background: var(--bg-hover);
  color: var(--text);
}

#chat-image-input {
  display: none;
}

.image-preview {
  display: flex;
  flex: 1;
  gap: 6px;
  min-width: 0;
  overflow: hidden;
}

.image-preview span {
  max-width: 150px;
  padding: 4px 8px;
  border-radius: 6px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.74rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Per-message temporary knowledge (Stage D) — collapsible panel above the
   composer actions + a count badge on its toggle button. */
.chat-ref-panel {
  margin: 6px 0;
}

.chat-ref-panel textarea {
  width: 100%;
  resize: vertical;
  min-height: 2.6rem;
  padding: 6px 8px;
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 8px;
  background: var(--bg-input, #fff);
  font: inherit;
  font-size: 0.82rem;
}

#chat-ref-button {
  position: relative;
}

.composer-icon-button.has-attachment {
  color: var(--accent, #1d4ed8);
}

.chat-ref-count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--accent, #1d4ed8);
  color: #fff;
  font-size: 0.62rem;
  line-height: 15px;
  text-align: center;
}

.compact-model {
  width: 168px;
  min-height: 38px;
  padding: 8px 10px;
}

.hidden-model-select {
  display: none;
}

.model-picker {
  position: relative;
}

.model-icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  min-width: 40px;
  height: 36px;
  min-height: 36px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 0.74rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.model-icon-button:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.model-menu {
  position: absolute;
  right: 0;
  bottom: 46px;
  width: 240px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.12);
  z-index: 20;
}

.model-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 36px;
  margin: 0;
  padding: 7px 10px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
}

.model-menu-item:hover {
  background: var(--bg-hover);
}

.model-menu-item span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 24px;
  border-radius: 6px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.7rem;
  font-weight: 800;
}

/* Legacy #send-chat rules removed — the button now uses .composer-icon-button
   + .send-icon-button (circular, modern). Old .ready / .needs-config classes
   set by updateSendButtonState still work via the disabled attribute path. */
#send-chat {
  margin-top: 0;
}

.home-chat .model-status {
  display: none;
}

.settings-block {
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.settings-block button {
  width: 100%;
}

#api-key-status {
  margin: 8px 0 0;
  color: var(--warn);
  font-size: 0.78rem;
}

#api-key-status.ready {
  color: var(--success);
}

.reference-block {
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.reference-block h4 {
  margin: 0 0 10px;
  color: var(--text-muted);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.reference-block img {
  display: block;
  width: 100%;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
}

.reference-block p {
  margin: 10px 0 0;
  color: var(--text-subtle);
  font-size: 0.8rem;
  line-height: 1.5;
}

label {
  display: block;
  margin: 12px 0 6px;
  color: var(--text-muted);
  font-size: 0.78rem;
  font-weight: 500;
}

/* Non-textual input types are deliberately EXCLUDED — the appearance:none + full width
   below is for text inputs and selects only. Applied to a checkbox/radio it renders a
   bordered "dropdown button" with no tick; applied to color/file/range/image it stretches the
   control to 100% width (blowing compact swatches and icon cells out of their rows) and hides
   the native affordance. Those keep native rendering; compact styling is per component. */
input:not([type="checkbox"]):not([type="radio"]):where(:not([type="color"]):not([type="file"]):not([type="range"]):not([type="image"]):not([type="button"]):not([type="submit"]):not([type="reset"])),
textarea,
select,
/* A BUTTON that opens a chooser instead of a native list — App Admin's Columns filter. It is
   declared HERE so it shares the field geometry to the pixel; a control 8px out of step with the
   selects beside it is exactly the drift this block exists to stop. */
.aa-colsbtn {
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--border);
  padding: 8px 12px;
  font-family: inherit;
  font-size: 0.88rem;
  color: var(--text);
  /* background-COLOR (not the shorthand) so the <select> chevron background-image survives */
  background-color: var(--bg-surface);
  /* Reset native chrome in every engine (Safari otherwise draws its aqua select control and
     ignores border-radius/background; date fields would show a native icon). The dropdown
     chevron itself is SELECT-ONLY below — a text box must never look like a dropdown. */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  box-sizing: border-box;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
/* The "v" affordance belongs to dropdowns ONLY. appearance:none removed the native arrow,
   so supply a consistent SVG chevron (+ right padding for it) on single selects across
   Safari / Chrome / Firefox. Native multi-selects render as list boxes — no chevron. */
select:not([multiple]) {
  padding-right: 34px; /* room for the chevron */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}

/* THE FOCUS RING IS DRAWN INSIDE THE FIELD, and that is what stops it being cut off.

   2026-09-26, user report with a screenshot of a NAME box whose highlight was flat on both sides:
   *"in all the forms in all the modules, right side and left side fields are showing like they are
   getting cut when highlighting them"*.

   THE RING WAS `0 0 0 3px` — three pixels OUTSIDE the border box — and a module's content area is a
   SCROLLPORT. `overflow-y: auto` cannot leave the other axis `visible` (the spec resolves it to
   `auto`), so every one of those boxes clips horizontally, and the bodies that hold forms carry no
   side padding at all: `.kriya-main` and `.settings-main` are `padding: 20px 0`, measured at
   ZERO pixels of room beside the first and last column. The ring had nowhere to paint, so the
   fields in the outermost columns lost it on the outer side — every form, every module, which is
   exactly how it was reported.

   WHY NOT PAD THE SCROLLPORT. That is the obvious fix and it is the wrong one twice: those bodies
   are required NOT to pad their sides — `ui/pageEdges.spec.js` asserts `--page-pad-x: 0px` on each
   of them, because two gutters on one page is the "three edges" defect that spec exists to prevent
   — and there is a different scrollport per module, so it would be a fix applied N times and
   forgotten on the N+1th.

   INSIDE IS ALSO THE HOUSE PATTERN, not a workaround: the design system's own focus floor is
   `outline: 2px solid var(--primary); outline-offset: -2px` — negative, i.e. within the box — for
   this very reason. One rule here, and no container anywhere can clip it.

   `inset` KEEPS THE BORDER VISIBLE. The ring sits between the 1px border and the text, over the
   field's own padding, so nothing overlaps the value being typed. */
input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: inset 0 0 0 2px var(--focus-ring);
}

input::placeholder,
textarea::placeholder {
  color: var(--text-faint);
}

/* Inline filter select (e.g. the activity feed's module picker) — keeps the global
   token styling but auto-width so it sits beside a heading instead of stretching full. */
.activity-filter-select {
  width: auto;
  max-width: 14rem;
  margin-left: auto;
  font-size: 0.85rem;
  padding: 5px 10px;
}

/* Buttons — a clean, safe reset for EVERY button (no UA chrome, no surprise
   margin/background/colour; typography is kept). The opinionated "primary" fill
   is scoped to BARE, class-less buttons only, so a styled (classed) button never
   inherits a surprise background, margin or hover. Style buttons via a class
   (.primary-button, .ghost-button, …). This is what stops the global look
   bleeding into icon / flex buttons such as the header-search controls. */
button {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
}

button:not([class]) {
  margin-top: 12px;
  padding: 8px 14px;
  border-radius: 8px;
  background: var(--accent);
  color: var(--bg-surface);
  transition: background 0.15s ease, opacity 0.15s ease;
}

button:not([class]):hover {
  background: var(--brand-hover);
}

.model-status {
  margin-top: 8px;
  font-size: 0.78rem;
  color: var(--text-subtle);
}

.model-status .error,
.model-status.blocked {
  color: var(--warn);
}

.analytics-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-bottom: 16px;
}

.analytics-card,
.layer-card {
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
}

.analytics-card strong,
.layer-card strong {
  display: block;
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 4px;
}

.analytics-card > div,
.layer-card > div {
  font-size: 1.1rem;
  font-weight: 600;
  color: var(--text);
}

.layer-card {
  margin-bottom: 10px;
}

.task-row {
  display: grid;
  grid-template-columns: 100px 140px 100px 100px 1fr 140px 140px;
  grid-gap: 10px;
  margin-bottom: 6px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.82rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.task-row:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.task-header {
  background: var(--bg-muted);
  border-color: var(--border);
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: default;
  pointer-events: none;
}

.agent-row {
  display: grid;
  grid-template-columns: 160px 120px 1fr;
  grid-gap: 10px;
  margin-bottom: 6px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.85rem;
  color: var(--text);
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.agent-row:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.agent-header {
  background: var(--bg-muted);
  border-color: var(--border);
  color: var(--text-subtle);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: default;
  pointer-events: none;
}

.list-item {
  margin: 6px 0;
  padding: 8px 12px;
  border: 1px solid var(--border-muted);
  border-radius: 6px;
  background: var(--bg-surface);
  font-size: 0.85rem;
  color: var(--text);
}

pre {
  background: var(--bg-muted);
  border: 1px solid var(--border-muted);
  padding: 12px;
  border-radius: 8px;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--text);
}

.error {
  color: var(--danger);
}

.success {
  color: var(--success);
}

@media (max-width: 1200px) {
  .properties {
    width: 250px;
  }
}

@media (max-width: 900px) {
  .sidebar {
    width: 200px;
  }
  .properties {
    width: 200px;
  }
  .chat-toolbar {
    grid-template-columns: 1fr;
  }
}

.services-list,
.saved-tasks-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.service-row {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 14px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  transition: border-color 0.15s ease;
}

.service-row:hover {
  border-color: var(--border-strong);
}

.service-icon {
  width: 40px;
  height: 40px;
  border-radius: 8px;
  background: var(--bg-muted);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
}

.service-meta strong {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
}

.service-meta p {
  margin: 3px 0 8px;
  color: var(--text-subtle);
  font-size: 0.84rem;
  line-height: 1.5;
}

.service-tags {
  display: flex;
  gap: 6px;
}

.tag {
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.tag-muted {
  color: var(--text-faint);
}

.saved-task-row {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  transition: border-color 0.15s ease;
}

.saved-task-row:hover {
  border-color: var(--border-strong);
}

.saved-task-step {
  font-size: 0.88rem;
  color: var(--text);
}

.saved-task-row small {
  display: block;
  margin-top: 6px;
  color: var(--text-faint);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Auth overlay + governance UI */

.auth-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(15, 23, 42, 0.48);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}

.auth-overlay[hidden] {
  display: none;
}

.auth-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  padding: 22px 24px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.24);
}

.auth-close {
  position: absolute;
  top: 12px;
  right: 14px;
  padding: 0;
  border: none;
  background: none;
  color: var(--text-muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.45;
  transition: opacity 0.15s ease;
}

.auth-close:hover {
  opacity: 0.9;
}

.auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--text);
  text-transform: uppercase;
  margin-bottom: 8px;
}

.auth-brand .brand-logo {
  width: auto;
  height: 30px;
}

/* ── Public front door (long-form marketing home) ───────────────────────────
   Slate-dark landing shown to signed-out visitors at the root path. Sits over
   the app shell like the auth overlay and hides the moment a CTA opens the
   sign-in card. Long n8n-style layout (sticky nav → hero → sections → footer)
   on the brand slate palette, so it reads as SMAIVIZ regardless of the app's
   light chrome. */
/* Wordmark accent — the "AI" in SM·AI·VIZ picks up the prism logo's
   violet→orange gradient. Solid-violet fallback if text-clipping is unsupported.
   Global (used on the landing nav/footer/auth card AND the in-app sidebar). */
.brand-ai {
  /* The "AI" letters render in the SAME colour as the rest of the wordmark,
     in every context (sidebar, auth, landing nav + footer) — no separate accent. */
  color: inherit;
  background: none;
  -webkit-text-fill-color: currentColor;
}

.home-landing {
  position: fixed;
  inset: 0;
  z-index: 95;
  overflow-y: auto;
  /* The marketing home is a self-contained LIGHT surface: every text colour,
     card and border below is tuned for a soft, cool light background. Pin the
     background and color-scheme to fixed light values so it never inherits the
     app theme — otherwise it would invert under [data-theme='dark'] (e.g. Safari
     with the OS in dark mode) and wash out. */
  /* Brand palette — a trusted steel-blue primary with a warm gold accent
     (blue + gold: optimistic + human, matches the logo + sets us apart from the
     cold indigo/violet AI-app crowd). Scoped to the landing; mirrors the app theme. */
  --home-primary: #1d4ed8;          /* steel-blue — primary actions */
  --home-primary-hover: #1e40af;    /* deep navy */
  --home-primary-soft: rgba(37, 99, 235, 0.10);
  --home-accent: #0e7490;           /* intense teal — secondary accent text (AA on light) */
  --home-border: rgba(37, 99, 235, 0.12);
  background: #f5f8f7;
  color: #45565a;
  color-scheme: light;
  scroll-behavior: smooth;
}

.home-landing[hidden] {
  display: none;
}

/* Pre-paint front-door guard (class set by the inline <head> script): for a
   likely signed-out visitor at "/", show the landing and hide the app shell
   immediately so the console never flashes before initAuth mounts the landing.
   initAuth removes .pre-landing once auth state is known. */
html.pre-landing .app-layout {
  display: none !important;
}
/* Embed/mirror mode (?embed=1): the SPA runs inside a same-origin Projects "mirror" frame. Hide the
   app chrome — first-rail sidebar · topbar (breadcrumb/search/user) · footer · chat dock — so only the
   framed module's own body (and its second rail, for switching views) shows. Set by main.js. */
html.embed-mode .sidebar,
html.embed-mode .topbar,
html.embed-mode #app-footer,
html.embed-mode #chat-tab,
html.embed-mode #chat-dock-launcher { display: none !important; }
html.embed-mode .app-layout { grid-template-columns: 1fr; grid-template-areas: "canvas"; }
html.embed-mode .canvas { padding: 0 !important; }
/* Mirror shows ONLY the module's BODY — hide its own second rail too and reclaim the space (so the
   framed page is just e.g. Kriya's tasks board, not the whole Kriya page). */
html.embed-mode .page-left-rail,
html.embed-mode .workspace-rail { display: none !important; }
html.embed-mode .canvas { --rail-w: 0px !important; }
html.embed-mode .canvas-body,
html.embed-mode .canvas-body > .tab-content.active { margin-left: 0 !important; }

html.pre-landing #home-landing[hidden] {
  display: block;
}

/* Sticky nav */
.home-nav {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 14px 28px;
  background: rgba(245, 248, 247, 0.85);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(15, 23, 42, 0.07);
}

.home-nav-brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #0f172a;
  text-decoration: none;
}

.home-nav-brand .brand-logo {
  height: 1.55em;
  width: auto;
}

.home-nav-links {
  display: flex;
  gap: 24px;
  flex: 1;
  margin-left: 12px;
}

.home-nav-links a {
  color: #475569;
  text-decoration: none;
  font-size: 0.9rem;
  font-weight: 500;
}

.home-nav-links a:hover {
  color: #0f172a;
}

.home-nav-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}

.home-nav-signin {
  padding: 8px 16px;
  background: none;
  border: 1px solid var(--home-primary);
  border-radius: var(--radius-pill);
  color: var(--home-primary);
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.home-nav-signin:hover {
  background: var(--home-primary-soft);
  color: var(--home-primary-hover);
}

.home-nav-signin:focus-visible,
.home-cta:focus-visible,
.home-card-cta:focus-visible {
  outline: 2px solid var(--home-primary);
  outline-offset: 2px;
}

/* Brand wordmark (footer) */
.home-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #0f172a;
}

.home-brand .brand-logo {
  width: auto;
  height: 1.5em;
}

/* Buttons */
.home-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 132px;
  padding: 12px 24px;
  border: 1px solid var(--home-primary);
  border-radius: var(--radius-pill);
  background: #ffffff;
  color: var(--home-primary);
  font-size: 0.95rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.home-cta:hover {
  background: var(--home-primary-soft);
  border-color: var(--home-primary-hover);
  color: var(--home-primary-hover);
  transform: translateY(-1px);
}

.home-cta--primary {
  background: var(--home-primary);
  border-color: var(--home-primary);
  color: #fff;
}

.home-cta--primary:hover {
  background: var(--home-primary-hover);
  border-color: var(--home-primary-hover);
  color: #fff;
}

.home-cta--ghost {
  background: transparent;
}

.home-cta--sm {
  min-width: 0;
  padding: 8px 16px;
  font-size: 0.85rem;
}

/* Hero */
.home-hero {
  max-width: 1120px;
  margin: 0 auto;
  padding: 72px 24px 56px;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  gap: 48px;
  align-items: center;
  background:
    radial-gradient(720px 380px at 12% -8%, rgba(13, 148, 136, 0.10), transparent 60%),
    radial-gradient(680px 360px at 95% 0%, rgba(37, 99, 235, 0.10), transparent 62%);
}

.home-eyebrow {
  display: inline-block;
  margin-bottom: 14px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--home-accent);
}

.home-hero-title {
  margin: 0 0 18px;
  font-size: clamp(1.9rem, 4.4vw, 2.8rem);
  line-height: 1.1;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: #0f172a;
}

.home-hero-sub {
  margin: 0 0 26px;
  max-width: 52ch;
  font-size: 1.08rem;
  line-height: 1.75;
  color: #475569;
}

.home-hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

/* Trust line under the hero CTAs (free trial / no card) + the sales prompt in
   the pricing section — both quietly reassure business buyers. */
.home-hero-note {
  margin: 16px 0 0;
  font-size: 0.85rem;
  color: #64748b;
}
.home-sales-line {
  margin: 26px auto 0;
  max-width: 760px;
  font-size: 0.95rem;
  color: #475569;
}
.home-sales-line a {
  color: var(--brand, #2563eb);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.home-sales-line a:hover { text-decoration: underline; }

.home-hero-link {
  display: inline-block;
  margin-top: 18px;
  color: #334155;
  text-decoration: none;
  font-size: 0.92rem;
}

.home-hero-link:hover {
  color: #0f172a;
}

/* Hero flow visual (original SMAIVIZ pipeline, not an n8n asset) */
.home-hero-visual {
  display: flex;
  justify-content: center;
}

.home-flow {
  width: 100%;
  max-width: 360px;
}

.home-flow-node {
  position: relative;
  margin: 0 0 26px;
  padding: 14px 16px;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.10);
  border-radius: 12px;
  font-size: 0.9rem;
  color: #334155;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.06);
}

.home-flow-node:last-child {
  margin-bottom: 0;
}

.home-flow-node::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -26px;
  width: 2px;
  height: 26px;
  background: linear-gradient(rgba(100, 116, 139, 0.45), rgba(100, 116, 139, 0.06));
  transform: translateX(-50%);
}

.home-flow-node:last-child::after {
  display: none;
}

.home-flow-kicker {
  display: block;
  margin-bottom: 4px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--home-primary);
}

.home-flow-node--agent {
  border-color: rgba(13, 148, 136, 0.45);
}

.home-flow-node--gate {
  border-color: rgba(217, 119, 6, 0.4);
}

.home-flow-node--gate .home-flow-kicker {
  color: #d97706;
}

.home-flow-node--output {
  border-color: rgba(5, 150, 105, 0.4);
}

.home-flow-node--output .home-flow-kicker {
  color: #059669;
}

/* Hover: the box you point at highlights — a lift, a stronger shadow and the node's own accent ring
   (Goal/Plan → brand blue, Agent → teal, Approval → amber, Output → green). */
.home-flow-node {
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}
.home-flow-node:hover {
  transform: translateY(-3px);
  border-color: var(--home-primary, #1d4ed8);
  box-shadow: 0 14px 32px rgba(29, 78, 216, 0.20);
}
.home-flow-node--agent:hover { border-color: #0d9488; box-shadow: 0 14px 32px rgba(13, 148, 136, 0.26); }
.home-flow-node--gate:hover { border-color: #d97706; box-shadow: 0 14px 32px rgba(217, 119, 6, 0.26); }
.home-flow-node--output:hover { border-color: #059669; box-shadow: 0 14px 32px rgba(5, 150, 105, 0.26); }

/* Sections */
.home-section {
  max-width: 1080px;
  margin: 0 auto;
  padding: 72px 24px;
}

.home-section--feature {
  margin: 24px auto;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.06);
  border-radius: 20px;
}

.home-section-title {
  margin: 0 0 10px;
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #0f172a;
  text-align: center;
}

.home-section-sub {
  margin: 0 auto 36px;
  max-width: 60ch;
  font-size: 1.02rem;
  line-height: 1.55;
  color: #475569;
  text-align: center;
}

/* Card / feature grids */
.home-cards {
  display: grid;
  gap: 18px;
}

.home-cards--3 {
  grid-template-columns: repeat(3, 1fr);
}

.home-cards--4 {
  grid-template-columns: repeat(4, 1fr);
}

.home-cards--6 {
  grid-template-columns: repeat(3, 1fr);
}

.home-card,
.home-feature {
  padding: 22px;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Module cards stack their content so the Explore CTA can sit at the bottom,
   keeping the buttons aligned across cards of different text lengths. */
.home-card {
  display: flex;
  flex-direction: column;
}

.home-card-cta {
  margin-top: auto;
  padding-top: 14px;
  align-self: flex-start;
  background: none;
  border: 0;
  color: var(--home-primary);
  font: inherit;
  font-weight: 600;
  font-size: 0.9rem;
  cursor: pointer;
}

.home-card-cta:hover {
  text-decoration: underline;
}

/* Colourful, catchy module cards — a distinct accent per card from a curated,
   harmonious set (no indigo/violet). Cards stay white + clean so it reads
   business-friendly, with the colour as a top bar + matching Explore link. */
.home-cards--6 .home-card {
  border-top: 3px solid var(--home-primary);
}
.home-cards--6 .home-card:nth-child(1) { border-top-color: #1d4ed8; } /* cobalt */
.home-cards--6 .home-card:nth-child(2) { border-top-color: #d97706; } /* amber */
.home-cards--6 .home-card:nth-child(3) { border-top-color: #e2674a; } /* coral */
.home-cards--6 .home-card:nth-child(4) { border-top-color: #db2777; } /* marketing — rose */
.home-cards--6 .home-card:nth-child(5) { border-top-color: #0284c7; } /* sky */
.home-cards--6 .home-card:nth-child(6) { border-top-color: #15803d; } /* green */
.home-cards--6 .home-card:nth-child(1) .home-card-cta { color: #1d4ed8; }
.home-cards--6 .home-card:nth-child(2) .home-card-cta { color: #b45309; }
.home-cards--6 .home-card:nth-child(3) .home-card-cta { color: #c2410c; }
.home-cards--6 .home-card:nth-child(4) .home-card-cta { color: #be185d; }
.home-cards--6 .home-card:nth-child(5) .home-card-cta { color: #0369a1; }
.home-cards--6 .home-card:nth-child(6) .home-card-cta { color: #15803d; }

/* ── Three pillars (Reports · Reminders · Reproduce) ─────────────────────────
   Sits inside .home-section--feature (white card on the light landing); tokens
   mirror .home-card (cool-slate borders, r16, #0f172a/#475569 text) with a
   distinct accent per pillar from the curated home palette. */
.home-pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  align-items: stretch;
}
.home-pillar {
  --pillar-accent: var(--home-primary);
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 26px 24px 24px;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-top: 4px solid var(--pillar-accent);
  border-radius: 16px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
  overflow: hidden;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.home-pillar:hover {
  border-color: color-mix(in srgb, var(--pillar-accent) 45%, rgba(15, 23, 42, 0.16));
  box-shadow: 0 10px 26px rgba(15, 23, 42, 0.09);
  transform: translateY(-2px);
}
.home-pillar-mark {
  position: absolute;
  top: 2px;
  right: 14px;
  font-size: 5.4rem;
  font-weight: 800;
  line-height: 1;
  color: var(--pillar-accent);
  opacity: 0.1;
  pointer-events: none;
}
.home-pillar-tag {
  display: inline-block;
  width: fit-content;
  padding: 3px 9px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--pillar-accent);
  background: color-mix(in srgb, var(--pillar-accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--pillar-accent) 24%, transparent);
  border-radius: 999px;
}
.home-pillar h3 {
  margin: 12px 0 6px;
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #0f172a;
}
.home-pillar-line {
  margin: 0 0 16px;
  font-size: 0.98rem;
  line-height: 1.5;
  font-weight: 600;
  color: #334155;
}
.home-pillar-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}
.home-pillar-list li {
  position: relative;
  padding-left: 24px;
  font-size: 0.9rem;
  line-height: 1.5;
  color: #475569;
}
.home-pillar-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--pillar-accent) 18%, transparent);
  box-shadow: inset 0 0 0 3px var(--pillar-accent);
}
.home-pillar--reports   { --pillar-accent: #1d4ed8; }  /* cobalt */
.home-pillar--reminders { --pillar-accent: #0e7490; }  /* teal */
.home-pillar--reproduce { --pillar-accent: #15803d; }  /* green */
.home-pillars-cta {
  justify-content: center;
  margin-top: 32px;
}
@media (max-width: 860px) {
  .home-pillars { grid-template-columns: 1fr; }
  .home-pillar-mark { font-size: 4.6rem; }
}

/* ── Grouped module blocks — compact, scannable rows under a group label ──── */
.home-modgroup { margin-top: 34px; }
.home-modgroup:first-of-type { margin-top: 0; }
.home-modgroup-label {
  display: block;
  margin: 0 0 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(15, 23, 42, 0.08);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--home-primary);
  text-align: left;
}
.home-modgroup .home-feature { padding: 16px 18px; }
.home-modgroup .home-feature h3 { margin: 0 0 6px; font-size: 0.98rem; }
.home-modgroup-note {
  margin: 40px auto 0;
  max-width: 68ch;
  font-weight: 600;
  color: #334155;
}

.home-cards--compact .home-feature {
  padding: 18px;
}

.home-card:hover,
.home-feature:hover {
  border-color: rgba(15, 23, 42, 0.16);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.07);
}

/* Pricing cards: highlight the block you point at (border colour !important beats the inline border).
   Flex column + margin-top:auto on the CTA pushes every button to the card's bottom edge, so all
   three buttons line up at the same level (the grid already equal-heights the cards). */
.home-price-card {
  transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
  display: flex;
  flex-direction: column;
}
.home-price-card > .home-cta {
  margin-top: auto;
  align-self: flex-start;
}
.home-price-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 16px 36px rgba(15, 23, 42, 0.14);
  border-color: var(--brand, #2563eb) !important;
}

/* Module cards: hovering a card HIGHLIGHTS it (accent ring) and EXPANDS a detailed panel anchored
   just below it — a popover, so the grid never reflows. Card + panel read as one connected box. */
.home-feature { position: relative; }
.home-modgroup .home-feature:hover {
  border-color: var(--home-primary, #1d4ed8);
  box-shadow: 0 10px 26px rgba(29, 78, 216, 0.16);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
  z-index: 30;
}
.home-feature-more {
  position: absolute;
  left: -1px;
  right: -1px;
  top: 100%;
  z-index: 30;
  padding: 12px 18px 16px;
  background: #ffffff;
  border: 1px solid var(--home-primary, #1d4ed8);
  border-top: 0;
  border-radius: 0 0 14px 14px;
  box-shadow: 0 18px 34px rgba(15, 23, 42, 0.16);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
  text-align: left;
}
.home-feature:hover .home-feature-more {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
.home-feature-more p { margin: 0 0 6px; font-size: 0.85rem; line-height: 1.5; color: #475569; }
.home-feature-more ul { margin: 0; padding-left: 18px; }
.home-feature-more li { margin: 3px 0; font-size: 0.83rem; line-height: 1.45; color: #475569; }
/* On coarse-pointer / small screens there's no hover — show the detail inline instead of a popover. */
@media (hover: none), (max-width: 720px) {
  .home-feature-more { position: static; opacity: 1; visibility: visible; transform: none;
    border: 0; box-shadow: none; padding: 8px 0 0; }
}

.home-card h3,
.home-feature h3 {
  margin: 10px 0 8px;
  font-size: 1.05rem;
  color: #0f172a;
}

.home-card p,
.home-feature p {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.5;
  color: #475569;
}

.home-card-tag {
  display: inline-block;
  padding: 3px 9px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--home-primary);
  background: var(--home-primary-soft);
  border: 1px solid rgba(37, 99, 235, 0.22);
  border-radius: 999px;
}

/* Split (text + code) */
.home-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 40px;
  align-items: center;
}

.home-split-text .home-section-title,
.home-split-text .home-section-sub {
  margin-left: 0;
  text-align: left;
}

.home-bullets {
  list-style: none;
  margin: 18px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.home-bullets li {
  position: relative;
  padding-left: 26px;
  font-size: 0.95rem;
  line-height: 1.5;
  color: #334155;
}

.home-bullets li::before {
  content: '✓';
  position: absolute;
  left: 0;
  top: 0;
  color: #059669;
  font-weight: 700;
}

.home-code {
  margin: 0;
  padding: 20px;
  background: #f1f5f9;
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.84rem;
  line-height: 1.6;
  color: #1e293b;
  overflow-x: auto;
  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.05);
}

.home-code-c { color: #64748b; }
.home-code-k { color: #1d4ed8; }
.home-code-f { color: #0369a1; }
.home-code-n { color: #b45309; }

.home-enterprise-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 32px;
}

/* "How the modules work together" — numbered step flow */
.home-steps {
  list-style: none;
  margin: 0 0 28px;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}

.home-step {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 18px;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.08);
  border-radius: 14px;
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
}

.home-step-num {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  font-weight: 700;
  color: #fff;
  background: var(--home-primary);
}

.home-step-tag {
  display: block;
  margin-bottom: 3px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--home-primary);
}

.home-step p {
  margin: 0;
  font-size: 0.92rem;
  line-height: 1.5;
  color: #475569;
}

.home-together-note {
  max-width: 60ch;
  margin: 0 auto;
  text-align: center;
  font-size: 1.02rem;
  font-weight: 500;
  color: #334155;
}

/* Final CTA */
.home-finalcta {
  max-width: 1080px;
  margin: 24px auto;
  padding: 72px 24px 80px;
  text-align: center;
  background: linear-gradient(135deg, rgba(37, 99, 235, 0.10), rgba(14, 116, 144, 0.10));
  border: 1px solid var(--home-border);
  border-radius: 20px;
}

.home-finalcta h2 {
  margin: 0 0 12px;
  font-size: clamp(1.8rem, 4vw, 2.6rem);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #0f172a;
}

.home-finalcta p {
  margin: 0 0 28px;
  font-size: 1.05rem;
  color: #475569;
}

.home-finalcta .home-hero-cta {
  justify-content: center;
}

/* Footer */
.home-footer {
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  background: #0f172a;
}

.home-footer-top {
  max-width: 1080px;
  margin: 0 auto;
  padding: 48px 24px 32px;
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 32px;
}

.home-footer-brand .home-brand {
  margin-bottom: 14px;
  letter-spacing: 0; /* tighten the wordmark — no gaps between SMAIVIZ letters */
  color: #ffffff; /* bright wordmark on the dark footer */
  font-size: 1.5rem; /* much larger than the default 0.82rem; the logo (height:1.5em) scales with it */
  gap: 12px;
}

.home-footer-tagline {
  margin: 0;
  max-width: 30ch;
  font-size: 0.76rem;
  line-height: 1.45;
  color: #cbd5e1;
}

.home-footer-col {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.home-footer-col h4 {
  margin: 0 0 4px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #f1f5f9;
}

.home-footer-col a,
.home-footer-link {
  font: inherit;
  font-size: 0.9rem;
  color: #e2e8f0;
  text-decoration: none;
  text-align: left;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

.home-footer-col a:hover,
.home-footer-link:hover {
  color: #f8fafc;
}

/* Contact-sales line: spans the Product + Resources columns, sitting at the
   bottom of the footer columns (full width once the grid collapses on mobile). */
.home-footer-sales {
  grid-column: 2 / 4;
  align-self: end;
  font-size: 0.9rem;
  color: #cbd5e1;
}

.home-footer-sales a {
  color: inherit;
  text-decoration: none;
}

.home-footer-sales a:hover {
  text-decoration: underline;
}

/* Terms · Privacy · Cookies — a compact horizontal row of small, lightly-visible links sitting
   just above the Contact Sales line. */
.home-footer-legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 24px;
  margin-bottom: 10px;
  font-size: 0.72rem;
  line-height: 1;
}
.home-footer-legal a {
  color: #cbd5e1;
  opacity: 0.5;
  text-decoration: none;
}
.home-footer-legal a:hover {
  opacity: 0.9;
  text-decoration: underline;
}

.home-footer-bottom {
  max-width: 1080px;
  margin: 0 auto;
  padding: 18px 24px 28px;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  /* 1fr auto 1fr → copyright at the left, the legal links centered on the page, status at the right
     (the centre column stays centred even when the status pill is hidden). */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  font-size: 0.82rem;
  color: #cbd5e1;
}
.home-footer-copy { justify-self: start; }
.home-footer-bottom .home-footer-legal { justify-self: center; margin: 0; }
.home-footer-bottom .home-status { justify-self: end; }
@media (max-width: 560px) {
  .home-footer-bottom { grid-template-columns: 1fr; justify-items: center; text-align: center; gap: 10px; }
  .home-footer-copy,
  .home-footer-bottom .home-footer-legal,
  .home-footer-bottom .home-status { justify-self: center; }
}

/* Status pill (footer) */
.home-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8rem;
  color: #475569;
}

.home-status[hidden] {
  display: none;
}

.home-status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #10b981;
  box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.18);
  animation: home-status-pulse 2.4s ease-in-out infinite;
}

.home-status--degraded .home-status-dot {
  background: #f59e0b;
  box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.18);
}

.home-status--down .home-status-dot {
  background: #ef4444;
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18);
}

.home-status-link {
  margin-left: 4px;
  color: #e2e8f0;
  text-decoration: underline;
}

.home-status-link:hover {
  color: #f8fafc;
}

@keyframes home-status-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* Responsive */
@media (max-width: 860px) {
  .home-hero {
    grid-template-columns: 1fr;
    padding-top: 48px;
  }
  .home-split {
    grid-template-columns: 1fr;
  }
  .home-cards--3,
  .home-cards--4,
  .home-cards--6 {
    grid-template-columns: repeat(2, 1fr);
  }
  .home-footer-top {
    grid-template-columns: 1fr 1fr;
  }
  .home-footer-sales {
    grid-column: 1 / -1;
  }
  .home-nav-links {
    display: none;
  }
}

@media (max-width: 520px) {
  .home-cards--3,
  .home-cards--4,
  .home-cards--6 {
    grid-template-columns: 1fr;
  }
  .home-footer-top {
    grid-template-columns: 1fr;
  }
  .home-cta {
    flex: 1 1 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .home-landing {
    scroll-behavior: auto;
  }
  .home-status-dot {
    animation: none;
  }
}

/* ── Public docs assistant (home "Chat") ────────────────────────────────────
   A small Q&A modal opened by the landing's Chat CTA. No AI, no backend — it
   answers from the in-page FAQ/Overview content (controllers/homeChat.js).
   NB: uses a `docs-chat` prefix, NOT `home-chat` — the app's own chat shell
   already uses `.home-chat` (see line ~1466), so the names must not collide. */
/* Floating launcher icon (bottom-right). Lives inside #home-landing, so it is
   only present for signed-out visitors. */
.docs-chat-launcher {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 120;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--primary);
  color: var(--primary-ink, #fff);
  box-shadow: 0 10px 28px rgba(15, 23, 42, 0.5);
  cursor: pointer;
  transition: transform 0.12s ease, background 0.15s ease;
}

.docs-chat-launcher:hover {
  background: var(--primary-hover);
  transform: translateY(-2px);
}

.docs-chat-launcher[hidden] {
  display: none;
}

/* LinkedIn-style docked panel (bottom-right). No backdrop — the page behind
   stays fully visible and interactive. */
.docs-chat {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 121;
  display: flex;
  flex-direction: column;
  width: 360px;
  max-width: calc(100vw - 32px);
  height: 520px;
  max-height: calc(100vh - 110px);
  background: #0f172a;
  border: 1px solid rgba(226, 232, 240, 0.14);
  border-radius: 14px;
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.5);
  overflow: hidden;
}

.docs-chat[hidden] {
  display: none;
}

.docs-chat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid rgba(226, 232, 240, 0.08);
}

.docs-chat-head h2 {
  margin: 0;
  font-size: 1.05rem;
  color: #f8fafc;
}

.docs-chat-close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  margin: 0; /* cancel the global button{margin-top:12px} leak */
  padding: 0;
  background: rgba(226, 232, 240, 0.08);
  border: 1px solid rgba(226, 232, 240, 0.16);
  border-radius: 8px;
  color: #cbd5e1;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.docs-chat-close:hover {
  background: rgba(226, 232, 240, 0.16);
  color: #fff;
}

.docs-chat-log {
  flex: 1;
  overflow-y: auto;
  padding: 18px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.docs-chat-msg {
  display: flex;
}

.docs-chat-msg--user {
  justify-content: flex-end;
}

.docs-chat-bubble {
  max-width: 84%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 0.9rem;
  line-height: 1.5;
}

.docs-chat-msg--bot .docs-chat-bubble {
  background: rgba(30, 41, 59, 0.8);
  border: 1px solid rgba(226, 232, 240, 0.10);
  color: #e2e8f0;
  border-bottom-left-radius: 4px;
}

.docs-chat-msg--user .docs-chat-bubble {
  background: var(--primary);
  color: var(--primary-ink, #fff);
  border-bottom-right-radius: 4px;
}

.docs-chat-mail {
  color: #a5b4fc;
  text-decoration: underline;
}

.docs-chat-mail:hover {
  color: #fff;
}

/* Collapsible "Suggested questions" disclosure — collapsed by default so the
   chips don't occupy the chat area; expands on click. */
.docs-chat-suggest {
  border-top: 1px solid rgba(226, 232, 240, 0.08);
}

.docs-chat-suggest-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 20px;
  list-style: none;
  font-size: 0.8rem;
  color: #94a3b8;
  cursor: pointer;
  user-select: none;
}

.docs-chat-suggest-toggle::-webkit-details-marker {
  display: none;
}

.docs-chat-suggest-toggle::before {
  content: '▸';
  font-size: 0.7rem;
  transition: transform 0.15s ease;
}

.docs-chat-suggest[open] .docs-chat-suggest-toggle::before {
  transform: rotate(90deg);
}

.docs-chat-suggest-toggle:hover {
  color: #e2e8f0;
}

.docs-chat-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 0 20px 12px;
}

.docs-chat-chip {
  margin: 0; /* cancel the global button{margin-top:12px} leak */
  padding: 6px 12px;
  font-size: 0.8rem;
  color: #cbd5e1;
  background: rgba(226, 232, 240, 0.06);
  border: 1px solid rgba(226, 232, 240, 0.16);
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.docs-chat-chip:hover {
  background: rgba(226, 232, 240, 0.14);
  color: #fff;
}

.docs-chat-form {
  display: flex;
  gap: 10px;
  padding: 14px 20px 18px;
  border-top: 1px solid rgba(226, 232, 240, 0.08);
}

.docs-chat-input {
  flex: 1;
  padding: 10px 14px;
  font: inherit;
  font-size: 0.9rem;
  color: #e2e8f0;
  background: rgba(15, 23, 42, 0.6);
  border: 1px solid rgba(226, 232, 240, 0.18);
  border-radius: 10px;
}

.docs-chat-input::placeholder {
  color: var(--text-subtle);
}

.docs-chat-input:focus {
  outline: none;
  border-color: rgba(129, 140, 248, 0.6);
}

/* Circular icon Send button. flex:none keeps it square; margin:0 cancels the
   global button{margin-top:12px} leak so it aligns with the input. */
.docs-chat-send {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: var(--primary);
  color: var(--primary-ink, #fff);
  cursor: pointer;
  transition: background 0.15s ease;
}

.docs-chat-send:hover {
  background: var(--primary-hover);
}

/* ── Cookie consent — banner + preference center ────────────────────────────
   App-wide first-party consent UI (controllers/cookies.js). Slate-dark so it
   reads consistently over both the light app and the dark landing. Sits above
   app chrome (banner 800 / prefs 900); the cookie-policy modal (.doc-modal,
   1000) opens above both. */
.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 800;
  /* One step lighter than the home page (#0f172a) so the bar stands apart and
     reads as an action the visitor needs to take. */
  background: #1e293b;
  border-top: 1px solid rgba(226, 232, 240, 0.18);
  box-shadow: 0 -8px 30px rgba(0, 0, 0, 0.4);
  color: #e2e8f0;
}

.cookie-banner[hidden] {
  display: none;
}

.cookie-banner-inner {
  max-width: 1080px;
  margin: 0 auto;
  padding: 16px 24px;
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}

.cookie-banner-text {
  flex: 1 1 320px;
  margin: 0;
  font-size: 0.78rem;
  line-height: 1.45;
  color: #cbd5e1;
}

.cookie-banner-actions {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.cookie-link {
  color: #a5b4fc;
  text-decoration: underline;
}

.cookie-link:hover {
  color: #fff;
}

/* Buttons — Accept all and Reject non-essential carry equal visual weight. */
.cookie-btn {
  margin: 0;
  padding: 8px 14px;
  border: 1px solid transparent;
  border-radius: 8px;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.cookie-btn--primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-ink, #fff);
}

.cookie-btn--primary:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.cookie-btn--outline {
  background: transparent;
  border-color: rgba(226, 232, 240, 0.4);
  color: #e2e8f0;
}

.cookie-btn--outline:hover {
  background: rgba(226, 232, 240, 0.1);
}

.cookie-btn--ghost {
  background: transparent;
  color: #cbd5e1;
}

.cookie-btn--ghost:hover {
  color: #fff;
  text-decoration: underline;
}

/* Preference center modal */
.cookie-prefs {
  position: fixed;
  inset: 0;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(8, 12, 22, 0.6);
}

.cookie-prefs[hidden] {
  display: none;
}

.cookie-prefs-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 520px;
  max-height: min(640px, 88vh);
  background: #0f172a;
  border: 1px solid rgba(226, 232, 240, 0.14);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  overflow: hidden;
  color: #e2e8f0;
}

.cookie-prefs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid rgba(226, 232, 240, 0.08);
}

.cookie-prefs-head h2 {
  margin: 0;
  font-size: 1.05rem;
  color: #f8fafc;
}

.cookie-prefs-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  margin: 0;
  padding: 0;
  background: rgba(226, 232, 240, 0.08);
  border: 1px solid rgba(226, 232, 240, 0.16);
  border-radius: 8px;
  color: #cbd5e1;
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
}

.cookie-prefs-close:hover {
  background: rgba(226, 232, 240, 0.16);
  color: #fff;
}

.cookie-prefs-body {
  flex: 1;
  overflow-y: auto;
  padding: 18px 20px;
}

.cookie-prefs-intro {
  margin: 0 0 12px;
  font-size: 0.78rem;
  line-height: 1.45;
  color: #94a3b8;
}

.cookie-cat {
  padding: 14px 0;
  border-top: 1px solid rgba(226, 232, 240, 0.08);
}

.cookie-cat:first-of-type {
  border-top: 0;
}

.cookie-cat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.cookie-cat-name {
  font-size: 0.85rem;
  font-weight: 600;
  color: #f8fafc;
}

.cookie-cat-state {
  font-size: 0.75rem;
  font-weight: 600;
  color: #34d399;
}

.cookie-cat-desc {
  margin: 5px 0 0;
  font-size: 0.76rem;
  line-height: 1.45;
  color: #94a3b8;
}

.cookie-prefs-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid rgba(226, 232, 240, 0.08);
}

/* Toggle switch */
.cookie-switch {
  position: relative;
  display: inline-block;
  flex: none;
  width: 40px;
  height: 22px;
  cursor: pointer;
}

.cookie-switch input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}

.cookie-slider {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: rgba(226, 232, 240, 0.2);
  transition: background 0.15s ease;
}

.cookie-slider::before {
  content: '';
  position: absolute;
  left: 3px;
  top: 3px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--bg-muted);
  transition: transform 0.15s ease;
}

.cookie-switch input:checked + .cookie-slider {
  background: var(--primary);
}

.cookie-switch input:checked + .cookie-slider::before {
  transform: translateX(18px);
}

.cookie-switch input:focus-visible + .cookie-slider {
  outline: 2px solid #a5b4fc;
  outline-offset: 2px;
}

/* Cookie policy inventory table (partials/cookies.html — light doc page) */
.cookie-table {
  width: 100%;
  margin: 12px 0 8px;
  border-collapse: collapse;
  font-size: 0.88rem;
}

.cookie-table th,
.cookie-table td {
  padding: 8px 10px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}

.cookie-table th {
  font-weight: 600;
  color: var(--text);
  background: var(--bg-muted);
}

@media (max-width: 560px) {
  .cookie-banner-actions {
    width: 100%;
  }
  .cookie-btn {
    flex: 1 1 auto;
  }
  .cookie-table {
    display: block;
    overflow-x: auto;
  }
}

/* Appearance (theme) control inside the cookie popup */
.cookie-appearance {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
  padding-bottom: 12px;
  border-bottom: 1px solid rgba(226, 232, 240, 0.08);
}

.cookie-appearance-label {
  font-size: 0.78rem;
  font-weight: 600;
  color: #f8fafc;
}

.cookie-theme {
  display: inline-flex;
  gap: 4px;
  padding: 3px;
  background: rgba(15, 23, 42, 0.6);
  border: 1px solid rgba(226, 232, 240, 0.16);
  border-radius: 10px;
}

.cookie-theme-opt {
  position: relative;
  cursor: pointer;
}

.cookie-theme-opt input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}

.cookie-theme-opt span {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 7px;
  font-size: 0.76rem;
  color: #cbd5e1;
  transition: background 0.15s ease, color 0.15s ease;
}

.cookie-theme-opt:hover span {
  color: #fff;
}

.cookie-theme-opt input:checked + span {
  background: var(--primary);
  color: var(--primary-ink, #fff);
}

.cookie-theme-opt input:focus-visible + span {
  outline: 2px solid #a5b4fc;
  outline-offset: 2px;
}

.cookie-appearance-note {
  margin: 8px 0 0;
  font-size: 0.78rem;
  color: var(--text-subtle);
}


.auth-title {
  margin: 0;
  font-size: 1.45rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.auth-subtitle {
  margin: 6px 0 22px;
  color: var(--text-subtle);
  font-size: 0.9rem;
  line-height: 1.5;
}

.auth-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  padding: 3px;
  margin-bottom: 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-muted);
}

.auth-tab {
  padding: 5px;
  margin: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.auth-tab.active {
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

.auth-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── Floating labels ─────────────────────────────────────────────────────────
   Each field is a single row: the label sits INSIDE the input box (no separate
   label line), resting over the field when empty and floating up to the top when
   the field is focused or filled. This renders the whole popup in much less
   vertical space — the point of the compact auth card. */
.auth-field {
  position: relative;
  display: block;
  margin: 0;
}

.auth-field > span:not(.auth-pw-wrap) {
  position: absolute;
  left: 12px;
  /* Anchor to the INPUT's vertical centre (fixed offset), not 50% of the whole
     field — the password field is taller (it also holds the "Forgot?" line below),
     which would otherwise drop its resting label below centre. */
  top: 22px;
  transform: translateY(-50%);
  transform-origin: left center;
  max-width: calc(100% - 24px);
  padding: 0 2px;
  font-size: 0.9rem;
  font-weight: 400;
  color: var(--text-faint);
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  background: var(--bg-surface);
  /* Sit above the password field's positioned .auth-pw-wrap (which would
     otherwise paint over the resting label). */
  z-index: 2;
  transition: transform 0.12s ease, color 0.12s ease, font-size 0.12s ease;
}

/* Room at the top of every control for the floated label. */
.auth-field input,
.auth-field select {
  margin: 0;
  font-size: 0.9rem;
  padding-top: 15px;
  padding-bottom: 5px;
}

/* Selects: strip the native control (Safari renders it cramped, jamming the value
   against its built-in arrow under the floated label) and draw our own chevron with
   comfortable padding + a proper hit height, so Country/State match the inputs. */
.auth-field select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  height: 44px;
  padding-right: 34px;
  line-height: 1.2;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
.auth-field select::-ms-expand { display: none; }

/* Float the label to the top edge when the field is focused, has a typed value,
   or is a <select> (selects always carry a value, so the label never rests). */
.auth-field:has(input:focus) > span:not(.auth-pw-wrap),
.auth-field:has(input:not(:placeholder-shown)) > span:not(.auth-pw-wrap),
.auth-field:has(select) > span:not(.auth-pw-wrap) {
  transform: translateY(-1.5rem) scale(0.78);
  color: var(--text-muted);
  font-weight: 500;
}
.auth-field:has(input:focus) > span:not(.auth-pw-wrap) {
  color: var(--accent);
}

/* The meaningful placeholder (e.g. you@example.com) only shows on focus, so at
   rest just the resting label is visible over the empty field. */
.auth-field input:not(:focus)::placeholder {
  color: transparent;
}

/* Inline show/hide password toggle (signin/signup + reset password). */
.auth-pw-wrap { position: relative; display: block; }
.auth-pw-wrap > input { width: 100%; box-sizing: border-box; padding-right: 2.5rem; }
.auth-pw-toggle {
  position: absolute;
  top: 50%;
  right: 0.35rem;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-subtle);
  cursor: pointer;
  transition: color 0.12s ease;
}
.auth-pw-toggle:hover { color: var(--text); }
.auth-pw-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* Slash the eye when the password is revealed. */
.auth-pw-toggle.is-on::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 1.3rem;
  height: 1.6px;
  background: currentColor;
  border-radius: 1px;
  transform: translate(-50%, -50%) rotate(45deg);
}

/* Password field: with the label floating inside the box, the "Forgot?" link
   (shown only on the Sign in tab) sits in the top-right of the same box, aligned
   with the floated label — no extra row. */
/* "Forgot?" (Sign in tab only) sits on its own line just below the password box,
   right-aligned. */
.auth-field--pw .auth-inline-link {
  display: block;
  width: fit-content;
  margin: 5px 2px 0 auto;
  padding: 0;
  font-size: 0.75rem;
}
.auth-field--pw .auth-inline-link[hidden] { display: none; }

/* Signup-only profile fields (company / phone / country / state) — same floating
   labels as the rest; country + state each get their own full-width row. */
.auth-signup-only { display: contents; }
.auth-signup-only[hidden] { display: none; }
#auth-signup-fields .auth-field input,
#auth-signup-fields .auth-field select { width: 100%; box-sizing: border-box; }
.auth-field-grid { display: contents; }
.auth-optional { color: var(--text-faint); font-weight: 400; font-size: 0.82em; }

.auth-hint {
  display: block;
  margin-top: 6px;
  color: var(--text-subtle);
  font-size: 0.74rem;
  line-height: 1.5;
}

/* Author display:block above would otherwise beat the UA [hidden] rule, so the
   hint stayed visible even when JS set hidden. Make the attribute win. */
.auth-hint[hidden] {
  display: none;
}

/* Free-trial note — highlighted in the brand accent so the "15-day free trial"
   reassurance stands out from the plain grey hints. */
.auth-trial-note {
  margin: 2px 0 4px;
  padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--success) 30%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, var(--success) 12%, transparent);
  color: var(--success);
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.45;
}

/* Honeypot — visually hidden but still rendered so naïve bots fill it.
   Keep off-screen rather than display:none so headless browsers see a real input. */
.auth-honeypot {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.auth-turnstile {
  margin: 6px 0 2px;
  min-height: 0;
}

.auth-turnstile iframe {
  border: 0;
}

.auth-error {
  margin: 0;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(185, 28, 28, 0.08);
  color: var(--danger);
  font-size: 0.82rem;
}

.auth-success {
  margin: 0;
  padding: 8px 10px;
  border-radius: 6px;
  background: rgba(22, 101, 52, 0.08);
  color: var(--success);
  font-size: 0.82rem;
}

.auth-field-label-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}

.auth-field-label-row > span {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-muted);
}

.auth-inline-link {
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  color: var(--text-subtle);
  font: inherit;
  font-size: 0.74rem;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  transition: color 0.15s ease;
}

.auth-inline-link:hover,
.auth-inline-link:focus-visible {
  color: var(--text);
  background: transparent;
  outline: none;
}

.auth-submit {
  margin: 4px 0 0;
  width: 100%;
  padding: 9px 14px;
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  background: var(--primary);
  color: var(--primary-ink, #fff);
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.auth-submit:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.auth-submit:disabled {
  opacity: 0.6;
  cursor: default;
}

.auth-divider {
  position: relative;
  margin: 14px 0 12px;
  text-align: center;
  color: var(--text-faint);
  font-size: 0.78rem;
}

.auth-divider::before,
.auth-divider::after {
  content: '';
  position: absolute;
  top: 50%;
  width: calc(50% - 24px);
  border-top: 1px solid var(--border);
}

.auth-divider::before { left: 0; }
.auth-divider::after { right: 0; }

.auth-google {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  padding: 10px;
  margin: 0 0 12px;
  text-decoration: none;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font-weight: 500;
  font-size: 0.88rem;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.auth-google:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.auth-google-icon {
  flex-shrink: 0;
}

.auth-google-disabled {
  color: var(--text-subtle);
}

.auth-google-disabled .auth-google-icon {
  opacity: 0.65;
}

.auth-footer {
  margin: 0;
  color: var(--text-faint);
  font-size: 0.74rem;
  text-align: center;
  line-height: 1.6;
}

.doc-link {
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.15s ease;
}

.doc-link:hover,
.doc-link:focus-visible {
  color: var(--text);
  outline: none;
}

/* Doc modal (Terms / Privacy / Governance popups) */

.doc-modal {
  position: fixed;
  inset: 0;
  z-index: 1000; /* above the cookie banner (800) + prefs (900) so the policy opens on top */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(15, 23, 42, 0.55);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}

.doc-modal[hidden] {
  display: none;
}

.doc-modal-card {
  display: flex;
  flex-direction: column;
  width: min(720px, 100%);
  max-height: calc(100vh - 48px);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 24px 60px rgba(15, 23, 42, 0.32);
  overflow: hidden;
}

.doc-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-surface);
}

.doc-modal-header h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.doc-modal-close {
  width: 30px;
  height: 30px;
  padding: 0;
  margin: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.doc-modal-close:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.doc-modal-body {
  padding: 18px 24px 24px;
  overflow-y: auto;
}

.doc-modal-body .doc-callout {
  margin-top: 0;
}

/* The Documentation page is a multi-page `.doc-content` (one page shown at a
   time via a side nav). When it opens in the doc modal as a popup we drop the
   page's own scroll/padding and reveal every page stacked into one scroll. */
.doc-modal-body .doc-content {
  overflow: visible;
  padding: 0;
}
.doc-modal-body .doc-content .doc-page {
  display: block;
  max-width: none;
}

/* Delete-account confirmation modal */

.delete-modal-card {
  width: min(460px, 100%);
}

.delete-modal-card .doc-modal-body {
  padding: 18px 22px 22px;
}

.delete-modal-card label {
  display: block;
  margin: 14px 0 6px;
  font-size: 0.84rem;
  color: var(--text-muted);
  font-weight: 500;
}

.delete-modal-card label code {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--bg-muted);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text);
}

.delete-modal-card input[type="text"] {
  width: 100%;
  margin: 0;
}

.delete-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 18px;
}

.delete-modal-actions button {
  margin: 0;
}

.delete-modal-actions .danger-button[disabled] {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Forgot-password + reset-password modals */

.forgot-modal-card {
  width: min(440px, 100%);
}

.forgot-modal-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 18px 22px 22px;
}

.forgot-modal-form label {
  margin: 4px 0 0;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-muted);
}

.forgot-modal-form input {
  margin: 0;
  width: 100%;
}

.forgot-modal-help {
  margin: 0;
  color: var(--text-subtle);
  font-size: 0.82rem;
  line-height: 1.5;
}

.forgot-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 10px;
}

.forgot-modal-actions button {
  margin: 0;
}

.consent-banner {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 90;
  display: flex;
  align-items: center;
  gap: 18px;
  width: min(720px, calc(100% - 32px));
  padding: 14px 18px;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(15, 23, 42, 0.16);
}

.consent-banner[hidden] {
  display: none;
}

.consent-text {
  flex: 1;
  color: var(--text-muted);
  font-size: 0.84rem;
  line-height: 1.5;
}

.consent-accept {
  margin: 0;
  padding: 7px 14px;
  font-size: 0.82rem;
}

/* Account + API key cards in Settings */

.account-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 16px;
  align-items: stretch;
}

.account-grid > .card {
  display: flex;
  flex-direction: column;
  min-height: 320px;
  margin-bottom: 0;
}

.account-grid > .card > .card-title {
  margin: 0 0 6px;
  flex-shrink: 0;
}

.account-grid > .card > .card-help {
  margin: 0 0 16px;
  flex-shrink: 0;
}

.account-grid > .card > .account-summary {
  margin: 0 0 16px;
}

.account-form {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 0;
}

.account-form label {
  margin: 14px 0 6px;
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--text-muted);
}

.account-form label:first-of-type {
  margin-top: 0;
}

.account-form input {
  margin: 0;
}

.account-form button[type="submit"] {
  margin: 18px 0 0;
  align-self: flex-start;
}

.account-form .account-status {
  margin: 12px 0 0;
}

.account-grid > .card > .ghost-button,
.account-grid > .card > .danger-button {
  margin-top: auto;
  align-self: flex-start;
}

.account-form input[disabled] {
  background: var(--bg-muted);
  color: var(--text-subtle);
}

.account-status {
  margin: 10px 0 0;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 0.82rem;
}

.account-status.success {
  background: rgba(22, 101, 52, 0.08);
  color: var(--success);
}

.account-status.error {
  background: rgba(185, 28, 28, 0.08);
  color: var(--danger);
}

.account-summary {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.account-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-muted);
  font-size: 0.85rem;
}

.account-row:last-of-type {
  border-bottom: 0;
}

.account-row span {
  color: var(--text-subtle);
}

.account-row strong {
  color: var(--text);
  font-weight: 500;
  text-align: right;
  overflow-wrap: anywhere;
}

.ghost-button,
.danger-button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 12px;
  margin-top: 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  /* Some ghost-buttons are <a> elements for native right-click + nav-friendly
     behaviour; strip the default anchor underline so both render identically. */
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.ghost-button:hover {
  background: var(--bg-hover);
  border-color: var(--border-strong);
}

.ghost-button.danger {
  color: var(--danger);
  border-color: rgba(185, 28, 28, 0.3);
}

.ghost-button.danger:hover {
  background: rgba(185, 28, 28, 0.06);
}

.danger-button {
  margin-left: 8px;
  color: var(--danger);
  border-color: rgba(185, 28, 28, 0.3);
}

.danger-button:hover {
  background: rgba(185, 28, 28, 0.06);
}

.card-help {
  margin: -6px 0 12px;
  color: var(--text-subtle);
  font-size: 0.8rem;
  line-height: 1.5;
}

.api-keys-layout {
  display: grid;
  grid-template-columns: minmax(280px, 380px) 1fr;
  gap: 16px;
  align-items: start;
}

@media (max-width: 840px) {
  .api-keys-layout {
    grid-template-columns: 1fr;
  }
}

.api-key-form {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.api-key-provider-hint {
  margin: 6px 0 12px;
  padding: 8px 10px;
  background: var(--bg-muted);
  border-radius: 6px;
  color: var(--text-muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.api-key-provider-hint a {
  color: var(--text);
  font-weight: 500;
}

.form-optional {
  color: var(--text-faint);
  font-weight: 400;
  font-size: 0.74rem;
}

.api-key-input-row {
  display: flex;
  gap: 6px;
  align-items: stretch;
}

.api-key-input-row input {
  flex: 1;
}

.api-key-field-row {
  display: flex;
  gap: 12px;
  margin-top: 4px;
}

.api-key-field {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* The global `label { margin: 12px 0 6px }` puts vertical air above each
   label. Inside this side-by-side row the field already owns the spacing, so
   neutralize the leading margin to keep the two columns top-aligned. */
.api-key-field > label {
  margin-top: 0;
}

@media (max-width: 480px) {
  .api-key-field-row {
    flex-direction: column;
    gap: 4px;
  }
}

.api-key-reveal {
  margin: 0;
  padding: 0 14px;
  font-size: 0.78rem;
  white-space: nowrap;
}

.api-key-list {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.api-key-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  transition: border-color 0.15s ease;
}

.api-key-row:hover {
  border-color: var(--border-strong);
}

.api-key-meta {
  min-width: 0;
}

.api-key-headline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.api-key-headline strong {
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
}

.api-key-provider-badge {
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.api-key-last4 {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--text-subtle);
  background: transparent;
  padding: 0;
}

.api-key-meta-line {
  display: block;
  margin-top: 4px;
  color: var(--text-subtle);
  font-size: 0.76rem;
}

.api-key-actions {
  display: flex;
  flex-shrink: 0;
  gap: 6px;
}

.api-key-actions .ghost-button {
  padding: 6px 10px;
  font-size: 0.78rem;
}

.api-key-pill {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.4;
}

.api-key-pill.default {
  background: rgba(34, 139, 230, 0.12);
  color: #1e6fbf;
}

.api-key-pill.expires {
  background: rgba(217, 119, 6, 0.12);
  color: #b45309;
}

.api-key-pill.expired {
  background: rgba(220, 38, 38, 0.12);
  color: #b91c1c;
}

.api-key-note {
  margin: 10px 0 4px;
  padding: 8px 12px;
  background: rgba(34, 139, 230, 0.08);
  border: 1px solid rgba(34, 139, 230, 0.18);
  border-radius: 8px;
  color: var(--text-muted);
  font-size: 0.78rem;
  line-height: 1.5;
}

.api-key-note > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}

/* Hide the native disclosure marker — we provide our own caret. */
.api-key-note > summary::-webkit-details-marker {
  display: none;
}
.api-key-note > summary::marker {
  display: none;
  content: '';
}

.api-key-note > summary::after {
  content: '';
  width: 6px;
  height: 6px;
  margin-left: auto;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  opacity: 0.6;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}

.api-key-note[open] > summary::after {
  transform: rotate(-135deg);
}

.api-key-note-icon {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(34, 139, 230, 0.18);
  color: #1e6fbf;
  font-family: var(--font-serif, Georgia, serif);
  font-style: italic;
  font-weight: 700;
  font-size: 0.78rem;
  line-height: 18px;
  text-align: center;
}

.api-key-note-summary {
  flex: 1 1 auto;
  min-width: 0;
}

.api-key-note-body {
  margin: 8px 0 0;
  padding-left: 28px;
}

.api-key-checkbox {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-muted);
  font-weight: 500;
  margin: 10px 0 2px;
  cursor: pointer;
  user-select: none;
}

/* The global `input { width: 100% }` rule would stretch the checkbox into a
   full-width rectangle. Reset to native sizing so the box stays compact. */
.api-key-checkbox input[type="checkbox"] {
  width: auto;
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
  accent-color: var(--accent, #1e6fbf);
}

.api-key-test-result {
  display: block;
  margin-top: 6px;
  font-size: 0.74rem;
  line-height: 1.4;
}

.api-key-test-result.success {
  color: #14803c;
}

.api-key-test-result.error {
  color: #b91c1c;
}

.link-button {
  background: none;
  border: none;
  padding: 0;
  color: var(--accent, #1e6fbf);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

.link-button:hover {
  color: var(--accent-strong, #154e8f);
  background: none; /* override the global button:hover dark fill */
}
.link-button--danger { color: var(--danger, #b42318); }
.link-button--danger:hover { color: var(--danger, #b42318); filter: brightness(0.9); }
/* " / " separator between action links (muted, not underlined). */
.appadmin-action-sep { color: var(--text-faint); padding: 0 2px; user-select: none; }

/* "User type" facet chips in the Accounts table (customer type · kind · owner role). */
.appadmin-type-chip { display: inline-block; margin: 0 4px 3px 0; padding: 1px 7px; font-size: 11px;
  font-weight: 600; line-height: 1.5; text-transform: capitalize; color: var(--text-subtle);
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: 999px; white-space: nowrap; }

/* No dark hover-fill on links inside policy docs (tab or modal views). */
.doc-article a:hover,
.doc-modal-body a:hover {
  background: none;
}

/* ---- Plans page ---- */

.plans-page {
  display: flex;
  flex-direction: column;
  align-items: center; /* centers every direct child on the cross axis */
  gap: 18px;
  margin-top: 8px;
}

/* Dev-bypass banner shown only when ENV != 'prod' — the backend's
   /billing/plans payload carries `dev_bypass_enabled` and the JS gates this
   element. Amber/striped so it's impossible to confuse with prod billing. */
.plans-dev-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 14px;
  border: 1px dashed rgba(204, 102, 0, 0.45);
  background: repeating-linear-gradient(
    45deg,
    rgba(204, 102, 0, 0.05) 0px,
    rgba(204, 102, 0, 0.05) 8px,
    rgba(204, 102, 0, 0.10) 8px,
    rgba(204, 102, 0, 0.10) 16px
  );
  color: var(--text);
  border-radius: 10px;
  font-size: 0.82rem;
  max-width: 720px;
  align-self: stretch;
}

.plans-dev-pill {
  flex-shrink: 0;
  padding: 2px 8px;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: #fff;
  background: #cc6600;
  border-radius: 4px;
}

.plans-page > .plans-section {
  /* the card section needs to stretch back to full width */
  align-self: stretch;
}

/* Claude-style centered pill segmented control. Container is a soft rounded
   capsule; active tab has a raised white surface with a subtle shadow. Used
   for primary in-page category navigation on pricing-style pages. */
.plans-tabs {
  display: inline-flex;
  align-items: stretch;
  gap: 4px;
  padding: 5px;
  margin: 8px 0 14px;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-sizing: border-box;
}

.plans-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 140px;
  padding: 9px 22px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--text-muted);
  font-size: 0.9rem;
  font-weight: 500;
  font-family: inherit;
  letter-spacing: 0.005em;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
  outline: none;
  white-space: nowrap;
}

.plans-tab:hover:not(:disabled):not(.active) {
  color: var(--text);
  background: rgba(0, 0, 0, 0.03);
}

.plans-tab:focus-visible {
  outline: 2px solid rgba(30, 111, 191, 0.45);
  outline-offset: 2px;
}

.plans-tab.active {
  background: var(--bg-surface);
  color: var(--text);
  font-weight: 600;
  box-shadow:
    0 1px 2px rgba(0, 0, 0, 0.06),
    0 1px 3px rgba(0, 0, 0, 0.04);
}

.plans-tab:disabled {
  color: var(--text-faint);
  cursor: not-allowed;
}

.plans-tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 19px;
  padding: 0 7px;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.06);
  color: var(--text-muted);
  transition: background 0.18s ease, color 0.18s ease;
}

.plans-tab.active .plans-tab-count {
  background: rgba(30, 111, 191, 0.12);
  color: #1e6fbf;
}

@media (max-width: 560px) {
  .plans-tabs {
    width: 100%;
    flex-direction: column;
    border-radius: 14px;
  }
  .plans-tab {
    min-width: 0;
    border-radius: 10px;
  }
}

.plans-section-header {
  margin-bottom: 12px;
}

.plans-section-header h2 {
  margin: 0;
  font-size: 1.05rem;
  font-weight: 700;
}

.plans-section-header p {
  margin: 4px 0 0;
  color: var(--text-muted);
  font-size: 0.85rem;
}

.plans-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-top: 8px;
}

.plan-card-current {
  border-color: rgba(20, 128, 60, 0.45);
  box-shadow: 0 4px 14px rgba(20, 128, 60, 0.08);
  position: relative;
}

.plan-card-current::before {
  content: 'Current plan';
  position: absolute;
  top: -10px;
  left: 16px;
  padding: 2px 8px;
  background: #14803c;
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  border-radius: 999px;
}

.plan-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 18px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--bg-surface);
  min-height: 380px;
}

.plan-card-featured {
  border-color: rgba(34, 139, 230, 0.45);
  box-shadow: 0 4px 14px rgba(34, 139, 230, 0.08);
  position: relative;
}

.plan-card-featured::before {
  content: 'Most popular';
  position: absolute;
  top: -10px;
  left: 16px;
  padding: 2px 8px;
  background: #1e6fbf;
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  border-radius: 999px;
}

.plan-card-header h2 {
  margin: 0 0 4px;
  font-size: 1.05rem;
}

.plan-tagline {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.45;
}

/* Plan name row + "25% off if annual" badge */
.plan-card-name {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.plan-annual-badge,
.pricing-tier-annual {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: #0e7490;
  background: rgba(34, 211, 238, 0.15);
  border: 1px solid rgba(14, 116, 144, 0.35);
  white-space: nowrap;
}
.pricing-tier-annual {
  margin-left: 8px;
}

/* "12 modules — included in every plan" grid */
.plans-savings__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  margin-top: 12px;
}
.plans-savings__card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px 20px;
  position: relative;
}
.plans-savings__pct {
  align-self: flex-start;
  font-weight: 800;
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  color: #0e7490;
  background: rgba(14, 116, 144, 0.12);
  border: 1px solid rgba(14, 116, 144, 0.25);
  border-radius: 999px;
  padding: 3px 10px;
}
.plans-savings__title {
  margin: 6px 0 0;
  font-size: 1.05rem;
}
.plans-savings__body {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--muted, #64748b);
}
.plans-savings__cta {
  margin-top: auto;
  align-self: flex-start;
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--primary);
  text-decoration: none;
  padding-top: 6px;
}
.plans-savings__cta:hover {
  text-decoration: underline;
}
.plans-savings__note {
  margin: 12px 0 0;
}
.plans-modules__grid {
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}
.plans-module {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  border: 1px solid var(--border, rgba(148, 163, 184, 0.25));
  border-radius: 10px;
  background: var(--surface-2, rgba(148, 163, 184, 0.06));
}
.plans-module__name {
  font-weight: 600;
  font-size: 0.9rem;
}

.plan-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.plan-amount {
  font-size: 1.55rem;
  font-weight: 700;
  color: var(--text);
}

/* Struck-through "list" price shown before the real (discounted, annual) headline. */
.plan-anchor {
  color: var(--text-faint, var(--text-muted));
  text-decoration: line-through;
  font-size: 1rem;
  font-weight: 600;
}

.plan-unit {
  color: var(--text-muted);
  font-size: 0.82rem;
}

.plan-cycle {
  flex-basis: 100%;
  color: var(--text-muted);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.plan-trial {
  margin: 8px 0 0;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--success, #16a34a);
}

.plans-notes {
  max-width: 960px;
  margin: 14px auto 0;
  padding-left: 18px;
  line-height: 1.7;
}

.plan-cadence {
  color: var(--text-muted);
  font-size: 0.78rem;
}

.plan-features {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.plan-features li {
  position: relative;
  padding-left: 18px;
  line-height: 1.45;
}

.plan-features li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: #14803c;
  font-weight: 700;
}

.plan-cta {
  margin-top: auto;
  width: 100%;
}

button.plan-cta {
  /* When this is the featured CTA (no ghost-button class on the element), use
     the brand button. The ghost variant inherits from .ghost-button styles. */
  background: #1e6fbf;
  color: #fff;
  border: 1px solid #1e6fbf;
  padding: 10px 14px;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
}

button.plan-cta:hover:not(:disabled) {
  background: #154e8f;
  border-color: #154e8f;
}

button.plan-cta:disabled {
  background: var(--bg-muted);
  border-color: var(--border);
  color: var(--text-faint);
  cursor: not-allowed;
}

button.ghost-button.plan-cta {
  /* Reset brand styling when the ghost-button class is applied. */
  background: var(--bg-surface);
  color: var(--text);
  border-color: var(--border);
}

button.ghost-button.plan-cta:hover:not(:disabled) {
  background: var(--bg-muted);
  border-color: var(--border-strong);
}

/* ---- Billing page ---- */

.billing-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin-top: 8px;
}

@media (min-width: 720px) {
  .billing-layout {
    grid-template-columns: repeat(2, 1fr);
  }
  .billing-layout > .card:last-child {
    grid-column: span 2;
  }
}

.billing-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.billing-cards {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.billing-card-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.85rem;
}

.billing-card-row small {
  color: var(--text-subtle);
  font-size: 0.74rem;
}

.billing-invoices {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.billing-invoice-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.85rem;
  gap: 12px;
}

.billing-invoice-meta {
  display: block;
  color: var(--text-subtle);
  font-size: 0.72rem;
  margin-top: 2px;
}

.billing-invoice-amount {
  display: flex;
  align-items: center;
  gap: 10px;
  font-weight: 600;
}

.account-plan-actions {
  display: flex;
  gap: 8px;
  margin-top: 12px;
  flex-wrap: wrap;
}

/* ---- Usage meter (industry-standard progress bar) ---- */

.billing-usage:empty {
  display: none;
}

.usage-meter {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-muted);
}

.usage-meter-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.82rem;
  margin-bottom: 6px;
}

.usage-meter-value {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: 0.78rem;
}

.usage-meter-bar {
  position: relative;
  height: 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.08);
  overflow: hidden;
}

.usage-meter-fill {
  position: absolute;
  inset: 0 auto 0 0;
  background: linear-gradient(90deg, #1e6fbf, #14803c);
  border-radius: 999px;
  transition: width 0.2s ease;
}

.usage-meter-meta {
  display: block;
  margin-top: 6px;
  color: var(--text-subtle);
  font-size: 0.72rem;
}

.usage-meter-fill.is-over {
  background: #c0392b;
}

/* Compact balance for the collapsed icon rail — hidden when the panel is open. */
.usage-meter-short {
  display: none;
  font-variant-numeric: tabular-nums;
  font-size: 0.7rem;
  font-weight: 600;
  text-align: center;
  color: var(--text-muted);
}
.usage-meter-short.is-over {
  color: #c0392b;
}
.left-collapsed #sidebar-usage-meter .usage-meter {
  display: none;
}
/* Collapsed rail: show usage as a small circular % bubble with a progress ring
   (driven by the inline --pct). The radial mask hollows the centre for the text. */
.left-collapsed #sidebar-usage-meter .usage-meter-short {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  margin: 0 auto;
  border-radius: 50%;
  font-size: 0.52rem;
  font-weight: 700;
  color: var(--text);
  /* Thin ring: the inner disc fills to 90% of the radius, leaving a ~1.5px band. */
  background:
    radial-gradient(closest-side, var(--bg-surface) 90%, transparent 91% 100%),
    conic-gradient(var(--primary) calc(var(--pct, 100) * 1%), var(--border) 0);
}
.left-collapsed #sidebar-usage-meter .usage-meter-short.is-over {
  color: #c0392b;
  background:
    radial-gradient(closest-side, var(--bg-surface) 90%, transparent 91% 100%),
    conic-gradient(#e74c3c 100%, #e74c3c 0);
}

/* Combined AI + Cloud credits block in the sidebar footer (one card, two rows):
   expanded → "AI 1K" / "Cloud 1.5K/25K"; collapsed → "A28%" / "C37%" stacked. */
.usage-combo {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.usage-combo-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.76rem;
}
.usage-combo-row .uc-label {
  color: var(--text-muted);
}
.usage-combo-row .uc-val {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.usage-combo-row.is-over .uc-val {
  color: #c0392b;
}
.usage-combo-short {
  display: none;
}
.usage-combo-short .ucs-row {
  font-variant-numeric: tabular-nums;
}
.usage-combo-short .ucs-row.is-over {
  color: #c0392b;
}
/* Collapsed rail: hide the labelled rows, show the two stacked percentages. */
.left-collapsed #sidebar-usage-meter .usage-combo {
  display: none;
}
.left-collapsed #sidebar-usage-meter .usage-combo-short {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  font-size: 0.56rem;
  font-weight: 700;
  color: var(--text);
}
.left-collapsed .sidebar-usage {
  padding: 6px 2px;
}

/* ── Plans page: comparison matrix + prepaid top-up packs ───────────────── */
.plans-compare__scroll {
  overflow-x: auto;
}
.plans-compare__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
}
.plans-compare__table th,
.plans-compare__table td {
  border: 1px solid var(--border);
  padding: 8px 12px;
  text-align: center;
}
.plans-compare__table thead th {
  background: var(--bg-muted);
  font-weight: 600;
}
.plans-compare__table thead th.is-featured {
  color: var(--accent, #4c8cff);
}
.plans-compare__table th[scope="row"] {
  text-align: left;
  font-weight: 500;
  color: var(--text-muted);
}
.plans-cta { display: flex; justify-content: center; margin-top: 22px; }
.plans-cta .primary-button { min-width: 220px; padding: .7rem 1.4rem; font-size: .95rem; border-radius: 10px; }
.plans-personal { text-align: center; margin-top: 20px; }

/* Landing licence table. The marketing home is a self-contained LIGHT surface (see
   `.home-landing` — it pins light values so it never inherits the app theme), so these are
   tuned for light and deliberately carry no prefers-color-scheme override: a dark-mode rule
   would fire from the visitor's OS setting on a page that is always light. The shared
   .lc-matrix markup is reused verbatim so the numbers can never differ from the app. */
.home-licence-table { max-width: 1040px; margin: 28px auto 0; }
.home-licence-table .lc-matrix { min-width: 640px; font-size: .84rem; background: #fff; }
.home-licence-table .lc-matrix th,
.home-licence-table .lc-matrix td { border-color: rgba(15, 23, 42, .12); }
.home-licence-table .lc-matrix thead th,
.home-licence-table .lc-cat { background: rgba(15, 23, 42, .04); }
.home-licence-table .lc-band__name,
.home-licence-table .lc-cat__name { color: #0f172a; }
.home-licence-table .lc-band__price { color: #0f172a; }
.home-licence-table .lc-cat__blurb,
.home-licence-table .lc-band__unit,
.home-licence-table .lc-band__alt { color: #475569; }
/* Ticks carry the whole meaning of the table, so they read stronger than the dashes: a deep,
   saturated green at a heavier weight rather than a pale mint. */
.home-licence-table .lc-cell { color: #94a3b8; opacity: 1; }
.home-licence-table .lc-cell.is-on { color: #15803d; font-weight: 800; font-size: 1.05rem; }
/* The landing quotes a RANGE, not a ladder (the five-column grid answered "what will this cost
   us?" with homework) — and ONE currency, the visitor's, so there is a single figure to read
   rather than a conversion to do. It carries the section, so it is set at display size with the
   unit trailing it quietly. The currency that doesn't apply is `hidden`, never shrunk. */
/* The in-app Plans page quotes the same range in the same voice (controllers/billing.js). */
.plans-range { margin: 18px 0 0; text-align: center; line-height: 1.25; }
.plans-range__amount { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 800; letter-spacing: -.02em; color: var(--text); }
.plans-range__unit { display: block; margin-top: 6px; font-size: .9rem; font-weight: 600; color: var(--text-muted); }
.plans-range-lead { max-width: 640px; margin: 14px auto 0; text-align: center; font-size: .92rem; line-height: 1.55; color: var(--text-muted); }
.home-price-range { max-width: 760px; margin: 26px auto 0; text-align: center; line-height: 1.25; }
.home-price-range[hidden] { display: none; }
.home-price-range__amount { font-size: clamp(2rem, 5vw, 3rem); font-weight: 800; letter-spacing: -.02em; color: #0f172a; }
.home-price-range__unit { display: block; margin-top: 6px; font-size: .92rem; font-weight: 600; color: #475569; }
.home-price-fineprint { font-size: .84rem; opacity: .85; margin-top: 10px; }
.home-licence-note, .home-personal-note { max-width: 760px; margin: 16px auto 0; text-align: center; font-size: .85rem; }
.home-licence-note { color: #475569; }
/* The free-forever promise is the one line a visitor should not miss — its own accent, so it
   lifts off the muted paragraph around it. Teal matches the "Free forever" badge. */
.home-personal-note { color: #475569; }
.home-free-highlight { color: #0e7490; font-weight: 700;
  background: rgba(14, 116, 144, .10); border-radius: 6px; padding: 2px 8px; }

/* "Choose the right licence" on the Plans page (and to signed-out visitors). Wider gutters
   than the in-flow copy, since it sits under the plan cards as reference material. */
.plans-licences { margin-top: 28px; }
.plans-licences .lc-matrix { min-width: 720px; }
.plans-signin { text-align: center; margin-top: 14px; }

/* ── Licence flow: full-page steps ──────────────────────────────────────────────
   Each step is a PAGE, not a panel — everything else on the host page is hidden while the
   flow is open (`[data-lc-hidden]`), so the buyer moves forward through screens instead of
   scrolling an accordion. Centred, generous, one decision per screen. */
[data-lc-hidden] { display: none !important; }
.lc-page { max-width: 860px; margin: 0 auto; padding: 8px 20px 64px; }
.lc-page__bar { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 0 26px; }
.lc-back { background: none; border: 0; padding: 6px 8px; margin: 0 -8px; cursor: pointer;
  font: inherit; font-size: .88rem; color: var(--text-muted); border-radius: 8px; }
.lc-back:hover { color: var(--text); background: var(--hover, #f1f5f9); }
.lc-steps { display: inline-flex; align-items: center; gap: 10px; }
.lc-steps__dot { position: relative; font-size: .76rem; font-weight: 600; color: var(--text-faint);
  padding-left: 20px; }
.lc-steps__dot::before { content: ''; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 12px; height: 12px; border-radius: 50%; border: 2px solid var(--border); background: var(--bg-surface); }
.lc-steps__dot.is-on { color: var(--text); }
.lc-steps__dot.is-on::before { border-color: var(--primary); background: var(--primary); }
.lc-steps__dot.is-done::before { border-color: var(--primary); background: var(--primary); opacity: .45; }
.lc-steps__label { font-size: .76rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-faint); }
.lc-page__head { text-align: center; margin-bottom: 30px; }
.lc-page__head h1 { margin: 0 0 8px; font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; }
.lc-page__head p { margin: 0 auto; max-width: 620px; font-size: .95rem; line-height: 1.55;
  color: var(--text-muted); }
.lc-page__body { margin-bottom: 28px; }
.lc-page__foot { display: flex; justify-content: center; gap: 10px; }
.lc-page__foot .primary-button, .lc-page__foot .ghost-button {
  min-width: 180px; padding: .7rem 1.4rem; font-size: .95rem; border-radius: 10px; }
/* Review step — the receipt before the charge. */
.lc-review__table { margin-bottom: 22px; }
.lc-review__total { display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 20px 22px; border: 1px solid var(--border); border-radius: 14px; background: var(--app-filter-bg); }
.lc-review__amount { display: block; font-size: 2.1rem; font-weight: 800; line-height: 1.15; letter-spacing: -.02em; }
.lc-review__gw { font-size: .8rem; color: var(--text-muted); white-space: nowrap; }
@media (max-width: 640px) {
  .lc-page { padding: 4px 14px 48px; }
  .lc-page__head h1 { font-size: 1.5rem; }
  .lc-steps__dot { padding-left: 16px; font-size: 0; }
  .lc-page__foot .primary-button, .lc-page__foot .ghost-button { min-width: 0; flex: 1; }
  .lc-review__total { flex-direction: column; align-items: flex-start; }
}

/* ── Licence configurator (Plans → More details) ─────────────────────────────
   Step 1 is a comparison matrix (9 categories x 3 levels x 5 bands); step 2 allocates
   licences per band. Both scroll horizontally on their own so the page never does. */
.lc { border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); margin-bottom: 20px; }
.lc__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 16px 18px; border-bottom: 1px solid var(--border); }
.lc__head h2 { margin: 0 0 2px; font-size: 1.05rem; }
.lc__body { padding: 16px 18px; }
.lc__foot { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 18px;
  border-top: 1px solid var(--border); }
.lc-tablewrap { overflow-x: auto; }
.lc-matrix { border-collapse: collapse; width: 100%; min-width: 680px; font-size: 0.86rem; }
.lc-matrix th, .lc-matrix td { border: 1px solid var(--border); padding: 7px 10px; text-align: center; }
.lc-matrix thead th { background: var(--app-filter-bg); vertical-align: top; }
.lc-band__name { display: block; font-weight: 700; }
.lc-band__price { display: block; font-size: 1.05rem; font-weight: 700; margin-top: 2px; }
.lc-band__unit { display: block; font-size: 0.72rem; font-weight: 400; color: var(--text-muted); }
.lc-cat { text-align: left; vertical-align: top; background: var(--app-filter-bg); min-width: 210px; }
.lc-cat__name { display: block; font-weight: 700; }
.lc-cat__blurb { display: block; font-size: 0.72rem; font-weight: 400; color: var(--text-muted); }
.lc-cell { color: var(--text-faint); }
.lc-cell.is-on { color: #16a34a; font-weight: 700; }
.lc-alloc { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.lc-alloc th, .lc-alloc td { border-bottom: 1px solid var(--border); padding: 10px; text-align: left; }
.lc-alloc thead th { font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); }
.lc-alloc input { width: 90px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 7px;
  font: inherit; background: var(--bg-surface); color: var(--text); }
.lc-band__alt { display: block; font-size: .7rem; font-weight: 400; color: var(--text-faint); margin-top: 2px; }
.lc-alloc__grouphead { text-align: center !important; border-bottom: 0 !important; padding-bottom: 2px !important; }
.lc-alloc__sub-th { font-weight: 600 !important; }
.lc-alloc__sub-th.is-best, .lc-alloc__price.is-best { color: var(--text); }
.lc-alloc__price.is-best { font-weight: 700; }
.lc-alloc__per { font-size: .72rem; font-weight: 400; color: var(--text-faint); margin-left: 2px; }
.lc-save-chip { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  background: #dcfce7; color: #166534; font-size: .68rem; font-weight: 700; text-transform: none;
  letter-spacing: 0; }
.lc-alloc__price, .lc-alloc__sub { white-space: nowrap; }
.lc-alloc__sub { font-weight: 700; }
.lc-totals { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; margin-top: 18px; }
.lc-total { border: 2px solid var(--border); border-radius: 10px; padding: 12px 14px; cursor: pointer; }
.lc-total.is-on { border-color: var(--primary); background: var(--accent-soft, #eef2ff); }
.lc-total__k { display: block; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); }
.lc-total__v { display: block; font-size: 1.5rem; font-weight: 800; }
.lc-total__v small { font-size: 0.8rem; font-weight: 500; color: var(--text-muted); }
.lc-total__note, .lc-saving { display: block; font-size: 0.78rem; color: var(--text-muted); }
.lc-saving { color: #16a34a; font-weight: 700; }
.lc-min-note { align-self: center; margin-right: 10px; font-size: .8rem; font-weight: 600; color: var(--danger, #b91c1c); }
.lc-summary { margin-top: 12px; font-size: 0.84rem; color: var(--text-muted); }
.lc-empty { margin-top: 16px; color: var(--text-muted); font-size: 0.88rem; }
@media (max-width: 640px) { .lc-alloc thead { display: none; } }

.lc-current { display: flex; flex-wrap: wrap; gap: .3rem 1.4rem; align-items: center; margin-top: 14px;
  padding: 10px 12px; border: 1px solid var(--app-filter-border); border-radius: 9px; background: var(--app-filter-bg); }

/* ── Billing hero: the money question, answered above everything else ────────── */
.bill-hero { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 28px; margin-bottom: 18px;
  padding: 18px 20px; border: 1px solid var(--border); border-radius: 14px; background: var(--bg-surface); }
.bill-hero__main { flex: 1 1 260px; min-width: 0; }
.bill-hero__eyebrow { display: block; font-size: .72rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-faint); }
.bill-hero__title { margin: 2px 0 4px; font-size: 1.25rem; font-weight: 800; }
.bill-hero__meta { margin: 0; font-size: .86rem; color: var(--text-muted); }
.bill-hero__next { flex: 0 0 auto; padding: 10px 16px; border-radius: 10px; background: var(--app-filter-bg);
  border: 1px solid var(--app-filter-border); }
.bill-hero__k { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-faint); }
.bill-hero__amount { display: block; font-size: 1.45rem; font-weight: 800; line-height: 1.2; }
.bill-hero__actions { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 8px; }
.bill-hero__actions button[hidden] { display: none; }
.bill-bands { width: 100%; border-collapse: collapse; font-size: .88rem; margin: 6px 0 10px; }
.bill-bands th, .bill-bands td { border-bottom: 1px solid var(--border); padding: 8px 10px; text-align: left; }
.bill-bands thead th { font-size: .72rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); }
.bill-bands__sub { font-weight: 700; }
/* Card title left, its one action right — the CTA sits on the thing it acts on rather than
   only in the hero's button row, where it was a third small button between two ghosts. */
.bill-card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.bill-card__head .card-title { margin: 0; }
.bill-card__head button[hidden] { display: none; }
@media (max-width: 620px) { .bill-hero { flex-direction: column; align-items: stretch; } }

/* Coupon section (Plans page + in-app Billing — shared markup). */
.coupon-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.coupon-input {
  flex: 1 1 200px;
  min-width: 160px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
}
.coupon-status:empty {
  display: none;
}

/* ── Billing page: org picker (orange accent), rich tables, status pills ──── */
.billing-org-picker {
  margin: 0 0 14px;
}
.billing-org-picker__label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-subtle);
}
.billing-org-picker__label select {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
}
/* Highlighted while a coupon is being redeemed/requested, so the target org is unmistakable. */
.billing-org-picker--accent .billing-org-picker__label {
  color: var(--warn, #b45309);
}
.billing-org-picker--accent .billing-org-picker__label select {
  border-color: var(--warn, #b45309);
  box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.16);
  background: var(--warn-bg, #fef3c7);
}
/* The org field inside the "Have a coupon?" card — always visible so the target is explicit. */
.billing-coupon-org {
  margin: 0 0 10px;
}
.coupon-org__label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-subtle);
}
.coupon-org__label select {
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
}
.billing-coupon-org.billing-org-picker--accent .coupon-org__label {
  color: var(--warn, #b45309);
}
.billing-coupon-org.billing-org-picker--accent .coupon-org__label select {
  border-color: var(--warn, #b45309);
  box-shadow: 0 0 0 3px rgba(180, 83, 9, 0.16);
  background: var(--warn-bg, #fef3c7);
}
/* Two-column-spanning cards for the wide invoice + coupon tables. */
.card--wide {
  grid-column: 1 / -1;
}
.table-wrap {
  overflow-x: auto;
}
.billing-table {
  min-width: 640px;
}
.billing-table code {
  font-size: 0.8rem;
}
.billing-table .ta-right,
th.ta-right,
td.ta-right {
  text-align: right;
}
/* A highlighted "amount due" row when the org is grace/disabled. */
.billing-row--due td {
  background: var(--warn-bg, #fef3c7);
  color: var(--warn-fg, #b45309);
  font-weight: 600;
}
/* Status pills (paid/open/pending/void/disabled + coupon effects). */
.billing-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: capitalize;
  line-height: 1.5;
  background: var(--bg-hover);
  color: var(--text-subtle);
}
.billing-pill--ok {
  background: rgba(22, 128, 61, 0.14);
  color: #15803d;
}
.billing-pill--warn {
  background: rgba(180, 83, 9, 0.16);
  color: #b45309;
}
.billing-pill--bad {
  background: rgba(190, 40, 40, 0.14);
  color: #be2828;
}
.billing-pill--muted {
  background: var(--bg-hover);
  color: var(--text-faint, var(--text-subtle));
}

/* =========================================================================
   Dark theme — applied via [data-theme="dark"] on <html>. The bootstrap
   <script> in index.html sets the attribute before paint, so there's no
   flash-of-light when the user prefers dark.
   ========================================================================= */
html[data-theme='dark'] {
  /* Google's dark surfaces: one elevation step per level, not a single flat black. */
  --bg-app: #0f172a;      /* the signed-out page's navy — slate family, not Google black (2026-08-20) */
  --bg-surface: #1e293b;  /* cards on the cookie-banner slate, one step off the navy body */
  --bg-canvas: #0f172a;
  --bg-muted: #334155;
  --bg-hover: #33415e;

  --border: #475569;
  --border-strong: #64748b;
  --border-muted: #334155;

  --text: #e8eaed;        /* 11.9:1 on the surface */
  --text-muted: #bdc1c6;  /* 7.9:1 */
  --text-subtle: #9aa0a6; /* 5.4:1 */
  --text-faint: #9aa0a6;

  --accent: #e8eaed;
  --accent-soft: #35363a;
  --brand: #bdc1c6;
  --brand-hover: #e8eaed;
  /* Blue 300. A saturated blue on a dark surface fails AA, which is why Material
     lightens the primary in dark rather than reusing the light-mode one. */
  --primary: #8ab4f8;
  --primary-hover: #aecbfa;
  --primary-ink: #202124; /* dark ink on the light blue — white would fail here */
  --focus-ring: rgba(138, 180, 248, 0.35);

  /* Soft status washes, inverted: a tint of the status colour ON the dark surface,
     not a dimmed pale one. */
  /* The soft blue tints, inverted: a tint OF the accent on a dark surface. */
  --primary-50: #1e2a3d;
  --unread: #232a35;

  --danger-soft: #3b2222;
  --danger-bg: #3b2222;
  --warn-bg: #3a3320;
  --ok-soft: #1e3226;

  /* Matched to light by RATIO against the surface, not picked by eye: light's divider
     sits at 1.21 and its stripe at 1.13 against white, and these land on 1.21 / 1.13
     against #202124. The previous values were tuned when the module surface was still
     #292a2d; once it became #202124 the same greys read as heavy banding.
     The header inverts rather than repeats: a dark slate header is high contrast on
     white and all but invisible on a dark surface (1.10), so in dark it LIFTS. */
  --table-stripe: #292a30;
  --table-stripe-soft: #26272b;
  --table-divider: #2e2f35;
  --table-row-hover: #33363f;
  --table-head-bg: #31343b;
  --table-head-fg: #e8eaed;
  --table-divider-raised: #37383f; /* 1.23 on the #292a2d card — matches 1.21 on the page */
  --wash-ink: rgba(232, 234, 237, 0.08);
  --wash-ink-strong: rgba(232, 234, 237, 0.12);
  --wash-primary: rgba(138, 180, 248, 0.16);
  --crumb-sep: #80868b;
  --search-pill: #2b2f36;
  --search-pill-hover: #333840;

  /* Status, lightened the same way and for the same reason. */
  --success: #81c995;
  --danger: #f28b82;
  --warn: #fdd663;

  /* The themeable header/filter bands. In dark these follow the surface rather
     than staying brand-blue — a saturated bar under a dark UI reads as an error. */
  --app-header-bg: #202124;
  --app-header-text: #e8eaed;
  --app-toolbar-bg: #202124;
  --app-filter-bg: #292a2d;
  --app-filter-border: #3c4043;

  /* Layout zones (dark): content darkest, chrome lifts off it. */
  --zone-nav: var(--zone-rail);   /* tracks the rail tone -- see the light block's note */
  /* The first rail sits on the CONTENT tone, not a lifted one: in light a white rail
     against a grey canvas reads as chrome, but in dark a lighter slab down the left
     edge reads as a panel floating over the page. */
  --zone-rail: #0f172a;
  --orgbrand-ink: #a78bfa;   /* the violet's dark-band counterpart — 5.92:1 on this rail tone */
  --zone-content: #0f172a;
  /* A LIGHT hairline (2026-08-18, UX pass): the old #212226 sat ONE point off the #202124
     rail — an invisible seam, so the dark zones ran together. Dark surfaces divide with light
     lines (the inverse of light mode's darker-than-both rule); .08 white reads on the rail
     AND on the #292a2d body without glowing. */
  --zone-divider: rgba(255, 255, 255, 0.08);

}


html[data-theme='dark'] body {
  background: var(--bg-app);
  color: var(--text);
}

html[data-theme='dark'] ::selection {
  background: rgba(229, 231, 235, 0.18);
}

/* =========================================================================
   Accessibility classes — applied to <html>
   ========================================================================= */
html.pref-reduced-motion *,
html.pref-reduced-motion *::before,
html.pref-reduced-motion *::after {
  animation-duration: 0.001ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important;
  scroll-behavior: auto !important;
}

html.pref-larger-text body {
  font-size: 15.5px;
}

/* Darker text — deepen the primary + secondary text colours for easier reading. */
html.pref-darker-text {
  --text: #020617;
  --text-muted: #1e293b;
  --text-subtle: #334155;
}
html.pref-darker-text[data-theme='dark'] {
  --text: #ffffff;
  --text-muted: #f1f5f9;
  --text-subtle: #e2e8f0;
}

html.pref-high-contrast {
  --border: #4b5563;
  --border-strong: #1f2937;
  --text-muted: #1f2937;
  --text-subtle: #1f2937;
}
html.pref-high-contrast[data-theme='dark'] {
  --border: #cbd5e1;
  --border-strong: #f8fafc;
  --text-muted: #f1f5f9;
  --text-subtle: #e2e8f0;
}

/* =========================================================================
   Preferences page
   ========================================================================= */

.prefs-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  margin-top: 8px;
}

@media (min-width: 720px) {
  .prefs-layout {
    grid-template-columns: repeat(2, 1fr);
  }
}

.prefs-section {
  padding: 18px 20px;
}

.prefs-section > .card-title {
  margin-top: 0;
}

.prefs-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 14px;
  margin-top: 12px;
}

@media (max-width: 520px) {
  .prefs-row {
    grid-template-columns: 1fr;
  }
}

.prefs-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.78rem;
  color: var(--text-muted);
  font-weight: 500;
  margin: 0;
}

.prefs-field > span {
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* Header & filter colour pickers (Appearance → Header & filter colours). */
.prefs-section[data-prefs-section="header-colors"] .prefs-row { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 640px) { .prefs-section[data-prefs-section="header-colors"] .prefs-row { grid-template-columns: 1fr 1fr; } }
.prefs-field--color > input[type="color"] {
  width: 100%; height: 34px; padding: 2px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-surface); cursor: pointer;
}
.prefs-colors-actions { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 14px; }
/* Live preview — reads the same global vars applyThemeColors() sets, so it updates live. */
.prefs-colors-preview { display: inline-flex; align-items: stretch; border-radius: 9px; overflow: hidden; border: 1px solid var(--border); min-width: 220px; }
.prefs-colors-preview__header { display: inline-flex; align-items: center; padding: 6px 12px; font-size: 12px; font-weight: 650; background: var(--app-header-bg); color: var(--app-header-text); }
.prefs-colors-preview__filter { display: inline-flex; align-items: center; padding: 6px 12px; font-size: 12px; font-weight: 500; color: var(--text); background: var(--app-filter-bg); border-left: 1px solid var(--app-filter-border); flex: 1; }

/* The global `label { margin: 12px 0 6px }` would space these out — neutralize. */
.prefs-section .prefs-field {
  margin-top: 0;
}

.prefs-theme-options {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
  margin-top: 12px;
}

.prefs-theme-option {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 12px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  margin: 0;
}

.prefs-theme-option:hover {
  border-color: var(--border-strong);
}

.prefs-theme-option input[type='radio'] {
  position: absolute;
  opacity: 0;
  pointer-events: none;
  width: 0;
  height: 0;
}

.prefs-theme-option:has(input[type='radio']:checked) {
  border-color: #1e6fbf;
  box-shadow: 0 0 0 3px rgba(34, 139, 230, 0.18);
}

.prefs-theme-swatch {
  width: 100%;
  height: 56px;
  border-radius: 8px;
  border: 1px solid var(--border);
  position: relative;
  overflow: hidden;
}

.prefs-theme-swatch-system {
  background: linear-gradient(135deg, #fafafa 0 50%, #14171c 50% 100%);
}

.prefs-theme-swatch-light {
  background: #fafafa;
}
.prefs-theme-swatch-light::after {
  content: '';
  position: absolute;
  inset: 8px 8px auto 8px;
  height: 8px;
  background: #e5e7eb;
  border-radius: 4px;
}

.prefs-theme-swatch-dark {
  background: #14171c;
}
.prefs-theme-swatch-dark::after {
  content: '';
  position: absolute;
  inset: 8px 8px auto 8px;
  height: 8px;
  background: #2a3038;
  border-radius: 4px;
}

.prefs-theme-name {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
}

.prefs-toggle {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 0;
  border-top: 1px solid var(--border-muted);
  cursor: pointer;
  margin: 0;
}

.prefs-toggle:first-of-type {
  border-top: none;
  padding-top: 14px;
}

.prefs-toggle input[type='checkbox'] {
  width: auto;
  margin: 3px 0 0;
  padding: 0;
  flex: 0 0 auto;
  accent-color: var(--accent, #1e6fbf);
}

.prefs-toggle-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.prefs-toggle-body strong {
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--text);
}

.prefs-toggle-body small {
  font-size: 0.76rem;
  color: var(--text-subtle);
  line-height: 1.45;
}

.prefs-toggle-locked {
  opacity: 0.7;
  cursor: not-allowed;
}

/* Small "Answered with X" line shown above the assistant bubble when the
   backend auto-fell-back to a different model (e.g. SMAIVIZ Pro → Flash on
   503). Intentionally subtle — it's an info breadcrumb, not a status. */
.chat-message.assistant-meta {
  margin: 4px 0 -4px;          /* nest tight against the bubble below */
  padding: 0 4px;
  background: transparent;
  border: none;
  color: var(--text-faint);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
}

.chat-message.assistant-meta .message-text {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  line-height: 1.4;
}

/* Guardrail system-notice bubble inside the chat transcript. Less prominent
   than an assistant/user message — it's metadata, not part of the dialogue. */
.chat-message.system {
  background: rgba(34, 139, 230, 0.05);
  border: 1px dashed rgba(34, 139, 230, 0.3);
  color: var(--text-muted);
  font-size: 0.72rem;
  line-height: 1.4;
  padding: 5px 8px;
  border-radius: 6px;
  margin: 4px 0;
  letter-spacing: 0.005em;
}

.chat-message.system .message-role {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #1e6fbf;
  margin-bottom: 2px;
}

/* Sticky strip above the transcript that shows the most recent user
   question (Claude-style chat title). Small, subtle, stays visible on
   scroll so the user can always see what they're asking about. */
.chat-active-question {
  position: sticky;
  top: 0;
  z-index: 2;
  margin: 0 0 6px;
  padding: 6px 10px;
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  color: var(--text-subtle);
  font-size: 0.74rem;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Diagnosis card — structured remediation rendered in place of a plain
   "Issue: …" string when /chat embeds a `diagnosis` block in the error
   response. Severity drives the accent color (info / warning / error).
   Pattern follows Stripe / Linear / Vercel inline-error cards. */
.chat-message.diagnosis {
  padding: 14px 16px;
  border-radius: 12px;
  border: 1px solid rgba(204, 0, 0, 0.22);
  background: rgba(204, 0, 0, 0.04);
  margin: 10px 0;
  max-width: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.chat-message.diagnosis-info {
  border-color: rgba(30, 111, 191, 0.28);
  background: rgba(30, 111, 191, 0.05);
}

.chat-message.diagnosis-warning {
  border-color: rgba(204, 102, 0, 0.28);
  background: rgba(204, 102, 0, 0.05);
}

.chat-message.diagnosis-error {
  border-color: rgba(204, 0, 0, 0.28);
  background: rgba(204, 0, 0, 0.05);
}

.diagnosis-header {
  display: flex;
  align-items: center;
  gap: 8px;
}

.diagnosis-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  font-size: 0.95rem;
  border-radius: 50%;
  flex-shrink: 0;
}

.chat-message.diagnosis-error .diagnosis-icon {
  color: #a30000;
  background: rgba(204, 0, 0, 0.12);
}

.chat-message.diagnosis-warning .diagnosis-icon {
  color: #8a4b00;
  background: rgba(204, 102, 0, 0.14);
}

.chat-message.diagnosis-info .diagnosis-icon {
  color: #1e6fbf;
  background: rgba(30, 111, 191, 0.14);
}

.diagnosis-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  line-height: 1.3;
}

.diagnosis-explanation {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--text-muted);
}

.diagnosis-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}

.diagnosis-action {
  display: inline-flex;
  align-items: center;
  padding: 7px 14px;
  border-radius: 7px;
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  color: var(--text);
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.diagnosis-action:hover {
  background: var(--bg-muted);
  border-color: var(--border-strong, rgba(0, 0, 0, 0.15));
}

.diagnosis-action.primary {
  background: #1e6fbf;
  border-color: #1e6fbf;
  color: #fff;
}

.diagnosis-action.primary:hover {
  background: #154e8f;
  border-color: #154e8f;
  color: #fff;
}

.diagnosis-docs {
  font-size: 0.78rem;
  color: var(--text-muted);
  text-decoration: none;
  margin-top: 2px;
}

.diagnosis-docs:hover {
  color: var(--text);
  text-decoration: underline;
}

/* Safety profile radios use the same .prefs-toggle styling as the
   accessibility checkboxes. The locked note has its own muted style. */
.prefs-safety-options {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin-top: 8px;
}

.prefs-safety-locked {
  margin-top: 12px;
  padding: 8px 10px;
  background: rgba(180, 83, 9, 0.08);
  border: 1px solid rgba(180, 83, 9, 0.25);
  border-radius: 8px;
  color: #92400e;
  font-size: 0.78rem;
}

/* Age confirmation banner — shows above the preferences grid when the user's
   age band is "unknown" (legacy account pre-migration). */
.prefs-age-banner {
  margin-top: 4px;
  padding: 14px 16px;
  background: rgba(34, 139, 230, 0.08);
  border: 1px solid rgba(34, 139, 230, 0.25);
  border-radius: 10px;
}

.prefs-age-banner strong {
  display: block;
  margin-bottom: 4px;
  font-size: 0.95rem;
  color: var(--text);
}

.prefs-age-banner p {
  margin: 0 0 10px;
  font-size: 0.82rem;
  color: var(--text-muted);
}

.prefs-age-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}


.tb-selector-empty {
  background: var(--bg-muted);
}

.tb-selector-empty .tb-value {
  color: var(--text-faint);
  font-style: italic;
}


/* Long-form documentation pages */

.doc-article {
  max-width: 760px;
  margin: 0 auto;
  padding-bottom: 40px;
  color: var(--text);
  font-size: 0.92rem;
  line-height: 1.65;
}

.doc-article h2 {
  margin: 32px 0 10px;
  font-size: 1.1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}

.doc-article h3 {
  margin: 16px 0 6px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.doc-article p {
  margin: 8px 0;
  color: var(--text-muted);
}

.doc-article .lead {
  margin: 4px 0 18px;
  font-size: 1rem;
  color: var(--text);
}

.doc-article code {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--bg-muted);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.82rem;
}

.doc-code {
  display: block;
  padding: 12px 16px;
  margin: 12px 0;
  background: var(--bg-muted);
  border: 1px solid var(--border-muted);
  border-radius: 8px;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text);
  overflow-x: auto;
}

.doc-list {
  margin: 8px 0 16px;
  padding-left: 24px;
  color: var(--text-muted);
}

.doc-list li {
  margin: 6px 0;
}

.doc-list strong {
  color: var(--text);
}

.doc-callout {
  margin: 18px 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  background: var(--bg-muted);
  color: var(--text-muted);
  font-size: 0.86rem;
}

.doc-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 24px 0 0;
}

.doc-cta .ghost-button {
  margin-top: 0;
}

/* FAQ accordion */

.faq-item {
  border-bottom: 1px solid var(--border-muted);
}

.faq-item:last-of-type {
  border-bottom: 0;
}

.faq-item summary {
  padding: 14px 0;
  cursor: pointer;
  color: var(--text);
  font-weight: 500;
  font-size: 0.95rem;
  list-style: none;
}

.faq-item summary::-webkit-details-marker {
  display: none;
}

.faq-item summary::before {
  content: '+';
  display: inline-block;
  width: 18px;
  margin-right: 4px;
  color: var(--text-subtle);
  font-weight: 500;
  transition: transform 0.15s ease;
}

.faq-item[open] summary::before {
  content: '−';
}

.faq-item p {
  margin: 0 0 14px 22px;
  color: var(--text-muted);
}

/* Security feature grid */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin: 12px 0 18px;
}

.feature-card {
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
}

.feature-card h3 {
  margin: 0 0 6px;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
}

.feature-card p {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.55;
}

/* ── Blog index: responsive tile grid ─────────────────────────────────────── */

.blog-index-body {
  max-width: 1140px;
  margin: 0 auto;
  padding: 10px 24px 56px;
}

.blog-intro {
  margin: 4px 0 26px;
  max-width: 720px;
  color: var(--text-muted);
  font-size: 0.98rem;
  line-height: 1.6;
}

.blog-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 22px;
  align-items: stretch;
}

/* A card is a tile (the <a> is the whole card). */
a.blog-card {
  display: flex;
  flex-direction: column;
  min-height: 232px;
  padding: 22px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

a.blog-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

a.blog-card:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 2px;
}

.blog-meta {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.78rem;
  color: var(--text-subtle);
}

.blog-tag {
  display: inline-block;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--bg-muted);
  border: 1px solid var(--border-muted);
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.blog-card h2 {
  margin: 14px 0 8px;
  font-size: 1.2rem;
  font-weight: 650;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.blog-excerpt {
  margin: 0 0 16px;
  color: var(--text-muted);
  font-size: 0.92rem;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.blog-footer {
  margin: auto 0 0;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text);
}

a.blog-card:hover .blog-footer {
  text-decoration: underline;
}

/* Featured hero tile — the first post spans the full row on desktop. No extra
   class (test pins class="blog-card" exactly); driven purely by :first-child. */
.blog-grid > a.blog-card:first-child {
  grid-column: 1 / -1;
  min-height: 0;
  padding: 30px 32px;
  background:
    radial-gradient(120% 140% at 100% 0%, var(--bg-muted) 0%, transparent 60%),
    var(--bg-surface);
}

.blog-grid > a.blog-card:first-child h2 {
  font-size: 1.85rem;
  line-height: 1.18;
  letter-spacing: -0.02em;
  -webkit-line-clamp: 3;
  max-width: 22ch;
}

.blog-grid > a.blog-card:first-child .blog-excerpt {
  font-size: 1.02rem;
  -webkit-line-clamp: 4;
  max-width: 70ch;
}

/* ── Single post: industry-standard reading layout ────────────────────────── */
.blog-post {
  max-width: 720px;
  margin: 0 auto;
  padding: 0 24px 8px;
}

.blog-post-back {
  display: inline-block;
  margin-bottom: 22px;
  font-size: 0.85rem;
  color: var(--text-subtle);
  text-decoration: none;
}

.blog-post-back:hover {
  color: var(--text);
  text-decoration: underline;
}

/* Eyebrow (the tag row sits above the title as a category eyebrow). */
.blog-post > .blog-meta {
  margin-bottom: 14px;
}

.blog-post h1 {
  margin: 0 0 18px;
  font-size: 2.2rem;
  font-weight: 700;
  letter-spacing: -0.022em;
  line-height: 1.14;
  color: var(--text);
}

/* Standfirst / deck. */
.blog-lead {
  font-size: 1.24rem;
  line-height: 1.55;
  color: var(--text-muted);
  margin: 0 0 8px;
  font-weight: 400;
}

.blog-post h2 {
  margin: 40px 0 12px;
  font-size: 1.5rem;
  font-weight: 650;
  letter-spacing: -0.01em;
  line-height: 1.25;
  color: var(--text);
}

.blog-post h3 {
  margin: 26px 0 8px;
  font-size: 1.16rem;
  font-weight: 650;
  color: var(--text);
}

.blog-post p,
.blog-post li {
  color: var(--text);
  font-size: 1.06rem;
  line-height: 1.78;
}

.blog-post p {
  margin: 0 0 20px;
}

.blog-post ul {
  margin: 16px 0 22px 0;
  padding-left: 22px;
}

.blog-post li {
  margin-bottom: 10px;
  padding-left: 4px;
}

.blog-post em {
  color: var(--text-muted);
}

.blog-table {
  width: 100%;
  border-collapse: collapse;
  margin: 18px 0 26px;
  font-size: 0.95rem;
}

.blog-table th,
.blog-table td {
  border: 1px solid var(--border-muted);
  padding: 10px 12px;
  text-align: left;
  vertical-align: top;
  color: var(--text-muted);
}

.blog-table th {
  background: var(--bg-muted);
  color: var(--text);
  font-weight: 600;
}

/* Closing CTA / promo strip. */
.blog-promo {
  margin: 36px 0 28px;
  padding: 16px 20px;
  background: var(--bg-muted);
  border: 1px solid var(--border-muted);
  border-radius: var(--radius-md);
  font-size: 0.9rem;
  color: var(--text-subtle);
}

/* "Keep reading" — related posts as a card row. */
.blog-related {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 30px;
  padding-top: 26px;
  border-top: 1px solid var(--border);
}

.blog-related > span {
  flex-basis: 100%;
  margin-bottom: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-subtle);
}

.blog-related a {
  flex: 1 1 200px;
  padding: 14px 16px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}

.blog-related a::after {
  content: " →";
  color: var(--text-faint);
}

.blog-related a:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

/* Blog media (figures / inline SVG illustrations) */
.blog-figure {
  margin: 22px 0;
  text-align: center;
}

.blog-figure svg,
.blog-figure img {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  color: var(--text-subtle);
  border: 1px solid var(--border-muted);
  border-radius: 10px;
  background: var(--bg-muted);
  padding: 18px;
}

.blog-figure figcaption {
  margin-top: 8px;
  font-size: 0.8rem;
  line-height: 1.5;
  color: var(--text-faint);
  font-style: italic;
}

/* Blog reader Q&A — native <details> accordion, no JS */
.blog-faq {
  margin: 26px 0 8px;
}

.blog-faq details {
  border-bottom: 1px solid var(--border-muted);
  padding: 2px 0;
}

.blog-faq details:first-of-type {
  border-top: 1px solid var(--border-muted);
}

.blog-faq summary {
  list-style: none;
  cursor: pointer;
  padding: 12px 28px 12px 0;
  position: relative;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
}

.blog-faq summary::-webkit-details-marker {
  display: none;
}

.blog-faq summary::after {
  content: "+";
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 1.1rem;
  font-weight: 400;
  color: var(--text-subtle);
}

.blog-faq details[open] summary::after {
  content: "−";
}

.blog-faq summary:hover {
  color: var(--text);
  text-decoration: underline;
}

.blog-faq details p {
  margin: 0 0 14px;
  padding-right: 8px;
}

/* Hero figure — the first illustration in a post reads as the lead image. */
.blog-post > .blog-figure:first-of-type {
  margin: 26px 0 30px;
}

.blog-post > .blog-figure:first-of-type svg,
.blog-post > .blog-figure:first-of-type img {
  padding: 26px;
  border-radius: var(--radius-lg);
}

/* ── Blog responsive ──────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .blog-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .blog-grid > a.blog-card:first-child h2 {
    font-size: 1.55rem;
  }
}

@media (max-width: 520px) {
  .blog-index-body {
    padding: 8px 16px 40px;
  }
  .blog-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  /* Featured tile collapses to an ordinary tile on mobile. */
  .blog-grid > a.blog-card:first-child {
    grid-column: auto;
    padding: 22px;
  }
  .blog-grid > a.blog-card:first-child h2 {
    font-size: 1.2rem;
  }
  .blog-grid > a.blog-card:first-child .blog-excerpt {
    font-size: 0.92rem;
  }
  .blog-post {
    padding: 0 16px 8px;
  }
  .blog-post h1 {
    font-size: 1.7rem;
  }
  .blog-lead {
    font-size: 1.1rem;
  }
  .blog-post h2 {
    font-size: 1.3rem;
  }
  .blog-post p,
  .blog-post li {
    font-size: 1.02rem;
  }
}

/* ── Colorful per-category accents — drawings, panels, chips ───────────────── */
/* Every SVG in a post draws with currentColor, so one --accent recolors the whole
   illustration; --accent2 adds a second hue to the figure's gradient panel. Set on
   both the article (#post-<slug>) and its index tile (a.blog-card[href$=...]). */
#post-automate-your-busywork,            a.blog-card[href$="/automate-your-busywork"]            { --accent: #7c3aed; --accent2: #ec4899; }
#post-plan-and-track-your-work,          a.blog-card[href$="/plan-and-track-your-work"]          { --accent: #2563eb; --accent2: #06b6d4; }
#post-every-customer-in-one-place,       a.blog-card[href$="/every-customer-in-one-place"]       { --accent: #0d9488; --accent2: #3b82f6; }
#post-run-your-marketing-with-ai,        a.blog-card[href$="/run-your-marketing-with-ai"]        { --accent: #db2777; --accent2: #1d4ed8; }
#post-one-clear-customer-not-twelve,     a.blog-card[href$="/one-clear-customer-not-twelve"]     { --accent: #0891b2; --accent2: #6366f1; }
#post-one-team-people-and-ai,            a.blog-card[href$="/one-team-people-and-ai"]            { --accent: #d97706; --accent2: #f43f5e; }
#post-keep-proper-books-without-the-dread, a.blog-card[href$="/keep-proper-books-without-the-dread"] { --accent: #059669; --accent2: #65a30d; }
#post-your-own-ai-assistant,             a.blog-card[href$="/your-own-ai-assistant"]             { --accent: #9333ea; --accent2: #2563eb; }
#post-one-platform-for-your-business,    a.blog-card[href$="/one-platform-for-your-business"]    { --accent: #1d4ed8; --accent2: #7c3aed; }
#post-keep-a-human-in-control,           a.blog-card[href$="/keep-a-human-in-control"]           { --accent: #e11d48; --accent2: #f59e0b; }
#post-your-data-stays-accountable,       a.blog-card[href$="/your-data-stays-accountable"]       { --accent: #4f46e5; --accent2: #0ea5e9; }
#post-getting-started-in-your-first-week, a.blog-card[href$="/getting-started-in-your-first-week"] { --accent: #16a34a; --accent2: #14b8a6; }

/* Line-art draws in the post accent; the panel stays a clean, neutral canvas so
   the colour lives in the shapes themselves (each SVG also fills key shapes with
   --accent / --accent2 for a multi-colour look). */
.blog-figure svg {
  color: var(--accent, var(--text-subtle));
}

.blog-figure svg,
.blog-figure img {
  background: var(--bg-surface);
  border-color: var(--border-muted);
}

/* Two-tone drawings: lines + text keep the accent (currentColor), while filled
   shapes take the second hue. Scoped to shape primitives so text labels stay on
   the readable accent. No SVG markup changes needed. */
.blog-figure svg circle[fill="currentColor"],
.blog-figure svg rect[fill="currentColor"],
.blog-figure svg path[fill="currentColor"],
.blog-figure svg polygon[fill="currentColor"],
.blog-figure svg polyline[fill="currentColor"],
.blog-figure svg ellipse[fill="currentColor"] {
  fill: var(--accent2, var(--accent));
}

/* Colored category chips — on index tiles and the article eyebrow. */
a.blog-card .blog-tag,
.blog-post > .blog-meta .blog-tag {
  color: var(--accent, var(--text-muted));
  background: color-mix(in srgb, var(--accent, var(--text-muted)) 12%, var(--bg-surface));
  border-color: color-mix(in srgb, var(--accent, var(--text-muted)) 38%, var(--border-muted));
}

/* "Read more →" and the hover border pick up the accent on tiles. */
a.blog-card .blog-footer {
  color: var(--accent, var(--text));
}

a.blog-card:hover {
  border-color: color-mix(in srgb, var(--accent, var(--text)) 45%, var(--border));
}

/* Personal workspace (Agents / Routines / Tasks) — three collapsible
   <details> blocks acting as an exclusive accordion (HTML `name=` attribute
   in modern browsers; JS fallback in personal_workspace.js for older ones).
   All three start collapsed; expanding one closes the other two. */
.personal-group {
  border-bottom: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.04));
}

.personal-group:last-child {
  border-bottom: none;
}

.personal-group-summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  cursor: pointer;
  user-select: none;
  font-size: 0.85rem;
  color: var(--text-color);
  border-radius: 4px;
  /* The Tools group's summary is an <a> (not a <details> summary) so right-
     click "Open in new tab" works on the Tools entry; strip the default
     anchor underline so it matches the other group summaries. */
  text-decoration: none;
}

.personal-group-summary::-webkit-details-marker {
  display: none;
}

.personal-group-summary:hover {
  background: var(--surface-muted, rgba(255, 255, 255, 0.04));
}

.personal-group-chevron {
  display: inline-block;
  transition: transform 0.15s ease;
  color: var(--text-muted);
  font-size: 1.3rem;
  line-height: 1;
}

details[open] > .personal-group-summary .personal-group-chevron {
  transform: rotate(90deg);
}

.personal-group-label {
  font-weight: 500;
}

.personal-group-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 2px 0 4px;
}

/* "Tools" is a navigation menu, not a collapsible group: a button styled like
   a group summary. No chevron rotation (nothing expands); active = highlighted. */
.personal-group-link {
  width: 100%;
  background: none;
  border: 0;
  text-align: left;
  font: inherit;
}

.personal-group-link.active {
  background: var(--accent-soft);
  font-weight: 600;
}

/* ─── Tools page (icon grid + connect dialog, 2026-05-31) ────────────────────
   v1 was a vertical list with id + description + a runtime badge. v2 is
   an icon-led grid: utilities and connectors get distinct sections, each
   card is a button (clickable for connectors, decorative for utilities),
   and a Connect dialog drives the OAuth / MCP setup per-tool. Prompt-
   only entries are hidden — they shape an Agent's system prompt, not
   the user's capability inventory. */

.tools-catalog {
  max-width: 960px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.tools-section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 0 0 10px;
}
.tools-section__head h2 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}
.tools-section__head p {
  margin: 0;
}

.tools-grid {
  display: grid;
  /* Auto-fit ~140px-wide cards — gives 3-6 per row on a typical viewport
     without needing per-breakpoint media queries. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}

/* "Connected" section (top of Tools page, Notion / Zapier pattern).
   One row per active connection with provider icon + account email +
   relative connected-at + a Disconnect button. Only renders when the
   user has at least one connection — a fresh user sees the catalogue
   first + the section appears once they've connected. */
.tools-section--connected {
  /* Subtle accent so the active connections stand apart from the
     "browse the catalogue" sections below. */
  background: var(--bg-muted);
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid var(--border);
}
.connected-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.connected-row {
  display: grid;
  grid-template-columns: 32px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.connected-row__icon {
  font-size: 1.3rem;
  line-height: 1;
  text-align: center;
}
.connected-row__title {
  font-weight: 600;
  font-size: 0.92rem;
}
.connected-row__sub {
  margin-top: 2px;
}
.connected-row__actions {
  display: flex;
  align-items: center;
  gap: 6px;
}
.connected-row__reconnect {
  /* Reconnect = the "soft" action, primary affordance for someone who
     sees an expiry warning. Sits to the left of Disconnect so the
     destructive action stays at the right edge. */
  text-decoration: none;
  color: var(--text);
}
.connected-row__reconnect:hover {
  background: var(--bg-muted);
}
.connected-row__disconnect {
  /* Disconnect is destructive but not dramatic — danger styling would
     scare a user who's just trying to switch accounts. Muted by default;
     hover reveals the red. */
  color: var(--text-subtle);
  border-color: var(--border);
}
.connected-row__disconnect:hover {
  color: #b91c1c;
  border-color: #b91c1c;
  background: rgba(254, 226, 226, 0.4);
}
.connected-row__disconnect:disabled,
.connected-row__reconnect.is-disabled {
  opacity: 0.5;
  cursor: progress;
}

/* Vault connection groups (WhatsApp/Instagram/… — one group per provider, each
   listing every saved connection by name, with an "+ Add another" affordance). */
.connected-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.connected-group + .connected-group {
  margin-top: 8px;
}
.connected-group__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 2px;
}
.connected-group__name {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.connected-row__delete {
  /* Same restrained-destructive treatment as Disconnect. */
  color: var(--text-subtle);
  border-color: var(--border);
}
.connected-row__delete:hover {
  color: #b91c1c;
  border-color: #b91c1c;
  background: rgba(254, 226, 226, 0.4);
}
.connected-row__delete:disabled {
  opacity: 0.5;
  cursor: progress;
}

/* "Set up inbound" modal — route a WhatsApp number to an agent + show the Meta callback. */
.conn-inbound-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}
.conn-inbound-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.4);
}
.conn-inbound-card {
  position: relative;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 18px 20px;
  width: min(560px, calc(100vw - 32px));
  max-height: calc(100vh - 64px);
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.conn-inbound-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.conn-inbound-steps {
  margin: 0;
  padding-left: 18px;
  font-size: 0.85rem;
  color: var(--text-subtle);
}
.conn-inbound-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.85rem;
}
.conn-inbound-copy {
  display: flex;
  gap: 6px;
}
.conn-inbound-copy input {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-mono, monospace);
  font-size: 0.8rem;
}
.conn-inbound-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 6px;
}
.conn-inbound-ok {
  font-size: 0.88rem;
  color: #047857;
  background: #d1fadf;
  border-radius: 8px;
  padding: 8px 10px;
  margin: 0;
}
.conn-inbound-warn {
  font-size: 0.85rem;
  color: #92400e;
  background: #fef3c7;
  border-radius: 8px;
  padding: 8px 10px;
  margin: 0;
}

/* Expiry / health chip — inline next to the title. Yellow for the
   warning window (6–7 days); softer grey-yellow for "expired, please
   reconnect" (post-7-days). Tooltip carries the explanation so the
   chip itself stays short. */
.connected-row__chip {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 600;
  white-space: nowrap;
  vertical-align: middle;
}
.connected-row__chip.is-warn {
  /* Approaching / expired — warm tones drive the eye to take action. */
  background: #fef3c7;
  color: #92400e;
}
.connected-row__chip.is-soft {
  /* Days remaining — informative, not alarming. */
  background: var(--bg-muted);
  color: var(--text-subtle);
}

.tool-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 14px 10px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
  text-align: center;
  cursor: default;
  transition: background 0.12s ease, border-color 0.12s ease, transform 0.06s ease;
  /* Buttons inherit default focus styles via :focus-visible below. */
}
.tool-card--clickable {
  cursor: pointer;
}
.tool-card--clickable:hover {
  background: var(--bg-muted);
  border-color: var(--border-strong, var(--border));
}
.tool-card--clickable:active {
  transform: translateY(1px);
}
.tool-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.tool-card__icon {
  font-size: 1.8rem;
  line-height: 1;
}
/* Real brand logos on connector cards (connectorIcons.js) — fixed size so they sit
   consistently next to the emoji icons, in both the Tools grid + the Connected rows. */
.connector-logo {
  width: 26px;
  height: 26px;
  display: inline-block;
  vertical-align: middle;
}
.connected-row__icon .connector-logo {
  width: 22px;
  height: 22px;
}
.tool-card__name {
  font-weight: 600;
  font-size: 0.88rem;
}
/* ── Flux → Agents (gallery cards, inline builder, chat panel) ── */
.agents-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
.agent-card { padding: 0; overflow: hidden; }
.agent-card__open {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 100%; padding: 16px 10px 12px; border: 0; background: transparent; cursor: pointer; color: var(--text);
}
.agent-card__open:hover { background: var(--bg-hover); }
.agent-card__acts {
  display: flex; gap: 4px; justify-content: center; flex-wrap: wrap;
  padding: 8px; border-top: 1px solid var(--border);
}
.agent-builder { max-width: 760px; }
.agent-form-row { display: flex; gap: 12px; flex-wrap: wrap; }
.agent-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; color: var(--text-subtle); flex: 1 1 auto; }
.agent-field--icon { flex: 0 0 80px; }
.agent-field--grow { flex: 1 1 220px; }
.agent-field--check { flex-direction: row; align-items: center; gap: 8px; }
.agent-subhead { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-subtle); margin: 14px 0 2px; }
.agent-form-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; }
.agent-checklist { max-height: 220px; overflow-y: auto; }
/* Tools picker: each row is a tool + (for vault connectors) a connection sub-picker
   or a Connect badge when nothing's connected yet. */
.agent-tool { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
.agent-tool .iam-perm { flex: 1 1 auto; min-width: 0; }
.agent-tool .iam-perm.is-disabled { opacity: 0.55; cursor: not-allowed; }
.agent-tool-conn { flex: 0 0 auto; max-width: 48%; font-size: 0.8rem; }
.agent-tool-conn:disabled { opacity: 0.5; }
.agent-tool--gated .agent-tool-connect { flex: 0 0 auto; }
.agent-tool-inbound { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; font-size: 0.8rem; color: var(--text-subtle); white-space: nowrap; }
.agent-tool-inbound input:disabled { opacity: 0.5; }
.agent-tool-name { flex: 1 1 auto; min-width: 0; font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agent-tool-remove { flex: 0 0 auto; }
.agent-model-select { width: 100%; }

/* ── Multi-select dropdowns (Tools / Skills / Governance) ───────────────────────── */
.ms-field { position: relative; }
.ms-trigger {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-surface); color: var(--text); cursor: pointer; font: inherit; font-size: 0.86rem; text-align: left;
}
.ms-trigger:hover { border-color: var(--primary); }
.ms-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-summary.is-placeholder { color: var(--text-subtle); }
.ms-caret { flex: 0 0 auto; color: var(--text-subtle); }
.ms-add { align-self: flex-start; }
.ms-add-wrap { position: relative; }
.ms-popover {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0,0,0,0.12); padding: 8px; max-width: 520px;
}
.ms-popover[hidden] { display: none; }
.ms-search { width: 100%; box-sizing: border-box; margin-bottom: 6px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg); font-size: 0.84rem; }
.ms-options { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
.ms-option {
  display: flex; align-items: baseline; gap: 8px; width: 100%; text-align: left;
  padding: 6px 8px; border: 0; background: transparent; border-radius: 6px; cursor: pointer; font: inherit; font-size: 0.84rem; color: var(--text);
}
.ms-option:hover { background: var(--bg-muted); }
.ms-option.is-checked { background: var(--accent-soft, rgba(37, 99, 235,.1)); }
.ms-option.is-disabled { opacity: 0.85; justify-content: space-between; cursor: default; }
.ms-option[hidden] { display: none; }
.ms-option input[type="checkbox"] { flex: 0 0 auto; }
.ms-opt-label { flex: 0 0 auto; font-weight: 500; }
.ms-option-desc { flex: 1 1 auto; min-width: 0; color: var(--text-subtle); font-size: 0.78rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-selected { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
/* Skills/Governance: chips flow horizontally. Tools: rich rows stack vertically (default). */
.ms-chips .ms-selected { flex-direction: row; flex-wrap: wrap; align-items: center; }
.ms-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 4px 3px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-muted); font-size: 0.82rem; }
.ms-empty { padding: 3px 0; }
.ms-chip-x { border: 0; background: transparent; color: var(--text-subtle); cursor: pointer; font-size: 1rem; line-height: 1; padding: 0 4px; }
.ms-chip-x:hover { color: #b91c1c; }
/* Tools rows (selected): compact bordered rows with the connection sub-picker + remove. */
.ms-tools .agent-tool { border: 1px solid var(--border); border-radius: 8px; padding: 5px 8px; background: var(--bg-surface); }
/* Tighter checkbox row. The global `input { width:100% }` rule (style.css ~L2278) would
   stretch native checkboxes into a full-width bordered box — reset them to native sizing. */
.agent-field--check { gap: 6px; }
.agent-builder input[type="checkbox"] {
  width: auto; margin: 0; padding: 0; border: 0; flex: 0 0 auto;
  accent-color: var(--accent, #1e6fbf);
}
/* Chat panel */
.agent-chat { display: flex; flex-direction: column; gap: 10px; max-width: 760px; height: calc(100vh - 220px); min-height: 320px; }
.agent-chat__msgs { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding: 4px; }
.agent-bubble { max-width: 80%; padding: 8px 12px; border-radius: 12px; font-size: 0.88rem; line-height: 1.4; white-space: pre-wrap; word-break: break-word; }
.agent-bubble--user { align-self: flex-end; background: var(--accent-soft, rgba(37, 99, 235,.12)); color: var(--text); }
.agent-bubble--bot { align-self: flex-start; background: var(--bg-surface); border: 1px solid var(--border); }
.agent-chat__starters { display: flex; flex-wrap: wrap; gap: 6px; }
.agent-chip { font-size: 0.78rem; padding: 4px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-surface); color: var(--text-subtle); cursor: pointer; }
.agent-chip:hover { border-color: var(--primary); color: var(--primary); }
.agent-chat__form { display: flex; gap: 8px; align-items: flex-end; }
.agent-chat__form textarea { flex: 1 1 auto; resize: none; font: inherit; font-size: 0.88rem; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
/* System-prompt field header row: the "System prompt" label + the ✨ Generate button. */
.agent-field-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.agent-field-row label { font: inherit; }
/* Per-agent WhatsApp Conversations inbox: thread list (left) + thread (right). */
.wa-convos { display: grid; grid-template-columns: 260px 1fr; gap: 16px; align-items: start; }
.wa-threads { display: flex; flex-direction: column; gap: 4px; max-height: calc(100vh - 220px); overflow-y: auto; border-right: 1px solid var(--border); padding-right: 8px; }
.wa-thread-item { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; padding: 8px 10px; border: 1px solid transparent; border-radius: 8px; background: none; color: var(--text); cursor: pointer; font: inherit; font-size: 0.86rem; }
.wa-thread-item:hover { background: var(--bg-surface); }
.wa-thread-item.is-active { background: var(--accent-soft, rgba(37, 99, 235,.12)); border-color: var(--border); }
.wa-thread-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wa-badge { font-size: 0.66rem; font-weight: 650; padding: 1px 6px; border-radius: 999px; }
.wa-badge--human { background: var(--accent-soft, rgba(37, 99, 235,.15)); color: var(--accent, #1d4ed8); }
.wa-thread { display: flex; flex-direction: column; gap: 10px; height: calc(100vh - 220px); min-height: 320px; }
.wa-thread-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.wa-mode-toggle { display: inline-flex; align-items: center; gap: 6px; font-size: 0.8rem; color: var(--text-muted, #666); cursor: pointer; }
.wa-msgs { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding: 4px; }
.wa-status { display: block; margin-top: 2px; font-size: 0.66rem; opacity: 0.6; text-align: right; }
/* The Agents mount fills its Flux view and scrolls internally. The Flux tab is
   overflow:hidden and every Flux view designates its own scroll child, but
   #flux-agents-root had none — so a tall builder form clipped its footer
   (Create/Save) buttons. Document-flow scroll (not nested flex) is right here:
   the long form scrolls naturally, and the chat box sizes itself. */
#flux-agents-root { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* .app-page* are bare semantic containers (no global CSS) — give them layout
   here, scoped to the Agents mount so no other page is affected. */
#flux-agents-root .app-page-head { display: flex; align-items: center; gap: 12px; padding: 16px 24px 8px; }
#flux-agents-root .app-page-title { font-size: 1.05rem; font-weight: 650; color: var(--text); margin: 0; }
#flux-agents-root .app-page-actions { margin-left: auto; display: flex; gap: 8px; }
#flux-agents-root .app-page-body { padding: 8px 24px 40px; }
/* Optional one-line description under the name (Skills + Powers cards).
   Clamped to two lines so cards stay uniform height in the grid. */
.tool-card__desc {
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--text-subtle, var(--text-muted));
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tool-card__status {
  font-size: 0.7rem;
  padding: 2px 8px;
  border-radius: 999px;
  font-weight: 600;
  white-space: nowrap;
}
.tool-card__status.is-on {
  color: #047857;
  background: #d1fadf;
}
.tool-card__status.is-cta {
  color: var(--bg-surface);
  background: var(--accent);
}
.tool-card__status.is-soft {
  color: var(--text-subtle);
  background: var(--bg-muted);
}

/* ── Shared data-table standard ─────────────────────────────────────────────────
   One look for every module's tabular listing: add `class="app-table"` to a
   <table> and keep any module-specific cell classes (right-align, mono, badges).
   Mirrors the polished `.appadmin-table--lined` pattern, so module tables that
   were previously unstyled browser-default (artha, cdp) pick up the same look.
   Theme-aware via CSS vars (light/dark). */
.app-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.app-table th,
.app-table td {
  padding: 9px 14px; text-align: left; vertical-align: top;
  /* Table-scoped so tuning row separation can't ripple into cards, drawers or panels. */
  border-bottom: 1px solid var(--table-divider, var(--border-muted, var(--border)));
}
.app-table thead th {
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-faint, var(--text-subtle)); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.app-table tbody tr:last-child td { border-bottom: 0; }
.app-table tbody tr:hover { background: var(--bg-hover); }

/* ── Unified Tools list (toolbar chips + search + one row per tool) ───────────── */
.tools-page {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.tools-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg);
  padding: 4px 0 8px;
}
.tools-chips {
  display: inline-flex;
  gap: 4px;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px;
}
.tools-chip {
  border: 0;
  background: transparent;
  color: var(--text-subtle);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 5px 14px;
  border-radius: 999px;
  cursor: pointer;
}
.tools-chip:hover {
  color: var(--text);
}
.tools-chip.is-active {
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}
.tools-search {
  flex: 0 1 280px;
  min-width: 160px;
  padding: 7px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: 0.85rem;
}
.tools-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tool-row {
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  overflow: hidden;
}
.tool-row[hidden] {
  display: none;
}
.tool-row__main {
  display: grid;
  grid-template-columns: 34px 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
}
.tool-row__icon {
  font-size: 1.35rem;
  line-height: 1;
  text-align: center;
}
.tool-row__meta {
  min-width: 0;
}
.tool-row__name {
  font-weight: 600;
  font-size: 0.92rem;
}
.tool-row__desc {
  margin-top: 1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tool-row__action {
  display: flex;
  align-items: center;
  gap: 6px;
}
.tool-row__action [data-tool-expand].is-open {
  background: var(--bg-muted);
}
.tool-row__connections {
  border-top: 1px solid var(--border);
  background: var(--bg-muted);
  padding: 10px 14px 12px;
}
.tool-conns {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tool-conns__add {
  padding-top: 2px;
}
/* Power needing a human OK (approval gate). Amber, like a caution pill. */
.tool-card__status.is-warn {
  color: #92400e;
  background: #fef3c7;
}

/* Connect dialog — same visual family as the Output / Memory / Welcome
   dialogs (border + radius + backdrop alpha) so all modal surfaces feel
   like one app. */
.tool-connect-dialog {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0;
  width: min(520px, 94vw);
  max-height: 86vh;
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.tool-connect-dialog::backdrop {
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.tool-connect-dialog__form {
  margin: 0;
  display: flex;
  flex-direction: column;
  max-height: inherit;
}
.tool-connect-dialog__head {
  display: grid;
  grid-template-columns: 28px 1fr 28px;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.tool-connect-dialog__icon {
  font-size: 1.2rem;
  text-align: center;
}
.tool-connect-dialog__head h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}
.tool-connect-dialog__close {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-subtle);
  font-size: 1rem;
  cursor: pointer;
}
.tool-connect-dialog__close:hover {
  background: var(--bg-muted);
  color: var(--text);
}
.tool-connect-dialog__body {
  padding: 16px 20px 20px 20px;
  overflow-y: auto;
}
.tool-connect-dialog__intro {
  margin: 0 0 16px;
  font-size: 0.92rem;
  color: var(--text);
}
.tool-connect-dialog__footer {
  margin: 14px 0 0;
}

/* Provider buttons inside the OAuth flow (Google / Microsoft / …). */
.tool-connect-providers {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tool-connect-provider {
  display: grid;
  grid-template-columns: 28px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-muted);
  color: var(--text);
  text-decoration: none;
  font-weight: 500;
  transition: background 0.1s ease, border-color 0.1s ease, transform 0.06s ease;
}
.tool-connect-provider:hover {
  background: var(--bg-surface);
  border-color: var(--accent);
}
.tool-connect-provider:active {
  transform: translateY(1px);
}
.tool-connect-provider.is-connected {
  /* Already connected — muted styling + the CTA chip swaps to "Connected ✓". */
  opacity: 0.78;
}
.tool-connect-provider__icon {
  font-size: 1.2rem;
  text-align: center;
}
.tool-connect-provider__cta {
  font-size: 0.78rem;
  color: var(--text-subtle);
  font-weight: 600;
}
.tool-connect-provider.is-connected .tool-connect-provider__cta {
  color: #047857;
}

/* MCP setup body — a numbered list of operator steps. */
.tool-connect-mcp__steps {
  margin: 12px 0;
  padding-left: 20px;
}
.tool-connect-mcp__steps li {
  margin-bottom: 8px;
  font-size: 0.88rem;
  line-height: 1.5;
}
.tool-connect-mcp__steps code {
  background: var(--bg-muted);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.82rem;
}

/* Sub-accordion (sits inside a sidebar group OR directly in a section) —
   collapsible "Recents" / "Templates" / "Recent agents" blocks. Lighter
   summary styling than the top-level groups, and the body indents so the
   parent/child relationship is visible. */
.sidebar-sub-group {
  margin: 0 0 2px;
}

.sidebar-sub-summary {
  list-style: none;
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px;
  cursor: pointer;
  user-select: none;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-faint);
  border-radius: 4px;
}

.sidebar-sub-summary::-webkit-details-marker {
  display: none;
}

.sidebar-sub-summary:hover {
  color: var(--text-muted);
}

.sidebar-sub-chevron {
  display: inline-block;
  transition: transform 0.15s ease;
  font-size: 1.3rem;
  line-height: 1;
  color: var(--text-faint);
}

details[open] > .sidebar-sub-summary .sidebar-sub-chevron {
  transform: rotate(90deg);
}

.sidebar-sub-body {
  /* Visible indentation so the body reads as a child of the summary. */
  padding-left: 18px;
}

.new-personal-button {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 8px;
}

.personal-row-status {
  margin-left: auto;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--surface-muted, rgba(255, 255, 255, 0.06));
  color: var(--text-muted);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Personal home — landing tab when the user clicks the Personal section.
   Three big cards for Agents / Routines / Tasks plus a templates preview. */
.personal-home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  max-width: 960px;
  margin-bottom: 28px;
}

.personal-home-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 18px 18px 14px;
  border: 1px solid var(--border-color, rgba(255, 255, 255, 0.08));
  border-radius: 10px;
  background: var(--surface-muted, rgba(255, 255, 255, 0.03));
  color: var(--text-color);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.05s ease;
}

.personal-home-card:hover {
  border-color: var(--accent, #4c8cff);
  background: var(--surface-muted, rgba(255, 255, 255, 0.05));
}

.personal-home-card:active {
  transform: translateY(1px);
}

.personal-home-card-icon {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  background: var(--accent-soft, rgba(76, 140, 255, 0.16));
  color: var(--accent, #4c8cff);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
}

.personal-home-card-title {
  font-size: 1.05rem;
  font-weight: 600;
}

.personal-home-card-desc {
  font-size: 0.85rem;
  color: var(--text-muted);
  line-height: 1.4;
}

.personal-home-card-cta {
  margin-top: auto;
  font-size: 0.82rem;
  color: var(--accent, #4c8cff);
}

.personal-home-section {
  max-width: 960px;
}

.personal-home-section-header h2 {
  margin: 0 0 4px;
  font-size: 1rem;
}

.personal-home-section-header p {
  margin: 0 0 14px;
  color: var(--text-muted);
  font-size: 0.85rem;
}

/* Minimal active-agent label — matches the AI-disclaimer line above the
   textarea (small, italic, faint). Click to open the slash menu; typing
   `/` in the textarea works too. */
.chat-agent-label {
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  font: inherit;
  font-size: 0.66rem;
  line-height: 1.2;
  color: var(--text-faint);
  font-style: italic;
  letter-spacing: 0.02em;
  opacity: 0.75;
  cursor: pointer;
}

.chat-agent-label:hover {
  opacity: 1;
  color: var(--text-subtle);
}

.chat-agent-label.is-active {
  color: var(--text-muted);
  opacity: 0.9;
  font-style: normal;
}

/* Slash menu — light, in-flow list ABOVE the textarea. Uses the design
   system vars so it looks at home on the (light) app theme. */
.chat-slash-menu {
  margin: 4px 0;
  padding: 4px;
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 8px;
  max-height: 220px;
  overflow-y: auto;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}

.chat-slash-row {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  width: 100%;
  padding: 6px 10px;
  border: 0;
  background: transparent;
  border-radius: 6px;
  cursor: pointer;
  color: var(--text);
  font: inherit;
  text-align: left;
  transition: background 0.1s ease;
}

.chat-slash-row:hover {
  background: var(--bg-hover);
}

.chat-slash-row.is-active {
  background: var(--accent-soft);
}

.chat-slash-name {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text);
}

.chat-slash-hint {
  font-size: 0.7rem;
  color: var(--text-subtle);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.chat-slash-empty {
  padding: 8px 10px;
  font-size: 0.76rem;
  color: var(--text-subtle);
}

/* Editor panel — full-width single-column form. */
.personal-grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 720px;
}

.personal-editor h3 {
  margin: 0 0 12px;
  font-size: 1.05rem;
}

.personal-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.personal-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.personal-form label > span {
  font-weight: 500;
  color: var(--text-color);
}

.personal-form input[type='text'],
.personal-form textarea,
.personal-form select {
  width: 100%;
  padding: 8px 10px;
  border-radius: 6px;
  border: 1px solid var(--border-color, rgba(255, 255, 255, 0.1));
  background: var(--surface-muted, rgba(255, 255, 255, 0.03));
  color: var(--text-color);
  font: inherit;
  resize: vertical;
}

.personal-form-inline {
  flex-direction: row;
  align-items: center;
  gap: 12px;
}

.personal-form-inline > span {
  min-width: 80px;
}

.personal-form-inline select {
  width: auto;
}

.personal-form-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 4px;
}

.personal-form-status {
  margin: 4px 0 0;
  min-height: 1em;
  font-size: 0.78rem;
  color: var(--text-muted);
}

/* Built-in task agent details card. Replaces the editable form when the
   user clicks a built-in (read-only) agent in the sidebar list. Shows
   id, name, model, tools, description, and the system-prompt excerpt. */
.personal-builtin-details {
  padding: 4px 0 0;
}

.personal-builtin-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 8px;
}

.personal-builtin-head h3 {
  margin: 0;
  font-size: 1.05rem;
}

.personal-builtin-meta {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 14px;
  row-gap: 4px;
  margin: 0 0 12px;
  font-size: 0.84rem;
}

.personal-builtin-meta dt {
  font-weight: 600;
  color: var(--text-muted);
}

.personal-builtin-meta dd {
  margin: 0;
}

.personal-builtin-meta code {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--bg-hover);
}

.personal-builtin-summary {
  margin: 0 0 12px;
  font-size: 0.88rem;
  line-height: 1.5;
}

/* ─── Task-plan timeline (vertical, top-down) ──────────────────────────── */

.planner-timeline {
  margin: 12px 12px 16px;
  padding: 12px 14px;
  list-style: none;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  position: relative;
}

.planner-timeline-row {
  display: grid;
  grid-template-columns: 18px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 6px 0;
  font-size: 0.86rem;
  position: relative;
}

/* Connector line between the dots. Drawn on every row except the last
   via a pseudo-element that runs from this dot to the next. */
.planner-timeline-row:not(:last-child)::after {
  content: '';
  position: absolute;
  left: 8px;
  top: 22px;
  bottom: -6px;
  width: 2px;
  background: var(--border);
}

.planner-timeline-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--bg-hover);
  border: 2px solid var(--border);
  box-sizing: border-box;
}

.planner-timeline-dot.planner-stage-status-done {
  background: var(--accent);
  border-color: var(--accent);
}
.planner-timeline-dot.planner-stage-status-running {
  background: rgba(180, 83, 9, 0.6);
  border-color: var(--warn);
  animation: planner-pulse 1.6s ease-in-out infinite;
}
.planner-timeline-dot.planner-stage-status-awaiting {
  background: rgba(234, 179, 8, 0.5);
  border-color: #92400e;
}
.planner-timeline-dot.planner-stage-status-failed {
  background: var(--danger);
  border-color: var(--danger);
}

.planner-timeline-label {
  color: var(--text);
}

/* ─── Cards (one per pipeline section) ─────────────────────────────────── */

.planner-cards {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0 12px;
}

.planner-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  overflow: hidden;
}

.planner-card-summary {
  display: grid;
  grid-template-columns: 28px 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-size: 0.92rem;
}

.planner-card-summary::-webkit-details-marker {
  display: none;
}

.planner-card-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--text);
  font-size: 0.78rem;
  font-weight: 700;
}

.planner-card-title {
  font-weight: 600;
}

.planner-card-toggle {
  font-size: 1rem;
  transition: transform 0.18s ease;
  color: var(--text-muted);
}

.planner-card[open] .planner-card-toggle {
  transform: rotate(180deg);
}

.planner-card-body {
  padding: 4px 12px 12px;
}

.planner-plan-header-actions {
  margin-left: auto;
  display: flex;
  gap: 6px;
}

/* Sub-cards inside Steps / Phases / Action items — same toggle UX as
   the outer cards but lighter visual weight. */
.planner-substep,
.planner-phase,
.planner-action-item {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-hover);
  margin-top: 6px;
}

.planner-substep > summary,
.planner-phase > summary,
.planner-action-item > summary {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  cursor: pointer;
  list-style: none;
  font-size: 0.86rem;
}

.planner-substep > summary::-webkit-details-marker,
.planner-phase > summary::-webkit-details-marker,
.planner-action-item > summary::-webkit-details-marker {
  display: none;
}

.planner-substep[open] .planner-card-toggle,
.planner-phase[open] .planner-card-toggle,
.planner-action-item[open] .planner-card-toggle {
  transform: rotate(180deg);
}

.planner-substep-no {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 22px;
  padding: 0 6px;
  border-radius: 4px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
}

.planner-substep-title {
  font-weight: 500;
}

.planner-substep-body {
  padding: 4px 12px 10px;
  margin: 0;
  font-size: 0.84rem;
  color: var(--text);
}

.planner-substeps,
.planner-actions,
.planner-phase-list,
.planner-phase-steps {
  list-style: none;
  padding-left: 0;
  margin: 0;
}

.planner-phase-steps {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px 12px 10px;
}

.planner-phase-steps li {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
}

.planner-phase-notes {
  margin: 4px 0 8px;
  font-size: 0.86rem;
  color: var(--text);
}

.planner-action-leaf {
  margin: 4px 12px 10px;
  font-size: 0.86rem;
}

.planner-plan-sources-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.84rem;
}

.planner-plan-sources-list a {
  color: var(--accent);
  text-decoration: none;
  word-break: break-all;
}
.planner-plan-sources-list a:hover { text-decoration: underline; }

/* ─── Application-form-style Q&A ───────────────────────────────────────── */

.planner-application-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.planner-form-progress {
  margin: 0 0 4px;
  padding: 8px 12px;
  background: var(--accent-soft);
  border-radius: 6px;
  font-size: 0.84rem;
}

.planner-application-fields {
  list-style: none;
  padding-left: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.planner-application-field {
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-hover);
}

.planner-application-field-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  font-size: 0.78rem;
  color: var(--text-muted);
}

.planner-application-field-no {
  font-weight: 700;
  font-size: 0.82rem;
  padding: 2px 8px;
  border-radius: 4px;
  background: var(--accent);
  color: white;
}

.planner-application-field-kind {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
}

.planner-application-field-id {
  margin-left: auto;
  font-family: var(--font-mono);
}

.planner-application-field-prompt {
  margin: 4px 0 8px;
  font-size: 0.96rem;
  color: var(--text);
  font-weight: 500;
}

.planner-application-answer {
  margin: 8px 0 0;
  padding: 8px 10px;
  border-left: 3px solid var(--accent);
  background: var(--bg-surface);
  border-radius: 4px;
  font-size: 0.9rem;
}

.planner-radio-group {
  display: grid;
  grid-template-columns: 1fr;
  gap: 6px;
  margin-bottom: 8px;
}

.planner-radio-group-inline {
  grid-template-columns: 1fr 1fr;
}

.planner-radio {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  cursor: pointer;
  font-size: 0.86rem;
}

.planner-radio:hover {
  border-color: var(--accent);
}

.planner-radio input[type="radio"] {
  margin: 0;
}

.planner-human-form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.planner-human-form textarea {
  width: 100%;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
  font-size: 0.88rem;
  line-height: 1.4;
  resize: vertical;
}

.planner-human-form button {
  align-self: flex-start;
}

/* ─── Agent execution plan card ───────────────────────────────────────── */

.planner-section-h {
  margin: 12px 0 6px;
  font-size: 0.86rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.planner-agent-classes {
  list-style: none;
  padding: 0;
  margin: 0 0 12px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 8px;
}

.planner-agent-class {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-hover);
}

.planner-agent-class-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.86rem;
}

.planner-agent-class-head code {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--text-muted);
}

.planner-agent-instances {
  list-style: none;
  padding: 4px 12px 10px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.planner-agent-instance {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
}

.planner-agent-instance-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
  font-size: 0.82rem;
}

.planner-agent-instance-id {
  font-family: var(--font-mono);
  font-size: 0.76rem;
  padding: 1px 6px;
  border-radius: 3px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
}

.planner-agent-instance-class {
  font-weight: 600;
}

.planner-agent-instance-action {
  margin: 4px 0;
  font-size: 0.88rem;
}

.personal-builtin-prompt {
  margin: 0 0 12px;
  padding: 10px 12px;
  background: var(--bg-hover);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.78rem;
  line-height: 1.45;
  white-space: pre-wrap;
  max-height: 280px;
  overflow: auto;
}

/* Task-agent tool picker — fieldset of checkbox rows. At least one must be
   chosen before save; submit handler blocks otherwise. */
.personal-form-fieldset {
  border: 1px solid var(--border-muted);
  border-radius: 6px;
  padding: 8px 12px 10px;
  margin: 0;
}

.personal-form-fieldset legend {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--text);
  padding: 0 4px;
}

.personal-form-required {
  margin-left: 6px;
  font-size: 0.72rem;
  font-weight: 400;
  color: var(--text-subtle);
  font-style: italic;
}

/* Agent builder (component-sectioned editor + live test) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.btn-sm {
  padding: 5px 10px;
  font-size: 0.8rem;
}
/* Small inline icon (emoji) inside a ghost-button/label. The button is already
   inline-flex with a gap, so this just normalises the glyph size + baseline so
   the icon reads as a compact affordance next to the label. */
.btn-ico {
  font-size: 0.95em;
  line-height: 1;
  flex: 0 0 auto;
}
/* IAM (Settings → IAM): roles / groups / bindings cards. Reuses .card/.ghost-button/.tag. */
.iam-grid { display: grid; gap: 16px; }
/* Phase 7c: each IAM section renders as a standard .app-page; stack them in the full grid. */
.iam-pages { display: flex; flex-direction: column; gap: 28px; }
/* Inside the .app-page-filters bar the project picker sits next to its label, not pushed right. */
.app-page-filters #iam-project-select { margin-left: 0; }
.iam-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-top: 1px solid var(--border);
}
.iam-row__act { display: inline-flex; gap: 6px; align-items: center; flex: 0 0 auto; flex-wrap: wrap; }
/* Inline role-tier dropdowns (member rows, project overrides) + the project picker — auto-width,
   not the global full-width select. */
.iam-row__act select, #iam-project-select {
  width: auto; max-width: 12rem; padding: 5px 8px; font-size: 0.82rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); color: var(--text);
}
#iam-project-select { margin-left: auto; }
/* People card: a person row is a full-width plain clickable (overrides the global dark
   button) that opens the provenance drawer. */
.iam-person {
  width: 100%; text-align: left; margin: 0; padding: 2px 0; cursor: pointer;
  background: none; border: none; color: var(--text); font: inherit; display: flex;
  align-items: center; gap: 6px; flex-wrap: wrap;
}
.iam-person:hover { color: var(--primary); }
.iam-trace {
  margin: 2px 0 10px; padding: 10px 12px; border-radius: 8px;
  background: var(--bg-muted); border: 1px solid var(--border);
}
.iam-trace-mod { margin-top: 8px; }
.iam-trace-mod > strong { font-size: 0.8rem; text-transform: capitalize; }
.iam-trace-mod ul { margin: 4px 0 0; padding: 0; list-style: none; }
.iam-trace-mod li {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: 0.82rem; padding: 2px 0;
}
.iam-form { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.iam-inline { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; }
.iam-form input[type="text"], .iam-form select, .iam-inline select {
  padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-surface); color: var(--text); font: inherit; font-size: 0.82rem;
}
.iam-perm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
/* AND THE RULE THAT MAKES THE ADVANCED TOGGLE WORK AT ALL (2026-09-18, user report: "why are we
   doing samething in two different ways", against a screenshot of the level radios AND the
   permission grid both on screen with Advanced UNTICKED).

   They were never meant to be visible together: the levels are the everyday answer and the grid is
   the escape hatch for a mix no level produces, which is why there is a checkbox between them. The
   checkbox set `grid.hidden = true` and nothing happened, because **an author `display` un-hides
   `[hidden]` at ANY specificity** — origin settles before specificity, so the line above beat the
   browser's `[hidden] { display: none }` every time. The toggle had never worked; the editor
   always showed both, and the reader was right to ask why the app was offering one answer twice.
   *Ask "does this rule set `display`", not "is it more specific".* */
.iam-perm-grid[hidden] { display: none; }
.iam-perm-group { border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; }
.iam-perm-group legend {
  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted, #9aa0a6); padding: 0 4px;
}
.iam-perm { display: block; font-size: 0.82rem; padding: 2px 0; cursor: pointer; }
/* ── THE ROLE EDITOR'S "WHAT IT CAN DO" ──────────────────────────────────────
   Redesigned 2026-09-18 (user direction: "not as per industry standard user interface design…
   use UI skills"). Built to `.claude/skills/design-system/SKILL.md`; every size, space and radius
   below reads a token, which is the touch rule that skill states.

   FOUR MEASURED DEFECTS, not four preferences. Numbers from the shipped page at 1280px through
   `probe/settings.html?role=1`, which was added in the same commit because none of this is
   answerable in jsdom:

   · **The control column wandered 38px across seven rows.** `.iam-modrow` was `display: flex` with
     `justify-content: space-between`, so each row's segmented control started wherever its module
     NAME happened to end — 635, 624, 662, 640, 657, 636, 628. The reader re-finds the control on
     every line. It is a GRID now, and the control track is the same width on every row by
     construction rather than by coincidence.
   · **Half the form was inside a nested scrollport.** `max-height: 46vh; overflow-y: auto` held
     759px of rows in a 374px box, on a page that already scrolls — so the wheel did different
     things an inch apart and three of seven modules sat below the fold of an inner box whose only
     clue was its own scrollbar. This editor was moved out of a DRAWER onto a page precisely to
     stop a scroll inside a scroll; the list then grew one back. The page scrolls. Nothing inside
     it does.
   · **Remove was `opacity: 0` until hover.** No affordance for a pointer user until they happened
     to be over it, NOTHING for a touch user, and an invisible hit target left behind. It is a
     24x24 icon button now, always present at low emphasis, gaining emphasis on hover — which is
     what "quiet until needed" was reaching for and is not the same as absent.
   · **The view switch was pixel-identical to the level control.** It borrowed `.iam-lvls`, on the
     stated reasoning that "a switch that looked different would read as a different KIND of
     control". That was exactly backwards: it IS a different kind of control — one reshapes the
     whole editor, the other sets one module — and giving them one silhouette put forty of the
     second directly under one of the first. Different radius, different scale, different fill.

   The fifth change is an addition rather than a defect: a SUMMARY, because a role page that
   cannot answer "what does this role grant" without reading every row is a page the reader has
   to hold in their head. */
.iam-roleform { display: flex; flex-direction: column; gap: var(--space-3); }

/* What the role grants, in a sentence, above the rows that say it in detail. Rewritten on every
   repaint from the live grid — never from the seed, which is the role as it was LOADED. */
.iam-rolesum { font-size: var(--fs-md); line-height: var(--lh); color: var(--text-muted); }
.iam-rolesum strong { color: var(--text); font-weight: 600; }

.iam-roleform__head { display: flex; align-items: center; gap: var(--space-3); }
.iam-roleform__head .app-search { flex: 0 1 200px; font-size: var(--fs-md); }

/* THE VIEW SWITCH. A tab-shaped control in a tray, at the smaller step, with the selected option
   raised on the surface rather than filled with `--primary` — so it cannot be mistaken for the
   level ladder below it even at a glance. `--radius-sm`, never the pill. */
.iam-roleview { margin-left: auto; display: inline-flex; gap: 2px; padding: 2px;
  background: var(--bg-muted); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.iam-roleview__o { font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; cursor: pointer;
  display: inline-flex; align-items: center; min-height: 24px; padding: 0 var(--space-3);
  border-radius: var(--radius-xs); color: var(--text-muted);
  transition: background .12s ease, color .12s ease; }
.iam-roleview__o input { position: absolute; opacity: 0; width: 0; height: 0; }
.iam-roleview__o:hover:not(.is-on) { color: var(--text); }
.iam-roleview__o.is-on { background: var(--bg-surface); color: var(--text); box-shadow: var(--shadow-sm); }
.iam-roleview__o:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: 1px; }

/* NO BORDER, NO MAX-HEIGHT, NO OVERFLOW. The rows carry their own separators; a box around things
   that are already separated is the second of the three nested boxes this app removes on sight.

   AND NO `display` EITHER. It said `display: block`, which is the default and therefore bought
   nothing — while being exactly the declaration that un-hides `[hidden]` at any specificity, on
   the element that used to carry `data-role-levels`. That pane is `.iam-rolelevels` now and this
   is just the row list, but a needless `display` on a box a toggle has ever hidden is a trap left
   armed for whoever moves the attribute back. */

/* THE ROW IS A GRID: name (flexible) · level (its own track) · remove (24px). The level track is
   `auto`, so every row's control is the same width and therefore starts at the same x — the
   alignment is a property of the layout rather than of how long the module names happen to be. */
.iam-modrow { display: grid; grid-template-columns: minmax(0, 1fr) auto 24px;
  align-items: center; column-gap: var(--space-3); row-gap: var(--space-2);
  padding: var(--space-2) 0; border-bottom: 1px solid var(--border-muted, var(--border)); }
.iam-modrow:last-child { border-bottom: 0; }
.iam-modrow[hidden] { display: none; }

/* NARROW: THE CONTROL DROPS UNDER THE NAME. Measured at 420px before this existed — the name
   track collapsed to **13px** while the segmented control kept its full width, because `.iam-lvls`
   is `flex: none` and a `minmax(0, 1fr)` track yields to anything that will not shrink. Nothing
   overflowed and no scrollbar appeared, so the page looked intact and the module names were simply
   gone. That is the failure mode worth naming: a `1fr` track beside an unshrinkable one does not
   break, it silently starves.

   A CONTAINER QUERY, not a media query: this list sits inside a settings pane with a rail beside
   it, so the viewport is never the width that matters — at 1100px of viewport the pane can be
   narrower than this breakpoint. The sheet already uses `@container` in two places. */
/* NEITHER `.iam-rolelevels` NOR `.iam-modrows` MAY TAKE A `display` — see above. The container
   query below is the only geometry either of them needs. */
.iam-modrows { container-type: inline-size; }
@container (max-width: 560px) {
  .iam-modrow { grid-template-columns: minmax(0, 1fr) 24px; }
  .iam-modrow .iam-lvls { grid-column: 1; justify-self: start; }
  .iam-modrow__x { grid-row: 1; grid-column: 2; align-self: start; }
}

/* The name cell is two lines: what the module is called, and the quiet detail under it. The
   permission COUNT lives on the second line — it is not something an admin acts on, and at the
   name's weight it competed with the name. */
.iam-modrow__n { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.iam-modrow__name { font-size: var(--fs-base); font-weight: 600; color: var(--text);
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; line-height: var(--lh-tight); }
.iam-modrow__meta { font-size: var(--fs-sm); color: var(--text-muted); line-height: var(--lh-tight); }

/* Per-row remove. ALWAYS VISIBLE — see the defect note above. 24x24 is WCAG 2.5.8's floor and the
   button is exactly it, so the grid's third track can be that width and no wider. */
.iam-modrow__x { width: 24px; height: 24px; display: inline-flex; align-items: center;
  justify-content: center; padding: 0; border: 0; background: none; cursor: pointer;
  border-radius: var(--radius-xs); color: var(--text-faint); font-size: var(--fs-lg);
  line-height: 1; transition: background .12s ease, color .12s ease; }
.iam-modrow__x:hover { background: var(--hover, #f1f5f9); color: var(--danger, #d93025); }
.iam-modrow__x:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

/* The parts a role's module row narrows to — its own line, spanning every track, indented so it
   reads as belonging to the row above rather than as a sibling of it. */
.iam-modrow__parts { grid-column: 1 / -1; min-width: 0;
  margin: var(--space-1) 0 var(--space-2) var(--space-2);
  padding-left: var(--space-3); border-left: 2px solid var(--border-muted, var(--border)); }

/* The "General" divider — the app's own capabilities, after the modules. A heading, not a row. */
.iam-modcat { padding: var(--space-4) 0 var(--space-2); font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--text-subtle); }
.iam-modcat .muted { text-transform: none; letter-spacing: 0; font-weight: 400; }

/* "Add a module" — the control that replaced thirty-nine preset rows, so on an empty role it is
   the ONLY thing to do and is sized accordingly. Outside the row list, full width, dashed: the
   standard "there is one more of these to add" affordance. */
.iam-modadd { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  margin-top: var(--space-3); padding: var(--space-3);
  border: 1px dashed var(--border); border-radius: var(--radius); background: var(--bg-muted); }
.iam-modadd__l { font-size: var(--fs-md); font-weight: 600; color: var(--text); }
.iam-modadd select { font-size: var(--fs-md); min-height: 28px; padding: 0 var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--bg-surface); color: var(--text); flex: 1 1 200px; max-width: 320px; }
.iam-modadd select:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

/* ── THE SEGMENTED SWITCH ────────────────────────────────────────────────────
   One control, N mutually exclusive options — the level ladder on every role row. Native radios
   inside labels, so the browser gives the group its arrow-key behaviour and announces "2 of 4"
   for free; the input is taken out of the flow and the LABEL is what you see.

   TWO THINGS THAT WERE MISSING AND ARE NOT DECORATION (2026-09-18):

   · **A FOCUS RING.** The input is `opacity: 0`, and nothing styled the label on focus — so a
     keyboard user tabbing into forty of these saw NOTHING move. WCAG 2.4.7 Focus Visible, failed
     outright, on the console's most-used control. The ring hangs off `:has()` because the input
     is INSIDE the label and no sibling selector can reach it; WebKit prices `:has()` at
     specificity zero, which is harmless here — nothing else sets `outline` on this class, so
     there is no conflict to lose.
   · **A 24px MINIMUM TARGET.** `.2rem` of padding on a `.76rem` label lands near 21px, under
     WCAG 2.5.8's 24x24 floor. `min-height` rather than more padding, so the pill grows to the
     floor without the label's own box changing shape.

   The selected ink is `--primary-ink`, NEVER `#fff`: `--primary` is a LIGHT blue in dark mode and
   white on it measures about 2.1:1. That token exists for exactly this and the sheet had 24 rules
   ignoring it. */
.iam-lvls { display: inline-flex; flex: none; border: 1px solid var(--border);
  border-radius: var(--radius-pill); overflow: hidden; background: var(--bg-surface); }
.iam-lvl { font-size: var(--fs-md); padding: 0 var(--space-3); cursor: pointer; white-space: nowrap;
  min-height: 28px; display: inline-flex; align-items: center;
  border-right: 1px solid var(--border); color: var(--text-muted, #64748b);
  transition: background .12s ease, color .12s ease; }
.iam-lvl:last-child { border-right: 0; }
.iam-lvl input { position: absolute; opacity: 0; width: 0; height: 0; }
.iam-lvl.is-on { background: var(--primary); color: var(--primary-ink, #fff); font-weight: 600; }
.iam-lvl:hover:not(.is-on) { background: var(--hover, #f1f5f9); color: var(--text); }
/* The ring sits on the SEGMENT, not the group: it has to say which option is focused, and a ring
   round the whole pill would say only "you are somewhere in here". */
.iam-lvl:has(input:focus-visible) { outline: 2px solid var(--primary); outline-offset: -2px; }
/* Inline group-members expander: a quiet text toggle on the group row + an indented
   sub-list of members (each with its own Remove). */
.iam-link-btn {
  background: none; border: 0; padding: 0 2px; margin: 0 2px; cursor: pointer;
  font: inherit; font-size: 0.8rem; color: var(--primary); text-decoration: none;
}
.iam-link-btn:hover { text-decoration: underline; }
.iam-subrows { margin: 0 0 4px 22px; border-left: 2px solid var(--border); padding-left: 10px; }
.iam-subrow { padding: 5px 0; }
.iam-subrow:first-child { border-top: 0; }
/* Inline role editor (expands in place of the role row). */
.iam-role-edit { margin: 8px 0; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); }
/* Read-only assigned-permissions view under an expanded role row. */
.iam-perm-view-mod { padding: 4px 0; font-size: 0.82rem; line-height: 1.9; }
.iam-perm-view-mod > strong { text-transform: capitalize; margin-right: 6px; }
/* Org Analytics (Settings → Analytics): KPI cards + horizontal breakdown bars. */
.an-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.an-window { display: inline-flex; gap: 4px; }
.an-window .ghost-button.is-active { border-color: var(--border-strong); background: var(--bg-hover); }
.an-kpis {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px; margin-bottom: 16px;
}
.an-kpi {
  border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px;
  display: flex; flex-direction: column; gap: 2px;
}
.an-kpi__val { font-size: 1.4rem; font-weight: 600; line-height: 1.1; }
.an-kpi__lbl { font-size: 0.8rem; }
.an-kpi__sub { font-size: 0.72rem; color: var(--text-muted, #9aa0a6); }
.an-bar { display: flex; align-items: center; gap: 10px; padding: 4px 0; }
.an-bar__lbl { flex: 0 0 96px; font-size: 0.8rem; text-transform: capitalize; }
.an-bar__track {
  flex: 1; height: 8px; border-radius: 4px; overflow: hidden;
  background: var(--bg-hover, rgba(127, 127, 127, 0.12));
}
.an-bar__fill { display: block; height: 100%; border-radius: 4px; background: var(--accent, #6366f1); }
.an-bar__val {
  flex: 0 0 auto; min-width: 24px; text-align: right;
  font-size: 0.8rem; color: var(--text-muted, #9aa0a6);
}
.an-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.an-chip {
  font-size: 0.8rem; padding: 4px 10px; border: 1px solid var(--border);
  border-radius: 999px; text-transform: capitalize;
}
.an-chip strong { margin-right: 4px; }
/* Module enablement picker (Settings → Modules). */
.mod-grid { display: grid; gap: 8px; }
.mod-row { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; cursor: pointer; padding: 4px 0; }
.agent-builder {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.agent-builder__config {
  min-width: 0;
}

/* Editor header — title + the rolling-30-day usage chip, sitting together. */
.agent-editor-header {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.agent-editor-header h3 {
  margin: 0;
}

/* Graph on the left, the selected node's editor right beside it (not below). */
.agent-builder__main {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
}
@media (max-width: 760px) {
  .agent-builder__main {
    grid-template-columns: 1fr;
  }
}
.agent-node-editor {
  position: sticky;
  top: 8px;
}
.builder-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  margin-bottom: 12px;
  background: var(--bg-muted);
}
.builder-card__head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.builder-card__head h4 {
  margin: 0;
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text);
}
.builder-card__icon {
  font-size: 1.05rem;
  line-height: 1;
}
.builder-card--soon {
  opacity: 0.85;
}
.soon-badge {
  margin-left: auto;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-subtle);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 8px;
}
.builder-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  color: var(--text);
}
/* Agent node graph (compact vertical pipeline: boxes + connectors) */
.agent-graph {
  display: flex;
  flex-direction: column;
  margin-bottom: 14px;
}
.agent-node {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-muted);
  color: var(--text);
  font: inherit;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease, box-shadow 0.12s ease;
}
.agent-node:hover {
  border-color: var(--border-strong);
  background: var(--bg-hover);
}
.agent-node--active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.agent-node--hub {
  background: var(--accent-soft);
}
.agent-node__icon {
  font-size: 1.15rem;
  line-height: 1;
  flex-shrink: 0;
}
.agent-node__body {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.agent-node__label {
  font-weight: 600;
  font-size: 0.84rem;
}
.agent-node__summary {
  font-size: 0.74rem;
  color: var(--text-subtle);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
/* Vertical connector between the stacked top nodes (model → instructions → hub) */
.agent-graph > .agent-node + .agent-node,
.agent-graph > .agent-branches {
  margin-top: 18px;
  position: relative;
}
.agent-graph > .agent-node + .agent-node::before,
.agent-graph > .agent-branches::before {
  content: "";
  position: absolute;
  left: 19px;
  top: -18px;
  height: 18px;
  width: 2px;
  background: var(--border);
}
/* Branches under the hub: a left spine + a stub to each node */
.agent-branches {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-left: 28px;
}
.agent-branches::after {
  content: "";
  position: absolute;
  left: 19px;
  top: 0;
  bottom: 17px;
  width: 2px;
  background: var(--border);
}
.agent-branches .agent-node::before {
  content: "";
  position: absolute;
  left: -9px;
  top: 50%;
  width: 9px;
  height: 2px;
  background: var(--border);
}
.agent-branches .agent-node {
  position: relative;
}

.agent-node-editor {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px;
  background: var(--bg-surface);
}
.node-panel[hidden] {
  display: none;
}
.node-panel__head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 10px;
}
/* Trailing hint after the panel title (e.g. "— layered memory the Agent
   draws on") — visually downplayed so the panel title still reads first. */
.node-panel__hint {
  font-weight: 400;
  font-size: 0.82rem;
  color: var(--text-subtle);
}

/* Skill tiers (ADR-0002 Phase D) — the three sub-cards inside the Skills
   panel: Book / Experience / Contextual. Visually framed so the user reads
   them as one Skill grouping with three layers, not three unrelated cards. */
.skill-tier {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 12px;
  background: var(--bg-muted);
}
.skill-tier:last-child {
  margin-bottom: 0;
}
.skill-tier__head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  font-size: 0.88rem;
  margin-bottom: 6px;
}
.skill-tier__icon {
  font-size: 1rem;
  line-height: 1;
}
.skill-tier__label {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 6px;
}
.skill-tier__subhead {
  margin-top: 8px;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-subtle);
}

/* Book-Skill domain-pack picker — same checkbox-grid pattern as
   .personal-tool-grid so the rows look like the Tools / Powers grids. The
   grid is one column on narrow screens; two on wider. */
.agent-book-packs-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-bottom: 8px;
}
@media (max-width: 600px) {
  .agent-book-packs-grid {
    grid-template-columns: 1fr;
  }
}

/* Base agent info (name / model / instructions / notes) below the graph. */
.agent-info {
  margin-top: 16px;
  border-top: 1px solid var(--border);
  padding-top: 14px;
}
.agent-info__head {
  font-weight: 600;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: 10px;
}
.agent-info__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
@media (max-width: 600px) {
  .agent-info__grid {
    grid-template-columns: 1fr;
  }
}

/* Live test panel */
.agent-builder__test {
  display: flex;
  flex-direction: column;
}
.agent-test-messages {
  flex: 1;
  min-height: 160px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 0;
  margin: 8px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.agent-test-msg {
  max-width: 90%;
  padding: 7px 10px;
  border-radius: 10px;
  font-size: 0.85rem;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}
.agent-test-msg--user {
  align-self: flex-end;
  background: var(--accent-soft);
  color: var(--text);
}
.agent-test-msg--assistant {
  align-self: flex-start;
  background: var(--bg-muted);
  color: var(--text);
}
.agent-test-msg--error {
  color: var(--err, #c0392b);
}
.agent-test-form {
  display: flex;
  gap: 6px;
  align-items: flex-end;
}
.agent-test-form textarea {
  flex: 1;
  resize: vertical;
}
.agent-test-actions {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.personal-tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 6px 16px;
  margin-top: 6px;
}

/* Agent-edit tools picker — simple list. Each row is a single line
   (icon + name) by default; the description tucks underneath and is
   revealed only on hover, keyboard focus, or when the tool is
   selected. `title=""` on the row provides the same content as a
   native tooltip for mobile long-press / screen readers. */
.personal-tool-row {
  display: grid;
  grid-template-columns: auto auto 1fr;
  align-items: center;
  gap: 0 8px;
  padding: 4px 6px;
  border-radius: 4px;
  cursor: pointer;
}

.personal-tool-row:hover {
  background: var(--bg-hover);
}

.personal-tool-row input[type='checkbox'] {
  align-self: center;
}

.personal-tool-name {
  font-weight: 500;
  color: var(--text);
  font-size: 0.85rem;
}

/* Descriptions are now exposed exclusively via native `title=""`
   tooltips on the row — no inline reveal — so .personal-tool-desc
   is no longer rendered. The selector is retained as a defensive
   `display: none` in case stale markup ships from a cached bundle. */
.personal-tool-desc { display: none; }

/* ─── Agent-edit tool picker v2 (2026-05-31) ───────────────────────────────
   Splits the picker into "Ready to use" (utility + connected connectors —
   checkboxes the user can toggle into the Agent) and "Needs setup"
   (unconnected connectors — read-only with a Set-up → button that jumps
   to the Tools page). Footer link lets the user browse all tools.
   Prompt-only entries are hidden from this picker entirely; they live
   in the Agent's system prompt, not as a real capability the Agent
   gets to call. */

.personal-tool-icon {
  /* Compact icon column shared by both ready + needs-setup rows. Sits
     between the checkbox (ready rows) and the name. */
  font-size: 0.95rem;
  line-height: 1;
  grid-column: auto;
  width: 18px;
  text-align: center;
  align-self: center;
}

/* Section heading inside the tool picker — small all-caps separator. */
.agent-tools-section {
  margin-top: 8px;
}
.agent-tools-section:first-child {
  margin-top: 0;
}
.agent-tools-section__head {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--text-subtle);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 4px 6px 4px;
}

/* Needs-setup row — same simple-list shape as the ready rows, just
   without a checkbox + with a Set-up button on the right. Description
   stays hidden by default and reveals on hover (no `:checked` branch
   here since these rows aren't selectable). */
.personal-tool-row--needs-setup {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0 8px;
  padding: 4px 6px;
  border-radius: 4px;
  background: transparent;
  cursor: default;
  opacity: 0.85;
}
.personal-tool-row--needs-setup:hover {
  background: var(--bg-hover);
}
.personal-tool-row--needs-setup .personal-tool-setup {
  align-self: center;
  white-space: nowrap;
}

/* Footer link — sits below both sections in the picker. */
.agent-tools-footer {
  margin: 14px 0 0;
  padding: 8px 4px 0;
  border-top: 1px dashed var(--border);
}
.agent-tools-footer a {
  margin-right: 4px;
}

/* Task planner — chat-style layout on the Personal home tab.
   Output area scrolls; composer sticks at the bottom. */
.planner-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  max-width: 880px;
  margin: 0 auto;
}

.planner-output {
  flex: 1;
  overflow-y: auto;
  padding: 24px 8px 12px;
}

.planner-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 48px 16px 16px;
}

/* `[hidden]`'s display:none has lower CSS specificity than the
   `.planner-empty` / `.planner-plan` class selectors above + below, so
   the browser keeps `display: flex` even when `hidden=true` is set.
   Spell out the override so `_showEmpty()` actually hides what it
   says it hides. */
.planner-empty[hidden],
.planner-plan[hidden] {
  display: none;
}

.planner-title {
  margin: 0 0 8px;
  font-size: 1.4rem;
  font-weight: 600;
  color: var(--text);
}

.planner-subtitle {
  margin: 0 0 24px;
  max-width: 620px;
  color: var(--text-subtle);
  font-size: 0.9rem;
  line-height: 1.5;
}

.planner-examples {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 8px;
  width: 100%;
  max-width: 720px;
}

.planner-example {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border-muted);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  line-height: 1.35;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.planner-example:hover {
  border-color: var(--border-strong);
  background: var(--bg-hover);
}

.planner-example-icon {
  color: var(--text-faint);
  flex-shrink: 0;
  margin-top: 2px;
}

/* Plan view */
.planner-plan {
  padding: 4px 4px 16px;
}

.planner-plan-header h2 {
  margin: 0 0 4px;
  font-size: 1.1rem;
  font-weight: 600;
}

.planner-plan-header p {
  margin: 0 0 8px;
  color: var(--text-subtle);
  font-size: 0.88rem;
  line-height: 1.5;
}

.planner-prompt {
  font-style: italic;
  color: var(--text-muted);
}

.planner-meta {
  font-size: 0.72rem;
  color: var(--text-faint);
  margin: 4px 0 16px;
}

/* Pipeline stage rows */
.planner-stages {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.planner-stage {
  border: 1px solid var(--border-muted);
  border-radius: 8px;
  background: var(--bg-surface);
  overflow: hidden;
}

.planner-stage.is-deferred {
  opacity: 0.6;
}

.planner-stage-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: 0;
  background: transparent;
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--text);
}

.planner-stage-head[aria-disabled='true'] {
  cursor: default;
}

.planner-stage-head:not([aria-disabled='true']):hover {
  background: var(--bg-hover);
}

.planner-stage-head[disabled] {
  cursor: default;
  opacity: 0.6;
}

.planner-stage-id {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  min-width: 24px;
}

.planner-stage-name {
  flex: 1;
  font-size: 0.92rem;
  font-weight: 500;
}

.planner-stage-status {
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-hover);
  color: var(--text-muted);
}

.planner-stage-status-done { background: var(--accent-soft); color: var(--text); }
.planner-stage-status-running { background: var(--accent-soft); color: var(--text); }
.planner-stage-status-awaiting { background: rgba(234, 179, 8, 0.18); color: #92400e; }
.planner-stage-status-failed { background: rgba(185, 28, 28, 0.12); color: var(--danger); }
.planner-stage-status-queued { background: transparent; color: var(--text-faint); }
.planner-stage-status-deferred { background: transparent; color: var(--text-faint); font-weight: 500; }

.planner-stage-body {
  padding: 0 12px 12px;
}

.planner-knowledge {
  margin: 0;
  padding: 10px 12px;
  background: var(--bg-app);
  border-radius: 6px;
  border-left: 3px solid var(--border-strong);
}

.planner-knowledge code {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.5;
  white-space: pre-wrap;
  color: var(--text);
  background: transparent;
  padding: 0;
}

/* Architect spec view */
.planner-agents {
  list-style: none;
  padding: 0;
  margin: 0 0 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.planner-agent {
  padding: 10px 12px;
  background: var(--bg-app);
  border-radius: 6px;
}

.planner-agent-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}

.planner-agent-role {
  font-weight: 600;
  font-size: 0.9rem;
}

.planner-agent-badge {
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--accent-soft);
  color: var(--text);
}

.planner-agent-prompt {
  margin: 0 0 6px;
  color: var(--text-muted);
  font-size: 0.84rem;
  line-height: 1.45;
}

.planner-edges {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.planner-edge {
  font-size: 0.78rem;
  padding: 2px 8px;
  background: var(--bg-hover);
  border-radius: 999px;
  color: var(--text-muted);
}

.planner-edge em {
  font-style: normal;
  color: var(--text-faint);
  margin: 0 4px;
}

.planner-edge small {
  font-size: 0.7rem;
  color: var(--text-faint);
}

/* P1.5 — edit / restage / delete affordances. */
.planner-plan-header-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.planner-plan-header-row h2 {
  flex: 1;
  margin: 0;
}

.small-button {
  font-size: 0.78rem;
  padding: 4px 10px;
}

.planner-stage-head-row {
  display: flex;
  align-items: stretch;
  gap: 0;
}

.planner-stage-head-row .planner-stage-head {
  flex: 1;
}

.planner-stage-head-row .ghost-button {
  margin: 6px 6px 6px 0;
  align-self: center;
}

.planner-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 12px;
  border-radius: 6px;
  margin: 0 0 12px;
  font-size: 0.84rem;
}

.planner-banner-warn {
  background: rgba(180, 83, 9, 0.08);
  color: var(--warn);
  border: 1px solid rgba(180, 83, 9, 0.18);
}

.planner-banner-info {
  background: rgba(70, 120, 220, 0.08);
  color: var(--text, #1a1d21);
  border: 1px solid rgba(70, 120, 220, 0.22);
}

.planner-banner-action {
  margin-left: auto;
  padding: 2px 10px;
  border: 1px solid var(--warn);
  border-radius: 999px;
  background: transparent;
  color: var(--warn);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.planner-banner-action:hover {
  background: rgba(180, 83, 9, 0.12);
}

.planner-banner-actions {
  margin-left: auto;
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.planner-edit-area {
  width: 100%;
  min-height: 240px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.5;
  resize: vertical;
}

.planner-edit-area:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.planner-edit-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 6px 0;
}

.planner-edit-hint {
  margin-left: auto;
  font-size: 0.72rem;
  color: var(--text-subtle);
  font-style: italic;
}

/* Sub-agent progress strip — one dot per sub-agent (e.g. S1.A1..A7). */
.planner-subagent-strip {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 4px 12px 8px;
  flex-wrap: wrap;
}

.planner-subagent-dot {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border: 1px solid transparent;
  border-radius: 999px;
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  background: var(--bg-hover);
  color: var(--text-faint);
  text-transform: uppercase;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
  cursor: pointer;
  justify-content: center;
}

.planner-subagent-dot:hover {
  border-color: var(--accent);
}

.planner-subagent-dot.is-expanded {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}

.planner-subagent-name {
  font-size: 0.6rem;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  margin-left: 4px;
  opacity: 0.85;
}

.planner-subagent-dot-queued {
  background: var(--bg-hover);
  color: var(--text-faint);
}
.planner-subagent-dot-running {
  background: var(--accent-soft);
  color: var(--text);
  /* Subtle pulse so the user notices which one is active */
  animation: planner-pulse 1.6s ease-in-out infinite;
}
.planner-subagent-dot-done {
  background: var(--accent-soft);
  color: var(--text);
}
.planner-subagent-dot-failed {
  background: rgba(185, 28, 28, 0.12);
  color: var(--danger);
}

@keyframes planner-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

.planner-subagent-label {
  font-family: var(--font-mono);
  font-size: 0.6rem;
}

.planner-subagent-iter {
  font-size: 0.58rem;
  opacity: 0.7;
}

/* Per-agent detail card — shown when a dot is clicked. Contains one
   block per PDCA iteration (status, tokens, duration, failed checks,
   collapsible JSON output). */
.planner-agent-detail {
  margin: 4px 12px 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
}

.planner-agent-detail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 4px;
}

.planner-agent-detail-head > div {
  display: flex;
  align-items: center;
  gap: 8px;
}

.planner-agent-detail-desc {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin: 0 0 8px;
}

.planner-iter {
  margin-top: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-hover);
}

.planner-iter-head {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 0.74rem;
}

.planner-iter-no {
  font-family: var(--font-mono);
  font-weight: 600;
  color: var(--text-muted);
}

.planner-iter-check {
  padding: 1px 6px;
  border-radius: 3px;
  font-weight: 600;
  font-size: 0.66rem;
  text-transform: uppercase;
}
.planner-iter-check-pass { background: rgba(34,197,94,0.18); color: #15803d; }
.planner-iter-check-fail { background: rgba(220,38,38,0.18); color: #b91c1c; }
.planner-iter-check-inconclusive { background: var(--bg-hover); color: var(--text-faint); }

.planner-iter-act {
  font-size: 0.72rem;
  color: var(--text-muted);
}

.planner-iter-failed {
  margin: 6px 0 0;
  padding-left: 18px;
  font-size: 0.74rem;
  color: var(--danger);
}

.planner-iter-output {
  margin-top: 6px;
  font-size: 0.72rem;
}

.planner-iter-output summary {
  cursor: pointer;
  color: var(--text-muted);
}

.planner-iter-output pre {
  margin: 6px 0 0;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  max-height: 280px;
  overflow: auto;
  font-size: 0.7rem;
  line-height: 1.35;
}

.planner-stage-desc {
  margin: 0 12px 4px;
  font-size: 0.72rem;
  color: var(--text-muted);
}

.planner-subagent-strip-label {
  padding: 4px 12px 2px;
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}

/* Clarification bubble — shows above the pipeline rows when the
   pipeline is awaiting a user answer for a blocker question. */
.planner-bubble {
  margin: 10px 0;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--accent-soft);
  border-left: 3px solid var(--accent);
}

.planner-bubble-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.66rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
  margin-bottom: 6px;
}

.planner-bubble-iter {
  padding: 1px 6px;
  background: var(--bg-app);
  border-radius: 999px;
  font-size: 0.62rem;
  color: var(--accent);
}

.planner-bubble-body {
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--text);
  margin-bottom: 8px;
}

.planner-bubble-form {
  display: flex;
  align-items: flex-end;
  gap: 6px;
}

.planner-bubble-input {
  flex: 1;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
  font-size: 0.85rem;
  resize: vertical;
  min-height: 36px;
}

.planner-bubble-input:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

/* S3 agent rows — inline editor when "Edit" is open */
.planner-agent-actions {
  display: inline-flex;
  gap: 4px;
  margin-left: auto;
}

.planner-agent.is-editing {
  background: var(--bg-app);
  border: 1px dashed var(--border-strong);
}

.planner-agent-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.8rem;
  margin-bottom: 6px;
}

.planner-agent-field > span {
  color: var(--text-muted);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.planner-agent-field input,
.planner-agent-field textarea {
  font: inherit;
  font-size: 0.85rem;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--bg-surface);
  color: var(--text);
  resize: vertical;
}

.planner-agent-field input:focus,
.planner-agent-field textarea:focus {
  outline: none;
  border-color: var(--border-strong);
}

.planner-steps {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.planner-step {
  border: 1px solid var(--border-muted);
  border-radius: 8px;
  padding: 12px 14px;
  background: var(--bg-surface);
}

.planner-step-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.planner-step-id {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.planner-step-name {
  flex: 1;
  font-size: 0.95rem;
  font-weight: 600;
}

.planner-run-step {
  margin-left: auto;
  font-size: 0.78rem;
}

.planner-step-desc {
  margin: 0 0 8px;
  color: var(--text);
  font-size: 0.88rem;
  line-height: 1.45;
}

.planner-step-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
  font-size: 0.78rem;
  color: var(--text-subtle);
  line-height: 1.4;
}

.planner-step-tag {
  flex-shrink: 0;
  min-width: 44px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  font-size: 0.66rem;
  padding-top: 2px;
}

.planner-step-tag-list code {
  font-size: 0.76rem;
  background: var(--bg-hover);
  padding: 1px 5px;
  border-radius: 4px;
  margin-right: 2px;
}

.planner-step-result {
  margin-top: 10px;
  padding: 8px 10px;
  background: var(--bg-app);
  border-radius: 6px;
  border-left: 3px solid var(--accent, #4c8cff);
}

.planner-step-result pre {
  margin: 0;
  white-space: pre-wrap;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text);
  font-family: inherit;
}

.planner-step-result.is-error {
  border-left-color: var(--danger);
  color: var(--danger);
  font-size: 0.84rem;
}

/* Composer */
.planner-form {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 4px;
  background: var(--bg-app);
  border-top: 1px solid var(--border-muted);
}

.planner-form-status {
  min-height: 1em;
  font-size: 0.78rem;
  color: var(--text-subtle);
}

.planner-form-status.is-error {
  color: var(--danger);
}

.planner-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  color: var(--text);
  font: inherit;
  resize: vertical;
  min-height: 64px;
}

.planner-input:focus {
  outline: none;
  border-color: var(--border-strong);
  box-shadow: 0 0 0 3px var(--focus-ring);
}

.planner-form-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.primary-button {
  padding: 8px 14px;
  border-radius: 7px;
  border: 1px solid var(--primary);
  background: var(--primary);
  color: var(--primary-ink, #fff);
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.primary-button:hover {
  background: var(--primary-hover);
  border-color: var(--primary-hover);
}

.primary-button:disabled {
  opacity: 0.6;
  cursor: default;
}

/* ─── .btn family ────────────────────────────────────────────────────────────
   Base for the `btn btn--primary|--danger|--small` classes used across module
   controllers (app-admin, nirman, godam, …). These classes predated any
   stylesheet definition, so every such button rendered as an unstyled browser
   default. Tokens mirror .primary-button / .ghost-button / .danger-button. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 12px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--bg-surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.82rem;
  font-weight: 500;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.btn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.btn:disabled { opacity: 0.6; cursor: default; }
.btn--small { padding: 4px 9px; font-size: 0.76rem; border-radius: 6px; }
.btn--primary { background: var(--primary); border-color: var(--primary); color: var(--primary-ink, #fff); }
.btn--primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.btn--danger { color: var(--danger, #b42318); border-color: rgba(185, 28, 28, 0.3); }
.btn--danger:hover { background: rgba(185, 28, 28, 0.06); border-color: rgba(185, 28, 28, 0.5); }

/* ─── Flow Diagram card (SVG visualisation of the refined plan) ───────── */

.planner-flow-scroll {
  width: 100%;
  overflow-x: auto;
  padding: 8px 0 4px;
}

.planner-flow-svg {
  display: block;
  min-width: 100%;
  font-family: inherit;
}

.planner-flow-header {
  fill: var(--bg-muted, #eef1f5);
  stroke: var(--border-muted, #d8dce3);
  stroke-width: 1;
}

.planner-flow-header-parallel {
  fill: rgba(120, 170, 255, 0.18);
}

.planner-flow-header-single {
  fill: rgba(150, 150, 150, 0.18);
}

.planner-flow-header-text {
  font-size: 13px;
  font-weight: 600;
  fill: var(--text, #1a1d21);
}

.planner-flow-rect {
  fill: var(--bg-surface, #ffffff);
  stroke: var(--border-strong, #b6bcc5);
  stroke-width: 1.5;
}

.planner-flow-node:hover .planner-flow-rect {
  stroke: var(--accent, #4f7cff);
  stroke-width: 2;
}

.planner-flow-no {
  font-size: 12px;
  font-weight: 700;
  fill: var(--accent, #4f7cff);
}

.planner-flow-title {
  font-size: 13px;
  font-weight: 600;
  fill: var(--text, #1a1d21);
}

.planner-flow-tool {
  font-size: 11px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  fill: var(--text-muted, #5b6573);
}

.planner-flow-action {
  font-size: 11px;
  fill: var(--text-muted, #5b6573);
}

.planner-flow-arrow {
  stroke: var(--border-strong, #98a0ab);
  stroke-width: 2;
  fill: none;
}

.planner-flow-arrowhead-path {
  fill: var(--border-strong, #98a0ab);
}

/* ─── Human-interaction modal ─────────────────────────────────────────── */

.planner-modal-host {
  display: none;
}
.planner-modal-host.is-open {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 9000;
}

.planner-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 20, 28, 0.55);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}

.planner-modal {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(720px, calc(100vw - 32px));
  max-height: calc(100vh - 48px);
  background: var(--bg-surface, #ffffff);
  color: var(--text, #1a1d21);
  border: 1px solid var(--border-strong, #b6bcc5);
  border-radius: 10px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.planner-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border-muted, #d8dce3);
}
.planner-modal-header h3 {
  margin: 0;
  font-size: 1.05rem;
}

.planner-modal-close {
  background: transparent;
  border: 0;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  color: var(--text-muted, #5b6573);
  padding: 0 6px;
}
.planner-modal-close:hover {
  color: var(--text, #1a1d21);
}

.planner-modal-progress {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 18px;
  border-bottom: 1px solid var(--border-muted, #d8dce3);
  background: var(--bg-muted, #f4f6fa);
}
.planner-modal-progress-bar {
  flex: 1;
  height: 6px;
  background: rgba(120, 130, 145, 0.18);
  border-radius: 3px;
  overflow: hidden;
}
.planner-modal-progress-fill {
  height: 100%;
  background: var(--accent, #4f7cff);
  transition: width 0.2s ease;
}
.planner-modal-progress-text {
  font-size: 0.82rem;
  color: var(--text-muted, #5b6573);
  white-space: nowrap;
}

.planner-modal-form {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  flex: 1;
}
.planner-modal-form .planner-application-fields {
  margin: 0;
  padding: 16px 18px;
  list-style: none;
  overflow-y: auto;
  flex: 1;
}

.planner-modal-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 18px;
  border-top: 1px solid var(--border-muted, #d8dce3);
  background: var(--bg-muted, #f4f6fa);
}
.planner-modal-footer-msg {
  font-size: 0.82rem;
  color: var(--text-muted, #5b6573);
}
.planner-modal-footer-msg.is-error {
  color: var(--danger, #c0392b);
}
.planner-modal-footer-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

body.planner-modal-open {
  overflow: hidden;
}

/* Inline summary card — progress bar + open-modal button. */
.planner-human-summary {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}
.planner-human-summary-bar {
  height: 6px;
  background: rgba(120, 130, 145, 0.18);
  border-radius: 3px;
  overflow: hidden;
}
.planner-human-summary-fill {
  height: 100%;
  background: var(--accent, #4f7cff);
  transition: width 0.2s ease;
}
.planner-human-summary [data-planner-open-human-modal] {
  align-self: flex-start;
}

.planner-human-review {
  border: 1px solid var(--border-muted, #d8dce3);
  border-radius: 6px;
  padding: 8px 12px;
}
.planner-human-review > summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.9rem;
}
.planner-human-review-list {
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.planner-human-review-row {
  border-top: 1px dashed var(--border-muted, #d8dce3);
  padding-top: 10px;
}
.planner-human-review-row:first-child {
  border-top: 0;
  padding-top: 0;
}
.planner-human-review-q {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 0.9rem;
}

@media (max-width: 600px) {
  .planner-modal {
    width: calc(100vw - 16px);
    max-height: calc(100vh - 16px);
    top: 8px;
    left: 8px;
    transform: none;
  }
}

/* ─── Similarity-match modal ──────────────────────────────────────────── */

.planner-modal-similar .planner-modal-body {
  padding: 16px 18px;
  overflow-y: auto;
  flex: 1;
}

.planner-similar-match {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0 0 10px;
}

.planner-similar-prompt {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-left: 3px solid var(--accent, #4f7cff);
  background: var(--bg-muted, #f4f6fa);
  color: var(--text, #1a1d21);
  font-style: italic;
  border-radius: 0 4px 4px 0;
}

.planner-similar-hint {
  margin: 0 0 12px;
  font-size: 0.92rem;
  line-height: 1.5;
}

/* ─── Live pipeline diagram (the top of the planner result area) ─────── */

.planner-pipe-card {
  border: 1px solid var(--border-muted, #d8dce3);
  border-radius: 10px;
  background: var(--bg-surface, #ffffff);
  padding: 14px 16px;
  margin: 14px 0;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}

.planner-pipe-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}

.planner-pipe-card-head h3 {
  margin: 0;
  font-size: 1.0rem;
}

.planner-pipe-msg {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  margin: 0 0 10px;
  border-radius: 6px;
  background: rgba(70, 120, 220, 0.08);
  font-size: 0.86rem;
  color: var(--text, #1a1d21);
}

.planner-pipe-msg-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--accent, #4f7cff);
  animation: planner-pipe-pulse-dot 1.5s ease-in-out infinite;
  flex-shrink: 0;
}

@keyframes planner-pipe-pulse-dot {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.35); opacity: 0.5; }
}

.planner-pipe-scroll {
  width: 100%;
  overflow-x: auto;
}

.planner-pipe-svg {
  display: block;
  min-width: 100%;
  font-family: inherit;
}

.planner-pipe-chip rect.planner-pipe-rect {
  fill: var(--bg-surface, #ffffff);
  stroke: var(--border-strong, #c0c5cd);
  stroke-width: 1.4;
  transition: fill 0.15s ease, stroke 0.15s ease;
}

.planner-pipe-chip text.planner-pipe-label {
  font-size: 12px;
  font-weight: 600;
  fill: var(--text, #1a1d21);
}

.planner-pipe-chip text.planner-pipe-state {
  font-size: 10.5px;
  fill: var(--text-muted, #5b6573);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.planner-pipe-chip-queued rect.planner-pipe-rect {
  fill: var(--bg-muted, #f4f6fa);
  stroke: var(--border-muted, #d8dce3);
}

.planner-pipe-chip-running rect.planner-pipe-rect {
  fill: rgba(70, 120, 220, 0.10);
  stroke: var(--accent, #4f7cff);
  stroke-width: 2;
}

.planner-pipe-chip-running text.planner-pipe-state {
  fill: var(--accent, #4f7cff);
  font-weight: 700;
}

.planner-pipe-chip-done rect.planner-pipe-rect {
  fill: rgba(34, 167, 92, 0.10);
  stroke: rgba(34, 167, 92, 0.55);
}

.planner-pipe-chip-done text.planner-pipe-state {
  fill: rgba(20, 120, 65, 1);
  font-weight: 600;
}

.planner-pipe-chip-failed rect.planner-pipe-rect {
  fill: rgba(192, 57, 43, 0.10);
  stroke: rgba(192, 57, 43, 0.55);
}

.planner-pipe-chip-failed text.planner-pipe-state {
  fill: rgba(160, 40, 30, 1);
  font-weight: 700;
}

.planner-pipe-chip-awaiting rect.planner-pipe-rect {
  fill: rgba(232, 168, 56, 0.12);
  stroke: rgba(232, 168, 56, 0.65);
}

.planner-pipe-chip-awaiting text.planner-pipe-state {
  fill: rgba(160, 110, 20, 1);
  font-weight: 700;
}

.planner-pipe-chip-skipped rect.planner-pipe-rect {
  fill: var(--bg-muted, #f4f6fa);
  stroke: rgba(160, 165, 175, 0.5);
  stroke-dasharray: 4 3;
}

.planner-pipe-chip-skipped text.planner-pipe-label,
.planner-pipe-chip-skipped text.planner-pipe-state {
  opacity: 0.55;
}

.planner-pipe-chip-pulse rect.planner-pipe-rect {
  animation: planner-pipe-pulse 1.8s ease-in-out infinite;
}

/* Per-chip rerun control. Lives in the top-right corner. Hidden until
   the user hovers the parent chip (or focuses the inner element via
   keyboard) so the diagram stays clean when nothing's interactive. */
.planner-pipe-rerun {
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease-in;
  pointer-events: auto;
}

.planner-pipe-chip:hover .planner-pipe-rerun,
.planner-pipe-rerun:focus,
.planner-pipe-rerun:focus-within {
  opacity: 1;
}

.planner-pipe-rerun-bg {
  fill: rgba(255, 255, 255, 0.92);
  stroke: rgba(60, 70, 85, 0.5);
  stroke-width: 1;
}

.planner-pipe-rerun:hover .planner-pipe-rerun-bg {
  fill: #2462e0;
  stroke: #2462e0;
}

.planner-pipe-rerun-icon {
  font-size: 12px;
  font-weight: 600;
  fill: #2462e0;
  pointer-events: none;
  user-select: none;
}

.planner-pipe-rerun:hover .planner-pipe-rerun-icon {
  fill: #ffffff;
}

@keyframes planner-pipe-pulse {
  0%, 100% { stroke-width: 2; }
  50% { stroke-width: 3.5; }
}

.planner-pipe-arrow {
  stroke: rgba(120, 130, 145, 0.6);
  stroke-width: 1.5;
  fill: none;
}

.planner-pipe-arrowhead-path {
  fill: rgba(120, 130, 145, 0.6);
}

.planner-pipe-summary {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 10px;
  font-size: 0.82rem;
  color: var(--text-muted, #5b6573);
}

.planner-pipe-counts {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.planner-pipe-count {
  padding: 2px 8px;
  border-radius: 10px;
  background: var(--bg-muted, #f4f6fa);
  border: 1px solid var(--border-muted, #d8dce3);
  font-size: 0.78rem;
  font-weight: 600;
}

.planner-pipe-count-done {
  background: rgba(34, 167, 92, 0.08);
  border-color: rgba(34, 167, 92, 0.30);
  color: rgba(20, 120, 65, 1);
}
.planner-pipe-count-running {
  background: rgba(70, 120, 220, 0.10);
  border-color: rgba(70, 120, 220, 0.30);
  color: var(--accent, #4f7cff);
}
.planner-pipe-count-failed {
  background: rgba(192, 57, 43, 0.10);
  border-color: rgba(192, 57, 43, 0.30);
  color: rgba(160, 40, 30, 1);
}
.planner-pipe-count-awaiting {
  background: rgba(232, 168, 56, 0.12);
  border-color: rgba(232, 168, 56, 0.45);
  color: rgba(160, 110, 20, 1);
}

.planner-pipe-progress-track {
  flex: 1;
  max-width: 240px;
  height: 6px;
  background: rgba(120, 130, 145, 0.18);
  border-radius: 3px;
  overflow: hidden;
}

.planner-pipe-progress-fill {
  display: block;
  height: 100%;
  background: var(--accent, #4f7cff);
  transition: width 0.3s ease;
}

.planner-timeline-details {
  margin: 8px 0 12px;
  font-size: 0.86rem;
}
.planner-timeline-details > summary {
  cursor: pointer;
  color: var(--text-muted, #5b6573);
  padding: 4px 0;
}

/* ─── Skills modal ────────────────────────────────────────────────────── */

.planner-modal-skills {
  width: min(820px, calc(100vw - 32px));
}

.planner-skills-body {
  padding: 0;
  display: flex;
  flex: 1;
  overflow: hidden;
  height: 460px;
  max-height: calc(100vh - 200px);
}

.planner-skill-tabs {
  flex: 0 0 200px;
  border-right: 1px solid var(--border-muted, #d8dce3);
  background: var(--bg-muted, #f4f6fa);
  padding: 10px 6px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.planner-skill-tab {
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  padding: 8px 10px;
  cursor: pointer;
  font-size: 0.88rem;
  color: var(--text, #1a1d21);
  transition: background 0.12s ease;
}

.planner-skill-tab:hover {
  background: rgba(70, 120, 220, 0.06);
}

.planner-skill-tab.is-active {
  background: rgba(70, 120, 220, 0.12);
  border-color: rgba(70, 120, 220, 0.32);
  font-weight: 600;
}

.planner-skill-pane {
  flex: 1;
  padding: 14px 18px;
  overflow-y: auto;
  background: var(--bg-surface, #ffffff);
}

.planner-skill-pane-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}

.planner-skill-pane-head h4 {
  margin: 0;
  font-size: 1.0rem;
}

.planner-skill-markdown {
  font-size: 0.92rem;
  line-height: 1.55;
}

@media (max-width: 600px) {
  .planner-skills-body {
    flex-direction: column;
    height: auto;
  }
  .planner-skill-tabs {
    flex: 0 0 auto;
    border-right: 0;
    border-bottom: 1px solid var(--border-muted, #d8dce3);
    flex-direction: row;
    overflow-x: auto;
  }
  .planner-skill-tab {
    white-space: nowrap;
  }
}

/* ─── Executor (roadmap step #1): instance state + report ──────────── */

.planner-exec-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 4px 0 12px;
}

.planner-exec-report {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 8px 12px;
  border-radius: 6px;
  background: rgba(34, 167, 92, 0.07);
  border: 1px solid rgba(34, 167, 92, 0.25);
  font-size: 0.86rem;
}

.planner-exec-report-error {
  background: rgba(192, 57, 43, 0.07);
  border-color: rgba(192, 57, 43, 0.3);
}

/* Partial-result panel — run completed but some steps failed/were skipped. */
.planner-partial {
  margin: 10px 0;
  padding: 10px 14px;
  border-radius: 6px;
  background: rgba(217, 119, 6, 0.08);
  border: 1px solid rgba(217, 119, 6, 0.3);
  font-size: 0.88rem;
}
.planner-partial > p { margin: 4px 0 8px; }
.planner-partial-list { list-style: none; margin: 0; padding: 0; }
.planner-partial-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 3px 0;
}

.planner-agent-instance-artefact {
  margin: 6px 0 0;
  padding: 6px 10px;
  background: rgba(34, 167, 92, 0.06);
  border-left: 3px solid rgba(34, 167, 92, 0.55);
  font-size: 0.86rem;
  border-radius: 0 4px 4px 0;
}

.planner-agent-instance-error {
  margin: 6px 0 0;
  padding: 6px 10px;
  background: rgba(192, 57, 43, 0.06);
  border-left: 3px solid rgba(192, 57, 43, 0.55);
  font-size: 0.86rem;
  border-radius: 0 4px 4px 0;
  color: rgba(160, 40, 30, 1);
}

.planner-instance-output {
  margin: 6px 0 0;
  border: 1px solid var(--border, rgba(0, 0, 0, 0.12));
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.02);
}
.planner-instance-output > summary {
  cursor: pointer;
  padding: 6px 10px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--muted, #667085);
}
.planner-instance-output > summary:hover { color: var(--text, #1d2433); }
.planner-instance-output .planner-plan-body,
.planner-instance-output > pre,
.planner-instance-output > ul {
  margin: 0;
  padding: 4px 12px 12px;
}
.planner-output-pre {
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.8rem;
  margin: 0;
  padding: 4px 12px 12px;
}

.planner-agent-instance-running {
  border-color: var(--accent, #4f7cff);
  box-shadow: 0 0 0 1px rgba(70, 120, 220, 0.18) inset;
  animation: planner-exec-pulse 1.6s ease-in-out infinite;
}

@keyframes planner-exec-pulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(70, 120, 220, 0.18) inset; }
  50% { box-shadow: 0 0 0 2px rgba(70, 120, 220, 0.35) inset; }
}

.planner-agent-instance-done {
  border-color: rgba(34, 167, 92, 0.45);
}

.planner-agent-instance-failed {
  border-color: rgba(192, 57, 43, 0.5);
}

.planner-agent-instance-skipped {
  opacity: 0.65;
  border-style: dashed;
}

.planner-stage-status-skipped {
  background: var(--bg-muted, #f4f6fa);
  color: var(--text-muted, #5b6573);
  border: 1px dashed rgba(160, 165, 175, 0.6);
}

/* ─── Live refinement counters (inside the pipeline card) ─────────── */

.planner-refine-panel {
  margin: 10px 0 6px;
  padding: 10px 12px;
  border: 1px solid var(--border-muted, #d8dce3);
  border-radius: 8px;
  background: var(--bg-muted, #f4f6fa);
}

.planner-refine-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
  font-size: 0.94rem;
}

.planner-refine-tiles {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.planner-refine-tile {
  text-align: center;
  padding: 10px 6px;
  border-radius: 6px;
  background: var(--bg-surface, #ffffff);
  border: 1px solid var(--border-muted, #d8dce3);
}

.planner-refine-tile-num {
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--text, #1a1d21);
  font-variant-numeric: tabular-nums;
}

.planner-refine-tile-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted, #5b6573);
  margin-top: 2px;
}

.planner-refine-tile-judges .planner-refine-tile-num { color: var(--accent, #4f7cff); }
.planner-refine-tile-splits .planner-refine-tile-num { color: rgba(180, 83, 9, 1); }
.planner-refine-tile-replaced .planner-refine-tile-num { color: rgba(20, 120, 65, 1); }

.planner-refine-budget {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.planner-refine-budget-track {
  flex: 1;
  height: 6px;
  background: rgba(120, 130, 145, 0.18);
  border-radius: 3px;
  overflow: hidden;
}

.planner-refine-budget-fill {
  display: block;
  height: 100%;
  background: var(--accent, #4f7cff);
  transition: width 0.25s ease;
}

.planner-refine-budget-fill.planner-refine-budget-warn {
  background: rgba(232, 168, 56, 1);
}

.planner-refine-budget-fill.planner-refine-budget-danger {
  background: rgba(192, 57, 43, 1);
}

@media (max-width: 600px) {
  .planner-refine-tiles {
    grid-template-columns: 1fr;
  }
}

/* ─── Stage outputs feed (live activity log below pipeline) ────────── */

.planner-stage-outputs {
  margin: 12px 0 6px;
  padding: 12px 14px;
  border-top: 1px solid var(--border-muted, #d8dce3);
}

.planner-stage-outputs-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 8px;
}

.planner-stage-outputs-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.planner-stage-output-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 6px;
  background: var(--bg-surface, #ffffff);
  border: 1px solid var(--border-muted, #d8dce3);
  font-size: 0.9rem;
  line-height: 1.45;
}

.planner-stage-output-glyph {
  flex: 0 0 18px;
  font-size: 1.05rem;
  line-height: 1.2;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
  margin-top: 1px;
  color: var(--text-muted, #5b6573);
}

.planner-stage-output-done .planner-stage-output-glyph { color: rgba(20, 120, 65, 1); }
.planner-stage-output-running .planner-stage-output-glyph {
  color: var(--accent, #4f7cff);
  animation: planner-stage-spin 1.5s linear infinite;
  display: inline-block;
}
.planner-stage-output-failed .planner-stage-output-glyph { color: rgba(160, 40, 30, 1); }
.planner-stage-output-awaiting .planner-stage-output-glyph { color: rgba(160, 110, 20, 1); }
.planner-stage-output-skipped .planner-stage-output-glyph { color: var(--text-muted, #5b6573); }

@keyframes planner-stage-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.planner-stage-output-body {
  flex: 1;
  min-width: 0;
}

.planner-stage-output-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.planner-stage-output-label {
  font-weight: 600;
  color: var(--text, #1a1d21);
}

.planner-stage-output-meta {
  font-size: 0.78rem;
  color: var(--text-muted, #5b6573);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}

.planner-stage-output-text {
  margin: 4px 0 0;
  color: var(--text, #1a1d21);
  word-break: break-word;
}

.planner-stage-output-running {
  border-color: var(--accent, #4f7cff);
  background: rgba(70, 120, 220, 0.05);
}
.planner-stage-output-done {
  border-color: rgba(34, 167, 92, 0.35);
}
.planner-stage-output-failed {
  border-color: rgba(192, 57, 43, 0.45);
  background: rgba(192, 57, 43, 0.04);
}
.planner-stage-output-awaiting {
  border-color: rgba(232, 168, 56, 0.5);
  background: rgba(232, 168, 56, 0.06);
}
.planner-stage-output-skipped {
  opacity: 0.7;
  border-style: dashed;
}

/* Voice dictation (E7): planner mic recording state */
#planner-mic.is-listening { color: #d33; animation: mic-pulse 1.1s ease-in-out infinite; }
@keyframes mic-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }


/* ─── ADR-0002 Phase E.2 — Routines tab ─────────────────────────────────── */

/* Two-pane builder: form on the left, run-history on the right. Same shape
   as .agent-builder so the visual rhythm matches across tabs. */
.routine-builder {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 16px;
  margin-bottom: 24px;
}
@media (max-width: 900px) {
  .routine-builder {
    grid-template-columns: 1fr;
  }
}
.routine-builder__form fieldset {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin: 12px 0;
}
.routine-builder__form legend {
  font-weight: 600;
  font-size: 0.9rem;
  padding: 0 6px;
}
.routine-mode-radio {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0;
  font-size: 0.9rem;
}
.routine-schedule-fields {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

/* Day-of-week chip row for the "Specific days" picker. The chips behave
   like multi-select toggles — checked state pops the background. */
.routine-schedule-days {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 8px 0;
}
.routine-schedule-days__label {
  font-size: 0.85rem;
  font-weight: 500;
  margin-right: 4px;
}
.routine-schedule-day {
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  user-select: none;
}
.routine-schedule-day input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
.routine-schedule-day span {
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  font-size: 0.82rem;
  background: var(--bg-muted);
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.routine-schedule-day input:checked + span {
  background: var(--accent, #38a);
  border-color: var(--accent, #38a);
  color: #fff;
}
.routine-schedule-day input:focus-visible + span {
  outline: 2px solid var(--accent, #38a);
  outline-offset: 2px;
}

/* Live schedule summary — small italic helper text under the picker that
   tells the user what their selections actually mean. */
#routine-schedule-summary {
  margin-top: 6px;
  font-style: italic;
  color: var(--text-subtle);
}

/* Stage rows — each row is its own bordered card with reorder buttons. */
.routine-stages-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 10px;
}
.routine-stage-row {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px;
  background: var(--bg-muted);
}
.routine-stage-row__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}
.routine-stage-row__num {
  font-weight: 600;
  font-size: 0.82rem;
  color: var(--text-subtle);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.routine-stage-row__controls {
  display: flex;
  gap: 4px;
}
/* ─── Drag-and-drop reorder (UX P16) ───────────────────────────────────────
   Drag handle on the left of the row head. Only the handle is draggable —
   the row stays selectable so textareas work as expected. `cursor: grab`
   on idle, `grabbing` while held. The ↑/↓ buttons remain as a fallback
   for touch + a11y (HTML5 drag events don't fire on touch screens). */
.routine-stage-row__handle {
  border: 0;
  background: transparent;
  color: var(--text-subtle);
  cursor: grab;
  font-size: 1rem;
  line-height: 1;
  padding: 2px 6px;
  margin-right: 6px;
  border-radius: 4px;
  user-select: none;
  /* Slightly tighten the glyph so the two dots render compactly. */
  letter-spacing: -0.15em;
}
.routine-stage-row__handle:hover {
  background: var(--bg-surface);
  color: var(--text);
}
.routine-stage-row__handle:active {
  cursor: grabbing;
}
.routine-stage-row.is-dragging {
  /* Dim the source row so the user sees something is moving without
     hiding it entirely (hiding would jump the layout). */
  opacity: 0.4;
}
/* Drop-position indicator: a 2px accent line above or below the target row,
   driven by the dragover handler in routines.js. Negative margins so the
   line sits exactly between rows instead of nudging layout. */
.routine-stage-row.is-drop-above {
  box-shadow: 0 -2px 0 0 var(--accent, #3b82f6);
}
.routine-stage-row.is-drop-below {
  box-shadow: 0 2px 0 0 var(--accent, #3b82f6);
}

/* Run-history pane — compact rows with status colour coding. */
.routine-runs-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.routine-run-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.85rem;
}
.routine-run-row__head {
  display: grid;
  grid-template-columns: auto auto 1fr auto;
  gap: 8px;
  align-items: center;
}
/* Retry button stays compact + neutral; failed-row color cues come from
   the left-border colour set on .routine-run-row--failed below. */
.routine-run-row__retry {
  padding: 2px 10px;
  font-size: 0.78rem;
}
.routine-run-row__status {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-size: 0.72rem;
}
.routine-run-row--pending { border-left: 3px solid #888; }
.routine-run-row--running { border-left: 3px solid #38a; }
.routine-run-row--done { border-left: 3px solid #2a7; }
.routine-run-row--failed { border-left: 3px solid #d33; }
.routine-run-row__trigger {
  font-size: 0.72rem;
  color: var(--text-subtle);
}
.routine-run-row__time {
  font-size: 0.78rem;
  color: var(--text-subtle);
}
/* Per-run tokens + estimated $ — small monospace chip so the user can
   scan column-by-column down the runs list to spot a cost spike. Tooltip
   on hover explains the rate. */
.routine-run-row__cost {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  color: var(--text-subtle);
  background: var(--bg-muted);
  padding: 1px 8px;
  border-radius: 999px;
  white-space: nowrap;
  cursor: help;
}

/* Routine editor header — title on the left, rolling-30-day cost chip
   on the right when the routine has runs in the window. */
.routine-builder__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.routine-builder__header h3 {
  margin: 0;
}
.routine-month-cost {
  /* Slightly larger than the per-run chip — this is the headline cost
     number, the per-run chips are details below. Still monospace so
     the user reads it as data, not a label. */
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  color: var(--text);
  background: var(--bg-muted);
  padding: 3px 12px;
  border-radius: 999px;
  white-space: nowrap;
  cursor: help;
}
.routine-month-cost[hidden] {
  display: none;
}
/* Wraps the headline "Last 30d" + the projection "Next 30d" chips so
   they sit side-by-side on the routine editor header. Either may be
   hidden independently (e.g. on-demand routine has no forecast). */
.routine-cost-chips {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: baseline;
}
/* The forecast chip uses the same shape as routine-month-cost; the
   accent on the background distinguishes a projected figure from the
   actual one. Subtle — same neutral palette, just lighter so the
   "Last 30d" remains the headline. */
.routine-forecast-cost {
  background: transparent;
  border: 1px dashed var(--border);
  color: var(--text-muted, var(--text));
}
.routine-run-row__error {
  font-size: 0.82rem;
  color: #d33;
  line-height: 1.4;
  cursor: help;
  padding: 4px 0 2px 0;
}

/* Outputs strip — card grid below the builder. */
.routine-outputs {
  margin-top: 24px;
  border-top: 1px solid var(--border);
  padding-top: 16px;
}
.routine-outputs__head h2 {
  margin: 0 0 4px 0;
  font-size: 1.1rem;
}
.routine-outputs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
  margin-top: 12px;
}
.routine-output-card {
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  background: var(--bg-muted);
  cursor: pointer;
  transition: border-color 0.12s;
}
.routine-output-card:hover {
  border-color: var(--accent, #38a);
}
.routine-output-card__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 6px;
}
.routine-output-card__routine {
  font-weight: 600;
  font-size: 0.9rem;
}
.routine-output-card__kind {
  font-size: 0.72rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-subtle);
}
.routine-output-card__preview {
  margin: 4px 0;
  font-size: 0.85rem;
  color: var(--text);
  line-height: 1.4;
  /* clamp to ~3 lines so the cards stay even. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.routine-output-card__meta {
  font-size: 0.72rem;
  color: var(--text-subtle);
}

/* Output detail dialog — full content, monospace preserves whitespace. */
.routine-output-dialog {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0;
  max-width: min(800px, 90vw);
  max-height: 80vh;
  background: var(--bg-surface);
  color: var(--text);
}
.routine-output-dialog::backdrop {
  background: rgba(0, 0, 0, 0.4);
}
.routine-output-dialog__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.routine-output-dialog__head h3 {
  margin: 0;
  font-size: 1rem;
}
.routine-output-dialog__actions {
  display: inline-flex;
  gap: 6px;
}
.routine-output-dialog__body {
  padding: 16px;
  overflow: auto;
  max-height: 65vh;
}
.routine-output-dialog__pre {
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  font-family: inherit;
  font-size: 0.92rem;
  line-height: 1.5;
}

/* Status banner colour codes for the save / run feedback. */
.personal-form-status--ok { color: #2a7; }
.personal-form-status--error { color: #d33; }

/* ─── Manage-all-shares panel (ADR-0002 UX P11) ────────────────────────── */
/* "Shared with others" — list of every active share link the user has
   issued so they can audit + revoke from one place. Lives at the bottom
   of the Routines tab; hidden when there are zero shares. */

.routine-shares {
  margin-top: 24px;
  border-top: 1px solid var(--border);
  padding-top: 16px;
}
.routine-shares[hidden] {
  display: none;
}
.routine-shares__head h2 {
  margin: 0 0 4px 0;
  font-size: 1.1rem;
}
.routine-shares-list {
  list-style: none;
  padding: 0;
  margin: 12px 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.routine-share-row {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px;
  background: var(--bg-muted);
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* Same fade-on-hide as agent-memory-row for the undo pattern. */
  transition: opacity 180ms ease-out;
}
.routine-share-row__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-size: 0.85rem;
}
.routine-share-row__routine {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
}
.routine-share-row__expiry {
  font-size: 0.72rem;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-surface);
  white-space: nowrap;
}
.routine-share-row__expiry--ok    { color: var(--text-subtle); }
.routine-share-row__expiry--warn  { color: #a60; border-color: rgba(170, 102, 0, 0.4); }
.routine-share-row__expiry--soon  { color: #d33; border-color: rgba(221, 51, 51, 0.5); }

.routine-output-dialog__share-group {
  display: inline-flex;
  align-items: center;
}
.routine-output-dialog__share-group select {
  padding: 4px 8px;
  font-size: 0.82rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-surface);
  color: var(--text);
}
.routine-share-row__kind {
  font-size: 0.7rem;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--text-subtle);
}
.routine-share-row__time {
  font-size: 0.78rem;
  color: var(--text-subtle);
}
.routine-share-row__preview {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text);
  line-height: 1.4;
  /* Clamp to ~2 lines so the row stays scannable. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.routine-share-row__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  /* Token slug on the left (visual anchor); buttons hug the right edge. */
  justify-content: space-between;
  flex-wrap: wrap;
}
.routine-share-row__token {
  font-size: 0.72rem;
  color: var(--text-subtle);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* ─── Toast notifications (ADR-0002 UX P9 — personality pass) ──────────── */
/* Transient celebratory feedback for action successes ("Saved", "Link
   copied"). Fixed bottom-right; stack newest-at-the-bottom; slide-in from
   the right, fade out. Inline status banners stay for form errors —
   toasts are for action confirmations, not validation. */

.toast-container {
  position: fixed;
  bottom: 16px;
  right: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 9999;
  /* Don't block clicks on the page when no toast is visible. Individual
     toast nodes re-enable pointer events for their own buttons. */
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 260px;
  max-width: 380px;
  padding: 10px 14px;
  border-radius: 8px;
  background: var(--bg-surface, #fff);
  color: var(--text, #1a1a1a);
  border: 1px solid var(--border, #e5e5e5);
  border-left: 4px solid var(--border, #e5e5e5);
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.08),
    0 1px 2px rgba(0, 0, 0, 0.04);
  pointer-events: auto;
  font-size: 0.88rem;
  line-height: 1.4;
  /* Slide-in animation: starts off-screen-right + transparent. The
     `.toast--entering` class set on next animation frame snaps it back
     to the resting position so the transition runs. */
  transform: translateX(20px);
  opacity: 0;
  transition: transform 180ms ease-out, opacity 180ms ease-out;
}
.toast--entering {
  transform: translateX(0);
  opacity: 1;
}
.toast--leaving {
  /* Slide right + fade out on dismiss. */
  transform: translateX(20px);
  opacity: 0;
}
.toast--success {
  border-left-color: #2a7;
}
.toast--error {
  border-left-color: #d33;
}
.toast--info {
  border-left-color: #38a;
}
.toast__icon {
  font-size: 1rem;
  line-height: 1;
  /* Match the left-border colour to the kind icon so colourblind users
     have the icon-shape distinction in addition to the colour. */
}
.toast--success .toast__icon { color: #2a7; }
.toast--error   .toast__icon { color: #d33; }
.toast--info    .toast__icon { color: #38a; }
.toast__body {
  flex: 1;
  word-break: break-word;
}
.toast__action {
  background: transparent;
  border: 1px solid var(--border, #e5e5e5);
  border-radius: 6px;
  color: var(--accent, #38a);
  font-size: 0.82rem;
  font-weight: 600;
  padding: 4px 10px;
  cursor: pointer;
  transition: background 0.12s;
}
.toast__action:hover {
  background: var(--bg-muted, #f4f4f4);
}
.toast__close {
  background: transparent;
  border: none;
  color: var(--text-subtle, #888);
  font-size: 1.1rem;
  line-height: 1;
  padding: 2px 6px;
  cursor: pointer;
  border-radius: 4px;
}
.toast__close:hover {
  color: var(--text, #1a1a1a);
  background: var(--bg-muted, #f4f4f4);
}
.toast__close:focus-visible {
  outline: 2px solid var(--accent, #38a);
  outline-offset: 2px;
}

@media (max-width: 560px) {
  .toast-container {
    left: 12px;
    right: 12px;
    bottom: 12px;
  }
  .toast {
    min-width: 0;
    max-width: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    transition: opacity 120ms ease-out;
    transform: none;
  }
  .toast--leaving {
    transform: none;
  }
}

/* ─── Agent trust panel (ADR-0002 UX P8) ─────────────────────────────────── */
/* The "What this Agent remembers about me" disclosure inside the Experience
   Skill tier. Wrapped in a <details> so the user can collapse it once they've
   audited; opens by default the first time it's seen. */

.agent-trust-panel {
  margin-top: 12px;
  border-top: 1px dashed var(--border);
  padding-top: 10px;
}
.agent-trust-panel[hidden] {
  display: none;
}
.agent-trust-panel__head {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 0;
}
.agent-trust-panel__head::-webkit-details-marker {
  display: none;
}
.agent-trust-panel__head::before {
  content: "›";
  display: inline-block;
  transition: transform 0.14s;
  font-weight: 600;
  font-size: 1rem;
  color: var(--text-subtle);
}
details[open] > .agent-trust-panel__head::before {
  transform: rotate(90deg);
}
.agent-trust-panel__title {
  font-weight: 600;
  font-size: 0.88rem;
}
.agent-trust-panel__body {
  margin-top: 10px;
}
.agent-trust-panel__list {
  margin: 0 0 10px 0;
  padding: 0;
  /* Cap to ~3 rows visible at once; scroll for the rest so a long memory
     log doesn't push the Save buttons offscreen. */
  max-height: 320px;
  overflow-y: auto;
}
.agent-trust-panel__rows {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.agent-memory-row {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-muted);
  padding: 8px 10px;
  /* Smooth fade on optimistic-hide for the undo pattern (UX P14). The
     toast countdown is 5s; the fade itself is ~180ms so the row settles
     quickly without being jarring. */
  transition: opacity 180ms ease-out;
}
.agent-memory-row--user {
  border-left: 3px solid #6b8;
}
.agent-memory-row--assistant {
  border-left: 3px solid #88a;
}
.agent-memory-row__head {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 8px;
  align-items: center;
  margin-bottom: 4px;
}
.agent-memory-row__role {
  font-weight: 600;
  font-size: 0.78rem;
}
.agent-memory-row__time {
  font-size: 0.72rem;
  color: var(--text-subtle);
}
.agent-memory-row__content {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
  font-size: 0.85rem;
  line-height: 1.45;
  color: var(--text);
  /* Clamp very long turns so the panel scans quickly; full content stays
     in the <pre> so it can be selected + copied. */
  max-height: 6em;
  overflow-y: auto;
}
.agent-trust-panel__footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  /* The three buttons (Preview, Export, Forget all) can wrap on narrow
     screens rather than overflow horizontally. */
  flex-wrap: wrap;
}

/* Memory preview modal (ADR-0002 UX P13). Mirrors routine-output-dialog
   but uses a slightly different accent so users distinguish "Agent's
   prompt block" from "Routine Output content". */
.agent-memory-preview-dialog {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0;
  max-width: min(780px, 92vw);
  max-height: 82vh;
  background: var(--bg-surface);
  color: var(--text);
}
.agent-memory-preview-dialog::backdrop {
  background: rgba(0, 0, 0, 0.4);
}
.agent-memory-preview-dialog__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.agent-memory-preview-dialog__head h3 {
  margin: 0;
  font-size: 1rem;
}
.agent-memory-preview-dialog__body {
  padding: 16px;
  overflow: auto;
  max-height: 68vh;
}
.agent-memory-preview-dialog__body p {
  margin: 0 0 12px 0;
}
.agent-memory-preview-dialog__pre {
  margin: 0;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.85rem;
  line-height: 1.5;
  padding: 12px 14px;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
}

/* ─── Cmd+K command palette (ADR-0002 UX P15 — fast nav) ─────────────────────
   Lazy-mounted <dialog> rendered by features/palette/palette.js. Sits over
   the whole app, opened by ⌘K / Ctrl+K. Visual language borrows from the
   existing Output / Memory dialogs (same border radius, same backdrop alpha)
   so it feels like part of the same family. Anchored a little above centre
   so the keyboard doesn't push it off-screen on small viewports. */
.cmdk-dialog {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0;
  width: min(640px, 92vw);
  max-height: 70vh;
  background: var(--bg-surface);
  color: var(--text);
  /* Anchor toward the top — feels like Spotlight / VS Code's palette. */
  margin: 12vh auto auto auto;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.cmdk-dialog::backdrop {
  background: rgba(0, 0, 0, 0.32);
  /* Soft blur so the underlying UI is visible-but-defocused. */
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.cmdk-form {
  display: flex;
  flex-direction: column;
  max-height: inherit;
  margin: 0;
}
.cmdk-input {
  appearance: none;
  border: 0;
  border-bottom: 1px solid var(--border);
  padding: 14px 16px;
  font-size: 0.95rem;
  background: transparent;
  color: var(--text);
  outline: none;
  border-radius: 10px 10px 0 0;
}
.cmdk-input::placeholder {
  color: var(--text-subtle);
}
.cmdk-list {
  list-style: none;
  padding: 6px;
  margin: 0;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.cmdk-row {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
  user-select: none;
}
.cmdk-row__icon {
  font-size: 1rem;
  text-align: center;
}
.cmdk-row__label {
  font-size: 0.9rem;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cmdk-row__hint {
  font-size: 0.72rem;
  color: var(--text-subtle);
}
.cmdk-row.is-active,
.cmdk-row:hover {
  /* Mute-blue active row — readable on both light + dark themes since the
     palette inherits theme-aware vars below. */
  background: var(--bg-muted);
}
.cmdk-row.is-active .cmdk-row__label {
  color: var(--text);
  font-weight: 500;
}
.cmdk-empty {
  list-style: none;
  padding: 18px;
  text-align: center;
  color: var(--text-subtle);
  font-size: 0.85rem;
}
.cmdk-footer {
  display: flex;
  justify-content: center;
  gap: 16px;
  padding: 8px 16px;
  border-top: 1px solid var(--border);
  font-size: 0.7rem;
  color: var(--text-subtle);
}
.cmdk-footer kbd {
  display: inline-block;
  padding: 1px 5px;
  margin-right: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 3px;
}

/* ─── First-run welcome dialog (UX P17) ────────────────────────────────────
   Lazy-mounted by features/onboarding/welcome.js. Shown once to fresh
   users to introduce the 5-term vocabulary + surface 3 quick-start CTAs.
   Wider than the cmdk palette because the body has a definition list and
   readable line lengths matter. */
.welcome-dialog {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0;
  width: min(560px, 94vw);
  max-height: 88vh;
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.welcome-dialog::backdrop {
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.welcome-dialog__form {
  margin: 0;
  max-height: inherit;
  display: flex;
  flex-direction: column;
}
.welcome-dialog__head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 20px 12px 20px;
  border-bottom: 1px solid var(--border);
}
.welcome-dialog__head h2 {
  margin: 0;
  font-size: 1.15rem;
}
.welcome-dialog__icon {
  font-size: 1.3rem;
}
.welcome-dialog__body {
  padding: 16px 20px 20px 20px;
  overflow-y: auto;
}
.welcome-dialog__lede {
  margin: 0 0 12px 0;
  font-size: 0.92rem;
  color: var(--text);
}
.welcome-dialog__vocab {
  display: grid;
  /* Term column auto-sized; definition takes the rest. The blank row
     between each pair adds breathing room. */
  grid-template-columns: max-content 1fr;
  gap: 6px 14px;
  margin: 0 0 20px 0;
}
.welcome-dialog__vocab dt {
  font-weight: 600;
  font-size: 0.88rem;
  color: var(--text);
  white-space: nowrap;
}
.welcome-dialog__vocab dt span {
  margin-right: 4px;
}
.welcome-dialog__vocab dd {
  margin: 0;
  font-size: 0.86rem;
  color: var(--text-subtle);
  line-height: 1.4;
}
.welcome-dialog__ctas {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 16px 0;
}
.welcome-dialog__footer-hint {
  margin: 0;
  text-align: center;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}
.welcome-dialog__footer-hint kbd {
  display: inline-block;
  padding: 1px 5px;
  margin: 0 2px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 3px;
}

/* ─── Keyboard shortcuts help (UX P18) ─────────────────────────────────────
   Opened with `?`. Lazy-mounted dialog grouped by surface — Navigation,
   Command palette, Chat composer, Routine stages. Same dialog vocabulary
   as cmdk + welcome so it reads as one family. */
.shortcuts-dialog {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0;
  width: min(560px, 94vw);
  max-height: 86vh;
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.35);
}
.shortcuts-dialog::backdrop {
  background: rgba(0, 0, 0, 0.4);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.shortcuts-dialog__form {
  margin: 0;
  display: flex;
  flex-direction: column;
  max-height: inherit;
}
.shortcuts-dialog__head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.shortcuts-dialog__head h2 {
  margin: 0;
  font-size: 1rem;
}
.shortcuts-dialog__body {
  padding: 16px 20px 20px 20px;
  overflow-y: auto;
}
.shortcuts-group {
  margin-bottom: 18px;
}
.shortcuts-group:last-child {
  margin-bottom: 0;
}
.shortcuts-group__title {
  margin: 0 0 8px 0;
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-subtle);
}
.shortcuts-list {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.shortcuts-row {
  display: grid;
  /* Wide first column so the longest kbd run lines up; rest goes to the
     label. The auto sizing keeps short keys compact while letting "Drag ⋮⋮"
     widen as needed. */
  grid-template-columns: minmax(140px, max-content) 1fr;
  gap: 14px;
  align-items: baseline;
}
.shortcuts-row__keys {
  margin: 0;
  font-size: 0.82rem;
  white-space: nowrap;
}
.shortcuts-row__label {
  margin: 0;
  font-size: 0.86rem;
  color: var(--text);
}
.shortcuts-row__keys kbd {
  display: inline-block;
  padding: 2px 6px;
  margin: 0 1px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.72rem;
  background: var(--bg-muted);
  border: 1px solid var(--border);
  border-radius: 4px;
  color: var(--text);
}
.shortcuts-or {
  margin: 0 6px;
  font-size: 0.72rem;
  color: var(--text-subtle);
}

/* ─── Unified sidebar History (2026-05-30) ─────────────────────────────────
   Top-level "History" link in the sidebar that aggregates chats + agents +
   tasks + routines + plans + outputs into one expandable list, each row
   prefixed with its type ("chat - …", "task - …", "routine - …"). The
   per-section "Recents" sub-collapsers below are gone — section bodies
   now render their lists directly (the inlined contents reuse the
   .chat-history-list / .sidebar-sub-body classes that already drove the
   former Recents look, so no other layout tweaks are needed here). */
.unified-history-list {
  /* Slightly tighter than a free-floating list so the type-prefixed rows
     pack into the sidebar's column without truncating the title text. */
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.unified-history-row {
  /* Inherits .chat-history-row's button styling; tweak only the layout
     to fit the [type] [title] [meta] grid. */
  display: grid;
  grid-template-columns: max-content 1fr auto;
  align-items: baseline;
  gap: 6px;
  padding: 4px 8px;
  text-align: left;
}
.unified-history-row__type {
  /* Tag-like prefix — muted + monospace so the user reads "kind — title"
     as a two-part affordance, not one long title. */
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.7rem;
  color: var(--text-subtle);
  white-space: nowrap;
  text-transform: lowercase;
}
.unified-history-row__title {
  font-size: 0.86rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.unified-history-row__meta {
  font-size: 0.7rem;
  color: var(--text-subtle);
  white-space: nowrap;
}

/* ─── Sidebar section buttons + section-menu popup (2026-05-30) ────────────
   The sidebar's chat/agents/plans/tasks/routines links are now plain
   buttons that open the section-menu popup. The popup hosts a 3-button
   level-1 view (New / Templates / History) and swaps to a list view
   when the user picks one — list views show the existing renderers'
   target nodes, borrowed from #sidebar-data-mounts and returned on
   close. Visual language is intentionally aligned with #new-menu so
   the two popups feel like one family. */

/* Flat sidebar button — simple, tight, with a small icon. No chevron
   on plain buttons (clicking opens a popup or navigates). The
   expandable variants (Workspace, Components) carry .sidebar-link--
   expandable + a separate chevron span; their open/close state rotates
   the chevron via the host <details>[open] selector below. Sub-items
   under an expanded group use .sidebar-link--sub for a smaller icon
   + a left indent so the hierarchy reads at a glance. */
.sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 11px;
  margin: 0;
  background: transparent;
  border: 0;
  border-radius: 7px;
  color: var(--text);
  font: inherit;
  font-weight: 500;
  font-size: 0.88rem;
  text-align: left;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.1s ease;
}
/* The author `display:flex` above outranks the UA `[hidden]{display:none}`, so a
   JS-gated nav item (navGating.js — module + free-plan gating) would set the
   `hidden` attribute yet stay visible. Make `[hidden]` authoritative for the
   sidebar, same workaround used elsewhere in this file. */
.sidebar-link[hidden],
.sidebar-collapse[hidden] { display: none !important; }
.sidebar-link:hover {
  background: var(--bg-muted);
}
.sidebar-link:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* Active nav tile — the module/page currently in view. Set by switchTab() (and
   re-applied on reload/back-forward via restoreFromUrl). A soft accent chip; in
   the collapsed icon-rail this highlights just the icon. Listed after :hover so
   an active tile stays lit even while hovered. */
.sidebar-link.active,
.sidebar-link.active:hover {
  background: rgba(37, 99, 235, 0.10);
  color: var(--primary);
  font-weight: 600;
  box-shadow: inset 2px 0 0 var(--primary);
}
/* ── Customizable sidebar: pinned top set + "…/More" overflow ─────────────────
   sidebarNav.js lays out the first bar as a pinned top set (default Inbox/Kriya/
   Orbit/Echo/Flux, App Admin auto-pinned 6th for admins) followed by a controls
   row (More + Customize) and the overflow. Modules the user can't access aren't
   hidden — they sit in the overflow, greyed and non-clickable (navGating.js marks
   them, main.js blocks the click). Only App Admin is fully hidden for non-admins. */
.sidebar-link.is-perm-disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.sidebar-link.is-perm-disabled:hover { background: transparent; }
/* Overflow items fold away until the user opens "More". */
.sidebar-link--overflow { display: none !important; }
.sidebar--more-open .sidebar-link--overflow { display: flex !important; }
/* The controls row carries no box of its own — its buttons flow directly in the
   sidebar's flex column (so they collapse to the icon rail like any nav link). */
.sidebar-controls { display: contents; }
.sidebar-more-btn .sidebar-link-icon,
.sidebar-customize-btn .sidebar-link-icon { color: var(--text-subtle); }
/* Customize sits at the BOTTOM of the expanded list — it only appears once the user
   opens "More" (or when there's no overflow to open). Closed, just the pinned top
   set + the "…/More" toggle show, in both the full bar and the collapsed icon rail. */
.sidebar-customize-btn { display: none; color: var(--text-muted); font-size: 0.82rem; }
.sidebar--more-open .sidebar-customize-btn,
.sidebar--no-overflow .sidebar-customize-btn { display: flex; }
.sidebar--more-open .sidebar-more-btn { color: var(--primary); font-weight: 600; }
.sidebar-link-icon {
  /* Small icon — emoji at this size reads as a chip, not a logo. */
  font-size: 0.85rem;
  line-height: 1;
  width: 14px;
  text-align: center;
  flex-shrink: 0;
}
/* Hand-drawn SVG icons (features/ui/handIcons.js) injected into rail icon spans. */
.hicon { display: inline-block; vertical-align: middle; width: 1.05em; height: 1.05em; }
.sidebar-link-icon .hicon { width: 17px; height: 17px; }
/* Every module's secondary rail (Orbit/Hive/Flux/CDP/Artha/Echo/Workspace). The artha/cdp/
   echo rails put the icon in a bare aria-hidden span, so target the button too. */
.orbit-nav-ico .hicon, .hive-nav-ico .hicon, .flux-nav-ico .hicon, .cdp-nav-ico .hicon,
.pimly-nav-ico .hicon,
.workspace-rail__icon .hicon, .page-rail-title .hicon,
.artha-nav-btn > span[aria-hidden] .hicon, .cdp-nav-btn > span[aria-hidden] .hicon,
.echo-nav-btn > span[aria-hidden] .hicon { width: 18px; height: 18px; }
/* Icon-only (collapsed) rails — enlarge the hand-drawn icons since the labels are hidden. */
.left-collapsed .sidebar-link-icon .hicon,
.left-collapsed .tab-footer-ico .hicon { width: 25px; height: 25px; }
/* Class-agnostic fallback: ANY second-rail button/link whose icon sits in a direct
   aria-hidden <span> gets the standard glyph size (18px; 25px collapsed) — so a new
   module rail (Pimly/Inventra/Ordio/iShop/Bazaar/…) only needs the markup, never a
   per-module icon-sizing entry above. */
:is(.page-left-rail, .workspace-rail) :is(button, a) > span[aria-hidden] .hicon { width: 18px; height: 18px; }
:is(.page-left-rail, .workspace-rail).is-collapsed :is(button, a) > span[aria-hidden] .hicon { width: 25px; height: 25px; }
.sidebar-link-label {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Tiny caption under the icon — only shown in the collapsed icon rail (Slack-style). */
.sidebar-link-mini { display: none; }
.sidebar-link-chevron {
  /* Chevron only on the expandable group summaries. Tiny, muted; rotates
     to ▼ when its host <details> is open (rule below). */
  font-size: 1.3rem;
  color: var(--text-subtle);
  transition: transform 0.15s ease;
  width: 10px;
  text-align: center;
  flex-shrink: 0;
}
/* Expandable group summary — same look as a flat button but with the
   chevron column reserved. Native <details>+<summary> drives state. */
.sidebar-collapse {
  margin: 0;
}
.sidebar-collapse > summary.sidebar-link--expandable {
  list-style: none;  /* hide the native disclosure triangle */
}
.sidebar-collapse > summary.sidebar-link--expandable::-webkit-details-marker {
  display: none;
}
.sidebar-collapse[open] > summary .sidebar-link-chevron {
  transform: rotate(90deg);
}
/* Expanded body — minimum spacing between sub-items + a left rail to
   visually attach them to the parent. */
.sidebar-collapse-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 3px 0 4px 0;
}
.sidebar-link--sub {
  padding-left: 24px;  /* line up the icon with the parent's label */
  font-size: 0.82rem;
  font-weight: 400;
}
.sidebar-link--sub .sidebar-link-icon {
  font-size: 0.78rem;
  width: 12px;
}

/* Section-menu popup card — wider than the +New popup so list views
   inside it have enough room to render comfortably. */
.section-menu-card {
  max-width: 520px;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
  padding: 0;  /* head + body each carry their own padding */
}
.section-menu-head {
  display: grid;
  grid-template-columns: 32px 1fr 32px;
  align-items: center;
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.section-menu-title {
  margin: 0;
  font-weight: 600;
  font-size: 0.98rem;
  text-align: center;
}
.section-menu-back,
.section-menu-close {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--text-subtle);
  font-size: 1rem;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.section-menu-back:hover,
.section-menu-close:hover {
  background: var(--bg-muted);
  color: var(--text);
}
.section-menu-back[hidden] {
  visibility: hidden;  /* keep the grid column reserved */
  display: inline-flex;
}

/* Level-1 grid + list slot share the same body padding so the size
   doesn't jump when the user moves between views. */
.section-menu-grid,
.section-menu-list {
  padding: 16px;
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}
.section-menu-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px;
}
.section-menu-option {
  /* Inherits .new-menu-item; only the grid-column tweak differs (single
     column reads more deliberate for 3 vertical choices than 2-wide). */
  width: 100%;
}
/* The borrowed list node (chat history, personal agents/tasks/etc.) is
   a flex column. Strip the off-screen container's hidden behaviour
   when it lands inside the popup body. */
.section-menu-list > .chat-history-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
/* Empty-state messages inside a borrowed list need a bit more breathing
   room than they get in the sidebar's compact column. */
.section-menu-list .empty-state {
  padding: 12px 8px;
  text-align: center;
}

/* ─── Approvals inbox (2026-05-30) ─────────────────────────────────────────
   Sidebar pill + count badge, plus the tab content (filter pills + cards).
   The badge inherits the topbar notification-bell visual language so the
   user reads "X waiting" the same way regardless of which surface it
   shows up on. Cards are accordion-style: head shows summary + status +
   age; clicking expands the payload + actions inline. */

/* Tiny chip badge on the sidebar Approvals link — shows pending count. */
.sidebar-link-badge {
  background: var(--accent);
  color: var(--bg-surface);
  font-size: 0.62rem;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 8px;
  min-width: 14px;
  text-align: center;
  line-height: 1.3;
  flex-shrink: 0;
}
.sidebar-link-badge[hidden] {
  display: none;
}

/* Approvals tab content shell. */
.approvals-shell {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 8px 4px;
  max-width: 880px;
}
.approvals-toolbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.approvals-filters {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}
.filter-pill {
  /* Toggle-style pill — small + muted until active. Mirrors the run-status
     pills in the routines tab. */
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-subtle);
  padding: 4px 12px;
  border-radius: 999px;
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
.filter-pill:hover {
  background: var(--bg-muted);
  color: var(--text);
}
.filter-pill.is-active {
  background: var(--accent);
  color: var(--bg-surface);
  border-color: var(--accent);
}

.approvals-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Card = one approval row. Head is always visible; body shows on
   .is-expanded so only one card is open at a time. */
.approval-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  overflow: hidden;
}
.approval-card.is-expanded {
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.approval-card__head {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: var(--text);
  transition: background 0.1s ease;
}
.approval-card__head:hover {
  background: var(--bg-muted);
}
.approval-card__summary {
  flex: 1;
  font-weight: 500;
  font-size: 0.92rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.approval-card__meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.72rem;
  color: var(--text-subtle);
  flex-shrink: 0;
}
.approval-card__kind {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}
.approval-card__time {
  font-size: 0.72rem;
}
.approval-status {
  /* Per-status tinting. Visual signals match what the user expects:
     pending = neutral, executed = green, rejected/expired = grey,
     approved-but-failed = red. */
  display: inline-block;
  padding: 1px 8px;
  border-radius: 8px;
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.approval-status--pending { background: var(--bg-muted); color: var(--text); }
.approval-status--executed { background: #d1fadf; color: #047857; }
.approval-status--approved { background: #fee2e2; color: #b91c1c; }
.approval-status--rejected { background: var(--bg-muted); color: var(--text-subtle); }
.approval-status--expired { background: var(--bg-muted); color: var(--text-subtle); text-decoration: line-through; }

.approval-card__body {
  padding: 0 14px 12px 14px;
  border-top: 1px solid var(--border);
  margin-top: 0;
}
.approval-card__payload {
  background: var(--bg-muted);
  padding: 8px 10px;
  border-radius: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.78rem;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 8px 0;
  max-height: 320px;
  overflow: auto;
}
.approval-card__result,
.approval-card__error,
.approval-card__rejection {
  font-size: 0.82rem;
  padding: 6px 10px;
  border-radius: 6px;
  margin: 6px 0;
}
.approval-card__result { background: #d1fadf; color: #047857; }
.approval-card__error { background: #fee2e2; color: #b91c1c; }
.approval-card__rejection { background: var(--bg-muted); color: var(--text-subtle); }
.approval-card__actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}


/* ─── Cost & Billing card (Settings) ──────────────────────────────────── */
/* Wider card so the budget form + top-N tables breathe. The dashboard grid
   defaults to auto-fit columns — `grid-column: 1 / -1` makes this card
   span the full width when room allows. */
.billing-card {
  grid-column: 1 / -1;
}
.billing-hero {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0 16px;
  flex-wrap: wrap;
}
.billing-hero__metric {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.billing-hero__label {
  font-size: 0.78rem;
  color: var(--text-subtle, var(--text));
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.billing-hero__amount {
  font-size: 1.8rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.billing-hero__meta {
  margin-top: 2px;
}
.billing-hero__cap {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  min-width: 220px;
}
.billing-cap-bar {
  width: 200px;
  height: 8px;
  border-radius: 999px;
  background: var(--bg-muted);
  overflow: hidden;
}
.billing-cap-bar__fill {
  height: 100%;
  background: #16a34a;
  transition: width 240ms ease;
}
.billing-cap-bar__fill[data-state="alert"] { background: #d97706; }
.billing-cap-bar__fill[data-state="over"]  { background: #dc2626; }
.billing-cap-badge {
  font-size: 0.78rem;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--bg-muted);
  color: var(--text);
}
.billing-cap-badge[data-state="alert"] { background: #fef3c7; color: #92400e; }
.billing-cap-badge[data-state="over"]  { background: #fee2e2; color: #b91c1c; }

.billing-section {
  margin-top: 16px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.billing-section > summary {
  cursor: pointer;
  margin-bottom: 6px;
}

.billing-topn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
  margin-top: 8px;
}
.billing-topn-title {
  font-size: 0.85rem;
  margin: 0 0 6px;
  color: var(--text-subtle, var(--text));
}
.billing-topn {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.billing-topn-row {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: baseline;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 6px;
}
.billing-topn-row:nth-child(odd) {
  background: var(--bg-muted);
}
.billing-topn-name {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.billing-topn-meta {
  white-space: nowrap;
}
.billing-topn-cost {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
}

.checkbox-inline {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ─── Per-stage cost breakdown panel (routine editor sidebar) ────────── */
.routine-cost-breakdown {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.routine-cost-breakdown > summary {
  cursor: pointer;
  font-size: 0.85rem;
  margin-bottom: 6px;
}
.routine-cost-breakdown__rows {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
}
.routine-cost-breakdown__row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  grid-template-areas:
    "stage name total"
    ".     avg  avg";
  gap: 2px 8px;
  padding: 6px 8px;
  border-radius: 6px;
  font-size: 0.82rem;
}
.routine-cost-breakdown__row:nth-child(odd) {
  background: var(--bg-muted);
}
.routine-cost-breakdown__stage {
  grid-area: stage;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  color: var(--text-subtle, var(--text));
}
.routine-cost-breakdown__name {
  grid-area: name;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.routine-cost-breakdown__avg {
  grid-area: avg;
}
.routine-cost-breakdown__total {
  grid-area: total;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Top-N rows in Settings → Cost & Billing become buttons when they're
   click-through links to the routine / agent editor. Reset the button
   chrome so the row reads the same as the original <div>. */
.billing-topn-row--link {
  border: 0;
  background: transparent;
  width: 100%;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.billing-topn-row--link:hover,
.billing-topn-row--link:focus-visible {
  background: var(--bg-hover, var(--bg-muted));
  outline: 1px solid var(--accent, #6366f1);
  outline-offset: -1px;
}

/* ─── Recent runs feed (Settings → Cost & Billing) ──────────────────── */
.billing-recent-runs {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 8px;
}
/* Recent-run row uses a <details> wrapping <summary> (the row itself)
   + a <div class="...__expand"> (the inline output preview). Native
   chevron suppressed so we render our own that rotates on open. */
.billing-run-row {
  border-radius: 6px;
  padding: 0;
}
.billing-run-row:nth-child(odd) { background: var(--bg-muted); }
.billing-run-row[open] {
  background: var(--bg-hover, var(--bg-muted));
  outline: 1px solid var(--accent, #6366f1);
  outline-offset: -1px;
}
.billing-run-row__summary {
  display: grid;
  grid-template-columns: 14px 90px 1fr auto auto;
  grid-template-areas:
    "chev when name status cost"
    ".    .    error error error";
  align-items: baseline;
  gap: 4px 10px;
  padding: 6px 10px;
  list-style: none;
  cursor: pointer;
}
.billing-run-row__summary::-webkit-details-marker { display: none; }
.billing-run-row__chevron {
  grid-area: chev;
  color: var(--text-subtle, var(--text));
  font-size: 0.72rem;
  transition: transform 120ms ease;
  align-self: center;
}
.billing-run-row[open] .billing-run-row__chevron { transform: rotate(90deg); }
.billing-run-row__when   { grid-area: when; white-space: nowrap; }
.billing-run-row__name   { grid-area: name; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.billing-run-row__status { grid-area: status; font-size: 0.74rem; padding: 1px 8px; border-radius: 999px; background: var(--bg); border: 1px solid var(--border); }
.billing-run-row__status--done    { color: #047857; background: #d1fadf; border-color: transparent; }
.billing-run-row__status--failed  { color: #b91c1c; background: #fee2e2; border-color: transparent; }
.billing-run-row__status--running { color: #92400e; background: #fef3c7; border-color: transparent; }
.billing-run-row__status--pending { color: var(--text-subtle, var(--text)); }
.billing-run-row__cost   { grid-area: cost; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; white-space: nowrap; }
.billing-run-row__error  { grid-area: error; }

/* Expand panel — output preview (success) or full error (failure)
   + a small Open-editor sub-link. Indented to align with the row's
   name column so the panel reads as commentary on the row above. */
.billing-run-row__expand {
  padding: 8px 14px 10px 30px;
  border-top: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.billing-run-row__output {
  max-height: 280px;
  overflow: auto;
  padding: 8px 10px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.86rem;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
}
.billing-run-row__error-full {
  margin: 0;
  padding: 8px 10px;
  background: #fee2e2;
  color: #b91c1c;
  border-radius: 6px;
  font-size: 0.82rem;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 200px;
  overflow: auto;
}
.billing-run-row__open-editor {
  align-self: flex-start;
  font-size: 0.82rem;
  color: var(--accent, #6366f1);
  text-decoration: none;
}
.billing-run-row__open-editor:hover { text-decoration: underline; }

/* ── Today tab ─────────────────────────────────────────────────────────
   The landing "what needs me, right now?" feed. Four stacked sections
   (approvals, due runs, fresh outputs, notifications) each rendered as
   a list of row links. Layout mirrors `.approvals-shell` so the visual
   language is consistent across the personal section. */
.central-feed {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 8px 4px;
  max-width: 880px;
}
.central-feed-section {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 14px;
  background: var(--surface, transparent);
}
.central-feed-section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
}
.central-feed-section-head h2 {
  font-size: 0.95rem;
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.central-feed-count {
  font-size: 0.72rem;
  background: var(--border);
  color: var(--text-subtle);
  padding: 1px 8px;
  border-radius: 999px;
  font-weight: 500;
}
.central-feed-section-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.today-row {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  gap: 10px;
  align-items: start;
  padding: 8px 10px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  border: 1px solid transparent;
  transition: background 0.1s ease, border-color 0.1s ease;
}
/* Central notifications mirror the bell (read + unread); dim the already-seen ones. */
.today-row--notif.is-read { opacity: 0.6; }
.today-row--notif.is-unread .today-row-title { font-weight: 600; }
.today-row:hover,
.today-row:focus-visible {
  background: var(--hover, rgba(127,127,127,0.06));
  border-color: var(--border);
  outline: none;
}
.today-row-icon {
  font-size: 1.05rem;
  line-height: 1.2;
}
.today-row-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.today-row-title {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.today-row-meta {
  font-size: 0.78rem;
}
.today-row-preview {
  font-size: 0.82rem;
  color: var(--text-subtle);
  margin-top: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.today-row-cta {
  font-size: 0.78rem;
  color: var(--accent, #6366f1);
  white-space: nowrap;
  align-self: center;
}
.today-pill {
  display: inline-block;
  font-size: 0.7rem;
  padding: 1px 8px;
  border-radius: 999px;
  margin-left: 6px;
  font-weight: 500;
}
.today-pill--warn {
  background: rgba(220, 38, 38, 0.12);
  color: rgb(220, 38, 38);
}

/* ── Approvals bulk-select toolbar ─────────────────────────────────────
   Sticky bar above the cards once at least one is selected. Mirrors
   Gmail/Linear: a thin row of buttons + a count + a keyboard hint. */
.approval-bulkbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  margin-bottom: 8px;
  border: 1px solid var(--accent, #6366f1);
  border-radius: 8px;
  background: rgba(99, 102, 241, 0.08);
  position: sticky;
  top: 0;
  z-index: 1;
}
.approval-bulkbar kbd {
  font-family: inherit;
  font-size: 0.72rem;
  background: rgba(0, 0, 0, 0.18);
  color: inherit;
  border-radius: 3px;
  padding: 1px 5px;
  margin-left: 4px;
}
.approval-card__head-row {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 8px;
  align-items: center;
}
.approval-card__check {
  margin: 0;
  cursor: pointer;
}
.approval-card__check--spacer {
  display: inline-block;
}
.approval-card.is-selected {
  border-color: var(--accent, #6366f1);
  box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.15) inset;
}

/* ── Sidebar usage meter ───────────────────────────────────────────────
   Lives just above the footer (Report / Settings). Shows plan + runs
   used vs cap + a thin progress bar. Three states: ok (default), warn
   (≥80%), over (≥100%). The anchor is a real link to /billing so a
   click drills into the full breakdown. */
.sidebar-usage {
  display: block;
  padding: 10px 12px;
  margin: 4px 8px 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  font-size: 0.78rem;
  line-height: 1.3;
}
.sidebar-usage:hover {
  border-color: var(--accent, #6366f1);
}
/* The .sidebar-usage anchor is the single bordered card. The controller injects the
   generic `.usage-meter` component (reused as-is from the billing page), which carries
   its OWN border + radius + padding + muted background + top margin — producing a
   double border and wasted space inside the expanded sidebar. Strip that nested chrome
   so the anchor's own border/padding is the only frame. The collapsed % bubble
   (`.usage-meter-short`) is a separate element and is unaffected. */
.sidebar-usage .usage-meter {
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}
.sidebar-usage__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
}
.sidebar-usage__plan {
  text-transform: capitalize;
  font-weight: 600;
}
.sidebar-usage__count {
  font-variant-numeric: tabular-nums;
}
.sidebar-usage__bar {
  height: 4px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
  margin-top: 6px;
}
.sidebar-usage__fill {
  display: block;
  height: 100%;
  background: var(--accent, #6366f1);
  transition: width 0.3s ease;
}
.sidebar-usage__meta {
  margin-top: 4px;
  font-size: 0.72rem;
}
.sidebar-usage--warn .sidebar-usage__fill {
  background: #d97706;  /* amber */
}
.sidebar-usage--over .sidebar-usage__fill {
  background: #dc2626;  /* red */
}
.sidebar-usage--over {
  border-color: #dc2626;
}

/* ── Incident banner ──────────────────────────────────────────────────
   Sticky top bar shown when /status reports `degraded` or `down`.
   Pushes the rest of the page down via `body.has-incident { padding-top }`
   so it doesn't overlap any of the floating headers. */
.incident-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 9999;
  padding: 8px 14px;
  text-align: center;
  font-size: 0.86rem;
  color: #fff;
  background: #d97706;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
.incident-banner--down {
  background: #dc2626;
}
.incident-banner__link {
  color: #fff;
  text-decoration: underline;
  margin-left: 6px;
}
body.has-incident {
  padding-top: 36px;
}

/* ── Shared loading skeletons + error-state actions ────────────────────
   Reusable across feature surfaces. Mirrors the shimmer pattern from
   Linear / Notion / GitHub: a faint pulsing block that hints at the
   shape of the content arriving. */
@keyframes skeleton-pulse {
  0%   { opacity: 0.5; }
  50%  { opacity: 0.9; }
  100% { opacity: 0.5; }
}
.skeleton-line,
.skeleton-row {
  background: var(--border);
  border-radius: 6px;
  animation: skeleton-pulse 1.4s ease-in-out infinite;
}
.skeleton-line {
  display: block;
  height: 12px;
  width: 40%;
  margin: 4px 0;
}
.skeleton-line--md { width: 30%; }
.skeleton-line--lg { width: 60%; }
.skeleton-row {
  height: 44px;
  margin: 4px 0;
}
.empty-state__actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  justify-content: center;
}

/* ── Accessibility — universal focus ring ──────────────────────────────
   Visible on every keyboard-focused interactive element. Uses the
   :focus-visible selector so mouse clicks don't get the ring (which
   is the WCAG-recommended distinction). */
:focus-visible {
  outline: 2px solid var(--accent, #6366f1);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Skip-to-content link — invisible until focused, jumps to <main>.
   Required by WCAG 2.4.1 (Bypass Blocks). The link element itself
   lives in index.html; this rule controls visibility. */
.skip-to-content {
  position: absolute;
  left: -10000px;
  top: 0;
  z-index: 10000;
  background: var(--primary);
  color: var(--primary-ink, #fff);
  padding: 8px 14px;
  text-decoration: none;
  border-radius: 0 0 6px 0;
}
.skip-to-content:focus {
  left: 0;
}

/* Respect `prefers-reduced-motion`. Skeleton shimmer + banner shadows
   are decorative; users who've opted out of motion shouldn't see them
   pulse. */
@media (prefers-reduced-motion: reduce) {
  .skeleton-line,
  .skeleton-row {
    animation: none;
  }
  * {
    transition-duration: 0.01ms !important;
  }
}

/* ── Powers / Skills catalog tweaks ────────────────────────────────────
   Both pages reuse .tool-card / .tools-grid / .tools-section so they
   inherit the Tools page styling. A few small additions for the parts
   Tools doesn't render (a one-line hint under the name, and a chip
   strip in the detail dialog). */
.tool-card__hint {
  display: block;
  margin-top: 4px;
  font-size: 0.74rem;
  line-height: 1.3;
  text-align: left;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.power-detail__kws {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0;
}
.skill-detail-dialog__body {
  max-height: 60vh;
  overflow-y: auto;
}
.skill-detail-dialog__body h1,
.skill-detail-dialog__body h2,
.skill-detail-dialog__body h3 {
  margin-top: 14px;
}
.skill-detail-dialog__body p,
.skill-detail-dialog__body li {
  font-size: 0.9rem;
  line-height: 1.45;
}

/* ── Settings sub-tabs ─────────────────────────────────────────────────
   Vertical nav rail on the left of #config-tab; the active
   `.settings-section` panel fills the space to its right. The nested
   Admin section keeps its own horizontal pill nav. */
/* Flush rail scaffold (matches Hive / App Admin): cancel the global tab padding so
   the rail sits flush to the main sidebar; the tab is a flex column and the active
   panel scrolls inside `main.settings-main` — NOT inside `.settings-section`, which it did
   until `partials/config.html` introduced that wrapper. Measured, not assumed; the
   correction and why it matters are on `.settings-section` itself, below. */
#config-tab { padding: 0 var(--page-gutter); } /* gutter so header/toolbar + body get side spaces */
.canvas #config-tab.tab-content.active { display: flex; flex-direction: column; overflow: hidden; }
.settings-layout {
  display: flex;
  align-items: stretch;
  gap: 0;
  flex: 1;
  min-height: 0;
}
.settings-subnav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 0 0 200px;
  width: 200px;
  box-sizing: border-box;
  overflow-y: auto;
  border-right: 1px solid var(--border);
  padding: 12px 10px 28px;
  background: var(--bg-surface);
}
/* Match the app's rail buttons (.hive-subnav-btn / .kriya-rail-btn / .appadmin-nav-btn):
   icon + label row, subtle hover, filled accent-soft pill when active. */
.settings-subnav__btn {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  color: var(--text-muted);
  padding: 7px 9px;
  font: inherit;
  font-size: 0.855rem;
  font-weight: 500;
  cursor: pointer;
  border-radius: 6px;
  transition: background 0.12s ease, color 0.12s ease;
}
.settings-subnav__ico { width: 18px; text-align: center; flex-shrink: 0; }
.settings-subnav__btn:hover {
  background: var(--bg-muted);
  color: var(--text);
}
/* Active state comes from the shared second-rail standard (the `[class*="subnav__btn"].is-active`
   selector), so Settings' selected link matches every other module's rail (white wash + cyan bar
   on the teal skin). No per-module active rule here — that would diverge from the standard. */

/* NO `overflow-y` HERE — IT WAS A SCROLLPORT NOTHING EVER SCROLLED, AND IT BROKE EVERY STICKY
   ELEMENT IN SETTINGS (2026-09-17, user direction: "in org Business & Modules page, apply changes
   buttons after checkbox selection should be always displayed at page top, instead of going up and
   hiding when user scrolled down the page").

   The pane declared `overflow-y: auto` back when `.settings-layout` was a direct flex child of the
   column `#config-tab`, so the pane really was height-constrained and really did scroll — which is
   what the note above this block still describes. `partials/config.html` later put
   `.settings-split` > `main.settings-main` in between, and `.settings-main` is a BLOCK: the
   `flex: 1; min-height: 0` on `.settings-layout` is inert inside one, so the layout takes its
   content height and the pane stretches to it. The scrollbar moved to `.settings-main` and nobody
   noticed, because a pane that is exactly as tall as its content looks identical either way.

   What it left behind is a box with `overflow-y: auto` that CANNOT scroll — and a sticky element
   resolves against its nearest SCROLLPORT, not against the thing the reader is scrolling. So
   `.settings-topbar`'s `position: sticky; top: 0` had nothing to stick to: the save bar travelled
   off the top of every settings page while its computed position said `sticky`, which is the
   hardest kind of dead rule to spot. MEASURED in real Chrome over the real sheet and the real
   partial (`frontend/probe/settings.html`): the pane reported scrollHeight 1741 / clientHeight
   1741 — never scrolls — while `.settings-main` reported 1789 / 813; the bar sat at y=20 unscrolled
   and y=-680 after a 700px scroll, and at a steady y=8 once this declaration was gone.

   *A sticky element travels only inside its containing block* — the law this repo already records,
   in the variant where the containing block is a scrollport that never moves. Removing it also
   retires the clipper named in the ⋯-menu note further down this sheet.

   If a settings pane ever needs its own scrollbar, give THAT pane the overflow and a height;
   putting it back here silently un-sticks every save bar in the console again. */
.settings-section {
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  /* 12px top, not 20 (2026-08-20, user direction): the crumb row's hairline is the chrome
     boundary, and content starting a full band-height below it read as a hole on every
     settings page. Matches the breathing room a module body gives its first card. */
  padding: 12px 0 40px;
}
/* A settings pane that carries the SHARED LIST TOOLBAR (Company ▸ Orgs is the first) is a
   module list page and follows that standard exactly: the crumb row hands its hairline down
   (`.smz-has-listbar`, set by lkWire, cleared by renderShell on every section switch), and
   the pane gives up its top padding so the bar sits directly beneath the crumbs — the same
   two-band chrome as Kriya's list or Orbit's Deals. */
#config-tab.smz-has-listbar .settings-section.is-active { padding-top: 0; }
/* AND THE PANE GIVES UP ITS OWN INSET TOO (2026-09-19, user report against Form layouts: *"remove
   extra spaces, extra lines, border adjustments left and right side page body spaces"*).

   TRACED, because every band in this chain is individually correct and the sum was not. The tab
   owns the left gutter (`#config-tab` padding-left 32) and gives up the right one so the scroller's
   bar can hug the window; `#config-shell` pads that 32 back for the crumb row; the section pads it
   back for the content. All three agree — and `.settings-main` then adds **4px of its own on each
   side and 8px on top**, which no other band in the chain has. Measured at 1500px: the crumb row
   spans 32 → 1468 and the toolbar under it 36 → 1464, out by exactly that 4 on both sides, with
   the 8 showing as a gap under the breadcrumb.

   ONLY FOR A PAGE CARRYING THE SHARED LIST TOOLBAR, which is the set whose whole standard is that
   the crumb row and the toolbar read as one band. A settings pane WITHOUT one keeps the 4px: it is
   breathing room around a form, where here it is a misalignment against the row above. */
#config-tab.smz-has-listbar .settings-main { padding-top: 0; padding-inline: 0; }
.settings-section[hidden] {
  display: none;
}

/* Stack the rail above the panel on narrow viewports. */
@media (max-width: 720px) {
  .settings-layout {
    flex-direction: column;
    gap: 14px;
  }
  .settings-subnav {
    flex: 0 0 auto;
    flex-direction: row;
    flex-wrap: wrap;
    width: auto;
    overflow-y: visible;
    border-right: 0;
    border-bottom: 1px solid var(--border);
    padding: 8px;
  }
  .settings-subnav__btn {
    width: auto;
    border-left: 0;
  }
}

.settings-admin-nav {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.settings-admin-nav__btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-subtle);
  padding: 6px 12px;
  font: inherit;
  font-size: 0.82rem;
  cursor: pointer;
  border-radius: 999px;
  transition: background 0.1s ease, color 0.1s ease, border-color 0.1s ease;
}
.settings-admin-nav__btn:hover {
  border-color: var(--accent, #6366f1);
}
.settings-admin-nav__btn.is-active {
  background: var(--primary);
  color: var(--primary-ink, #fff);
  border-color: var(--accent, #6366f1);
}
.settings-admin-section[hidden] {
  display: none;
}
.settings-admin-section h2 {
  font-size: 1.05rem;
  margin: 0 0 10px;
}

/* ── Workspace tab (Organizations → Projects → resources) ─────────────── */
.workspace-view .ws-section { margin: 1.25rem 0; }
/* Flush, full-bleed leads-style toolbar for Workspace section headers (no card/radius/margin). */
.workspace-view { --page-pad-x: 40px; --page-pad-top: 20px; padding: 20px 40px 40px; } /* full-width (scrollbar at page edge) + roomy gutter, per UI Polish standard. */
/* Workspace renders its page header INSIDE the scroll area, so break it out of the view
   padding to a flush, full-width header (like every other module) and tighten the gap below. */
.workspace-view > .canvas-header { margin: calc(-1 * var(--page-pad-top, 0px)) calc(-1 * var(--page-pad-x, 0px)) 0; }
/* first content sits flush under the header like every other module (kill the 10px
   .canvas-header+* default AND the first .ws-section's 1.25rem top margin) */
.workspace-view > .canvas-header + * { margin-top: 0; }
.workspace-view > .canvas-header + * > .ws-section:first-child { margin-top: 0; }
.workspace-view .ws-section__head {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: 0.6rem; margin: 0 calc(-1 * var(--page-pad-x, 0px)) 0.5rem; padding: .42rem var(--page-pad-x, .7rem);
  border: 0; border-bottom: 1px solid var(--border); border-radius: 0; background: rgba(37, 99, 235, .05);
}
.workspace-view .ws-section__head h2,
.workspace-view .ws-section__head h3 { margin: 0; font-size: 0.95rem; }
.workspace-view .ws-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; padding: 0.5rem 0.75rem; border: 1px solid var(--border, #2a2a2a);
  border-radius: 8px; margin-bottom: 0.4rem;
}
.workspace-view .ws-row__actions { display: flex; gap: 0.5rem; align-items: center; }
/* Invites: a stacked row (email + revoke line, then the shareable invite code). */
.workspace-view .ws-invite-row { flex-direction: column; align-items: stretch; gap: 0.5rem; }
.workspace-view .ws-invite-top { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.workspace-view .ws-invite-code { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
  padding-top: 0.5rem; border-top: 1px dashed var(--border, #2a2a2a); }
.workspace-view .ws-invite-code code { flex: 1; min-width: 140px; word-break: break-all;
  background: var(--hover, #f1f5f9); border: 1px solid var(--border, #e3e8ef); border-radius: 6px; padding: 0.15rem 0.5rem; font-size: 0.8rem; }
/* Domains: a stacked row (header line + DNS-record verify block). */
.workspace-view .ws-domain { flex-direction: column; align-items: stretch; gap: 0.5rem; }
.workspace-view .ws-domain__top { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; }
.workspace-view .ws-domain__ok { color: var(--success, #047857); }
.workspace-view .ws-domain__verify { display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-start; }
.workspace-view .ws-domain__rec {
  display: flex; align-items: center; gap: 0.5rem; width: 100%;
  padding: 0.4rem 0.6rem; background: var(--bg-muted); border: 1px solid var(--border); border-radius: 7px;
}
.workspace-view .ws-domain__rec code {
  flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; font-size: 0.82rem; color: var(--text);
}
/* Project/org card + its action controls sit side by side; the card grows. */
.workspace-view .ws-project { position: relative; display: flex; align-items: stretch; gap: 0.4rem; }
.workspace-view .ws-project .tool-card { flex: 1 1 auto; min-width: 0; }
.workspace-view .ws-project__del { flex: 0 0 auto; align-self: flex-start; }
.workspace-view .ws-project__actions { flex: 0 0 auto; display: flex; flex-direction: column; gap: 0.25rem; align-self: flex-start; }
/* Lifecycle buttons (rename / set-default / delete) under a detail header. */
.workspace-view .ws-header-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.4rem; }
.workspace-view .ws-back {
  display: inline-block; margin-bottom: 0.5rem; font-size: 0.85rem;
  color: var(--muted, #9aa0a6); text-decoration: none;
}
.workspace-view .ws-back:hover { text-decoration: underline; }
.workspace-view .ws-subtabs {
  display: flex; flex-wrap: wrap; gap: 0.25rem; margin: 0.75rem 0;
  border-bottom: 1px solid var(--border, #2a2a2a);
}
.workspace-view .ws-subtab {
  background: none; border: none; padding: 0.4rem 0.7rem; cursor: pointer;
  color: var(--muted, #9aa0a6); border-bottom: 2px solid transparent;
  text-transform: capitalize; font-size: 0.85rem;
}
.workspace-view .ws-subtab.is-active { color: var(--text, #e8eaed); border-bottom-color: var(--accent, #8ab4f8); }

/* Sidebar brand → home link (inherits the heading look, no underline). */
.sidebar-header .brand-home {
  display: inline-flex; align-items: center; gap: 0.4rem;
  color: inherit; text-decoration: none; cursor: pointer;
}
.sidebar-header .brand-home:hover { opacity: 0.85; }
/* Brand wordmark ↔ active-org cross-slide (see features/org/brandSwap.js). The
   label gently slides out left + back in from the right on each swap. Clipped so
   the horizontal travel never overflows the sidebar header. */
.brand-home { overflow: hidden; }
.brand-swap {
  display: inline-block; white-space: nowrap; max-width: 11rem;
  overflow: hidden; text-overflow: ellipsis;
  transition: transform 0.55s ease, opacity 0.55s ease; will-change: transform, opacity;
}
.brand-swap--out { transform: translateX(-16px); opacity: 0; }
.brand-swap--in { transform: translateX(16px); opacity: 0; transition: none; }
@media (prefers-reduced-motion: reduce) {
  .brand-swap, .brand-swap--out, .brand-swap--in { transition: opacity 0.3s ease; transform: none; }
}

/* ── Members & Access (IAM) — Settings → access ───────────────────────── */
.access-root .access-picker {
  display: flex; align-items: center; gap: 0.6rem; margin: 0.75rem 0;
}
.access-root .access-picker > span { font-weight: 600; font-size: 0.9rem; }
.access-root .access-picker select { padding: 0.35rem 0.5rem; }
.access-section { margin: 1.25rem 0; }
.access-section__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; }
.access-section__head h3 { margin: 0; font-size: 0.95rem; }
.access-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.75rem; padding: 0.5rem 0.75rem; border: 1px solid var(--border, #2a2a2a);
  border-radius: 8px; margin-bottom: 0.4rem;
}
.access-row__actions { display: flex; align-items: center; gap: 0.5rem; }
.access-row__actions select { padding: 0.25rem 0.4rem; }
.access-invite { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.6rem; flex-wrap: wrap; }
.access-invite input[type="email"] { flex: 1 1 240px; padding: 0.4rem 0.5rem; }
.access-invites { margin-top: 0.75rem; }
.access-invites h4 { margin: 0 0 0.4rem; font-size: 0.85rem; color: var(--muted, #9aa0a6); }

/* "📖 Learn more" docs link — a subtle pill pinned to the header's far-right
   corner. Replaces the old inline descriptions on pages + builder panels. */
.canvas-header:has(.canvas-ref-link) {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}
.canvas-header:has(.canvas-ref-link) > h1 { margin-right: auto; } /* push the rest right */
.node-panel__head { display: flex; align-items: center; gap: 0.5rem; }

.canvas-ref-link {
  order: 9; /* after the title + any action button → the rightmost item */
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 500;
  line-height: 1;
  color: var(--muted, #9aa0a6);
  text-decoration: none;
  padding: 0.32rem 0.7rem;
  border: 1px solid var(--border, #2a2a2a);
  border-radius: 999px;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.canvas-ref-link:hover {
  color: var(--text, #e8eaed);
  border-color: var(--accent, #8ab4f8);
  background: color-mix(in srgb, var(--accent, #8ab4f8) 14%, transparent);
  text-decoration: none;
}
.node-panel__head .canvas-ref-link { margin-left: auto; }
/* Docs anchors: offset so smooth-scroll targets aren't flush to the top. */
.doc-article h2[id] { scroll-margin-top: 1rem; }

/* ── "Start from a template" popup ────────────────────────────────────── */
.templates-dialog {
  border: 1px solid var(--border); border-radius: 14px; padding: 0; max-width: 760px; width: 92vw;
  background: var(--bg-surface); color: var(--text);
  box-shadow: 0 20px 60px rgba(17, 24, 39, 0.22);
}
.templates-dialog::backdrop { background: rgba(17, 24, 39, 0.45); }
.templates-dialog__card { display: flex; flex-direction: column; max-height: 80vh; }
.templates-dialog__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.9rem 1.1rem; border-bottom: 1px solid var(--border);
}
.templates-dialog__head h3 { margin: 0; font-size: 1rem; color: var(--text); }
.templates-dialog__close {
  background: none; border: none; color: var(--text-subtle);
  font-size: 1.1rem; cursor: pointer; line-height: 1;
}
.templates-dialog__close:hover { color: var(--text); }
.templates-dialog__body { padding: 1.1rem; overflow-y: auto; }

/* Report-an-issue dialog (reuses the .templates-dialog shell). */
.report-dialog { max-width: 480px; }
.report-dialog__body { display: flex; flex-direction: column; gap: 0.9rem; }
.report-field { display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; color: var(--text-muted); }
.report-input {
  font: inherit; font-size: 0.9rem; color: var(--text);
  padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-surface); resize: vertical;
}
.report-input:focus { outline: none; border-color: var(--primary); box-shadow: inset 0 0 0 2px var(--focus-ring); }
.report-dialog__foot { display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem; margin-top: 0.2rem; }
.report-foot__spacer { flex: 1; }
.report-dialog__main { display: flex; flex-direction: column; gap: 0.9rem; min-width: 0; }
/* The report form and the user's reports SWAP — only one shows at a time. */
.report-mine { display: none; max-height: 60vh; overflow: auto; min-width: 0; }
.report-mine__head { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 12px; }
.report-mine__title { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-faint); }
/* Reports view: wider, form hidden, reports shown. */
.report-dialog--reports { max-width: 820px; }
.report-dialog--reports .report-dialog__main { display: none; }
.report-dialog--reports .report-mine { display: block; }
/* One report's conversation — third dialog state, swapped in over the list. */
.report-thread { display: none; max-height: 60vh; min-width: 0; flex-direction: column; }
.report-dialog--thread { max-width: 820px; }
.report-dialog--thread .report-dialog__main, .report-dialog--thread .report-mine { display: none; }
.report-dialog--thread .report-thread { display: flex; }
.report-thread__meta { font-size: .78rem; color: var(--text-muted); padding: 6px 2px 8px; border-bottom: 1px solid var(--border-muted); }
.report-thread__list { flex: 1 1 auto; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding: 12px 2px; min-height: 120px; }
.report-thread__msg { max-width: 78%; padding: 8px 12px; border-radius: 12px; font-size: .8437rem; line-height: 1.4; white-space: pre-wrap; overflow-wrap: anywhere; }
.report-thread__msg.is-you { align-self: flex-end; background: var(--primary); color: var(--primary-ink, #fff); border-bottom-right-radius: 4px; }
.report-thread__msg.is-support, .report-thread__msg.is-reporter { align-self: flex-start; background: var(--bg-hover); color: var(--text-normal); border-bottom-left-radius: 4px; }
.report-thread__who { display: block; font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .75; margin-bottom: 2px; }
.report-thread__when { display: block; font-size: .66rem; opacity: .65; margin-top: 3px; }
.report-thread__composer { display: flex; gap: 8px; align-items: flex-end; padding-top: 10px; border-top: 1px solid var(--border-muted); }
.report-thread__composer textarea { flex: 1 1 auto; resize: vertical; }
.report-mine__row { cursor: pointer; }
.report-mine__row:hover td { background: var(--bg-hover); }
/* App Admin's side of the same thread, inline under the issue row. */
.appadmin-thread { max-width: 720px; padding: 8px 4px 10px; }
.appadmin-thread .report-thread__list { max-height: 320px; }

/* Generic prompt dialog (utils/dialog.js). Reuses the report-field/input styles. */
.prompt-dialog { max-width: 460px; }
#prompt-dialog-label { white-space: pre-wrap; }

/* ── Data & Compliance settings section ─────────────────────────────────── */
.comp-form { display: flex; flex-direction: column; gap: 0.5rem; }
.comp-form--inline { flex-flow: row wrap; align-items: center; }
.comp-form--inline input, .comp-form--inline select { flex: 1 1 8rem; min-width: 7rem; }
.comp-field { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.8rem; color: var(--text-muted); }
.comp-field input, .comp-field select, .comp-form input, .comp-form select {
  font: inherit; font-size: 0.85rem; color: var(--text); padding: 0.4rem 0.55rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface);
}
.comp-field input:focus, .comp-field select:focus, .comp-form input:focus, .comp-form select:focus {
  outline: none; border-color: var(--primary); box-shadow: inset 0 0 0 2px var(--focus-ring);
}
.comp-check { display: flex; align-items: center; gap: 0.45rem; font-size: 0.85rem; color: var(--text); }
.comp-hint { font-size: 0.76rem; color: var(--text-subtle); margin: 0.2rem 0 0; }
.comp-muted { color: var(--text-subtle); font-size: 0.82rem; }
.comp-summary { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; font-size: 0.85rem; color: var(--text); }
.comp-list { display: flex; flex-direction: column; gap: 0.3rem; margin-top: 0.6rem; }
.comp-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.55rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); }
.comp-row-main { flex: 1; min-width: 0; font-size: 0.84rem; color: var(--text); display: flex; flex-direction: column; gap: 0.1rem; }
.comp-row-actions { display: inline-flex; gap: 0.35rem; flex: 0 0 auto; }
.comp-decls { display: grid; grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); gap: 0.4rem; }
.comp-badge { font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 0.1rem 0.45rem; border-radius: 999px; background: var(--bg-muted); color: var(--text-subtle); }
.comp-badge--warn, .comp-badge--high, .comp-badge--critical { background: #fef3c7; color: #92400e; }
.comp-badge--critical { background: #fee2e2; color: #991b1b; }
.comp-badge--ok { background: #dcfce7; color: #166534; }
.comp-status { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; margin: 0 0 0.7rem; }
.comp-inline-sel { font: inherit; font-size: 0.75rem; padding: 0.15rem 0.35rem; border: 1px solid var(--border);
  border-radius: 6px; background: var(--bg-surface); color: var(--text); }

/* Active sessions (Settings → Privacy & Data → features/account/sessions.js). */
.sessions-list { margin-top: 0.6rem; }
.sessions-ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.3rem; }
.sessions-row { display: flex; align-items: center; gap: 0.6rem; padding: 0.45rem 0.55rem;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); }
.sessions-row.is-current { border-color: var(--border-strong); }
.sessions-row__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.sessions-row__device { font-size: 0.85rem; font-weight: 600; color: var(--text); }
.sessions-row__meta { font-size: 0.74rem; color: var(--text-muted); }
.sessions-row .btn-sm { flex: 0 0 auto; }

/* Two-factor auth (Settings → Privacy & Data → features/account/mfa.js). */
.mfa-card { margin-top: 0.6rem; }
.mfa-status { font-size: 0.85rem; color: var(--text); margin: 0 0 0.6rem; }
.mfa-status.mfa-on strong { color: #166534; }
.mfa-form { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-top: 0.4rem; }
.mfa-form input { font: inherit; font-size: 0.85rem; padding: 0.4rem 0.5rem; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.mfa-secret { margin: 0.4rem 0; padding: 0.5rem 0.6rem; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-muted, var(--bg-surface)); }
.mfa-secret code { font-size: 1rem; letter-spacing: 0.12em; word-break: break-all; color: var(--text); }
.mfa-codes { list-style: none; margin: 0.5rem 0; padding: 0.6rem 0.7rem; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg-surface); display: grid; grid-template-columns: 1fr 1fr; gap: 0.25rem 1rem; }
.mfa-codes li { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.9rem; color: var(--text); }

/* Generic confirm dialog (utils/dialog.js). */
.confirm-dialog { max-width: 440px; }
.confirm-dialog__message { margin: 0; font-size: 0.9rem; line-height: 1.5; color: var(--text); white-space: pre-wrap; }
#confirm-ok.danger-button { background: var(--danger, #dc2626); border-color: var(--danger, #dc2626); color: #fff; }
#confirm-ok.danger-button:hover { background: var(--danger-strong, #b91c1c); border-color: var(--danger-strong, #b91c1c); }

/* ── Agent builder: per-agent attachment editor (Tools/Powers/Skills) ───── */
.agent-attach { display: flex; flex-direction: column; gap: 0.6rem; }
.agent-attach-add-row { display: flex; flex-direction: column; gap: 0.4rem; }
.agent-attach-add-row .agent-attach-add { width: 100%; padding: 0.4rem 0.5rem; }
.agent-attach-search-wrap { position: relative; }
.agent-attach-search-wrap .agent-attach-search {
  width: 100%;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border, #2a2a2a);
  border-radius: 8px;
  background: var(--bg-input, var(--bg-surface, #fff));
  font: inherit;
  font-size: 0.85rem;
}
/* Type-ahead results popup under the search box. */
.agent-attach-results {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 20;
  margin-top: 2px;
  max-height: 240px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border, #2a2a2a);
  border-radius: 8px;
  background: var(--bg-surface, #fff);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.agent-attach-result {
  text-align: left;
  padding: 0.45rem 0.6rem;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border, #2a2a2a);
  color: var(--text, #e8eaed);
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
}
.agent-attach-result:last-child { border-bottom: none; }
.agent-attach-result:hover,
.agent-attach-result:focus { background: var(--bg-hover, rgba(127, 127, 127, 0.15)); }
.agent-attach-result--empty { color: var(--muted, #9aa0a6); cursor: default; }
.agent-attach-result--empty:hover { background: none; }
/* Required-field asterisk in the Agent details panel. */
.req { color: var(--danger, #d9534f); font-weight: 600; margin-left: 1px; }

/* Org/Project selector row + inline "＋ New" button. */
.agent-scope-row { display: flex; gap: 6px; align-items: center; }
.agent-scope-row select { flex: 1 1 auto; min-width: 0; }
.agent-scope-row .agent-scope-new { flex: 0 0 auto; white-space: nowrap; }
/* Inline create-org / create-project panel. */
/* `:not([hidden])` so the `hidden` attribute (display:none) wins — without it
   this rule's display:flex overrides [hidden] and the box can't be closed. */
.agent-scope-create:not([hidden]) { display: flex; gap: 6px; align-items: center; margin-top: 8px; }
.agent-scope-create input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--border, #2a2a2a);
  border-radius: 8px;
  background: var(--bg-input, var(--bg-surface, #fff));
  font: inherit;
  font-size: 0.85rem;
}
.agent-scope-status { margin-top: 4px; min-height: 1em; }
.agent-attach-list { display: flex; flex-direction: column; gap: 0.4rem; }
.agent-attach-row { border: 1px solid var(--border, #2a2a2a); border-radius: 8px; }
.agent-attach-row.is-open { border-color: var(--accent, #8ab4f8); }
.agent-attach-row__head {
  display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.4rem 0.5rem;
}
.agent-attach-name {
  background: none; border: none; color: var(--text, #e8eaed); cursor: pointer;
  text-align: left; flex: 1; font-size: 0.9rem; padding: 0;
}
.agent-attach-name:hover { text-decoration: underline; }
.agent-attach-detail {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 0.6rem 0.6rem 0.7rem; border-top: 1px solid var(--border, #2a2a2a);
}
.agent-attach-field { display: flex; flex-direction: column; gap: 0.2rem; }
.agent-attach-field > span { font-size: 0.8rem; color: var(--muted, #9aa0a6); }
.agent-attach-field input,
.agent-attach-field select,
.agent-attach-field textarea {
  width: 100%; padding: 0.35rem 0.5rem; font: inherit;
}
.agent-attach-field textarea { resize: vertical; min-height: 2.5rem; font-family: ui-monospace, monospace; font-size: 0.82rem; }

/* Docs page — "On this page" table of contents + section polish. */
.doc-toc {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem;
  margin: 0 0 1.5rem; padding: 0.85rem 1rem;
  background: var(--bg-muted, rgba(255,255,255,0.03));
  border: 1px solid var(--border, #2a2a2a); border-radius: 12px;
}
.doc-toc__label {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--muted, #9aa0a6); margin-right: 0.4rem;
}
.doc-toc a {
  font-size: 0.82rem; color: var(--text, #e8eaed); text-decoration: none;
  padding: 0.25rem 0.6rem; border-radius: 999px;
  border: 1px solid var(--border, #2a2a2a);
}
.doc-toc a:hover {
  border-color: var(--accent, #8ab4f8);
  background: color-mix(in srgb, var(--accent, #8ab4f8) 14%, transparent);
}
.doc-article h2[id] {
  scroll-margin-top: 1rem;
  margin-top: 2rem; padding-top: 1.2rem;
  border-top: 1px solid var(--border, #2a2a2a);
}
.doc-article h2[id]:first-of-type { border-top: none; padding-top: 0; margin-top: 1rem; }

/* ── Org logo + DNA-swap fallback (M4) ─────────────────────────────────── */
.org-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--ol-size, 40px); height: var(--ol-size, 40px);
  position: relative; flex: none; border-radius: 8px; overflow: hidden;
  vertical-align: middle;
}
.org-logo__img { width: 100%; height: 100%; object-fit: cover; border-radius: 8px; display: block; }
/* No-logo fallback: a static badge with the org's first letter (no animation). */
.org-logo--initial {
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent, #8ab4f8) 22%, transparent), transparent);
  color: var(--accent, #8ab4f8);
  font-weight: 800;
  font-size: calc(var(--ol-size, 40px) * 0.46);
  line-height: 1;
}
.ws-org-identity { display: flex; align-items: center; gap: 0.6rem; }
.ws-org-identity h1 { margin: 0; }
.ws-logo-edit { display: inline-flex; gap: 0.4rem; align-items: center; }

/* ============================================================================
   Work module (/work) — Asana-like PM surface. Namespaced `.wk-*`. Uses the
   shared theme tokens (var(--…)) so it adapts to dark mode automatically.
   ========================================================================== */

.wk-header { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.wk-header__spacer { flex: 1 1 auto; }
.wk-nav { display: inline-flex; gap: 4px; background: var(--bg-muted); padding: 3px; border-radius: 10px; }
.wk-nav__item {
  border: 0; background: transparent; color: var(--text-subtle); font: inherit; font-size: 13px;
  font-weight: 600; padding: 6px 12px; border-radius: 8px; cursor: pointer; transition: all .12s;
}
.wk-nav__item:hover { color: var(--text); background: var(--bg-hover); }
.wk-nav__item.is-active { color: var(--text); background: var(--bg-surface); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.wk-org-picker { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-subtle); }
.wk-org-picker select { font: inherit; font-size: 13px; padding: 5px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }

.wk-view { padding-top: 0; }

/* Kriya second-left-menu shell (mirrors the Orbit/Flux/Hive rail pattern): header
   on top, then [rail | board]. The board keeps its own scroll inside .kriya-main. */
/* Cancel the global `.tab-content { padding: 24px 28px }` so the rail sits flush
   against the main sidebar (no gap) and the content starts at the top — matching
   Hive's `#hive-tab { padding: 0 }`. Inner padding lives on .kriya-main. */
#kriya-tab { padding: 0; }
.canvas #kriya-tab.tab-content.active { display: flex; flex-direction: column; overflow: hidden; }
.kriya-shell { flex: 1; display: flex; min-height: 0; }
/* Kriya's second rail is retired — navigation (Task Board / Risks) + all menus live in the
   board header, so the rail aside is fully hidden and the main board fills the shell width. */
.kriya-rail--hidden { display: none !important; }
/* Kriya's rail container comes from the shared .page-left-rail (no per-rail settings).
   Forge shares the exact same rail-button styling (.forge-nav-btn / .forge-nav-ico) so its
   second rail matches every other module. */
.kriya-rail-btn, .forge-nav-btn { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; border: 0;
  background: transparent; color: var(--text-muted); font: inherit; font-size: .78rem; font-weight: 500;
  padding: 7px 9px; border-radius: 6px; cursor: pointer; }
.kriya-rail-btn:hover, .forge-nav-btn:hover { background: var(--bg-muted); color: var(--text); }
.kriya-rail-btn.is-active, .forge-nav-btn.is-active { background: rgba(37, 99, 235,.10); color: var(--primary); font-weight: 600; box-shadow: inset 2px 0 0 var(--primary); }
.kriya-rail-ico, .forge-nav-ico { width: 18px; text-align: center; flex-shrink: 0; }
/* Section-group headers — match Orbit's .orbit-nav-label (small, uppercase, muted, no icon). */
.kriya-rail-label { margin: 6px 9px 4px; font-size: .62rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); }
/* Collapsible groups (Planning / Reports & automation / Setup), expanded by default — mirrors Orbit + Hive. */
.kriya-rail-group { margin: 0; }
.kriya-rail-summary { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  cursor: pointer; list-style: none; user-select: none; margin: 12px 0 3px; padding: 6px 9px;
  border-radius: 6px; font-size: .62rem; font-weight: 700; color: var(--text-subtle); }
.kriya-rail-summary::-webkit-details-marker { display: none; }
.kriya-rail-summary:hover { color: var(--text); background: var(--bg-muted); }
.kriya-rail-chev { font-size: 1.3rem; color: var(--text-faint); flex-shrink: 0; transition: transform .15s ease; }
.kriya-rail-group:not([open]) .kriya-rail-chev { transform: rotate(-90deg); }
/* overflow-x:hidden so a wide table / stray element can't push a horizontal scrollbar onto the whole
   page (the list table keeps its OWN internal scroll; full-bleed toolbars reach exactly this edge). */
/* NO SIDE PADDING OF ITS OWN (2026-09-15). The tab already gutters the whole module body, and
   this element added 24px on top of it — so content sat 24px in from the chrome above it while
   the full-bleed toolbar, which cancels exactly this variable, landed back on the chrome's edge.
   Both the base rule and its `.canvas-body` override are changed together: they disagreed, so
   which one won depended on how deep the module's markup nested, and two answers to "where is
   the edge" is the whole defect. */
.kriya-main { flex: 1; min-width: 0; overflow-x: hidden; overflow-y: auto; padding: 20px 0 36px; --page-pad-x: 0px; --page-pad-top: 20px; }

/* ══ Shared second-rail standard (Orbit pattern) ═══════════════════════════════
   ONE source of truth for every module's secondary rail (.page-left-rail /
   .workspace-rail). Items, icons, group labels, collapsible <details> sections and
   chevrons all render identically — the Orbit look is the standard. Selectors are
   class-agnostic ([class*="nav-btn"|"nav-ico"|"nav-label"|"nav-summary"|"nav-chev"|
   "nav-group"] + workspace's __item/__child/__icon/__summary/__chev/__group) and
   carry enough specificity to beat any per-module injected rule, so all modules —
   and any future one — share this exact style. */

/* Items (top-level + grouped) */
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="nav-btn"],
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="rail-btn"],
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav-btn"],
.workspace-rail .workspace-rail__item,
.workspace-rail .workspace-rail__child {
  /* Sized to match the first rail's .sidebar-link exactly (gap/padding/font). */
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; border: 0;
  background: transparent; color: var(--text-muted); font: inherit; font-size: .8rem;
  font-weight: 500; padding: 8px 11px; border-radius: 7px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="nav-btn"]:hover,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="rail-btn"]:hover,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav-btn"]:hover,
.workspace-rail .workspace-rail__item:hover,
.workspace-rail .workspace-rail__child:hover {
  background: var(--bg-muted); color: var(--text);
}
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="nav-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="rail-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav__btn"].is-active,
.workspace-rail .workspace-rail__item.is-active,
.workspace-rail .workspace-rail__child.is-active {
  background: rgba(37, 99, 235, 0.10); color: var(--primary); font-weight: 600;
  box-shadow: inset 2px 0 0 var(--primary);
}
.workspace-rail .workspace-rail__child { padding-left: 36px; }  /* keep tree indent */

/* Item icons / illustrations — box + the hand-drawn SVG sized uniformly to match the
   first rail's .sidebar-link-icon (17px hicon), so any module's rail icon matches
   regardless of its per-module class (fixes e.g. lens-nav-ico, which wasn't in the
   hard-coded .hicon size list above and so rendered ~15px). */
:is(.page-left-rail, .workspace-rail) [class*="nav-ico"],
:is(.page-left-rail, .workspace-rail) [class*="rail-ico"],
.workspace-rail .workspace-rail__icon {
  flex-shrink: 0; width: 17px; height: 17px; text-align: center; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}
:is(.page-left-rail, .workspace-rail) [class*="nav-ico"] .hicon,
:is(.page-left-rail, .workspace-rail) [class*="rail-ico"] .hicon,
.workspace-rail .workspace-rail__icon .hicon { width: 17px; height: 17px; }
/* Collapsed icon size + its box match the first rail's collapsed .sidebar-link-icon (23px
   hicon in an auto box), so the two rails' icons look the same. */
:is(.page-left-rail, .workspace-rail).is-collapsed [class*="nav-ico"],
:is(.page-left-rail, .workspace-rail).is-collapsed [class*="rail-ico"],
.workspace-rail.is-collapsed .workspace-rail__icon { width: auto; height: auto; }
/* EVERY collapsed second-rail icon = 24px (matches the first rail), regardless of the module's
   icon markup (nav-ico / rail-ico / bare aria-hidden span / any other). The [class] on the
   button lifts specificity above the per-module rules so nothing renders smaller/larger. */
:is(.page-left-rail, .workspace-rail).is-collapsed :is(button, a)[class] .hicon,
:is(.page-left-rail, .workspace-rail).is-collapsed [class*="nav-ico"] .hicon,
:is(.page-left-rail, .workspace-rail).is-collapsed [class*="rail-ico"] .hicon,
.workspace-rail.is-collapsed .workspace-rail__icon .hicon { width: 25px; height: 25px; }

/* Collapsible groups (native <details>) */
:is(.page-left-rail, .workspace-rail) [class*="nav-group"],
.workspace-rail .workspace-rail__group { width: 100%; }
:is(.page-left-rail, .workspace-rail) summary { list-style: none; }
:is(.page-left-rail, .workspace-rail) summary::-webkit-details-marker { display: none; }

/* Group / section labels (both plain labels and <summary> headers) */
:is(.page-left-rail, .workspace-rail) [class*="nav-label"],
:is(.page-left-rail, .workspace-rail) [class*="nav-summary"],
.workspace-rail .workspace-rail__summary {
  display: flex; align-items: center; justify-content: space-between;
  margin: .7rem 0 .15rem .25rem; font-size: .62rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--text-subtle);
}
:is(.page-left-rail, .workspace-rail) :is(summary)[class*="nav-label"],
:is(.page-left-rail, .workspace-rail) :is(summary)[class*="nav-summary"],
.workspace-rail summary.workspace-rail__summary { cursor: pointer; }

/* Section collapse chevron */
:is(.page-left-rail, .workspace-rail) [class*="nav-chev"],
.workspace-rail .workspace-rail__chev {
  margin-left: auto; flex-shrink: 0; font-size: 1.1rem; color: var(--text-faint);
  transition: transform .15s ease;
}
:is(.page-left-rail, .workspace-rail) [class*="nav-group"]:not([open]) [class*="nav-chev"],
.workspace-rail .workspace-rail__group:not([open]) .workspace-rail__chev {
  transform: rotate(-90deg);
}
/* ── Hive rail: nested ERP-taxonomy sub-modules + disabled placeholder leaves ──
   Level-1 areas use .hive-nav-group (styled by the shared rail standard above);
   each holds level-2 .hive-nav-subgroup <details> whose header (.hive-nav-sublabel)
   is smaller/indented and whose leaf buttons are further indented. .is-placeholder
   leaves are taxonomy slots with no view yet (no data-hive-view) — dimmed + inert. */
.page-left-rail .hive-nav-subgroup { width: 100%; }
.page-left-rail summary.hive-nav-sublabel {
  display: flex; align-items: center; justify-content: space-between;
  margin: .3rem 0 .1rem; padding-left: 1.15rem;
  font-size: .66rem; font-weight: 600; letter-spacing: .03em;
  text-transform: uppercase; color: var(--text-faint); cursor: pointer;
}
.page-left-rail .hive-nav-subgroup:not([open]) .hive-nav-chev { transform: rotate(-90deg); }
.page-left-rail .hive-nav-subgroup .hive-subnav-btn { padding-left: 1.7rem; font-size: .845rem; }
.page-left-rail .hive-subnav-btn.is-placeholder,
.page-left-rail .hive-subnav-btn.is-placeholder:hover {
  opacity: .42; cursor: default; pointer-events: none;
  background: none; color: var(--text-muted); font-weight: 500;
}

/* ── Hive org-unit master-data tables (Divisions/Departments/Geo) ─────────── */
.hive-table { width: 100%; border-collapse: collapse; font-size: .86rem; margin: 2px 0 14px; }
.hive-table th, .hive-table td {
  text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.hive-table th { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; color: var(--text-subtle); font-weight: 700; }
.hive-table tbody tr:hover { background: var(--bg-muted); }
.hive-ou-actions { text-align: right; white-space: nowrap; }
.hive-ou-actions .ghost-button { margin-left: 4px; }
.hive-geo-h { font-size: .9rem; font-weight: 700; margin: 14px 0 4px; }
.hive-pill-warn { background: #fef3c7; color: #92400e; border-radius: 999px; padding: 1px 8px; font-size: .7rem; font-weight: 600; }
.hive-ou-members { list-style: none; margin: 0 0 12px; padding: 0; }
.hive-ou-member { display: flex; align-items: center; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid var(--border); }
.hive-ou-add { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.hive-ou-add select { flex: 1; }

.wk-empty { color: var(--text-faint); font-size: 13px; padding: 18px; text-align: center; }
.wk-muted { color: var(--text-subtle); font-size: 12px; }

/* Summary stat strip */
.wk-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 22px; }
.wk-stat { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; }
.wk-stat__num { font-size: 26px; font-weight: 700; line-height: 1.1; color: var(--text); }
.wk-stat__label { font-size: 12px; color: var(--text-subtle); margin-top: 2px; }
.wk-stat--alert .wk-stat__num { color: var(--danger); }
.wk-stat--alert { border-color: rgba(185,28,28,.3); background: rgba(185,28,28,.05); }

/* Section blocks */
.wk-section { margin-bottom: 26px; }
.wk-section__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.wk-section__head h2 { font-size: 15px; font-weight: 700; margin: 0; color: var(--text); }

/* Card grids (projects, portfolios) */
.wk-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); gap: 12px; }
.wk-card {
  display: block; width: 100%; text-align: left; background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 12px; padding: 14px; cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .05s;
}
.wk-card:hover { border-color: var(--border-strong); box-shadow: 0 2px 10px rgba(0,0,0,.06); }
.wk-card:active { transform: translateY(1px); }
.wk-card__head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.wk-card__icon { font-size: 18px; }
.wk-card__title { font-size: 14px; font-weight: 650; color: var(--text); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-card__meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; font-size: 12px; color: var(--text-subtle); }

/* Progress bar */
.wk-progress { height: 7px; background: var(--bg-muted); border-radius: 99px; overflow: hidden; }
.wk-progress__fill { height: 100%; background: var(--success); border-radius: 99px; transition: width .25s; }
.wk-progress__label { font-size: 11px; color: var(--text-subtle); margin-top: 4px; }

/* Status pills (project / goal state) */
.wk-pill { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 99px; white-space: nowrap; }
.wk-pill--on_track { background: rgba(22,101,52,.12); color: var(--success); }
.wk-pill--at_risk  { background: rgba(180,83,9,.14); color: var(--warn); }
.wk-pill--off_track{ background: rgba(185,28,28,.12); color: var(--danger); }
.wk-pill--on_hold  { background: var(--bg-muted); color: var(--text-subtle); }
.wk-pill--complete { background: rgba(22,101,52,.12); color: var(--success); }

/* Priority flags */
.wk-prio { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 600; padding: 1px 6px; border-radius: 6px; }
.wk-prio--none { display: none; }
.wk-prio--low    { background: rgba(100,116,139,.14); color: var(--text-subtle); }
.wk-prio--medium { background: rgba(30,64,175,.12); color: var(--primary); }
.wk-prio--high   { background: rgba(180,83,9,.16); color: var(--warn); }
.wk-prio--urgent { background: rgba(185,28,28,.14); color: var(--danger); }

/* Due chips */
.wk-due { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 6px; background: var(--bg-muted); color: var(--text-subtle); white-space: nowrap; }
.wk-due--today { background: rgba(180,83,9,.14); color: var(--warn); }
.wk-due--overdue { background: rgba(185,28,28,.14); color: var(--danger); }

/* Avatars */
.wk-avatar {
  display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px;
  border-radius: 99px; font-size: 10px; font-weight: 700; color: #fff; flex: none; text-transform: uppercase;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.06);
}
.wk-avatar--sm { width: 20px; height: 20px; font-size: 9px; }
.wk-avatar--lg { width: 32px; height: 32px; font-size: 12px; }
.wk-avatar--none { background: var(--bg-muted) !important; color: var(--text-faint); box-shadow: inset 0 0 0 1px var(--border); }
/* Person chip = avatar + full name; the name truncates with an ellipsis when long. */
.wk-person { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; vertical-align: middle; }
.wk-person__name { min-width: 0; max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--text); }
/* Photo avatar: the <img> overlays the initials chip; if it fails to load, onerror removes it. */
.wk-avatar--photo { position: relative; overflow: hidden; }
.wk-avatar--photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* Agent (chat-agent) assignee — a bot chip instead of a human avatar. */
.wk-agent-chip {
  display: inline-flex; align-items: center; gap: 4px; max-width: 160px;
  padding: 1px 7px 1px 5px; border-radius: 99px; font-size: 11px; font-weight: 600;
  color: var(--primary); background: rgba(99, 102, 241, 0.12);
  box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.30); flex: none;
}
.wk-agent-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-agent-chip.wk-avatar--sm { font-size: 10px; padding: 0 6px 0 4px; }
.wk-status-dot { width: 7px; height: 7px; border-radius: 99px; flex: none; background: var(--text-faint); }
.wk-status-dot--idle { background: var(--text-faint); }
.wk-status-dot--running { background: var(--primary); }
.wk-status-dot--waiting { background: var(--warning, #f59e0b); }
.wk-status-dot--error { background: var(--danger, #ef4444); }
.wk-agent-badge {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--primary); background: rgba(99, 102, 241, 0.12); border-radius: 5px; padding: 1px 5px;
}
.wk-agentpanel {
  margin: 14px 0; padding: 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-muted); display: flex; flex-direction: column; gap: 6px;
}
.wk-agentpanel__head { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.wk-agentpanel__row { font-size: 12px; color: var(--text); }
.wk-agentpanel__actions { display: flex; align-items: center; gap: 8px; margin-top: 4px; flex-wrap: wrap; }
.wk-agentpanel__actions .wk-muted { font-size: 11px; }
.wk-agentpanel__row select, .wk-agentpanel__row input[type="time"] { font: inherit; font-size: 12px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); color: var(--text); }
.wk-sched { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 2px 0; }
.wk-presetrow { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.wk-chipbtn { font: inherit; font-size: 11px; padding: 2px 8px; border: 1px solid var(--border); border-radius: 99px; background: var(--bg-surface); color: var(--text-subtle); cursor: pointer; }
.wk-chipbtn:hover { border-color: var(--primary); color: var(--primary); }
.wk-task__chip--recurring { color: var(--primary); }

/* Continuous-monitor trigger rules */
.wk-rules { display: flex; flex-direction: column; gap: 4px; margin: 2px 0; }
.wk-rule { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); }
.wk-rule__txt { min-width: 0; }
.wk-rule__x { border: none; background: none; color: var(--text-faint); cursor: pointer; font-size: 12px; flex: none; }
.wk-rule__x:hover { color: var(--danger); }

/* Label chips */
.wk-label { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 99px; }
.wk-label__x { cursor: pointer; opacity: .6; }
.wk-label__x:hover { opacity: 1; }
.wk-label--slate { background: rgba(100,116,139,.16); color: #475569; }
.wk-label--red { background: rgba(185,28,28,.14); color: #b91c1c; }
.wk-label--orange { background: rgba(234,88,12,.16); color: #c2410c; }
.wk-label--amber { background: rgba(180,83,9,.16); color: #b45309; }
.wk-label--green { background: rgba(22,101,52,.14); color: #166534; }
.wk-label--teal { background: rgba(13,148,136,.16); color: #0f766e; }
.wk-label--blue { background: rgba(30,64,175,.14); color: var(--primary); }
.wk-label--violet { background: rgba(124,58,237,.16); color: #6d28d9; }
.wk-label--pink { background: rgba(219,39,119,.14); color: #be185d; }

/* Simple rows (goals, portfolios, my-work groups) */
.wk-row { display: flex; align-items: center; gap: 10px; padding: 10px 12px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 6px; }
.wk-row__main { flex: 1; min-width: 0; }
.wk-row__title { font-size: 13px; font-weight: 600; color: var(--text); }
.wk-row__sub { font-size: 11px; color: var(--text-subtle); }
.wk-row--clickable { cursor: pointer; }
.wk-row--clickable:hover { border-color: var(--border-strong); }
.wk-group-head { font-size: 12px; font-weight: 700; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .03em; margin: 18px 0 8px; }

/* Project view: header + toolbar */
.wk-proj-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.wk-back { font-size: 12px; color: var(--text-subtle); text-decoration: none; }
.wk-back:hover { color: var(--text); }
.wk-proj-title { font-size: 22px; font-weight: 700; margin: 2px 0; color: var(--text); }
.wk-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0 16px; }
.wk-toggle { display: inline-flex; background: var(--bg-muted, #eef2f6); border-radius: 8px; padding: 3px; }
/* Calendar month nav — lives in the shared Task Board toolbar (Calendar view). */
.wk-calnav { display: inline-flex; align-items: center; gap: 3px; }
.wk-calnav .wk-iconbtn { font-size: 15px; font-weight: 700; line-height: 1; }
.wk-calnav__label { font-size: 13px; font-weight: 600; color: var(--text); white-space: nowrap; padding: 0 6px; min-width: 118px; }
.wk-toggle button { border: 0; background: transparent; font: inherit; color: var(--text-subtle); width: 30px; height: 26px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border-radius: 6px; cursor: pointer; }
.wk-toggle button .hicon-wrap { display: inline-flex; align-items: center; justify-content: center; }
.wk-toggle button .hicon { width: 16px; height: 16px; display: block; }
.wk-toggle button.is-active { background: var(--bg-surface); color: var(--text); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
/* Text segmented control (Wiki pages filter: All · Recent · Needs review · Mine). The base .wk-toggle
   button is icon-sized (30px) which clips text labels — this variant sizes to content + a clear pill. */
.wk-toggle--seg { gap: 2px; background: #e2e8f0; border: 1px solid var(--border); }
.wk-toggle--seg button { width: auto; height: 28px; padding: 0 12px; font-size: 12.5px; font-weight: 600; white-space: nowrap; letter-spacing: .01em; transition: background .12s, color .12s; }
.wk-toggle--seg button:hover:not(.is-active) { background: color-mix(in srgb, var(--text-subtle, #64748b) 12%, transparent); color: var(--text); }
.wk-toggle--seg button.is-active { background: var(--bg-surface, #fff); color: var(--primary, #1d4ed8); box-shadow: 0 1px 3px rgba(16,24,40,.14); }
/* Colour-coded view switcher: each button's icon is tinted with its own accent (--vc); the
   active view fills its button with that accent to highlight it. (#kriya-tab wins the cascade
   over the generic .wk-toggle button:hover greying below.) */
#kriya-tab .wk-toggle--views button,
#kriya-tab .wk-toggle--entity button { color: var(--vc, var(--text-subtle)); }
#kriya-tab .wk-toggle--views button:hover,
#kriya-tab .wk-toggle--entity button:hover { background: color-mix(in srgb, var(--vc, #64748b) 14%, transparent); color: var(--vc, var(--text)); }
#kriya-tab .wk-toggle--views button.is-active,
#kriya-tab .wk-toggle--entity button.is-active { background: var(--vc, var(--primary)); color: #fff; box-shadow: 0 1px 2px rgba(16,24,40,.22); }
#kriya-tab .wk-toggle--views button.is-active:hover,
#kriya-tab .wk-toggle--entity button.is-active:hover { background: var(--vc, var(--primary)); color: #fff; }
/* The entity-type switch comes first, then the view switch — add clear separation between them. */
#kriya-tab .wk-toggle--entity { margin-right: 14px; }
/* Both segmented switches carry the app's standard default border. */
#kriya-tab .wk-toggle--views,
#kriya-tab .wk-toggle--entity { border: 1px solid var(--border); }
/* Group-by dropdown (replaces the old icon menu) — a compact label + native select pill. */
#kriya-tab .wk-groupsel { display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 8px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text-subtle);
  font-size: 12px; white-space: nowrap; }
#kriya-tab .wk-groupsel.is-on { border-color: var(--primary); color: var(--primary); }
#kriya-tab .wk-groupsel__lbl { font-weight: 600; letter-spacing: .02em; }
#kriya-tab .wk-groupsel__sel { border: 0; background: transparent; color: inherit; font: inherit; font-weight: 600;
  padding: 0 2px; cursor: pointer; outline: none; max-width: 120px; }
/* Placeholder panel for entity types not yet wired (Spec). */
#kriya-tab .wk-entity-ph { padding: 48px 16px; text-align: center; }
#kriya-tab .wk-entity-ph__icon { font-size: 30px; opacity: .8; }
#kriya-tab .wk-entity-ph__title { font-size: 15px; font-weight: 600; margin: 6px 0 2px; }
.wk-filters { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* TEXT inputs only. `.wk-filters input` matched the checkboxes inside the toolbar's own
   Columns / Types dropdowns too, so each one inherited a text box's border, padding and
   `min-width` — a 15px control forced to 130px+, which pushed its label far to the right
   and left a gap either side of every row. A checkbox is not a field; it styles itself. */
.wk-filters select,
.wk-filters input:not([type="checkbox"]):not([type="radio"]) { font: inherit; font-size: 12px; padding: 5px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.wk-filters input:not([type="checkbox"]):not([type="radio"]) { min-width: 160px; }

/* List view */
.wk-list { display: flex; flex-direction: column; gap: 14px; }
.wk-sec { border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); overflow: hidden; }
.wk-sec__head { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border-muted); cursor: pointer; }
.wk-sec__name { font-size: 13px; font-weight: 700; color: var(--text); }
.wk-sec__count { font-size: 11px; color: var(--text-faint); }
.wk-sec__chev { margin-left: auto; color: var(--text-faint); transition: transform .12s; }
.wk-sec.is-collapsed .wk-sec__chev { transform: rotate(-90deg); }
.wk-sec.is-collapsed .wk-sec__body, .wk-sec.is-collapsed .wk-addtask { display: none; }
.wk-task { display: flex; align-items: center; gap: 10px; padding: 9px 14px; border-bottom: 1px solid var(--border-muted); }
.wk-task:last-child { border-bottom: 0; }
.wk-task:hover { background: var(--bg-hover); }
.wk-check { appearance: none; -webkit-appearance: none; width: 17px; height: 17px; border: 1.5px solid var(--border-strong); border-radius: 50%; cursor: pointer; flex: none; position: relative; }
.wk-check:checked { background: var(--success); border-color: var(--success); }
.wk-check:checked::after { content: '✓'; color: #fff; font-size: 11px; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.wk-task__title { flex: 1; min-width: 0; font: inherit; font-size: 13px; line-height: 1.3; color: var(--text); background: none; border: 0; text-align: left; cursor: pointer; padding: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-task__title.is-done { color: var(--text-faint); text-decoration: line-through; }
/* Links that OPEN another page / item carry an underline so it's obvious they're clickable. Applies to
   item & task titles, breadcrumb links, Gantt row names, calendar task chips and hierarchy links. The
   underline is soft by default and firms up on hover; completed (struck-through) titles are excluded. */
.wk-task__title:not(.is-done),
.wk-tcard__title:not(.is-done),
.wk-tnode .wk-task__title:not(.is-done),
.wk-subtask__title:not(.is-done),
.wk-gt-name:not(.wk-gt-name--plain),
.wk-cal-task__t {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, currentColor 40%, transparent);
}
.wk-task__title:not(.is-done):hover,
.wk-tcard__title:not(.is-done):hover,
.wk-tnode .wk-task__title:not(.is-done):hover,
.wk-subtask__title:not(.is-done):hover,
.wk-gt-name:not(.wk-gt-name--plain):hover,
.wk-cal-task__t:hover {
  text-decoration-color: currentColor;
}
/* Plain text-button titles must NOT fall through to the global button:hover
   (near-black --brand-hover). On hover use the app accent + transparent bg;
   the row itself provides the subtle grey highlight. */
.wk-task__title:hover,
.wk-tcard__title:hover,
.wk-subtask__title:hover,
.wk-dep__title:hover { background: transparent; color: var(--primary); }
/* All remaining plain work-module buttons: standard grey hover, never the
   global near-black button:hover. (× remove buttons go danger, like wk-dep__x.) */
.wk-toggle button:hover,
.wk-dep-result:hover,
.wk-tnode__chev:hover,
.wk-delete:hover { background: var(--bg-hover); color: var(--text); }
.wk-file__x:hover { background: transparent; color: var(--danger); }
.wk-task__meta { display: flex; align-items: center; gap: 8px; flex: none; }
.wk-task__chip { font-size: 11px; color: var(--text-faint); display: inline-flex; align-items: center; gap: 3px; }
.wk-task__del { flex: none; border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 13px; line-height: 1; padding: 4px 7px; border-radius: 6px; opacity: 0; transition: opacity .12s, color .12s, background .12s; }
.wk-task:hover .wk-task__del, .wk-task:focus-within .wk-task__del { opacity: .6; }
.wk-task__del:hover, .wk-task__del:focus-visible { opacity: 1; color: var(--danger, #dc2626); background: var(--bg-hover); outline: none; }
.wk-addtask { display: flex; gap: 8px; padding: 8px 14px; border-top: 1px solid var(--border-muted); }
.wk-addtask input { flex: 1; font: inherit; font-size: 13px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); color: var(--text); }

/* monday.com-style List view — spreadsheet table: pinned Item column + reorderable/hide-able field columns */
/* One cohesive table: the outer border/radius lives on .wk-tbl; the header + groups flow inside
   with no gaps or per-box borders, so the column header sits directly atop the aligned rows. */
.wk-tbl { display: flex; flex-direction: column; gap: 0; overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); }
.wk-thead { position: sticky; top: 0; z-index: 3; display: grid; grid-template-columns: var(--wk-tbl-cols); background: var(--bg-surface); border-bottom: 1px solid var(--border); }
/* Horizontal-scroll fix: each header/row/group is an independent CSS grid that flexbox otherwise caps at
   the VISIBLE width, so on scroll the row background, borders and group banding stop at the viewport edge
   and the scrolled-in last columns lose their styling. --wk-tbl-minw (the sum of the columns' pixel
   minimums, set per table in JS) forces every element to the SAME full content width — deterministic, so
   columns stay aligned — and the styling then paints across all columns. Falls back to 100% (no-op) when
   the var is absent. */
.wk-thead, .wk-trow, .wk-tgroup, .wk-tgroup__head, .wk-tgroup__body { min-width: var(--wk-tbl-minw, 100%); }
.wk-thead__cell { display: flex; align-items: center; padding: 9px 12px; font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted, #64748b); border-left: 1px solid var(--border-muted); white-space: nowrap; cursor: grab; user-select: none; }
.wk-thead__cell:first-child { border-left: 0; }
.wk-thead__cell:active { cursor: grabbing; }
.wk-thead__name { cursor: default; position: sticky; left: 0; background: var(--bg-surface); z-index: 1; }
.wk-thead__cell--dragging { opacity: .45; }
.wk-thead__cell--dragover { box-shadow: inset 3px 0 0 var(--primary, #2563eb); }
/* Groups flow INSIDE the single table — no own border/radius; a divider separates one group from the
   next, and each group head is a subtle full-width section-separator row (keeps rows solid so the
   sticky name column still overlays scrolled cells). */
.wk-tgroup { background: var(--bg-surface); }
.wk-tgroup:not(:last-child) { border-bottom: 1px solid var(--border); }
.wk-tgroup__head { display: flex; align-items: center; gap: 8px; padding: 8px 14px; border-bottom: 1px solid var(--border-muted); background: var(--bg-muted); }
/* The head band is full-width, so making IT sticky does nothing (an element can't slide within a
   containing block it already fills). Instead pin a NARROW inner wrapper: it can shift within the
   full-width head, so the group name stays put while the columns scroll horizontally under it. */
.wk-tgroup__headin { position: sticky; left: 14px; z-index: 1; display: inline-flex; align-items: center; gap: 8px; }
.wk-tgroup__head .wk-tgroup__name { font-size: 13px; font-weight: 700; color: var(--text); }
.wk-tgroup__body { display: flex; flex-direction: column; }
.wk-trow { display: grid; grid-template-columns: var(--wk-tbl-cols); align-items: center; border-bottom: 1px solid var(--border-muted); }
.wk-trow:last-child { border-bottom: 0; }
.wk-trow:hover { background: var(--bg-hover); }
.wk-trow__cell { padding: 7px 12px; min-width: 0; font-size: 12.5px; color: var(--text); border-left: 1px solid var(--border-muted); overflow: hidden; }
.wk-trow__cell:first-child { border-left: 0; }
.wk-trow__cell .wk-statesel { width: 100%; }
/* Pinned first column: a SOLID background + z-index so horizontally-scrolled cells slide UNDER it
   (background:inherit was transparent → the data showed through / on top of this column). */
.wk-trow__name { display: flex; align-items: center; gap: 8px; position: sticky; left: 0; z-index: 2; background: var(--bg-surface); border-right: 1px solid var(--border-muted); }
.wk-trow:hover .wk-trow__name { background: var(--bg-hover); }
/* A filled name cell (e.g. the phase-coloured name) has WHITE text, so the pale --bg-hover on hover made
   it invisible. Keep the colour fill on hover (defined after, so it wins for .wk-trow__name.wk-cell--fill). */
.wk-trow:hover .wk-cell--fill { background: var(--fillbg); }
.wk-trow--edit .wk-trow__name { background: color-mix(in srgb, var(--primary, #2563eb) 8%, var(--bg-surface)); }
.wk-trow__name .wk-task__title { flex: 1; }
.wk-cell-dash { color: var(--text-faint); }
/* Columns menu (toolbar) */
.wk-colsmenu { position: relative; display: inline-flex; }
.wk-colsmenu__pop { position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; min-width: 200px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 10px 30px rgba(16,24,40,.16); padding: 6px; }
.wk-colsmenu__hd { display: flex; justify-content: space-between; font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted); padding: 6px 8px 4px; }
/* The checklist scrolls once it outgrows six rows — a twenty-column table otherwise runs the
   popup off the bottom of the window, taking the Reset row with it. Only the list scrolls, so
   the "N/M" heading and Reset stay put. A row is 29px (13px text at 1.3 + 6px padding twice);
   the half row is deliberate: on macOS the overlay scrollbar is invisible until you scroll, so
   a list clipped exactly at a row boundary looks complete when it isn't. */
.wk-colsmenu__list { max-height: calc(6.5 * 29px); overflow-y: auto; overscroll-behavior: contain; }
.wk-colsmenu__item { display: flex; align-items: center; gap: 6px; padding: 6px 8px; font-size: 13px; line-height: 1.3; color: var(--text); border-radius: 7px; cursor: pointer; }
/* Zebra striping — alternate row tint (theme-safe via color-mix); hover still wins (defined after). */
.wk-colsmenu__item:nth-of-type(even) { background: color-mix(in srgb, var(--text) 5%, transparent); }
.wk-colsmenu__item:hover { background: var(--bg-hover); }
/* Fixed native checkbox: flex:none stops the flex row from shrinking/stretching it (which squished the
   box next to long labels like "Milestones & deliverables"); margin:0 keeps it snug to its label. */
/* REORDERING, IN THE CHOOSER (2026-09-26, user direction: *"in columns filter provide an option to
   arrange the columns as desired"*).

   IT WAS DRAG-ONLY, on the table headers. *A control that only answers a drag is unreachable from a
   keyboard*, and a draggable header advertises nothing — the tooltip beside it was an admission of
   that rather than a fix. Two buttons per row are reachable, announce themselves, and sit where the
   reader was already looking.

   THE ARROWS ARE QUIET UNTIL WANTED: full contrast only on hover or keyboard focus, because a menu
   of twenty rows with forty dark arrows in it is a wall. They keep their 24px target at every
   state — that is the one thing that must not shrink — and `:focus-visible` is never traded away,
   since these buttons exist FOR the reader who cannot drag. */
.tk-colrow__name { flex: 1 1 auto; min-width: 0; cursor: pointer; }
.tk-colrow__move { flex: none; display: flex; gap: 2px; margin-inline-start: 8px; }
.tk-colmove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  min-height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text-faint, var(--text-subtle));
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
}
.tk-colmove:hover { background: var(--bg-hover); color: var(--text); }
.tk-colmove:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; color: var(--text); }
/* AN END-STOP, NOT A DEAD BUTTON. It is disabled because there is nowhere further to go, and it
   stays in place so the row does not reflow as a column travels up the list. */
.tk-colmove:disabled { opacity: .3; cursor: default; background: none; }

.wk-colsmenu__item > input[type="checkbox"] { flex: none; width: 15px; height: 15px; margin: 0; accent-color: var(--primary, #2563eb); cursor: pointer; }
.wk-colsmenu__ft { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-top: 1px solid var(--border-muted); margin-top: 4px; padding: 7px 8px 3px; font-size: 11px; }
.wk-colsmenu__reset { border: 1px solid var(--border); background: var(--bg-app); color: var(--text); font-size: 12px; padding: 3px 10px; border-radius: 7px; cursor: pointer; }
.wk-colsmenu__reset:hover { background: var(--bg-hover); }

/* Board (kanban) view */
.wk-board { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; align-items: flex-start; }
.wk-col { flex: 0 0 272px; background: var(--bg-muted); border-radius: 12px; padding: 8px; min-height: 80px; }
.wk-col--dragover { outline: 2px dashed var(--border-strong); outline-offset: -2px; }
.wk-col__head { display: flex; align-items: center; gap: 6px; padding: 4px 8px 8px; font-size: 12px; font-weight: 700; color: var(--text); }
.wk-col__count { font-size: 11px; color: var(--text-faint); font-weight: 600; }
.wk-col__body { display: flex; flex-direction: column; gap: 8px; min-height: 12px; }
.wk-tcard { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; padding: 10px; cursor: grab; }
.wk-tcard:hover { border-color: var(--border-strong); }
.wk-tcard--dragging { opacity: .5; }
.wk-tcard__title { font-size: 13px; color: var(--text); margin-bottom: 6px; }
.wk-tcard__title.is-done { color: var(--text-faint); text-decoration: line-through; }
.wk-tcard__meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* ── Items view — entity tile gallery (the default entity view) ── */
.wk-itiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; align-content: start; }
.wk-itile { position: relative; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 1px 2px rgba(16,24,40,.05); transition: box-shadow .15s ease, border-color .15s ease; }
.wk-itile:hover { border-color: var(--border-strong); box-shadow: 0 2px 6px rgba(16,24,40,.08), 0 12px 24px rgba(16,24,40,.08); }
.wk-itile__open { display: flex; flex-direction: column; gap: 7px; width: 100%; text-align: left; background: none; border: 0; font: inherit; color: inherit; cursor: pointer; padding: 12px 13px; }
.wk-itile__top { display: flex; align-items: center; gap: 6px; }
.wk-itile__title { font-size: 14px; font-weight: 640; line-height: 1.25; color: var(--text); overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; padding-right: 20px; }
.wk-itile__status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--text-subtle); }
.wk-itile__meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wk-itile__meta .wk-task__chip { border: 1px solid var(--border); border-radius: 6px; padding: 1px 6px; color: var(--text-subtle); }
.wk-itile__foot { display: flex; align-items: center; gap: 6px; margin-top: 2px; padding-top: 8px; border-top: 1px solid var(--border-muted, var(--border)); }
.wk-itile__fav { position: absolute; top: 7px; right: 7px; z-index: 1; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border: 0; background: none; border-radius: 7px; cursor: pointer; font-size: 16px; line-height: 1; color: var(--text-faint); opacity: 0; transition: opacity .12s, color .12s, background .12s; }
.wk-itile:hover .wk-itile__fav, .wk-itile__fav.is-on { opacity: 1; }
.wk-itile__fav:hover { background: var(--bg-hover); color: var(--text); }
.wk-itile__fav.is-on { color: #f59e0b; }
/* Items toolbar scope toggle (All / Recent / Favourite). */
.wk-itemscope { display: inline-flex; gap: 2px; }
.wk-col__add { width: 100%; margin-top: 8px; border: 1px dashed var(--border); background: transparent; color: var(--text-subtle); font: inherit; font-size: 12px; padding: 7px; border-radius: 8px; cursor: pointer; }
.wk-col__add:hover { border-color: var(--border-strong); color: var(--text); }

/* Task detail drawer — `hidden` must win over the display rules below. */
/* Task detail renders inside the SHARED app-drawer (ui/drawer.js). The drawer SHELL
   (fixed panel, backdrop, slide-in, close ✕) is provided by .app-drawer; only the
   task CONTENT styles below live here, rendered inside #app-drawer-body. */
.app-drawer.is-wide { width: min(60vw, 96vw); }
@media (max-width: 640px) { .app-drawer.is-wide { width: 100vw; } }
/* Neutralize the content head/body's own frame — app-drawer-body already provides
   its head + body padding, so the wk-drawer wrappers sit flush inside it. */
#app-drawer-body .wk-drawer__head { padding-left: 0; padding-right: 0; }
#app-drawer-body .wk-drawer__body { padding: 0; }
/* In-body task detail PAGE (Kriya): clicking a task / creating one opens the same
   .wk-drawer__* content as a full page inside #wk-tasks-body (not the overlay drawer);
   a sticky "← Back to tasks" bar returns to the list. Centered to a document width. */
.wk-detailpage { display: flex; flex-direction: column; min-height: 100%; }
.wk-detailpage__bar { position: sticky; top: 0; z-index: 6; display: flex; align-items: center; gap: 12px; padding: 10px 18px; background: #edf8ef; border-bottom: 1px solid #cddfcf; box-shadow: 0 1px 3px rgba(16,24,40,.04); }
.wk-detailpage__title { font-size: 14px; font-weight: 650; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* No page-specific background — the detail page inherits the app-wide grey
   (--bg-app); the white tiles + their thin hairline provide the structure. */
/* border-box for the WHOLE detail page (there is no global reset): otherwise any
   `width:100%` element with horizontal padding — the __inner container (width:100% +
   44px padding), the title input, etc. — renders wider than its parent and spills
   the content past the right body border. Universal here = bulletproof. */
.wk-detailpage, .wk-detailpage *, .wk-detailpage *::before, .wk-detailpage *::after { box-sizing: border-box; }
.wk-detailpage__inner { width: 100%; max-width: 1200px; margin: 0 auto; padding: 14px 22px 40px; }
.wk-detailpage .wk-drawer__head { padding-left: 0; padding-right: 0; }
.wk-detailpage .wk-drawer__body { padding: 0; overflow: visible; }
.wk-detailpage .wk-comment-box { position: static; }
/* Two columns of draggable white TILES — form LEFT, Activity RIGHT by default; the
   arrangement is a saved user preference (all tasks). Each tile scrolls on its own. */
.wk-detail-cols { display: flex; align-items: flex-start; gap: 20px; }
/* Columns are plain BLOCK containers (not flex-column): tiles stack at their
   natural height so they can never be flex-shrunk into thin lines when the page
   is resized. `flex: 1 1 0` here only sizes the column as a child of .wk-detail-cols. */
.wk-detail-col { flex: 1 1 0; min-width: 0; min-height: 120px; }
/* Task detail page: LEFT column takes 70% of the body, RIGHT column 30% (7:3 flex-grow accounts
   for the gap). Only above the stacking breakpoint — ≤900px they go full-width (see below). */
@media (min-width: 901px) {
  .wk-detail-cols > .wk-detail-col[data-col="left"] { flex: 7 1 0; }
  .wk-detail-cols > .wk-detail-col[data-col="right"] { flex: 3 1 0; }
}
/* Modern card tiles — soft border, layered shadow, subtle header gradient, hover lift. */
.wk-tile { background: var(--bg-surface); border: 1px solid var(--border-muted); border-radius: 12px; box-shadow: 0 1px 2px rgba(16,24,40,.05), 0 3px 8px rgba(16,24,40,.05); overflow: hidden; margin-bottom: 16px; transition: box-shadow .18s ease, border-color .18s ease, transform .18s ease; }
.wk-tile:last-child { margin-bottom: 0; }
.wk-tile:hover { border-color: var(--border-strong); box-shadow: 0 2px 6px rgba(16,24,40,.08), 0 12px 28px rgba(16,24,40,.09); }
.wk-tile__head { display: flex; align-items: center; gap: 8px; padding: 9px 14px; border-bottom: 1px solid var(--border-muted); cursor: grab; user-select: none; background: var(--bg-surface); }
/* The in-flow detail page spans the SAME full page width as the task board (the page gutter comes
   from .kriya-main), instead of the old centered max-width column. */
#work-content .wk-detailpage__inner { max-width: none; margin: 0; padding: 14px 0 40px; }
/* Detail-page tiles: flat, simple cards that match the rest of the app — plain (non-gradient,
   non-highlighted) header + no heavy shadow / hover lift. */
#work-content .wk-detailpage .wk-tile { box-shadow: none; transition: border-color .12s; }
#work-content .wk-detailpage .wk-tile:hover { box-shadow: none; transform: none; border-color: var(--border); }
#work-content .wk-detailpage .wk-tile__head { background: transparent; }
.wk-tile__head:active { cursor: grabbing; }
.wk-tile__grip { color: var(--text-faint); opacity: .5; font-size: 12px; line-height: 1; letter-spacing: -2px; transition: opacity .15s ease; }
.wk-tile:hover .wk-tile__grip { opacity: .9; }
.wk-tile__title { margin: 0; font-size: 10.5px; font-weight: 700; line-height: 1.4; text-transform: uppercase; letter-spacing: .05em; color: var(--text-subtle); }
/* Per-section view/edit toggle (entity detail tiles). The Edit button sits at the right of the header. */
.wk-tile__edit { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  border: 1px solid var(--border-muted, #e5e7eb); background: var(--bg-surface, #fff); color: var(--text-muted, #64748b);
  border-radius: 7px; padding: 3px 9px; font-size: 11.5px; font-weight: 600; line-height: 1.4; }
.wk-tile__edit:hover { background: var(--bg-muted, #f1f5f9); color: var(--text, #0f172a); }
.wk-tile.is-editing .wk-tile__edit { background: var(--accent-soft, rgba(37, 99, 235, .1)); color: var(--primary, #1d4ed8); border-color: transparent; }
.wk-tile__edit .hicon-wrap { width: 13px; height: 13px; }
/* View mode: disabled form controls read as plain text; add/remove affordances hide until you Edit. */
.wk-tile.is-viewing .wk-tile__body input:disabled,
.wk-tile.is-viewing .wk-tile__body select:disabled,
.wk-tile.is-viewing .wk-tile__body textarea:disabled {
  border-color: transparent !important; background: transparent !important; opacity: 1 !important;
  color: var(--text, #0f172a) !important; -webkit-text-fill-color: var(--text, #0f172a);
  padding-left: 0; padding-right: 0; cursor: default; resize: none; box-shadow: none !important; }
/* View mode: a disabled <select> reads as plain text — strip BOTH the custom and the native
   OS dropdown chevron (the "v") so it doesn't look like an editable control. */
.wk-tile.is-viewing .wk-tile__body select:disabled {
  background-image: none; -webkit-appearance: none; -moz-appearance: none; appearance: none;
}
/* The enhanced searchable comboboxes (Project/Parent/Assignee/Sprint) replace the native <select>
   with a custom button — make THAT read as plain text in view mode too (no border/chevron, not
   clickable) so the whole Details tile looks uniform until you Edit. */
.wk-tile.is-viewing .wk-tile__body .wk-ssel__btn {
  border-color: transparent; background: transparent; padding-left: 0; pointer-events: none; cursor: default;
}
.wk-tile.is-viewing .wk-tile__body .wk-ssel__chev { display: none; }
/* An EMPTY date input in view mode should read as "not set" (blank) — not the mm/dd/yyyy
   placeholder, which looks like a value. Empty ⇒ its value attribute is "". (List view already
   shows a dash for these; this brings the detail page in line.)
   Chrome paints the placeholder in ::-webkit-datetime-edit; SAFARI paints a today's-date hint in the
   individual month/day/year FIELD pseudos, so those must be made transparent too — otherwise Safari
   shows today's date in every empty date field. */
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit,
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit-fields-wrapper,
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit-text,
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit-month-field,
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit-day-field,
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-datetime-edit-year-field {
  color: transparent;
}
/* Safari also shows the calendar/clear indicators inside a disabled empty field — hide them so the
   cell reads as truly blank. */
.wk-tile.is-viewing .wk-tile__body input[type="date"][value=""]:disabled::-webkit-calendar-picker-indicator {
  opacity: 0;
}
/* SAFARI ONLY (desktop): an EMPTY, still-editable date input (task detail, entity edit mode, item
   rows) shows TODAY's date in Safari's month/day/year field pseudos — looking like a set value. Chrome
   correctly shows a grey "mm/dd/yyyy" placeholder there, so we must NOT touch Chrome. `-webkit-named-
   image` is a Safari-only feature, so this @supports block scopes the fix to Safari: blank the today
   hint so an unset date reads as empty (not today). (A literal "mm/dd/yyyy" placeholder isn't possible
   on a native Safari date input — Safari has no placeholder for date — so "blank when unset" is the
   correct not-set state.) */
@supports (background: -webkit-named-image(i)) {
  input[type="date"]:not([value]):not(:focus)::-webkit-datetime-edit,
  input[type="date"][value=""]:not(:focus)::-webkit-datetime-edit-year-field,
  input[type="date"][value=""]:not(:focus)::-webkit-datetime-edit-month-field,
  input[type="date"][value=""]:not(:focus)::-webkit-datetime-edit-day-field,
  input[type="date"][value=""]:not(:focus)::-webkit-datetime-edit-text {
    color: transparent;
  }
}
/* VIEW MODE: hide EVERY mutating affordance across the whole record — add forms, "＋ Add" / template
   buttons, attach, remove ✕, per-row delete, drag grips. Navigation (open a sub-entity, .wk-pv-name /
   data-open-*) is NOT a ghost-button, so it stays clickable. Turning on the page-level Edit reveals
   these again. This is what makes view mode a truly read-only record. */
.wk-tile.is-viewing .wk-tile__body .wk-addtask,
.wk-tile.is-viewing .wk-tile__body .wk-ent-add,
.wk-tile.is-viewing .wk-tile__body .wk-ent-attach,
.wk-tile.is-viewing .wk-tile__body .wk-tile-actions,
.wk-tile.is-viewing .wk-tile__body .ghost-button,
.wk-tile.is-viewing .wk-tile__body .primary-button,
.wk-tile.is-viewing .wk-tile__body .wk-xitem__x,
.wk-tile.is-viewing .wk-tile__body .wk-task__del,
.wk-tile.is-viewing .wk-tile__body [data-item-del],
.wk-tile.is-viewing .wk-tile__body .wk-status-grip { display: none !important; }
/* Description follows the SAME page Edit (no separate ✎ toggle): formatted text in view mode, the
   editable textarea in edit mode. */
.wk-desc [data-desc-edit] { display: none; }
.wk-tile.is-editing .wk-desc [data-desc-edit] { display: block; }
.wk-tile.is-editing .wk-desc [data-desc-view] { display: none; }
/* Every section grows with its content — no inner scrollbar; the page scrolls instead.
   (So adding more reminders / comments / rows expands the tile rather than capping it.) */
.wk-tile__body { padding: 14px; overflow: visible; overflow-wrap: anywhere; }
/* Status colour dot — shown next to a status label wherever it's rendered (board columns, list cells). */
.wk-sdot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; flex: none; box-shadow: 0 0 0 1px rgba(16,24,40,.08) inset; }
/* Per-entity dashboard (Project/Risk/Issue/Spec dashboard view): KPI cards + breakdown bars. */
.wk-entdash { padding: 4px 2px 24px; }
.wk-dashkpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.wk-dashkpi { background: var(--bg-surface, #fff); border: 1px solid var(--border-muted, #e5e7eb); border-radius: 12px; padding: 14px 16px; box-shadow: 0 1px 2px rgba(16,24,40,.04); }
.wk-dashkpi__v { font-size: 1.7rem; font-weight: 700; line-height: 1.05; color: var(--text, #0f172a); }
.wk-dashkpi__l { font-size: .8rem; color: var(--text-muted, #64748b); margin-top: 4px; }
.wk-dashgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 14px; }
/* Dashboard toolbar controls (dashboard picker + period) — inline next to the view switches. */
.wk-dashsels { display: inline-flex; align-items: center; gap: 6px; }
.wk-dashsels select { height: 30px; font-size: 12.5px; max-width: 190px; }
/* Editable ENTITY dashboard grid: 4 columns, widgets span 1–4 (resize handle), reuses the task
   dashboard's .wk-widget chrome (grip / drop / resize). Nested cards drop their own borders so
   the widget frame is the only card. */
/* Free-form CANVAS: widgets are absolutely positioned {x,y,w,h} in px — move anywhere, resize to
   any size, no grid rows. Alignment guides appear while dragging/resizing. */
.wk-edcanvas { position: relative; display: block; min-height: 320px; }
.wk-edw { position: absolute; box-sizing: border-box; overflow: hidden;
  background: var(--bg-surface, #fff); border: 1px solid var(--border, #e5e7eb); border-radius: 12px;
  padding: 12px 14px; box-shadow: 0 1px 2px rgba(16,24,40,.05); user-select: none; }
.wk-edw__body { height: 100%; overflow: hidden; }
.wk-edw--moving { cursor: grabbing; opacity: .92; box-shadow: 0 10px 26px rgba(16,24,40,.16); z-index: 4; }
.wk-edw--resizing { z-index: 4; box-shadow: 0 10px 26px rgba(16,24,40,.16); }
/* Move/resize + widget tools + the resize handle are EDIT-mode only; a locked view is static. */
.wk-edcanvas--edit .wk-edw { border-style: dashed; border-color: var(--border-strong, #cbd5e1); }
.wk-edcanvas--edit .wk-edw:hover { cursor: grab; border-color: var(--primary, #2563eb); }
.wk-edcanvas:not(.wk-edcanvas--edit) .wk-widget__resize { display: none; }
/* Edit / delete / grip — hover-only, and only while editing. */
.wk-edw__tools { position: absolute; top: 7px; right: 8px; display: inline-flex; align-items: center; gap: 3px; z-index: 2; opacity: 0; transition: opacity .12s; background: color-mix(in srgb, var(--bg-surface, #fff) 82%, transparent); border-radius: 7px; padding: 1px 2px; pointer-events: none; }
.wk-edcanvas--edit .wk-edw:hover .wk-edw__tools, .wk-edcanvas--edit .wk-edw:focus-within .wk-edw__tools { opacity: 1; pointer-events: auto; }
.wk-edw__btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; font: inherit; font-size: 11px; line-height: 1; color: var(--text-subtle); background: transparent; border: 0; border-radius: 6px; cursor: pointer; }
.wk-edw__btn:hover { background: var(--bg-hover, #f1f5f9); color: var(--text); }
.wk-edw__btn .hicon { width: 13px; height: 13px; }
.wk-edw .wk-dashcard, .wk-edw .wk-dashkpi { border: 0; box-shadow: none; background: transparent; padding: 0; margin: 0; }
.wk-edw .wk-dashcard__h { padding-right: 70px; }
/* Alignment guide lines (tangents to other widgets' edges), shown during drag/resize. */
.wk-edguide { position: absolute; z-index: 6; pointer-events: none; background: #ea580c; }
.wk-edguide--x { top: 0; bottom: 0; width: 1px; }
.wk-edguide--y { left: 0; right: 0; height: 1px; }
.wk-edguide[hidden] { display: none; }
/* Filter section above the canvas. */
.wk-edbar { margin-bottom: 12px; }
/* ── Profile photo (avatar) — uploaded image fills the avatar circle everywhere ── */
.avatar-img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; }
.account-avatar-row { display: flex; align-items: center; gap: 12px; margin: 4px 0 12px; }
.account-avatar { display: inline-flex; align-items: center; justify-content: center; width: 56px; height: 56px; border-radius: 50%; background: var(--primary, #2563eb); color: var(--primary-ink, #fff); font-size: 22px; font-weight: 700; overflow: hidden; flex: none; }
.account-avatar-actions { display: inline-flex; align-items: center; gap: 8px; }

/* ── Kriya project "Design photos" gallery (image attachments as thumbnails) ── */
.wk-design-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; margin-bottom: 8px; }
.wk-design-card { margin: 0; display: flex; flex-direction: column; gap: 4px; }
.wk-design-card__btn { padding: 0; border: 1px solid var(--border, #e5e7eb); border-radius: 10px; overflow: hidden; background: var(--bg-app, #f1f5f9); cursor: zoom-in; display: block; }
.wk-design-card__btn img { display: block; width: 100%; height: 110px; object-fit: cover; transition: transform .15s; }
.wk-design-card__btn:hover img { transform: scale(1.04); }
.wk-design-card__cap { display: flex; align-items: center; gap: 4px; min-width: 0; }
.wk-design-card__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--text-muted, #64748b); }

/* ── Central "My Dashboard" — user-composed panels of pages from other modules ── */
.cd-wrap { display: flex; flex-direction: column; gap: 10px; }
.cd-hint { margin: 0; font-size: 12px; color: var(--text-subtle); }
.cd-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; align-items: start; }
@media (max-width: 900px) { .cd-grid { grid-template-columns: 1fr; } .cd-grid > * { grid-column: 1 / -1 !important; } }
.cd-panel { position: relative; display: flex; flex-direction: column; min-height: 150px; background: var(--bg-surface, #fff); border: 1px solid var(--border, #e5e7eb); border-top: 3px solid var(--ph, var(--primary, #2563eb)); border-radius: 12px; padding: 10px 12px 12px; box-shadow: 0 1px 2px rgba(16,24,40,.04); }
.cd-panel--dragging { opacity: .5; }
.cd-panel--drop { outline: 2px dashed var(--primary, #2563eb); outline-offset: 2px; }
.cd-panel__h { display: flex; align-items: center; gap: 7px; margin-bottom: 6px; min-width: 0; }
.cd-panel__mod { flex: none; font-size: 9.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ph, var(--primary, #1d4ed8)); background: color-mix(in srgb, var(--ph, var(--primary, #2563eb)) 11%, transparent); border-radius: 5px; padding: 2px 6px; }
.cd-panel__t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 700; color: var(--text); }
.cd-panel__open { flex: none; font-size: 11px; font-weight: 600; color: var(--primary, #1d4ed8); text-decoration: none; white-space: nowrap; }
.cd-panel__open:hover { text-decoration: underline; }
.cd-panel__tools { flex: none; display: inline-flex; align-items: center; gap: 2px; opacity: 0; transition: opacity .12s; }
.cd-panel:hover .cd-panel__tools, .cd-panel:focus-within .cd-panel__tools { opacity: 1; }
.cd-panel__grip { cursor: grab; color: var(--text-faint); font-size: 12px; padding: 0 2px; }
.cd-panel__x { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; font: inherit; font-size: 11px; color: var(--text-subtle); background: transparent; border: 0; border-radius: 6px; cursor: pointer; }
.cd-panel__x:hover { background: rgba(220,38,38,.1); color: #dc2626; }
/* Panel body = a borderless TABLE: one shared 3-column grid (title | status | meta) so cells
   align across rows; rows contribute cells via display:contents. No separator lines. */
.cd-panel__b { flex: 1; min-height: 0; overflow: auto; display: grid; grid-template-columns: minmax(0, 1fr) max-content max-content; column-gap: 10px; align-content: start; }
.cd-panel__b > .cd-empty { grid-column: 1 / -1; }
.cd-resize { position: absolute; right: 3px; bottom: 3px; width: 14px; height: 14px; cursor: nwse-resize; border-right: 2px solid var(--border-strong, #cbd5e1); border-bottom: 2px solid var(--border-strong, #cbd5e1); border-radius: 0 0 4px 0; }
/* Rows are SUBGRIDS: cells stay aligned to the panel's shared columns, while the row itself
   is a real box — carrying a hairline separator between rows (last row bare). */
.cd-row { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; border-bottom: 1px solid var(--border-muted, #eef2f6); }
.cd-row:last-child { border-bottom: 0; }
.cd-th { font-size: 9.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: color-mix(in srgb, var(--ph, #2563eb) 60%, var(--text-faint, #94a3b8)); padding: 2px 0 5px; white-space: nowrap; border-bottom: 1px solid var(--border, #e5e7eb); }
.cd-row__t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12.5px; font-weight: 600; color: var(--text); padding: 5px 0; align-self: center; }
.cd-td { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; color: var(--text-muted, #64748b); padding: 5px 0; align-self: center; }
.cd-td--warn { color: #dc2626; font-weight: 700; }
.cd-cell--end { justify-self: end; text-align: right; }
.cd-chip { font-size: 10px; font-weight: 600; text-transform: capitalize; color: var(--text-muted, #64748b); background: var(--bg-app, #f1f5f9); border-radius: 10px; padding: 2px 8px; align-self: center; justify-self: start; white-space: nowrap; }
.cd-chip:empty { visibility: hidden; }
/* Semantic chip hues — done/green, in-progress/blue, pending/amber, urgent/red, new/indigo… */
.cd-chip--green { color: #15803d; background: #dcfce7; }
.cd-chip--blue { color: var(--primary); background: #dbeafe; }
.cd-chip--indigo { color: #4f46e5; background: #e0e7ff; }
.cd-chip--violet { color: #7c3aed; background: #ede9fe; }
.cd-chip--amber { color: #b45309; background: #fef3c7; }
.cd-chip--red { color: #b91c1c; background: #fee2e2; }
.cd-chip--gray { color: var(--text-subtle); background: var(--bg-muted); }
.cd-row__meta { font-size: 11px; color: var(--text-faint, #94a3b8); white-space: nowrap; align-self: center; justify-self: end; padding: 5px 0; }
.cd-empty { margin: 4px 0; font-size: 12px; color: var(--text-faint, #94a3b8); }
.cd-add { min-height: 150px; }
/* "＋ Add widget" create tile — the app-wide dashed create-tile pattern. */
.wk-edw-add { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 96px; border: 2px dashed #ea580c; border-radius: 12px; background: color-mix(in srgb, #ea580c 5%, transparent); color: var(--text); font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }
.wk-edw-add:hover { border-color: #c2410c; color: #c2410c; background: color-mix(in srgb, #ea580c 10%, transparent); }
.wk-edw-add__plus { font-size: 22px; line-height: 1; color: #ea580c; }
.wk-dashsection { margin: 22px 0 12px; font-size: .82rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-muted, #64748b); padding-bottom: 6px; border-bottom: 1px solid var(--border-muted, #eef1f5); }
#wk-projdash-children .wk-dashkpis { margin-bottom: 14px; }
.wk-dashcard { background: var(--bg-surface, #fff); border: 1px solid var(--border-muted, #e5e7eb); border-radius: 12px; box-shadow: 0 1px 2px rgba(16,24,40,.04); overflow: hidden; }
.wk-dashcard__h { padding: 11px 14px; font-size: .8rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted, #64748b); border-bottom: 1px solid var(--border-muted, #eef1f5); }
.wk-dashcard__b { padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
.wk-dashbar { display: grid; grid-template-columns: minmax(80px, 34%) 1fr auto; align-items: center; gap: 10px; font-size: 13px; }
.wk-dashbar__lbl { display: inline-flex; align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text, #0f172a); }
.wk-dashbar__track { height: 8px; border-radius: 5px; background: var(--bg-muted, #f1f5f9); overflow: hidden; }
.wk-dashbar__fill { display: block; height: 100%; border-radius: 5px; background: var(--primary, #2563eb); min-width: 3px; }
.wk-dashbar__n { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-muted, #64748b); min-width: 24px; text-align: right; }
/* Donut chart (composition) — SVG ring + legend. */
.wk-donut { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.wk-donut__svg { width: 108px; height: 108px; flex: none; }
.wk-donut__total { font-size: 22px; font-weight: 700; fill: var(--text, #0f172a); text-anchor: middle; dominant-baseline: central; }
.wk-donut__legend { display: flex; flex-direction: column; gap: 5px; flex: 1 1 auto; min-width: 120px; }
.wk-donleg { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 7px; font-size: 12.5px; }
.wk-donleg__l { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text, #0f172a); }
.wk-donleg__n { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-muted, #64748b); }
/* Progress / battery card (a single completion ratio). */
.wk-dashprog { gap: 6px !important; }
.wk-dashprog__pct { font-size: 1.9rem; font-weight: 700; line-height: 1; }
.wk-dashprog__track { height: 12px; }
.wk-dashprog__sub { font-size: 12px; color: var(--text-muted, #64748b); }
/* Project roll-up groups (milestones/deliverables/checklists/risks/schedules grouped by phase/kind). */
.wk-egroup + .wk-egroup { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-muted, #eef1f5); }
.wk-egroup__head { display: flex; align-items: center; gap: 8px; margin: 0 0 6px;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted, #64748b); }
.wk-egroup__n { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
  padding: 0 5px; border-radius: 9px; background: var(--bg-muted, #f1f5f9); color: var(--text-muted, #64748b);
  font-size: .68rem; font-weight: 600; letter-spacing: 0; }
/* Collapsible tiles (reminders/time/repeat/fields) — collapsed by default. */
.wk-tile__chev { margin-left: auto; color: var(--text-faint); font-size: 12px; line-height: 1; transition: transform .15s ease; }
/* Collapse chevron as a clickable button on entity sections (reset UA chrome; keep the rotate). */
button.wk-tile__chev { border: none; background: none; cursor: pointer; padding: 3px 5px; border-radius: 6px; flex: none; }
button.wk-tile__chev:hover { background: var(--track, #f1f5f9); color: var(--text, #0f172a); }
.wk-tile:not(.is-collapsed) .wk-tile__chev { transform: rotate(90deg); }
.wk-tile.is-collapsed .wk-tile__body { display: none; }

/* ── Dynamic sections: × remove button + "+ Section" / Templates bar ─────────────────────────── */
/* × removes a section (its data is kept). Revealed on header hover; disabled (greyed) when the
   section still holds data. Sits at the far right of the header, after the collapse chevron if any. */
.wk-tile__rm { margin-left: auto; border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 12px; line-height: 1; padding: 3px 6px; border-radius: 6px; flex: none; opacity: 0; transition: opacity .12s, background .12s, color .12s; }
.wk-tile__head:hover .wk-tile__rm, .wk-tile__rm:focus-visible { opacity: 1; }
.wk-tile__rm:hover:not([disabled]) { background: var(--danger-soft, #fee2e2); color: var(--danger, #dc2626); }
.wk-tile__rm[disabled] { cursor: not-allowed; opacity: .3; }
.wk-tile__head:hover .wk-tile__rm[disabled] { opacity: .3; }
.wk-tile__chev + .wk-tile__rm { margin-left: 4px; }   /* both present → single small gap, not double-auto */

.wk-secbar { display: flex; gap: 8px; align-items: center; padding: 8px 2px 2px; margin-top: 4px; }
.wk-secadd { position: relative; }
/* Opens UPWARD — the bar sits at the very bottom of the page, so a downward menu would be clipped. */
/* `width: max-content` pins the popover to its own content. Absolute positioning makes
   the width shrink-to-fit against the CONTAINING BLOCK, so wherever the section bar sits in
   a wide layout the menu stretched with it — a two-word menu spanning the page. The
   max-width keeps it on screen on a narrow viewport. */
.wk-secadd__pop { position: absolute; left: 0; bottom: calc(100% + 4px); z-index: var(--z-popover, 400); width: max-content; min-width: 232px; max-width: min(340px, calc(100vw - 32px)); max-height: 340px; overflow-y: auto; background: var(--bg-surface, #fff); border: 1px solid var(--border, #e5e7eb); border-radius: 10px; box-shadow: var(--shadow-md, 0 8px 24px rgba(15, 23, 42, .14)); padding: 6px; }
.wk-secadd__head { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); padding: 6px 10px 4px; }
.wk-secadd__item { display: flex; align-items: center; gap: 8px; width: 100%; border: 0; background: none; text-align: left; font: inherit; font-size: .86rem; color: var(--text); padding: 7px 10px; border-radius: 7px; cursor: pointer; }
.wk-secadd__item .hicon-wrap { width: 16px; flex: none; color: var(--text-subtle); }
.wk-secadd__item:hover:not(.is-disabled):not([disabled]) { background: var(--bg-muted, #f1f5f9); }
.wk-secadd__item.is-disabled, .wk-secadd__item[disabled] { color: var(--text-faint); cursor: default; }
.wk-secadd__added { margin-left: auto; font-size: .72rem; color: var(--text-faint); }
.wk-secadd__sep { height: 1px; background: var(--border-muted, #eef2f7); margin: 5px 4px; }
.wk-secadd__row { display: flex; align-items: center; }
.wk-secadd__row .wk-secadd__item { flex: 1 1 auto; min-width: 0; }
.wk-secadd__del { border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 11px; padding: 4px 8px; border-radius: 6px; flex: none; }
.wk-secadd__del:hover { background: var(--danger-soft, #fee2e2); color: var(--danger, #dc2626); }
/* Toolbar "Collapse all" reuses the Edit-button look but stays neutral (not primary-accented). */
.wk-detail-tabbar__collapse { color: var(--text-subtle, #64748b); border-color: var(--border, #e5e7eb); }
.wk-tile .wk-fieldgrid { grid-template-columns: repeat(2, 1fr); gap: 12px 14px; }
/* An amount and the currency it is written in are ONE control, so they cannot be read (or
   wrapped) apart. The picker is only rendered when the org trades in more than one — see
   `isMultiCurrency()` — so a single-currency business sees exactly the input it saw before. */
.wk-money { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.wk-money > input { min-width: 0; flex: 1 1 auto; }
.wk-money > .wk-ccy { flex: 0 0 auto; max-width: 8.5rem; }

/* Detail-page Details tiles: pack fields up to 4 per row (responsive down on narrow widths).
   Wide fields (.wk-field--wide, grid-column:1/-1) still span the full row. */
#work-content .wk-detailpage .wk-tile .wk-fieldgrid { grid-template-columns: repeat(auto-fill, minmax(max(200px, calc(25% - 12px)), 1fr)); }
.wk-tile.is-dragging { opacity: .5; box-shadow: 0 10px 28px rgba(16,24,40,.16); }
@media (max-width: 900px) { .wk-detail-cols { flex-direction: column; } .wk-detail-col { flex: 0 0 auto; width: 100%; } }

/* ═══════════════════════════════════════════════════════════════════════════
   APP PAGE STANDARD — the reusable, app-wide page chrome every module adopts,
   mirroring the Kriya task-editor reference (.wk-detailpage / .wk-detail-header
   / .wk-detail-tabbar / .wk-tile). Build any detail or list page from these
   `.app-*` classes and it inherits the themeable header + filter tokens
   (--app-header-bg / --app-filter-bg, Settings → Appearance) and the standard
   breadcrumb (put [data-crumb-tail] on the title). Decoupled from the Kriya
   `.wk-*` classes so modules stay independent, but visually identical.
   ─────────────────────────────────────────────────────────────────────────── */
.app-detailpage { max-width: 1100px; margin: 0 auto; }
.app-detailpage select, .app-detailpage input, .app-detailpage textarea { box-sizing: border-box; }

/* (A) Gradient page header — the themeable brand band carrying the title (with a
   [data-crumb-tail] hook for the breadcrumb). Mirrors .wk-detail-header. */
.app-detail-header { background: var(--app-header-bg); color: var(--app-header-text); border-radius: 14px;
  padding: 11px 16px 14px; margin-bottom: 10px; box-shadow: 0 8px 24px rgba(13,90,120,.26), inset 0 1px 0 rgba(255,255,255,.14); }
.app-detail-header__row { display: flex; align-items: center; flex-wrap: nowrap; gap: 12px; }
.app-detail-header__title { flex: 1 1 auto; min-width: 0; margin: 0; font-size: 20px; font-weight: 680; line-height: 1.2;
  color: var(--app-header-text); background: transparent; border: 0; }
.app-detail-header__title:focus { outline: none; }
.app-detail-header__sub { margin: 4px 0 0; font-size: 12px; color: var(--app-header-text); opacity: .82; }
.app-detail-header__key { flex: none; font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px;
  font-weight: 600; letter-spacing: .02em; color: var(--app-header-text); opacity: .9; white-space: nowrap; }
.app-detail-header .canvas-ref-link, .app-detail-header > a { color: var(--app-header-text);
  border-color: color-mix(in srgb, var(--app-header-text) 45%, transparent); }

/* (B) Light filter / tab bar — one themeable row of filters/actions. Mirrors .wk-detail-tabbar. */
.app-filterbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;
  background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px;
  padding: 3px 10px; margin-bottom: 16px; }
.app-filterbar__left, .app-filterbar__right { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; min-width: 0; }
.app-filterbar__back { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 13px;
  font-weight: 600; color: #0f766e; background: transparent; border: 0; padding: 3px 7px; border-radius: 8px; cursor: pointer; }
.app-filterbar__back:hover { background: rgba(15,118,110,.12); color: #0b5c55; }

/* (C) White tile card — soft border, layered shadow, subtle top-lit head, hover lift.
   Mirrors .wk-tile. Add .app-tile--collapsible + .is-collapsed for a collapse toggle. */
.app-tile { background: var(--bg-surface); border: 1px solid var(--border-muted); border-radius: 12px;
  box-shadow: 0 1px 2px rgba(16,24,40,.05), 0 3px 8px rgba(16,24,40,.05); overflow: hidden; margin-bottom: 16px;
  transition: box-shadow .18s ease, border-color .18s ease; }
.app-tile:last-child { margin-bottom: 0; }
.app-tile:hover { border-color: var(--border-strong); box-shadow: 0 2px 6px rgba(16,24,40,.08), 0 12px 28px rgba(16,24,40,.09); }
.app-tile__head { display: flex; align-items: center; gap: 8px; padding: 9px 14px; border-bottom: 1px solid var(--border-muted);
  background: linear-gradient(180deg, var(--bg-surface), var(--bg-app)); }
.app-tile__title { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-subtle); }
.app-tile__body { padding: 14px; overflow-wrap: anywhere; }
.app-tile__chev { margin-left: auto; color: var(--text-faint); font-size: 12px; line-height: 1; transition: transform .15s ease; }
.app-tile--collapsible .app-tile__head { cursor: pointer; }
.app-tile:not(.is-collapsed) .app-tile__chev { transform: rotate(90deg); }
.app-tile.is-collapsed .app-tile__body { display: none; }

/* ── Commerce & support modules adopt the standard (Pimly / Inventra / Ordio /
   Bazaar / iShop) — these render bespoke .*-head (page title + actions) and
   .*-toolbar (filters) rows that shipped unstyled. Point them at the app
   standard so their header + filter sections match the Kriya reference and
   re-skin with one Settings -> Appearance choice. */
.pimly-head, .inv-head, .ord-head, .bz-head, .shop-head {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin: 0 0 12px; }
.pimly-head h2, .inv-head h2, .ord-head h2, .bz-head h2, .shop-head h2 {
  margin: 0; font-size: 19px; font-weight: 680; letter-spacing: .01em; }
.pimly-row-actions, .inv-row-actions, .ord-row-actions, .bz-row-actions {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.pimly-toolbar, .inv-toolbar, .ord-toolbar, .bz-toolbar, .shop-toolbar {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  background: var(--app-filter-bg); border: 1px solid var(--app-filter-border);
  border-radius: 10px; padding: 6px 10px; margin: 0 0 14px; }
.pimly-toolbar > input, .pimly-toolbar > select, .inv-toolbar > input, .inv-toolbar > select,
.ord-toolbar > input, .ord-toolbar > select, .bz-toolbar > input, .bz-toolbar > select,
.shop-toolbar > input, .shop-toolbar > select {
  height: 34px; padding: 0 8px; border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-surface); color: var(--text); font: inherit; font-size: .86rem; }
.pimly-toolbar .grow, .inv-toolbar .grow, .ord-toolbar .grow, .bz-toolbar .grow, .shop-toolbar .grow {
  flex: 1 1 12rem; min-width: 10rem; }
/* Bazaar's inbox tabs. `.pimly-*` LEFT THIS RULE on 2026-09-11: the product record, the
   price-list picker and every create draft in the app render the shared `.smz-tabs` strip now
   (`ui/tabKit.js`), and a dead declaration is what the next reader reaching for "how do I make a
   tab strip" finds first and copies.
   WORTH KNOWING WHY NOBODY EVER SAW THESE PILLS: `partials/pimly.html` and `partials/bazaar.html`
   each declared the SAME classes as an UNDERLINE strip, and a partial is inlined into the shell
   AFTER this sheet — so the same-weight copy won every tie and this rule has never painted a tab
   in either module. Bazaar is the LAST module still on a private copy and is named as such in
   `ui/tabKit.js`; converting it is its own change. */
.bz-tab-btn:hover:not(.is-active) { background: rgba(29,78,216,.08); color: var(--text); }

/* Categories tree: a root node is what a product is filed under first, so it reads as the
   more definite of the two — filled, on-brand; a nested node is a finer cut of the same
   tree, so it reads as the quieter, outlined variant. Depth decides which; there is no
   separate "type" field behind this. */
.pimly-tree-kind { flex: 0 0 auto; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; padding: 1px 7px; border-radius: 999px;
  background: var(--primary); color: var(--primary-ink, #fff); }
.pimly-tree-kind--sub { background: transparent; color: var(--text-subtle);
  border: 1px solid var(--border); }

/* Overflow guard: no global border-box reset exists, so width:100% + padding + border
   rendered each control ~20px too wide and pushed the field blocks past the page edge.
   border-box fixes that WITHOUT a max-width:100% (which would beat the status select's
   own max-width:150px and stretch it across the page). */
.wk-detailpage select, .wk-detailpage input, .wk-detailpage textarea { box-sizing: border-box; }
.wk-detailpage .wk-field select, .wk-detailpage textarea[data-field="notes"] { max-width: 100%; }
.wk-detailpage .wk-fieldgrid > .wk-field { min-width: 0; margin: 0; }
/* Comment / reply composer: stack the button UNDER the input (not beside it) so it
   never overflows the narrow Activity column. */
.wk-detailpage .wk-comment-box, .wk-detailpage .wk-reply-box { flex-direction: column; align-items: flex-end; gap: 8px; padding-top: 0; border-top: 0; background: transparent; }
.wk-detailpage .wk-comment-box textarea, .wk-detailpage .wk-reply-box textarea { flex: none; width: 100%; min-width: 0; min-height: 72px; background: var(--bg-surface); border-radius: 9px; }
/* ── Detail-page form controls (task + entity detail) — one clean, consistent design ──
   Every field is a full-width control with a comfortable height, a solid surface colour
   (so a native <select>'s option list inherits a readable background in dark mode too),
   a healthy line-height (so native options never cram/overlap), and a cobalt focus ring. */
.wk-detailpage .wk-field select,
.wk-detailpage .wk-field input[type="date"],
.wk-detailpage .wk-field input[type="datetime-local"],
.wk-detailpage .wk-field input[type="number"],
.wk-detailpage .wk-field input[type="text"],
.wk-detailpage .wk-field textarea,
.wk-detailpage textarea[data-field="notes"] {
  width: 100%; box-sizing: border-box;
  /* background-COLOR (not the shorthand) so the <select> chevron image survives. */
  background-color: var(--bg-surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 9px;
  padding: 8px 11px; font: inherit; font-size: 13px; line-height: 1.5;
  transition: border-color .15s ease, box-shadow .15s ease;
}
/* Dropdowns: a comfortable min-height + the "v" caret (with room for it). Native <select>s
   render the browser's own scrollable option list — never overlapping, never closing on hover.
   Scoped to :enabled so a disabled (view-mode) select reads as plain text (handled elsewhere). */
.wk-detailpage .wk-field select { min-height: 38px; }
.wk-detailpage .wk-field select:not([multiple]):enabled {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  cursor: pointer; padding-right: 32px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
/* Native <option> rows — guarantee readable colours + comfortable height in every engine. */
.wk-detailpage .wk-field select option { background-color: var(--bg-surface); color: var(--text); padding: 6px 8px; }
/* Every long-text box on a detail page (task notes + entity Description / Mitigation / Steps)
   shares the task-Description look: full width, comfortable height, vertical resize. */
.wk-detailpage .wk-field textarea, .wk-detailpage textarea[data-field="notes"] { width: 100%; min-height: 72px; resize: vertical; font: inherit; font-size: 13px; color: var(--text); }
/* Read-only field value (e.g. Creator) — looks like a field but isn't editable. */
.wk-detailpage .wk-readonly { font-size: 13px; color: var(--text-subtle); background: var(--bg-muted); border: 1px solid var(--border); border-radius: 9px; padding: 8px 10px; min-height: 37px; display: flex; align-items: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; box-sizing: border-box; }
/* Description: read-only view by default, with a toggle to switch into the editable textarea. */
.wk-desc { display: flex; flex-direction: column; gap: 8px; }
.wk-desc__view { font-size: 13px; color: var(--text); white-space: pre-wrap; word-break: break-word; min-height: 20px; }
.wk-desc.is-empty .wk-desc__view { color: var(--text-faint); font-style: italic; }
.wk-desc__bar { display: flex; justify-content: flex-end; }
.wk-desc__toggle { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: transparent; border: 1px solid var(--border); border-radius: 8px; padding: 3px 10px; cursor: pointer; }
.wk-desc__toggle:hover { background: var(--bg-app); border-color: var(--border-strong); }
.wk-detailpage .wk-field select:hover, .wk-detailpage .wk-field input:hover, .wk-detailpage .wk-field textarea:hover, .wk-detailpage textarea[data-field="notes"]:hover { border-color: var(--border-strong); }
.wk-detailpage .wk-field select:focus, .wk-detailpage .wk-field input:focus, .wk-detailpage textarea:focus { outline: none; border-color: var(--primary); box-shadow: inset 0 0 0 2px rgba(29, 78, 216, .14); }
/* Searchable select (Project / Parent / Assignee / Sprint): the native <select> is hidden
   and driven by this combobox; the popup floats on <body> so tile overflow can't clip it. */
.wk-detailpage select.wk-ssel-native { display: none !important; }
.wk-ssel__btn { display: flex; align-items: center; gap: 8px; width: 100%; font: inherit; font-size: 13px; text-align: left; padding: 7px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
.wk-ssel__btn:hover { border-color: var(--border-strong); }
.wk-ssel__btn[aria-expanded="true"] { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(29, 78, 216, .14); }
.wk-ssel__val { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-ssel__chev { color: var(--text-faint); font-size: 11px; flex-shrink: 0; }
.wk-ssel__pop { position: fixed; z-index: 6000; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 32px rgba(16,24,40,.18); padding: 6px; display: flex; flex-direction: column; gap: 6px; max-width: 92vw; }
.wk-ssel__search { font: inherit; font-size: 13px; padding: 7px 9px; border: 1px solid var(--border); border-radius: 7px; outline: none; }
.wk-ssel__search:focus { border-color: var(--primary); box-shadow: inset 0 0 0 2px rgba(29, 78, 216, .14); }
.wk-ssel__list { max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; }
.wk-ssel__grp { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-faint); padding: 8px 8px 3px; }
.wk-ssel__opt { text-align: left; font: inherit; font-size: 13px; padding: 7px 9px; border: 0; border-radius: 6px; background: transparent; color: var(--text); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wk-ssel__opt:hover { background: var(--bg-app); }
.wk-ssel__opt.is-on { background: rgba(29,78,216,.1); color: var(--primary); font-weight: 600; }
.wk-ssel__empty { padding: 10px 9px; color: var(--text-faint); font-size: 12.5px; }
/* Emoji picker (comment / reply composer): a small icon button + a palette floated on body. */
.wk-comment-actions { display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 8px; }
.wk-emojibtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 28px; font-size: 15px; line-height: 1; background: transparent; border: 1px solid var(--border); border-radius: 8px; cursor: pointer; padding: 0; }
.wk-emojibtn:hover { background: var(--bg-app); border-color: var(--border-strong); }
.wk-emojipop { position: fixed; z-index: 6000; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 32px rgba(16,24,40,.18); padding: 8px; width: 272px; }
.wk-emojipop__grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; max-height: 220px; overflow-y: auto; }
.wk-emojipop__e { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; font-size: 18px; line-height: 1; background: transparent; border: 0; border-radius: 6px; cursor: pointer; padding: 0; }
.wk-emojipop__e:hover { background: var(--bg-app); }
.wk-detailpage .wk-field__label { color: var(--text-subtle); letter-spacing: .05em; margin-bottom: 6px; }
.wk-detailpage .wk-dtitle { font-size: 20px; font-weight: 680; }
/* Detail header: a highlighted brand-gradient (green→blue, logo colors) box holding —
   in ONE row — the type chip, editable title, status switch and delete, with a small
   "← Tasks" breadcrumb just above the title. White foreground on the gradient. */
/* Task / Project / Phase / Risk detail header — a decent solid brand colour (not the gradient).
   Scope --app-header-text to white so the child title/key/back text stays readable on the teal. */
/* Plain detail header — the teal/green banner is removed; the title sits on the page's neutral
   surface (dark text), with the toolbar band above it carrying the actions. */
.wk-detail-header { background: transparent; border-radius: 0; padding: 6px 0 8px; margin-bottom: 4px; color: var(--text); box-shadow: none; }
.wk-detail-header__back { display: inline-flex; align-items: center; gap: 4px; font: inherit; font-size: 12px; font-weight: 600; color: rgba(255,255,255,.9); background: transparent; border: 0; padding: 0 0 7px; cursor: pointer; }
.wk-detail-header__back:hover { color: #fff; text-decoration: underline; }
.wk-detail-header__top { display: flex; align-items: center; gap: 10px; }
.wk-detail-header__row { display: flex; align-items: center; flex-wrap: nowrap; gap: 12px; }
/* Human-friendly task key badge (monospace). Header variant sits on the gradient. */
.wk-taskkey { font-family: var(--font-mono, ui-monospace, monospace); font-size: 11px; font-weight: 600; letter-spacing: .02em; border-radius: 6px; padding: 2px 7px; white-space: nowrap; }
/* Task key + copy option now live in the toolbar (neutral band) — dark, quiet chips. */
.wk-detail-tabbar .wk-taskkey { flex: none; color: var(--text-subtle); background: var(--bg-muted); border: 1px solid var(--border); }
.wk-taskkey-copy { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px; font-size: 13px; color: var(--text-subtle); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 7px; cursor: pointer; }
.wk-taskkey-copy:hover { background: var(--bg-hover); color: var(--text); }
.wk-taskkey--sm { font-size: 10px; padding: 1px 5px; color: var(--text-subtle); background: var(--bg-muted); border: 1px solid var(--border-muted); border-radius: 5px; }
/* Required marker (*) + field/section info (?) icon with an explanatory tooltip. */
.wk-req { color: #dc2626; font-weight: 700; margin-left: 3px; }
.wk-req--hdr { flex: none; color: #fecaca; font-size: 15px; margin: 0; align-self: center; }
/* Suppress the GLOBAL [data-tip]::after tooltip inside the detail — it's clipped by the
   tiles' overflow, and would double up with the body-rendered .wk-tip-float below. */
.wk-detailpage [data-tip]::after, .wk-detailpage [data-tip]::before { content: none !important; display: none !important; }
.wk-detailpage .ws-tip-i { cursor: help; }
.wk-detailpage .ws-tip-i:hover, .wk-detailpage .ws-tip-i:focus-visible { opacity: 1; outline: none; }
/* Body-rendered instant tooltip (controller.js), styled like the Orbit leads tooltip. */
.wk-tip-float, .app-tip-float { position: fixed; z-index: 100000; width: max-content; max-width: 320px; padding: 7px 11px; font-size: 12px; font-weight: 450; line-height: 1.45; letter-spacing: .01em; color: #f1f5f9; background: #0f172a; border: 1px solid rgba(255, 255, 255, 0.09); border-radius: 8px; box-shadow: 0 2px 4px rgba(2, 6, 23, .18), 0 10px 28px rgba(2, 6, 23, .32); pointer-events: none; opacity: 1; }
.wk-detail-header__row .wk-dtitle { flex: 1 1 auto; min-width: 0; color: var(--app-header-text); font-size: 20px; font-weight: 680; background: transparent; border-color: transparent; }
.wk-detail-header__row .wk-dtitle::placeholder { color: rgba(255,255,255,.65); }
.wk-detail-header__row .wk-dtitle:hover { background: transparent; }
.wk-detail-header__row .wk-dtitle:focus { background: rgba(255,255,255,.16); border-color: transparent; box-shadow: none; }
.wk-detail-header .wk-type { background: rgba(255,255,255,.2); color: #fff; }
.wk-detail-header__del { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 30px; font-size: 14px; color: #fff; background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.28); border-radius: 8px; cursor: pointer; transition: background .15s ease, border-color .15s ease; }
.wk-detail-header__del:hover { background: rgba(239,68,68,.4); border-color: rgba(252,165,165,.75); }
/* Status control — a real radio-button group: each option shows its radio dial + label. */
.wk-stateradios { display: inline-flex; align-items: center; flex: none; gap: 12px; padding: 0; background: none; border: 0; border-radius: 0; overflow: visible; }
.wk-stateradio { position: relative; display: inline-flex; align-items: center; gap: 5px; }
.wk-stateradio input { position: static; inset: auto; margin: 0; width: 14px; height: 14px; opacity: 1; cursor: pointer; accent-color: #0f766e; flex: none; }
.wk-stateradio > span { padding: 0; font-size: 12px; font-weight: 600; color: rgba(255,255,255,.82); white-space: nowrap; cursor: pointer; transition: color .15s ease; }
.wk-stateradio:hover > span { color: #fff; }
.wk-stateradio.is-on > span { color: #fff; }
.wk-stateradio input:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.wk-stateradio--in_progress.is-on > span { color: #93c5fd; }
.wk-stateradio--in_review.is-on > span { color: #fcd34d; }
.wk-stateradio--done.is-on > span { color: #86efac; }
/* On the in-flow detail page the toolbar is a neutral band, so the status radios sit as a plain
   inline group (dial + label) on the surface — subtle inactive labels, emphasised active one. */
#work-content .wk-detailpage .wk-stateradios { background: none; border: 0; padding: 0; height: auto; align-self: center; align-items: center; overflow: visible; }
#work-content .wk-detailpage .wk-stateradio { display: inline-flex; align-items: center; gap: 5px; }
#work-content .wk-detailpage .wk-stateradio > span { display: inline-flex; align-items: center; color: var(--text-subtle); padding: 0; line-height: 1; }
#work-content .wk-detailpage .wk-stateradio:hover > span { color: var(--text); background: none; }
#work-content .wk-detailpage .wk-stateradio.is-on > span { background: none; box-shadow: none; color: var(--text); font-weight: 700; }
/* Tab bar below the title (light bluish-green) holding the "← Tasks" back button and
   the status switch. Status radios are re-tinted for the light surface. */
.wk-detail-tabbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px; padding: 3px 10px; margin-bottom: 16px; }
/* Config-page toolbar title — the section name + its active sub-section (a breadcrumb), shown on the
   left of the band now that the primary navigation lives in the rail. */
.wk-cfg-title { display: inline-flex; align-items: baseline; gap: 8px; padding: 4px 4px 4px 6px; font-size: 14px; font-weight: 680; color: var(--text); white-space: nowrap; }
.wk-cfg-title__sub { font-size: 13px; font-weight: 600; color: var(--text-subtle); }
.wk-cfg-title__sub::before { content: '·'; margin-right: 8px; color: var(--text-faint); }
/* When a task/entity detail opens IN-FLOW, its tabbar is lifted (see _liftDetailTabbar) to be a
   direct child of .wk-detailpage and becomes the full-bleed, sticky PAGE TOOLBAR band directly under
   the Kriya header — carrying the detail's back / status / delete controls. */
#work-content .wk-detailpage > .wk-detail-tabbar {
  /* Flush to the content column's top + side edges — undo #wk-tasks-body's 8px/14px
     padding — so the band lines up exactly with the board toolbar, then re-inset the
     content by 14px to match. */
  margin: -8px -14px 0;
  padding: 6px 14px;
  /* EXACTLY the board toolbar (.wk-bar--tasks): white surface + the standard hairline,
     and it scrolls with the page (static) like the real toolbar — only the header pins. */
  background: var(--bg-surface, #fff);
  border: 0; border-bottom: 1px solid var(--border, #e5e7eb); border-radius: 0;
  position: static; z-index: auto;
}
.wk-detail-tabbar__back { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 13px; font-weight: 600; color: #0f766e; background: transparent; border: 0; padding: 3px 7px; border-radius: 8px; cursor: pointer; }
.wk-detail-tabbar__back:hover { background: rgba(15,118,110,.12); color: #0b5c55; }
.wk-detail-tabbar__parent { display: inline-flex; align-items: center; gap: 5px; max-width: 260px; font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: rgba(29,78,216,.07); border: 1px solid rgba(29,78,216,.2); padding: 3px 9px; border-radius: 8px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wk-detail-tabbar__parent:hover { background: rgba(29,78,216,.13); border-color: rgba(29,78,216,.4); }
/* Hierarchy dropdown (a subtask's parents + children), shown beside "← Tasks". */
.wk-hier { position: relative; display: inline-flex; }
.wk-detail-tabbar__hier { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: rgba(29,78,216,.07); border: 1px solid rgba(29,78,216,.2); padding: 3px 9px; border-radius: 8px; cursor: pointer; }
.wk-detail-tabbar__hier:hover { background: rgba(29,78,216,.13); border-color: rgba(29,78,216,.4); }
.wk-hier__pop { position: absolute; top: calc(100% + 4px); left: 0; z-index: 50; min-width: 260px; max-width: 440px; max-height: 340px; overflow: auto; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 32px rgba(16,24,40,.18); padding: 6px; }
.wk-hier__row { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; font: inherit; font-size: 12.5px; color: var(--text); background: transparent; border: 0; border-radius: 6px; padding: 6px 8px; cursor: pointer; white-space: nowrap; }
.wk-hier__row:hover:not(:disabled) { background: var(--bg-app); }
.wk-hier__row.is-current { font-weight: 700; color: var(--primary); background: rgba(29,78,216,.08); cursor: default; }
.wk-hier__title { overflow: hidden; text-overflow: ellipsis; }
.wk-detail-tabbar__expand { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 26px; font-size: 15px; line-height: 1; color: #0f766e; background: transparent; border: 1px solid rgba(15,118,110,.22); border-radius: 8px; cursor: pointer; }
.wk-detail-tabbar__expand:hover { background: rgba(15,118,110,.1); border-color: rgba(15,118,110,.42); }
.wk-detail-tabbar__left { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; min-width: 0; }
.wk-detail-tabbar__right { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.wk-detail-tabbar__del { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: #b91c1c; background: transparent; border: 1px solid rgba(185,28,28,.3); border-radius: 8px; padding: 3px 9px; cursor: pointer; }
.wk-detail-tabbar__del:hover { background: rgba(185,28,28,.09); border-color: rgba(185,28,28,.5); }
/* Neutral tabbar button (Save as template) — same shape as __del, non-destructive colour. */
.wk-detail-tabbar__tpl { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-subtle); background: transparent; border: 1px solid var(--border); border-radius: 8px; padding: 3px 9px; cursor: pointer; }
.wk-detail-tabbar__tpl:hover { background: var(--bg-app); color: var(--text); }
/* Close (✕) — rightmost of the entity detail toolbar; closes the page back to the list/previous
   view. Soft red tint so the exit reads at a glance (app-wide page-close standard). */
.wk-detail-tabbar__close { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; font: inherit; font-size: 15px; line-height: 1; color: #dc2626; background: rgba(220,38,38,.07); border: 1px solid rgba(220,38,38,.28); border-radius: 8px; cursor: pointer; margin-left: 2px; }
.wk-detail-tabbar__close:hover { background: #dc2626; color: #fff; border-color: #dc2626; }
/* Single page-level Edit toggle — matches the sibling toolbar buttons; turns primary when editing. */
.wk-detail-tabbar__edit { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: var(--primary, #2563eb); background: transparent; border: 1px solid color-mix(in srgb, var(--primary, #2563eb) 40%, transparent); border-radius: 8px; padding: 3px 11px; cursor: pointer; }
.wk-detail-tabbar__edit:hover { background: color-mix(in srgb, var(--primary, #2563eb) 8%, transparent); }
.wk-detail-tabbar__edit.is-editing { color: var(--primary-ink, #fff); background: var(--primary, #2563eb); border-color: var(--primary, #2563eb); }
/* SOMETHING WAITING TO BE SAVED reads as unsaved, not as another primary button. Amber rather than
   the primary blue for the same reason the dirty bar on the form canvas is amber: blue is the colour
   of "press this", and this state's message is "there is work here that is not stored yet". The
   button already says how many.
   NOT `var(--warn)`, which is the obvious choice and wrong here: the dark theme redefines it to a
   PALE YELLOW (#fdd663, line ~7980) and this button carries `color: #fff` from `.is-editing`, so the
   token would give white on pale yellow — about 1.5:1, unreadable. A literal amber that holds white
   text in both themes, with the foreground restated so it cannot be inherited away. */
.wk-detail-tabbar__edit.has-edits { color: #fff; background: #b45309; border-color: #b45309; }
.wk-detail-tabbar__edit.has-edits:hover { background: #92400e; border-color: #92400e; }
/* Performance-metrics (KPI) manager in Settings */
.wk-mt-head h2 { margin: 0 0 2px; font-size: 1.05rem; }
.wk-mt-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
.wk-mt-tab { font: inherit; font-size: 13px; font-weight: 600; padding: 5px 12px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface,#fff); color: var(--text-subtle); cursor: pointer; }
.wk-mt-tab.is-active { background: var(--primary,#1d4ed8); border-color: var(--primary,#1d4ed8); color: var(--primary-ink, #fff); }
.wk-mt-toolbar { margin: 4px 0 12px; }
.wk-mt-cat { margin-bottom: 16px; }
.wk-mt-cat__hd { font-size: .68rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint,#94a3b8); margin: 0 0 4px; }
.wk-mt-row { display: grid; grid-template-columns: 20px 1fr auto auto auto; align-items: center; gap: 10px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 9px; margin-bottom: 5px; background: var(--surface,#fff); }
.wk-mt-row.is-off { opacity: .5; }
.wk-mt-dir { font-weight: 800; text-align: center; }
.wk-mt-dir--higher { color: #16a34a; } .wk-mt-dir--lower { color: #dc2626; } .wk-mt-dir--target { color: var(--primary); }
.wk-mt-name { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.wk-mt-name b { font-size: 13px; }
.wk-mt-def { font-size: 11px; color: var(--text-faint,#94a3b8); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-mt-val { font-size: 15px; font-weight: 800; color: var(--text,#0f172a); font-variant-numeric: tabular-nums; }
.wk-mt-val--na { color: var(--text-faint,#94a3b8); font-weight: 600; }
.wk-mt-target { font-size: 11px; color: var(--text-subtle); white-space: nowrap; }
.wk-mt-badge { font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 999px; background: var(--bg-app,#f1f5f9); color: var(--text-faint,#94a3b8); white-space: nowrap; }
.wk-mt-badge--custom { background: rgba(37,99,235,.12); color: var(--primary); }
.wk-mt-acts { display: inline-flex; align-items: center; gap: 8px; }
.wk-mt-ibtn { border: 0; background: none; cursor: pointer; font-size: 13px; color: var(--text-subtle); padding: 2px; }
.wk-mt-ibtn:hover { color: var(--text); }
.wk-mt-builder { display: flex; flex-direction: column; gap: 10px; }
.wk-mt-builder__hd { display: flex; align-items: center; gap: 10px; }
.wk-mt-builder__hd h3 { margin: 0; font-size: 1rem; }
.wk-mt-back { border: 0; background: none; color: var(--primary,#1d4ed8); font: inherit; font-weight: 600; cursor: pointer; padding: 0; }
.wk-mt-f { display: flex; flex-direction: column; gap: 3px; font-size: 12px; font-weight: 600; color: var(--text-subtle); }
.wk-mt-f input, .wk-mt-f select, .wk-mt-f textarea { font: inherit; font-size: 13px; font-weight: 400; padding: 7px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app,#fff); color: var(--text); }
.wk-mt-frow { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px,1fr)); gap: 10px; }
.wk-mt-vars { border: 1px dashed var(--border); border-radius: 9px; padding: 8px 10px; }
.wk-mt-varchips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.wk-mt-varchip { font: inherit; font-size: 11px; font-family: ui-monospace, monospace; padding: 3px 7px; border: 1px solid var(--border); border-radius: 6px; background: var(--surface,#fff); color: var(--text); cursor: pointer; }
.wk-mt-varchip:hover { border-color: var(--primary,#1d4ed8); color: var(--primary,#1d4ed8); }
.wk-mt-builder__ft { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
/* Metric (KPI) dashboard widget card */
.wk-kpi { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 80px; padding: 8px; }
.wk-kpi__val { display: inline-flex; align-items: baseline; gap: 6px; font-size: 2rem; font-weight: 800; color: var(--text,#0f172a); font-variant-numeric: tabular-nums; }
.wk-kpi__val .wk-mt-dir { font-size: 1.1rem; }
.wk-kpi__target { font-size: 11px; color: var(--text-subtle); }
.wk-detail-tabbar .wk-stateradios { background: none; border: 0; }
/* Task templates library (Kriya) */
.wk-tt { max-width: none; }
.wk-tt-head h2 { margin: 0 0 2px; font-size: 18px; }
/* ── Lists manager (Labels / Item library) ── */
/* No global box-sizing reset exists here, so scope one: width:100% inputs must include their
   padding + border, else every text box overflows its cell/row and stretches the layout. */
.wk-lists, .wk-lists *, .wk-lists *::before, .wk-lists *::after { box-sizing: border-box; }
.wk-lists { padding: 0 0 8px; }
/* Keep the standalone add / search / column boxes to a comfortable width, not the full popup. */
.wk-lists-search, .wk-lists-add, .wk-litem-addcol { max-width: 620px; }
.wk-lists-head h2 { margin: 0 0 4px; font-size: 1.05rem; }
.wk-lists-head p { margin: 0; }
.wk-lists-tabs { display: flex; align-items: center; gap: 4px; border-bottom: 1px solid var(--border); margin: 14px 0 14px; flex-wrap: wrap; }
.wk-lists-tab { border: 0; background: none; padding: 8px 12px; font: inherit; font-size: 13px; font-weight: 600; color: var(--text-muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.wk-lists-tab:hover { color: var(--text); }
.wk-lists-tab.is-active { color: var(--primary); border-bottom-color: var(--primary); }
.wk-lists-projfilter { display: flex; align-items: center; gap: 9px; background: var(--app-filter-bg, var(--bg-muted, #f1f5f9)); border: 1px solid var(--app-filter-border, var(--border, #e5e7eb)); border-radius: 10px; padding: 8px 12px; margin: 0 0 12px; }
.wk-lists-projfilter > span { font-size: 12px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.wk-lists-projfilter select { flex: 1; min-width: 0; font: inherit; font-size: 13px; padding: 6px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.wk-lists-list { display: flex; flex-direction: column; gap: 7px; }
.wk-lrow { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-muted, #eef1f5); border-radius: 10px; background: var(--bg-surface, #fff); transition: border-color .12s, box-shadow .12s; }
.wk-lrow:hover { border-color: var(--border, #e5e7eb); box-shadow: 0 1px 3px rgba(15,23,42,.05); }
.wk-lchip { flex: none; width: 13px; height: 13px; border-radius: 4px; background: var(--wk-lc, #64748b); }
.wk-lchip--slate { --wk-lc: #64748b; } .wk-lchip--red { --wk-lc: #dc2626; } .wk-lchip--orange { --wk-lc: #ea580c; }
.wk-lchip--amber { --wk-lc: #d97706; } .wk-lchip--green { --wk-lc: #16a34a; } .wk-lchip--teal { --wk-lc: #0d9488; }
.wk-lchip--blue { --wk-lc: #2563eb; } .wk-lchip--violet { --wk-lc: #7c3aed; } .wk-lchip--pink { --wk-lc: #db2777; }
/* Name box mirrors Type-settings' .wk-type-name: a visible, always-bordered box that grows wide. */
.wk-linput { flex: 1 1 auto; min-width: 0; font: inherit; font-size: 13px; padding: 6px 9px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text); }
.wk-linput:hover { border-color: var(--border-strong, #cbd5e1); } .wk-linput:focus { outline: none; border-color: var(--primary, #2563eb); box-shadow: inset 0 0 0 2px rgba(37,99,235,.18); }
/* width overrides the global `input { width:100% }` so date fields stay compact, not full-width. */
.wk-ldate { flex: none; width: 140px; font: inherit; font-size: 12px; padding: 5px 7px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text); }
/* Compact color select (like .wk-type-level) — sized to content, not stretched. */
.wk-lselect { flex: none; width: auto; font: inherit; font-size: 11px; padding: 5px 6px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text-muted); }
.wk-lsep { flex: none; color: var(--text-faint); font-size: 12px; }
/* Native colour picker for labels (any colour) — same swatch style as Status settings' .wk-status-color.
   Explicit width overrides the global `input { width:100% }`. */
.wk-lcolor { flex: none; width: 32px; height: 30px; padding: 1px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); cursor: pointer; }
.wk-lcolor::-webkit-color-swatch-wrapper { padding: 0; }
.wk-lcolor::-webkit-color-swatch { border: none; border-radius: 5px; }
.wk-lstate { flex: none; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.wk-lists-add { display: flex; gap: 8px; margin-top: 12px; }
.wk-lists-add input { flex: 1; min-width: 0; font: inherit; font-size: 13px; padding: 8px 11px; border: 1px solid var(--border, #e5e7eb); border-radius: 8px; }
.wk-lists-add input:focus { outline: none; border-color: var(--primary, #2563eb); }
/* Item library search bar */
.wk-lists-search { display: flex; align-items: center; gap: 8px; padding: 7px 11px; margin: 0 0 12px; border: 1px solid var(--border, #e5e7eb); border-radius: 10px; background: var(--bg-surface, #fff); }
.wk-lists-search .hicon-wrap { color: var(--text-faint); flex: none; }
.wk-lists-search input { flex: 1; min-width: 0; border: 0; background: none; font: inherit; font-size: 13px; color: var(--text); }
.wk-lists-search input:focus { outline: none; }
/* Item library — user-defined columns: add-column controls grouped as one single-row block */
.wk-litem-addcol { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; margin: 0 0 12px; padding: 8px 10px; border: 1px solid var(--border, #e5e7eb); border-radius: 10px; background: var(--bg-surface, #fff); }
.wk-litem-addcol__lbl { flex: none; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); }
.wk-litem-addcol input { flex: 1 1 auto; min-width: 0; font: inherit; font-size: 13px; padding: 7px 10px; border: 1px solid var(--border, #e5e7eb); border-radius: 8px; }
.wk-litem-addcol input:focus { outline: none; border-color: var(--primary, #2563eb); }
/* width:auto overrides the global `select { width:100% }`, so the type picker stays compact. */
.wk-litem-addcol select { flex: none; width: auto; font: inherit; font-size: 13px; padding: 7px 8px; border: 1px solid var(--border, #e5e7eb); border-radius: 8px; background: var(--bg-surface, #fff); color: var(--text); }
.wk-litem-addcol button { flex: none; white-space: nowrap; }
/* Item-library create block (New item + From-link) pinned at the top of the list */
.wk-lists-create { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; }
.wk-lists-create .wk-lists-add { margin: 0; }
/* Item-library table — one real table so header + rows share column tracks (perfect alignment).
   Horizontal-only scroll here; the vertical scrollbar stays at the popup/page level. */
.wk-litems-scroll { overflow-x: auto; overflow-y: hidden; border: 1px solid var(--border, #e5e7eb); border-radius: 10px; }
.wk-ltable { width: 100%; border-collapse: collapse; font-size: 13px; }
.wk-ltable th, .wk-ltable td { text-align: left; min-width: 84px; }
.wk-lth { padding: 8px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); background: var(--bg-muted, #f1f5f9); border-bottom: 1px solid var(--border, #e5e7eb); white-space: nowrap; }
.wk-lth__wrap { display: inline-flex; align-items: center; gap: 4px; }
.wk-ltd { padding: 3px 6px; border-bottom: 1px solid var(--border-muted, #eef1f5); vertical-align: middle; }
.wk-ltable tbody tr:last-child .wk-ltd { border-bottom: 0; }
.wk-ltable tbody tr:hover td { background: var(--bg-app, #f8fafc); }
.wk-ltable__empty { padding: 18px 10px; color: var(--text-muted); text-align: center; }
.wk-ltable .wk-ltd--name { min-width: 190px; }
.wk-ltable .wk-ltd--img { width: 52px; min-width: 52px; }
.wk-ltable .wk-ltd--num { text-align: right; }
.wk-ltable .wk-ltd--used { width: 56px; min-width: 56px; text-align: center; }
.wk-ltable .wk-ltd--x { width: 36px; min-width: 36px; text-align: center; }
.wk-litem__in { width: 100%; min-width: 0; font: inherit; font-size: 13px; padding: 5px 7px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); }
.wk-litem__in:hover { border-color: var(--border, #e5e7eb); } .wk-litem__in:focus { outline: none; border-color: var(--primary, #2563eb); background: var(--bg-surface, #fff); }
.wk-litem__in::placeholder { color: var(--text-faint); }
.wk-litem__in--name { font-weight: 600; }
.wk-litem__in--num { text-align: right; }
.wk-litem__used { font-size: 11px; font-weight: 600; color: var(--text-muted); background: var(--bg-muted, #f1f5f9); border-radius: 999px; padding: 2px 8px; }
.wk-xrow__x { border: none; background: none; cursor: pointer; color: var(--text-faint); font-size: 14px; line-height: 1; padding: 4px; border-radius: 6px; }
.wk-xrow__x:hover { color: var(--danger, #dc2626); background: var(--danger-soft, rgba(220,38,38,.08)); }

/* Automations center — entity tabs · presets · rule list · custom builder */
.wk-auto { display: flex; flex-direction: column; gap: 12px; padding: 4px 2px 12px; }
.wk-auto-tabs { display: flex; gap: 4px; flex-wrap: wrap; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
.wk-auto-tab { font: inherit; font-size: 13px; font-weight: 600; padding: 6px 12px; border: 1px solid transparent; border-radius: 8px; background: transparent; color: var(--text-subtle); cursor: pointer; }
.wk-auto-tab:hover { background: var(--app-filter-bg); color: var(--text); }
.wk-auto-tab.is-active { background: var(--primary, #1d4ed8); color: var(--primary-ink, #fff); }
.wk-auto-presets { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.wk-auto-recipe { font: inherit; font-size: 12px; padding: 5px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-surface); color: var(--text); cursor: pointer; }
.wk-auto-recipe:hover { border-color: var(--primary); color: var(--primary); }
.wk-auto-recipe--new { border-style: dashed; font-weight: 600; }
.wk-auto-list { display: flex; flex-direction: column; gap: 8px; }
.wk-auto-row { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: start; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); }
.wk-auto-name { min-width: 0; }
.wk-auto-actions { display: flex; gap: 4px; align-items: center; }
.wk-auto-runs { grid-column: 1 / -1; border-top: 1px dashed var(--border); margin-top: 4px; padding-top: 6px; display: flex; flex-direction: column; gap: 3px; }
/* Builder */
.wk-auto-builder { border: 1px solid var(--border); border-radius: 10px; background: var(--app-filter-bg); }
.wk-auto-build { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; }
.wk-auto-build__row { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; color: var(--text); }
.wk-auto-sub { font-size: 12px; font-weight: 700; color: var(--text-subtle); margin-top: 4px; }
.wk-auto-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wk-auto-input { font: inherit; font-size: 13px; font-weight: 400; padding: 5px 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); }
.wk-auto-build__foot { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }
.wk-auto-build__title { font-size: 13px; font-weight: 700; color: var(--text); }
.wk-auto-badge { display: inline-block; font-size: 10px; font-weight: 700; padding: 1px 7px; border-radius: 999px; background: var(--app-filter-bg); color: var(--text-subtle); vertical-align: middle; }
.wk-auto-badge--on { background: var(--ok-soft, rgba(22,163,74,.12)); color: var(--ok, #16a34a); }
.wk-auto-row.is-off { opacity: .72; }
.wk-auto-row.is-off .wk-auto-name strong { color: var(--text-subtle); }

/* ── Automations Center (redesigned) — Gallery / My automations / Activity + guided builder ────── */
.wk-au { display: flex; flex-direction: column; gap: 12px; padding: 0 0 12px; }
.wk-au-head { display: flex; flex-direction: column; gap: 10px; }
/* Config pages render INSIDE the content column (the Config rail sits to their left), so — unlike the
   full-width detail page — their toolbar must NOT full-bleed with negative page-gutter margins (that
   pulled the band left, under the rail and out of alignment with the body below). Keep the contained,
   rounded .wk-detail-tabbar band that fits the column exactly and aligns flush with the body content;
   make it sticky to the top of the scroll area with a small inset. */
#work-content .wk-au-bar,
#work-content .wk-tt-bar,
#work-content .wk-mt-bar,
#work-content .wk-ag-bar,
#work-content .wk-set-bar,
#work-content .wk-lists-bar {
  margin: 0 0 14px;
}
.wk-au-bar .wk-au-tabs { border-bottom: 0; }
.wk-au-bar .wk-au-tab { padding: 6px 2px; }
/* Templates + KPI toolbars: their kind/filter tab rows sit inline in the band (drop block margins +
   the filter row's underline so they read as toolbar controls, not stacked sections). */
.wk-tt-bar .wk-tt-kinds, .wk-tt-bar .wk-tt-tabs, .wk-mt-bar .wk-mt-tabs, .wk-lists-bar .wk-lists-tabs { margin: 0; }
.wk-tt-bar .wk-tt-tabs, .wk-lists-bar .wk-lists-tabs { border-bottom: 0; }
.wk-au-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); }
.wk-au-tab { position: relative; font: inherit; font-size: 13px; font-weight: 600; padding: 8px 4px; margin-right: 14px; border: 0; background: none; color: var(--text-subtle); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.wk-au-tab:hover { color: var(--text); }
.wk-au-tab.is-active { color: var(--primary, #1d4ed8); border-bottom-color: var(--primary, #1d4ed8); }
.wk-au-count { display: inline-block; min-width: 18px; text-align: center; font-size: 11px; font-weight: 700; padding: 0 5px; border-radius: 999px; background: var(--app-filter-bg); color: var(--text-subtle); }
.wk-au-tab.is-active .wk-au-count { background: color-mix(in srgb, var(--primary, #1d4ed8) 14%, transparent); color: var(--primary, #1d4ed8); }
.wk-au-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.wk-au-searchwrap { position: relative; display: inline-flex; align-items: center; }
.wk-au-searchic { position: absolute; left: 9px; font-size: 12px; opacity: .5; pointer-events: none; }
.wk-au-search { font: inherit; font-size: 13px; padding: 7px 10px 7px 28px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); width: 230px; max-width: 100%; }
.wk-au-search:focus { outline: none; border-color: var(--primary, #2563eb); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary, #2563eb) 14%, transparent); }
.wk-au-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.wk-au-chip { font: inherit; font-size: 12px; font-weight: 600; padding: 5px 11px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-surface); color: var(--text-subtle); cursor: pointer; }
.wk-au-chip:hover { color: var(--text); border-color: var(--border-strong); }
.wk-au-chip.is-active { background: var(--primary, #1d4ed8); border-color: var(--primary, #1d4ed8); color: var(--primary-ink, #fff); }
/* explainer strip */
.wk-au-explain { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 14px; border-radius: 10px; background: linear-gradient(90deg, color-mix(in srgb, var(--primary,#2563eb) 7%, transparent), transparent); border: 1px solid var(--border-muted, #eef0f3); font-size: 12px; color: var(--text-subtle); }
.wk-au-explain__t { font-weight: 700; color: var(--text); margin-right: 4px; }
.wk-au-step b { color: var(--text); font-weight: 700; }
.wk-au-opt { font-style: italic; opacity: .7; }
.wk-au-arrow { color: var(--primary, #2563eb); font-weight: 700; }
/* recipe gallery */
.wk-au-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.wk-au-card { display: flex; flex-direction: column; gap: 8px; padding: 12px 13px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); transition: border-color .12s, box-shadow .12s, transform .12s; }
.wk-au-card:hover { border-color: var(--border-strong); box-shadow: 0 3px 12px rgba(15,23,42,.06); }
.wk-au-card__tag { align-self: flex-start; font-size: 11px; font-weight: 700; padding: 2px 9px; border-radius: 999px; background: hsl(var(--h) 85% 96%); color: hsl(var(--h) 55% 34%); border: 1px solid hsl(var(--h) 70% 88%); }
.wk-au-card__name { font-size: 13.5px; font-weight: 700; color: var(--text); line-height: 1.3; }
.wk-au-card__sentence { font-size: 12px; color: var(--text-subtle); line-height: 1.45; flex: 1; }
.wk-au-card__foot { display: flex; justify-content: flex-end; }
.wk-au-card--new { align-items: center; justify-content: center; text-align: center; gap: 4px; border: 2px dashed #ea580c; cursor: pointer; color: var(--text); min-height: 140px; background: color-mix(in srgb, #ea580c 5%, transparent); font: inherit; }
.wk-au-card--new:hover { border-color: #c2410c; color: #c2410c; background: color-mix(in srgb, #ea580c 10%, transparent); box-shadow: 0 3px 12px color-mix(in srgb, #ea580c 18%, transparent); }
.wk-au-card--new__plus { font-size: 26px; line-height: 1; color: #ea580c; }
.wk-au-card--new__t { font-size: 13.5px; font-weight: 700; }
.wk-au-card--new__s { font-size: 12px; color: var(--text-subtle); }
/* coloured sentence bits */
.wk-au-etag { display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 7px; border-radius: 999px; background: hsl(var(--h) 85% 96%); color: hsl(var(--h) 55% 34%); border: 1px solid hsl(var(--h) 70% 88%); vertical-align: middle; }
.wk-au-etag--sm { padding: 0 5px; }
.wk-au-kw { font-weight: 800; color: var(--primary, #1d4ed8); text-transform: uppercase; font-size: .86em; letter-spacing: .02em; }
/* my automations list */
.wk-au-rules { display: flex; flex-direction: column; gap: 8px; }
.wk-au-rule { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; padding: 12px 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); }
.wk-au-rule.is-off { opacity: .74; }
.wk-au-rule__name { font-size: 13.5px; font-weight: 700; color: var(--text); }
.wk-au-rule__sentence { font-size: 12.5px; color: var(--text-subtle); line-height: 1.45; margin-top: 2px; }
.wk-au-rule__meta { font-size: 11.5px; color: var(--text-faint, #94a3b8); margin-top: 4px; }
.wk-au-live { color: var(--ok, #16a34a); font-weight: 700; }
.wk-au-paused { color: var(--text-subtle); font-weight: 700; }
.wk-au-rule__acts { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.wk-au-del:hover { color: var(--danger, #dc2626); border-color: var(--danger, #dc2626); }
.wk-au-runs { grid-column: 1 / -1; border-top: 1px dashed var(--border); margin-top: 2px; padding-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.wk-au-run { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text-subtle); }
/* ON/OFF switch */
.wk-au-switch { position: relative; display: inline-flex; cursor: pointer; }
.wk-au-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.wk-au-switch__track { width: 38px; height: 22px; border-radius: 999px; background: var(--border-strong, #cbd5e1); transition: background .15s; display: inline-block; }
.wk-au-switch__knob { position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--bg-surface); box-shadow: 0 1px 3px rgba(0,0,0,.25); transition: transform .15s; }
.wk-au-switch input:checked + .wk-au-switch__track { background: var(--ok, #16a34a); }
.wk-au-switch input:checked + .wk-au-switch__track .wk-au-switch__knob { transform: translateX(16px); }
.wk-au-switch input:focus-visible + .wk-au-switch__track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok,#16a34a) 30%, transparent); }
/* empty state */
.wk-au-empty { text-align: center; padding: 40px 20px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.wk-au-empty__ic { font-size: 34px; }
.wk-au-empty__t { font-size: 16px; font-weight: 700; color: var(--text); }
.wk-au-empty__s { font-size: 13px; color: var(--text-subtle); max-width: 380px; line-height: 1.5; }
.wk-au-empty button { margin-top: 6px; }
/* guided builder */
.wk-au-build { border: 1px solid var(--primary, #2563eb); border-radius: 12px; background: color-mix(in srgb, var(--primary,#2563eb) 3%, var(--bg-surface)); padding: 14px 16px; display: flex; flex-direction: column; gap: 12px; }
.wk-au-build__head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.wk-au-build__title { font-size: 14px; font-weight: 800; color: var(--text); }
.wk-au-build__name { flex: 1; min-width: 180px; font: inherit; font-size: 13px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.wk-au-blocks { display: flex; flex-direction: column; gap: 8px; }
.wk-au-block { border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); padding: 10px 12px; border-left: 3px solid var(--border-strong); }
.wk-au-block--when { border-left-color: var(--primary); }
.wk-au-block--if { border-left-color: #d97706; }
.wk-au-block--then { border-left-color: #16a34a; }
.wk-au-block__lbl { font-size: 11px; font-weight: 800; letter-spacing: .04em; color: var(--text-subtle); margin-bottom: 8px; }
.wk-au-block__row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wk-au-block__pre { font-size: 13px; color: var(--text-subtle); }
.wk-au-lines { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }
.wk-au-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* width:auto beats the global select{width:100%} so builder controls flow inline as a sentence, not
   stacked full-width rows. background-COLOR (not the shorthand) keeps the global chevron image. */
.wk-au-sel { width: auto; min-width: 0; max-width: 100%; font: inherit; font-size: 13px; padding: 6px 26px 6px 9px; border: 1px solid var(--border); border-radius: 7px; background-color: var(--bg-surface); color: var(--text); }
input.wk-au-sel { padding-right: 9px; }
.wk-au-sel--op { color: var(--text-subtle); }
.wk-au-sel--wide { min-width: 220px; }
.wk-au-num { width: 56px; font: inherit; font-size: 13px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); }
.wk-au-days { font-size: 13px; color: var(--text-subtle); display: inline-flex; align-items: center; gap: 6px; }
.wk-au-linex { border: 0; background: none; color: var(--text-faint, #94a3b8); cursor: pointer; font-size: 14px; padding: 2px 4px; border-radius: 6px; }
.wk-au-linex:hover { color: var(--danger, #dc2626); background: var(--danger-soft, rgba(220,38,38,.08)); }
.wk-au-preview { font-size: 13px; line-height: 1.5; color: var(--text); padding: 10px 12px; border-radius: 10px; background: color-mix(in srgb, var(--primary,#2563eb) 6%, transparent); border: 1px dashed color-mix(in srgb, var(--primary,#2563eb) 40%, transparent); }
.wk-au-preview__lbl { font-weight: 700; color: var(--text-subtle); margin-right: 4px; }
.wk-au-build__foot { display: flex; justify-content: flex-end; gap: 8px; }
/* activity feed */
.wk-au-activity { display: flex; flex-direction: column; gap: 2px; }
.wk-au-actrow { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: center; padding: 9px 4px; border-bottom: 1px solid var(--border-muted, #f1f5f9); }
.wk-au-actdot { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; border-radius: 50%; font-size: 11px; font-weight: 800; flex: none; }
.wk-au-actdot.is-ok { background: var(--ok-soft, rgba(22,163,74,.14)); color: var(--ok, #16a34a); }
.wk-au-actdot.is-err { background: var(--danger-soft, rgba(220,38,38,.12)); color: var(--danger, #dc2626); }
.wk-au-acttime { font-size: 11.5px; color: var(--text-faint, #94a3b8); white-space: nowrap; }

/* Entity file rows — link-style name button + small action buttons (open in masked popup / download) */
.wk-file__name--btn { background: none; border: 0; padding: 0; font: inherit; color: var(--primary, #1d4ed8); cursor: pointer; text-align: left; }
.wk-file__name--btn:hover { text-decoration: underline; }
.wk-efile-acts { display: inline-flex; gap: 4px; align-items: center; }

/* Project-settings danger zone */
.wk-danger { margin-top: 16px; border: 1px solid var(--danger, #dc2626); border-radius: 10px; padding: 12px 14px; background: var(--danger-soft, rgba(220,38,38,.05)); }
.wk-danger__head { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--danger, #dc2626); margin-bottom: 8px; }
.wk-danger__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.wk-danger__btn { color: var(--danger, #dc2626); border-color: var(--danger, #dc2626); }
.wk-danger__btn:hover { background: var(--danger, #dc2626); color: #fff; }
.wk-recdel { margin-top: 12px; }
.wk-recdel__head { font-size: 11px; font-weight: 700; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .03em; margin-bottom: 6px; }
.wk-recdel__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); margin-bottom: 6px; font-size: 13px; }
/* Project key handle (PRJ-XXXXXX) — monospace chip in lists + a read-only field on the detail */
.wk-pkey { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; font-weight: 600; background: var(--app-filter-bg); color: var(--text-subtle); padding: 2px 6px; border-radius: 5px; margin-right: 6px; white-space: nowrap; }
.wk-pkey-field { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: .04em; font-weight: 600; }
/* Risk rating chip — a colour bar + band label + score, coloured by the org's risk-score bands. */
.wk-rating { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: var(--text); padding: 2px 8px 2px 4px; border-radius: 999px; background: color-mix(in srgb, var(--rc, #64748b) 13%, transparent); white-space: nowrap; }
.wk-rating__bar { width: 4px; height: 13px; border-radius: 2px; background: var(--rc, #64748b); flex: none; }
.wk-rating__score { font-size: 10px; color: var(--text-subtle); font-weight: 700; }

/* Entity detail two-column layout: tiles (left) + Activity & comments rail (right) */
.wk-ent-layout { display: flex; gap: 16px; align-items: flex-start; }
.wk-ent-main { flex: 1; min-width: 0; }
.wk-ent-side { flex: none; width: 340px; position: sticky; top: 8px; }
@media (max-width: 960px) { .wk-ent-layout { flex-direction: column; } .wk-ent-side { width: 100%; position: static; } }
/* Activity & comments + Automation rules are real .wk-tile cards (identical to the other detail
   sections) — these classes only add the inner scroll + rail spacing, and the composer/thread reuse
   the task detail's .wk-comment / .wk-timeline classes so they render identically. */
.wk-entc { display: flex; flex-direction: column; max-height: calc(100vh - 170px); }
.wk-entc__body { flex: 1; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; }
/* Rail cards aren't drag-reorderable — don't show the tile header's grab cursor. */
.wk-ent-side .wk-tile__head, .wk-entc-below .wk-tile__head { cursor: default; }
/* Automation-rules panel: a .wk-tile card; just needs list spacing + a plain link button. */
.wk-ent-autos { flex: none; }
.wk-ent-autos__list { padding: 10px 14px; display: flex; flex-direction: column; gap: 10px; }
.wk-ent-autos__row { font-size: 13px; line-height: 1.45; }
.wk-ent-autos__row strong { font-weight: 600; margin-left: 4px; }
.wk-linkbtn { background: none; border: 0; padding: 0; font: inherit; color: var(--primary, #1d4ed8); cursor: pointer; text-decoration: underline; }
/* Schedules: Activity & comments sit BELOW the full-width grid (not a right rail) */
.wk-entc-below { margin-top: 16px; }
.wk-entc-below .wk-entc { max-height: none; }
.wk-entc-below .wk-entc__body { max-height: 420px; }
.wk-litem__delcol { border: none; background: none; cursor: pointer; color: var(--text-faint); font-size: 11px; line-height: 1; padding: 1px 3px; border-radius: 4px; }
.wk-litem__delcol:hover { color: var(--danger, #dc2626); background: var(--danger-soft, rgba(220,38,38,.08)); }
/* Item-library row image (thumbnail uploader) */
.wk-litem__imgcell { position: relative; display: inline-block; width: 40px; height: 40px; }
.wk-litem__img { display: flex; align-items: center; justify-content: center; width: 40px; height: 40px; border: 1px dashed var(--border, #e5e7eb); border-radius: 8px; overflow: hidden; cursor: pointer; background: var(--bg-app, #f8fafc); }
.wk-litem__img.has-img { border-style: solid; }
.wk-litem__img:hover { border-color: var(--primary, #2563eb); }
.wk-litem__img img { width: 100%; height: 100%; object-fit: cover; }
.wk-litem__img-ph { font-size: 16px; opacity: .45; }
.wk-litem__imgx { position: absolute; top: -6px; right: -6px; width: 16px; height: 16px; border: none; border-radius: 50%; background: var(--danger, #dc2626); color: #fff; font-size: 9px; line-height: 1; cursor: pointer; display: none; align-items: center; justify-content: center; padding: 0; box-shadow: 0 1px 2px rgba(0,0,0,.25); }
.wk-litem__imgcell:hover .wk-litem__imgx { display: flex; }
/* Template KIND selector (Task / Project / Phase / Risk / Bug) — pill row above the source tabs. */
.wk-tt-kinds { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 4px 0 2px; }
.wk-tt-kind { border: 1px solid var(--border-muted, #e5e7eb); background: var(--bg-surface, #fff); color: var(--text-muted, #64748b);
  border-radius: 999px; padding: 5px 12px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.wk-tt-kind:hover { background: var(--bg-muted, #f1f5f9); color: var(--text, #0f172a); }
.wk-tt-kind.is-active { background: var(--accent-soft, rgba(37, 99, 235, .1)); color: var(--primary, #1d4ed8); border-color: transparent; }
.wk-tt-tabs { display: flex; align-items: center; gap: 4px; border-bottom: 1px solid var(--border); margin: 12px 0 14px; flex-wrap: wrap; }
.wk-tt-tab { border: 0; background: none; padding: 8px 12px; font: inherit; font-size: 13px; font-weight: 600; color: var(--text-muted); cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px; }
.wk-tt-tab:hover { color: var(--text); }
.wk-tt-tab.is-active { color: var(--primary); border-bottom-color: var(--primary); }
.wk-tt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 12px; }
.wk-tt-card { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--bg-surface); display: flex; flex-direction: column; gap: 6px; }
/* "＋ New template" create tile — the Automations "Create your own" look (orange dashed).
   Doubled class beats the base .wk-tt-card border/background above. */
.wk-tt-card.wk-tt-card--new { align-items: center; justify-content: center; text-align: center; gap: 4px; border: 2px dashed #ea580c; cursor: pointer; color: var(--text); min-height: 140px; background: color-mix(in srgb, #ea580c 5%, transparent); font: inherit; }
.wk-tt-card.wk-tt-card--new:hover { border-color: #c2410c; color: #c2410c; background: color-mix(in srgb, #ea580c 10%, transparent); box-shadow: 0 3px 12px color-mix(in srgb, #ea580c 18%, transparent); }
.wk-tt-card__h { font-size: 14px; }
.wk-tt-badges { display: flex; gap: 4px; flex-wrap: wrap; }
.wk-tt-badge { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 11px; font-weight: 600; border: 1px solid var(--border); color: var(--text-subtle); text-transform: capitalize; }
.wk-tt-desc { margin: 2px 0 0; font-size: 12.5px; color: var(--text-muted); }
.wk-tt-actions { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.wk-detail-tabbar .wk-stateradio > span { color: rgba(15,118,110,.78); }
.wk-detail-tabbar .wk-stateradio:hover > span { color: #0f766e; }
.wk-detail-tabbar .wk-stateradio.is-on > span { background: none; box-shadow: none; color: #0f766e; }
.wk-detail-tabbar .wk-stateradio--in_progress.is-on > span { color: var(--primary); }
.wk-detail-tabbar .wk-stateradio--in_review.is-on > span { color: #b45309; }
.wk-detail-tabbar .wk-stateradio--done.is-on > span { color: #166534; }
.wk-drawer__head { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--border-muted); }
.wk-drawer__body { flex: 1; overflow-y: auto; padding: 16px 18px 28px; }
.wk-dtitle { width: 100%; font-size: 18px; font-weight: 650; color: var(--text); border: 1px solid transparent; border-radius: 8px; padding: 6px 8px; background: transparent; }
.wk-dtitle:hover { border-color: var(--border); }
.wk-dtitle:focus { border-color: var(--border-strong); background: var(--bg-app); outline: none; }
.wk-field { margin: 14px 0; }
.wk-field__label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-faint); margin-bottom: 5px; }
.wk-fieldgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
/* Wide fields (Name/Title + the long-text boxes at the end of a Details grid) span the full row
   EVERYWHERE a fieldgrid renders — previously only the legacy detail modal had this rule. */
.wk-fieldgrid .wk-field--wide { grid-column: 1 / -1; }
.wk-field select, .wk-field input[type="date"], .wk-field input[type="datetime-local"], .wk-field input[type="number"], .wk-field input[type="text"], .wk-field textarea {
  width: 100%; font: inherit; font-size: 13px; padding: 7px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); color: var(--text);
}
.wk-field textarea { resize: vertical; min-height: 64px; }
/* Inline-editable Phases table (project detail page): compact controls sized to the table cells. */
.wk-trow__cell > [data-phasefield] { width: 100%; box-sizing: border-box; font: inherit; font-size: 12px; padding: 5px 7px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text); }
.wk-trow__cell > select[data-phasefield] { padding-right: 24px; background-position: right 6px center; cursor: pointer; }
.wk-trow__cell > [data-phasefield]:focus { outline: none; border-color: var(--primary, #2563eb); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--primary, #2563eb) 18%, transparent); }
/* Phase dependency picker — an explicit checkbox list (tick = assign, untick = remove). Scrolls when
   there are many phases; used both in the phase-detail form and the project page's Phases table cell. */
/* Depends-on: a <details> dropdown whose summary shows the current selection and whose popover holds the
   ticklist. Position:relative so the popover floats over the row/table without stretching it. */
.wk-depdd { position: relative; display: inline-block; width: 100%; }
.wk-depdd__btn { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 4px 8px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); cursor: pointer; list-style: none; user-select: none; }
.wk-depdd__btn::-webkit-details-marker { display: none; }
.wk-depdd__btn:hover { border-color: var(--primary, #2563eb); }
.wk-depdd__sel { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-depdd__car { flex: none; color: var(--text-muted, #6b7280); font-size: 10px; transition: transform .12s ease; }
.wk-depdd[open] .wk-depdd__car { transform: rotate(180deg); }
.wk-depdd__pop { position: absolute; z-index: 40; top: calc(100% + 3px); left: 0; min-width: 100%; max-width: 260px; max-height: 168px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; padding: 4px; border: 1px solid var(--border, #e5e7eb); border-radius: 8px; background: var(--bg-surface, #fff); box-shadow: 0 8px 24px rgba(0,0,0,.12); }
.wk-depcheck { display: flex; align-items: center; gap: 6px; font-size: 12px; padding: 3px 5px; border-radius: 5px; cursor: pointer; }
.wk-depcheck:hover { background: var(--bg-hover); }
.wk-depcheck > input[type="checkbox"] { flex: none; width: 14px; height: 14px; margin: 0; accent-color: var(--primary, #2563eb); cursor: pointer; }
.wk-depcheck > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-depcheck > input:disabled { cursor: default; }
/* Read-only (view mode): dim the summary and stop it opening. */
.wk-depdd:has(input:disabled) .wk-depdd__btn { opacity: .7; cursor: default; }
.wk-depdd:has(input:disabled) .wk-depdd__car { display: none; }
/* Hierarchy tile (project & phase parent/child) — a child list + detach ✕, matching the subtasks look. */
.wk-hierlist { display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; }
.wk-hierrow { display: flex; align-items: center; gap: 6px; }
.wk-hierrow__open { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; min-width: 0; padding: 5px 8px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text); cursor: pointer; text-align: left; }
.wk-hierrow__open:hover { border-color: var(--primary, #2563eb); background: var(--bg-hover); }
.wk-hierrow__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.wk-hierrow__x { flex: none; width: 26px; height: 26px; border: 1px solid var(--border, #e5e7eb); border-radius: 7px; background: var(--bg-surface, #fff); color: var(--text-muted, #64748b); cursor: pointer; line-height: 1; }
.wk-hierrow__x:hover { border-color: #dc2626; color: #dc2626; }
.wk-hieradd { max-width: 320px; }
/* View mode: the tile is read-only — hide the detach ✕ (the selects are already disabled → plain text). */
.wk-tile--items.is-viewing .wk-tile__body .wk-hierrow__x { display: none !important; }
.wk-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.wk-subtasks { display: flex; flex-direction: column; gap: 4px; }
.wk-subtask { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
.wk-subtask__title { font-size: 13px; color: var(--text); cursor: pointer; }
.wk-subtask__title.is-done { color: var(--text-faint); text-decoration: line-through; }
/* Shared existing-item rows: reminders / time logs / recurrences / labels / attachments /
   child items — organised, spaced, bordered rows with the info on one line. Defined here
   (not only in taskpanels' injected CSS) so the detail tiles render styled immediately. */
.wk-xlist { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.wk-xitem { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); font-size: 12.5px; }
.wk-xitem__info { display: flex; align-items: center; gap: 8px; min-width: 0; overflow-wrap: anywhere; }
.wk-remicon { color: var(--primary); flex: none; }  /* reminder bell — calm cobalt, not red */
.wk-xitem__x { border: none; background: none; cursor: pointer; color: var(--text-faint); flex: none; font-size: 13px; line-height: 1; padding: 2px; }
.wk-xitem__x:hover { color: var(--danger); }
.wk-subitem__ops { display: inline-flex; align-items: center; gap: 4px; flex: none; }
.wk-subitem__op { border: none; background: none; cursor: pointer; color: var(--text-subtle); font-size: 13px; line-height: 1; padding: 2px; }
.wk-subitem__op:hover { color: var(--primary); }
/* Child-item completion box is a checkbox (square), not a radio (circle). */
.wk-subitem .wk-check { border-radius: 5px; }

/* Timeline: comments + replies + activity */
.wk-timeline { display: flex; flex-direction: column; gap: 12px; margin-top: 6px; }
.wk-activity { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--text-subtle); }
.wk-activity__dot { width: 6px; height: 6px; border-radius: 99px; background: var(--border-strong); flex: none; margin: 0 8px; }
.wk-comment { display: flex; gap: 10px; }
.wk-comment__av { flex: none; }
.wk-comment__main { flex: 1; min-width: 0; }
.wk-comment__head { display: flex; align-items: baseline; gap: 8px; }
.wk-comment__author { font-size: 12px; font-weight: 700; color: var(--text); }
.wk-comment__time { font-size: 11px; color: var(--text-faint); }
.wk-comment__body { font-size: 13px; color: var(--text); white-space: pre-wrap; word-break: break-word; margin-top: 2px; }
.wk-comment__reply { font-size: 11px; color: var(--text-subtle); background: none; border: 0; cursor: pointer; padding: 2px 0; margin-top: 2px; }
.wk-comment__reply:hover { color: var(--text); }
.wk-comment__ops { display: flex; align-items: center; gap: 14px; margin-top: 3px; }
.wk-comment__op { font-size: 11px; color: var(--text-subtle); background: none; border: 0; cursor: pointer; padding: 0; }
.wk-comment__op:hover { color: var(--text); }
/* Comments toolbar: a Refresh that reloads just the timeline (no full-page reload). */
.wk-timeline-bar { display: flex; justify-content: space-between; align-items: center; gap: 8px; flex-wrap: wrap; margin: 2px 0 6px; }
/* Comments / activity segmented filter. */
.wk-actfilter { display: inline-flex; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.wk-actfilter__btn { font: inherit; font-size: 11.5px; font-weight: 600; color: var(--text-subtle); background: var(--bg-surface); border: 0; padding: 3px 10px; cursor: pointer; }
.wk-actfilter__btn + .wk-actfilter__btn { border-left: 1px solid var(--border); }
.wk-actfilter__btn:hover { background: var(--bg-app); }
.wk-actfilter__btn.is-on { background: var(--primary); color: var(--primary-ink, #fff); }
.wk-timeline-refresh { display: inline-flex; align-items: center; gap: 5px; font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: transparent; border: 1px solid var(--border); border-radius: 8px; padding: 3px 10px; cursor: pointer; }
.wk-timeline-refresh:hover { background: var(--bg-app); border-color: var(--border-strong); }
/* Nested replies indent per level but stop compounding past a few levels so deep chains stay readable. */
.wk-replies { margin-top: 8px; padding-left: 14px; border-left: 2px solid var(--border-muted); display: flex; flex-direction: column; gap: 10px; }
.wk-replies .wk-replies .wk-replies .wk-replies { padding-left: 8px; }
.wk-reply-box, .wk-comment-box { display: flex; gap: 8px; margin-top: 8px; }
.wk-reply-box textarea, .wk-comment-box textarea { flex: 1; font: inherit; font-size: 13px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); color: var(--text); resize: vertical; min-height: 38px; }
.wk-comment-box { position: sticky; bottom: 0; padding-top: 10px; background: var(--bg-surface); border-top: 1px solid var(--border-muted); }
.wk-drawer__meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; font-size: 11px; color: var(--text-faint); margin-top: 18px; padding-top: 12px; border-top: 1px solid var(--border-muted); }
.wk-delete { border: 1px solid rgba(185,28,28,.4); background: transparent; color: var(--danger); font: inherit; font-size: 12px; font-weight: 600; padding: 5px 10px; border-radius: 8px; cursor: pointer; }
.wk-delete:hover { background: rgba(185,28,28,.08); }

/* Issue type chips (Goal/Epic/Story/Task/Subtask) */
.wk-type { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; padding: 1px 6px; border-radius: 5px; flex: none; }
.wk-type--goal { background: rgba(202,138,4,.16); color: #a16207; }
.wk-type--epic { background: rgba(124,58,237,.16); color: #6d28d9; }
.wk-type--story { background: rgba(22,101,52,.14); color: #166534; }
.wk-type--task { background: rgba(30,64,175,.14); color: var(--primary); }
.wk-type--subtask { background: rgba(100,116,139,.16); color: #475569; }

/* Workflow state chips */
.wk-state { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 99px; }
.wk-state--todo { background: var(--bg-muted); color: var(--text-subtle); }
.wk-state--in_progress { background: rgba(30,64,175,.12); color: var(--primary); }
.wk-state--in_review { background: rgba(180,83,9,.14); color: var(--warn); }
.wk-state--done { background: rgba(22,101,52,.12); color: var(--success); }

/* Saved-view chips */
.wk-views { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 4px 0 14px; }
.wk-viewchip { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-subtle); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 99px; padding: 4px 11px; cursor: pointer; }
.wk-viewchip:hover { border-color: var(--border-strong); color: var(--text); }
.wk-viewchip.is-active { background: var(--accent-soft); color: var(--text); border-color: var(--border-strong); }
.wk-viewchip__x { opacity: .5; font-size: 10px; }
.wk-viewchip__x:hover { opacity: 1; }
.wk-viewchip--add { border-style: dashed; }

/* Board card head (type chip + title) */
.wk-tcard__head { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
.wk-tcard__head .wk-tcard__title { margin: 0; background: none; border: 0; text-align: left; cursor: pointer; padding: 0; color: var(--text); font: inherit; font-size: 13px; }

/* ── Compact layout refinements (single-line bars, minimal filters) ───────── */

/* The Kriya page header is hidden (the rail shows the module name), like the
   other rail modules — so no header styling is needed here. */
.wk-header-actions { display: inline-flex; align-items: center; gap: 8px; }

/* Toolbar rows: a context/actions row, then a filter row. Each splits into a
   left and right cluster (space-between) so the left edges (saved-views ↔ search)
   and right edges (view switcher/New ↔ filters) line up across both rows. */
.wk-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px 12px; flex-wrap: wrap; }
.wk-bar__left, .wk-bar__right { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
/* Tasks page header — the themeable gradient banner titled "Task Board", sharing the
   task-detail header's identity so the list page and a task page read as one module.
   Body-width (contained, rounded), NOT full-bleed — matches the task-detail page. */
.wk-board-header {
  /* Full-bleed band (edge to edge); its CONTENT is inset by the page gutter (--page-pad-x)
     so it lines up with the board content, which sits at the same gutter. Header + toolbar
     form one white chrome zone (the only divider is under the toolbar). */
  margin: calc(-1 * var(--page-pad-top, 0px)) calc(-1 * var(--page-pad-x, 0px)) 0;
  display: flex;
  align-items: center;
  min-height: 60px;
  padding: 0 var(--page-pad-x, 16px);
  /* Modern, minimal page-title treatment (Linear/Vercel/Stripe): near-white neutral
     surface + dark, high-contrast title. */
  background: var(--bg-surface);
  color: var(--text);
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* Drop the divider between the app header (topbar) and the module page header so they read as one
   continuous surface. GLOBAL: applies to Kriya's board header AND every module on the shared
   .app-shell — so the topbar merges into the header identically everywhere (no stray seam/line). */
.canvas:has(.wk-board-header) .topbar,
.canvas:has(.app-shell) .topbar { border-bottom-color: transparent; }
/* When #work-content flushes its top padding (wk-flush-top), the header must NOT keep its
   negative-top-margin — otherwise it over-pulls. Zeroing it makes the header sit flush at the
   top and the toolbar directly beneath, with no gap (the sticky toolbar + negative-margin
   cancel-dance was what left the ~20px gap on the async Timeline/Calendar/Workload views). */
#work-content.wk-flush-top > .wk-board-header { margin-top: 0; }
.wk-board-header__row { display: flex; align-items: center; gap: 9px; min-width: 0; }
.wk-board-header__icon { font-size: 14px; line-height: 1; opacity: .9; color: var(--text-subtle); }
.wk-board-header__title { margin: 0; font-size: 14px; font-weight: 650; letter-spacing: .01em; color: var(--text); white-space: nowrap; }
/* Breadcrumb trail in the page-title bar: org › [project] › Task Board. Earlier crumbs are
   muted context; the last crumb (an <h1>) is the current page / title. */
/* Breadcrumb stays on ONE row (never wraps → never grows the header height); the current
   crumb truncates with an ellipsis when space is tight. Shared by every module's header. */
.wk-bc { display: flex; align-items: center; gap: 7px; min-width: 0; flex-wrap: nowrap; overflow: hidden; }
.wk-bc__crumb { margin: 0; font-size: 14.5px; font-weight: 500; letter-spacing: .01em; line-height: 1.2; white-space: nowrap; }
.wk-bc__crumb--muted { color: var(--text-subtle); }
.wk-bc__crumb--link { border: 0; background: transparent; padding: 0; cursor: pointer; color: var(--text-subtle);
  font: inherit; font-size: 14.5px; /* `font:` is a shorthand — it wipes the crumb's size */ }
.wk-bc__crumb--link:hover { color: var(--primary, #1d4ed8); text-decoration: none; }
.wk-bc__crumb--current { font-weight: 650; color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* The separator tracks the crumbs (+15%) and takes a token — #cbd5e1 was a light-mode grey
   that sat at 6.1:1 on a dark header, louder than the crumbs it separates. */
.wk-bc__sep { color: var(--crumb-sep); font-size: 15px; user-select: none; }

/* ── Kriya Schedules (spec/product-management sub-module) ─────────────────────── */
.sch-view { display: flex; flex-direction: column; gap: 12px; }
.sch-header { background: var(--app-header-bg); color: var(--app-header-text); border-radius: 12px; padding: 12px 16px 13px; box-shadow: 0 6px 20px rgba(13,90,120,.18), inset 0 1px 0 rgba(255,255,255,.12); }
.sch-header__row { display: flex; align-items: center; gap: 10px; }
.sch-header__icon { font-size: 16px; opacity: .9; }
.sch-header__title { margin: 0; font-size: 19px; font-weight: 680; letter-spacing: .01em; color: var(--app-header-text); }
.sch-header__sub { margin: 4px 0 0 26px; font-size: 12px; color: var(--app-header-text); opacity: .82; }
.sch-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sch-field { display: inline-flex; flex-direction: column; gap: 3px; font-size: 11px; font-weight: 600; color: var(--text-subtle); }
.sch-select, .sch-input { font: inherit; font-size: 13px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.sch-shell { display: grid; grid-template-columns: 232px 1fr; gap: 14px; align-items: start; }
@media (max-width: 760px) { .sch-shell { grid-template-columns: 1fr; } }
.sch-sidelist { background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px; padding: 8px; }
.sch-sidelist__head { display: flex; align-items: center; justify-content: space-between; font-size: 12px; font-weight: 700; color: var(--text); padding: 2px 4px 8px; }
.sch-mini { font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: transparent; border: 0; cursor: pointer; }
.sch-newform { display: flex; flex-direction: column; gap: 6px; padding: 6px; margin-bottom: 8px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; }
.sch-newform__row { display: flex; gap: 6px; justify-content: flex-end; }
.sch-sidelist__items { display: flex; flex-direction: column; gap: 4px; }
.sch-schedule { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; text-align: left; padding: 7px 10px; border: 1px solid transparent; border-radius: 8px; background: transparent; cursor: pointer; }
.sch-schedule:hover { background: rgba(2,6,23,.04); }
.sch-schedule.is-active { background: rgba(29,78,216,.1); border-color: rgba(29,78,216,.22); }
.sch-schedule__name { font-size: 13px; font-weight: 600; color: var(--text); }
.sch-schedule__kind { font-size: 11px; color: var(--text-subtle); text-transform: capitalize; }
.sch-main { min-width: 0; }
.sch-grid-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.sch-grid-title { font-size: 15px; font-weight: 680; color: var(--text); }
.sch-count { font-size: 12px; font-weight: 500; color: var(--text-subtle); }
.sch-btn { font: inherit; font-size: 12.5px; font-weight: 600; padding: 6px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); cursor: pointer; }
.sch-btn--primary { background: var(--primary); border-color: var(--primary); color: var(--primary-ink, #fff); }
.sch-btn--danger { color: var(--danger, #dc2626); }
.sch-btn--danger:hover { background: rgba(220,38,38,.08); border-color: rgba(220,38,38,.4); }
/* THE SCHEDULE GRID reads like Kriya's task list — same surface, same 11px/700/uppercase muted
   head ink, same hairline separators, same row hover, NO zebra — at the grid's own density, because
   every cell here is an input and 12px of cell padding around a 7px control is 38px of row.
   It is deliberately NOT in the MODULE LIST TABLES block: that block's padding IS its contract
   (`ui/tableSkin.spec.js` asserts the value), and this is an editor rather than a list. The
   library list, which IS a list, is `.sch-ltable` and is in that block. */
.sch-tablewrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); }
.sch-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12.5px; background: var(--bg-surface); }
.sch-table th { position: sticky; top: 0; z-index: 3; text-align: left; font-size: 11px; font-weight: 700; color: var(--text-muted, #64748b); text-transform: uppercase; letter-spacing: .03em; padding: 9px 10px; background: var(--bg-surface); border-bottom: 1px solid var(--border); border-left: 1px solid var(--border-muted); white-space: nowrap; }
.sch-table thead th:first-child { border-left: 0; }
.sch-table td { padding: 3px 6px; border-bottom: 1px solid var(--border-muted, #eef1f5); border-left: 1px solid var(--border-muted); vertical-align: middle; }
.sch-table tbody td:first-child { border-left: 0; }
.sch-table tbody tr:last-child td { border-bottom: 0; }
.sch-table tbody tr:hover td { background: var(--bg-hover); }
/* An empty-state row spans the grid, so it must not be hovered or hairlined like data. */
.sch-table td:has(> .sch-blank) { background: none !important; border-left: 0; padding: 0; }
.sch-cell { font: inherit; font-size: 13px; width: 100%; min-width: 90px; padding: 5px 7px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); }
.sch-cell:hover { border-color: var(--border); }
.sch-cell:focus { border-color: var(--primary); background: var(--bg-app); outline: none; }
.sch-cell--sm { min-width: 80px; }
.sch-cell--num { min-width: 66px; text-align: right; }
.sch-cell-sub { font-size: 10.5px; color: var(--text-faint, #94a3b8); padding: 0 7px; }
/* spec_item custom-field grid cells — mirror .sch-cell so they blend into the table */
.sch-cf-th { color: var(--primary) !important; }
.sch-cf-cell { font: inherit; font-size: 13px; width: 100%; min-width: 90px; padding: 5px 7px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: var(--text); }
.sch-cf-cell:hover { border-color: var(--border); }
.sch-cf-cell:focus { border-color: var(--primary); background: var(--bg-app); outline: none; }
.sch-cf-cell--num { min-width: 66px; text-align: right; }
.sch-cf-cell[type="checkbox"] { width: auto; min-width: 0; }
.sch-status { min-width: 172px; font-weight: 600; }
.sch-status--approved { color: #166534; }
.sch-status--ordered, .sch-status--shipped { color: var(--primary); }
.sch-status--delivered, .sch-status--installed { color: #0f766e; }
.sch-status--rejected, .sch-status--cancelled, .sch-status--backordered { color: #b91c1c; }
.sch-x { font: inherit; color: var(--text-faint, #94a3b8); background: transparent; border: 0; cursor: pointer; padding: 4px 6px; border-radius: 6px; }
.sch-x:hover { color: #b91c1c; background: rgba(185,28,28,.08); }
.sch-empty { padding: 24px; text-align: center; color: var(--text-subtle); font-size: 13px; }
.sch-empty.small { padding: 14px; font-size: 12px; }
/* P1 — totals bar, thumbnails, actions, item drawer */
.sch-totals { display: flex; flex-wrap: wrap; gap: 8px 16px; padding: 8px 12px; margin-bottom: 8px; background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px; font-size: 12.5px; color: var(--text); }
.sch-total { display: inline-flex; align-items: baseline; gap: 6px; font-variant-numeric: tabular-nums; }
.sch-total b { font-weight: 700; }
/* A figure with no word on it is a number nobody can act on: the strip used to read
   "INR cost 9,000 · client 12,400 (2)". */
.sch-total__k { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-subtle); }
.sch-total__n { color: var(--text-subtle); font-size: 11.5px; }
.sch-namecell { display: flex; gap: 8px; align-items: flex-start; }
.sch-namecell__txt { flex: 1; min-width: 0; }
.sch-thumb { width: 34px; height: 34px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border); flex: none; margin-top: 3px; }
.sch-actions { white-space: nowrap; text-align: right; }
.sch-num { text-align: right; font-variant-numeric: tabular-nums; }
.sch-dash { color: var(--text-faint, #94a3b8); }
/* Visually hidden but read aloud — for a column head or a search box whose visible label is the
   placeholder. A `placeholder` is not an accessible name. */
.sch-vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
/* A row's own name, as the way into its record. A button, not a bare span with a click handler:
   it is focusable, it is announced as a control, and Enter works. */
.sch-linkbtn { font: inherit; font-size: inherit; font-weight: 600; color: var(--primary); background: none;
  border: 0; padding: 0; text-align: left; cursor: pointer; }
.sch-linkbtn:hover { text-decoration: underline; }
.sch-linkbtn[disabled] { color: var(--text); cursor: default; font-weight: 550; }
/* The catalogue product a library entry names, printed BESIDE the entry's own words rather than
   over them — the two are allowed to differ and seeing both is the point. Muted, because the row's
   own spec is what the reader came for; the link is a fact about it. */
.sch-catlink { font-size: 12px; color: var(--text-subtle); white-space: nowrap; }
.sch-clear { font: inherit; font-size: 12px; font-weight: 600; color: var(--primary); background: none;
  border: 0; padding: 2px 6px; border-radius: 6px; cursor: pointer; }
.sch-clear:hover { background: var(--accent-soft, rgba(29,78,216,.08)); }

/* ── WHAT · WHY · CTA — the app-wide empty state, in this module's surfaces (a grid body, a
   gallery, a list). The WHY is shown to everyone; the button only to somebody who can act. ── */
.sch-blank { display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 30px 24px; text-align: center; }
.sch-blank__what { font-size: 13.5px; font-weight: 650; color: var(--text); }
.sch-blank__why { max-width: 52ch; margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--text-subtle); }
.sch-blank .sch-btn { margin-top: 2px; }

/* ── The INDEX above the stack. Every schedule is shown stacked, so this is the map: one chip per
   register with its item count, plus collapse/expand all. ── */
.sch-index { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; padding: 7px 12px;
  background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px; }
.sch-index__lbl { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--text-subtle); flex: none; }
.sch-index__chips { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 auto; min-width: 0; }
.sch-index__all { flex: none; }
.sch-jump { display: inline-flex; align-items: center; gap: 6px; max-width: 22ch; font: inherit; font-size: 12px;
  font-weight: 600; color: var(--text); background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 999px; padding: 4px 6px 4px 11px; cursor: pointer; }
.sch-jump:hover { border-color: var(--primary); color: var(--primary); }
.sch-jump__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sch-jump__n { flex: none; min-width: 18px; font-size: 10.5px; font-weight: 700; text-align: center;
  color: var(--text-subtle); background: var(--app-filter-bg); border-radius: 999px; padding: 1px 6px; }
.sch-jump.is-collapsed { color: var(--text-subtle); border-style: dashed; }

/* The per-schedule fold. A 28px square so it clears the 24px minimum on a pointer, and it is the
   first thing in the header row so the disclosure sits where a disclosure sits. */
.sch-fold { flex: none; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px;
  font: inherit; font-size: 15px; line-height: 1; color: var(--text-muted, #64748b); background: transparent;
  border: 1px solid transparent; border-radius: 7px; cursor: pointer; }
.sch-fold:hover { background: var(--bg-app); color: var(--text); border-color: var(--border); }
/* Folding hides the register's own bars and grid and leaves its title row — the block is still
   visibly THERE, which is what tells a reader they folded it rather than lost it. */
.sch-block.is-collapsed > .sch-itembar,
.sch-block.is-collapsed > .sch-tile { display: none; }
.sch-block.is-collapsed > .sch-schedhead { border-bottom: 1px solid var(--border-muted); border-radius: 12px; }

/* ── EVERYTHING YOU CAN DO TO THE WHOLE REGISTER, as one labelled menu. Mirrors `.wk-viewsel__pop`,
   the app's own popover, so this is the same control rather than a fourth kind of menu. Absolute
   rather than fixed: the filter bar it hangs off sits in no overflow container, so the arithmetic
   `ui/menuPlacement.js` exists for buys nothing here. ── */
.sch-menu { position: relative; display: inline-flex; flex: none; }
.sch-menu__btn { font-size: 15px; letter-spacing: .5px; }
.sch-menu__pop { position: absolute; right: 0; top: calc(100% + 5px); z-index: 30; width: min(21rem, 78vw);
  max-height: 60vh; overflow-y: auto; padding: 5px; background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 30px rgba(0,0,0,.16); }
.sch-menu__pop[hidden] { display: none; }
.sch-menu__item { display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left; font: inherit;
  color: var(--text); background: none; border: 0; border-radius: 8px; padding: 7px 10px; cursor: pointer; }
.sch-menu__item:hover, .sch-menu__item:focus-visible { background: var(--bg-hover); }
.sch-menu__lbl { font-size: 12.5px; font-weight: 600; }
.sch-menu__why { font-size: 11px; line-height: 1.45; color: var(--text-subtle); }
.sch-menu__sep { height: 1px; background: var(--border); margin: 4px 6px; }
.sch-inline--q { gap: 0; }
/* The library list is `.sch-ltable` — the app-wide MODULE LIST TABLES skin, which owns the card
   edge itself — so its wrapper only scrolls. `.sch-tablewrap` draws a second border, which on a
   rounded card reads as a double outline. */
.sch-ltablewrap { overflow-x: auto; }
.sch-open { font: inherit; color: var(--text-subtle); background: transparent; border: 0; cursor: pointer; padding: 4px 6px; border-radius: 6px; }
.sch-open:hover { color: var(--primary); background: rgba(29,78,216,.08); }
.sch-drawer { position: fixed; inset: 0; z-index: 80; display: none; }
.sch-drawer.is-open { display: block; }
.sch-drawer__backdrop { position: absolute; inset: 0; background: rgba(2,6,23,.34); }
.sch-drawer__panel { position: absolute; top: 0; right: 0; height: 100%; width: min(460px, 94vw); background: var(--bg-surface); box-shadow: -12px 0 40px rgba(2,6,23,.22); display: flex; flex-direction: column; }
.sch-drawer__head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--border); }
.sch-drawer__title { flex: 1; min-width: 0; font: inherit; font-size: 16px; font-weight: 680; color: var(--text); border: 1px solid transparent; border-radius: 8px; padding: 5px 8px; background: transparent; }
.sch-drawer__title:hover { border-color: var(--border); }
.sch-drawer__title:focus { border-color: var(--primary); background: var(--bg-app); outline: none; }
.sch-drawer__body { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 16px; }
.sch-drawer__img { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.sch-drawer__img img { max-width: 100%; max-height: 220px; border-radius: 10px; border: 1px solid var(--border); }
.sch-drawer__noimg { width: 100%; height: 120px; display: flex; align-items: center; justify-content: center; color: var(--text-faint, #94a3b8); background: var(--app-filter-bg); border: 1px dashed var(--border); border-radius: 10px; font-size: 12px; }
.sch-drawer__imgrow { display: flex; gap: 8px; }
.sch-imgbtn { position: relative; overflow: hidden; cursor: pointer; }
.sch-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.sch-fld { display: flex; flex-direction: column; gap: 3px; font-size: 11px; font-weight: 600; color: var(--text-subtle); }
.sch-fld--wide { grid-column: 1 / -1; }
.sch-fld input, .sch-fld textarea, .sch-fld select { font: inherit; font-size: 13px; font-weight: 400; color: var(--text); padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); resize: vertical; }
.sch-fld input[type="checkbox"] { width: auto; align-self: flex-start; }
.sch-vendor { border-top: 1px dashed var(--border); margin-top: 12px; padding-top: 10px; }
.sch-vendor__head { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 6px; }
.sch-vendor__cur { font-size: 13px; color: var(--text); margin-bottom: 6px; }
.sch-vendor__cur code { background: var(--app-filter-bg); padding: 1px 6px; border-radius: 5px; font-size: 12px; }
.sch-vendor__acts { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.sch-vendor__acts [data-sch-vendor-search] { flex: 1 1 160px; max-width: none; }
.sch-vendor__list { display: flex; flex-direction: column; gap: 5px; margin-top: 8px; }
.sch-vendor__opt { text-align: left; justify-content: flex-start; }
.sch-comments__head { font-size: 12px; font-weight: 700; color: var(--text); margin-bottom: 8px; }
.sch-comments__list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.sch-comment { background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 8px; padding: 7px 10px; }
.sch-comment--sys { background: transparent; border-style: dashed; }
.sch-comment__body { font-size: 13px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
.sch-comment__meta { font-size: 10.5px; color: var(--text-subtle); margin-top: 3px; }
.sch-comments__new { display: flex; flex-direction: column; gap: 6px; }
.sch-comments__new textarea { resize: vertical; }
/* P2 — product library, add-from-library picker. (Section nav moved to the second
   rail; the old .sch-tabs strip was removed.) */
.sch-headbtns { display: inline-flex; gap: 8px; }
.sch-libq { min-width: 240px; }
.sch-savelib { font-size: 12px; padding: 5px 10px; }
/* Add-from-library picker — rich item rows (thumb + name/category + brand/SKU + spec chips) */
.sch-drawer__panel--wide { width: min(560px, 96vw); }
.sch-pick { display: flex; flex-direction: row; align-items: flex-start; gap: 10px; width: 100%; text-align: left; padding: 9px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); cursor: pointer; margin-bottom: 7px; }
.sch-pick:hover { border-color: var(--primary); background: rgba(29,78,216,.05); }
.sch-pick__thumb { flex: none; width: 44px; height: 44px; border-radius: 8px; object-fit: cover; background: var(--bg-muted, #f1f5f9); border: 1px solid var(--border-muted, #eef1f5); }
.sch-pick__thumb--none { display: flex; align-items: center; justify-content: center; font-size: 18px; opacity: .5; }
.sch-pick__main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.sch-pick__top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.sch-pick__name { font-size: 13.5px; font-weight: 650; color: var(--text); }
.sch-pick__cat { font-size: 10.5px; font-weight: 600; text-transform: capitalize; color: var(--text-muted); background: var(--bg-muted, #f1f5f9); border-radius: 999px; padding: 1px 8px; }
.sch-pick__meta { font-size: 11.5px; color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sch-pick__meta--spec { color: var(--text-faint); }
.sch-pick__chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 2px; }
.sch-pick__chip { font-size: 10.5px; color: var(--text-muted); background: var(--bg-muted, #f1f5f9); border: 1px solid var(--border-muted, #eef1f5); border-radius: 6px; padding: 1px 7px; white-space: nowrap; }
.sch-pick__chip b { font-weight: 650; color: var(--text-subtle); }
.sch-pick__chip--cost { color: #0f766e; background: rgba(13,148,136,.08); border-color: rgba(13,148,136,.2); font-weight: 650; }
.sch-pick__add { flex: none; align-self: center; font-size: 16px; font-weight: 700; color: var(--text-faint); }
.sch-pick:hover .sch-pick__add { color: var(--primary); }
/* Customize drawer — section headers + always-visible inline notes (hover tooltips get clipped by
   the body-level drawer's scroll, so descriptions are shown inline instead). */
.sch-cz-sec { font-weight: 700; font-size: 13px; color: var(--text); margin: 16px 0 6px; padding-top: 14px; border-top: 1px solid var(--border); }
.sch-cz-sec:first-of-type { border-top: none; padding-top: 4px; margin-top: 6px; }
.sch-cz-note { color: var(--text-subtle); font-size: 12px; line-height: 1.45; margin: 0 0 8px; }
.sch-cz-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
/* Status stages — structured [Party] [Status] [✕] rows */
.sch-cz-stages { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.sch-cz-stage { display: grid; grid-template-columns: 140px 1fr 26px; gap: 6px; align-items: center; }
.sch-cz-stage__party, .sch-cz-stage__label { width: 100%; min-width: 0; }
.sch-cz-stage--head { font-size: 10px; font-weight: 700; color: var(--text-subtle); text-transform: uppercase; letter-spacing: .04em; padding: 0 2px 2px; }
.sch-cz-stage .sch-linkx { justify-self: center; }
.sch-cz-addstage { align-self: flex-start; }
/* P3 — client-portal share manager */
.sch-mut { color: var(--text-subtle); font-size: 12px; margin: 0 0 12px; }
.sch-check { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text); margin: 8px 0; }
.sch-share-new { display: flex; flex-direction: column; gap: 8px; padding: 12px; background: var(--app-filter-bg); border: 1px solid var(--app-filter-border); border-radius: 10px; margin-bottom: 14px; }
.sch-share-list { display: flex; flex-direction: column; gap: 8px; }
.sch-sharerow { border: 1px solid var(--border); border-radius: 10px; padding: 10px; }
.sch-sharerow.is-revoked { opacity: .6; }
.sch-sharerow__top { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 6px; }
.sch-sharebadge { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--primary); background: rgba(29,78,216,.1); border-radius: 999px; padding: 2px 8px; }
.sch-sharebadge--off { color: #b91c1c; background: rgba(185,28,28,.1); }
.sch-sharelink { display: flex; gap: 6px; margin-bottom: 6px; }
.sch-sharelink input { flex: 1; font: inherit; font-size: 12px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); color: var(--text); }
.sch-linkx { font: inherit; font-size: 12px; font-weight: 600; color: #b91c1c; background: transparent; border: 1px solid rgba(185,28,28,.3); border-radius: 8px; padding: 4px 10px; cursor: pointer; }

/* ── Schedules redesign: filter-tab bar (schedules as tabs + New), then the active schedule as a
   Kriya-task-detail-style box (gradient title bar + light filter bar + tile grid). Reuses the app-wide
   .app-detail-header / .app-filterbar / .app-tile chrome; these are the schedule-specific bits. ── */
.sch-filterbar .app-filterbar__left { flex: 1 1 auto; }
.sch-inline { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; color: var(--text-muted); }
.sch-inline .sch-select { font-weight: 500; }
.sch-select--sm, .sch-input--sm { font-size: 12px; padding: 4px 8px; }
.sch-input--sm { min-width: 120px; max-width: 200px; }
.sch-schedtabs { flex-wrap: wrap; }
.sch-schedtab { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
/* All schedules are shown stacked (one grid below another) — space the blocks apart. */
.sch-stack { display: flex; flex-direction: column; gap: 22px; }
/* NO GAP: the head (`border-radius: 12px 12px 0 0`, no bottom border), the item bar (no top
   border, square top corners) and the tile (`margin-top: -1px`) are three pieces written to join
   into ONE card, and a 12px flex gap between them defeated all three — every schedule read as
   three loose bands with hairlines floating between them. The gap between BLOCKS is `.sch-stack`'s. */
.sch-block { display: flex; flex-direction: column; gap: 0; }
.sch-newbtn { display: inline-flex; align-items: center; gap: 5px; }
.sch-newbtn .hicon { width: 1em; height: 1em; }
/* The schedule's title bar reads as a NORMAL box header (light, top-lit) — not the page-header
   gradient — so it sits quietly above its own filter bar + grid. Override the app-detail-header skin. */
.sch-schedhead { background: linear-gradient(180deg, var(--bg-surface), var(--bg-app)); color: var(--text);
  border: 1px solid var(--border-muted); border-bottom: 0; box-shadow: none;
  border-radius: 12px 12px 0 0; padding: 9px 14px; margin-bottom: 0; }
.sch-schedhead .app-detail-header__row { flex-wrap: wrap; }
.sch-schedhead .app-detail-header__title { color: var(--text); font-size: 16px; font-weight: 700; }
.sch-kind-badge { flex: none; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--text-subtle); background: var(--bg-app); border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px; }
.sch-hcount { flex: none; font-size: 12px; color: var(--text-subtle); }
.sch-hinfo { flex: none; margin-left: auto; }
.sch-schedhead .ws-tip-i { color: var(--text-subtle); opacity: .7; }
.sch-hbtn { flex: none; display: inline-flex; align-items: center; justify-content: center; min-width: 30px; height: 28px;
  font: inherit; font-size: 13px; color: var(--text-muted); background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 8px; cursor: pointer; padding: 0 8px; }
.sch-hbtn:hover { background: var(--bg-app); color: var(--text); border-color: var(--border-strong); }
.sch-hbtn[disabled] { opacity: .4; cursor: default; }
.sch-hbtn--danger:hover { color: var(--danger, #dc2626); background: rgba(220,38,38,.08); border-color: rgba(220,38,38,.4); }
/* The per-schedule filter/action bar sits flush under the gradient head. */
.sch-itembar { border-top-left-radius: 0; border-top-right-radius: 0; margin-bottom: 0;
  border-top: 0; box-shadow: 0 1px 2px rgba(16,24,40,.04); }
.sch-itembar .app-filterbar__right { gap: 6px; }
.sch-tile { border-top-left-radius: 0; border-top-right-radius: 0; margin-top: -1px; margin-bottom: 0; }
.sch-tile .app-tile__body { padding: 10px 12px; }
/* Reorder controls (whole schedule ← →, item ↑ ↓). */
.sch-th-move { width: 34px; }
.sch-movecell { white-space: nowrap; text-align: center; width: 34px; padding-top: 6px !important; }
.sch-movebtn { border: 0; background: transparent; color: var(--text-faint, #94a3b8); cursor: pointer; font-size: 11px; line-height: 1; padding: 1px 2px; border-radius: 4px; }
.sch-movebtn:hover { background: var(--bg-app); color: var(--text); }
.sch-movebtn[disabled] { opacity: .3; cursor: default; }
/* Create / edit drawer fields. */
.sch-drawer__foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--border); }
.sch-fld > span { display: inline-flex; align-items: center; gap: 4px; }
.sch-req { color: #dc2626; font-style: normal; }
.sch-hint { font-size: 11.5px; color: var(--text-subtle); margin: 4px 0 0; }

/* Flush, full-bleed leads-style toolbar for the Kriya board — the head row and the filter
   row are one connected tinted bar (head has no divider; the filter row carries it). */
.wk-bar--head { margin: calc(-1 * var(--page-pad-top, 0px)) calc(-1 * var(--page-pad-x, 0px)) 0; padding: .42rem var(--page-pad-x, .7rem);
  border: 0; border-radius: 0; background: rgba(37, 99, 235, .05); }
.wk-filters { margin: 0 calc(-1 * var(--page-pad-x, 0px)) 14px; padding: .42rem var(--page-pad-x, .7rem);
  border: 0; border-bottom: 1px solid var(--border); border-radius: 0; background: rgba(37, 99, 235, .05); }
/* The merged single-row Tasks toolbar: full-bleed + flush under the page header (was two bars). */
.wk-bar--tasks, .wk-dash-filters { margin-top: calc(-1 * var(--page-pad-top, 0px)); flex-wrap: nowrap; }
.wk-bar--tasks .wk-bar__right, .wk-dash-filters .wk-bar__right { margin-left: auto; }
@media (max-width: 900px) { .wk-bar--tasks, .wk-dash-filters { flex-wrap: wrap; } }
.wk-views { margin: 0; }

/* Saved-views dropdown (label = active view; popover lists All + saved views). */
.wk-viewsel { position: relative; display: inline-flex; }
.wk-viewsel__btn {
  display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: 0.8rem; font-weight: 500;
  color: var(--text); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px;
  padding: 0 10px; cursor: pointer; max-width: 220px;
}
.wk-viewsel__btn:hover { background: var(--bg-hover); border-color: var(--border-strong); color: var(--text); }
.wk-viewsel__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-viewsel__chev { color: var(--text-faint); font-size: 10px; line-height: 1; }
.wk-viewsel__pop {
  position: absolute; left: 0; top: calc(100% + 4px); z-index: 30; min-width: 220px; max-height: 320px; overflow-y: auto;
  padding: 4px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: 0 12px 30px rgba(0,0,0,.16);
}
.wk-viewsel__item {
  display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; text-align: left;
  font: inherit; font-size: 12px; color: var(--text); background: none; border: 0; border-radius: 7px;
  padding: 6px 10px; cursor: pointer;
}
.wk-viewsel__sep { height: 1px; background: var(--border); margin: 4px 6px; }
.wk-bar__right .wk-viewsel__pop { left: auto; right: 0; }
.wk-viewsel__item:hover { background: var(--bg-hover); }
.wk-viewsel__item.is-active { background: var(--accent-soft); font-weight: 600; }
.wk-viewsel__name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-viewsel__star { flex: none; font-size: 13px; line-height: 1; color: var(--text-faint, #94a3b8); cursor: pointer; padding: 0 1px; }
.wk-viewsel__star:hover { color: var(--warning, #d97706); }
.wk-viewsel__star.is-on { color: var(--warning, #f59e0b); }
.wk-viewsel__x { flex: none; opacity: .5; font-size: 11px; padding: 0 2px; }
.wk-viewsel__x:hover { opacity: 1; color: var(--danger); }

/* Unified-board filter bar: search · my-tasks · organize · sort · filters popover */
.wk-search { font: inherit; font-size: 12px; padding: 5px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); min-width: 180px; flex: 1 1 200px; max-width: 320px; }
.wk-inline { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-subtle); }
.wk-inline select { font: inherit; font-size: 12px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
/* Toggle button (My tasks / Show completed) — clear on/off, no checkbox, no wasted space. */
.wk-toggle-btn { font: inherit; font-size: 12px; font-weight: 600; color: var(--text-subtle); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 5px 12px; cursor: pointer; white-space: nowrap; transition: background .12s, border-color .12s, color .12s; }
.wk-toggle-btn:hover { background: var(--bg-hover); border-color: var(--border-strong); color: var(--text); }
.wk-toggle-btn.is-on { background: rgba(99, 102, 241, 0.14); border-color: var(--primary); color: var(--primary); }
.wk-fp__donebtn { align-self: flex-start; }
.wk-toggle[hidden] { display: none; }

.wk-fp { position: relative; display: inline-flex; }
.wk-fp__badge { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 4px; border-radius: 99px; background: #64748b; color: #fff; font-size: 10px; font-weight: 700; }
.wk-fp__pop { position: absolute; left: 0; top: calc(100% + 6px); z-index: 30; width: 252px; padding: 10px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 12px 30px rgba(0,0,0,.16); display: flex; flex-direction: column; gap: 8px; }
.wk-fp__pop[hidden] { display: none !important; }
.wk-fp__row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--text-subtle); white-space: nowrap; }
.wk-fp__row select { font: inherit; font-size: 12px; padding: 4px 6px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-app); color: var(--text); max-width: 150px; }
.wk-fp__sep { height: 1px; background: var(--border); margin: 2px 0; }
.wk-fp__check { justify-content: flex-start; gap: 4px; color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; white-space: nowrap; }
.wk-fp__clear { align-self: flex-start; margin-top: 2px; }

/* Icon-only toolbar buttons: square, name shown via the native title tooltip (UI Polish standard). */
/* 30×30 button + 16px glyph — the ONE app-wide icon-button size (module header + toolbars), so
   every module reads identically AND matches the top application header's 30×30 / 16px
   .topbar-iconbtn. Single global standard — no module may re-size its icons. */
.wk-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0; flex: none;
  color: var(--text-subtle); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s; position: relative; }
.wk-iconbtn:hover { background: var(--bg-hover); border-color: var(--border-strong); color: var(--text); }
.wk-iconbtn .hicon { width: 16px; height: 16px; }
.wk-iconbtn.is-on { background: rgba(99, 102, 241, 0.14); border-color: var(--primary); color: var(--primary); }
/* Icon buttons that carry a tiny caption under the glyph (Kriya header + toolbar) stack
   vertically and grow into small chips; caption-less buttons keep the 30×30 standard, so
   other modules are untouched. Same treatment for Kriya's segmented view/entity toggles. */
.wk-iconbtn:has(> .wk-iconbtn__cap),
.wk-toggle button:has(> .wk-iconbtn__cap) { flex-direction: column; gap: 1px; width: auto; min-width: 34px; height: auto; padding: 3px 5px 2px; }
/* View/entity switcher segments: uniform, wider than the plain 34px icon buttons so the
   captions sit clearly; the view switcher slightly wider than the entity one. */
.wk-toggle--entity button:has(> .wk-iconbtn__cap) { min-width: 41px; padding: 3px 7px 2px; }
.wk-toggle--views button:has(> .wk-iconbtn__cap) { min-width: 47px; padding: 3px 9px 2px; }
.wk-iconbtn__cap { font-size: 8px; line-height: 1.1; font-weight: 600; letter-spacing: .02em; white-space: nowrap; color: var(--text-subtle); }
.wk-iconbtn:hover .wk-iconbtn__cap,
.wk-iconbtn.is-on .wk-iconbtn__cap,
.wk-iconbtn--primary .wk-iconbtn__cap,
.wk-toggle button .wk-iconbtn__cap { color: inherit; }
/* Calendar ‹ ◉ › nav glyphs are text (15px) — keep their captions at caption size. */
.wk-calnav .wk-iconbtn .wk-iconbtn__cap { font-size: 8px; font-weight: 600; }
/* Import button is a <label> (wraps a hidden file input) — keep it aligned in the action row */
.wk-import-btn { vertical-align: middle; cursor: pointer; }
.wk-import-btn input[type="file"] { display: none; }

/* Databases (collections) section — card grid + create panel */
.wk-db { display: flex; flex-direction: column; gap: 14px; padding: 4px 2px 16px; max-width: 920px; }
.wk-db-head { display: flex; align-items: baseline; gap: 10px; }
.wk-db-title { margin: 0; font-size: 16px; font-weight: 700; }
.wk-db-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.wk-db-card { display: flex; align-items: stretch; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); overflow: hidden; transition: border-color .12s, box-shadow .12s; }
.wk-db-card:hover { border-color: var(--primary); box-shadow: 0 2px 10px rgba(16,24,40,.08); }
.wk-db-card__open { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; align-items: flex-start; text-align: left; background: none; border: 0; padding: 11px 14px; cursor: pointer; font: inherit; }
.wk-db-card__ico { font-size: 17px; }
.wk-db-card__name { font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.wk-db-card__desc { font-size: 12px; color: var(--text-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.wk-db-card__del { flex: none; width: 38px; border: 0; border-left: 1px solid var(--border); background: none; color: var(--text-faint, #94a3b8); cursor: pointer; font-size: 14px; }
.wk-db-card__del:hover { background: var(--danger-soft, rgba(220,38,38,.08)); color: var(--danger, #dc2626); }
.wk-db-empty { color: var(--text-subtle); font-size: 13px; padding: 18px; border: 1px dashed var(--border); border-radius: 10px; text-align: center; }
.wk-db-create { display: flex; flex-direction: column; gap: 8px; border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--app-filter-bg); max-width: 480px; }
.wk-db-create__head { font-size: 13px; font-weight: 700; color: var(--text); }
.wk-db-input { font: inherit; font-size: 13px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.wk-db-input:focus { border-color: var(--primary); outline: none; }
.wk-db-create .primary-button { align-self: flex-start; }
/* Wiki rail in Databases mode — a simple list of database links */
.wiki-rail__dblist { list-style: none; margin: 0; padding: 6px; display: flex; flex-direction: column; gap: 2px; }
.wiki-rail__db { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; background: none; border: 0; padding: 7px 10px; border-radius: 7px; font: inherit; font-size: 13px; color: var(--text); cursor: pointer; }
.wiki-rail__db:hover { background: var(--bg-hover, #f1f5f9); }
.wiki-rail__db.is-active { background: var(--primary); color: var(--primary-ink, #fff); }
.wk-iconbtn--primary { color: var(--primary-ink, #fff); background: var(--primary); border-color: var(--primary); }
.wk-iconbtn--primary:hover { color: var(--primary-ink, #fff); filter: brightness(1.06); background: var(--primary); }
/* THE CAPTION TAKES THE BUTTON'S OWN INK ON A FILLED BUTTON (2026-08-24, user report: the text
   on "+ Estimate" / "+ Invoice" was invisible).

   `.wk-iconbtn__cap` sets `color: var(--text-subtle)` for every caption — right on the plain
   grey icon button it was written for, and unreadable on the PRIMARY variant, where that grey
   lands on a saturated `--primary` fill. There was already a fix for this, but scoped to
   `:is(.app-shell__actions, .wk-hdr-actions)` — the first-rail actions cluster — so it never
   reached a module that paints its own toolbar. Artha renders these into `.artha-bar-actions`
   (`chrome.js` turns any `+ X` label into an icon button with a cap), which is why exactly those
   two buttons showed the defect.

   `inherit`, not a literal: the button above already resolves `--primary-ink`, which is #fff on
   the light theme and near-black on dark, where `--primary` is a LIGHT blue. The rail cluster's
   older copy of this hardcodes `#fff !important`, which is why it is not the rule being widened.
   (0,2,0) beats the (0,1,0) caption rule and is declared after it; written longhand with no
   `:is()`, since WebKit prices that at zero specificity. */
.wk-iconbtn--primary .wk-iconbtn__cap { color: inherit; -webkit-text-fill-color: currentColor; }
/* Filters badge sits on the corner of the icon button now that it has no text. */
.wk-fp .wk-iconbtn .wk-fp__badge { position: absolute; top: -5px; right: -5px; }
/* Collapsed: the Filters button is filled with the DEFAULT filter colour (soft neutral grey
   #f2f4f7 / #dfe3ea), full border incl. the bottom. Expanded: it DARKENS to the connector
   tone (#e5e8ee / #c6ccd7) to match the v caret and the expanded filter panel. */
.wk-fp .wk-iconbtn { background: var(--bg-muted); border-color: var(--border-muted); color: var(--text-muted); }
.wk-fp .wk-iconbtn:hover { background: #e9edf2; border-color: #cfd5df; color: #334155; }
.wk-fp .wk-iconbtn.is-on { background: #e5e8ee; border-color: #c6ccd7; color: #334155; }
.wk-fp .wk-iconbtn.is-on:hover { background: #dde1e9; border-color: #b9c0cc; color: #334155; }
/* A small downward "v" caret below the Filters button. It is ALWAYS filled with the default
   filter colour (the light-green filter-bar tone) — even when the filter isn't selected — and
   darkens to the connector tone when the bar is expanded. Open at the top; height is 7px so the
   point sits 1px lower. */
.wk-fp::after {
  content: ''; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  width: 22px; height: 7px;
  background: no-repeat center / contain;
  /* Below the sticky page header (z 25) + app header (z 30) so the caret tucks BEHIND
     them as the toolbar scrolls up — it must never poke through the pinned headers.
     Still above the static toolbar/panel content (z 0), so the connector reads cleanly. */
  pointer-events: none; z-index: 5;
  /* Collapsed: filled with the default filter colour (#f2f4f7), border #dfe3ea. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23f2f4f7' stroke='%23dfe3ea' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.wk-fp.is-open::after {
  /* Expanded: darker connector tone (#e5e8ee / #c6ccd7). */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23e5e8ee' stroke='%23c6ccd7' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
/* The connector is a data-URI, and a data-URI cannot read a CSS variable — so it is
   the one part of the filter surface that needs stating twice. Without this the
   caret stayed a light chevron poking out of a dark filter band. Values match
   --bg-muted / --border-muted and --bg-hover / --border in dark. */
html[data-theme='dark'] .wk-fp::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%2335363a' stroke='%233c4043' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
html[data-theme='dark'] .wk-fp.is-open::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%233c4043' stroke='%235f6368' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ── App-wide icon button (UI Polish standard) ──────────────────────────────────
   Reusable across every module's toolbars (the module-agnostic sibling of .wk-iconbtn):
   square, quiet surface, name shown via the INSTANT data-rail-tip tooltip (never native
   `title`). Utility actions become .app-iconbtn; the primary create action keeps its label
   as a .primary-button. Icons paint via data-hicon (see handIcons.js observeHandIcons). */
.app-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; padding: 0;
  flex: none; color: var(--text-subtle); background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px;
  cursor: pointer; transition: background .12s, border-color .12s, color .12s; position: relative; }
.app-iconbtn:hover { background: var(--bg-hover); border-color: var(--border-strong); color: var(--text); }
.app-iconbtn .hicon { width: 16px; height: 16px; }
.app-iconbtn.is-on, .app-iconbtn.is-active { background: rgba(37, 99, 235, .12); border-color: var(--primary); color: var(--primary); }
.app-iconbtn--primary { color: var(--primary-ink, #fff); background: var(--primary); border-color: var(--primary); }
.app-iconbtn--primary:hover { color: var(--primary-ink, #fff); filter: brightness(1.06); background: var(--primary); }
.app-iconbtn[disabled] { opacity: .5; cursor: default; }

/* Instant tooltip for any icon button carrying data-rail-tip — shows immediately on hover
   (the native `title` is delayed ~1s). Drops below the control; z-index tops the toolbars. */
.app-iconbtn[data-rail-tip] { position: relative; }
.app-iconbtn[data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px; font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18); opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000; }
.app-iconbtn[data-rail-tip]:hover::after, .app-iconbtn[data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }
/* Toolbar-right icon buttons: right-align the tip so it can't clip off the page edge. */
.app-page-actions > .app-iconbtn:last-child[data-rail-tip]::after { left: auto; right: 0; transform: none; }

/* Instant tooltip for the toolbar icon buttons (data-rail-tip) — same mechanism as the
   collapsed nav rail: replaces the native `title` the browser delays ~1s, shows on hover
   with no delay, dropping below the icon. Keep aria-label for screen readers. */
.wk-bar [data-rail-tip] { position: relative; }
.wk-bar [data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000;
}
.wk-bar [data-rail-tip]:hover::after,
.wk-bar [data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }
/* The far-right primary (New task) sits near the content edge — right-align its tip so it
   never clips off-screen. */
.wk-bar__right > [data-rail-tip]:last-child::after { left: auto; right: 0; transform: none; }
/* Same instant tooltip for the header icon actions (right-aligned so they never clip). GLOBAL for
   both the shell header (.app-shell__actions) and Kriya's board header (.wk-hdr-actions). */
.app-shell__actions [data-rail-tip]::after,
.wk-hdr-actions [data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; top: calc(100% + 6px); left: auto; right: 0;
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000;
}
.app-shell__actions [data-rail-tip]:hover::after,
.app-shell__actions [data-rail-tip]:focus-visible::after,
.wk-hdr-actions [data-rail-tip]:hover::after,
.wk-hdr-actions [data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }

/* Collapsible search: an icon that expands to reveal the field.
   (It does NOT collapse on blur — this comment claimed it did for a long time and no such handler
   has ever existed in `ui/listKit.js`. The icon is the only way back, which is why it stays
   clickable when open rather than being hidden with its caption.) */
.wk-search-collapse { display: inline-flex; align-items: center; gap: 0; }
/* THE CAPTION GOES WHEN THE FIELD ARRIVES (2026-09-26, user report with a screenshot of the party
   toolbar: *"when i expand search functionality, it is showing 2 times"*).

   Row 2's tools are each "an icon over a one-word caption", and that caption is what labels a TOOL.
   Once this one has expanded, the field labels itself — `Search trade partners` sits right beside
   it — so the caption was the word "Search" printed twice, 40px apart.

   THE BUTTON ITSELF STAYS, and that is not timidity: it is the only control that closes the field
   again, because the blur-collapse the old comment above promised was never built. Hiding it would
   trade a duplicated word for a search box nobody can put away. The icon keeps `aria-label="Search"`,
   so nothing is lost to a screen reader either. */
.wk-search-collapse.is-open .wk-iconbtn__cap { display: none; }
/* Collapsed = fully REMOVED from layout via display:none. A zero width/max-width can't shrink a form
   control below its intrinsic min-width, so the "collapsed" field still reserved ~35px and left a
   stray gap before the next toolbar icon (My tasks). Toggling display sidesteps that entirely. */
.wk-search-collapse .wk-search { width: 220px; min-width: 160px; max-width: 260px; margin-left: 6px; }
/* Collapsed = hidden. `:not(.is-open)` raises specificity to (0,3,0) so it beats
   the generic `.app-shell__bar .wk-search` display rule (0,2,0) — otherwise the
   equal-specificity, later-in-file bar rule kept the field always visible. */
.wk-search-collapse:not(.is-open) .wk-search { display: none; }
.wk-search-collapse.is-open .wk-search { display: block; }
@media (max-width: 900px) {
  .wk-search-collapse .wk-search { width: 200px; min-width: 0; max-width: 200px; }
  .wk-search-collapse:not(.is-open) .wk-search { display: block; }
}

/* Hierarchy tree (Goal>Epic>Story>Task>Subtask) with expand/collapse. */
.wk-tree { border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); overflow: hidden; }
.wk-tnode { display: flex; align-items: center; gap: 8px; padding: 7px 12px; padding-left: calc(12px + var(--depth, 0) * 22px); border-bottom: 1px solid var(--border-muted); }
.wk-tnode:last-child { border-bottom: 0; }
.wk-tnode:hover { background: var(--bg-hover); }
.wk-tnode .wk-task__title { flex: 1; min-width: 0; background: none; border: 0; text-align: left; cursor: pointer; color: var(--text); font: inherit; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-tnode .wk-task__title.is-done { color: var(--text-faint); text-decoration: line-through; }
.wk-tnode__chev { width: 18px; height: 18px; flex: none; border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 11px; transition: transform .12s; padding: 0; }
.wk-tnode__chev.is-open { transform: rotate(90deg); }
.wk-tnode__chev--leaf { cursor: default; visibility: hidden; }

/* Compact the top of the Work tab — no wasted space above the toolbar.
   The toolbar buttons are .ghost-buttons, which carry margin-top:12px by
   default — zero it inside the bars so the toolbar sits flush to the top. */
/* Kriya scroll container: ZERO top padding so the STICKY tasks toolbar pins flush to the
   topbar — a sticky bar is offset by any top padding on its scroll container, which shows as
   a gap. This needs (0,2,0) specificity to beat `.canvas-body :is(… .kriya-main …)` above.
   The 20px top breathing room is moved onto #work-content for the non-tasks sections; the
   tasks view flushes it (.wk-flush-top, set by the controller) so the toolbar sits at y=0. */
/* Full-width scroll container (scrollbar at the page's far-right edge) with a roomier
   inner gutter for content. The side padding sits between the content and the scrollbar,
   so widening it adds breathing room without moving the scrollbar off the edge. Bump
   --page-pad-x too so the full-bleed sticky toolbar still reaches the edges. */
/* Responsive side gutter (industry standard): scales with the viewport — ~24px on narrow
   screens, growing to a 64px cap on wide ones — instead of a fixed value that cramps small
   windows and never adapts. Both the board content and the full-bleed bars use it. */
/* ONE GUTTER, AND THE TAB OWNS IT (2026-09-15, user report: "content of body also should fit
   without any unnecessary margins left and right side").
   This element used to add its OWN responsive inline padding on top of the tab's 32px, so
   ordinary content sat at 56 while the full-bleed toolbar — which bleeds by this element's
   `--page-pad-x` — landed back at 32. Measured in Chrome: breadcrumb 32, toolbar 32, table 56.
   Three edges on one page. The gutter is the tab's (the rule that says so is ~400 lines down and
   states "everything inside carries no padding of its own"); this one just stopped obeying it.
   `--page-pad-x: 0` is what makes the toolbar stop bleeding as well — the bleed exists to cancel
   a gutter, and there is no longer one here to cancel. */
.canvas-body .kriya-main { padding-top: 0; max-width: none; margin-inline: 0; --page-pad-x: 0px; padding-inline: 0; }
#work-content { margin-top: 0; padding-top: var(--page-pad-top, 20px); }
#work-content.wk-flush-top { padding-top: 0; }
#kriya-tab .wk-bar { margin-top: 0; }
/* The merged Tasks bar is sticky + full-bleed, flush at the very top (no negative margin
   needed now the container has no top padding). The page scroll stays on .kriya-main
   (scrollbar at the far-right edge); only the board/list scrolls beneath. Opaque background
   (tint composited over the canvas bg) so rows never show through. */
/* ── A page that carries its own list toolbar ─────────────────────────────────────
   `.smz-has-listbar` is set by `lkWire` on the tab whose page rendered the shared list
   toolbar, and cleared by `renderShell` on every view switch. It makes ANY module's page
   read like Kriya's list — two bands, not three outlined strips:

     · the header drops its divider, because the toolbar below carries the only hairline
       (a page with no list toolbar keeps its divider — there it separates chrome from
       content, which is what it is for);
     · the shell host drops the page gutter it adds for modules whose body is inset by
       the same amount. Where the body ISN'T (its `--page-pad-x` resolves to 0) that
       gutter applied twice, leaving the breadcrumb sitting a gutter right of the toolbar
       beneath it. Artha, Hive and Prism all had that; so did Orbit.

   Generic on purpose: every module that adopts the shared toolbar gets this, including
   ones added later, with nothing to remember. */
/* IT TARGETED AN ELEMENT THAT DOES NOT EXIST (2026-09-15, user report: "line after bread crumb
   not in original reference"). `ui/appShell.js` emits `.app-shell__bar` — there is no
   `.app-shell__header` in its markup, and there never was — so this rule matched nothing and the
   hand-off it describes never happened: the crumb row kept its divider AND the toolbar drew one,
   which is the two-lines-in-three-bands shape the whole `.smz-has-listbar` idea exists to remove.
   A selector that matches nothing is the quietest kind of dead CSS: the sheet reads as though the
   rule is in force. */
.tab-content.smz-has-listbar .app-shell .app-shell__bar { border-bottom: 0; }
/* The gutter these cancelled is gone (see the note where it stood, further down this
   sheet), so there is nothing left to outrank: a shell host has no side padding of its own
   on ANY page now, and its chrome shares the one edge its body does. */

/* Kriya's project-list toolbar is the reference every other module's list bar copies.
   `.lk-bar__row` (ui/listKit.js) is listed IN these rules rather than given a lookalike
   set of its own: one declaration means the surface, the bleed, the hairline and the
   sticky behaviour are literally the same CSS in every module, and cannot drift. */
#kriya-tab .wk-bar--tasks,
#kriya-tab .wk-dash-filters,
.lk-bar__row {
  margin-top: 0;
  position: sticky;
  top: 0;
  z-index: 20;
  background: transparent;   /* no own colour — sits on the white module body */
}
/* The Tasks toolbar is FULL-BLEED and shares the header's neutral white so title + toolbar
   read as one chrome zone; a single hairline divider under the toolbar separates chrome from
   the board. Opaque white keeps the sticky bar from showing rows through. */
#kriya-tab .wk-bar--tasks,
.lk-bar__row {
  /* Full-bleed band; content inset by the page gutter. `width: auto` beats the
     `.wk-filters { width: 100% }` rule that would otherwise pin the width. */
  width: auto;
  /* 8px under the hairline, not 14 (2026-09-15, user direction: "without any unnecessary lines
     and spaces"). The line is what separates chrome from content and it stays — the zone always
     ends in one — but the gap after it was doing the same job a second time.
     THE NUMBER IS `--lk-bar-gap`, NOT A LITERAL, because the expanded filter band cancels it
     with a negative margin of its own and the two must always be the same number. They were not:
     this dropped 14 -> 8 and the band kept `-14px`, so the band rode 6px too high and its orange
     rule drew as a SECOND line above the hairline. See the band's rule for the whole story. */
  margin: 0 calc(-1 * var(--page-pad-x, 0px)) var(--lk-bar-gap);
  padding: 6px var(--page-pad-x, 8px);
  border: 0;
  border-bottom: 1px solid var(--border-muted); /* thin divider under the toolbar */
  border-radius: 0;
  background: transparent;   /* no own colour — sits on the white module body */
  box-sizing: border-box;
  min-width: 0;
  flex-wrap: wrap;
}
/* Expandable filter bar shown below the toolbar (toggled by the Filters button). Full-bleed
   band; its content is inset by the page gutter like the toolbar. A subtle grey tint sets it
   apart from the white chrome, flush under the toolbar. Filters wrap horizontally. */
#kriya-tab .wk-filterbar,
.lk-filterbar,
.app-shell .app-shell__filterbar {
  width: auto;
  /* THE BAND BLEEDS EXACTLY WHAT THE TOOLBAR BLEEDS — no floor, no more (2026-09-15, user
     report: "when filter expanded, the upper orange lane is not in match with single hair
     line … this issue is across app, all pages").

     The toolbar one row up is `margin: 0 calc(-1 * var(--page-pad-x, 0px)); padding: 6px
     var(--page-pad-x, 8px)`. This band used `max(gutter, 10px)`, and `--page-pad-x` is DEFINED
     as 0 on most list pages (the full-bleed trick), so the floor fired almost everywhere: the
     band's box — and therefore the two accent rules it draws when open — ran 10px past each end
     of the hairline they are supposed to continue. Closed, the zone ends in one line; open, the
     line above the filters was 20px longer than it. On every page.

     The floor was added when this band was a TINTED SURFACE, to keep the first select off its
     fill. The expanded band has been `background: transparent` since (see "the EXPANDED band"
     block near the end of this sheet), so there is no fill to stand clear of — the floor now
     buys nothing and costs the alignment. A full-bleed band must pad back exactly what it pulls
     out, AND pull out exactly what the row above it pulls out; the second half is what makes the
     borders line up rather than only the content. */
  --lk-band-inset: var(--page-pad-x, 0px);
  /* THE ORANGE RULE LANDS ON THE HAIRLINE, NOT NEAR IT (2026-09-15, user report: "why there are
     two lines, one thin hair line and other filter expanded bar … make orange line align on hair
     thin line. need to move filter borders few pixels down").

     The toolbar ends in a 1px hairline and then a `--lk-bar-gap` margin. To put this band's top
     border ON that hairline, the negative margin has to cancel the gap AND one more pixel — the
     width of the hairline itself — so the band's border-box starts where the hairline starts and
     the orange (later in document order) paints over the grey. One line, in exactly the place the
     single line sits when the band is closed.

     It was a hardcoded `-14px`, paired with a toolbar margin that was ALSO 14. When that dropped
     to 8 earlier the same day, nobody changed this, and a pair of numbers that only work together
     had drifted 6px apart — which is not a rounding error, it is a whole second line. Deriving
     both from `--lk-bar-gap` is what stops the next change to one of them doing it again. */
  margin: calc(-1 * var(--lk-bar-gap) - 1px) calc(-1 * var(--lk-band-inset)) 14px;
  padding: 9px var(--lk-band-inset);
  /* Soft neutral grey — same tone as the collapsed Filters button + caret, so the
     button, connector and panel read as one connected filter surface. */
  background: var(--bg-muted);
  border-bottom: 1px solid var(--border-muted);
  box-sizing: border-box;
}
#kriya-tab .wk-filterbar[hidden], .lk-filterbar[hidden], .app-shell__filterbar[hidden] { display: none; }
#kriya-tab .wk-filterbar__inner, .lk-filterbar__inner, .app-shell__filterbar .wk-filterbar__inner { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 14px; }
#kriya-tab .wk-filterbar__sep { width: 1px; align-self: stretch; min-height: 22px; background: #d7dce4; }
/* Highlighted "Clear all" — stands out as the reset action on the filter bar. */
/* THE CLASS IS REPEATED TO RAISE THE WEIGHT, AND THAT IS THE WHOLE FIX (2026-09-15, user report:
   "in some pages buttons are looking like that" — a filled blue pill with no readable text).

   This button is a `.ghost-button` that OVERRIDES its own ghost treatment: it paints itself
   `background: var(--primary)` and white ink, because "Clear all" is the one emphasised control
   in the filter band. Later, `.ghost-button { color: var(--primary) }` was added further down the
   sheet for legibility — same specificity (0,1,0), later in document order, so it WON. The button
   kept its blue fill and took blue ink: invisible text on a visible pill, on every list page in
   the app that has filters.

   Doubling the class makes this (0,2,0), so it wins on specificity rather than on where it
   happens to sit in the file — the tie-break that moved under it once and would again.
   Swept app-wide by `ui/buttonLegibility.spec.js`. */
#kriya-tab .wk-filterbar__clear.wk-filterbar__clear,
.lk-filterbar__clear.lk-filterbar__clear,
.app-shell__filterbar .wk-filterbar__clear.wk-filterbar__clear {
  margin-left: auto;
  background: var(--primary, #1d4ed8);
  border: 1px solid var(--primary, #1d4ed8);
  color: var(--primary-ink, #fff);
  font-weight: 600;
  box-shadow: 0 1px 2px rgba(29, 78, 216, .28);
}
#kriya-tab .wk-filterbar__clear:hover, .lk-filterbar__clear:hover, .app-shell__filterbar .wk-filterbar__clear:hover {
  background: var(--primary-hover, #1e40af);
  border-color: var(--primary-hover, #1e40af);
  color: var(--primary-ink, #fff);
}
/* Filter items stack the name ABOVE the control (not side-by-side). */
#kriya-tab .wk-filterbar .wk-fp__row, .lk-filterbar .lk-fp__row, .app-shell__filterbar .wk-fp__row { flex-direction: column; align-items: stretch; gap: 3px; white-space: normal; }
#kriya-tab .wk-filterbar .wk-fp__row > span, .lk-filterbar .lk-fp__row > span, .app-shell__filterbar .wk-fp__row > span { font-size: 10px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted, #64748b); }
#kriya-tab .wk-filterbar .wk-fp__row select, .lk-filterbar .lk-fp__row select, .app-shell__filterbar .wk-fp__row select { max-width: none; width: 100%; min-width: 132px; }
#kriya-tab .wk-bar .ghost-button, #kriya-tab .wk-bar .primary-button, #kriya-tab .wk-bar .wk-iconbtn, #kriya-tab .wk-bar .wk-toggle button,
.lk-bar__row .ghost-button, .lk-bar__row .wk-iconbtn { margin-top: 0; }
/* ── THE STACKED-FORM MARGINS DO NOT BELONG IN A CHROME ROW ───────────────────────────────
   This sheet gives two elements a vertical margin for STACKED FORMS — `label { margin: 12px
   0 6px }` and `.ghost-button { margin-top: 12px }`. Both are right in a form and both are
   wrong in a toolbar or a filter band, where they are not space around a control but extra
   ROW HEIGHT with nothing on screen to account for it.

   It has been found twice. Orbit's LEADS toolbar sat 18px taller than DEALS' because Leads
   wraps its sort picker in `<label class="orbit-sort">` and Deals has no label (30 + 12 + 6
   = a 48px flex line, every other control centring inside a band half again as tall as
   itself). And every filter band's "Clear all" is a `.ghost-button`, so it carries 12px that
   pushes it out of line with the selects beside it — everywhere except Orbit, which had a
   private `#orbit-tab button { margin-top: 0 }` blanket in its partial and therefore looked
   correct while every other module did not.

   So it is stated ONCE, for both chrome zones of all three families: Kriya's bar, the shared
   `lkBar`, and the app shell's own. A bar that puts a label or a ghost button in it gets this
   with nothing to remember. */
/* A POPOVER WRAPPER in a chrome row is a control too. `.wk-hdrmenu` (the Columns / Sort /
   Export / Saved-views menus every `VIEW_DEFS` toolbar is built from) is
   `display: inline-block`, so the 30px button inside it gets a line box around it and the
   wrapper measures ~36 — six pixels the row grows by and nothing on screen accounts for.
   Its siblings `.wk-search-collapse` and `.wk-fp` are already flex and already land on 30.
   Measured: Artha's tools row was 45.9px against Deals' 40. */
#kriya-tab .wk-bar .wk-hdrmenu,
.lk-bar__row .wk-hdrmenu { display: inline-flex; align-items: center; }

#kriya-tab .wk-filterbar .ghost-button,
.lk-filterbar .ghost-button,
.app-shell__filterbar .ghost-button,
#kriya-tab .wk-filterbar label,
.lk-filterbar label,
.app-shell__filterbar label { margin-top: 0; }
/* The band's rows are `<label>`s, and they DO want their own bottom spacing from the shared
   `.wk-fp__row` rule — only the form margin goes. */

/* A <label> in a TOOLBAR is a control WRAPPER, not a form label — and it is the one
   element in the bar the normalisation above cannot see, because that rule names the
   shared BUTTON classes and a label carries none of them.

   The global `label { display: block; margin: 12px 0 6px }` is written for stacked
   forms. Inside a row that centres its items, those 18px are not white space around a
   label — they are 18px of ROW HEIGHT with nothing on screen to account for them: the
   flex line grows to `30 + 12 + 6`, every OTHER control centres inside a band half again
   as tall as itself, and the reader sees the whole toolbar pushed down from the crumbs
   above it and away from the divider below.

   That is exactly why Orbit's LEADS bar sat 18px taller than DEALS'. Their controls are
   the same 30px; Leads wraps its sort picker in `<label class="orbit-sort">Sort …</label>`
   and Deals has no label at all. IAM's grant list has the same `<label class="iam-groupby">`
   and the same 18px. So the fix belongs HERE, beside the height normalisation, and not in
   either module: any bar that puts a label around a control gets it, including ones added
   later, with nothing to remember. */
#kriya-tab .wk-bar label,
.lk-bar__row label { margin: 0; }
#kriya-tab #wk-views-bar, #kriya-tab #wk-views-bar .wk-viewsel { display: inline-flex; align-items: center; }
/* The saved-views mount point is empty until _renderSavedViews() fills it (and stays empty in views
   that don't show it). An empty flex item still eats a gap on BOTH sides, which reads as an odd extra
   gap between the toolbar icons — collapse it out of the flow while empty. */
#kriya-tab #wk-views-bar:empty { display: none; }
/* One consistent control height across the toolbar so every edge lines up.
   `.lk-bar__row` rides along so a list bar in ANY module sizes its controls exactly
   like the Kriya project list it copies. */
#kriya-tab .wk-bar .ghost-button,
#kriya-tab .wk-bar .primary-button,
#kriya-tab .wk-bar .wk-iconbtn,
#kriya-tab .wk-bar .wk-toggle-btn,
#kriya-tab .wk-bar .wk-viewsel__btn,
#kriya-tab .wk-bar .wk-toggle,
.lk-bar__row .ghost-button,
.lk-bar__row .primary-button,
.lk-bar__row .wk-iconbtn,
.lk-bar__row .wk-toggle-btn,
.lk-bar__row .wk-toggle {
  height: 30px; box-sizing: border-box; display: inline-flex; align-items: center;
  padding-top: 0; padding-bottom: 0; line-height: 1;
}
#kriya-tab .wk-bar .wk-inline select,
#kriya-tab .wk-bar .wk-dash-select,
#kriya-tab .wk-bar .wk-search,
.lk-bar .lk-bar__row .wk-search-collapse .wk-search { height: 30px; box-sizing: border-box; padding-top: 0; padding-bottom: 0; }
/* Native <select>s ignore CSS height on macOS (they enforce a taller intrinsic
   control height), which made the dashboard filter bar thicker than the Tasks bar.
   Opt the dashboard bar's selects out of native rendering so they size to exactly
   30px like every other control; re-add a chevron since native appearance is gone. */
#kriya-tab .wk-bar.wk-dash-filters select {
  -webkit-appearance: none;
  appearance: none;
  height: 30px;
  box-sizing: border-box;
  padding: 0 26px 0 10px;
  line-height: 28px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
#kriya-tab .wk-bar .wk-toggle { padding: 0 3px; }
#kriya-tab .wk-bar .wk-toggle button { width: 30px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }

/* ============================================================================
   GLOBAL APP SHELL — the one page-chrome shared by every module.
   Promoted from Kriya's #kriya-tab chrome above so ANY module can render the
   same header (breadcrumb + right-aligned icon actions), toolbar and green
   filter bar just by calling renderShell() (frontend/src/ui/appShell.js).
   A module renders ONLY its body; this chrome is identical everywhere and
   follows the global Appearance preferences. Kriya keeps its own #kriya-tab
   rules for now; other modules adopt these. Selectors mirror the Kriya block
   1:1 so the two look pixel-identical.
   ============================================================================ */
/* Flush the shell to the top of its host (kills the host's top padding, like
   Kriya's .wk-flush-top). renderShell() adds .app-shell-host to the container. */
.app-shell-host { padding-top: 0 !important; }
.app-shell { display: flex; flex-direction: column; min-width: 0; --page-pad-x: var(--page-gutter); }
/* NOTE: the chrome rules are scoped `.app-shell .app-shell__*` (specificity 0,2,0) on purpose —
   the bar/header borrow the global `.wk-bar` helper for tooltips, and stray single-class helper
   rules (e.g. `.wk-filters { width: 100% }`) must NOT be able to override the full-bleed width. */
/* Header band: full-bleed (edge to edge), content inset by the page gutter. */
.app-shell .app-shell__header {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: nowrap;
  width: auto; box-sizing: border-box;
  margin: 0 calc(-1 * var(--page-pad-x, 0px)) 0;
  padding: 0 var(--page-pad-x, 16px);
  min-height: 60px;
  background: var(--bg-surface);
  color: var(--text);
}
.app-shell__crumbs { min-width: 0; flex: 1 1 auto; }
.app-shell__icon { font-size: 16px; line-height: 1; opacity: .9; color: var(--text-subtle); }
/* Header-action cluster is GLOBAL: `.app-shell__actions` (shell modules) and `.wk-hdr-actions`
   (Kriya's board header) are the same chrome, so they share ONE definition — no module may
   re-declare it. `margin-left: auto` right-aligns it when the header isn't space-between. */
.app-shell__actions,
.wk-hdr-actions { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; margin-left: auto; }
/* Toolbar: full-bleed, sticky, shares the header's neutral white so title +
   toolbar read as one chrome zone; a single hairline separates it from the body. */
.app-shell .app-shell__bar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 8px 12px;
  width: auto; box-sizing: border-box; min-width: 0;
  margin: 0 calc(-1 * var(--page-pad-x, 0px)) 14px;
  padding: 6px var(--page-pad-x, 8px);
  border: 0; border-bottom: 1px solid #d7dce4; border-radius: 0;
  background: var(--bg-surface);
  flex-wrap: nowrap;
}
.app-shell__bar-left, .app-shell__bar-right { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
.app-shell__bar-right { margin-left: auto; }
@media (max-width: 900px) { .app-shell .app-shell__bar { flex-wrap: wrap; } }
/* Toolbar item dropdowns stay on-screen by opening AWAY from the nearest body edge: LEFT-group
   items open rightward (aligned to the item's left), RIGHT-group items open leftward (aligned to
   the item's right). Prevents them running off the module body's left/right edges and clipping. */
.canvas .tab-content.active :is(.app-shell__bar-left, .wk-bar__left) :is([role="menu"], [class*="__pop"]) {
  left: 0 !important; right: auto !important;
}
.canvas .tab-content.active :is(.app-shell__bar-right, .wk-bar__right) :is([role="menu"], [class*="__pop"]) {
  left: auto !important; right: 0 !important;
}

/* ══ Central tiled home (module launcher / app landing) ═══════════════════════════
   A professional responsive grid of module tiles. The first tile (Central Dashboard) is a filled
   accent hero; the rest are white cards with an accent-tinted icon block. Each `--tile-accent` is
   set inline per module. */
#central-home { padding: 6px 0 40px; }
/* "Welcome, <first name> 👋" pinned to the right end of Central's breadcrumb line (Home + Dashboards).
   It sits in the header flex row beside the crumbs (crumbs grow, so this lands at the far right).
   A large, warm greeting — brand-gradient name text + a gentle wave. */
.app-shell .app-shell__header .central-hello {
  flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; gap: 8px;
  font-size: 1.1rem; font-weight: 700; letter-spacing: -0.01em; line-height: 1.1; white-space: nowrap;
  color: var(--text, #111827);
}
/* "Welcome," reads lighter/regular; the name stays bold black at the same size. */
.app-shell .app-shell__header .central-hello__lead { font-weight: 400; color: var(--text-muted, #64748b); }
.app-shell .app-shell__header .central-hello__wave {
  font-size: 1.15rem; display: inline-block;
  transform-origin: 72% 78%; animation: central-wave 2.6s ease-in-out 0.6s 2;
}
@keyframes central-wave {
  0%, 60%, 100% { transform: rotate(0deg); }
  10% { transform: rotate(14deg); } 20% { transform: rotate(-8deg); }
  30% { transform: rotate(14deg); } 40% { transform: rotate(-4deg); } 50% { transform: rotate(10deg); }
}
@media (prefers-reduced-motion: reduce) { .app-shell .app-shell__header .central-hello__wave { animation: none; } }
@media (max-width: 900px) { .app-shell .app-shell__header .central-hello { font-size: 1.2rem; } }
@media (max-width: 640px) { .app-shell .app-shell__header .central-hello { display: none; } }
/* Org settings → "Module changes" history */
.ws-mod-history__list { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow: auto; }
.ws-mod-history__row { border: 1px solid var(--border, #e5e7eb); border-radius: 8px; padding: 8px 10px; }
.ws-mod-history__meta { font-size: 12px; color: var(--text-muted, #64748b); margin-bottom: 4px; }
.ws-mod-history__mods { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; font-size: 12.5px; }
.ws-mod-lbl { font-weight: 600; }
.ws-mod-chip { font-size: 11px; font-weight: 600; border-radius: 999px; padding: 1px 8px; }
.ws-mod-chip--on { background: #dcfce7; color: #166534; }
.ws-mod-chip--off { background: #fee2e2; color: #b91c1c; }
.app-shell .app-shell__header .tb-orgbrand--trial #tb-orgbrand-name,
.tb-orgbrand--trial #tb-orgbrand-name,
.tb-orgbrand--grace #tb-orgbrand-name { background: #c2410c; color: #fff; border-radius: 6px; padding: 2px 10px; }
/* Central-home paywall when the active org is disabled (unpaid). */
.central-paywall { max-width: 560px; margin: 48px auto; text-align: center; padding: 36px 28px; border: 1px solid var(--border, #e5e7eb); border-radius: 16px; background: var(--bg-surface, #fff); box-shadow: 0 1px 2px rgba(16,24,40,.04); }
.central-paywall__ico { width: 56px; height: 56px; margin: 0 auto 14px; display: grid; place-items: center; border-radius: 14px; background: #fff7ed; color: #c2410c; }
.central-paywall__ico .hicon { width: 28px; height: 28px; }
.central-paywall__title { margin: 0 0 8px; font-size: 1.3rem; font-weight: 750; }
.central-paywall__sub { margin: 0 0 18px; color: var(--text-muted, #64748b); font-size: .92rem; line-height: 1.5; }
.central-paywall__actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
/* App Admin → Accounts & Orgs: KPI card grid (each card filters the table below) + list toolbar. */
/* THREE ZONES, THREE TEXTURES (2026-09-25, user report with a screenshot: *"here above metrics,
   middle filters and below table, everything looks mixed up. differentiate these in UI
   perspective. also try to see if the size of the metrics cards can be reduced"*).

   Every band was the same weight — a row of bordered boxes, then a row of bordered boxes, then a
   table — so the page read as one undifferentiated stack. They are told apart by what each one IS
   rather than by adding chrome: the metrics are a LIGHT strip that ends in a hairline, the
   controls sit bare on the page ground with nothing around them, and the table is a framed object.
   No band gains a heading and none gains a fill; this is the one line the design keeps plus the
   space either side of it.

   THE CARDS LOST A THIRD OF THEIR HEIGHT. A 1.5rem number at weight 760 is a headline, and there
   are THIRTEEN of them above the thing the reader came for. Measured before: 82px a card. */
.aa-kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 8px;
  margin: 0 0 14px; padding-bottom: 14px;
  border-bottom: 1px solid var(--border-muted, var(--border, #e5e7eb));
}
.aa-kpi-card { display: flex; flex-direction: column; gap: 1px; align-items: flex-start; text-align: left; padding: 8px 10px; border: 1px solid var(--border, #e5e7eb); border-radius: 9px; background: var(--bg-surface, #fff); cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .05s; }
.aa-kpi-card:hover { border-color: var(--accent, #2563eb); box-shadow: 0 1px 3px rgba(16,24,40,.08); }
.aa-kpi-card:active { transform: translateY(1px); }
.aa-kpi-card__num { font-size: 1.125rem; font-weight: 700; line-height: 1.15; }
.aa-kpi-card__label { font-size: .72rem; font-weight: 600; line-height: 1.25; }
/* STILL LIVE, and checked before it was nearly deleted: the KPI cards stopped rendering a hint
   line (it is the card's `title` now), but the org drill-in header uses this same class for the
   org id, the currency and the read-only note. A rule with no caller gets removed; this one has
   four. */
.aa-kpi-card__hint { font-size: .66rem; line-height: 1.25; color: var(--text-muted, #64748b); }
/* ── App Admin ▸ Accounts & Orgs, 2026-09-25 ──────────────────────────────────
   The console grew a page, a sort, removable filter chips and a drill-in on the relationship
   between an org and its accounts. These are the controls that carry them. */

/* WHICH CARD IS DRIVING THE LIST. Thirteen cards that all look alike, one of which silently
   filters the table under them, is how a reader concludes the estate has four orgs in it. */
.aa-kpi-card.is-on {
  border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary);
}

/* ── OVERVIEW: TILES, CARDS, CHART ───────────────────────────────────────────────────────────
   2026-09-25, user direction: the overview *"is missing so many parameters to track… currently
   charts are not available… overall UI is very bad."*

   A tile is a KPI card that also says how the number compares with the window before it — a
   number with nothing beside it is a number nobody can act on, which is most of what was wrong.
   It reuses `.aa-kpi-grid` so this page and Accounts & Orgs have one metrics strip between them
   rather than two that drift. */
/* TILES ARE GROUPED BY WHAT THEY ARE (2026-09-25, user direction: *"group the metric cards by
   their type"*). Two kinds sat in one undifferentiated run — what MOVED inside the chosen window,
   and what the estate IS right now — and a tile from each looks identical, so the range control
   above appeared to govern all of them. The caption is what says which. The strip's own closing
   hairline moves to the wrapper: one line under the whole thing, not one under each group. */
.aa-tiles { margin: 0 0 14px; padding-bottom: 14px; border-bottom: 1px solid var(--border-muted, var(--border, #e5e7eb)); }
.aa-tilegroup + .aa-tilegroup { margin-top: 14px; }
.aa-tilegroup > .aa-kpi-grid { margin: 0; padding-bottom: 0; border-bottom: 0; }
.aa-tilegroup__cap {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
  margin: 0 0 7px; font-size: .78rem; font-weight: 700; color: var(--text);
}
.aa-tilegroup__sub { font-size: .7rem; font-weight: 400; color: var(--text-muted); }
/* The strip's own control, under it rather than in a caption: it acts on the whole strip, not on
   either group. */
.aa-tiles__foot { display: flex; justify-content: flex-end; margin-top: 10px; }

/* ── TOP-N LISTS (web activity) ───────────────────────────────────────────────────────────────
   2026-09-25, user report with a screenshot: three columns of URLs painted across each other.
   They were in `.appadmin-stat-grid`, whose tracks are `minmax(180px, 1fr)` — and a grid item's
   default `min-width` is `auto`, i.e. its MIN-CONTENT width. A URL has no break opportunities, so
   `http://localhost:5173/orbit/pipeline/oppy_7gzk912fa0z5yd8q1sms` measured wider than its track,
   refused to shrink into it, and overflowed straight over the next column.

   ABLATED IN HEADLESS CHROME, because the obvious answer was the wrong one. `min-width: 0` on the
   column and on the row changes nothing here — the row is a `space-between` flex line, so the
   key's BOX is already constrained. What escapes is the TEXT inside that box, and the only rule
   that stops it is `overflow-wrap: anywhere` on the key: with it, the string wraps to two lines
   and ends at x=325 inside a column ending at 363; without it, one line ending at **418**, 55px
   over the neighbour, which is the screenshot. Measured by a Range over the text node — the
   element's own rect fits in every variant and cannot see this at all.

   `min-width: 0` stays because it is what makes the box shrinkable in the general case (a row
   without `space-between`, a key with no break rule), but the comment says which line was proven,
   so the next reader deletes the ornament rather than the fix. That grid is for STAT CARDS, whose
   content is a number and can never do this. */
.aa-toplists {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 18px 24px; margin-top: 16px;
}
.aa-toplist { min-width: 0; }
.aa-toplist .aa-tilegroup__cap { margin-bottom: 6px; }
.aa-toplist__rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.aa-toplist__rows > li {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  min-width: 0; padding: 5px 0;
  border-bottom: 1px solid var(--border-muted, var(--border));
  font-size: .78rem;
}
.aa-toplist__rows > li:last-child { border-bottom: 0; }
/* The KEY is the long one, so it is the one that may wrap; the count never leaves its own line. */
.aa-toplist__k { min-width: 0; overflow-wrap: anywhere; color: var(--text); }
.aa-toplist__v { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 600; color: var(--text-muted); }

.aa-tile {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start; text-align: left;
  padding: 8px 10px; border: 1px solid var(--border, #e5e7eb); border-radius: 9px;
  background: var(--bg-surface, #fff); font: inherit; color: inherit;
}
button.aa-tile { cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .05s; }
button.aa-tile:hover { border-color: var(--accent, #2563eb); box-shadow: 0 1px 3px rgba(16,24,40,.08); }
button.aa-tile:active { transform: translateY(1px); }
button.aa-tile:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* WHICH TILE IS DRIVING THE CHART, said the same way a KPI card says it on the other screen. */
.aa-tile.is-on { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
.aa-tile__num { font-size: 1.125rem; font-weight: 700; line-height: 1.15; }
.aa-tile__label { font-size: .72rem; font-weight: 600; line-height: 1.25; }
.aa-tile__delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: .66rem; line-height: 1.25; color: var(--text-muted, #64748b);
}
/* UP IS NOT ALWAYS GOOD — accounts deleted rising is not a success — so the direction is stated in
   a neutral ink with an arrow, and the page does not pretend to know which way a reader wants any
   particular measure to move. */
.aa-tile__delta > span[aria-hidden] { font-size: 8px; }
.aa-tile__delta.is-up > span[aria-hidden] { color: var(--text); }
.aa-tile__delta.is-down > span[aria-hidden] { color: var(--text); }
.aa-tile__delta.is-none { color: var(--text-faint, #98a2b3); }

/* A card is the framed object this page is built from — the same frame the tables wear, so the
   three textures (bare controls, light tile strip, framed objects) hold across both screens. */
.aa-card {
  border: 1px solid var(--border-muted, var(--border, #e5e7eb));
  border-radius: 10px; background: var(--bg-surface, #fff);
  padding: 14px 16px; margin: 0 0 14px;
}
.aa-card__head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 10px 16px; }
.aa-card__head .appadmin-section__title { margin: 0 0 2px; }
.aa-card__head .appadmin-section__lede { margin: 0; }
.aa-card__tools { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; }
.aa-card__tools .lk-fp__row {
  display: flex; flex-direction: column; align-items: stretch; gap: 3px;
  margin: 0; white-space: normal; font-size: 12px; color: var(--text-subtle);
}
.aa-card__tools .lk-fp__row > span {
  padding: 0; font-size: 10px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--text-muted, #64748b);
}
.aa-card__tools .lk-fp__row .input {
  box-sizing: border-box; height: 32px; padding-top: 0; padding-bottom: 0;
  width: auto; min-width: 150px;
}
.aa-card__tools .lk-fp__row .input:disabled { opacity: .55; cursor: not-allowed; }
/* A SWITCH among pickers. It sits on the controls' line rather than under a caption of its own,
   because it qualifies the chart beside it rather than choosing anything. */
.aa-card__toggle {
  box-sizing: border-box; display: inline-flex; align-items: center; gap: 6px;
  height: 32px; margin: 0; padding: 0;
  font-size: .78rem; color: var(--text-subtle); white-space: nowrap; cursor: pointer;
}
.aa-card__toggle > input[type="checkbox"] { margin: 0; }
/* THE CHART NEEDS A HEIGHT OF ITS OWN. ECharts measures its container, and a container that hugs
   its (empty) content measures zero — which renders nothing at all, with no error. */
.aa-chart { height: 340px; margin-top: 12px; }
.aa-chart > .empty-state { display: flex; flex-direction: column; justify-content: center; height: 100%; text-align: center; }
@media (max-width: 720px) { .aa-chart { height: 260px; } }

/* The active-filter chips. A row of SELECTS shows the controls; it does not show that three of
   them are set — this does, and each one removes its own filter. */
.aa-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 8px 0 0; }
/* The global `.ghost-button { margin-top: 12px }` is written for stacked forms; in a chip row
   it is row height with nothing on screen to account for it. The filter band states the same
   reset for the same button — this row is the third chrome zone that holds one. */
.aa-chips .ghost-button { margin-top: 0; }
.aa-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 24px; padding: 2px 8px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--bg-muted);
  color: var(--text); font: inherit; font-size: 12px; cursor: pointer;
}
.aa-chip:hover { border-color: var(--primary); }
.aa-chip:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.aa-chip__x { color: var(--text-subtle); font-size: 14px; line-height: 1; }

/* A sortable header. The button carries the target, not the cell — a 2px-tall click area on a
   table header is a control that only works by accident. */
.aa-th__btn {
  display: inline-flex; align-items: center; gap: 2px; width: 100%; min-height: 24px;
  padding: 0; border: 0; background: none; color: inherit; font: inherit; font-weight: inherit;
  text-align: left; cursor: pointer;
}
/* EVERY HEADER IS THE SAME TYPE (2026-09-25, user report with a screenshot: *"some of the column
   headers font is looking small for which sorting option is available"*).

   A `<button>` does not inherit `text-transform` or `letter-spacing`: the UA stylesheet resets both
   on form controls, and `font: inherit` above does not cover them because neither is part of the
   `font` shorthand. Measured in Chrome — the `th` resolved `uppercase` / `0.46px`, the button
   inside it `none` / `normal`. Identical pixel size, but mixed-case untracked text next to small
   uppercase reads as a different and larger font, so a row of headers had two treatments in it and
   the difference tracked exactly which columns could be sorted. */
.aa-th__btn { text-transform: inherit; letter-spacing: inherit; }
/* Underline, not a colour: see the contrast note below. */
.aa-th__btn:hover { text-decoration: underline; }
.aa-th__btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* THE SORTED COLUMN IS NOT MARKED IN BLUE. Measured against the head band this table carries:
   `--primary` (#1a73e8) on #1f2937 is **3.26:1**, under the 4.5 this 11.5px text needs. The head's
   own ink is 13.40:1, so the weight and the solid arrow carry the distinction instead. */
.aa-th.is-sorted { color: var(--table-head-fg, #f1f5f9); font-weight: 700; }
/* THE SYMBOL IS ON EVERY SORTABLE COLUMN, dim until it is the one in force. An affordance that
   only appears once you have used it is one nobody finds.

   A STACKED PAIR OF FILLED TRIANGLES, not a `↕` (2026-09-25, user report with a screenshot:
   *"sorting symbol beside header is not clearly visible"*). The single arrow was one hairline
   glyph at 10px on a dark band, and at the opacity it carried it measured **4.49:1** — under the
   4.5 minimum, before allowing for the fact that a 1px stroke at 10px is thinner than the text
   around it. A filled triangle is a shape, not a stroke, so it survives that size; the pair says
   "this sorts" and the lit one says "this is the one, this way" with the same object.

   Measured composites against the head band (#1f2937 under #f1f5f9 ink): .5 -> 4.49:1,
   .7 -> 7.34:1, .8 -> 9.08:1, 1 -> 13.4:1. The resting pair sits at .8 so it is plainly legible on
   its own; the active triangle goes to full and its partner drops back to .35 so the pair reads as
   a direction rather than as two marks of equal weight. */
.aa-th__sort {
  display: inline-flex; flex-direction: column; justify-content: center;
  margin-left: 4px; font-size: 9px; line-height: 0.75; gap: 2px; letter-spacing: 0;
  /* A gap, or the two bases meet and the pair reads as one filled hourglass rather than as
     an up arrow and a down arrow. */
}
.aa-th__sort > span { opacity: .8; }
.aa-th__sort > span.is-on { opacity: 1; }
/* When one is lit the other steps back, so the pair reads as a direction rather than as two marks
   of equal weight. */
.aa-th.is-sorted .aa-th__sort > span:not(.is-on) { opacity: .35; }

/* A count that opens what it counts. Styled as a link because that is what it does — 12 members
   is the number somebody is looking at when they ask who they are. */
.aa-link {
  padding: 0; border: 0; background: none; color: var(--primary); font: inherit;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.aa-link:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* The row's own name: heavier than the cells beside it, because it is what the row IS and the
   thing a reader scans down. */
.aa-name { font-weight: 600; text-decoration: none; }
.aa-name:hover { text-decoration: underline; }
/* Slugs and ids are read character by character when somebody is matching one against a log. */
.aa-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--text-subtle); }
/* Paid / Unpaid in the org table. The difference carries in WEIGHT and ink, not in colour alone —
   a green/red pair would be the only two colours on a table of plain cells, and would say
   "something is wrong" about every org on a free plan. */
.aa-yes { font-weight: 600; color: var(--text); }
.aa-no { color: var(--text-subtle); }

.aa-pager { display: flex; align-items: center; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
/* `width: auto` because the base `select` rule is `width: 100%` — written for stacked forms, and
   in a flex row it makes the page-size picker demand the whole line, wrap onto its own row and
   paint as a full-width bar under the pager (2026-09-25, seen in headless Chrome). It is a
   control in a chrome row: it is as wide as its longest option and no wider. */
.aa-pager .input { box-sizing: border-box; width: auto; min-height: 28px; }

/* The drill-in: an account's organisations, or an org's people. A page, not a stack of cards. */
.aa-detail__head { display: flex; align-items: center; gap: 12px; margin-bottom: 4px; }
.aa-detail__head .appadmin-section__title { margin: 0; }
/* THE SYSTEM CARD, IN TWO GROUPS (2026-09-25, user direction: *"system parameters display is not
   looking good. make it more appealing."*). Nine identical grey pairs in one row said nothing
   about which of them to look at — "Stuck 0" and "Migration head org_numbering5" were the same
   weight and the same colour, and one is a health signal while the other is an identifier. */
.aa-sysgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px 28px; margin-top: 12px; }
.aa-sysgroup { min-width: 0; }
/* `.aa-facts` carries the org drill-in's own closing rule and spacing; inside a card that already
   has a frame, that is a second line saying the same thing. */
.aa-sysgroup .aa-facts { margin: 0; padding-bottom: 0; border-bottom: 0; grid-template-columns: repeat(auto-fit, minmax(122px, 1fr)); }
.aa-sysgroup .aa-fact__v { font-size: 15px; font-weight: 600; line-height: 1.2; }
.aa-fact__sub { font-size: 11px; line-height: 1.3; color: var(--text-faint, #98a2b3); }
/* The value carries the state, not a badge beside it: one object, read at a glance. */
.aa-fact__v.is-good { color: var(--success, #067647); }
.aa-fact__v.is-warn { color: var(--warning, #b54708); }
.aa-fact__v.is-bad { color: var(--danger, #b42318); }
.aa-fact__v.is-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }

.aa-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px 20px; margin: 12px 0 18px; padding-bottom: 14px; border-bottom: 1px solid var(--border);
}
.aa-fact { display: flex; flex-direction: column; gap: 2px; }
.aa-fact__k { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--text-subtle); }
.aa-fact__v { font-size: 14px; color: var(--text); }
.appadmin-section__sub { margin: 18px 0 8px; font-size: 14px; font-weight: 700; }

.aa-listbar { margin: 0 0 16px; }
.aa-listbar__row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.aa-listbar__right { display: flex; align-items: center; gap: 8px; }

/* ONE HEIGHT FOR EVERY CONTROL IN ROW 2 (2026-09-25, user report with a screenshot: the segmented
   control, the search box, the buttons and the count were four different heights on one line).
   `align-items: center` was already set — centring things of different heights is what produced
   the ragged line. The fix is that they are the same height. */
.aa-listbar__row .aa-seg,
.aa-listbar__right > * {
  box-sizing: border-box;
  height: 32px;
  margin: 0;
}
.aa-listbar__right > .input { min-width: 220px; }

/* THE FILTER ROW HERE IS ALWAYS ON SCREEN, SO IT IS NOT THE EXPANDABLE BAND (2026-09-25, user
   report with a screenshot: "all these options are not properly aligned" — an orange rule ran
   straight through the segmented control, the search box and the count).

   It was built as `.lk-filterbar`, which is the band that DROPS OPEN under an `.lk-bar` toolbar,
   and that class carries two things this screen cannot honour. It pulls itself up by
   `calc(-1 * var(--lk-bar-gap) - 1px)` so its top border lands exactly ON the toolbar's own 1px
   hairline; `.aa-listbar__row` draws no hairline, so the pull-up landed the border INSIDE the row
   of controls instead. And it paints both edges in `--filter-accent` because in this app orange
   means the filters are OPEN — a band that never closes is orange for ever and therefore says
   nothing, which is exactly why the sheet's `:not([hidden])` block already leaves the
   always-visible page filter zones out of that accent.

   So this row is its own thing: no accent, no pull-up, no fill. Whitespace separates it from the
   toolbar, and every control in it lands on the same 32px line as the controls above. */
.aa-filterbar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 14px; margin-top: 10px; }
/* Caption ABOVE the control, the way the expandable band stacks its filters — stated here rather
   than borrowed from `.lk-filterbar`, whose descendant rules come with the accent attached. */
.aa-filterbar .lk-fp__row {
  display: flex; flex-direction: column; align-items: stretch; gap: 3px;
  margin: 0; white-space: normal; font-size: 12px; color: var(--text-subtle);
}
/* `.empty-state.small` — the class these captions are built with — carries `padding: 8px 10px`,
   which indented every caption 10px past the control underneath it. A caption sits on its
   control's left edge or it is not a caption for it. */
.aa-filterbar .lk-fp__row > span {
  padding: 0; font-size: 10px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--text-muted, #64748b);
}
/* 32px is the row's control height, and the base `select` padding is `8px 12px` — 8 + 8 + 2
   borders leaves 14px for a 17px line box, so a forced 32px clipped the descender off "All orgs".
   The height is what the row needs; the vertical padding is what has to give. The 34px on the
   right is the chevron's room and stays. */
.aa-filterbar .lk-fp__row .input {
  box-sizing: border-box; width: 100%; min-width: 132px;
  height: 32px; padding-top: 0; padding-bottom: 0;
}
/* A checkbox in here is a CONTROL WRAPPER, not a stacked-form label — and it carries the same
   `.empty-state.small` padding. The global `label { display: block; margin: 12px 0 6px }` is row
   height with nothing on screen to account for it; both are what put a checkbox out of line with
   the selects beside it. `:not(.lk-fp__row)` because the selects above are labels too. */
.aa-filterbar > label:not(.lk-fp__row) {
  box-sizing: border-box; display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0; margin: 0; color: var(--text-subtle);
}
.aa-filterbar > label:not(.lk-fp__row) > input[type="checkbox"] { margin: 0; }
/* COLUMNS IS A FILTER, SO IT WEARS A FILTER'S CLOTHES (2026-09-25, user report with a screenshot:
   *"columns filter is not looking like a filter and now it is looking like a button"*). It sits in
   an `.lk-fp__row` like the selects, and the control itself joins the two authoritative field
   declarations at the top of this sheet — the `input/textarea/select` block for the border, the
   radius, the fill and the padding, and the `input[list]` block for the chevron — rather than
   restating either. A restated constant is correct the day it is written and silent the day its
   partner moves. All that is left for it here is the two things a BUTTON gets wrong: it centres
   its text and it does not show a pointer. */
.aa-colsbtn { text-align: left; cursor: pointer; }

/* ── THE ROW ⋯ AND ITS MENU ─────────────────────────────────────────────────────────────────
   2026-09-25, user report with a screenshot: four buttons in the last cell of every org row,
   three of them destructive and painted alike. The trigger is the narrowest control on the row;
   the menu is `position: fixed` on the BODY (placed by `ui/menuPlacement.js`), because a table
   that scrolls clips anything opened inside it. */
.aa-more {
  box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; min-width: 30px; height: 30px; padding: 0;
  font-size: 16px; line-height: 1;
}
.aa-menu {
  z-index: 60; min-width: 176px; overflow: auto; padding: 4px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: 10px; box-shadow: 0 8px 24px rgba(16, 24, 40, .16);
}
.aa-menu__item {
  display: block; width: 100%; box-sizing: border-box; text-align: left;
  padding: 7px 10px; border: 0; border-radius: 7px; background: none;
  font: inherit; font-size: 13px; color: var(--text); cursor: pointer; white-space: nowrap;
}
.aa-menu__item:hover { background: var(--bg-app); }
/* Hover is a courtesy; the keyboard needs its own. `outline: none` anywhere near this would be
   a control a keyboard reader cannot find. */
.aa-menu__item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
/* THE DANGEROUS ONES ARE INK, NOT FILL. Three filled red pills in a column is a menu that reads
   as an alarm; the colour is on the word, and the separator is what says "past here it is
   permanent". */
.aa-menu__item.is-danger { color: var(--danger, #b42318); }
.aa-menu__item.is-danger:hover { background: var(--danger-soft, #fef3f2); }
.aa-menu__sep { height: 1px; margin: 4px 6px; background: var(--border-muted, var(--border)); }

/* THE ACTIONS COLUMN IS PINNED TO THE RIGHT EDGE OF THE SCROLLPORT (2026-09-25, user report:
   *"this menu button is not visible"*). The table's min-content width is 887px with the default
   columns and real content; at anything narrower it overflowed its container and the LAST column
   went over the edge first. Sticky resolves against the nearest scrollport, which is exactly the
   `.appadmin-table-scroll` wrapper this table now sits in — so the ⋯ stays on screen at every
   width while the rest of the columns scroll under it.

   The cell needs an OPAQUE fill or the columns travel visibly through it, and a left hairline so
   it reads as pinned rather than as a rendering fault. */
/* ALL THE COLUMNS AT THEIR OWN WIDTH — the reader scrolls, the content does not fold
   (2026-09-25, user direction: *"if i am selecting all the columns in column filter, the table is
   getting wrapped. it should not try to fit all the columns in a fixed width, instead i should be
   able to do side scrolling to see all the columns."*).

   `.appadmin-table` is `width: 100%`, which means a table SHARES a fixed width between however
   many columns happen to be switched on: turn the last two on and every cell in the table folds
   into two and three lines rather than the table getting wider. `max-content` asks the columns
   what they actually need; `min-width: 100%` keeps the table filling its box on the way back down,
   so a three-column view does not shrink to a strip with dead space beside it. The wrapper is
   already `overflow-x: auto`, so what the columns need past the box is a scroll.

   Cells stop wrapping for the same reason: a cell that folds is a table trying to fit, and fitting
   is what the reader asked it to stop doing. */
.aa-tablescroll > .appadmin-table { width: max-content; min-width: 100%; margin-top: 0; }
.aa-tablescroll > .appadmin-table > thead > tr > th,
.aa-tablescroll > .appadmin-table > tbody > tr > td { white-space: nowrap; vertical-align: middle; }
/* `vertical-align: middle`, not the standard's `top`, because two cells in a row now disagree about
   how tall their content is: a sortable header's button is 24px (its click target — a 14px one is a
   control that works by accident) against ~14px of plain header text, and a row's ⋯ is 30px against
   its text. Top-aligned, the taller control centres inside its own box and the shorter text sits
   above it, which is the same header row reading as two rows. Every row here is one line, so there
   is nothing that middle alignment costs. */
/* THE TABLE IS ONE OBJECT, so it is framed. The controls above it are bare on the page ground and
   the metrics strip above those ends in a hairline — three textures, no two bands alike. The
   border is earned: this is a surface the reader scrolls sideways and presses inside.

   `overflow-x: auto` makes this a scroll container, which clips to the radius, so the dark head
   band's corners round with the box instead of squaring off over it. */
.aa-tablescroll {
  position: relative;
  margin-top: 2px;
  border: 1px solid var(--border-muted, var(--border, #e5e7eb));
  border-radius: 10px;
}
.aa-tablescroll > .appadmin-table > thead > tr > th:last-child,
.aa-tablescroll > .appadmin-table > tbody > tr > td:last-child {
  position: sticky;
  right: 0;
  background: var(--bg-surface);
  border-left: 1px solid var(--border-muted, var(--border));
  text-align: right;
  white-space: nowrap;
  /* `min-width: 100%` hands the surplus of a narrow view to the columns, and auto layout shares it
     out proportionally — which made the ⋯ column one of the widest things on a three-column table.
     A 1px width on a nowrap cell resolves to "what the content needs and no more", so the surplus
     goes to the columns that have something to show. */
  width: 1px;
}
/* A PINNED CELL CANNOT INHERIT A ROW'S FILL — it paints its own, and the three the table gives a
   row (the head band, the stripe, the hover) each have to be repeated here or the columns travel
   visibly through the one cell that does not move. This is the cost of pinning, stated once. */
.aa-tablescroll > .appadmin-table > thead > tr > th:last-child {
  background: var(--table-head-bg, #1f2937);
}
.aa-tablescroll > .appadmin-table > tbody > tr:nth-child(even) > td:last-child {
  background: var(--table-stripe, #eef1f8);
}
.aa-tablescroll > .appadmin-table > tbody > tr:hover > td:last-child {
  background: var(--table-row-hover, #e7edfa);
}

.aa-seg { display: inline-flex; border: 1px solid var(--border, #e5e7eb); border-radius: 9px; overflow: hidden; }
.aa-seg__btn { display: inline-flex; align-items: center; padding: 0 16px; background: var(--bg-surface, #fff); border: 0; cursor: pointer; font-size: .82rem; font-weight: 600; color: var(--text-muted, #64748b); white-space: nowrap; }
.aa-seg__btn.is-on { background: var(--accent, #2563eb); color: #fff; }
/* The values with NO semantic token — a white gradient, a teal accent, a white active border —
   get explicit dark twins. Teal-700 on the dark surface is ~2.5:1; teal-300 is ~9:1. These are
   every hardcoded-ink-on-token-surface pair the sweep found (see darkInk.spec.js). */
html[data-theme='dark'] .app-shell .app-shell__bar .wk-iconbtn.is-on,
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-iconbtn.is-on,
html[data-theme='dark'] .app-shell .app-shell__bar .wk-toggle button[aria-selected="true"],
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-toggle button[aria-selected="true"],
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active { color: #5eead4; }
html[data-theme='dark'] .app-shell .app-shell__bar .wk-iconbtn.is-on,
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-iconbtn.is-on,
html[data-theme='dark'] .app-shell .app-shell__bar .wk-iconbtn--primary,
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-iconbtn--primary { border-color: var(--border); }
html[data-theme='dark'] #kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active::before { border-color: var(--border); }
/* The shell's filter band is the SHARED band now (see the folded rules above), so its
   geometry, its surface and its inset all come from one declaration. What stood here was a
   parallel copy carrying a hardcoded green fill from an older Kriya — already overridden to
   `var(--bg-muted)` further down, so the green had not painted for some time — plus the
   un-floored `var(--page-pad-x, 8px)` inset that put the first filter against the edge. */
.app-shell__filterbar[hidden] { display: none; }
.app-shell__body { flex: 1 1 auto; min-width: 0; }

/* ── The toolbar's ink is the PAGE'S — the gradient skin this block replaced is why the
   breadcrumb vanished in light mode. These rules were written when the crumbs lived in a BLUE
   header (`--app-header-bg`) and painted everything white for it: white crumbs, white chips,
   rgba-white separators. The header row is gone and the crumbs ride the toolbar now, whose
   surface is `--app-toolbar-bg` — WHITE in the light theme — so every one of those rules put
   white ink on a white bar. Dark survived by accident (`--app-header-text` is near-white on a
   dark toolbar), which is exactly the reported symptom: invisible in light, fine in dark.
   Token ink follows both themes; the special states keep their accents. */
.app-shell .app-shell__bar { background: var(--app-toolbar-bg); color: var(--text); }
.app-shell .app-shell__bar { border-bottom-color: var(--border-muted, var(--border)); }
.app-shell .app-shell__icon,
.app-shell .app-shell__crumbs .hicon-wrap { color: var(--text-subtle); }
.app-shell .app-shell__crumbs .wk-bc__crumb--current { color: var(--text); }
.app-shell .app-shell__crumbs .wk-bc__crumb--muted,
.app-shell .app-shell__crumbs .wk-bc__crumb--link { color: var(--text-muted); }
.app-shell .app-shell__crumbs .wk-bc__crumb--link:hover { color: var(--text); }
.app-shell .app-shell__crumbs .wk-bc__sep { color: var(--text-faint, var(--text-muted)); }
.app-shell .app-shell__crumbs .ws-tip-i { color: var(--text-muted); }
/* Toolbar icon buttons: plain ink, wash on hover — the translucent-white chips were for the
   gradient. */
.app-shell .app-shell__bar .wk-iconbtn {
  color: var(--text-subtle); background: transparent; border-color: transparent; }
.app-shell .app-shell__bar .wk-iconbtn:hover {
  color: var(--text); background: var(--bg-hover, var(--bg-muted)); border-color: transparent; }
.app-shell .app-shell__bar .wk-iconbtn .wk-iconbtn__cap { color: inherit; }
/* Active "where am I" chip. */
.app-shell .app-shell__bar .wk-iconbtn.is-on { color: var(--primary); background: var(--accent-soft, var(--bg-muted)); border-color: transparent; }
.app-shell .app-shell__bar .wk-iconbtn.is-on .wk-iconbtn__cap { color: var(--primary); }
/* Primary create controls (+ New, + X…) keep the filled CTA look. */
.app-shell .app-shell__bar .wk-iconbtn--primary { color: var(--primary-ink, #fff); background: var(--primary); border-color: var(--primary); }
.app-shell .app-shell__bar .wk-iconbtn--primary:hover { color: var(--primary-ink, #fff); background: var(--primary-hover, var(--primary)); border-color: var(--primary-hover, var(--primary)); }
.app-shell .app-shell__header .wk-iconbtn--primary .wk-iconbtn__cap,
.app-shell .app-shell__bar .wk-iconbtn--primary .wk-iconbtn__cap { color: var(--primary); }
/* Record/detail Close keeps its danger cue on the gradient. */
.app-shell .app-shell__bar .wk-iconbtn--close { color: #fff; background: rgba(220, 38, 38, 0.85); border-color: rgba(255, 255, 255, 0.4); }
.app-shell .app-shell__bar .wk-iconbtn--close:hover { color: #fff; background: #dc2626; border-color: #fff; }

/* ══ Module header — pure white (no brand gradient) ══════════════════════════════════
   The breadcrumb header drops the teal→cobalt gradient for a plain white background; the
   foreground (crumbs, icon) flips to dark for contrast. Applies to the shared shell header and
   Kriya's board header. Detail-record headers keep their own styling. */
.app-shell .app-shell__header,
.wk-board-header { background: var(--bg-surface) !important; color: var(--text) !important; }
.wk-board-header { border-bottom: 1px solid var(--border-muted); }
.app-shell:not(:has(.app-shell__bar)) .app-shell__header { border-bottom: 1px solid var(--border-muted); }
/* The chrome->content hairline is declared with the rest of the zone's geometry (see "THE
   BREADCRUMB ROW AND THE TOOLBAR ARE ONE CHROME ZONE" near the end of this sheet), so its
   tone cannot drift from the toolbar's. What stood here was `#e8eaed` -- `--border-muted`,
   one step lighter than the `--border` every list toolbar actually paints. */
/* Breadcrumb foreground → dark / neutral grey. */
/* --text, not a literal: this was #202124, and in dark the module header IS #202124, so the
   breadcrumb icon and the current crumb both painted at contrast 1.00 — invisible, not faint.
   In light --text resolves to the same #202124 this always was. */
.app-shell .app-shell__header :is(.app-shell__icon, .app-shell__crumbs .hicon-wrap, .wk-bc__crumb--current),
.wk-board-header :is(.wk-board-header__icon, .wk-board-header__title, .wk-bc__crumb--current) { color: var(--text) !important; }
/* The trail and its separators, tokenised. These were #5f6368 / #202124 / #9aa0a6 — light
   greys that made the ancestor crumbs fail contrast on a dark header (2.66:1) and the hover
   state land on the header's own colour. Each token resolves to the literal it replaces in
   light, so nothing moves there. */
.app-shell .app-shell__header .app-shell__crumbs :is(.wk-bc__crumb--muted, .wk-bc__crumb--link),
.wk-board-header :is(.wk-bc__crumb--muted, .wk-bc__crumb--link) { color: var(--text-muted) !important; }
.app-shell .app-shell__header .app-shell__crumbs .wk-bc__crumb--link:hover,
.wk-board-header .wk-bc__crumb--link:hover { color: var(--text) !important; }
.app-shell .app-shell__header .app-shell__crumbs .wk-bc__sep,
.wk-board-header .wk-bc__sep { color: var(--crumb-sep) !important; }
.app-shell .app-shell__header .app-shell__crumbs .ws-tip-i { color: #80868b !important; }.canvas .tab-content.active .wk-newmenu__pop{ overflow-y: auto; }
.canvas .tab-content.active .wk-newmenu__pop { max-height: min(62vh, 460px); }.canvas .tab-content.active .wk-newmenu__pop::-webkit-scrollbar{ width: 10px; }.canvas .tab-content.active .wk-newmenu__pop::-webkit-scrollbar-track{ background: transparent; }.canvas .tab-content.active .wk-newmenu__pop::-webkit-scrollbar-thumb{
  background: #c4c7cc; border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }.canvas .tab-content.active .wk-newmenu__pop::-webkit-scrollbar-thumb:hover{ background: #9aa0a6; background-clip: content-box; }
/* Waffle app-launcher grid — same always-visible styled scrollbar (it lives in the topbar, outside
   the .canvas scope above, so it needs its own rules). Shows a persistent classic scrollbar whenever
   the module list overflows, instead of the OS overlay scrollbar that fades away. (No standard
   scrollbar-width/color — see the note above: setting them would disable ::-webkit-scrollbar.) */
.app-launcher__grid::-webkit-scrollbar { width: 10px; }
.app-launcher__grid::-webkit-scrollbar-track { background: transparent; }
.app-launcher__grid::-webkit-scrollbar-thumb { background: #c4c7cc; border-radius: 5px; border: 2px solid transparent; background-clip: content-box; }
.app-launcher__grid::-webkit-scrollbar-thumb:hover { background: #9aa0a6; background-clip: content-box; }

/* ══ Module BODY — a white content surface on the app's grey ═════════════════════════
   The module's header + toolbar + body form one white surface. It used to meet the app header and
   the rails with no gap at all; since 2026-09-19 it is one of THREE ROUNDED PANELS on that grey
   (user direction: "keep, left rail, right rail and center page body.. all of their border
   corners curved"), so it now sits inside a `--panel-gap` seam. The margins that make the seam
   are on `.canvas-body`, with the rails; the radius is below. */
.canvas-body { background: var(--zone-content); }
.canvas .tab-content.active {
  background: var(--bg-surface) !important;
  margin: 0 !important;                    /* the SEAM is the parent's; the tab fills what is left */
  border: 0 !important;                    /* no lines between the body and the surrounding chrome */
  /* THE SQUARE CORNER IS GONE, and this is where it was enforced — `border-radius: 0 !important`,
     which is why rounding `.canvas-body > .tab-content.active` on its own rendered no curve at
     all while every stylesheet-text assertion passed. Measured in real Chrome, which is the only
     thing that can tell a rule that applies from one that is overruled.
     The reversal, recorded rather than quietly made: the 0 was written when the bands spanned the
     full width and there was "no junction left to soften". There is one again — the panel is an
     island on the grey now — so the radius comes back, once, at
     `.canvas-body > .tab-content.active` beside the two rails it has to match. Scoped there and
     not restated here because THIS selector also matches the signed-out marketing tabs, which are
     full-bleed by design. */
  /* ONE 32px SIDE GUTTER FOR THE ENTIRE MODULE BODY (left + right), and the BODY adds none of its
     own. Everything inside aligns to this one inset — header, toolbar and content alike.

     RESTORED 2026-09-25, HOURS AFTER BEING REMOVED, and both moves are recorded because the middle
     state shipped and was wrong in the other direction. The report was *"remove this extra space"*
     against Customers & Vendors, and the extra space was REAL — but it was the SECOND edge, not this
     one: `.pty-main` carried `clamp(12px, 3vw, 28px)` of its own on top of this 32, so the
     breadcrumb sat at 32 and the table at 60. Zeroing THIS made every page edge-to-edge (*"now it
     went edge to edge"*), which is not what Kriya or the settings console look like — and they are
     the reference, `.kriya-main` having carried `padding-inline: 0` and taken its inset from here
     for months.

     THE LESSON IS WHICH OF TWO INSETS TO REMOVE. When a page shows two edges, the one to delete is
     the one the BODY added, never the one the panel owns — the panel's is shared by every module and
     is what makes them look like one product. `ui/bodyGutters.spec.js` sweeps the seventeen partials
     that were adding a second. */
  padding-left: 32px !important;
  padding-right: 32px !important;
  /* AND A TOP ONE (2026-09-19, user direction: "need some space from edge to the page body to
     its contents"). It was 0 on every module body — `#kriya-tab`, `#central-tab` and friends
     zero their own padding, and `.tab-content:has(.app-shell)` zeroes the rest — which was right
     while the body ran edge-to-edge under the band and its first row continued the chrome. The
     body is a rounded PANEL now, and its first row was sitting on the curve.

     12px, NOT the 32 the sides carry, and the asymmetry is the point: a side gutter holds content
     off a vertical edge for the panel's whole height, where 32px is one column of air. The top
     holds ONE row off ONE curve — it need only clear the 12px radius — and 32px there would push
     the breadcrumb a third of the way down the header zone before anything is read.

     `!important` for the same reason the sides carry it: the module ids above zero their own
     padding, and a gutter the PANEL owns must not be defeated by the module sitting in it. The
     module MAIN stays flush inside it (`.orbit-main` and friends keep `padding-top: 0`, held by
     `features/orbit/pageChrome.spec.js`) — the panel owns the gutter, the module fills the rest. */
  padding-top: 12px !important;
}
/* Header/toolbar: no gutter of their own — they fill the module body's inner area. */
.canvas .tab-content.active .app-shell { --page-pad-x: 0px; }

/* ── Inner-scroller tabs: the scrollbar belongs at the BROWSER edge ─────────────────
   Most modules scroll on `.tab-content.active` itself, so the 32px right gutter above sits
   INSIDE the scroll container and the scrollbar hugs the window edge, as it should. But four
   tabs set `overflow: hidden` on the tab and scroll an inner element (central-main, kriya-main,
   settings-MAIN — the pane below it stopped being the scroller when `partials/config.html` added
   that wrapper, see `.settings-section` — appadmin-main). For them the tab's right padding sat
   OUTSIDE the scroller —
   the whole scroll area, scrollbar included, floated 32px in from the window edge and read as
   a rendering mistake. So: the tab gives up its RIGHT gutter, the scroller takes it as its own
   padding — same visual inset for content, scrollbar where every other app puts it.
   Selectors written out, not `:is()` — WebKit ships `:is()` with specificity ZERO (see the
   rail-ink note above), and these must beat the `!important` gutter, hence their own. */
.canvas #central-tab.tab-content.active,
.canvas #kriya-tab.tab-content.active,
.canvas #config-tab.tab-content.active,
.canvas #app-admin-tab.tab-content.active { padding-right: 0 !important; }
/* The scrollers inherit the gutter (IDs beat the (0,2,0) zero-padding group above).
   THE SAME VALUE THE ELEMENT USES ON ITS LEFT, not a hardcoded 32px (2026-09-15, user report:
   "content of body also should fit without any unnecessary margins left and right side").
   `.kriya-main` pads its inline sides with `--page-pad-x` — a responsive `clamp(12px, 4vw, 32px)`
   — and this rule pinned only the RIGHT at 32. Measured in Chrome at a 1440px viewport: left 24,
   right 32. The full-bleed toolbar bleeds by ONE value (`margin-inline: calc(-1 * --page-pad-x)`),
   so it could cancel one side and not the other: the breadcrumb spanned 32→1438, the toolbar
   32→1382 and the table 56→1406. Three edges on one page, which is exactly what "unnecessary
   margins left and right" looks like.
   With both sides on the same variable the bleed cancels symmetrically and the chrome, the
   toolbar and the content share one inset — the rule this sheet already states twice and this
   line quietly broke. */
/* RESTORED WITH THE GUTTER THEY HAND BACK (2026-09-25). They were commented out for the few hours
   the tab's side gutter was zero — correctly, since there was nothing to hand back — and they come
   back with it. That pairing is the law: *removing a body's gutter breaks every band that was
   cancelling it, and restoring one restores them too.* */
#central-tab .central-main,
#kriya-tab .kriya-main,
#app-admin-tab .appadmin-main { padding-right: 32px; }
/* …and the HEADER, which sits outside that scroller, takes the same 32 on its right. Without it
   the chrome runs to the window edge while the body stops 32px short — the asymmetry this whole
   note is about, one element along. */
/* …and its header half, back for the same reason and on the same day. */
#kriya-tab > .app-shell,
#kriya-tab > #kriya-shell { padding-right: 32px; }
/* settings-section keeps its own 24px content padding ON TOP of the tab gutter it replaces. */
/* ONE GUTTER HERE TOO (2026-09-15, user direction: "remove left and right side extra margins").
   The tab gives up its right padding so the scrollbar hugs the window edge and the scroller takes
   it — 32px, the same as the tab's left. The pane was taking 56 (its own 24 plus the 32 it
   inherited), and its own 24 on the LEFT on top of the tab's 32, so Settings' content sat 24px in
   from the breadcrumb above it and 24px further in on the right than on the left. Three edges,
   the same defect `.kriya-main` had. */
#config-tab .settings-section { padding-right: 32px; padding-left: 0; }
/* Headers that sit OUTSIDE their tab's scroller would otherwise run to the window edge. */
#central-tab #central-shell-host,
#app-admin-tab #appadmin-shell-host,
#config-tab #config-shell { padding-right: 32px; }
/* Module content area: remove the bluish-grey (--bg-app) fill AND its own side padding, so the
   content background is plain and flush to the module body's 32px inner area. */
/* WRITTEN OUT, NOT `:is()` — AND THIS IS THE RULE THAT DELIVERS THE ONE-EDGE LAW (2026-09-15,
   user direction: "fix table width and page body and all"). It is what zeroes each module body's
   own side padding so the chrome and the content share one edge.

   Behind `:is()` it could not be relied on: WebKit prices `:is()` at specificity ZERO, so in
   Safari this resolved at (0,1,0) — tied with every module's own `.X-main { padding: 20px 24px }`
   — and document order decided the winner. Those module rules are INJECTED AT RUNTIME by each
   controller, so the order is whichever module the reader happened to open first: the same page
   could show one gutter or two depending on their navigation, and neither the sheet nor any
   jsdom-based spec would show it. Nine selectors is the price of a law that actually holds. */
.canvas-body .orbit-main,
.canvas-body .hive-main,
.canvas-body .kriya-main,
.canvas-body .echo-main,
.canvas-body .cdp-main,
.canvas-body .artha-main,
.canvas-body .lens-main,
.canvas-body .appadmin-main,
.canvas-body .pty-main,
.canvas-body .central-main {
  background: transparent;
  padding-left: 0; padding-right: 0;
  --page-pad-x: 0px;
}
/* Keep the shell chrome INSIDE the body — drop its full-bleed break-out (negative margins) so the
   header/toolbar/filterbar span the body width without overflowing the rounded corner or forcing a
   horizontal scrollbar; their own padding still insets the content. */
.canvas .tab-content.active :is(.app-shell__header, .app-shell__bar, .app-shell__filterbar) {
  margin-left: 0 !important; margin-right: 0 !important;
}
/* The header's top-LEFT corner follows the body's rounding (top-right stays square, flush to edge). */
.canvas .tab-content.active .app-shell__header { border-top-left-radius: 16px; border-top-right-radius: 0; }
.app-shell .app-shell__bar .wk-iconbtn--close .wk-iconbtn__cap { color: #fff; }
/* Segmented toggles (view/entity switchers) read on the gradient. */
/* Token ink, not gradient-era white: these rules dressed the toggle for the blue header band,
   and the toolbar surface they actually sit on is WHITE in light — white-on-white controls. */
.app-shell .app-shell__bar .wk-toggle { border-color: var(--border); background: var(--bg-muted); }
.app-shell .app-shell__bar .wk-toggle button { color: var(--text-subtle); }
.app-shell .app-shell__bar .wk-toggle button:hover { background: var(--bg-hover, var(--bg-muted)); color: var(--text); }
.app-shell .app-shell__bar .wk-toggle button.is-active,
.app-shell .app-shell__bar .wk-toggle button[aria-selected="true"] { background: var(--bg-surface); color: #0f766e; box-shadow: none; }
.app-shell .app-shell__bar .wk-search { background: var(--bg-surface); color: var(--text, #0f172a); border-color: var(--border); }
/* Dropdowns/popovers open on a white surface — reset the inherited white text. */
.app-shell .wk-newmenu__pop,
.app-shell .wk-hdrmenu__pop,
.app-shell .wk-sortmenu__pop,
.app-shell .wk-groupmenu__pop { color: var(--text, #0f172a); }
/* Filter zone highlight — soft neutral grey (matches Kriya's Filters button, caret + panel). */
.app-shell .app-shell__bar .wk-fp .wk-iconbtn.is-on { color: #334155; background: #e5e8ee; border-color: #c6ccd7; }
.app-shell .app-shell__bar .wk-fp .wk-iconbtn.is-on:hover { color: #334155; background: #dde1e9; border-color: #b9c0cc; }
.app-shell .app-shell__filterbar { background: var(--bg-muted); border-bottom-color: var(--border-muted); }

/* GLOBAL: the toolbar band is white (--app-toolbar-bg) — revert its foreground from
   the header's white-on-gradient chrome back to dark for contrast on white. The
   HEADER band keeps the gradient + white foreground above. */
.app-shell .app-shell__bar { color: var(--text, #0f172a); border-bottom-color: var(--border); }
.app-shell .app-shell__bar .wk-iconbtn { color: var(--text-subtle); background: var(--bg-surface); border-color: var(--border); }
.app-shell .app-shell__bar .wk-iconbtn:hover { color: var(--text); background: var(--bg-hover); border-color: var(--border-strong); }
.app-shell .app-shell__bar .wk-iconbtn .wk-iconbtn__cap { color: var(--text-subtle); }
.app-shell .app-shell__bar .wk-iconbtn.is-on { color: var(--primary); background: var(--accent-soft, #eef2ff); border-color: var(--primary); }
.app-shell .app-shell__bar .wk-iconbtn.is-on .wk-iconbtn__cap { color: var(--primary); }
.app-shell .app-shell__bar .wk-iconbtn--primary { color: var(--primary-ink, #fff); background: var(--primary); border-color: var(--primary); }
.app-shell .app-shell__bar .wk-iconbtn--primary .wk-iconbtn__cap { color: #fff; }
.app-shell .app-shell__bar .wk-iconbtn--close { color: #dc2626; background: rgba(220,38,38,.07); border-color: rgba(220,38,38,.28); }
.app-shell .app-shell__bar .wk-iconbtn--close:hover { color: #fff; background: #dc2626; border-color: #dc2626; }
.app-shell .app-shell__bar .wk-iconbtn--close .wk-iconbtn__cap { color: inherit; }
.app-shell .app-shell__bar .wk-toggle { background: var(--bg-muted); border-color: var(--border); }
.app-shell .app-shell__bar .wk-toggle button { color: var(--text-subtle); }
.app-shell .app-shell__bar .wk-toggle button.is-active,
.app-shell .app-shell__bar .wk-toggle button[aria-selected="true"] { background: var(--bg-surface); color: var(--text); }
.app-shell .app-shell__bar .wk-search { background: var(--bg-surface); color: var(--text); border-color: var(--border); }

/* ── Scroll behaviour (GLOBAL) ─────────────────────────────────────────────────
   The page HEADER stays pinned; the toolbar + filter bar scroll away with the
   content. The left rail also stays pinned with its own internal scroll. */
.app-shell .app-shell__header { position: sticky; top: 0; z-index: 25; }
.app-shell .app-shell__bar { position: static; z-index: auto; }
#kriya-tab .wk-board-header { position: sticky; top: 0; z-index: 25; }
#kriya-tab .wk-bar--tasks,
#kriya-tab .wk-dash-filters,
.lk-bar__row { position: static; z-index: auto; }
/* Kriya left rail: pinned below the sticky header, with its own scroll — it does
   NOT move with the page. top = header height; height leaves room for the topbar
   + the scroll container's top inset (nudge the 128px if it over/undershoots). */
#kriya-tab .wk-rail { position: sticky; top: 60px; align-self: flex-start;
  height: calc(100vh - 128px); overflow-y: auto; }

/* "+ New" create button — a distinct TEAL accent so it's clearly different from
   the cobalt "active/selected" chips (active icon = cobalt, create = teal). */
.app-shell .app-shell__header .wk-iconbtn--primary,
.app-shell .app-shell__bar .wk-iconbtn--primary,
#kriya-tab .wk-board-header .wk-iconbtn--primary,
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary { color: #fff; background: #0d9488; border-color: #0d9488; }
.app-shell .app-shell__header .wk-iconbtn--primary:hover,
.app-shell .app-shell__bar .wk-iconbtn--primary:hover,
#kriya-tab .wk-board-header .wk-iconbtn--primary:hover,
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary:hover { color: #fff; background: #0f766e; border-color: #0f766e; filter: none; }
.app-shell .app-shell__header .wk-iconbtn--primary .wk-iconbtn__cap,
.app-shell .app-shell__bar .wk-iconbtn--primary .wk-iconbtn__cap,
#kriya-tab .wk-board-header .wk-iconbtn--primary .wk-iconbtn__cap,
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary .wk-iconbtn__cap, .lk-bar__row .wk-iconbtn--primary .wk-iconbtn__cap { color: #fff; }

/* ── Kriya (work) chrome → SAME global gradient theme ──────────────────────────
   Kriya predates .app-shell and uses its own .wk-board-header (header) +
   .wk-bar--tasks (toolbar) bands, so the .app-shell rules above don't reach it.
   Point those two bands at the identical --app-header-bg tokens + white chrome. */
.wk-board-header { background: var(--app-header-bg); color: var(--app-header-text); }
#kriya-tab .wk-bar--tasks { background: var(--app-toolbar-bg); color: var(--app-header-text); }
.wk-board-header { border-bottom: 0; }
#kriya-tab .wk-bar--tasks { border-bottom-color: rgba(255, 255, 255, 0.22); }
/* Title + breadcrumb white. */
.wk-board-header__icon { color: var(--app-header-text); opacity: 0.9; }
.wk-board-header__title { color: var(--app-header-text); }
.wk-board-header .wk-bc__crumb--current { color: var(--app-header-text); }
.wk-board-header .wk-bc__crumb--muted,
.wk-board-header .wk-bc__crumb--link { color: rgba(255, 255, 255, 0.82); }
.wk-board-header .wk-bc__crumb--link:hover { color: #fff; }
.wk-board-header .wk-bc__sep { color: rgba(255, 255, 255, 0.55); }
/* Icon buttons → white chips (header + toolbar). */
.wk-board-header .wk-iconbtn,
#kriya-tab .wk-bar--tasks .wk-iconbtn {
  color: #fff; background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.30); }
.wk-board-header .wk-iconbtn:hover,
#kriya-tab .wk-bar--tasks .wk-iconbtn:hover {
  color: #fff; background: rgba(255, 255, 255, 0.22); border-color: rgba(255, 255, 255, 0.55); }
.wk-board-header .wk-iconbtn .wk-iconbtn__cap,
#kriya-tab .wk-bar--tasks .wk-iconbtn .wk-iconbtn__cap { color: #fff; }
.wk-board-header .wk-iconbtn.is-on,
#kriya-tab .wk-bar--tasks .wk-iconbtn.is-on { color: #0f766e; background: var(--bg-surface); border-color: #fff; }
.wk-board-header .wk-iconbtn.is-on .wk-iconbtn__cap,
#kriya-tab .wk-bar--tasks .wk-iconbtn.is-on .wk-iconbtn__cap { color: #0f766e; }
/* Primary "+ New" CTA → white with cobalt ink. */
.wk-board-header .wk-iconbtn--primary,
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary { color: var(--primary); background: var(--bg-surface); border-color: #fff; }
.wk-board-header .wk-iconbtn--primary .wk-iconbtn__cap,
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary .wk-iconbtn__cap { color: var(--primary); }
/* View selector + segmented toggles read on the gradient. */
#kriya-tab .wk-bar--tasks .wk-viewsel__btn,
#kriya-tab .wk-bar--tasks .wk-toggle button {
  color: rgba(255, 255, 255, 0.9); background: rgba(255, 255, 255, 0.10); border-color: rgba(255, 255, 255, 0.30); }
#kriya-tab .wk-bar--tasks .wk-viewsel__btn:hover,
#kriya-tab .wk-bar--tasks .wk-toggle button:hover { color: #fff; background: rgba(255, 255, 255, 0.2); border-color: rgba(255, 255, 255, 0.5); }
#kriya-tab .wk-bar--tasks .wk-toggle button.is-active,
#kriya-tab .wk-bar--tasks .wk-toggle button[aria-selected="true"] { background: var(--bg-surface); color: #0f766e; }
/* Filter selects / inputs + search stay light fields with dark text. */
#kriya-tab .wk-bar--tasks .wk-filters select,
#kriya-tab .wk-bar--tasks .wk-filters input:not([type="checkbox"]):not([type="radio"]),
#kriya-tab .wk-bar--tasks .wk-search { background: var(--bg-surface); color: var(--text, #0f172a); border-color: rgba(255, 255, 255, 0.5); }
/* Dropdowns/popovers inside the chrome reset to dark text. */
.wk-board-header .wk-newmenu__pop,
.wk-board-header .wk-hdrmenu__pop,
#kriya-tab .wk-bar--tasks .wk-hdrmenu__pop,
#kriya-tab .wk-bar--tasks .wk-sortmenu__pop,
#kriya-tab .wk-bar--tasks .wk-groupmenu__pop { color: var(--text, #0f172a); }

/* Kriya in-body left rail (toggled by the toolbar hamburger) — the "Config"
   tools (KPIs / AI agents / Lists / Configurations) moved off the header. */
/* Fill the scroll viewport when the rail layout is present, so the layout — and
   its stretched rail — reach the page bottom regardless of content height. */
#kriya-tab #work-content:has(> .wk-layout) { display: flex; flex-direction: column; min-height: 100%; }
#kriya-tab .wk-layout { flex: 1 1 auto; display: flex; flex-direction: row; align-items: stretch; gap: 0; }
/* Content column (right of the rail) holds the toolbar + filter bar + body, so
   they all resize when the rail expands/collapses. */
/* PAGE BODY = ONE component: the content column is a single white canvas —
   rounded + softly raised on the lavender app bg — so the toolbar, filters and
   content read as one surface (not scattered cards), framed by the dark rail and
   the gradient header. */
/* Flat content column: no own background colour, border, shadow or side padding — the toolbar,
   filters and body sit directly on the white module body and align to the same 32px gutter as the
   module header (their inset comes only from the module body's gutter, nothing here). */
#kriya-tab .wk-content-col { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column;
  background: transparent; border: 0; border-radius: 0; box-shadow: none; }
#kriya-tab .wk-content-col > #wk-tasks-body { flex: 1 1 auto; min-width: 0; padding: 8px 0 28px; }
#kriya-tab .wk-content-col > .wk-bar--tasks,
#kriya-tab .wk-content-col > .wk-filters,
#kriya-tab .wk-content-col > #wk-filter-bar { margin-left: 0; margin-right: 0; padding-left: 0; padding-right: 0; }
/* Drop the scroll container's bottom padding when the rail is present so the rail
   reaches the very bottom; the content keeps its own breathing room instead. */
#kriya-tab .kriya-main:has(#work-content > .wk-layout) { padding-bottom: 0; }
/* Left rail — a DARK nav sidebar that matches the app topbar (deep teal-navy
   #0b2740) and the teal→cobalt brand gradient, framing the light content. Crisp
   light text (not dull), soft white hover, and a bright CYAN accent for the
   selected item (pill + accent bar), pulling from the brand's cyan (#22d3ee). */
#kriya-tab .wk-rail { flex: 0 0 178px; width: 178px; background: #0b2740;
  border: 0; border-right: 1px solid rgba(255, 255, 255, 0.10); padding: 12px 8px 18px; }
#kriya-tab .wk-rail[hidden] { display: none; }
#kriya-tab .wk-rail__head { font-size: .66rem; text-transform: uppercase; letter-spacing: .07em; color: rgba(255, 255, 255, 0.46); font-weight: 700; margin: 15px 8px 6px; }
#kriya-tab .wk-rail__head:first-child { margin-top: 2px; }
#kriya-tab .wk-rail__item { position: relative; display: flex; align-items: center; gap: 10px; width: 100%; border: 0; background: none; font: inherit; font-size: .87rem; color: rgba(255, 255, 255, 0.80); text-align: left; padding: 7px 10px; border-radius: 8px; cursor: pointer; transition: background .12s, color .12s; }
#kriya-tab .wk-rail__item:hover { background: rgba(255, 255, 255, 0.09); color: #fff; }
#kriya-tab .wk-rail__item.is-active { background: rgba(34, 211, 238, 0.15); color: #67e8f9; font-weight: 600; }
/* (Kriya's private cyan ::before indicator retired 2026-08-20: the shared selected-tab rule
   carries the cyan bar for EVERY rail now, and this rule's ::before collided with the tab's
   top wing — an ID-scoped property-merge that rendered as a clipped nub.) */
#kriya-tab .wk-rail__item .hicon { width: 16px; height: 16px; }
#kriya-tab .wk-rail__empty { padding: 7px 10px; font-size: .82rem; color: rgba(255, 255, 255, 0.55); }
/* Config hierarchy: an active section's sub-sections, nested + indented under it (a guide line on
   the left ties them to the parent). The active sub gets the cyan pill like a rail item. */
#kriya-tab .wk-rail__subs { display: flex; flex-direction: column; gap: 1px; margin: 2px 0 6px 20px; padding-left: 9px; border-left: 1px solid rgba(255, 255, 255, 0.12); }
#kriya-tab .wk-rail__sub { display: flex; align-items: center; gap: 9px; width: 100%; border: 0; background: none; font: inherit; font-size: .82rem; color: rgba(255, 255, 255, 0.66); text-align: left; padding: 5px 9px; border-radius: 7px; cursor: pointer; transition: background .12s, color .12s; }
#kriya-tab .wk-rail__sub:hover { background: rgba(255, 255, 255, 0.08); color: #fff; }
#kriya-tab .wk-rail__sub.is-active { background: rgba(34, 211, 238, 0.14); color: #67e8f9; font-weight: 600; }
#kriya-tab .wk-rail__sub .hicon { width: 14px; height: 14px; }
#kriya-tab .wk-rail__sub .hicon-wrap { flex: none; display: inline-flex; opacity: .78; }
#kriya-tab .wk-rail__sub.is-active .hicon-wrap { opacity: 1; }
/* "default" tag on a dashboard rail item. */
#kriya-tab .wk-rail__tag { margin-left: 6px; font-size: .58rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: rgba(255, 255, 255, 0.55); border: 1px solid rgba(255, 255, 255, 0.22); border-radius: 4px; padding: 0 4px; }
#kriya-tab .wk-rail__item.is-active .wk-rail__tag { color: #67e8f9; border-color: rgba(103, 232, 249, 0.4); }
/* "+ New dashboard" rail button — quieter than the dashboard items. */
#kriya-tab .wk-rail__newdash { color: rgba(255, 255, 255, 0.6); font-size: .82rem; margin-bottom: 4px; }
#kriya-tab .wk-rail__newdash:hover { color: #fff; }
/* Dashboard rail item + its "⋯" options kebab on one row. */
#kriya-tab .wk-rail__row { display: flex; align-items: center; }
#kriya-tab .wk-rail__row .wk-rail__item { flex: 1 1 auto; min-width: 0; }
#kriya-tab .wk-rail__row .wk-rail__item > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#kriya-tab .wk-rail__kebab { flex: none; border: 0; background: none; color: rgba(255, 255, 255, 0.55); cursor: pointer; font-size: 17px; line-height: 1; padding: 4px 7px; margin-right: 2px; border-radius: 6px; opacity: 0; transition: opacity .12s, background .12s, color .12s; }
#kriya-tab .wk-rail__row:hover .wk-rail__kebab, #kriya-tab .wk-rail__row.is-active .wk-rail__kebab { opacity: 1; }
#kriya-tab .wk-rail__kebab:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
#kriya-tab .wk-rail__plus { display: inline-flex; width: 16px; justify-content: center; font-size: 1rem; line-height: 1; }
/* Setup rail — a section's collapse/expand chevron (right of the section row). Points right when
   collapsed, rotates down when open. Always visible so the section reads as collapsible. */
#kriya-tab .wk-rail__chev { flex: none; display: inline-flex; align-items: center; justify-content: center; border: 0; background: none; color: rgba(255, 255, 255, 0.5); cursor: pointer; padding: 4px 6px; margin-right: 2px; border-radius: 6px; transition: background .12s, color .12s, transform .15s; transform: rotate(-90deg); }
#kriya-tab .wk-rail__chev.is-open { transform: rotate(0deg); }
#kriya-tab .wk-rail__chev:hover { background: rgba(255, 255, 255, 0.12); color: #fff; }
#kriya-tab .wk-rail__row.is-active .wk-rail__chev { color: rgba(103, 232, 249, 0.85); }
/* Connect — a collapsible group: it carries .wk-rail__head (so it inherits the head colour in BOTH
   the dark in-body rail and the promoted light "Gmail" rail, which recolours [class*="__head"]);
   this rule only adds the flex layout to seat the chevron on the right. */
#kriya-tab .wk-rail__grouphead { display: flex; align-items: center; justify-content: space-between; }
/* It is a <button> now (the whole header toggles, not just the arrow), so it has to shed the
   control defaults a bare .wk-rail__head never had to think about, and gain the hover/focus
   the label was missing — a row that toggles has to look like it does. */
#kriya-tab button.wk-rail__grouphead { width: 100%; border: 0; background: none; font: inherit;
  font-size: .66rem; text-transform: uppercase; letter-spacing: .07em; font-weight: 700;
  text-align: left; cursor: pointer; padding: 4px 8px; border-radius: 7px; }
/* Mixed from currentColor, not a fixed white: this rail renders dark in the module body and
   LIGHT once promoted to the second rail, and a hardcoded white wash is invisible on one of
   them. The text colour already tracks the surface, so deriving from it works in both. */
#kriya-tab button.wk-rail__grouphead:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
#kriya-tab button.wk-rail__grouphead:focus-visible { outline: 2px solid currentColor; outline-offset: -2px; }
/* The chevron inside it is a <span> (a button cannot nest in a button), so the rotation and
   colour that the standalone button carries have to reach it here too. */
#kriya-tab .wk-rail__grouphead .wk-rail__chev { margin-right: -2px; pointer-events: none; }
#kriya-tab button.wk-rail__grouphead:hover .wk-rail__chev { color: inherit; }
#kriya-tab .wk-rail__groupbody { display: flex; flex-direction: column; gap: 1px; }#kriya-tab .wk-burger { width: 16px; height: 16px; display: block; }
/* Entity-dashboard links row atop the Dashboards section. */
#kriya-tab .wk-dashlinks { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
#kriya-tab .wk-dashlinks__lbl { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; color: var(--text-faint); margin-right: 4px; }

/* View switcher rendered as radio-button tabs: a radio dot + label per option,
   single-select, active = filled radio inside a white pill (on the gradient bar). */
#kriya-tab .wk-bar--tasks .wk-toggle--views {
  background: transparent; border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 8px; gap: 2px; padding: 2px; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button {
  width: auto; height: 26px; padding: 0 11px 0 9px; gap: 7px; flex-direction: row; align-items: center;
  color: rgba(255, 255, 255, 0.92); background: transparent; border-radius: 6px; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button > .hicon-wrap { display: none; } /* radio dot replaces the icon */
#kriya-tab .wk-bar--tasks .wk-toggle--views button > .wk-iconbtn__cap { font-size: 12px; font-weight: 600; letter-spacing: .01em; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button::before {
  content: ''; flex: none; width: 12px; height: 12px; box-sizing: border-box;
  border: 1.5px solid currentColor; border-radius: 50%; background: transparent; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active { background: var(--bg-surface); color: #0f766e; box-shadow: none; }
#kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active::before {
  border-color: #0f766e; background: #0f766e; box-shadow: inset 0 0 0 2px #fff; } /* filled radio */

/* Kriya TOOLBAR → plain white (matches every module; the header keeps the gradient).
   Reverts the white-on-gradient chrome to dark foreground for contrast on white. */
#kriya-tab .wk-bar--tasks, .lk-bar__row { background: var(--bg-surface); color: var(--text); border-bottom-color: var(--border); }
#kriya-tab .wk-bar--tasks .wk-iconbtn, .lk-bar__row .wk-iconbtn { color: var(--text-subtle); background: var(--bg-surface); border-color: var(--border); }
#kriya-tab .wk-bar--tasks .wk-iconbtn:hover, .lk-bar__row .wk-iconbtn:hover { color: var(--text); background: var(--bg-hover); border-color: var(--border-strong); }
#kriya-tab .wk-bar--tasks .wk-iconbtn .wk-iconbtn__cap, .lk-bar__row .wk-iconbtn .wk-iconbtn__cap { color: var(--text-subtle); }
#kriya-tab .wk-bar--tasks .wk-iconbtn.is-on, .lk-bar__row .wk-iconbtn.is-on { color: var(--primary); background: var(--accent-soft, #eef2ff); border-color: var(--primary); }
#kriya-tab .wk-bar--tasks .wk-iconbtn.is-on .wk-iconbtn__cap, .lk-bar__row .wk-iconbtn.is-on .wk-iconbtn__cap { color: var(--primary); }
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary, .lk-bar__row .wk-iconbtn--primary { color: var(--primary-ink, #fff); background: var(--primary); border-color: var(--primary); }
#kriya-tab .wk-bar--tasks .wk-iconbtn--primary .wk-iconbtn__cap { color: #fff; }

/* ── Filters button = soft amber/honey in EVERY toolbar ──────────────────────────
   The neutral toolbar-icon overrides above (and their app-shell twins) out-specify
   the base .wk-fp colours, so the Filters button needs matching-specificity rules to
   stay amber — highlighted, and matching its own caret + expanded panel. */
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn ,
.lk-bar__row .wk-fp .wk-iconbtn {
  background: var(--bg-muted); border-color: var(--border-muted); color: var(--text-muted);
}
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn:hover,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn:hover ,
.lk-bar__row .wk-fp .wk-iconbtn:hover {
  background: #e9edf2; border-color: #cfd5df; color: #334155;
}
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn.is-on,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn.is-on ,
.lk-bar__row .wk-fp .wk-iconbtn.is-on {
  background: #e5e8ee; border-color: #c6ccd7; color: #334155;
}
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn.is-on:hover,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn.is-on:hover ,
.lk-bar__row .wk-fp .wk-iconbtn.is-on:hover {
  background: #dde1e9; border-color: #b9c0cc; color: #334155;
}
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn .wk-iconbtn__cap,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn .wk-iconbtn__cap ,
.lk-bar__row .wk-fp .wk-iconbtn .wk-iconbtn__cap { color: var(--text-muted); }
#kriya-tab .wk-bar--tasks .wk-fp .wk-iconbtn.is-on .wk-iconbtn__cap,
.app-shell .app-shell__bar .wk-fp .wk-iconbtn.is-on .wk-iconbtn__cap ,
.lk-bar__row .wk-fp .wk-iconbtn.is-on .wk-iconbtn__cap { color: var(--text); }
#kriya-tab .wk-bar--tasks .wk-viewsel__btn { color: var(--text-subtle); background: var(--bg-surface); border-color: var(--border); }
#kriya-tab .wk-bar--tasks .wk-filters select,
#kriya-tab .wk-bar--tasks .wk-filters input:not([type="checkbox"]):not([type="radio"]),
#kriya-tab .wk-bar--tasks .wk-search,
.lk-bar__row .wk-search { background: var(--bg-surface); color: var(--text); border-color: var(--border); }
/* Project filter pinned on the toolbar's left, after the view/data-shaping icons. */
.wk-bar--tasks .wk-bar-projfilter { max-width: 190px; width: auto; }
/* View-switcher radio tabs on the cream bar. */
#kriya-tab .wk-bar--tasks .wk-toggle--views { border-color: var(--border); background: var(--bg-surface); }
#kriya-tab .wk-bar--tasks .wk-toggle--views button { color: var(--text-subtle); }
#kriya-tab .wk-bar--tasks .wk-toggle--views button:hover { background: var(--bg-hover); color: var(--text); }
#kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active { background: var(--primary); color: var(--primary-ink, #fff); }
#kriya-tab .wk-bar--tasks .wk-toggle--views button.is-active::before { border-color: #fff; background: var(--bg-surface); box-shadow: inset 0 0 0 2px var(--primary); }

/* Flush-layout modules zero their tab's `--page-gutter` padding (rail/flex scaffolds) — but the
   full-bleed `.app-shell` bands RELY on that padding to cancel against, so without it the header +
   toolbar CONTENT renders flush to the edges (no left/right side spaces). Re-apply the gutter on
   each such shell host: the toolbar content then insets like Wiki/Kriya/Central while the band
   background still bleeds edge-to-edge (host padding == the band's `--page-pad-x`). These modules'
   bodies are ALREADY inset by the same `var(--page-gutter)` via the `.canvas-body :is(.orbit-main,
   .hive-main, …)` rule above — the toolbar was the only flush part — so toolbar + body now align.
   `#central-shell-host` carries the same fix inline near its module block.
   OMITTED (handled/deferred): the full-bleed canvas/browser modules (Flux node-editor, Forge IDE,
   Drive file browser) whose bodies are edge-to-edge by design; and the two-pane/rail bodies
   (Settings, Docs, Forms) whose flush side panes would need a coordinated tab-level inset. */
/* REMOVED (2026-08-19), with the `.smz-has-listbar` override that existed only to undo it.
   The rule gave these five shell hosts their own `var(--page-gutter)` on the reading that
   "these modules' bodies are ALREADY inset by the same var(--page-gutter) … so toolbar +
   body now align". Measured in a real engine against the real sheet, they did not: the
   body's gutter comes from `.canvas-body :is(.artha-main, …)` and applies INSIDE the tab,
   while this one applied to the shell host as well — so the breadcrumb and the toolbar sat
   32px INSIDE the content beneath them, and the chrome's hairline stopped 32px short of the
   page edges the content reached. Deals looked right only because
   `.tab-content.smz-has-listbar > #artha-shell, … { padding-inline: 0 }` cancelled it, and
   that cancellation was gated on the page having a shared list toolbar — which every
   `VIEW_DEFS`-driven page in Artha (~100 views) and most of Orbit's do not.
   With both gone, Artha and Deals measure IDENTICALLY: crumb and content on one edge, one
   hairline spanning the same width. */

/* Grouped "sections" dropdown for the toolbar (appShell.js sectionMenu) — for modules with many
   sections. A compact trigger showing the active section + a grouped popup of all sections. */
.wk-secmenu { position: relative; display: inline-block; }
.wk-secmenu__btn { display: inline-flex; align-items: center; gap: 6px; height: 30px; box-sizing: border-box; padding: 0 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); cursor: pointer; font: inherit; font-size: 0.82rem; }
.wk-secmenu__btn:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.wk-secmenu__btn .hicon { width: 16px; height: 16px; }
.wk-secmenu__cur { font-weight: 600; max-width: 13rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-secmenu__chev { color: var(--text-faint); font-size: 11px; }
.wk-secmenu__pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 50; min-width: 224px; max-height: min(72vh, 560px); overflow: auto; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 32px rgba(15, 23, 42, .18); padding: 6px; display: flex; flex-direction: column; gap: 1px; }
.wk-secmenu__pop[hidden] { display: none; }
.wk-secmenu__head { font-size: 0.62rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint); padding: 8px 10px 3px; }
.wk-secmenu__item { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; border: 0; background: none; border-radius: 7px; padding: 7px 10px; font: inherit; font-size: 0.86rem; color: var(--text); cursor: pointer; white-space: nowrap; }
.wk-secmenu__item:hover { background: var(--bg-hover); }
.wk-secmenu__item.is-active { background: var(--accent-soft, #eef2ff); color: var(--primary); font-weight: 600; }
.wk-secmenu__item .hicon { width: 16px; height: 16px; flex: none; }
/* One consistent 30px control height across the shell toolbar (mirrors Kriya). */
.app-shell__bar .ghost-button,
.app-shell__bar .primary-button,
.app-shell__bar .wk-iconbtn,
.app-shell__bar .wk-toggle-btn,
.app-shell__bar .wk-viewsel__btn,
.app-shell__bar .wk-toggle,
.app-shell__bar .wk-search {
  height: 30px; box-sizing: border-box; display: inline-flex; align-items: center;
  padding-top: 0; padding-bottom: 0; line-height: 1; margin-top: 0;
}
.app-shell__bar .wk-toggle { padding: 0 3px; }
.app-shell__bar .wk-toggle button { width: 30px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
/* Shell icon buttons inherit the global 30×30 / 16px .wk-iconbtn standard (matching the top
   application header) — no shell-specific size pin, so it stays uniform everywhere. */
/* The filter band's geometry, surface, stacked rows and highlighted "Clear all" used to be
   declared a SECOND time here for `.app-shell__filterbar`. Two hand-maintained copies of one
   look is how they drift, and they had: the shared set grew `--lk-band-inset` (a floor under
   the page gutter, because `--page-pad-x` is DEFINED as 0 on a module body, so the `8px`
   fallback never fires and the first filter sat hard against the band's left edge) and this
   copy did not. `.app-shell__filterbar` is listed IN the shared rules now — see the "list
   toolbar band, shared verbatim" block above. */
/* Instant tooltips on shell header actions (same as .wk-bar tooltips). */
.app-shell__actions [data-rail-tip],
.wk-hdr-actions [data-rail-tip] { position: relative; }

/* Comment box moved to the top of the timeline (newest-first). */
.wk-comment-box--top { position: static; border-top: 0; padding-top: 0; margin: 0 0 10px; }

/* Dependencies */
.wk-task__chip--blocked { color: var(--danger); }
.wk-deps { display: flex; flex-direction: column; gap: 8px; margin-bottom: 6px; }
.wk-deps__group { display: flex; flex-direction: column; gap: 4px; }
.wk-deps__lbl { font-size: 11px; font-weight: 700; color: var(--text-subtle); }
.wk-dep { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.wk-dep__title { flex: 1; min-width: 0; background: none; border: 0; text-align: left; cursor: pointer; color: var(--text); font: inherit; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-dep__title--locked { cursor: default; color: var(--text-subtle); }
.wk-dep__proj { font-size: 11px; color: var(--text-faint); flex: none; }
.wk-dep__x { border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 11px; flex: none; }
.wk-dep__x:hover { color: var(--danger); }
.wk-dep__req { flex: none; }
.wk-dep-results { display: flex; flex-direction: column; gap: 2px; max-height: 260px; overflow-y: auto; margin: 4px 0; }
.wk-dep-result { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; font: inherit; font-size: 13px; color: var(--text); background: none; border: 0; border-radius: 7px; padding: 7px 9px; cursor: pointer; }
.wk-dep-result:hover { background: var(--bg-hover); }

/* Task attachments */
.wk-files { display: flex; flex-direction: column; gap: 4px; margin-bottom: 6px; }
.wk-file { display: flex; align-items: center; gap: 8px; font-size: 13px; }
.wk-file__name { flex: 1; min-width: 0; color: var(--text); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wk-file__name:hover { text-decoration: underline; }
.wk-file__size { font-size: 11px; color: var(--text-faint); flex: none; }
.wk-file__x { border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: 11px; flex: none; }
.wk-file__x:hover { color: var(--danger); }

/* Minimal-width filters: small font + padding so they don't eat horizontal space. */
.wk-filters { gap: 6px; width: 100%; }
.wk-filters select { font-size: 11px; padding: 4px 6px; max-width: 132px; }
.wk-filters input:not([type="checkbox"]):not([type="radio"]) { font-size: 11px; padding: 4px 8px; min-width: 130px; flex: 1 1 150px; max-width: 240px; }
.wk-toggle button { min-width: 30px; }

/* Per-row status dropdown (replaces the checkbox). Native arrow kept for affordance. */
.wk-statesel {
  font: inherit; font-size: 10px; font-weight: 700; border: 1px solid var(--border);
  border-radius: 6px; padding: 2px 4px; background: var(--bg-surface); color: var(--text-subtle);
  cursor: pointer; max-width: 116px; flex: none;
}
.wk-statesel--in_progress { color: var(--primary); border-color: rgba(30,64,175,.4); }
.wk-statesel--in_review { color: var(--warn); border-color: rgba(180,83,9,.4); }
.wk-statesel--done { color: var(--success); border-color: rgba(22,101,52,.4); }
.wk-statesel--head { font-size: 12px; padding: 4px 8px; max-width: 150px; }
.wk-statesel--todo { color: var(--text-muted, #64748b); }
/* Status shown in the task Details section as a full-width dropdown button with bold, colour-coded
   text (the wk-statesel--<state> modifier tints it). Carries its own chevron (the base rule's
   background shorthand strips the global one). */
.wk-statesel--detail {
  width: 100%; max-width: none; flex: 1 1 auto; font-size: 13px; font-weight: 700;
  padding: 8px 32px 8px 12px; border-radius: 8px; background-color: var(--bg-surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 11px center;
}
.wk-drawer__head { gap: 8px; }

/* Responsive: 3 cols → 2 on medium, 1 on narrow (the drawer is wide but bounded) */
@media (max-width: 1100px) {
  .wk-fieldgrid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 720px) {
  .wk-fieldgrid { grid-template-columns: 1fr; }
}

/* ── Header consolidation: page title + "Help docs" live in the topbar ─────── */

/* Page title in the topbar (mirrored from the active page's heading). */
/* The left-bar expand/collapse toggle now lives in the topbar, where the page
   title used to sit — borderless so it reads as a quiet control by the search. */
.topbar-panel-toggle {
  flex-shrink: 0;
  border-color: transparent;
  background: transparent;
  color: var(--text-subtle);
  font-size: 1.05rem;
}
.topbar-panel-toggle:hover {
  background: var(--bg-muted);
  color: var(--text);
  border-color: var(--border);
}
/* Context-aware docs link in the topbar (subtle pill, like the old ref link). */
.topbar-help {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  font-size: 1rem;          /* icon-only */
  line-height: 1;
  color: var(--text-subtle);
  text-decoration: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.topbar-help:hover { background: var(--bg-muted); color: var(--text); border-color: var(--border-strong); }

/* Settings + Report-an-issue icon buttons in the header's right cluster (mirror .topbar-help). */
.topbar-iconbtn { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px;
  color: var(--text-subtle); background: none; border: 1px solid var(--border); border-radius: 999px; cursor: pointer;
  text-decoration: none; font-size: 0.9rem; line-height: 1; transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease; }
.topbar-iconbtn:hover { background: var(--bg-muted); color: var(--text); border-color: var(--border-strong); }
.topbar-iconbtn[hidden] { display: none; } /* Upgrade (Plans) is hidden from non-admin org members */
.topbar-iconbtn__ico { display: inline-flex; align-items: center; justify-content: center; }
/* App header (topbar) icons — borderless (report / settings / search / help / notifications / account). */
.topbar .topbar-iconbtn,
.topbar .topbar-search-toggle,
.topbar .topbar-help,
.topbar .tb-notif-button,
.topbar .tb-profile-button { border: 0; }
.topbar .topbar-iconbtn:hover,
.topbar .topbar-search-toggle:hover,
.topbar .topbar-help:hover,
.topbar .tb-notif-button:hover,
.topbar .tb-profile-button:hover { border-color: transparent; }
/* …except the AI agent (Beta Agent) icon, which keeps its border circle. */
.topbar .topbar-iconbtn.tb-smai { border: 1px solid var(--border); }
.topbar .topbar-iconbtn.tb-smai:hover { border-color: var(--border-strong); }
.topbar-iconbtn__ico .hicon { width: 16px; height: 16px; }
/* The "New chat" launcher moved to the header ("Ask smAI"); hide the footer copy. The
   footer chat bar still hosts minimized-chat tabs + history (the dock's collapse target). */
#chat-dock-launcher { display: none !important; }

/* Search toggle icon (mirrors .topbar-help). The search bar collapses to this icon;
   clicking it expands the full bar. The wrap stays flex:1 so the right-side icons stay
   right-aligned whether the bar is open or not. */
.topbar-search-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  color: var(--text-subtle);
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.topbar-search-toggle:hover { background: var(--bg-muted); color: var(--text); border-color: var(--border-strong); }
/* Collapsed by default: hide the bar itself; the wrap remains as the flex spacer. */
.topbar-search-wrap:not(.is-open) > .topbar-search,
.topbar-search-wrap:not(.is-open) > .topbar-search__suggest { display: none; }
/* Open: highlight the toggle so it reads as active. */
.topbar-search-wrap.is-open ~ .topbar-search-toggle,
.topbar-search-toggle[aria-expanded="true"] { color: var(--text); border-color: var(--border-strong); }

/* The page title + page-level docs link now live in the topbar — hide the
   in-page copies. Only DIRECT children of .canvas-header (the builder
   node-panel "Help docs" links stay). */
.canvas-header > h1,
.canvas-header > .canvas-ref-link { display: none; }
/* Rail modules: their canvas-header only held the page title + help link — both
   now hidden (title removed from the topbar; help is the topbar icon) — leaving an
   empty bar between the topbar and the content. The module rail shows the name, so
   drop the empty header. */
#hive-tab > .canvas-header,
#orbit-tab > .canvas-header,
#flux-tab > .canvas-header,
#kriya-tab > .canvas-header,
#artha-tab > .canvas-header,
#app-admin-tab > .canvas-header,
#config-tab > .canvas-header,
#central-tab > .canvas-header,
#docs-tab > .canvas-header { display: none; }

/* Collapse the page-title band entirely unless it still holds interactive
   controls (nav / buttons / the templates link). Pages with only a title get
   no empty header band → cleaner, more room for content. */
.canvas-header:not(:has(button, nav, select, input, textarea, .ghost-button, .primary-button, .tab, [data-open-templates])) {
  display: none;
}

/* ── Workspace: folder-style Orgs → Portfolios → Projects ─────────────────── */

.ws-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; margin-bottom: 12px; }
.ws-grid--create { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); margin-bottom: 14px; }
/* Org switcher on Settings ▸ Org settings. Bigger than the generic tile: this is the page
   where you pick which organization to work on, and the logo is the thing you actually aim
   at — at 32px in a 220px card it was a detail on a row, not a target. */
.ws-grid--orgs { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.ws-grid--orgs .ws-tile__main { gap: 14px; padding: 18px 18px; }
.ws-grid--orgs .ws-tile__name { font-size: 15px; }
.ws-grid--orgs .ws-tile__desc { font-size: 12.5px; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.ws-grid--orgs .ws-create-tile { min-height: 92px; }

/* Org AI agent module toggles — one checkbox row per module. */
.ws-tool-toggles { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 6px 14px; }
.ws-tool-toggle { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); cursor: pointer; }
.ws-tool-toggle:hover { border-color: var(--border-strong); }
.ws-tool-toggle.is-unavailable { opacity: 0.7; }

/* Tile (org / project) — a card with a clickable main area + a ⋯ menu. */
.ws-tile { position: relative; display: flex; align-items: stretch; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 12px; transition: border-color .12s, box-shadow .12s; }
.ws-tile:hover { border-color: var(--border-strong); box-shadow: 0 2px 10px rgba(0,0,0,.05); }
/* The currently-active org in the Org Settings switcher. */
.ws-tile.is-active { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.ws-tile__main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; padding: 12px 14px; background: none; border: 0; text-align: left; cursor: pointer; color: var(--text); }
.ws-tile__icon { font-size: 20px; display: inline-flex; flex: none; }
.ws-tile__body { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ws-tile__name { font-size: 14px; font-weight: 650; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* "Your default" — where this person lands after logging in, in the list view: a radio (exactly one org can be it) with the Clear that a
   checked radio cannot express on its own. */
.ws-defcell { display: inline-flex; align-items: center; gap: 6px; }
.ws-defcell input[type=radio] { cursor: pointer; margin: 0; }
.ws-tile__desc { font-size: 12px; color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-tile--project { background: var(--bg-app); }

/* "➕ New …" create tile — dashed, leads each grid. */
.ws-create-tile { display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 64px; border: 1.5px dashed var(--border-strong); border-radius: 12px; background: transparent; color: var(--text-subtle); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; transition: all .12s; }
.ws-create-tile:hover { border-color: var(--brand); color: var(--text); background: var(--bg-hover); }
.ws-create-tile__plus { font-size: 18px; line-height: 1; }

/* Portfolio folder — expandable; reveals its projects. */
.ws-folder { border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); margin-bottom: 10px; overflow: hidden; }
.ws-folder__head { display: flex; align-items: center; gap: 6px; padding: 4px 10px 4px 4px; }
.ws-folder__toggle { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px; padding: 9px 10px; background: none; border: 0; text-align: left; cursor: pointer; color: var(--text); font: inherit; }
.ws-folder__chev { color: var(--text-faint); transition: transform .12s; display: inline-block; }
.ws-folder.is-open .ws-folder__chev { transform: rotate(90deg); }
.ws-folder__icon { font-size: 16px; }
.ws-folder__name { font-size: 14px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-folder__count { font-size: 11px; color: var(--text-faint); font-weight: 600; }
.ws-folder__desc { margin: 0 14px 6px 40px; font-size: 12px; color: var(--text-subtle); }
.ws-folder__projects { display: none; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 8px; padding: 0 12px 12px 38px; }
.ws-folder.is-open .ws-folder__projects { display: grid; }
.ws-folder--loose { border-style: dashed; background: transparent; }
.ws-folder--loose .ws-folder__head { padding: 9px 12px; }
/* Hover-revealed edit/delete icon buttons on a portfolio folder (Project Settings). */
.ws-folder__actions { display: inline-flex; gap: 2px; opacity: 0; transition: opacity .12s; }
.ws-folder:hover .ws-folder__actions, .ws-folder__actions:focus-within { opacity: 1; }
.ws-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; padding: 0; border: 1px solid transparent; border-radius: 7px; background: transparent; cursor: pointer; font-size: 13px; line-height: 1; opacity: .75; transition: background .12s, opacity .12s, border-color .12s; }
.ws-icon-btn:hover { opacity: 1; background: var(--bg-app); border-color: var(--border); }
.ws-icon-btn.danger:hover { border-color: var(--danger); }
/* AI Agents page: per-project agent groups + the read-only effective-prompt preview. */
.ws-agent-group { margin: 10px 0; }
.ws-agent-group > h3 { font-size: 13px; font-weight: 700; margin: 0 0 6px; color: var(--text); }
.ws-effective { width: 100%; resize: vertical; font: 11px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text-subtle); background: var(--bg-app); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; white-space: pre-wrap; }
/* Hover-revealed edit/open/delete icons on a project tile (overlaid on the right,
   on a soft fade so they stay legible over the project name/description). */
.ws-tile__actions { position: absolute; top: 0; right: 0; height: 100%; display: inline-flex; align-items: center; gap: 2px; padding: 0 8px 0 22px; border-radius: 0 12px 12px 0; opacity: 0; transition: opacity .12s; pointer-events: none; }
.ws-tile:hover .ws-tile__actions, .ws-tile__actions:focus-within { opacity: 1; pointer-events: auto; }
.ws-tile--project .ws-tile__actions { background: linear-gradient(to right, transparent, var(--bg-app) 30%); }

/* ⋯ actions menu */
.ws-menu { position: relative; display: flex; align-items: center; flex: none; padding-right: 6px; }
.ws-menu__btn { width: 28px; height: 28px; border: 0; background: transparent; color: var(--text-subtle); font-size: 16px; line-height: 1; border-radius: 7px; cursor: pointer; }
.ws-menu__btn:hover { background: var(--bg-hover); color: var(--text); }
.ws-menu__popover { position: absolute; right: 4px; top: calc(100% - 2px); z-index: 20; min-width: 180px; padding: 4px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: 0 12px 30px rgba(0,0,0,.16); }
.ws-menu__item { display: block; width: 100%; text-align: left; padding: 7px 10px; border: 0; background: none; color: var(--text); font: inherit; font-size: 13px; border-radius: 7px; cursor: pointer; }
.ws-menu__item:hover { background: var(--bg-hover); }
.ws-menu__item.danger { color: var(--danger); }

/* Edit / create / move dialog */
.ws-dialog { border: 1px solid var(--border); border-radius: 14px; padding: 0; background: var(--bg-surface); color: var(--text); max-width: 440px; width: 92vw; box-shadow: 0 20px 50px rgba(0,0,0,.25); }
.ws-dialog::backdrop { background: rgba(0,0,0,.35); }
.ws-dialog__form { display: flex; flex-direction: column; gap: 12px; padding: 20px; }
.ws-dialog__form h3 { margin: 0; font-size: 16px; }
.ws-dialog__field { display: flex; flex-direction: column; gap: 5px; font-size: 12px; font-weight: 600; color: var(--text-subtle); }
.ws-dialog__field input, .ws-dialog__field textarea, .ws-dialog__field select { font: inherit; font-size: 13px; font-weight: 400; color: var(--text); padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); }
.ws-dialog__field textarea { resize: vertical; }
/* Standard: section/block descriptions are not shown inline — they live as a
   small-font tooltip revealed on hover of the header (with a tiny ⓘ affordance). */
/* Carrying a tooltip must NOT change the cursor — every element keeps its natural
   cursor (buttons → pointer, text → default, inputs → text). Only the dedicated ⓘ
   info icon (.ws-tip-i, below) uses the help (?) cursor, since that's its sole purpose. */
[data-tip] { position: relative; }
/* Belt-and-braces: clickable tooltip-bearers keep the pointer (hand) cursor. */
button[data-tip], a[data-tip], label[data-tip], [role="button"][data-tip] { cursor: pointer; }
/* Form fields keep their natural cursor (text / default). */
input[data-tip], textarea[data-tip], select[data-tip] { cursor: auto; }
.ws-tip-i { font-size: 10px; color: var(--text-subtle); opacity: .65; margin-left: 4px; vertical-align: middle; font-weight: 400; }
[data-tip]:hover .ws-tip-i { opacity: 1; }
[data-tip]::after {
  content: attr(data-tip);
  position: absolute; left: 0; top: calc(100% + 4px); z-index: 60;
  width: max-content; max-width: 320px;
  padding: 5px 8px;
  font-size: 10px; font-weight: 400; line-height: 1.35; letter-spacing: 0; text-transform: none;
  white-space: normal; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px; box-shadow: 0 6px 18px rgba(0,0,0,.18);
  opacity: 0; visibility: hidden; transform: translateY(-2px); transition: opacity .12s ease, transform .12s ease;
  pointer-events: none;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after { opacity: 1; visibility: visible; transform: translateY(0); }
/* 2026-07: ONE body-level JS tooltip (.app-tip-float, ui/tooltip.js) serves the whole
   app — opaque, position:fixed, viewport-clamped, unclippable. Every CSS pseudo-element
   tooltip is suppressed so nothing renders translucent or gets cut by overflow. */
[data-tip]::after, [data-tip]::before,
[data-rail-tip]::after, [data-rail-tip]::before { content: none !important; display: none !important; }
/* SINGLE TOOLTIP inside Kriya: the tab, its popups and drawers render ONE body-level JS float
   (controller._wkShowTip) that can't be clipped by overflow. Suppress the CSS ::after tooltip in
   every Kriya scope so the two never stack into a double popup. (Non-Kriya pages keep ::after.) */
#kriya-tab [data-tip]::after, #kriya-tab [data-tip]::before,
.wk-popup-ov [data-tip]::after, .wk-popup-ov [data-tip]::before,
.wk-drawer [data-tip]::after, .wk-drawer [data-tip]::before,
.wk-detailpage [data-tip]::after, .wk-detailpage [data-tip]::before { content: none !important; display: none !important; }
.ws-tip-i { cursor: help; }
.ws-tip-i:focus-visible { outline: 2px solid var(--primary, #1d4ed8); outline-offset: 2px; border-radius: 3px; }

/* ── Agent Factory (Flux → Agents) ──────────────────────────────────────────── */
.agent-factory-form { display: flex; flex-direction: column; gap: 8px; max-width: 720px; }
.agent-factory-form .field-label { font-weight: 600; font-size: 13px; }
.agent-factory-form textarea.ws-input { width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-app); color: var(--text); font: inherit; resize: vertical; }
.agent-factory-blueprints { margin-top: 4px; }
.blueprint-card { display: flex; flex-direction: column; gap: 6px; }
.blueprint-card .tool-card__name { font-weight: 600; }
.blueprint-meta { font-size: .8em; }
.blueprint-card .agent-card__acts { margin-top: 6px; display: flex; flex-wrap: wrap; gap: 6px; }
.agent-factory-result { max-width: 720px; display: flex; flex-direction: column; gap: 10px; }
.agent-factory-warnings { margin: 0; padding-left: 18px; color: var(--warn, #b45309); font-size: 13px; }
.agent-factory-nodes summary { cursor: pointer; font-weight: 600; }
.agent-factory-nodes ul { margin: 6px 0 0; padding-left: 18px; font-size: 13px; }
.agent-factory-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* Deploy success — "your agent is live + how to use it" */
.deploy-success { gap: 14px; }
.deploy-how { display: flex; flex-direction: column; gap: 10px; }
.deploy-how h4 { margin: 0; font-size: 14px; }
.deploy-steps { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; line-height: 1.45; }
.deploy-steps code { background: var(--bg-app); border: 1px solid var(--border-muted, var(--border)); border-radius: 4px; padding: 1px 5px; font-size: .92em; }
.code-box { position: relative; display: flex; flex-direction: column; gap: 8px; background: var(--bg-app); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }
.code-box pre { margin: 0; overflow-x: auto; }
.code-box code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-all; color: var(--text); }
.code-box .ghost-button { align-self: flex-start; }

/* Inline Org Settings editor (replaces the org dialogs) — a card at the top of the
   Organizations list with name/description, logo, access, and a danger zone. */
.ws-org-editor { border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; margin-bottom: 16px; background: var(--bg-surface); }
.ws-org-editor .ws-section__head { margin-bottom: 4px; }
.ws-org-editor .ws-section__head h2 { font-size: 1rem; }
/* ── Org detail page (Settings ▸ Org settings) ──────────────────────────────
   Reads as a record, not a form: the profile groups come from the shared detail toolkit,
   and the blocks below them (logo, default org, access, history, danger) keep the plain
   stacked treatment. The header is sticky because the Edit/Save control lives in it and a
   long profile would otherwise scroll it out of reach. */
.ws-org-detail { padding: 0; border: 0; background: none; }
.ws-org-detail__head {
  position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 12px;
  padding: 10px 2px 12px; margin: 0 0 4px; background: var(--bg-canvas);
  border-bottom: 1px solid var(--border-muted);
}
.ws-org-detail__head h2 { margin: 0; font-size: 1.05rem; }
.ws-org-detail__tools { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
.ws-org-detail .orbit-dgroup { margin-bottom: 12px; }
/* ── THE ORGS LIST IS A TABLE (2026-09-15, user direction: "in org details list view show all
   details as columns and tabular way", with Kriya's task list given as the reference) ──────────
   THE SKIN IS `.ws-table`, the shared MODULE LIST TABLES block at the end of this sheet, which
   IS Kriya's task list — card, sticky header on the same surface, hairline column separators, row
   hover, no zebra. It was already registered for this list in `ui/tableSkin.spec.js`. Nothing
   here re-states any of it; what follows is only what that block cannot know about. */
.ws-orgtbl__scroll { overflow-x: auto; }
/* The group heading, as a spanning row. A HEADING keeps one line and no box of its own — the
   same call the record page's field groups made. `left: 0` pins its text while the table scrolls
   sideways, so the heading stays readable at any horizontal offset. */
.ws-orgtbl__grp td {
  padding: 9px 12px 6px;
  background: var(--bg-app);
  font-size: .7rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted);
}
.ws-orgtbl__row.is-current td:first-child { box-shadow: inset 2px 0 0 var(--primary); }
/* The ⋯ cell is one control at the end of a row of data, so it must not stretch. `position:
   static` on the wrapper is load-bearing: the menu is placed `fixed` by `ui/menuPlacement.js`,
   and any ancestor with a transform or its own positioning context would resolve those viewport
   coordinates against itself instead. */
.ws-orgtbl td:last-child { width: 1%; white-space: nowrap; }
.ws-orgtbl .ws-menu { position: static; }

/* ── The org's addresses: a TYPE column and one row per address (2026-09-15) ────────────────
   Borders earn their place around something you press or type into — so the table has no card
   around it, only a hairline under the head row and between rows, which is what the app's list
   tables already do. */
.ws-addr__scroll { overflow-x: auto; }
.ws-addr__tbl { width: 100%; border-collapse: collapse; font-size: .84rem; }
.ws-addr__tbl th { text-align: left; padding: 4px 10px 6px 0; font-size: .66rem; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase; color: var(--text-faint);
  border-bottom: 1px solid var(--border-muted); white-space: nowrap; }
.ws-addr__tbl td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--border-muted);
  vertical-align: top; }
.ws-addr__tbl tr:last-child td { border-bottom: 0; }
.ws-addr__kind { white-space: nowrap; font-weight: 600; }
.ws-addr__act { text-align: right; white-space: nowrap; }
.ws-addr__act .ghost-button { margin-top: 0; }
.ws-addr__empty { padding: 14px 0; }
.ws-addr__empty .muted { display: block; margin-top: 3px; max-width: 52ch; }
/* The add/edit form sits under the table rather than in a dialog: it is part of the page you are
   already editing, and a second surface for a field that could be edited here is the thing this
   app removes on sight. */
.ws-addr__form { margin-top: 12px; padding: 12px; border: 1px solid var(--border);
  border-radius: 10px; background: var(--bg-surface); }
.ws-addr__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 8px 14px; align-items: end; }
.ws-addr__grid label { display: flex; flex-direction: column; gap: 3px; margin: 0;
  font-size: .66rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--text-faint); }
.ws-addr__grid input, .ws-addr__grid select { font: inherit; font-size: .84rem; font-weight: 400;
  text-transform: none; letter-spacing: 0; color: var(--text); padding: 6px 8px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg-app); min-width: 0; }
.ws-addr__wide { grid-column: 1 / -1; }
.ws-addr__formact { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }
.ws-addr__formact .ghost-button, .ws-addr__formact .primary-button { margin-top: 0; }
/* IDENTITY IS THREE COLUMNS, WITH THE DESCRIPTION UNDER THEM (2026-09-15, user direction: "keep
   display name, legal name, Industry, side by side columns, and description in next row").

   The shared grid is `repeat(auto-fill, minmax(220px, 1fr))`, which on a wide settings pane makes
   seven or eight tracks: the three fields would sit side by side but bunched into the left third,
   with the full-width Description below them on a different measure. Three explicit tracks line
   the row up with the description underneath it.

   MEASURED AGAINST THE PANE, NOT THE VIEWPORT — the settings pane is the navigation column
   narrower than the window, so a viewport media query collapses these columns while there is
   still room for them (that exact mistake is recorded above, on `.secpick__cols`). A container
   query needs a declared container, so `.ws-org-detail` becomes one; without that the rule
   matches nothing at all, silently. The viewport fallback underneath is for a browser with no
   container queries, and degrades to the shared auto-fill rather than to one column. */
.ws-org-detail { container-type: inline-size; }
@container (min-width: 46rem) {
  .ws-org-detail [data-lead-sec="org-identity"] .orbit-detail-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@supports not (container-type: inline-size) {
  @media (min-width: 60rem) {
    .ws-org-detail [data-lead-sec="org-identity"] .orbit-detail-grid {
      grid-template-columns: repeat(3, minmax(0, 1fr));
    }
  }
}
.ws-org-detail .ws-edit-block { padding: 14px 16px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); margin-top: 12px; }
.ws-org-detail .ws-edit-block + .ws-edit-block { border-top: 1px solid var(--border); padding-top: 14px; }
.ws-org-detail .ws-edit-danger { border-color: color-mix(in srgb, var(--danger, #dc2626) 35%, var(--border)); }

/* ── Settings ▸ Setup ▸ Currency ─────────────────────────────────────────────
   One card per setting, because the three of them (org default / ledger base / rates) are
   genuinely different decisions that happen to share a word. */
/* ── Settings ▸ Numbering (2026-09-24) ──────────────────────────────────────
   Every record code the org issues, one row each. Deliberately shares `.cur-card` with
   Currency & statutory one item up the rail rather than growing a second card style: they are
   two pages of one console and a card that differs by 2px is how that stops being true. */
/* The row-2 module filter. Shares `.pt-toolsel`'s shape rather than inventing a second one —
   including, since 2026-09-24, the WRAPPER's lack of a box: see the note on that block for why it
   went, why the height stayed, and why the SELECT keeps the shared dropdown framing. Changed in
   step with it deliberately; they are the same control on two pages of one console, and a picker
   that differs by a border is how that stops being true. */
.num-toolsel {
  display: inline-flex; align-items: center; height: 30px; padding: 0;
}
/* THE SELECT IS THE APP'S OWN DROPDOWN — see the long note on `.pt-toolsel .pt-toolsel__sel`,
   which these two share and which is where the reasoning lives. In short: this morning's fix for
   *"extra borders"* correctly removed the WRAPPER's box and then also forced the SELECT's off,
   leaving a bold word with a chevron. The wrapper stays boxless; the select stops overriding
   `.wk-filters select`, so it is framed exactly like every other filter dropdown in row 2.

   THE SPECIFICITY FINDING IS WHAT MADE THAT FIX POSSIBLE AND IS WORTH KEEPING: `.num-toolsel__sel`
   alone is (0,1,0) and loses to `.wk-filters select` at (0,1,1), so a one-class `border: 0` here
   NEVER APPLIED — and jsdom's cascade is not the browser's, so only real Chrome reported which
   rule won. Anything this component ever needs to take back from the shared rule must be written
   at two classes and never rely on document order. No `:is()` — WebKit prices it at zero. */
/* ON THE SELECT, not `:focus-within` on the wrapper — and doubly right now that the select is the
   thing with a box: the ring belongs around the edge the reader can see, and the wrapper is layout
   and nothing else. This is also what `.pt-toolsel__sel` does, so the two match. The ring itself
   is not optional — six states, and focus is the one that gets dropped. */
.num-toolsel .num-toolsel__sel:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.num-page { display: flex; flex-direction: column; gap: 14px; max-width: 980px; }
.num-group { margin: 16px 0 6px; font-size: 13px; font-weight: 700; color: var(--text-subtle); }
.num-group:first-of-type { margin-top: 4px; }
/* THE TABLE SCROLLS INSIDE ITS OWN BOX, never the page. Six columns of controls do not fit a
   narrow window, and a settings page whose BODY scrolls sideways takes the rail with it. */
.num-scroll { overflow-x: auto; }
.num-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.num-table th, .num-table td { padding: 7px 10px; text-align: left; vertical-align: middle; }
.num-table thead th { font-weight: 600; color: var(--text-subtle); white-space: nowrap; }
/* The one line a design keeps is under a heading. */
.num-table thead th { border-bottom: 1px solid var(--border); }
.num-table tbody th { font-weight: 500; white-space: nowrap; }
.num-table tbody tr + tr th, .num-table tbody tr + tr td { border-top: 1px solid var(--border-subtle, var(--border)); }
.num-on { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
/* >= 24x24, the app-wide target floor. */
.num-on input { width: 16px; height: 16px; margin: 4px; }
.num-table input[type="text"], .num-table input[type="number"], .num-table select {
  box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-surface); color: var(--text); font: inherit;
}
.num-table input[type="text"] { width: 96px; }
.num-table input[type="number"] { width: 84px; }
.num-table select { width: 152px; }
.num-table input:focus-visible, .num-table select:focus-visible {
  outline: 2px solid var(--primary); outline-offset: 1px;
}
.num-preview code { font-size: 13px; color: var(--text); white-space: nowrap; }
/* A SWITCHED-OFF ROW STAYS READABLE. Dimming the whole row hid the prefix somebody is about to
   set before switching it on, which is the order people actually do it in. */
.num-table tr.is-off .num-preview code { color: var(--text-subtle); font-style: italic; }
/* AN ADDED BOOK reads as belonging to the record above it — indented, and its name in the
   regular weight against the record's own. Not a nested table: a second series is a peer counter,
   not a child record, and boxing it would say otherwise. */
.num-table tr.is-extra th { padding-left: 26px; font-weight: 400; }
.num-table tr.is-extra th::before {
  content: ''; position: absolute; margin-left: -14px; margin-top: 7px;
  width: 7px; height: 7px; border-left: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.num-table tr.is-extra th { position: relative; }
.num-del { margin-left: 8px; }

.cur-page { display: flex; flex-direction: column; gap: 14px; max-width: 860px; }
.cur-card { padding: 16px 18px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg-surface); }
.cur-card__head h3 { margin: 0 0 3px; font-size: .95rem; font-weight: 700; }
.cur-card__head p { margin: 0 0 12px; line-height: 1.45; }
.cur-field { display: flex; align-items: center; gap: 10px; }
.cur-select { font: inherit; font-size: .88rem; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); min-width: 260px; }
/* Statutory & finance, moved here from the org detail page. Two to a row so four short fields
   read as one card rather than a column of boxes; one column where that would squeeze them. */
.cur-orgfields { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px 16px; }
.cur-orgfield { display: flex; flex-direction: column; gap: 3px; font-size: .82rem; min-width: 0; }
.cur-orgfield__l { font-weight: 650; color: var(--text-subtle); }
.cur-orgfield__h { font-size: .75rem; color: var(--text-muted); line-height: 1.35; }
.cur-orgfield input, .cur-orgfield .cur-select { width: 100%; min-width: 0; font: inherit; font-size: .86rem;
  padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface); color: var(--text); }
.cur-orgfield input:disabled, .cur-orgfield .cur-select:disabled { color: var(--text-muted); background: var(--bg-muted); }
.cur-select:disabled { opacity: .65; cursor: not-allowed; }
/* The org default and the ledger base disagreeing is legitimate but consequential — it is
   stated, not buried, and it is not an error so it is not styled as one. */
.cur-warn { margin: 0; padding: 12px 14px; border: 1px solid color-mix(in srgb, #d97706 45%, var(--border)); border-left-width: 3px; border-radius: 10px; background: color-mix(in srgb, #d97706 8%, transparent); font-size: .86rem; line-height: 1.5; }
.cur-table { width: 100%; border-collapse: collapse; font-size: .87rem; }
.cur-table th { text-align: left; font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); font-weight: 700; padding: 0 8px 6px 0; }
.cur-table td { padding: 7px 8px 7px 0; border-top: 1px solid var(--border-muted); }
.cur-table .cur-num, .cur-table th.cur-num { text-align: right; font-variant-numeric: tabular-nums; }
.cur-row-act { text-align: right; width: 1%; white-space: nowrap; }
.cur-addrate { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-muted); }
.cur-addrate label { display: flex; flex-direction: column; gap: 4px; font-size: .74rem; font-weight: 650; color: var(--text-subtle); }
.cur-addrate select, .cur-addrate input { font: inherit; font-size: .86rem; padding: 6px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); }

/* Payment terms — shares the currency page's card/table chrome. */
.term-table td { vertical-align: top; }
.term-table tr.term-off { opacity: .6; }
.term-acts { text-align: right; white-space: nowrap; }
.term-acts .ghost-button { margin-left: 4px; }
.term-banner { margin: 0 0 14px; padding: 12px 14px; border: 1px solid var(--border); border-left: 3px solid var(--primary); border-radius: 10px; background: var(--bg-muted); font-size: .86rem; line-height: 1.5; }
.term-banner .ghost-button { margin-left: 6px; }
.term-add { margin-top: 12px; }
.term-note { margin: 12px 0 0; line-height: 1.5; }
.term-form { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border-muted); }
.term-form__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
.term-form label { display: flex; flex-direction: column; gap: 4px; font-size: .74rem; font-weight: 650; color: var(--text-subtle); }
.term-form input { font: inherit; font-size: .86rem; padding: 6px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); }
.term-form__acts { display: flex; gap: 8px; margin-top: 10px; }
.dt-table .dt-desc { max-width: 420px; font-size: .84rem; line-height: 1.45; color: var(--text-subtle); }
.dt-descfield { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; font-size: .74rem; font-weight: 650; color: var(--text-subtle); }
.dt-descfield textarea { font: inherit; font-size: .86rem; padding: 6px 9px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); resize: vertical; }

/* ── Org logo variants (size x theme) ───────────────────────────────────────
   Each slot previews on the background it is DRAWN for — a dark-mode mark on a light card
   is invisible, which would make a correctly-uploaded logo look like a failed upload. */
.ws-logo-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.ws-logo-slot { display: flex; gap: 12px; align-items: flex-start; padding: 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-surface); }
.ws-logo-slot__frame {
  flex: none; width: 92px; height: 62px; display: grid; place-items: center; overflow: hidden;
  border: 1px dashed var(--border); border-radius: 8px; background: #ffffff;
}
.ws-logo-slot--dark .ws-logo-slot__frame { background: #1f2124; border-color: #3c4043; }
.ws-logo-slot__img { max-width: 100%; max-height: 100%; object-fit: contain; }
.ws-logo-slot__empty { font-size: 11px; color: var(--text-faint); }
.ws-logo-slot--dark .ws-logo-slot__empty { color: #9aa0a6; }
.ws-logo-slot__meta { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ws-logo-slot__label { font-size: 12.5px; font-weight: 650; color: var(--text); }
.ws-logo-slot__hint { font-size: 11.5px; line-height: 1.35; color: var(--text-subtle); }
.ws-logo-slot__actions { display: inline-flex; gap: 6px; margin-top: 4px; }
.ws-logo-legacy { grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; padding-top: 4px; }

.ws-edit-block { margin: 12px 0 0; display: flex; flex-direction: column; gap: 8px; }
.ws-edit-block + .ws-edit-block { border-top: 1px solid var(--border-muted); padding-top: 12px; }
.ws-edit-block h3 { margin: 0; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-subtle); }
.ws-edit-block .primary-button { align-self: flex-start; }
.ws-add-access { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.ws-add-access input[type="email"] { flex: 1 1 220px; min-width: 180px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; font: inherit; font-size: 13px; background: var(--bg-app); color: var(--text); }
.ws-edit-danger h3 { color: var(--danger); }
.ws-create-tile.is-disabled { opacity: 0.5; cursor: not-allowed; border-style: dashed; }
.ws-create-tile.is-disabled:hover { border-color: var(--border-strong); color: var(--text-subtle); background: transparent; }
/* Two-path delete dialog (archive vs permanent). */
.ws-del-opt { border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin: 10px 0; display: flex; flex-direction: column; gap: 6px; }
.ws-del-opt--danger { border-color: #f3c9c9; }
.ws-del-opt .ghost-button { align-self: flex-start; }
.ws-confirm-hint { margin: 4px 0 0; }
.ws-confirm-hint.is-ok { color: #15803d; }
.ws-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }

/* ════════════════════════════════════════════════════════════════════════
   UX OVERHAUL — persistent left icon rail
   Collapsed (.left-collapsed) is now a slim ICON RAIL (icons stay clickable
   + tooltips), not a hidden panel. Pinned-expanded shows icons + labels.
   Toggled via #collapse-left; the topbar hamburger is retired since the rail
   is always visible. These rules sit last so they win by source order.
   ════════════════════════════════════════════════════════════════════════ */
.left-collapsed .sidebar {
  width: 60px;
  overflow: visible;
  border-right: 0; /* the skin's gradient divider (from 52px) draws the line below the header */
}
/* The topbar ☰ is gone — the rail is always present; expand via the rail's
   own ‹/› pin toggle. */
.topbar-toggle,
.left-collapsed .topbar-toggle { display: none !important; }

/* Header: keep the brand mark + pin toggle, drop the wordmark. */
.left-collapsed .sidebar-header { padding: 0 6px; justify-content: center; gap: 4px; }
.left-collapsed .sidebar-header h2 { gap: 0; }
.left-collapsed .brand-swap { display: none; }

/* In-rail search hides when collapsed (it lives in the header now); the
   +New button condenses to its glyph (font-size:0 hides the bare "New" text
   node, the .plus span restores the glyph). */
.left-collapsed .global-search-wrap { display: none; }
.left-collapsed .sidebar-toolbar { padding: 8px 10px; justify-content: center; }
.left-collapsed .new-button { width: 40px; padding: 0; justify-content: center; font-size: 0; }
.left-collapsed .new-button .plus { font-size: 1.05rem; }

/* Nav links: icon-only, centered, labels/chevrons hidden; a pending badge
   shrinks to a corner dot. */
.left-collapsed .sidebar-link { position: relative; flex-direction: column; align-items: center; justify-content: center; padding: 6px 2px; gap: 2px; }
/* Slack-style caption under each icon in the collapsed rail — very small, centered,
   clipped to the rail width so the rail never widens. */
.left-collapsed .sidebar-link-mini {
  display: block;
  max-width: 100%;
  font-size: 8.5px;
  line-height: 1.05;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--text-subtle);
}
.left-collapsed .sidebar-link.active .sidebar-link-mini { color: var(--primary); }
/* The collapsed icon rail scrolls VERTICALLY when the viewport is too short to fit every
   icon — the nav list stays inside its own scroller instead of overflowing and pushing the
   footer / disturbing the page layout. (Tooltips no longer need `overflow: visible` to
   escape the rail — they render body-level as .rail-tip-rich / .rail-more-flyout.) A thin
   scrollbar keeps the 54px rail from widening. */
.left-collapsed .sidebar-section {
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  padding-left: 6px;
  padding-right: 6px; /* match Orbit's tighter 6px collapsed inset */
}
.left-collapsed .sidebar-section::-webkit-scrollbar { width: 6px; }
.left-collapsed .sidebar-section::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.22); border-radius: 3px; }
/* Instant tooltip for the collapsed icon rail (set as data-rail-tip in panels.js).
   Replaces the native `title` (which the browser delays ~1s) — shows on hover with
   no delay, to the right of the icon. Styled like the header Help-docs tooltip
   (surface bg + border), and on top of everything so it's never clipped/covered. */
.left-collapsed #sidebar-usage-meter:not([hidden]) { position: relative; }
/* The module icons (.sidebar-link) use the richer JS floating tooltip (.rail-tip-rich,
   name + one-line description); the footer items (Settings / Report / usage) keep this
   plain single-line tooltip. */
.left-collapsed .tab-footer[data-rail-tip]::after,
.left-collapsed #sidebar-usage-meter[data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; left: calc(100% + 8px); top: 50%; transform: translateY(-50%);
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000;
}
.left-collapsed #sidebar-usage-meter[data-rail-tip]:hover::after,
.left-collapsed .tab-footer[data-rail-tip]:hover::after,
.left-collapsed .tab-footer[data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }

/* Rich floating tooltip for the collapsed first rail (built in sidebarNav.js) — a
   bigger surface-styled card: module name + one-line description, and for "More" a
   list of the folded-away modules + a Customize hint. Rendered on <body> so it's
   never clipped by the rail. */
.rail-tip-rich {
  position: fixed; z-index: 1200; left: 0; top: 0; max-width: 260px;
  background: var(--bg-surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.22);
  padding: 8px 11px; font-size: 0.78rem; line-height: 1.35;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.09s ease;
}
.rail-tip-rich.is-open { opacity: 1; visibility: visible; }
.rail-tip-rich__title { font-weight: 700; font-size: 0.84rem; color: var(--text); }
.rail-tip-rich__desc { margin-top: 2px; color: var(--text-muted); font-size: 0.75rem; }
.rail-tip-rich__list { margin-top: 5px; display: flex; flex-direction: column; gap: 1px;
  max-height: 52vh; overflow-y: auto; }
.rail-tip-rich__item { display: flex; align-items: center; gap: 7px; padding: 2px 0; font-size: 0.76rem; }
.rail-tip-rich__ico { width: 1.05rem; text-align: center; flex: 0 0 auto; opacity: 0.9; }
.rail-tip-rich__muted { color: var(--text-faint); font-size: 0.74rem; padding: 2px 0; }
.rail-tip-rich__edit { margin-top: 7px; padding-top: 6px; border-top: 1px solid var(--border);
  color: var(--text-muted); font-size: 0.72rem; }

/* Interactive "More" flyout for the collapsed rail (built in sidebarNav.js) — a spacious
   pop-out menu of the folded-away modules you can click to open, styled like the expanded
   rail (icon + name + description). Rendered on <body>; pointer-events on only when open. */
.rail-more-flyout {
  position: fixed; z-index: 1200; left: 0; top: 0; width: 252px;
  /* Bound the whole flyout to the viewport so it never spans top-to-bottom; the list
     scrolls WITHIN it (head + Customize stay pinned). */
  max-height: calc(100vh - 24px); display: flex; flex-direction: column;
  background: var(--bg-surface); color: var(--text);
  border: 1px solid var(--border); border-radius: 11px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.26);
  padding: 6px; box-sizing: border-box;
  opacity: 0; visibility: hidden; pointer-events: none; transform: translateX(-4px);
  transition: opacity 0.1s ease, transform 0.1s ease;
}
.rail-more-flyout.is-open { opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
.rail-more-flyout__head { flex: 0 0 auto; padding: 5px 9px 6px; font-size: 0.66rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint); }
.rail-more-flyout__list { display: flex; flex-direction: column; gap: 1px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.rail-more-flyout__item { display: flex; align-items: center; gap: 10px; width: 100%;
  text-align: left; background: none; border: 0; border-radius: 8px; padding: 7px 9px;
  cursor: pointer; color: var(--text); font: inherit; }
.rail-more-flyout__item:hover:not(:disabled),
.rail-more-flyout__item:focus-visible { background: var(--bg-muted); outline: none; }
.rail-more-flyout__item:disabled { opacity: 0.45; cursor: default; }
.rail-more-flyout__ico { flex: 0 0 auto; width: 1.5rem; text-align: center; font-size: 1.05rem; }
.rail-more-flyout__text { display: flex; flex-direction: column; min-width: 0; }
.rail-more-flyout__name { font-size: 0.84rem; font-weight: 600; line-height: 1.2; }
.rail-more-flyout__desc { margin-top: 1px; font-size: 0.72rem; color: var(--text-muted);
  line-height: 1.25; white-space: normal; }
.rail-more-flyout__muted { padding: 8px 9px; color: var(--text-faint); font-size: 0.78rem; }
.rail-more-flyout__edit { flex: 0 0 auto; display: block; width: 100%; text-align: left; margin-top: 5px;
  padding: 8px 9px; border: 0; border-top: 1px solid var(--border); background: none;
  color: var(--text-muted); font: inherit; font-size: 0.76rem; cursor: pointer; }
.rail-more-flyout__edit:hover { background: var(--bg-muted); color: var(--text); }

/* Every top app-header control carrying data-rail-tip gets the same instant, surface-styled
   tooltip as the rail — replaces the slow/absent native title. Drops BELOW the item, centered,
   with the two header ends aligned inward so tips stay on-screen. */
.topbar [data-rail-tip] { position: relative; }
.topbar [data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000;
}
.topbar [data-rail-tip]:hover::after,
.topbar [data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }
/* Left-cluster items align their tip to the left edge; right-cluster to the right edge. */
.topbar .tb-brand[data-rail-tip]::after,
.topbar #app-launcher-btn[data-rail-tip]::after,
.topbar #page-nav-btn[data-rail-tip]::after { left: 0; transform: none; }
.topbar #header-search-toggle[data-rail-tip]::after,
.topbar #topbar-help[data-rail-tip]::after,
.topbar #topbar-chat[data-rail-tip]::after,
.topbar #topbar-report[data-rail-tip]::after,
.topbar #notif-button[data-rail-tip]::after,
.topbar #profile-button[data-rail-tip]::after { left: auto; right: 0; transform: none; }
.left-collapsed .sidebar-link-label,
.left-collapsed .sidebar-link-chevron { display: none; }
.left-collapsed .sidebar-link-icon { width: auto; font-size: 1.05rem; }
/* The More / Customize icons are text glyphs (⋯ / ✎), not SVG hicons, so they render smaller
   than the 25px module icons. Bump their font-size so they read the same size. */
.left-collapsed .sidebar-more-btn .sidebar-link-icon,
.left-collapsed .sidebar-customize-btn .sidebar-link-icon { font-size: 1.5rem; }
.left-collapsed .sidebar-link-badge {
  position: absolute; top: 4px; right: 12px; min-width: 8px; height: 8px;
  padding: 0; font-size: 0; border-radius: var(--radius-pill);
}
/* Expandable groups (Components) only reveal their sub-items when expanded. */
.left-collapsed .sidebar-collapse-body { display: none; }

/* Footer: drop the Learn sub-list when collapsed. */
.left-collapsed .learn-sublist { display: none; }
/* Usage meter collapses to a compact % bubble (no outer box), centered in the
   rail. Stays hidden (no data) via the `hidden` attribute. */
.left-collapsed #sidebar-usage-meter:not([hidden]) {
  display: flex; justify-content: center; align-items: center;
  padding: 6px 0; margin: 4px 0 6px; border: 0; background: none;
}
.left-collapsed #sidebar-usage-meter[hidden] { display: none; }
/* Footer items (Report / Settings) collapse to icon-only, like the nav links —
   matching the collapsed nav-icon size (1.05rem). */
.left-collapsed .tab-footer { position: relative; justify-content: center; gap: 0; padding: 8px 0; }
.left-collapsed .tab-footer-label { display: none; }
.left-collapsed .tab-footer .tab-footer-ico { font-size: 1.05rem; }
.left-collapsed .learn-summary { justify-content: center; font-size: 0; }
.left-collapsed .learn-summary .learn-chevron { font-size: 0.8rem; }

/* The pin toggle reads ‹ when expanded, › when collapsed (set in JS via the
   aria-label, but flip the glyph here for clarity). */
.left-collapsed .panel-toggle { transform: rotate(180deg); }

/* ════════════════════════════════════════════════════════════════════════
   Global left sidebar (the "first bar") — deep brand-navy skin + slim icon-rail
   collapse. App-wide. Scoped CSS-variable overrides re-skin every child via the
   cascade; collapse now folds to a 60px icon rail (was fully hidden) so the
   footer pin toggle stays reachable. Auto-collapse-on-navigation is removed in JS.
   ════════════════════════════════════════════════════════════════════════ */
.sidebar {
  --zone-nav: #0f172a;
  --zone-rail: #0f172a;
  --bg-surface: #0f172a;
  --bg-muted: rgba(255, 255, 255, 0.14);
  --bg-hover: rgba(255, 255, 255, 0.16);
  --text: #ffffff;
  --text-muted: rgba(255, 255, 255, 0.86);
  --text-subtle: rgba(255, 255, 255, 0.72);
  --text-faint: rgba(255, 255, 255, 0.55);
  --border: rgba(255, 255, 255, 0.16);
  --zone-divider: rgba(255, 255, 255, 0.16);
  --primary: #ffffff;
  /* Dark-slate skin matching the signed-out landing footer (#0f172a). The right-edge divider
     runs the FULL height (top 0) so there's a continuous line between the first rail and the
     second rail — including across the header band (first-rail header ↔ second-rail header). */
  background: linear-gradient(var(--zone-divider), var(--zone-divider)) right 0 top 0 / 1px 100% no-repeat, linear-gradient(160deg, #1e293b 0%, #0f172a 100%);
  border-right: 0;
}

/* ── Second rail (module secondary rail) — MATCHES THE TOPBAR: the same plain app surface
   (var(--bg-surface)) with a right-edge divider. No hard-coded zone tokens, so the rail tracks
   the topbar in both light and dark themes and its content stays legible (dark-on-light /
   light-on-dark) via the root tokens. */
.page-left-rail,
.workspace-rail {
  /* The nav zone, like Orbit's and Kriya's rails and the org-logo band above them — not
     the app surface. On --bg-surface the rail was the SAME colour as the content beside
     it in light, so it needed a hairline to exist at all; on the nav zone the step does
     that job, in both themes, and the seam stops being a hard line at --border (#5f6368
     in dark, brighter than the rail on either side of it). */
  background: var(--zone-nav);
  border-right: 0;
}
/* Active item: soft primary tint + a primary accent bar (theme-aware, on the plain surface). */
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="nav-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="rail-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav-btn"].is-active,
:is(.page-left-rail, .workspace-rail) :is(button, a)[class*="subnav__btn"].is-active,
.workspace-rail .workspace-rail__item.is-active,
.workspace-rail .workspace-rail__child.is-active {
  background: var(--accent-soft, rgba(29, 78, 216, 0.12));
  color: var(--primary);
  box-shadow: inset 3px 0 0 var(--primary);
}
:is(.page-left-rail, .workspace-rail) :is(button, a):focus-visible { outline-color: var(--primary); }

/* Extend the teal→cobalt gradient onto the rail's header + footer bands (the header
   otherwise sat on a flat surface color, breaking the sweep). Transparent so the
   sidebar's single full-height gradient shows through; keep only faint dividers. */
.sidebar .sidebar-header,
.sidebar .sidebar-footer { background: transparent; }
.sidebar .sidebar-header { border-bottom-color: rgba(255, 255, 255, 0.14); }
.sidebar .sidebar-footer { border-top: 0; }
/* Active tile: the base rule's primary-tint bg is invisible on the dark rail — wash white,
   with a bright cyan shimmer accent bar for the brand jewel-tone pop. */
.sidebar .sidebar-link.active,
.sidebar .sidebar-link.active:hover { background: rgba(255, 255, 255, 0.18); color: #fff; box-shadow: inset 3px 0 0 #22d3ee; }
.sidebar .sidebar-link:focus-visible { outline-color: #fff; }
/* First-rail header is a white brand bar (sits above the gradient rail) with a dark
   wordmark. Keep it at the standard 52px header height (matches the topbar /
   .canvas-header) and size the logo to sit fully inside it. */
.sidebar .sidebar-header {
  background: transparent;   /* show the rail's teal→cobalt gradient behind the brand */
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
}
.sidebar .sidebar-header h2 { color: #ffffff; }
.sidebar .sidebar-header .brand-ai { color: #22d3ee; }   /* keep the "AI" pop on the dark rail */
.sidebar .sidebar-header .brand-logo { height: 30px; max-height: 34px; max-width: 3em; }
/* The Settings footer link is a `.tab` (data-tab="config"), so when its tab is active it
   picks up `.tab.active` — a light `--accent-soft` background meant for the light canvas,
   which renders as a white block on the dark sidebar. Give it the SAME active treatment
   as the sidebar nav icons (translucent-white overlay + white text + accent bar), in both
   expanded and collapsed states. `!important` on color overrides `.tab-footer`'s base. */
.sidebar .tab-footer.active,
.sidebar .tab-footer.active:hover {
  background: rgba(255, 255, 255, 0.18);
  color: #fff !important;
  box-shadow: inset 3px 0 0 #22d3ee;
}

/* Collapse → slim 60px icon rail (override the old width:0 fully-hidden behavior). */
.left-collapsed .sidebar { width: 57px; overflow: visible; } /* 54px + 3px */
.left-collapsed .sidebar-header { justify-content: center; padding: 0; }
.left-collapsed .brand-home { gap: 0; }
.left-collapsed .brand-swap { display: none; }

/* Footer pin toggle — sits just below Settings, always visible. Glyph flips ‹ → ›. */
.sidebar-pin { width: 100%; cursor: pointer; border: 0; background: transparent; border-radius: 7px; font: inherit; }
.sidebar-pin:hover { background: rgba(255, 255, 255, 0.14); }
.sidebar-pin-ico { display: inline-block; font-size: 1.05rem; transition: transform 0.16s ease; }
.left-collapsed .sidebar-pin-ico { transform: rotate(180deg); }

/* Every module shows BOTH bars: keep the module rail visible even when the global
   sidebar is expanded (two independent left bars, app-wide). Workspace uses its own
   `.workspace-rail` (toggled via the [hidden] attribute by features/workspace/shell.js),
   so it needs the same override — guarded by :not([hidden]) so it stays hidden outside
   the Workspace family of tabs. */
.app-layout:not(.left-collapsed) .page-left-rail,
.app-layout:not(.left-collapsed) .workspace-rail:not([hidden]) { display: flex; }

/* ════════════════════════════════════════════════════════════════════════
   Full-height secondary rail — ALL modules (Orbit, Hive, Flux, CDP, Artha,
   Kriya, Settings, Docs, App-Admin…). When a tab with a left rail is active, the
   rail spans the WHOLE canvas height like the first bar, and the topbar + body +
   chat-footer all start AFTER it. Those three sections live under #app-tabs
   (display:contents) so they're targeted by descendant, not direct child. A
   uniform 210px keeps every module aligned; Orbit can collapse to a 56px rail.
   ════════════════════════════════════════════════════════════════════════ */
/* The module .page-left-rail no longer shifts the layout on its own — it's hidden and opened
   from the header hamburger (see the hamburger-gated rules near the top), which shift only the
   page BODY (never the topbar). Only the legacy workspace-rail keeps the old always-on column
   shift below (it's retired, so these selectors are effectively inert). */
.canvas:has(.workspace-rail:not([hidden])) { --rail-w: var(--page-rail-w, 147px); }
.canvas:has(.workspace-rail:not([hidden]).is-collapsed) { --rail-w: 57px; }
.canvas:has(.workspace-rail:not([hidden])) .topbar,
.canvas:has(.workspace-rail:not([hidden])) .canvas-body,
.canvas:has(.workspace-rail:not([hidden])) .app-footer {
  margin-left: var(--rail-w);
  transition: margin-left 0.16s ease;
}
.canvas:has(.workspace-rail:not([hidden])) .workspace-rail {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--rail-w);
  z-index: 40;
}

/* ── Draggable rail resizer ─────────────────────────────────────────────────
   A body-level, fixed-position grab strip that controllers/rails.js parks on the
   navigation column's right edge. Dragging it writes --nav-w (persisted to
   localStorage), which is the width the column is laid out at — one place, one column.
   Hidden when the column is collapsed, because a hidden column has no edge to drag.
   Double-click resets to the 248px default. */
.rail-resizer {
  position: fixed;
  width: 8px;
  z-index: 60;              /* above the rail column (z-index:40) */
  cursor: col-resize;
  background: transparent;
  touch-action: none;
}
.rail-resizer[hidden] { display: none; }
.rail-resizer::before {    /* thin visible seam, brightens on hover / while dragging */
  content: "";
  position: absolute; top: 0; bottom: 0; left: 3px; width: 2px;
  background: transparent; transition: background .12s ease;
}
.rail-resizer:hover::before,
body.is-rail-resizing .rail-resizer::before { background: var(--primary); }
body.is-rail-resizing { cursor: col-resize; user-select: none; }
/* Track the cursor 1:1 while dragging — kill the rail's margin/width easing. */
body.is-rail-resizing .canvas :is(.topbar, .canvas-body, .app-footer),
body.is-rail-resizing .page-left-rail,
body.is-rail-resizing .workspace-rail { transition: none !important; }
/* The rail's title row matches the topbar height so all three top headers align. */
.page-rail-title { min-height: 52px; box-sizing: border-box; }

/* ── Generic rail chrome (EVERY secondary rail, Orbit included) ─────────────────
   Fixed header that doesn't scroll, a scrolling middle, and a collapse toggle pinned
   at the bottom — appended by controllers/rails.js. The rail is the scroll container,
   so the title + footer use sticky to stay put. ──────────────────────────────── */
.page-left-rail,
.workspace-rail { display: flex; flex-direction: column; padding-top: 0; padding-bottom: 0; }
.page-left-rail > .page-rail-title,
.workspace-rail > .page-rail-title {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--zone-rail);
}
.rail-foot {
  flex: 0 0 46px;  /* match the global app footer height (min-height:46px) so the collapse
                      button lines up with the footer band across the bottom */
  height: 46px;
  box-sizing: border-box;
  margin-top: auto;          /* pin to the bottom when the list is short */
  position: sticky;
  bottom: 0;                 /* …and keep it pinned while the list scrolls */
  z-index: 3;
  display: flex;
  align-items: center;
  padding: 0 8px;
  border-top: 1px solid var(--zone-divider);
  background: var(--zone-rail);
}
/* The sticky title + collapse-foot need an OPAQUE fill (to mask content scrolling under them);
   use the same surface as the rail/topbar so they blend in. */
:is(.page-left-rail, .workspace-rail) > .page-rail-title { background: var(--bg-surface); }
:is(.page-left-rail, .workspace-rail) > .rail-foot { background: var(--bg-surface); }
.rail-collapse {
  display: flex; align-items: center; gap: 9px; width: 100%; margin: 0;
  border: 0; background: transparent; color: var(--text-muted);
  font: inherit; font-size: 0.8rem; font-weight: 600;
  padding: 8px 10px; border-radius: 6px; cursor: pointer;
}
.rail-collapse:hover { background: var(--bg-muted); color: var(--text); }
.rail-collapse-ico {
  width: 18px; text-align: center; flex-shrink: 0; font-size: 1.15rem;
  line-height: 1; display: inline-block; transition: transform 0.16s ease;
}
.rail-collapse-label { white-space: nowrap; }
.is-collapsed > .rail-foot .rail-collapse { justify-content: center; padding: 9px 0; }
.is-collapsed > .rail-foot .rail-collapse-label { display: none; }
.is-collapsed > .rail-foot .rail-collapse-ico { transform: rotate(180deg); font-size: 1.6rem; }.workspace-rail.is-collapsed > .page-rail-title{ font-size: 0; justify-content: center; padding-left: 0; padding-right: 0; }.workspace-rail.is-collapsed > .page-rail-title > span{ font-size: 1.2rem; }.workspace-rail.is-collapsed :is(button, a){ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 6px 0; }
/* Nested group/subgroup buttons carry an expanded-state left indent (e.g. Hive's
   .hive-nav-subgroup .hive-subnav-btn { padding-left: 1.7rem }) whose 3-class
   selector out-specifies the collapse rule above, so their icon drifts right and
   the collapsed rail looks staggered/"tabbed". Pin every collapsed rail item's
   side padding to zero (this selector out-specifies the indent) so all icons —
   nested or not — share one vertical centre line. */
:is(.page-left-rail, .workspace-rail).is-collapsed :is(button, a):not(.rail-collapse) { padding-left: 0; padding-right: 0; }.workspace-rail.is-collapsed{ padding-left: 3px; padding-right: 3px; gap: 3px; }.workspace-rail.is-collapsed :is(button, a):not(.rail-collapse) > span:not([aria-hidden="true"]):not([class*="ico"]):not([class*="chev"]){
  display: block; max-width: 100%; font-size: 8.5px; line-height: 1.05; font-weight: 500;
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--text-subtle);
}
:is(.page-left-rail, .workspace-rail).is-collapsed :is(button, a).is-active > span:not([aria-hidden="true"]):not([class*="ico"]):not([class*="chev"]) { color: var(--primary); }.workspace-rail.is-collapsed summary{ display: none; }.workspace-rail.is-collapsed [class*="nav-label"],
.workspace-rail.is-collapsed [class*="rail__label"],
.workspace-rail.is-collapsed [class*="rail-label"]{ display: none; }.workspace-rail.is-collapsed{ overflow-y: auto; }

/* ── Narrow screens / phones ─────────────────────────────────────────────────
   The secondary rail forces its icon-only collapsed appearance (so content gets
   real horizontal room instead of being squeezed to a sliver) and the body padding
   tightens. Desktop is untouched — everything here is scoped to <=760px. ──────── */
@media (max-width: 760px) {
  .canvas:has(.tab-content.active .page-left-rail),
  .canvas:has(.workspace-rail:not([hidden])) { --rail-w: 56px; }
  :is(.page-left-rail, .workspace-rail) { padding-left: 6px; padding-right: 6px; overflow-y: auto; }
  :is(.page-left-rail, .workspace-rail) :is(button, a) { justify-content: center; gap: 0; padding-left: 0; padding-right: 0; }
  :is(.page-left-rail, .workspace-rail) > .page-rail-title { font-size: 0; justify-content: center; padding-left: 0; padding-right: 0; }
  :is(.page-left-rail, .workspace-rail) > .page-rail-title > span { font-size: 1.2rem; }
  :is(.page-left-rail, .workspace-rail) :is(button, a) > span:not([aria-hidden="true"]):not([class*="ico"]),
  :is(.page-left-rail, .workspace-rail) summary,
  :is(.page-left-rail, .workspace-rail) [class*="nav-label"],
  :is(.page-left-rail, .workspace-rail) [class*="rail__label"],
  :is(.page-left-rail, .workspace-rail) [class*="rail-label"],
  .rail-collapse-label { display: none; }
  .canvas-body :is(.orbit-main, .hive-main, .kriya-main, .echo-main, .cdp-main, .artha-main, .lens-main, .appadmin-main),
  /* LONGHAND FOR PARTY, and the difference is deliberate (2026-09-21). WebKit prices `:is()` at
     specificity ZERO, so the list above resolves to (0,1,0) — the same weight as the desktop rule
     it is meant to override on a narrow screen, settled by document order alone. That is a
     pre-existing fragility in eight modules and is NAMED here rather than fixed under a different
     module's task: rewriting it changes the mobile behaviour of eight pages nobody reported. */
  .canvas-body .pty-main,
  .workspace-view { padding-left: 12px; padding-right: 12px; }
}

/* ════════════════════════════════════════════════════════════════════════
   UX OVERHAUL — the one app-wide search (Amazon-style: scope dropdown + input
   + go button), centered in the header. Wired by features/search/header.js.
   ════════════════════════════════════════════════════════════════════════ */
.topbar-search-wrap {
  position: relative;          /* anchor for the suggestions dropdown */
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  min-width: 0;
  padding: 0 var(--space-3);
}
/* Centered active-org brand — occupies the (empty) search spacer while the search is collapsed,
   and disappears the instant the search bar expands, so the two never fight for the centre. */
.tb-orgbrand { display: none; align-items: center; gap: 8px; text-decoration: none; color: var(--text);
  font-weight: 600; font-size: 18px; letter-spacing: -0.01em; max-width: 100%; min-width: 0; } /* +30% */
.topbar-search-wrap:not(.is-open) > .tb-orgbrand.is-shown { display: inline-flex; }
.tb-orgbrand__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* (No hover underline: the brand is a LABEL — it says whose data the page shows — not a link.) */
body.search-expanded .tb-orgbrand { display: none !important; }
/* Narrow headers: the centred org brand would crowd the clusters — drop it there. */
@media (max-width: 820px) { .tb-orgbrand { display: none !important; } }
.topbar-search {
  display: flex;
  align-items: stretch;
  width: 100%;
  max-width: 560px;
  height: 32px;            /* fixed height so every control fills it evenly */
  border: 1px solid transparent;   /* no border line — only the caret shows on focus */
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  transition: box-shadow 0.15s ease;
}
/* Entering the field shows only the text cursor — no border, no focus ring. */
.topbar-search:focus-within {
  border-color: transparent;
  box-shadow: none;
}
/* Custom scope dropdown — replaces the native <select> so the menu always opens
   downward (the native popup re-positions over the selected item, jumping up). */
.topbar-search__scope {
  position: relative;
  flex-shrink: 0;
  align-self: stretch;
  display: flex;
}
.topbar-search__scope-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;   /* label left, chevron right */
  gap: 6px;
  width: 96px;                      /* fixed so it doesn't resize per selection */
  margin: 0;
  border: 0;
  border-right: 1px solid var(--border);
  border-radius: var(--radius-pill) 0 0 var(--radius-pill);
  background: var(--bg-muted);
  color: var(--text-subtle);
  font: inherit;
  font-size: 0.76rem;
  padding: 0 10px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s ease;
}
.topbar-search__scope-btn:hover { background: var(--bg-muted); color: var(--text); }
.topbar-search__scope-label { overflow: hidden; text-overflow: ellipsis; }
/* --text-faint lands at 4.47:1 on the tinted pill in dark — a hair under AA. */
.topbar-search__scope-chev { flex-shrink: 0; font-size: 1.1rem; color: var(--text-muted); }
.topbar-search__scope-menu {
  position: absolute;
  top: calc(100% + 7px);
  left: 0;
  min-width: 168px;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 70;
}
.topbar-search__scope-menu[hidden] { display: none; }
.topbar-search__scope-menu li {
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  font-size: 0.84rem;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
}
.topbar-search__scope-menu li:hover { background: var(--accent-soft); }
.topbar-search__scope-menu li.is-active { background: var(--accent-soft); color: var(--primary); font-weight: 600; }
.topbar-search__input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 0.8rem;
  padding: 0 12px;        /* height comes from the pill; input centers its text */
}
.topbar-search__input:focus-visible { outline: none; }
.topbar-search__input::placeholder { color: var(--text-faint); }
.topbar-search__go {
  flex-shrink: 0;
  align-self: stretch;       /* fill the full pill height */
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0;                 /* override the global button margin-top:12px (pushed it down) */
  border: 0;
  border-radius: 0;          /* the pill handles rounding; no dark rounded block */
  background: transparent;   /* blend into the search area */
  color: var(--text-subtle);
  padding: 0 12px;
  cursor: pointer;
  transition: color 0.15s ease;
}
.topbar-search__go svg { width: 16px; height: 16px; display: block; }
.topbar-search__go:hover,
.topbar-search__go:focus,
.topbar-search__go:active { background: transparent; color: var(--primary); }

/* Live autocomplete dropdown (shown for the index-backed scopes). */
.topbar-search__suggest {
  position: absolute;
  top: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  width: min(560px, calc(100% - 2 * var(--space-3)));   /* match the pill width */
  max-height: 60vh;
  overflow-y: auto;
  background: var(--bg-surface);
  color: var(--text);   /* readable on the surface — items use `color: inherit`; without this they'd
                           inherit the dark topbar's light text and vanish on the light popup */
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 60;
  padding: 6px;
}
.tb-sg-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  text-align: left;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 0.84rem;
  padding: 7px 10px;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.tb-sg-item:hover,
.tb-sg-item.is-active { background: var(--accent-soft); }
.tb-sg-item__kind {
  flex-shrink: 0;
  text-transform: uppercase;
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: var(--radius-xs);
  padding: 1px 5px;
}
.tb-sg-item__title { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tb-sg-hint { color: var(--text-subtle); }
.tb-sg-empty { padding: 8px 10px; color: var(--text-subtle); font-size: 0.82rem; }
/* Section label inside the command/search dropdown ("Actions & pages", "Results"). */
.tb-sg-group { padding: 7px 10px 3px; font-size: 0.66rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-faint, var(--text-subtle)); pointer-events: none; }
.tb-sg-item--cmd .tb-sg-item__kind { color: var(--primary); }

/* The header bar is the app-wide search. Former per-module search boxes are kept in the
   DOM (the header drives them programmatically) but hidden — EXCEPT Kriya Tasks' #wk-search,
   which is a live client-side task filter shown via the collapsible toolbar control
   (.wk-search-collapse manages its show/hide). */
.orbit-search-wrap,
#orbit-account-search, #orbit-contact-search,
#hive-worker-search, #flux-search,
#search-tab #search-input { display: none !important; }

/* Narrow viewports: drop the scope dropdown; keep the input + go button. */
@media (max-width: 760px) {
  .topbar-search__scope { display: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   UX OVERHAUL — chat dock (LinkedIn-style: launcher bar → panel → maximize)
   #chat-tab is repurposed from a full tab into a fixed bottom-right dock that
   lives on every screen. State is driven by a body class:
     .chat-dock-min  → only the launcher bar shows
     .chat-dock-open → the floating panel (default size)
     .chat-dock-max  → the panel fills the work area for focus mode
   The chat shell + controller inside are untouched; only positioning changes.
   ════════════════════════════════════════════════════════════════════════ */
:root { --chat-footer-h: 40px; }
/* THE FLOATING WINDOW IS GONE, and its CSS with it (2026-09-19). `#chat-tab.chat-dock` — a
   draggable, resizable LinkedIn-style box in the bottom-right corner — had not matched anything
   since the assistant became a `.chat-page`: NOTHING adds the `chat-dock` class to `#chat-tab`,
   and `#chat-dock-head` / `#chat-dock-resize` / `#chat-footer` / `#chat-dock-launcher` are not
   in the shell either. Deleted rather than left uncalled, because it was the first thing a reader
   looking for "where does the assistant open" would find, and it described a surface nobody can
   reach. The assistant opens in THE RIGHT RAIL now — search `body.chat-dock-open`.
   The body class it keyed on is still live and still means the same three things. */
/* ── Ask smAI as a full PAGE (global left rail + page body) ─────────────────────
   #chat-tab is a normal tab-content again; when active it fills the canvas with the
   real .page-left-rail + the conversation, exactly like every other module. */
.canvas #chat-tab.chat-page.tab-content.active { display: flex; overflow: hidden; padding: 0; }
.chat-page__body { flex: 1; display: flex; min-height: 0; min-width: 0; }
.chat-page .chat-shell { flex: 1; min-width: 0; min-height: 0; }
/* Chat is a page now, and the AI/cloud pills moved into the profile menu — the app footer
   is empty, so retire it (its chat elements stay in the DOM for chatDock.js wiring). */
#app-footer { display: none; }

/* Chat rail contents (inside the real .page-left-rail; only its history list scrolls). */
.chat-page #chat-dock-rail { display: flex; overflow: hidden; }
.chat-rail { gap: 4px; position: relative; }
.chat-rail .chat-dock-info-pop { position: absolute; top: 34px; left: 10px; right: 10px; z-index: 40; }
/* Rail heading row: "💬 Ask smAI" + info button (the rail title is the page heading). */
.chat-rail .rail-module-name { display: flex; align-items: center; gap: 6px; }
.chat-rail__title { flex: 1; min-width: 0; }
.chat-rail__info { border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: .9rem; line-height: 1; padding: 0 2px; }
.chat-rail__info:hover, .chat-rail__info[aria-expanded="true"] { color: var(--primary); }
.chat-rail__ctx { font-size: .7rem; color: var(--text-faint); padding: 0 8px 6px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-rail__newbtn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; margin: 2px 4px 8px; padding: 9px 12px;
  border: 0; border-radius: 8px; background: var(--primary); color: var(--primary-ink, #fff); font: inherit; font-weight: 600; font-size: .85rem; cursor: pointer; }
.chat-rail__newbtn:hover { filter: brightness(1.06); }
.chat-rail__sec { display: flex; flex-direction: column; gap: 3px; min-height: 0; }
.chat-rail__sec--grow { flex: 1; overflow: hidden; }
.chat-rail__hd { font-size: .64rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-faint); padding: 6px 8px 2px; }
.chat-rail__search { font: inherit; font-size: .78rem; padding: 6px 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface); color: var(--text); margin: 2px 4px 4px; }
.chat-rail__list { display: flex; flex-direction: column; gap: 2px; overflow-y: auto; min-height: 0; }
.chat-rail__list--scroll { flex: 1; }
.chat-rail-row { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 7px; cursor: pointer; font-size: .8rem; color: var(--text-muted); }
.chat-rail-row:hover { background: var(--bg-muted); color: var(--text); }
.chat-rail-row.is-active { background: rgba(37, 99, 235, .10); color: var(--primary); font-weight: 600; }
.chat-rail-row__t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-rail-row__d { font-size: .66rem; color: var(--text-faint); flex: none; }
.chat-rail-row__x { border: 0; background: none; color: var(--text-faint); cursor: pointer; font-size: .7rem; opacity: 0; flex: none; }
.chat-rail-row:hover .chat-rail-row__x { opacity: 1; }
.chat-rail-row__x:hover { color: var(--danger, #c0392b); }
.chat-rail-empty { font-size: .76rem; color: var(--text-faint); padding: 6px 8px; margin: 0; }
/* The chat shell fills whatever space the dock gives it. In the narrow LinkedIn-size
   box, the composer must stay inside 360px: there is no global box-sizing reset, so
   border-box the composer subtree (otherwise width:100% + padding spills past the
   right edge under overflow:hidden and clips the entry row + send button). Side insets
   are trimmed so the textarea runs nearly edge-to-edge. */
#chat-tab .chat-form,
#chat-tab .chat-form *,
#chat-tab .chat-shell { box-sizing: border-box; }
#chat-tab .chat-shell { height: 100%; min-height: 0; flex: 1; padding: 0; display: flex; flex-direction: column; }
#chat-tab .chat-messages { flex: 1; min-height: 0; padding: var(--space-3) var(--space-4); }
#chat-tab .chat-form { padding: var(--space-2) var(--space-4) 12px; }
#chat-tab .chat-form textarea { padding: 4px 2px; }
#chat-tab .composer-actions { flex-wrap: wrap; gap: 8px; }

/* Dock header — title + context chip + min/max controls. Injected by
   features/chat/chatDock.js as the first child of #chat-tab. */
.chat-dock-head {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-muted);
  flex: 0 0 auto;
  cursor: grab; /* the header is the drag handle for the floating window */
  user-select: none;
}
.chat-dock-head:active { cursor: grabbing; }
body.chat-dock-max .chat-dock-head { cursor: default; } /* maxed window doesn't move */
.chat-dock-head__title { font-weight: 650; font-size: 0.9rem; color: var(--text); }
/* Small, subtle ⓘ info icon next to the title — same understated style as the
   Orbit leads tip icon (.ws-tip-i): a muted glyph that brightens on hover/open. */
.chat-dock-head__info {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; padding: 0;
  border: 0; background: transparent;
  color: var(--text-subtle); opacity: 0.65;
  font-size: 12px; line-height: 1; cursor: pointer;
}
.chat-dock-head__info:hover,
.chat-dock-head__info[aria-expanded="true"] { opacity: 1; color: var(--primary); }

/* Info popover — anchored under the header, left-aligned with the title. */
.chat-dock-info-pop {
  position: absolute;
  top: calc(100% + 6px);
  left: 8px;
  width: 280px; max-width: calc(100vw - 32px);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  padding: 12px 14px;
  z-index: 5;
  cursor: default;
  font-size: 0.8rem;
}
.chat-dock-info-pop[hidden] { display: none; }
.chat-info__grid {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 10px;
  margin: 0 0 8px;
}
.chat-info__grid dt { color: var(--text-subtle); font-size: 0.74rem; }
.chat-info__grid dd { margin: 0; color: var(--text); font-weight: 600; word-break: break-word; }
.chat-info__disclaimer {
  margin: 8px 0; padding-top: 8px;
  border-top: 1px solid var(--border);
  font-style: italic; font-size: 0.74rem; color: var(--text-faint);
}
.chat-info__help h4 { margin: 8px 0 4px; font-size: 0.74rem; color: var(--text-subtle); text-transform: uppercase; letter-spacing: 0.04em; }
.chat-info__help ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.chat-info__help li { font-size: 0.76rem; color: var(--text-muted); }
.chat-info__help code {
  font-family: var(--font-mono, monospace); font-size: 0.74rem;
  background: var(--bg-muted); border: 1px solid var(--border);
  border-radius: 4px; padding: 0 4px; color: var(--text);
}

/* Top-left resize grip for the floating window. */
.chat-dock-resize {
  position: absolute;
  top: 0; left: 0;
  width: 16px; height: 16px;
  cursor: nwse-resize;
  z-index: 6;
  touch-action: none;
}
.chat-dock-resize::before {
  content: ""; position: absolute; top: 4px; left: 4px;
  width: 7px; height: 7px;
  border-top: 2px solid var(--text-subtle);
  border-left: 2px solid var(--text-subtle);
  border-radius: 2px 0 0 0;
  opacity: 0.6;
}

/* Composer meta (agent label · disclaimer · model) is now surfaced in the header
   info popover — keep the input itself clean. The elements stay in the DOM so the
   model/agent renderers keep updating them (read by the popover). */
#chat-tab .composer-topbar { display: none; }
.chat-dock-head__btn--close:hover { background: var(--danger-soft, #fde8e8); color: var(--danger, #c0392b); }
.chat-dock-head__ctx {
  font-size: 0.72rem;
  color: var(--text-subtle);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 160px;
}
.chat-dock-head__spacer { flex: 1; }
.chat-dock-head__btn {
  width: 28px;
  height: 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-size: 0.95rem;
}
.chat-dock-head__btn:hover { background: var(--bg-hover); color: var(--text); }

/* Footer bar — in-flow at the bottom of the working area (a flex child of .canvas),
   so it reserves its own height and never overlays content. Holds the New-chat
   launcher + a tab per open conversation. It shifts right by the rail width when a
   secondary rail is shown (stopping at the rail's edge — see the rail rules above);
   `position: relative` makes its z-index real so it always sits above the rail + the
   floating dock where they meet. */
.chat-footer {
  position: relative;
  flex: 0 0 var(--chat-footer-h);
  height: var(--chat-footer-h);
  display: flex;
  align-items: stretch;
  gap: 6px;
  padding: 0 8px;
  border-top: 1px solid var(--border);
  background: var(--bg-surface);
  z-index: calc(var(--z-dock) + 1);
}
.chat-footer__new {
  display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto;
  border: 0; background: transparent; color: var(--text);
  font: inherit; font-size: 0.82rem; font-weight: 600;
  padding: 0 10px; cursor: pointer; border-radius: 6px;
}
.chat-footer__new:hover { background: var(--bg-muted); }
.chat-footer__new-label { white-space: nowrap; }
/* Actions pinned to the right corner (history + New chat). */
.chat-footer__actions { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; order: 2; }
.chat-footer__icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 28px; border: 0; background: transparent; color: var(--text-muted);
  font-size: 0.95rem; cursor: pointer; border-radius: 6px;
}
.chat-footer__icon:hover { background: var(--bg-muted); color: var(--text); }
.chat-footer__icon.is-active { background: var(--accent-soft, rgba(37, 99, 235,.09)); color: var(--primary); }
/* Tabs fill the bar and sit NEXT TO the right-corner actions, growing leftward. */
.chat-footer__tabs {
  display: flex; align-items: center; gap: 4px; order: 1;
  flex: 1; min-width: 0; overflow-x: auto; padding: 5px 0;
  justify-content: flex-end;
}
/* One open-chat tab (a saved conversation, or the live "New chat"). */
.chat-ftab {
  display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto;
  max-width: 220px; height: 28px; padding: 0 4px 0 10px;
  border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-app); color: var(--text-muted);
  font: inherit; font-size: 0.8rem; cursor: pointer; white-space: nowrap;
}
.chat-ftab:hover { background: var(--bg-muted); color: var(--text); }
.chat-ftab.is-active {
  background: var(--accent-soft, rgba(37, 99, 235,.09));
  color: var(--primary); border-color: var(--border-strong); font-weight: 600;
}
.chat-ftab__title { overflow: hidden; text-overflow: ellipsis; }
.chat-ftab__x {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border: 0; background: transparent; color: inherit;
  cursor: pointer; font-size: 0.95rem; line-height: 1; border-radius: 4px; opacity: .55;
}
.chat-ftab__x:hover { opacity: 1; color: var(--danger); background: var(--bg-hover); }
/* Unread dot on the launcher. */
.chat-dock-launcher__dot {
  width: 8px; height: 8px; border-radius: var(--radius-pill);
  background: var(--primary); display: none;
}
#chat-dock-launcher.has-unread .chat-dock-launcher__dot { display: inline-block; }

/* Chat history popover — opens above the footer, right-anchored (ChatGPT-style).
   Lists saved chats; click to reopen, ✕ to delete from history. */
.chat-history-pop {
  position: fixed;
  right: 10px;
  bottom: calc(var(--chat-footer-h) + 6px);
  z-index: calc(var(--z-dock) + 2);
  width: 340px;
  max-width: calc(100vw - 20px);
  max-height: min(60vh, 480px);
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.chat-history-pop[hidden] { display: none; }
.chat-history-pop__search {
  margin: 8px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-app); color: var(--text); font: inherit; font-size: 0.85rem;
}
.chat-history-pop__list { overflow-y: auto; padding: 0 6px 8px; }
.chat-history-pop__row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  border: 0; background: transparent; color: var(--text); cursor: pointer;
  padding: 8px 8px; border-radius: 8px; font: inherit;
}
.chat-history-pop__row:hover { background: var(--bg-hover); }
.chat-history-pop__row.is-active { background: var(--accent-soft, rgba(37, 99, 235,.09)); }
.chat-history-pop__title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.85rem; }
.chat-history-pop__date { color: var(--text-faint); font-size: 0.72rem; flex: 0 0 auto; }
.chat-history-pop__del {
  flex: 0 0 auto; width: 22px; height: 22px; border: 0; background: transparent; color: var(--text-faint);
  cursor: pointer; border-radius: 5px; font-size: 0.95rem; line-height: 1; opacity: .6;
}
.chat-history-pop__del:hover { opacity: 1; color: var(--danger); background: var(--bg-muted); }
.chat-history-pop__empty { padding: 16px; text-align: center; color: var(--text-faint); font-size: 0.82rem; }

/* The mobile sheet went with the floating window above — same dead selector, same reason. The
   panel's own narrow behaviour lives with the right rail (`body.chat-dock-open` under
   `@media (max-width: 720px)`). */
@media (max-width: 600px) {
  .chat-footer__new-label { display: none; }
}

/* ════════════════════════════════════════════════════════════════════════
   UX OVERHAUL — Inbox rail (All / Approvals / Tasks)
   The rail lives in partials/today.html; clicking an item adds a flt-<kind> class
   to #central-list, and these rules hide the non-matching sections (CSS-only).
   ════════════════════════════════════════════════════════════════════════ */
/* Flush rail scaffold — mirrors Hive: the in-page header is hidden, then
   [rail | content]; the content scrolls inside .central-main. (#central-tab is in the
   header-hide + .canvas-header+* margin-reset groups above.) */
#central-tab { padding: 0; }
/* The module body already carries ONE 32px side gutter on `.tab-content.active` (see the module-body
   block), and `.central-main`'s own gutter is zeroed to align to it — so the header host must NOT add
   a second `--page-gutter` on top, or the header sits further in than the body. Keep it flush so the
   breadcrumb/greeting line up exactly with the content below. */
#central-shell-host { padding-inline: 0; }
.canvas #central-tab.tab-content.active { display: flex; flex-direction: column; overflow: hidden; }
.central-shell { flex: 1; display: flex; min-height: 0; }
.central-main { flex: 1; min-width: 0; overflow-y: auto; padding: 20px var(--page-gutter) 40px; --page-pad-x: var(--page-gutter); }

/* Omni launcher on the Central home — a Google-style "what do you want to do today?" search that
   routes to modules / specific pages / create actions (features/search/commands.js). */
.central-omni { max-width: 680px; margin: 4px auto 28px; position: relative; }
.central-omni__box {
  display: flex; align-items: center; gap: 12px; margin: 0;
  background: var(--bg-surface, #fff);
  border: 1px solid transparent;   /* no border line — only the caret shows on focus */
  border-radius: 999px;
  padding: 13px 20px;
  box-shadow: 0 2px 10px rgba(15, 23, 42, .06);
  transition: box-shadow .15s ease;
}
/* Entering the field: a soft shadow lift only — no border, no focus ring, just the text cursor. */
.central-omni__box:focus-within {
  box-shadow: 0 4px 16px rgba(15, 23, 42, .10);
}
.central-omni__icon { width: 20px; height: 20px; flex: 0 0 auto; fill: none; stroke: var(--text-muted, #94a3b8); stroke-width: 2; stroke-linecap: round; }
.central-omni__input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none; padding: 0;
  font: inherit; font-size: 1.05rem; color: var(--text, #0f172a);
}
.central-omni__input::placeholder { color: var(--text-muted, #94a3b8); }
/* Kill the global `input:focus` border + 3px focus ring on the search inputs — Google-style, the
   caret is the only focus affordance (specificity beats `input:focus`). */
.central-omni__input:focus,
.topbar-search__input:focus { border-color: transparent; box-shadow: none; outline: none; }
.central-omni__results {
  position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 30;
  background: var(--bg-surface, #fff);
  border: 1px solid var(--border, #e5e7eb);
  border-radius: 16px;
  box-shadow: 0 16px 44px rgba(15, 23, 42, .17);
  padding: 6px; max-height: 60vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
}
.central-omni__results[hidden] { display: none; }
.central-omni__item {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: none; border: 0; border-radius: 10px; padding: 10px 14px;
  font: inherit; color: inherit; cursor: pointer;
}
.central-omni__item:hover, .central-omni__item.is-active { background: var(--track, #f1f5f9); }
.central-omni__kind {
  flex: 0 0 auto; min-width: 64px; text-align: center;
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--primary, #1d4ed8); background: rgba(29, 78, 216, .10);
  padding: 3px 8px; border-radius: 6px;
}
.central-omni__label { flex: 1; min-width: 0; font-size: .95rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Results open → merge the search box + results into ONE connected box (Google-style): the box's
   bottom corners flatten with a hairline divider, the results attach directly below (no gap) with a
   rounded bottom, and both share one shadow — the opaque results panel occludes the box's downward
   cast, so the pair reads as a single rounded card. */
.central-omni:has(.central-omni__results:not([hidden])) .central-omni__box {
  border-radius: 22px 22px 0 0;
  border-bottom: 1px solid var(--border, #e5e7eb);
  box-shadow: 0 8px 28px rgba(15, 23, 42, .12);
}
.central-omni:has(.central-omni__results:not([hidden])) .central-omni__results {
  top: 100%; margin-top: 0;
  border: 0; border-radius: 0 0 22px 22px;
  box-shadow: 0 8px 28px rgba(15, 23, 42, .12);
}
/* Approvals view → only approval sections; My Pendings → tasks + due runs;
   Notifications → only notification sections. */
.central-feed.flt-approvals .central-feed-section:not([data-kind='approvals']) { display: none; }
.central-feed.flt-tasks .central-feed-section:not([data-kind='tasks']):not([data-kind='due']) { display: none; }
.central-feed.flt-notifications .central-feed-section:not([data-kind='notifications']) { display: none; }
.central-feed.flt-due .central-feed-section:not([data-kind='due']) { display: none; }
.central-feed.flt-outputs .central-feed-section:not([data-kind='outputs']) { display: none; }

/* The view switch toggles [hidden]; .central-feed sets display:flex (an author rule
   that outranks the UA [hidden] rule), so hide the panes explicitly by id/class. */
#central-list[hidden], #central-home[hidden], .central-view[hidden] { display: none; }
/* Analytics/Manage placeholder panels — full-width scroll surface like .central-main. */
.central-view { min-width: 0; }
.central-view .empty-state { padding: 32px 8px; max-width: 60ch; }
/* Today's per-kind content pages (2026-08-24, user direction) — a plain title over the same
   .iq-ftable a dashboard feed widget renders, just with its row cap lifted. */
.central-panel__title { font-size: 1.1rem; font-weight: 700; margin: 4px 2px 14px; }

/* ── Module Home page ──────────────────────────────────────────────────────────
   A reusable module landing view (.mod-home). Relaxed, airy typography; every
   block is a "hand-drawn" curvy rectangle (.hd-box) with an asymmetric radius +
   sketchy line art. Module-agnostic, so any module can reuse the markup. */
.mod-home {
  max-width: 1080px;
  margin: 0 auto;
  padding: 6px 6px 56px;
  font-size: 0.95rem;
  line-height: 1.75;
  letter-spacing: 0.005em;
  color: var(--text);
}
/* The hand-drawn box: organic, uneven corner radii read as a sketched rectangle. */
.hd-box {
  border: 2px solid var(--border);
  border-radius: 24px 18px 26px 20px / 20px 26px 18px 24px;
  background: var(--bg-surface);
  box-shadow: 0 1px 0 var(--border);
}

/* Top row: intro (left) + Get started box (right). */
.mod-home-top {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 22px;
  margin-bottom: 30px;
}
.mod-home-intro { min-width: 240px; }
.mod-home-name {
  margin: 4px 0 6px;
  font-size: 1.9rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.mod-home-sub {
  margin: 0;
  max-width: 46ch;
  color: var(--text-muted);
  font-size: 1rem;
}
.mod-home-getstarted {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 22px;
  min-width: 240px;
}
.mod-home-getstarted-title { font-weight: 700; font-size: 1.02rem; }
/* Two compact CTAs side by side (My queue · My dashboard) on the Central home. */
.mod-home-cta-row { display: flex; flex-wrap: wrap; gap: 10px; }
.mod-home-cta--ghost {
  background: transparent;
  color: var(--primary);
  box-shadow: inset 0 0 0 1.5px var(--primary);
}
.mod-home-cta--ghost:hover { background: var(--accent-soft, rgba(29, 78, 216, .08)); filter: none; }
.mod-home-cta {
  align-self: flex-start;
  display: inline-block;
  padding: 9px 18px;
  border-radius: 16px 12px 16px 12px / 12px 16px 12px 16px;
  background: var(--primary);
  color: var(--primary-ink, #fff);
  font-weight: 700;
  font-size: 0.9rem;
  text-decoration: none;
  transition: transform 0.12s ease, filter 0.12s ease;
}
.mod-home-cta:hover { filter: brightness(1.06); transform: translateY(-1px); }
/* Coming-soon modules get an inert CTA. */
.mod-home-cta.is-disabled { opacity: 0.5; cursor: default; pointer-events: none; }

/* Overview block. */
.mod-home-overview { padding: 20px 24px; margin-bottom: 34px; }
.mod-home-overview h3 { margin: 0 0 8px; font-size: 1.1rem; font-weight: 700; }
.mod-home-overview p { margin: 0; max-width: 78ch; color: var(--text-muted); }

/* Important links. */
.mod-home-links-title {
  margin: 0 0 16px;
  font-size: 1.1rem;
  font-weight: 700;
}
.mod-home-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 20px;
}
.mod-home-card {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 20px 22px;
  text-decoration: none;
  color: inherit;
  transition: transform 0.14s ease, box-shadow 0.14s ease, border-color 0.14s ease;
}
.mod-home-card:hover {
  transform: translateY(-3px);
  border-color: var(--primary);
  box-shadow: 0 8px 22px rgba(37, 99, 235, 0.12);
}
/* Sketchy line art — inherits stroke from currentColor, no fill. */
.mod-home-art {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  color: var(--primary);
  fill: none;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Give each card's art a faintly different hue so the row feels hand-made. */
.mod-home-card:nth-child(2) .mod-home-art { color: #b45309; }
.mod-home-card:nth-child(3) .mod-home-art { color: var(--primary); }
.mod-home-card:nth-child(4) .mod-home-art { color: #9333ea; }
.mod-home-card:nth-child(5) .mod-home-art { color: #db2777; }
.mod-home-card-body { display: flex; flex-direction: column; gap: 6px; }
.mod-home-card-title { font-weight: 700; font-size: 1.02rem; }
.mod-home-card-tag { color: var(--text-muted); font-size: 0.88rem; line-height: 1.65; }

/* ── Kriya resource pages (.wk-doc) ─────────────────────────────────────────────
   The Home "Important links" destinations: Quick Start, Wiki, Support, Request
   Features, General. Airy help-center prose reusing the .hd-box card look. */
.wk-doc {
  max-width: 920px;
  margin: 0 auto;
  padding: 6px 6px 64px;
  font-size: 0.95rem;
  line-height: 1.72;
  color: var(--text);
}
.wk-doc-back {
  display: inline-block;
  margin-bottom: 18px;
  font-size: 0.86rem;
  font-weight: 600;
  color: var(--primary);
  text-decoration: none;
}
.wk-doc-back:hover { text-decoration: underline; }
.wk-doc-hero { margin-bottom: 34px; }
.wk-doc-title {
  margin: 0 0 8px;
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
}
.wk-doc-promise { margin: 0; max-width: 62ch; color: var(--text-muted); font-size: 1.05rem; }
.wk-doc-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.wk-doc-chip {
  padding: 3px 11px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--bg-surface);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-muted);
}
.wk-doc-sec { margin-bottom: 38px; }
.wk-doc-h2 {
  margin: 0 0 16px;
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.wk-doc p { margin: 0 0 10px; max-width: 74ch; }
.wk-doc-inline-link { color: var(--primary); font-weight: 600; text-decoration: none; }
.wk-doc-inline-link:hover { text-decoration: underline; }

/* Numbered steps (Quick Start). */
.wk-doc-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 18px; }
.wk-doc-step { display: flex; gap: 16px; align-items: flex-start; }
.wk-doc-step-n {
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-ink, #fff);
  font-weight: 800;
  font-size: 0.95rem;
}
.wk-doc-step-body { min-width: 0; }
.wk-doc-step-title { margin: 4px 0 6px; font-size: 1.05rem; font-weight: 700; }
.wk-doc-path {
  margin: 4px 0 0;
  font-size: 0.82rem;
  color: var(--text-muted);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}

/* Concept / channel cards (Wiki, Support, Request Features). */
.wk-doc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.wk-doc-card { padding: 16px 18px; }
.wk-doc-card-title { margin: 0 0 6px; font-size: 1rem; font-weight: 700; }
.wk-doc-card p { margin: 0; color: var(--text-muted); font-size: 0.9rem; }
.wk-doc-channel { display: flex; gap: 14px; align-items: flex-start; }
.wk-doc-channel-ico { color: var(--primary); flex-shrink: 0; }
.wk-doc-channel-ico svg { width: 26px; height: 26px; }

/* Buttons. */
.wk-doc-btn {
  display: inline-block;
  margin-top: 12px;
  padding: 8px 15px;
  border: 1px solid var(--border);
  border-radius: 14px 10px 14px 10px / 10px 14px 10px 14px;
  background: var(--bg-surface);
  color: var(--text);
  font-weight: 600;
  font-size: 0.86rem;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s ease, filter 0.12s ease, border-color 0.12s ease;
}
.wk-doc-btn:hover { border-color: var(--primary); transform: translateY(-1px); }
.wk-doc-btn--primary { background: var(--primary); color: var(--primary-ink, #fff); border-color: var(--primary); }
.wk-doc-btn--primary:hover { filter: brightness(1.06); }
.wk-doc-btn--lg { margin-top: 18px; padding: 11px 22px; font-size: 0.95rem; }

/* FAQ accordion (Support). */
.wk-doc-faq { padding: 0; margin-bottom: 12px; }
.wk-doc-faq-q {
  padding: 14px 18px;
  font-weight: 700;
  font-size: 0.96rem;
  cursor: pointer;
  list-style: none;
}
.wk-doc-faq-q::-webkit-details-marker { display: none; }
.wk-doc-faq-q::before { content: '＋'; margin-right: 10px; color: var(--primary); font-weight: 700; }
.wk-doc-faq[open] .wk-doc-faq-q::before { content: '－'; }
.wk-doc-faq-a { padding: 0 18px 16px 40px; }
.wk-doc-faq-a p { margin: 0; color: var(--text-muted); }

/* Tips + next-step cards. */
.wk-doc-tips { margin: 0; padding-left: 22px; display: flex; flex-direction: column; gap: 9px; }
.wk-doc-tips li { color: var(--text-muted); }
.wk-doc-tips strong { color: var(--text); }
.wk-doc-next { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.wk-doc-nextcard {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 16px 18px;
  text-decoration: none;
  color: inherit;
  transition: transform 0.14s ease, border-color 0.14s ease;
}
.wk-doc-nextcard:hover { transform: translateY(-2px); border-color: var(--primary); }
.wk-doc-nextcard strong { font-size: 1rem; }
.wk-doc-nextcard span { color: var(--text-muted); font-size: 0.85rem; }

/* Request-a-feature form. */
.wk-doc-form { padding: 22px 24px; display: flex; flex-direction: column; gap: 16px; }
.wk-doc-field { display: flex; flex-direction: column; gap: 6px; font-weight: 600; font-size: 0.9rem; }
.wk-doc-field input, .wk-doc-field textarea, .wk-doc-field select {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  color: var(--text);
  font: inherit;
  font-weight: 400;
}
.wk-doc-field textarea { resize: vertical; }
.wk-doc-form-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.wk-doc-field--sm { flex: 1; min-width: 200px; }
.wk-doc-form-note { margin: 0; font-size: 0.84rem; color: var(--text-muted); }

/* Community landing (General). */
.wk-doc-community { padding: 30px 28px; text-align: center; }
.wk-doc-community p { margin: 0 auto 6px; max-width: 60ch; color: var(--text-muted); }
.wk-doc-community-ico { display: inline-block; margin-bottom: 8px; }
.wk-doc-community-ico svg { width: 40px; height: 40px; }

/* Bell → Inbox link (ties notifications to the unified Inbox). */
.tb-notif-foot {
  display: block;
  text-align: center;
  padding: 9px;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary);
  border-top: 1px solid var(--border);
  text-decoration: none;
}
.tb-notif-foot:hover { background: var(--bg-hover); }

/* ── App Admin (platform back-office, appadmin/) ─────────────────────────── */
/* Second-left-menu shell (mirrors the Orbit/Flux/Hive rail pattern): header on top,
   then [rail | content]. The content scrolls inside .appadmin-main. */
/* Cancel the global `.tab-content { padding: 24px 28px }` so the rail sits flush
   against the main sidebar and the content starts at the top (matches Hive). */
#app-admin-tab { padding: 0; }
.canvas #app-admin-tab.tab-content.active { display: flex; flex-direction: column; overflow: hidden; }
#app-admin-tab > #appadmin-shell-host { flex: 0 0 auto; }
.appadmin-shell { flex: 1; display: flex; min-height: 0; }
/* App Admin's rail container comes from the shared .page-left-rail (no per-rail settings). */
.appadmin-nav-btn { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; border: 0;
  background: transparent; color: var(--text-muted); font: inherit; font-size: .78rem; font-weight: 500;
  padding: 7px 9px; border-radius: 6px; cursor: pointer; }
.appadmin-nav-btn:hover { background: var(--bg-muted); color: var(--text); }
.appadmin-nav-btn.is-active { background: var(--accent-soft, rgba(37, 99, 235,.09)); color: var(--primary); font-weight: 600; }
.appadmin-nav-ico { width: 18px; text-align: center; flex-shrink: 0; }
.appadmin-main { flex: 1; min-width: 0; overflow-y: auto; padding: 20px 24px 40px; }
.appadmin-body { padding: 4px 2px 24px; }
.appadmin-section { margin: 18px 0; }
.appadmin-section__title {
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0 0 10px;
  color: var(--text);
}
.appadmin-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 12px;
}
.appadmin-stat { padding: 14px 16px; }
.appadmin-stat__value { font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
.appadmin-stat__label { font-size: 0.82rem; color: var(--text-muted); margin-top: 4px; }
.appadmin-stat__sub { font-size: 0.74rem; color: var(--text-muted); margin-top: 2px; opacity: 0.8; }
.appadmin-table { width: 100%; margin-top: 6px; }
/* Plans & Limits (and reusable elsewhere): each block is a white card box. */
.appadmin-card {
  background: var(--bg-surface, #fff);
  border: 1px solid var(--border-muted, var(--border, #e5e7eb));
  border-radius: 12px;
  padding: 16px 18px;
  margin: 0 0 16px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 2px 6px rgba(16, 24, 40, .04);
}
.appadmin-card--head { background: var(--bg-muted, #f8fafc); }
.appadmin-card__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 2px; }
.appadmin-card__title { font-size: 0.95rem; font-weight: 700; margin: 0; color: var(--text); }
.appadmin-card__tag {
  font-size: 0.68rem; font-weight: 600; color: var(--text-muted);
  background: var(--bg-muted, #f1f5f9); padding: 1px 7px; border-radius: 8px;
}
.appadmin-card__desc { font-size: 0.8rem; color: var(--text-muted); margin: 3px 0 10px; line-height: 1.45; }
.appadmin-card .appadmin-table { margin-top: 8px; }
/* Wide tables (module matrix, limits) scroll horizontally inside their card instead of overflowing. */
.appadmin-table-scroll { overflow-x: auto; margin-top: 8px; }
.appadmin-table-scroll .appadmin-table { margin-top: 0; }
/* Responsive grid of per-plan pricing cards. */
.appadmin-cards-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 14px;
  margin-top: 8px;
}
.appadmin-plan-card { margin: 0; overflow-x: auto; }
/* Module matrix footer: each plan's Save button sits in its own column cell. */
.appadmin-matrix-foot > td { padding-top: 10px; text-align: center; border-top: 1px solid var(--border-muted, var(--border, #e5e7eb)); }
.appadmin-matrix-foot__cell .btn { width: 100%; }
.appadmin-grant-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 12px;
  align-items: flex-start;
  margin-bottom: 6px;
}
.appadmin-grant-row .input { width: 100%; min-width: 0; }
/* EACH BOX SAYS WHAT IT IS, above itself (2026-09-25). The row was three placeholder-only inputs,
   and a placeholder stops being a label the moment somebody types in it — including the one whose
   whole text was "Reason (required, audited)". */
.appadmin-grant-field { display: flex; flex-direction: column; gap: 3px; flex: 1 1 200px; min-width: 170px; margin: 0; }
.appadmin-grant-field__label {
  font-size: .7rem; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--text-muted);
}
.appadmin-grant-field__hint { font-size: .7rem; color: var(--text-faint); }
/* The form is one object on the page, like the tables above it are. */
.appadmin-grant-form {
  padding: 14px 16px;
  border: 1px solid var(--border-muted, var(--border));
  border-radius: 10px;
  background: var(--bg-surface);
}
.appadmin-grant-form .appadmin-section__title { margin-top: 0; }

/* A section's supporting line. It was `.empty-state small`, which is the vocabulary for "there is
   nothing here" — borrowed for prose it renders the page's explanations in the faint ink reserved
   for absence. */
.appadmin-section__lede {
  margin: -4px 0 10px;
  font-size: .8rem;
  line-height: 1.45;
  color: var(--text-muted);
  max-width: 78ch;
}

/* ── THE PERMISSION MATRIX ────────────────────────────────────────────────────────────────────
   2026-09-25, user report: App Admin ▸ People & Roles, *"the permission table in it are not
   looking good"*.

   It was `<table class="analytics-table">` — a class with NO rules anywhere in this sheet — so
   every cell fell through to the UA stylesheet: `th` centred with 1px of padding, which is why
   "Analyst Billing Admin Security Admin Super Admin Support" read as one run-on line. The cells
   carried their alignment and colour as inline `style=` attributes, which no sweep here can see.

   It joins the shared data-table standard now (`.app-table`), and adds the two things a MATRIX
   needs that a list does not: the permission column pinned to the left while the roles scroll,
   and a mark per cell that is a shape rather than a stroke. */
.rbac-matrix { overflow-x: auto; border: 1px solid var(--border-muted, var(--border)); border-radius: 10px; }
.rbac-matrix__table { width: max-content; min-width: 100%; margin: 0; }
/* The names are the content; the columns are as wide as they need and no wider. */
.rbac-matrix__table th,
.rbac-matrix__table td { white-space: nowrap; vertical-align: middle; }
/* PINNED, so the row being read never loses its name. Sticky resolves against the nearest
   scrollport, which is the wrapper above. An opaque fill is not optional on a pinned cell: the
   scrolling columns would otherwise travel visibly through it. */
.rbac-matrix__perm {
  position: sticky; left: 0; z-index: 1;
  background: var(--bg-surface);
  text-align: left; font-weight: 500; color: var(--text);
  border-right: 1px solid var(--border-muted, var(--border));
}
.rbac-matrix__table tbody tr:hover .rbac-matrix__perm { background: var(--table-row-hover, #e7edfa); }
.rbac-matrix__cell { text-align: center; }
/* Granted is a filled tick in the success ink; not-granted is a quiet dash rather than a middle
   dot — at this size a `·` is a speck and reads as a rendering fault. Neither is colour ALONE:
   the two glyphs differ, so the matrix survives being read without colour. */
.rbac-matrix__cell.is-yes { color: var(--success, #067647); font-weight: 700; }
.rbac-matrix__cell.is-no { color: var(--text-faint, #98a2b3); }
/* A group divider collects the rows under it — it is a heading inside the table, so it takes the
   heading treatment rather than a bold cell with an inline background. */
.rbac-matrix__group > td {
  position: sticky; left: 0;
  background: var(--bg-muted);
  font-size: .68rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted);
}
.appadmin-badge { font-size: 0.7rem; font-weight: 600; padding: 1px 6px; border-radius: 8px; margin-left: 6px; }
.appadmin-badge--danger { background: var(--danger-bg, #fde8e8); color: var(--danger, #b42318); }
/* Bug-report status badges (margin-left:0 — they stand alone in the Status cell). */
.appadmin-badge--submitted, .appadmin-badge--open { margin-left: 0; background: #e0e7ff; color: #3730a3; }
.appadmin-badge--approved, .appadmin-badge--triaged { margin-left: 0; background: #cffafe; color: #155e75; }
.appadmin-badge--in_fix { margin-left: 0; background: #fef3c7; color: #92400e; }
.appadmin-badge--fixed { margin-left: 0; background: #dcfce7; color: #166534; }
.appadmin-badge--closed { margin-left: 0; background: #e5e7eb; color: #374151; }
.appadmin-badge--rejected { margin-left: 0; background: #fde8e8; color: #b42318; }
/* Priority badges (stand alone in the Priority cell). */
.appadmin-badge--prio-low { margin-left: 0; background: var(--bg-muted); color: var(--text-subtle); }
.appadmin-badge--prio-normal { margin-left: 0; background: #e0e7ff; color: #3730a3; }
.appadmin-badge--prio-high { margin-left: 0; background: #fef3c7; color: #92400e; }
.appadmin-badge--prio-critical { margin-left: 0; background: #fde8e8; color: #b42318; font-weight: 700; }

/* Bug-report list: neatly aligned, with classy, light row lines. */
.appadmin-table--lined { border-collapse: collapse; }
.appadmin-table--lined th,
.appadmin-table--lined td {
  padding: 9px 14px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--border-muted, var(--border));
}
.appadmin-table--lined thead th {
  font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-faint); border-bottom: 1px solid var(--border); white-space: nowrap;
}
.appadmin-table--lined tbody tr:last-child td { border-bottom: 0; }
.appadmin-table--lined tbody tr:hover { background: var(--bg-hover); }
.appadmin-td--nowrap { white-space: nowrap; color: var(--text-muted); font-size: 0.82rem; }
.appadmin-td--actions { white-space: nowrap; }
/* ══ A RECORD'S ACTIVITY, ONE ROW PER ACTIVITY ═══════════════════════════════════════════════
   (2026-08-23, user direction.) It was a stack of cards printing every note in full, so a lead
   with fifteen logged calls ran for pages and the fields a reader compares — owner, when, done —
   sat in a run-on meta line instead of in columns. The note is the only thing a line cannot
   hold, so it is the only thing behind the toggle.

   The table itself takes the app-wide `.orbit-table` treatment (the "MODULE LIST TABLES" block
   at the end of this sheet); everything here is the four things that block cannot know about:
   the two icon columns, the toggle, and the detail row. */
.orbit-actt { width: 100%; table-layout: fixed; min-width: 620px; }
.orbit-actt__c { vertical-align: middle; }
/* AND THE HOST SCROLLS SIDEWAYS (2026-08-25, user direction). On a record page this table sits
   in the right rail at 280-360px, where `table-layout: fixed` gave the five fixed columns their
   460px and left the SUBJECT — the one column with the content — about forty pixels to ellipsize
   into. Every row read "Cal…". A floor on the table plus a scroller on its host means the columns
   keep the widths that make them comparable and the reader pans to the rest, which is what the
   list pages elsewhere in the app already do. `min-width` beats `width: 100%` here, so the table
   still fills a wide host. */
.orbit-timeline-body { overflow-x: auto; }
/* Fixed layout so the subject is the column that gives, not the dates and names a reader
   scans down — those must stay aligned or the table is no better than the cards it replaced. */
.orbit-actt__c--tog  { width: 26px; padding-right: 0; }
.orbit-actt__c--done { width: 30px; text-align: center; padding-left: 0; }
.orbit-actt__c--type { width: 78px; text-transform: capitalize; color: var(--text-muted); }
.orbit-actt__c--who  { width: 132px; color: var(--text-muted); }
.orbit-actt__c--when { width: 132px; white-space: nowrap; color: var(--text-muted); }
.orbit-actt__c--act  { width: 62px; white-space: nowrap; text-align: right; }
/* The subject is the line's content: it takes the rest and ellipsizes rather than wrapping,
   because one row per activity is the whole point. Its `title` carries the full text. */
.orbit-actt__c--subj { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
.orbit-actt__tog {
  border: 0; background: transparent; color: var(--text-muted); cursor: pointer;
  padding: 2px 4px; border-radius: 6px; font-size: .8rem; line-height: 1;
}
.orbit-actt__tog:hover { color: var(--text); background: var(--bg-muted); }
/* An activity with no note gets a SPACER, not a dead control — the column stays aligned and
   nothing invites a click that would reveal an empty box. */
.orbit-actt__tog.is-empty { display: inline-block; width: 18px; }
.orbit-actt__ico { font-size: .95rem; }
.orbit-actt__when { font-variant-numeric: tabular-nums; }
.orbit-actt__flag {
  margin-left: 6px; font-size: .64rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; color: var(--danger, #b91c1c);
}
.orbit-actt__btn { border: 0; background: transparent; color: var(--text-muted); cursor: pointer; padding: 2px 4px; border-radius: 6px; }
.orbit-actt__btn:hover { color: var(--text); background: var(--bg-muted); }
.orbit-actt__btn--danger:hover { color: var(--danger, #b91c1c); }
/* A done task is quieter but still readable — struck through would fight the ellipsis. */
.orbit-actt__row.is-done .orbit-actt__c--subj { color: var(--text-muted); font-weight: 500; }
.orbit-actt__row.is-overdue .orbit-actt__c--when { color: var(--danger, #b91c1c); }
.orbit-actt__row.is-open > .orbit-actt__c { border-bottom: 0; }
/* The note, indented under its line so the two read as one thing. */
.orbit-actt__detail > .orbit-actt__c { padding-top: 0; padding-left: 56px; }
.orbit-actt__note { white-space: pre-wrap; color: var(--text); font-size: .86rem; line-height: 1.5; }
.orbit-actt__note:empty { display: none; }
.orbit-actt__facts { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 6px; font-size: .74rem; color: var(--text-muted); }
.orbit-actt__fact b { font-weight: 700; }

/* ══ NARROWING A FINANCE TABLE ═══════════════════════════════════════════════════════════════
   (2026-08-23, user direction: search and filters on every page.) Artha declares `search` and
   `filters` per view, but a declared filter does nothing until that view's own loader passes it
   to its API call — 4 of 79 pages had search and 32 had filters, so declaring the rest would
   have shipped forty controls that move nothing. This strip narrows the rows ON SCREEN instead,
   and is derived from the table the page just rendered, so it cannot be dead.

   Above the table it acts on, never in the module toolbar: the toolbar is painted before the
   data exists and cannot know this page has a Status column with four values. */
.artha-narrow {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 10px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--bg-muted);
}
.artha-narrow__q {
  flex: 1 1 200px; min-width: 160px; max-width: 320px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface);
  color: var(--text); font: inherit; font-size: .84rem; padding: 5px 9px;
}
.artha-narrow__f { display: inline-flex; align-items: center; gap: 5px; font-size: .76rem; color: var(--text-muted); }
.artha-narrow__f > span { white-space: nowrap; }
.artha-narrow__f .app-select { font-size: .8rem; padding: 4px 20px 4px 7px; max-width: 170px; }
/* The count is empty while nothing is narrowed — a chip that always reads "40 of 40" trains
   people to stop reading it. */
.artha-narrow__count { font-size: .78rem; font-weight: 700; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.artha-narrow__count:empty { display: none; }
.artha-narrow__clear { margin-left: auto; }
.artha-narrow__none { margin: 8px 0 0; }

/* ══ A DEAL'S QUOTATIONS, ONE ROW PER QUOTE ══════════════════════════════════════════════════
   (2026-08-23, user direction.) They were flex rows with a `<details>` hanging off each, so the
   quotes section and the activity section two blocks below answered the same question —
   "what is the history here" — in two different shapes on one page. Same contract as the
   activity table: the line holds everything that fits, the items are the only thing behind the
   toggle, and the detail row is in the DOM already so opening it costs nothing.

   `.orbit-table` (the app-wide MODULE LIST TABLES block) does the skin; these are the columns
   it cannot know about. The whole table is inside `.orbit-li-scroll`, which is what gives a
   quote with many columns a horizontal SCROLLBAR instead of pushing the section wide. */
.orbit-qt { width: 100%; }
.orbit-qt__c { vertical-align: middle; }
.orbit-qt__c--tog { width: 26px; padding-right: 0; }
/* THE CUSTOMER-FACING REFERENCE (2026-09-04). `Q-0142` is what the customer says on the phone,
   what prints on their document and what names the downloaded file — and the row showed only
   `v1`. Tabular figures so a column of references lines up; nowrap because a broken reference is
   worse than a scrollbar, which this table already has. */
.orbit-qt__c--num { width: 92px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.orbit-qt__c--ver { width: 62px; }
.orbit-qt__c--date { width: 104px; white-space: nowrap; color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* VALID TILL is edited in the row while the quote is a draft — one field, in the column somebody
   looks for it in, rather than a form for a single date. Quiet until hovered or focused: a boxed
   input down an otherwise read-only table reads as a form, and only this cell is editable. */
.orbit-qt__valid { width: 100%; min-width: 0; margin: 0; padding: .15rem .25rem; font: inherit;
  font-size: .82rem; color: var(--text); background: transparent; border: 1px solid transparent;
  border-radius: 6px; }
.orbit-qt__valid:hover { border-color: var(--border-muted); background: var(--bg-surface); }
.orbit-qt__valid:focus { border-color: var(--border); background: var(--bg-surface); outline: none; }
.orbit-qt__c--status { width: 124px; }
.orbit-qt__c--disc { width: 74px; }
.orbit-qt__c--total { width: 120px; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The actions cluster WRAPS rather than widening the row — a quote carries up to five controls
   (document · print · email · → Order · delete) where a line item carries two, and it was
   `white-space: nowrap` on the old row that pushed them out of it. */
.orbit-qt__c--act { white-space: normal; text-align: right; }
.orbit-qt__c--act > * { margin-left: 4px; }
/* A quote blocked on a discount approval is marked on the LINE, so the reader does not have to
   expand every row to find the one that cannot be sent. */
.orbit-qt__row.is-blocked .orbit-qt__c--total { color: var(--danger, #b91c1c); }
.orbit-qt__row.is-open > .orbit-qt__c { border-bottom: 0; }
.orbit-qt__detail > .orbit-qt__c { padding-top: 0; padding-left: 34px; }
.orbit-qt__warn {
  margin: 0 0 8px; padding: 6px 10px; border-radius: 8px; font-size: .8rem;
  border: 1px solid color-mix(in srgb, var(--danger, #b91c1c) 30%, transparent);
  background: color-mix(in srgb, var(--danger, #b91c1c) 8%, transparent);
}

/* ── Selecting bug reports for a bulk action ──────────────────────────────────────────────────
   The tick is a LEAD cell, not a column: it decides what an action applies to, so it must not
   be hideable or reorderable (`ui/tableKit.js`'s rule for non-data cells). Narrow and centred so
   the reading columns keep the width they had. */
.appadmin-th--tick, .appadmin-td--tick { width: 34px; text-align: center; padding-right: 0; }
.appadmin-tick { cursor: pointer; margin: 0; vertical-align: middle; }
/* A selected row is tinted, not outlined: the bar states the count, and this only has to make
   "which ones" answerable at a glance while scrolling a long queue. */
.appadmin-table tr.is-selected > td { background: color-mix(in srgb, var(--primary) 8%, transparent); }
/* The bar appears only while something is ticked, so it is allowed to be loud. It sits between
   the filters and the table — above what it acts on, and below the controls that decided the
   set. */
.appadmin-bulk {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 10px 0 12px; padding: 8px 12px;
  border: 1px solid color-mix(in srgb, var(--primary) 34%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--primary) 10%, var(--bg-surface, #fff));
}
.appadmin-bulk__n { font-size: .86rem; }
/* Why there is nothing to press, when the selection spans stages that share no next step. */
.appadmin-bulk__why { margin: 0; }
.appadmin-bulk__clear { margin-left: auto; }
.appadmin-td--msg { color: var(--text); font-size: 0.85rem; min-width: 280px; max-width: 520px; }
/* Sales-Discounts cells that stack a headline over muted supporting lines — the customer's
   org NAME over its id, or the granted terms over the date they lapse. Spans stay inline in a
   plain <td>, so the column owns the stacking rather than each span. */
.appadmin-table td.appadmin-td--stack { display: flex; flex-direction: column; gap: 2px; }
.appadmin-td--stack .empty-state.small { padding: 0; }

/* Filter bar above the bug list. */
.appadmin-filters { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin: 10px 0 6px; }

/* Themeable filter/toolbar bars across modules — the same identity as the Kriya filter bar:
   a contained, body-width, rounded bar using the user's filter colour (Settings → Header &
   filter colours). Covers the named filter rows modules render — Orbit/Artha .app-page-filters,
   Hive .hive-page-filters, App-Admin .appadmin-filters. Kriya's own bars are themed separately. */
/* `.artha-gateline` is an inline CONTROL ROW inside a panel — a policy switch beside the
   state it governs — not a page header. It shares the look because it is the same object
   visually, and it is named separately because `ui/chromeConformance.spec.js` counts
   `.app-page-filters` as pre-standard PAGE chrome and holds each module to a debt figure.
   Borrowing that class for a panel row would have added three to Artha's count and taught
   the next reader that this is how a page header is written. */
.app-page-filters,
.artha-gateline,
.hive-page-filters,
.appadmin-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 8px 12px;
  margin: 12px 0;
  background: var(--app-filter-bg);
  border: 1px solid var(--app-filter-border);
  border-radius: 10px;
}
.appadmin-filter { display: inline-flex; align-items: center; gap: 6px; font-size: 0.78rem; color: var(--text-subtle); }

/* ─── App-admin stacked-field form (create coupon) ───────────────────────────
   A proper card form: labels above full-width inputs in a responsive grid,
   per-field hints, and a right-aligned footer button row. */
.appadmin-coupon-form { max-width: 880px; }
.appadmin-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 12px 16px;
  margin-top: 10px;
}
.appadmin-form-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; }
.appadmin-form-field__label {
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-subtle);
}
.appadmin-form-field__req { color: var(--danger, #b42318); }
.appadmin-form-field__hint { font-size: 0.72rem; color: var(--text-faint); line-height: 1.35; }
.appadmin-form-field .input { width: 100%; }
.appadmin-input-group { display: flex; gap: 6px; }
.appadmin-input-group .input { flex: 1; min-width: 0; }
.appadmin-input-group .btn { flex: 0 0 auto; }
/* The what-does-this-type-do line under the Type select. */
.appadmin-form-hint { font-size: 0.78rem; color: var(--text-muted); margin: 10px 0 0; line-height: 1.45; }
/* Inline status/validation line — red only when it carries an error. */
.appadmin-form-error { font-size: 0.8rem; color: var(--text-muted); margin: 10px 0 0; min-height: 1em; }
.appadmin-form-error.error { color: var(--danger, #b42318); }
.appadmin-form-actions {
  display: flex; justify-content: flex-end; gap: 8px;
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid var(--border-muted, var(--border, #e5e7eb));
}

/* ─── Global filter/tab-row standard (UI Polish) ─────────────────────────────
   The row of view/section/filter tabs atop a module's body must be TIGHT and
   ICON-LED: small padding + gap, an icon on each chip, labels kept short. Wrapping
   is allowed (never clip/scroll-hide), but the compact sizing means it usually sits
   on one line and takes minimal vertical space — leaving maximum room for content.
   Reuse .app-filter-tabs / .app-filter-tab for new bars; the shared rule below also
   tightens the per-module tab groups that opt in by class. */
.app-filter-tabs { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 2px; padding: 2px;
  background: var(--bg-app); border: 1px solid var(--border-muted, var(--border)); border-radius: 9px; }
.app-filter-tab { display: inline-flex; align-items: center; gap: 5px; border: 0; background: none;
  padding: 4px 10px; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-muted);
  cursor: pointer; border-radius: 7px; white-space: nowrap; }
.app-filter-tab:hover { color: var(--text); }
.app-filter-tab.is-active { background: var(--bg-surface); color: var(--primary); box-shadow: 0 1px 2px rgba(16,24,40,.08); }
/* Icons inside any filter chip stay compact so an icon + short label reads as one tight unit. */
.app-filter-tab .hicon,
.app-filter-tabs .hicon,
.app-page-filters .hicon { width: 1em; height: 1em; }
/* Filter chips may drop their label to icon-only on very narrow screens (tooltip keeps the name). */
@media (max-width: 640px) {
  .app-filter-tab--iconlabel .app-filter-tab__lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
}
.appadmin-filter__sel {
  font: inherit; font-size: 0.8rem; padding: 5px 8px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg-surface); color: var(--text);
}
.appadmin-approvals { margin: 6px 0; padding-left: 18px; font-size: 0.85rem; }

/* ─── Pricing gate / configurator (features/onboarding/pricing.js) ───────── */
.pricing-dialog {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 0;
  width: min(880px, 94vw);
  max-height: 92vh;
  background: var(--bg-surface);
  color: var(--text);
  box-shadow: var(--shadow-lg);
}
.pricing-dialog::backdrop { background: rgba(15, 23, 42, 0.45); }
.pricing-dialog__form { display: flex; flex-direction: column; max-height: 92vh; }
.pricing-dialog__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 18px 22px; border-bottom: 1px solid var(--border);
}
.pricing-dialog__title { margin: 0 0 2px; font-size: 1.15rem; }
.pricing-dialog__body { padding: 18px 22px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; }
.pricing-dialog__foot {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px;
  padding: 14px 22px; border-top: 1px solid var(--border);
}
.pricing-section-title { margin: 0 0 8px; font-size: 0.95rem; font-weight: 600; }

.pricing-currency { display: inline-flex; gap: 4px; }
.pricing-currency-btn,
.pricing-size-chip {
  font: inherit; cursor: pointer; background: var(--bg-surface);
  border: 1px solid var(--border); color: var(--text);
  padding: 6px 12px; border-radius: var(--radius-pill);
}
.pricing-currency-btn.is-active,
.pricing-size-chip.is-active { border-color: var(--primary); background: var(--bg-hover); color: var(--primary); }
.pricing-sizes { display: flex; flex-wrap: wrap; gap: 8px; }

.pricing-tiers { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.pricing-tier-card {
  position: relative; text-align: left; cursor: pointer; font: inherit;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px;
}
.pricing-tier-card:hover { border-color: var(--border-strong); }
.pricing-tier-card.is-active { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary) inset; }
.pricing-tier-card.is-featured { border-color: var(--primary); }
.pricing-tier-badge {
  position: absolute; top: -9px; right: 10px; font-size: 0.65rem; font-weight: 700;
  background: var(--primary); color: var(--primary-ink, #fff); padding: 1px 8px; border-radius: var(--radius-pill);
}
.pricing-tier-name { font-weight: 600; }
.pricing-tier-price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 5px; font-size: 0.95rem; color: var(--text); }
.pricing-tier-anchor { color: var(--text-faint, var(--text-muted)); text-decoration: line-through; font-size: 0.9rem; font-weight: 600; }
.pricing-tier-amount { font-size: 1.4rem; font-weight: 700; }
.pricing-tier-unit { color: var(--text-muted); font-size: 0.76rem; }
.pricing-tier-cycle { flex-basis: 100%; color: var(--text-muted); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; }
.pricing-tier-modules, .pricing-tier-tagline { font-size: 0.78rem; }
.pricing-notes { margin: 12px 0 0; padding-left: 18px; line-height: 1.6; font-size: 0.78rem; }
.pricing-notes li { margin: 2px 0; }

.pricing-addons { display: flex; flex-wrap: wrap; gap: 14px; }
.pricing-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.8rem; color: var(--text-muted); }
.pricing-field input, .pricing-field select {
  font: inherit; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-surface); color: var(--text); min-width: 120px;
}
.pricing-summary {
  background: var(--bg-muted); border: 1px solid var(--border);
  border-radius: var(--radius-md); padding: 12px 14px;
}
.pricing-summary-row, .pricing-summary-total { display: flex; justify-content: space-between; padding: 3px 0; }
.pricing-summary-total { border-top: 1px solid var(--border); margin-top: 6px; padding-top: 8px; font-size: 1.05rem; }
.pricing-summary-contact { margin: 0; color: var(--text-muted); }
.pricing-error { color: var(--danger, #b42318); font-size: 0.85rem; margin: 0; }

/* AI-credit usage meter — overage state (features/usage/controller.js) */
.usage-meter-fill.is-over { background: linear-gradient(90deg, #d97706, #b42318); }

/* Pricing gate — coupon row (features/onboarding/pricing.js) */
.pricing-coupon { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pricing-coupon input {
  font: inherit; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-surface); color: var(--text); min-width: 150px;
}

/* Pricing gate — tax / GSTIN row (features/onboarding/pricing.js) */
.pricing-tax { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pricing-tax input {
  font: inherit; padding: 6px 10px; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--bg-surface); color: var(--text); min-width: 170px;
}

/* ── Upcoming-module placeholders ──────────────────────────────────────────────
   A muted, non-clickable "More coming soon" hint in the sidebar, and the centered
   "coming soon" page shown when an upcoming module (Forge/Lens/Echo) is opened. */
.sidebar-link--hint { opacity: 0.55; cursor: default; pointer-events: none; }
.left-collapsed .sidebar-link--hint { justify-content: center; }
.coming-soon {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; gap: 0.4rem; min-height: 60vh; padding: 2rem;
}
.coming-soon__icon { font-size: 3rem; line-height: 1; }
.coming-soon__title { margin: 0.4rem 0 0; font-size: 1.6rem; }
.coming-soon__tag {
  margin: 0; text-transform: uppercase; letter-spacing: 0.06em; font-size: 0.72rem;
  font-weight: 700; color: var(--text-muted, #6b7280);
}
.coming-soon__msg { margin: 0.6rem 0 0; color: var(--text-muted, #6b7280); max-width: 28rem; }

/* ══════════════════════════════════════════════════════════════════════════════════════════════
   Kriya work module — menus, task-detail modal, native entity (project/phase/risk/bug) detail pages,
   sub-item rows, and the Project/Risk portfolio views. Relocated here from the runtime `<style>` that
   `_ensureShellStyles()` used to inject into <head> (frontend/src/features/work/controller.js), so the
   styles apply on FIRST PAINT (no flash), ship in the versioned/cached sheet, and are tooling-visible.
   ══════════════════════════════════════════════════════════════════════════════════════════════ */
.kriya-rail-sep{height:1px;background:var(--border,#e5e7eb);margin:.4rem .3rem}
.wk-filters .wk-inline{font-size:.78rem}
.wk-newmenu{position:relative;display:inline-block}
.wk-newmenu__pop{position:absolute;top:calc(100% + 6px);right:0;z-index:40;min-width:190px;
  max-height:min(72vh,540px);overflow:auto;
  background:var(--surface,#fff);border:1px solid var(--border,#e5e7eb);border-radius:10px;
  box-shadow:0 12px 32px rgba(15,23,42,.18);padding:6px;display:flex;flex-direction:column;gap:2px}
.wk-newmenu__pop[hidden]{display:none}
.wk-newmenu__grp{font-size:.66rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted,#94a3b8);padding:8px 12px 3px}
.wk-newmenu__grp:first-child{padding-top:3px}
.wk-newmenu__item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  background:none;border:none;border-radius:7px;padding:9px 12px;font:inherit;font-size:.9rem;
  color:inherit;cursor:pointer}
.wk-newmenu__item span{width:1.1em;text-align:center}
.wk-newmenu__item:hover{background:var(--track,#f1f5f9)}
/* Inventra + Godam ship bespoke "+ New" menus (.inv-new-menu / .gd-new-menu) that had NO CSS, so the
   panel was transparent and its buttons inherited the toolbar's light text — invisible on white.
   Style them like .wk-newmenu: a white surface panel with explicit dark items. */
.inv-new-wrap,.gd-new-wrap{position:relative;display:inline-block}
.inv-new-menu,.gd-new-menu{position:absolute;top:calc(100% + 6px);right:0;z-index:40;min-width:190px;
  max-height:min(72vh,540px);overflow:auto;
  background:var(--surface,#fff);border:1px solid var(--border,#e5e7eb);border-radius:10px;
  box-shadow:0 12px 32px rgba(15,23,42,.18);padding:6px;display:flex;flex-direction:column;gap:2px}
.inv-new-menu[hidden],.gd-new-menu[hidden]{display:none}
.inv-new-menu button,.gd-new-menu button{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  background:none;border:none;border-radius:7px;padding:9px 12px;font:inherit;font-size:.9rem;
  color:#202124;cursor:pointer}
.inv-new-menu button:hover,.gd-new-menu button:hover{background:var(--track,#f1f5f9)}
.wk-fp__pop{max-height:min(74vh,460px)}
.wk-fp__list{display:flex;flex-direction:column;gap:8px;overflow-y:auto;min-height:0;padding-right:2px}
.wk-fp__foot{display:flex;flex-direction:column;gap:6px;padding-top:8px;margin-top:2px;border-top:1px solid var(--border,#e5e7eb);flex-shrink:0}
.wk-fp__has{display:flex;flex-wrap:wrap;gap:6px}
.wk-fp__footrow{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.wk-task__chip--timing{color:var(--accent,#0e7490)}
.wk-hdrmenu{position:relative;display:inline-block}
.wk-hdrmenu__pop{position:absolute;top:calc(100% + 6px);right:0;z-index:50;min-width:196px;
  background:var(--surface,#fff);border:1px solid var(--border,#e5e7eb);border-radius:10px;
  box-shadow:0 12px 32px rgba(15,23,42,.18);padding:6px;display:flex;flex-direction:column;gap:2px}
.wk-hdrmenu__pop[hidden]{display:none}
.wk-hdrmenu__item{display:flex;align-items:center;gap:10px;width:100%;text-align:left;background:none;
  border:none;border-radius:7px;padding:9px 12px;font:inherit;font-size:.9rem;color:inherit;cursor:pointer;white-space:nowrap}
.wk-hdrmenu__item:hover{background:var(--track,#f1f5f9)}
.wk-sortmenu,.wk-groupmenu{position:relative;display:inline-block}
.wk-sortmenu__pop,.wk-groupmenu__pop{position:absolute;top:calc(100% + 6px);left:0;z-index:50;min-width:154px;
  background:var(--surface,#fff);border:1px solid var(--border,#e5e7eb);border-radius:10px;
  box-shadow:0 12px 32px rgba(15,23,42,.18);padding:6px;display:flex;flex-direction:column;gap:2px}
.wk-sortmenu__pop[hidden],.wk-groupmenu__pop[hidden]{display:none}
.wk-sortmenu__item,.wk-groupmenu__item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;background:none;border:none;
  border-radius:7px;padding:7px 11px;font:inherit;font-size:.86rem;color:inherit;cursor:pointer;white-space:nowrap}
.wk-sortmenu__item:hover,.wk-groupmenu__item:hover{background:var(--track,#f1f5f9)}
.wk-sortmenu__item.is-active,.wk-groupmenu__item.is-active{background:var(--accent-soft,#eef2ff);color:var(--primary,#1d4ed8);font-weight:600}
.wk-sortmenu__label{font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--text-muted,#64748b);padding:6px 11px 3px}
.wk-sortmenu__sep{height:1px;background:var(--border,#e5e7eb);margin:5px 0}
.wk-sortmenu__dir{display:flex;gap:6px;padding:2px 11px 4px}
.wk-sortmenu__dirbtn{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:4px;border:1px solid var(--border,#e5e7eb);background:var(--bg-surface,#fff);border-radius:7px;padding:5px 8px;font:inherit;font-size:.8rem;color:var(--text-subtle,#475569);cursor:pointer}
.wk-sortmenu__dirbtn:hover:not(:disabled){background:var(--track,#f1f5f9)}
.wk-sortmenu__dirbtn.is-active{background:var(--accent-soft,#eef2ff);border-color:var(--primary,#1d4ed8);color:var(--primary,#1d4ed8);font-weight:600}
.wk-sortmenu__dirbtn:disabled{opacity:.45;cursor:default}
.wk-sortmenu__dirbtn span{font-size:.95rem;line-height:1}
.wk-hdr-recipes{display:flex;flex-direction:column;gap:8px}
.wk-hdr-recipe{text-align:left;background:var(--bg-app,#f8fafc);border:1px solid var(--border,#e5e7eb);
  border-radius:9px;padding:11px 13px;font:inherit;font-size:.9rem;color:inherit;cursor:pointer}
.wk-hdr-recipe:hover{border-color:var(--primary,#2563eb);background:var(--bg-surface,#fff)}
.wk-hdr-recipe:disabled{opacity:.5;cursor:default}
.wk-detail-modal-ov{position:fixed;inset:0;z-index:60;display:flex;align-items:flex-start;justify-content:center;
  padding:4vh 16px 24px;background:rgba(15,23,42,.44);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);overflow-y:auto}
.wk-detail-modal{position:relative;width:min(1000px,100%);max-height:92vh;display:flex;flex-direction:column;
  background:var(--bg-surface,#fff);border:1px solid var(--border,#e5e7eb);border-radius:14px;
  box-shadow:0 24px 64px rgba(15,23,42,.28);overflow:hidden;animation:wk-popup-in .14s ease}
.wk-detail-modal__host{flex:1;min-height:0;overflow-y:auto}
.wk-detail-modal__x{position:absolute;top:9px;right:11px;z-index:8;border:none;background:var(--bg-surface,#fff);
  color:var(--text-faint,#94a3b8);font-size:15px;cursor:pointer;width:28px;height:28px;border-radius:8px;line-height:1;
  display:inline-flex;align-items:center;justify-content:center;box-shadow:0 1px 2px rgba(16,24,40,.08)}
.wk-detail-modal__x:hover{background:var(--track,#f1f5f9);color:var(--text,#0f172a)}
.wk-detail-modal .wk-detailpage{min-height:0}
.wk-detail-modal .wk-detailpage__inner{max-width:none;margin:0;padding:14px 22px 28px}
.wk-detail-modal .wk-detail-tabbar__back{display:none}
.wk-detail-modal .wk-detail-header__row{padding-right:40px}
.wk-ent-badge{display:inline-flex;align-items:center;font-size:.6rem;font-weight:700;letter-spacing:.05em;text-transform:uppercase;color:var(--primary-ink, #fff);background:var(--primary,#1d4ed8);border-radius:6px;padding:2px 7px;flex:none}
.wk-ent-body{display:flex;flex-direction:column;gap:12px}
.wk-eitems{display:flex;flex-direction:column;gap:6px;margin-bottom:8px}
/* Milestone / deliverable / checklist rows: a growable name box + fixed-width controls that WRAP on
   narrow tiles instead of squishing the text box. All controls share one 32px height. */
.wk-eitem{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.wk-eitem>input,.wk-eitem>select{height:32px;box-sizing:border-box}
.wk-eitem__text{flex:1 1 200px;min-width:150px;font:inherit;font-size:.85rem;padding:6px 9px;border:1px solid var(--border,#e5e7eb);border-radius:7px;background:var(--bg-surface,#fff);color:var(--text,#0f172a)}
.wk-eitem__text:hover{border-color:var(--border-strong,#cbd5e1)}
.wk-eitem__status,.wk-eitem__due,.wk-eitem__owner{font:inherit;font-size:.82rem;padding:5px 7px;border:1px solid var(--border,#e5e7eb);border-radius:7px;flex:none;background:var(--bg-surface,#fff);color:var(--text,#0f172a);cursor:pointer}
.wk-eitem__status{width:118px}
.wk-eitem__due{width:140px;cursor:text}
.wk-eitem__owner{width:150px;max-width:150px;overflow:hidden;text-overflow:ellipsis}
.wk-eitem__text:focus,.wk-eitem__status:focus,.wk-eitem__due:focus,.wk-eitem__owner:focus{outline:none;border-color:var(--primary,#2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary,#2563eb) 18%,transparent)}
.wk-eitem .wk-xitem__x{flex:none;border:none;background:none;color:var(--text-faint,#94a3b8);cursor:pointer;font-size:14px;line-height:1;padding:4px 6px;border-radius:6px}
.wk-eitem .wk-xitem__x:hover{color:var(--danger,#dc2626);background:var(--danger-bg,#fee2e2)}
.wk-eitem__file{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem}
.wk-ent-link{border:none;background:none;color:var(--primary,#1d4ed8);font:inherit;font-size:.85rem;font-weight:600;cursor:pointer;padding:0;text-align:left}
.wk-ent-link:hover{text-decoration:underline}
/* Project/phase detail lists reuse the task-board list view (.wk-tbl grid). The item lists
   (milestones/deliverables/checklists) hold LIVE controls in their cells. Style every control as a
   "ghost" field — transparent, borderless, filling its cell — so the row reads like the display rows
   and the editing affordance (border + surface) only appears on hover/focus. Editable rows also get
   tighter cell padding so the control isn't a box inside a padded box. */
/* Item rows carry BOTH .wk-trow and .wk-eitem; the base .wk-eitem{display:flex} (defined later in the
   sheet) was overriding .wk-trow{display:grid}, so rows laid out as flex and ignored the column grid
   while the header (no .wk-eitem) stayed grid — header aligned, cells did not. Force the grid back. */
.wk-eitem.wk-trow{display:grid;grid-template-columns:var(--wk-tbl-cols);align-items:center;gap:0;flex-wrap:nowrap}
.wk-eitem.wk-trow .wk-trow__cell{padding:3px 8px}
/* A plain-text cell in an editable row (the read-only Phase roll-up column) has no ghost control to
   supply the control's own 8px inset, so its value sat 8px left of the Status/Due values in the rows
   above. Match the control inset + vertical rhythm so every column in a roll-up list lines up. */
.wk-eitem.wk-trow .wk-trow__cell:not(.wk-trow__name):not(:has(input)):not(:has(select)){padding-left:16px;padding-right:16px}
.wk-eitem .wk-eitem__text,
.wk-eitem .wk-eitem__status,
.wk-eitem .wk-eitem__due,
.wk-eitem .wk-eitem__owner{
  width:100%;min-width:0;height:30px;box-sizing:border-box;font:inherit;font-size:12.5px;
  padding:0 8px;border:1px solid transparent;border-radius:7px;background:transparent;
  color:var(--text,#0f172a);cursor:pointer;appearance:none;-webkit-appearance:none;text-overflow:ellipsis;
}
.wk-eitem .wk-eitem__text{flex:1;font-weight:500;cursor:text}
.wk-eitem .wk-eitem__due{cursor:text}
/* selects carry a chevron so they still read as pickers without a full border */
.wk-eitem .wk-eitem__status,.wk-eitem .wk-eitem__owner{
  padding-right:22px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath d='M2 3.5L5 6.5L8 3.5' stroke='%2394a3b8' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 7px center;
}
.wk-eitem .wk-eitem__text:hover,
.wk-eitem .wk-eitem__status:hover,
.wk-eitem .wk-eitem__due:hover,
.wk-eitem .wk-eitem__owner:hover{border-color:var(--border,#e5e7eb);background-color:var(--bg-surface,#fff)}
.wk-eitem .wk-eitem__text:focus,
.wk-eitem .wk-eitem__status:focus,
.wk-eitem .wk-eitem__due:focus,
.wk-eitem .wk-eitem__owner:focus{outline:none;border-color:var(--primary,#2563eb);background-color:var(--bg-surface,#fff);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary,#2563eb) 16%,transparent)}
.wk-trow__cell .wk-task__del{opacity:.55}
.wk-trow:hover .wk-task__del{opacity:1}
/* Inline row editing (list view): ✎ reveals on row hover; the editing row turns its cells into form
   controls with Save/Cancel. */
/* Per-record "⋯" actions button + its floating menu (Edit · Duplicate · New · Delete). */
.wk-task__menu{opacity:.6;flex:none;margin-left:auto;border:1px solid var(--border);background:var(--bg-surface);cursor:pointer;color:var(--text-muted,#64748b);font-size:15px;line-height:1;padding:0 7px;height:24px;border-radius:6px}
.wk-trow:hover .wk-task__menu{opacity:1}
.wk-task__menu:hover{color:var(--primary,#2563eb);border-color:var(--primary,#2563eb);background:var(--bg-hover,#f1f5f9)}
.wk-rowmenu{position:fixed;z-index:100;min-width:156px;background:var(--bg-surface,#fff);border:1px solid var(--border);border-radius:10px;box-shadow:0 12px 32px rgba(16,24,40,.18);padding:5px;display:flex;flex-direction:column;gap:1px}
.wk-rowmenu__item{display:flex;align-items:center;gap:9px;width:100%;text-align:left;font:inherit;font-size:13px;color:var(--text);background:none;border:0;border-radius:7px;padding:7px 10px;cursor:pointer}
.wk-rowmenu__item:hover{background:var(--bg-hover,#f1f5f9)}
.wk-rowmenu__item--danger{color:var(--danger,#dc2626)}
.wk-rowmenu__item--danger:hover{background:var(--danger-soft,rgba(220,38,38,.08))}
.wk-trow--edit{background:color-mix(in srgb,var(--primary,#2563eb) 5%,transparent)}
.wk-trow--edit .wk-trow__cell{padding-top:5px;padding-bottom:5px}
.wk-trow--edit .wk-trow__name{gap:4px}
.wk-erow-in{width:100%;box-sizing:border-box;font:inherit;font-size:12.5px;border:1px solid var(--border);border-radius:6px;background-color:var(--bg-surface);color:var(--text);min-width:0}
input.wk-erow-in{padding:5px 7px}
select.wk-erow-in{padding:5px 24px 5px 8px}
.wk-erow-in:focus{outline:none;border-color:var(--primary,#2563eb);box-shadow:0 0 0 2px color-mix(in srgb,var(--primary,#2563eb) 16%,transparent)}
.wk-erow-title{font-weight:600;flex:1}
.wk-erow-btn{flex:none;width:26px;height:26px;display:inline-flex;align-items:center;justify-content:center;border-radius:6px;border:1px solid var(--border);background:var(--bg-surface);cursor:pointer;font-size:13px;line-height:1}
.wk-erow-save{color:#fff;background:var(--ok,#16a34a);border-color:var(--ok,#16a34a)}
.wk-erow-save:hover{background:#15803d}
.wk-erow-cancel{color:var(--text-muted)}
.wk-erow-cancel:hover{background:var(--bg-hover,#f1f5f9);color:var(--text)}
/* Inline "＋ Add record" form under a list view. */
.wk-listadd{display:flex;align-items:center;gap:8px;margin:8px 2px 0;padding:8px 12px;border:1px dashed var(--border-strong,#cbd5e1);border-radius:10px;background:var(--bg-surface,#fff);max-width:520px}
.wk-listadd:focus-within{border-color:var(--primary,#2563eb);border-style:solid}
.wk-listadd__plus{color:var(--primary,#2563eb);font-weight:700;flex:none}
.wk-listadd__in{flex:1;min-width:0;font:inherit;font-size:13px;border:0;background:none;color:var(--text);padding:2px 0}
.wk-listadd__in:focus{outline:none}
.wk-listadd__btn{flex:none;font:inherit;font-size:12.5px;font-weight:600;padding:5px 12px;border:1px solid var(--primary,#2563eb);border-radius:8px;background:var(--primary,#2563eb);color:var(--primary-ink, #fff);cursor:pointer}
.wk-listadd__btn:hover{background:#1d4ed8;border-color:var(--primary)}
.wk-file-dl{flex:none;text-decoration:none;color:var(--text-muted,#64748b);font-size:14px;line-height:1;padding:2px 4px;border-radius:6px}
.wk-file-dl:hover{color:var(--primary,#2563eb);background:var(--bg-hover,#f1f5f9)}
.wk-cellowner{max-width:100%;overflow:hidden;text-overflow:ellipsis;display:inline-block;vertical-align:bottom}
/* Color-coded Phase chip (tinted by the phase's org label). Ellipsise long names within the cell. */
.wk-phasechip{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.6}
/* Phase / label colour FILL — a whole table cell flooded with the phase's (or label's) colour. The
   cell carries --fillbg inline; text + any chips inside flip to white so they read on the fill. */
.wk-phasecell{display:inline-block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;line-height:1.6}
.wk-cell--fill{background:var(--fillbg);}
.wk-cell--fill,
.wk-cell--fill .wk-phasecell,
.wk-cell--fill .wk-task__title,
.wk-cell--fill .wk-task__del,
.wk-cell--fill .pj-badge{color:#fff !important;}
.wk-cell--fill .wk-label,
.wk-cell--fill .pj-badge{background:transparent !important;color:#fff !important;border-color:transparent !important;}
.wk-cell--fill .wk-pkey{background:rgba(255,255,255,.22) !important;color:#fff !important;border-color:transparent !important;}
.wk-cellbadge{display:inline-block;padding:1px 9px;border-radius:999px;font-size:.78rem;line-height:1.5;background:var(--bg-subtle,#eef2f7);border:1px solid var(--border-muted,#e2e8f0);color:var(--text,#334155);white-space:nowrap}
/* Semantic tint for detail-list status/severity/priority badges. Keys mirror the fixed status/severity
   vocabularies (_PHASE_STATUS / _RISK_ / _BUG_ prefixes); unknown keys (custom statuses) keep the neutral base
   above. Palette matches the Status-update badges + risk-severity chips so a badge reads the same
   everywhere on the page. Borders are transparent so the tint carries the meaning. */
.wk-cellbadge--low,.wk-cellbadge--done,.wk-cellbadge--resolved,.wk-cellbadge--closed,.wk-cellbadge--complete{background:var(--ok-soft,rgba(22,163,74,.12));color:var(--ok,#15803d);border-color:transparent}
.wk-cellbadge--med,.wk-cellbadge--mitigating,.wk-cellbadge--triaged,.wk-cellbadge--in_review{background:#fef3c7;color:#92600b;border-color:transparent}
.wk-cellbadge--high{background:#ffedd5;color:#b45309;border-color:transparent}
.wk-cellbadge--critical{background:#fee2e2;color:#b91c1c;border-color:transparent}
.wk-cellbadge--active,.wk-cellbadge--in_progress{background:#dbeafe;color:var(--primary);border-color:transparent}
.wk-cellbadge--open{background:var(--bg-subtle,#eef2f7);color:var(--text-muted,#4b5563);border-color:var(--border,#e5e7eb)}
.wk-cellbadge--wont_fix,.wk-cellbadge--on_hold{background:var(--app-filter-bg,#f1f5f9);color:var(--text-subtle,#475569);border-color:transparent}
.wk-ent-attach{display:inline-block;cursor:pointer;margin-top:2px}
/* Row of actions under a detail-list tile (e.g. ＋ Add phase · ⧉ From template). */
.wk-tile-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:8px}
/* Add-item row: the text input grows and an explicit "＋ Add" button sits at the row's end (so adding
   is a clear click, not only the Enter key — Enter still submits the form for keyboard users). */
.wk-ent-add{display:flex;gap:8px;align-items:center;border-top:none!important;padding:4px 0 0!important;margin:0}
.wk-ent-add input{flex:1 1 auto;width:auto;min-width:0;font:inherit;font-size:.85rem;padding:7px 10px;border:1px dashed var(--border,#cbd5e1);border-radius:8px;background:var(--bg-surface,#fff);color:var(--text,#0f172a)}
.wk-ent-add input:focus{outline:none;border-style:solid;border-color:var(--primary,#2563eb);box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--primary,#2563eb) 18%,transparent)}
.wk-ent-add__btn{flex:0 0 auto;font:inherit;font-size:.82rem;font-weight:600;padding:7px 14px;border:1px solid var(--primary,#2563eb);border-radius:8px;background:var(--primary,#2563eb);color:var(--primary-ink, #fff);cursor:pointer;white-space:nowrap}
.wk-ent-add__btn:hover{filter:brightness(1.06)}
.wk-ent-add__btn:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--primary,#2563eb) 30%,transparent)}
/* Roll-up add form with a Where picker: text input grows, the target <select> sits beside it. */
.wk-ent-add--where{display:flex;gap:8px;align-items:center}
.wk-ent-add--where input{flex:1 1 auto}
/* The "Add to" phase picker must size to its content — NOT the global select{width:100%}, which with
   flex-shrink:0 would force it full-width and crush the text input beside it. background-COLOR (not the
   shorthand) so the global chevron survives; padding-right leaves room for it. */
.wk-ent-add--where select{flex:0 0 auto;width:auto;max-width:210px;min-width:130px;font:inherit;font-size:.82rem;padding:7px 28px 7px 10px;border:1px solid var(--border,#e5e7eb);border-radius:8px;background-color:var(--bg-surface,#fff);color:var(--text,#0f172a);cursor:pointer}
.wk-ent-add--where select:focus{outline:none;border-color:var(--primary,#2563eb);box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--primary,#2563eb) 18%,transparent)}
.wk-sitem__sm{flex:1 1 120px;min-width:100px;font:inherit;font-size:.82rem;padding:6px 8px;border:1px solid var(--border,#e5e7eb);border-radius:7px;background:var(--bg-surface,#fff);color:var(--text,#0f172a)}
.wk-sitem__num{flex:none;width:84px;font:inherit;font-size:.82rem;padding:6px 8px;border:1px solid var(--border,#e5e7eb);border-radius:7px;background:var(--bg-surface,#fff);color:var(--text,#0f172a)}
.wk-sitem__sm:focus,.wk-sitem__num:focus{outline:none;border-color:var(--primary,#2563eb);box-shadow:0 0 0 3px color-mix(in srgb,var(--primary,#2563eb) 18%,transparent)}
.wk-detail-modal .wk-field--wide{grid-column:1/-1}
.wk-detail-modal .wk-field--wide textarea{width:100%;font:inherit;font-size:.85rem;padding:6px 9px;border:1px solid var(--border,#e5e7eb);border-radius:8px;resize:vertical}
.wk-pv,.wk-rv{display:flex;flex-direction:column;gap:12px;padding-bottom:24px}
.wk-pv-filters{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.wk-pv-filters select,.wk-pv-search{font:inherit;font-size:.82rem;padding:6px 9px;border:1px solid var(--border,#e5e7eb);border-radius:8px;background:var(--bg-surface,#fff)}
.wk-pv-search{min-width:200px}
.wk-pv-list{display:flex;flex-direction:column;gap:8px}
.wk-pv-card{border:1px solid var(--border,#e5e7eb);border-radius:11px;background:var(--bg-surface,#fff);overflow:hidden}
.wk-pv-head{display:flex;align-items:center;gap:10px;padding:10px 12px;flex-wrap:wrap}
.wk-pv-toggle{border:none;background:none;cursor:pointer;color:var(--text-faint,#94a3b8);font-size:12px;width:18px;flex:none}
.wk-pv-name{border:none;background:none;font:inherit;font-size:.92rem;font-weight:650;color:var(--text,#0f172a);cursor:pointer;padding:0;text-align:left}
.wk-pv-name:hover{color:var(--primary,#1d4ed8)}
.wk-pv-badge{font-size:.62rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--text-muted,#64748b);background:var(--bg-app,#f1f5f9);border-radius:6px;padding:2px 7px}
.wk-pv-badge--health{color:#0e7490;background:#ecfeff}
.wk-pv-counts{margin-left:auto}
.wk-pv-body{border-top:1px solid var(--border-muted,#f0f1f3);padding:10px 12px;display:flex;flex-direction:column;gap:10px}
.wk-pv-sec{display:flex;gap:10px;align-items:flex-start}
.wk-pv-lbl{font-size:.66rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--text-faint,#94a3b8);min-width:66px;padding-top:4px}
.wk-pv-chips{display:flex;flex-wrap:wrap;gap:6px;flex:1}
.wk-pv-chip{border:1px solid var(--border,#e5e7eb);background:var(--bg-app,#f8fafc);border-radius:999px;padding:3px 10px;font:inherit;font-size:.78rem;cursor:pointer;color:var(--text,#0f172a)}
.wk-pv-chip:hover{border-color:var(--primary,#1d4ed8)}
.wk-pv-chip--risk{background:#fff7ed;border-color:#fed7aa}
.wk-pv-tasks{flex:1;min-width:0}
.wk-rv-top{display:flex;gap:16px;flex-wrap:wrap}
.wk-rv-mxlabel{display:flex;gap:8px;align-items:baseline;margin-bottom:4px}
.wk-rv-matrix{border-collapse:collapse;font-size:.72rem}
.wk-rv-matrix th{color:var(--text-muted,#64748b);font-weight:600;padding:3px 8px;text-align:center}
.wk-rv-cell{width:34px;height:30px;text-align:center;font-weight:700;color:var(--text);border:1px solid #fff}
.wk-rv-cell--l2{background:#dcfce7}.wk-rv-cell--l3{background:#fef9c3}.wk-rv-cell--l4{background:#fed7aa}.wk-rv-cell--l5{background:#fecaca}.wk-rv-cell--l6{background:#fca5a5}
.wk-rv-table{width:100%;border-collapse:collapse;font-size:.83rem}
.wk-rv-table th{text-align:left;font-size:.68rem;text-transform:uppercase;letter-spacing:.04em;color:var(--text-faint,#94a3b8);font-weight:700;padding:6px 10px;border-bottom:1px solid var(--border,#e5e7eb)}
.wk-rv-th{cursor:pointer;user-select:none;white-space:nowrap}
.wk-rv-th:hover{color:var(--text,#0f172a)}
.wk-rv-table td{padding:8px 10px;border-bottom:1px solid var(--border-muted,#f0f1f3)}
.wk-rv-row{cursor:pointer}
.wk-rv-row:hover{background:var(--bg-hover,#f1f3f5)}
.wk-rv-title{font-weight:600;color:var(--text,#0f172a)}
.wk-rv-sev{font-size:.7rem;font-weight:700;border-radius:6px;padding:2px 7px}
.wk-rv-sev--high{color:#b91c1c;background:#fee2e2}.wk-rv-sev--med{color:#b45309;background:#ffedd5}.wk-rv-sev--low{color:#166534;background:#dcfce7}
@keyframes wk-popup-in{from{opacity:0;transform:translateY(6px) scale(.99)}to{opacity:1;transform:none}}
/* Goals (OKRs) manager — a list of org objectives; reuses the .wk-eitem__* control styling above. */
.wk-goals{display:flex;flex-direction:column;gap:12px}
.wk-goals__list{display:flex;flex-direction:column;gap:4px}
.wk-goal{display:flex;align-items:flex-start;gap:10px;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border-muted,#eef1f5)}
.wk-goal:last-child{border-bottom:0}
.wk-goal__main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.wk-goal__ctrls{display:flex;align-items:center;gap:6px;flex-wrap:wrap;flex:none}
/* Project "Status updates" tile — a composer + a RAG-tagged check-in timeline. */
.wk-statusupd{display:flex;flex-direction:column;gap:12px}
.wk-statusupd__form{display:flex;flex-direction:column;gap:8px}
.wk-statusupd__row{display:flex;gap:8px;flex-wrap:wrap}
.wk-statusupd__row select{flex:none;font:inherit;font-size:.85rem;padding:6px 8px;border:1px solid var(--border,#e5e7eb);border-radius:7px;background:var(--bg-surface,#fff)}
.wk-statusupd__row input{flex:1 1 180px;min-width:150px;font:inherit;font-size:.85rem;padding:7px 10px;border:1px solid var(--border,#e5e7eb);border-radius:8px;background:var(--bg-surface,#fff)}
.wk-statusupd__form textarea{width:100%;font:inherit;font-size:.85rem;padding:7px 10px;border:1px solid var(--border,#e5e7eb);border-radius:8px;background:var(--bg-surface,#fff);resize:vertical}
.wk-statusupd__actions{display:flex;justify-content:flex-end}
.wk-statusupd__list{display:flex;flex-direction:column;gap:10px}
.wk-statusupd__item{display:flex;flex-direction:column;gap:3px;padding:8px 0;border-bottom:1px solid var(--border-muted,#eef1f5)}
.wk-statusupd__item:last-child{border-bottom:0}
.wk-statusupd__head{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.wk-statusupd__badge--on_track{background:var(--ok-soft,rgba(22,163,74,.12));color:var(--ok,#16a34a)}
.wk-statusupd__badge--at_risk{background:#fef3c7;color:#b45309}
.wk-statusupd__badge--off_track{background:#fee2e2;color:#b91c1c}
.wk-statusupd__badge--on_hold{background:var(--app-filter-bg,#f1f5f9);color:var(--text-subtle,#475569)}
.wk-statusupd__badge--complete{background:#dbeafe;color:var(--primary)}

/* ─────────────────────────────────────────────────────────────────────────────
   App-wide table treatment (appended last so it wins ordering):
   • dark headers with light text so the table reads as a distinct block,
   • zebra striping so adjacent rows are easy to track,
   • very minimal corner rounding to match the app's squared surfaces.
   Covers the shared real-<table> classes used across modules.
   ───────────────────────────────────────────────────────────────────────────── */
.app-table thead th,
.appadmin-table--lined thead th,
.hive-table th,
.wk-ltable .wk-lth {
  background: var(--table-head-bg, #1f2937);
  color: var(--table-head-fg, #f1f5f9);
  border-bottom-color: rgba(255, 255, 255, 0.14);
}
.app-table tbody tr:nth-child(even) td,
.appadmin-table--lined tbody tr:nth-child(even) td,
.hive-table tbody tr:nth-child(even) td,
.wk-ltable tbody tr:nth-child(even) .wk-ltd {
  background: var(--table-stripe, #eef1f8);
}
.app-table tbody tr:hover td,
.appadmin-table--lined tbody tr:hover td,
.hive-table tbody tr:hover td,
.wk-ltable tbody tr:hover .wk-ltd {
  background: var(--table-row-hover, #e7edfa);
}
/* Very minimal corners on the table surfaces + their scroll wrappers. */
.app-table,
.appadmin-table,
.appadmin-table-scroll,
.hive-table,
.wk-ltable {
  border-radius: 3px;
}
/* `.sch-table` LEFT this block (2026-09-10). It is Schpec's editable schedule grid — every cell an
   input — and the dark head band plus zebra striping had it reading as a third kind of table:
   the stripes fight the cell borders of the controls sitting on them, and the head band is the
   `.wk-ltable` look CLAUDE.md names as the reason these tables drifted in the first place. Its own
   rules (see "Schedules") now carry Kriya's list values at the grid's own density; the LIBRARY
   list, which is a plain list page, is `.sch-ltable` in the MODULE LIST TABLES block at the end. */

/* ─────────────────────────────────────────────────────────────────────────────
   Form-control affordances (appended last so component `background:` shorthands
   elsewhere can't strip the chevron or tint a text field):
   • the dropdown "v" belongs to single <select>s ONLY — never text inputs,
   • text inputs + textareas default to a white surface.
   ───────────────────────────────────────────────────────────────────────────── */
select:not([multiple]) {
  padding-right: 34px; /* room for the chevron */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
}
input:not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="file"]):not([type="range"]):not([type="image"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([type="search"]):not(:disabled):not([readonly]),
textarea:not(:disabled):not([readonly]) {
  background-color: var(--bg-surface);
}

/* ═══════════════════════════════════════════════════════════════════════════════
   GMAIL-STYLE TOP HEADER — appended last so it wins over the earlier per-control topbar
   rules. A light white bar with Google-grey circular icon buttons (40px hit target, ~24px
   glyphs), a wide rounded search field, the customer (org) logo on the left, and the
   right-cluster order help · report · settings · AI · upgrade · alert · waffle · profile.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* Circular Google-grey icon buttons — one uniform 40px hit target. */
.topbar :is(.topbar-iconbtn, .tb-notif-button, .tb-applauncher, .tb-hamburger, .topbar-help) {
  width: 40px; height: 40px; padding: 0; margin: 0;
  border: 0 !important; border-radius: 50%;
  background: transparent; color: var(--text-muted);
  font-size: 20px; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}
.topbar :is(.topbar-iconbtn, .tb-notif-button, .tb-applauncher, .tb-hamburger, .topbar-help):hover {
  background: var(--bg-muted); color: var(--text);
}
/* Glyph sizes — the 9-dot waffle, hamburger, hand-icons + inline SVGs all land near 24px. */
.topbar .tb-applauncher svg,
.topbar .tb-hamburger svg { width: 24px; height: 24px; }
.topbar .topbar-iconbtn__ico { display: inline-flex; }
.topbar .topbar-iconbtn__ico .hicon { width: 22px; height: 22px; }
.topbar .topbar-iconbtn__ico svg { width: 24px; height: 24px; }
.topbar .tb-smai { border: 0 !important; }
.topbar .tb-smai .tb-smai__ico svg { width: 26px; height: 26px; }
/* Upgrade — amber premium accent (Google yellow). */
/* (The header's amber Upgrade icon is deleted, 2026-09-14 — it lives in the account menu, where it
   already did. Its styling goes with it: a rule for an element nobody renders reads as covered.) */

/* Profile avatar — Gmail-sized (32px) inside a 40px hit area, no ring. */
.topbar .tb-profile-button { width: 40px; height: 40px; border: 0; background: transparent; }
.topbar .tb-profile-button:hover { background: var(--bg-muted); }
.topbar .tb-avatar { width: 32px; height: 32px; font-size: 0.95rem; }
.topbar .tb-notif-button .tb-notif-badge { top: 3px; right: 3px; }

/* Search — always visible (Gmail-style); the collapse toggle is hidden. */
/* Left LEAD zone — fixed to the second-rail width and flush to the first rail, so the search
   bar that follows starts EXACTLY where the module body begins (rail1 + rail2). The topbar's own
   left padding is dropped so the zone aligns to the rails precisely. */
.topbar { padding-left: 0; }
.tb-lead {
  flex: 0 0 var(--rail2-w, 210px);
  width: var(--rail2-w, 210px);
  box-sizing: border-box;
  display: inline-flex; align-items: center; gap: 8px;
  /* 4px, down from 12 (2026-09-14, user direction: "reduce the space between logo and hamburger
     menu"). The 12 was the SECOND rail's own left offset, so the hamburger's box started on the
     line that rail's items started on — an alignment that died with the second rail: the
     navigation column starts at x=0 now, while this zone starts after the brand corner, so the
     two cannot line up whatever the number. With nothing left to align to, the value is free to
     be what it should be — the gap between the mark and the control beside it. */
  padding: 0 8px 0 4px;
  /* What the zone spends before the org brand: that 12px inset + the 30px hamburger. Named
     because the brand's alignment below subtracts it, and a bare 42 there would be a magic
     number that silently rots the day either value moves. */
  --tb-lead-run: 42px;
  min-width: 0; overflow: hidden;
}
/* The toggle used to be hidden here — the bar was always shown, so its collapse icon was dead
   markup. It is the PRIMARY control now: the bar is a popup behind it. Left in place this rule
   made the search icon invisible while every handler behind it still worked.
   (The `flex: 1` wrap and the forced-visible field went with it — the wrap is an absolutely
   positioned panel now, styled through `.tb-searchbtn` at the end of this sheet.) */
/* Cute Gmail search — a soft rounded pill; #eaf1fb resting, white + shadow on focus. */
.topbar .topbar-search {
  width: 100%; max-width: 720px; height: 38px;
  background: var(--search-pill); /* tinted search field */
  border: 1px solid transparent; border-radius: 24px;
  box-shadow: none; overflow: hidden;
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
.topbar .topbar-search:hover { background: var(--search-pill-hover); }
.topbar .topbar-search:focus-within {
  background: var(--bg-surface); border-color: transparent;
  box-shadow: 0 1px 1px 0 rgba(65, 69, 73, 0.30), 0 1px 3px 1px rgba(65, 69, 73, 0.15);
}
.topbar .topbar-search:focus-within:hover { background: var(--bg-surface); }
/* Scope dropdown rounds the pill's left end; go button rounds the right. */
.topbar .topbar-search__scope-btn {
  /* Full ink, not the subtle ramp: this is an interactive control label sitting on a
     tinted pill, where --text-subtle only reaches 3.4:1. */
  background: transparent; border-right: 1px solid var(--border); color: var(--text);
  border-radius: 24px 0 0 24px; padding-left: 16px;
}
.topbar .topbar-search__scope-btn:hover { background: rgba(60, 64, 67, 0.06); color: var(--text); }
.topbar .topbar-search__input { background: transparent; color: var(--text); font-size: 0.94rem; padding-left: 14px; }
.topbar .topbar-search__go { color: var(--text-subtle); border-radius: 0 24px 24px 0; padding: 0 16px 0 12px; }
.topbar .topbar-search__go:hover { color: var(--text); background: rgba(60, 64, 67, 0.06); }
/* The waffle now sits on the RIGHT, so its panel must open leftward (not off-screen). */
.topbar .tb-applauncher-wrap .app-launcher-panel { left: auto; right: 0; }

/* When the global sidebar is EXPANDED the secondary rail is forced expanded (both bars
   open), so its own collapse toggle is hidden — it only applies when the global sidebar
   is collapsed and the secondary rail works independently. (rails.js drives the class.) */
.app-layout:not(.left-collapsed) .rail-foot,

/* ═══════════════════════════════════════════════════════════════════════════════
   LIST TOOLKIT (`ui/listKit.js`) — the app-standard tools/filter bar above every
   module list, mirroring the KRIYA ENTITY-LIST toolbar: one row with the utilities
   on the RIGHT (collapsible search · count chip · module extras · the FILTERS
   icon-button with an active-count badge, right-most) + an expandable filter BAR
   below (soft grey band, stacked label-over-control rows, highlighted Clear all).
   Reuses the global wk-search-collapse / wk-fp / wk-iconbtn primitives.
   ═══════════════════════════════════════════════════════════════════════════════ */
/* The lk-bar mirrors Kriya's tasks toolbar EXACTLY: transparent band on the module body,
   compact 6px vertical padding (30px icon buttons -> ~42px row), a single #e8eaed hairline
   under the whole unit (row + expanded filter band), 14px below before content. Top-level
   bars (direct children of a module's #<mod>-main) additionally full-bleed + stick — see the
   ERP layout block near the end of this sheet. */
/* The row/band surfaces themselves are declared ONCE, next to Kriya's — see the
   "list toolbar band, shared verbatim" block above. What stays here is only the
   structure listKit's markup needs on top of that shared skin. */
.lk-bar { margin: 0; background: transparent; border: 0; }
.lk-bar__row { display: flex; align-items: center; justify-content: space-between; gap: 6px; flex-wrap: wrap; }
.lk-bar__left { display: flex; align-items: center; gap: 6px 12px; flex-wrap: wrap; min-width: 0; }
.lk-bar__right { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lk-filterbar__clear[hidden] { display: none; }
.lk-count { font-size: 11.5px; color: var(--text-faint, #94a3b8); white-space: nowrap;
  padding: 2px 8px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg-surface, #fff); }
/* The breadcrumb-row "+ New" (lkNewButton) inherits .wk-iconbtn--primary; give the caption a
   little presence so the affordance reads as "create" at a glance. */
.lk-new { flex: none; }

/* ─────────────────────────────────────────────────────────────────────────────
   ERP module tables — REMOVED, superseded by "MODULE LIST TABLES" at the end of this
   file, which matches Kriya's task list exactly.

   What stood here was an earlier "modern data-grid" treatment (slate header band,
   whisper zebra, `border-radius: 0`, and the literals `#f0f5ff` / `#f1f5f9` for hover and
   dividers). It was ID-prefixed specifically to out-rank each partial's own rules — which
   also meant it out-ranked the shared block, so every one of these thirteen tables kept
   the old look no matter what the shared block said.

   It also hid itself from the guard: `ui/tableSkin.spec.js` built its probe DOM with tab
   ids derived from the CLASS (`#inv-tab` for `.inv-table`) rather than the real ones
   (`#inventra-tab`), so these selectors never matched there and the spec passed while the
   app disagreed. The spec now uses the real tab ids.

   Two of its hardcoded values are why the dark-mode `!important` patch further up existed
   at all: near-white hover and divider literals read as bright bands on a dark page. Both
   are tokens in the shared block, so dark follows without a second patch.
   ───────────────────────────────────────────────────────────────────────────── */
/* Inventra's own `.inv-toolbar` filter rows adopt the lk-bar surface so mixed pages read as one system. */
#inventra-tab .inv-toolbar { padding: 7px 10px; background: var(--bg-surface, #fff);
  border: 1px solid var(--border); border-radius: 10px; margin-bottom: 12px; }

/* ═════════════════════════════════════════════════════════════════════════════
   ERP FLAT LAYOUT — the Kriya project-list chrome, flush edition: tab owns no
   padding; the module main is the WHITE scroll container, flush left/right/top
   (no gutter — content starts at the rail edge); the header spans the full tab,
   crumbs flush left, no hairline of its own; the top-level lk-bar sticks as the
   chrome band carrying THE one hairline; content is full-width (no left-aligned
   max-width cap); tables use the modern light data-grid treatment below.
   ═════════════════════════════════════════════════════════════════════════════ */
/* 1 — tabs own no padding (the Artha/CDP normalization). */
#nirman-tab, #inventra-tab, #godam-tab, #procure-tab, #sampada-tab, #kavach-tab, #ordio-tab, #pimly-tab, #vikray-tab, #dravya-tab { padding: 0; }
/* 1b — the tab is a constrained flex COLUMN (the exact #kriya-tab rule): the header stays
   put and only the .x-main scrolls beneath it — so the viewport-pinned + New never drifts
   away from its header, and the sticky lk-bar sticks inside the scrolling main. */
.canvas :is(#nirman-tab, #inventra-tab, #godam-tab, #procure-tab, #sampada-tab, #kavach-tab, #ordio-tab, #pimly-tab, #vikray-tab, #dravya-tab).tab-content.active { display: flex; flex-direction: column; overflow: hidden; }
/* 2 — header: full tab width, flush left, no bleed, no own hairline. */
#nirman-tab .app-shell,
#inventra-tab .app-shell,
#godam-tab .app-shell,
#procure-tab .app-shell,
#sampada-tab .app-shell,
#kavach-tab .app-shell,
#ordio-tab .app-shell,
#pimly-tab .app-shell,
#vikray-tab .app-shell,
#dravya-tab .app-shell { --page-pad-x: 0px; }
/* Flush-left: no gutter inset on the header — crumbs start at the content edge. A small
   right pad keeps the icon cluster off the window edge. */
#nirman-tab .app-shell__header,
#inventra-tab .app-shell__header,
#godam-tab .app-shell__header,
#procure-tab .app-shell__header,
#sampada-tab .app-shell__header,
#kavach-tab .app-shell__header,
#ordio-tab .app-shell__header,
#pimly-tab .app-shell__header,
#vikray-tab .app-shell__header,
#dravya-tab .app-shell__header { padding: 0 10px 0 0; border-bottom: 0 !important; }
/* 3 — the mains: WHITE body surface, flush left/right (no gutter), flush top.
   `.canvas`-prefixed to OUTRANK the partials' inline <style> rules, which are injected
   after this sheet and would otherwise win equal-specificity ties. */
.canvas #nirman-tab .nir-main,
.canvas #inventra-tab .inv-main,
.canvas #godam-tab .gd-main,
.canvas #procure-tab .prc-main,
.canvas #sampada-tab .sm-main,
.canvas #kavach-tab .kv-main,
.canvas #ordio-tab .ord-main,
.canvas #pimly-tab .pimly-main,
.canvas #vikray-tab .vk-main,
.canvas #dravya-tab .drv-main {
  background: var(--bg-surface, #fff);
  padding: 0 0 40px;
  --page-pad-x: 0px;
}
/* 4 — full-width content column, like the Kriya list (kills the asymmetric right gap). */
.canvas #nirman-tab #nirman-main, .canvas #inventra-tab #inventra-main, .canvas #godam-tab #godam-main, .canvas #procure-tab #procure-main, .canvas #sampada-tab #sampada-main, .canvas #kavach-tab #kavach-main, .canvas #ordio-tab #ordio-main, .canvas #pimly-tab #pimly-main, .canvas #vikray-tab #vikray-main, .canvas #dravya-tab #dravya-main { max-width: none; }
/* 4b — section rails: flush full-height columns with a single right hairline (the Kriya
   second-rail shape) instead of floating rounded cards — the body column starts immediately
   at the rail's edge. `.canvas`-prefixed to outrank the partials' inline rules. */
.canvas #nirman-tab .nir-rail,
.canvas #inventra-tab .inv-rail,
.canvas #godam-tab .gd-rail,
.canvas #procure-tab .prc-rail,
.canvas #sampada-tab .sm-rail,
.canvas #kavach-tab .kv-rail,
.canvas #ordio-tab .ord-rail,
.canvas #pimly-tab .pimly-rail,
.canvas #vikray-tab .vk-rail,
.canvas #dravya-tab .drv-rail {
  margin: 0;
  border: 0;   /* the rail/content step does the dividing — see .page-left-rail above */
  border-radius: 0;
  padding: 10px 8px 16px;
}
/* 5 — TOP-LEVEL list toolbars are the chrome band: full-bleed via the kriya negative-margin
   trick, sticky over the scrolling list, content inset at the gutter, opaque body surface. */
:is(#nirman-main, #inventra-main, #godam-main, #procure-main, #sampada-main, #kavach-main, #ordio-main, #pimly-main, #vikray-main, #dravya-main) > .lk-bar {
  margin: 0 0 14px;
  position: sticky; top: 0; z-index: 20;
  background: var(--bg-surface, #fff);
}
:is(#nirman-main, #inventra-main, #godam-main, #procure-main, #sampada-main, #kavach-main, #ordio-main, #pimly-main, #vikray-main, #dravya-main) > .lk-bar .lk-bar__row { padding: 6px 10px 6px 0; }
/* The band's left padding must MATCH its right, and that is not a symmetry for its own sake:
   the base `.lk-filterbar` rule bleeds the band out with `margin: -14px calc(-1 * inset) 14px`
   and insets its content back with the same value, so the filters line up with the toolbar and
   the table. This rule overrides the PADDING and not the margin — so a `0` on the left left the
   bleed uncancelled and every filter in these ten modules started 10px outside the page gutter,
   under the rail (user report 2026-09-01: "filters fields are hiding or getting cut on left").
   It was `9px 10px 9px 0`, copied from the `.lk-bar__row` rule above — right THERE, because that
   row carries no negative margin, and wrong here for exactly that reason. */
:is(#nirman-main, #inventra-main, #godam-main, #procure-main, #sampada-main, #kavach-main, #ordio-main, #pimly-main, #vikray-main, #dravya-main) > .lk-bar .lk-filterbar { padding: 9px 10px; }
/* 6 — bordered cards keep an explicit white surface. */
#nirman-tab .nir-card, #inventra-tab .inv-card, #godam-tab .gd-card,
#procure-tab .prc-card { background: var(--bg-surface, #fff); }
/* Kriya's list header row is NOT sticky — match it (also avoids the header row sliding
   beneath the sticky lk-bar chrome band). */
.canvas #inventra-tab .inv-table th, .canvas #ordio-tab .ord-table th, .canvas #pimly-tab .pimly-table th { position: static; }

/* ── Keyboard-reachable table rows ──────────────────────────────────────────
   Nine modules render their lists as `<tr data-open-x="…">` with a delegated CLICK
   handler: fine with a mouse, impossible with a keyboard — a `<tr>` is not focusable
   and takes no keystrokes, so a work order, ticket, order, requisition, inspection,
   asset, product or subscription simply could not be opened without a pointer.
   The fix is a real button around the row's identifying cell (its number/name), which
   is what a screen-reader user needs to hear anyway ("Open work order WO-1042"). This
   class makes that button LOOK like the text it wraps, so the tables are unchanged
   visually, while keeping a strong focus ring for the people who need it. */
.smz-rowbtn {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; text-align: inherit; cursor: pointer;
}
.smz-rowbtn:focus-visible {
  outline: 2px solid var(--primary, #1a73e8);
  outline-offset: 2px;
  border-radius: 3px;
}


/* ── Dark: the module is ONE surface ──────────────────────────────────────────
   In light the module panel is a white card lifted off a grey canvas, and that
   reads well — it stays. In dark the same idea inverts badly: the header and
   toolbar were painted --bg-surface (#292a2d) while the body below them is the
   content tone (#202124, showing through the transparent page wrappers), so the
   chrome sat as a lighter slab above a darker page.

   These rules put the header, the toolbar and the module surface on the content
   tone, so the whole module reads as one continuous dark surface. Scoped to dark
   only, and specific enough to beat the `!important` white-surface rule that the
   light design relies on. */
html[data-theme='dark'] .canvas .tab-content.active,
html[data-theme='dark'] .canvas .tab-content.active :is(.app-shell__header, .app-shell__bar, .app-page-head),
html[data-theme='dark'] .app-shell .app-shell__header,
html[data-theme='dark'] #kriya-tab .wk-bar--tasks,
html[data-theme='dark'] .lk-bar__row {
  background: var(--zone-content) !important;
}

/* The ERP module BODIES.
   Rule 3 further up ("the mains: WHITE body surface") paints them `--bg-surface`, which is
   right in light — a white page — and wrong in dark, where --bg-surface is the RAISED CARD
   grey (#292a2d) against a #202124 page. The result was a pale slab under every list in
   these modules, while Kriya and Orbit — whose mains set no background at all and simply
   inherit `.canvas` — read as one continuous dark surface. Naming the mains here puts them
   on exactly what those two inherit. Light is untouched.

   Its own rule rather than another branch of the list above: a multi-line comment placed
   BETWEEN two comma-separated selectors ends up inside the branch that follows it in at
   least one parser, which silently kills that branch. Comments go above a rule, never
   inside its selector list. */
html[data-theme='dark'] .canvas #nirman-tab .nir-main,
html[data-theme='dark'] .canvas #inventra-tab .inv-main,
html[data-theme='dark'] .canvas #godam-tab .gd-main,
html[data-theme='dark'] .canvas #procure-tab .prc-main,
html[data-theme='dark'] .canvas #sampada-tab .sm-main,
html[data-theme='dark'] .canvas #kavach-tab .kv-main,
html[data-theme='dark'] .canvas #ordio-tab .ord-main,
html[data-theme='dark'] .canvas #pimly-tab .pimly-main,
html[data-theme='dark'] .canvas #vikray-tab .vk-main,
html[data-theme='dark'] .canvas #dravya-tab .drv-main,
html[data-theme='dark'] .canvas #seva-tab .sv-main,
html[data-theme='dark'] .canvas #samuha-tab .sg-main,
html[data-theme='dark'] .canvas #sutra-tab .su-main,
html[data-theme='dark'] .canvas #pulse-tab .pul-main,
html[data-theme='dark'] .canvas #chakra-tab .ck-main,
html[data-theme='dark'] .canvas #bazaar-tab .bz-main,
html[data-theme='dark'] .canvas #ishop-tab .shop-main {
  background: var(--zone-content) !important;
}
/* The module headers that are their own banner rather than the shell's: Kriya's board
   header, the entity detail header, the schedule header. Two reasons they needed adding
   separately, both invisible until you set a header colour:
     · `.wk-board-header` resolves to --bg-surface, one step LIGHTER than the body;
     · the others take --app-header-bg, which Settings → Header & filter colours writes as
       an INLINE style on <html> — so a user's brand colour lands a saturated bar in the
       middle of a dark page, and no token value can override an inline custom property.
   Overriding the usage (not the variable) is the only thing that reaches it. Light keeps
   the banner exactly as chosen; this is dark-only. */
html[data-theme='dark'] :is(.wk-board-header, .app-detail-header, .sch-header) {
  background: var(--zone-content) !important;
  color: var(--text) !important;
  box-shadow: none !important;
}
/* ── A date field that can carry a time of day (Kriya task detail) ────────────
   The time input is revealed by the "Time" checkbox rather than always shown:
   most tasks are due on a day, and an empty time box on every date field reads as
   something you forgot to fill in. Tokens only, so it themes with everything else. */
.wk-dtfield { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.wk-dtfield__time { flex: none; }
.wk-dtfield__opt {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: 12px; color: var(--text-subtle); cursor: pointer; user-select: none;
}
.wk-dtfield__opt input { margin: 0; cursor: pointer; }
.wk-dtfield__shown { margin-left: 2px; }

/* ── Dark: the module list tables need no patch ────────────────────────────────
   A block here used to force a dark head band, a hover tint and a raised divider onto
   thirteen module tables with `!important`, because the "modern data-grid" rules they
   carried were ID-scoped (unreachable by any theme selector) and hardcoded near-white
   literals (#f0f5ff hover, #f1f5f9 divider) that read as bright bands on a dark page.

   Those rules are gone. The shared "MODULE LIST TABLES" block at the end of this file uses
   `--bg-surface` / `--border-muted` / `--bg-hover` — the same tokens Kriya's task list
   uses — so dark follows by construction. `.orbit-table` was the last holdout and now sits
   in that block too, which is what makes the leads list and the task list the same object.

   Deleting the patch matters as much as adding the block: `!important` on a dark head band
   would have fought the shared surface and won, leaving dark and light disagreeing again. */

/* Most modules render `.page-left-rail`, which is still a live container class — the navigation
   column CLONES it, so its item styling reaches the screen even though the original is parked.
   (This rule once existed because promotion added `.smz-second-rail` to some rails and not
   others, leaving the plain ones with a right border at --border (#5f6368), far brighter than the
   rail seam. There is no promotion now; the rule stays because the container still renders.) */
.canvas .tab-content.active .page-left-rail {
  border-right: 0 !important;
}

/* ── Central: Kriya project images widget ─────────────────────────────────────
   A plain responsive grid — the widget's own box already supplies the card, so this
   only lays the thumbnails out. Tokens throughout so it themes with the dashboard. */
.iq-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: 8px;
}
.iq-gallery__item { margin: 0; min-width: 0; }
.iq-gallery__img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 8px;
  border: 1px solid var(--border-muted, var(--border));
  background: var(--bg-muted);
}
.iq-gallery__cap {
  margin-top: 4px;
  font-size: 0.72rem;
  color: var(--text-subtle);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.iq-gallery__more {
  margin: 8px 0 0;
  font-size: 0.74rem;
  color: var(--text-faint, var(--text-subtle));
}

/* ── The mirrored first rail ──────────────────────────────────────────────────
   The real buttons stay in the module's own header, where every module's delegated
   handler expects to find them. What sits in the rail is a COPY, and a click on the
   copy is forwarded to the button it stands for — so the icons escape Safari's clip
   without changing what any handler sees.

   The originals therefore have to stop painting, or Chromium would show both. They are
   still laid out in the rail (same rules), just invisible — and `visibility: hidden`
   also stops them swallowing the clicks meant for the copy. Only the destination icons
   are mirrored; the pinned +New keeps its own place at the header's right edge, which
   is inside the tab's box and was never clipped. */
.canvas .tab-content.active :is(.app-shell__actions, .wk-hdr-actions) > [data-rail-idx] {
  visibility: hidden;
}
/* ── Artha: the Setup-defined custom-field values, on the document ────────────
   `cfSummaryHtml` shipped with custom fields and was never called, so an admin could
   define "PO reference" on bills, fill it in, and never see it on the bill again. The
   block now renders under every document's Overview; these are its only styles, kept to
   the same two-column label/value shape the rest of the detail markup reads as. */
.artha-cf-block { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--border-muted); }
.artha-cf-head { display: flex; align-items: center; gap: .6rem; margin-bottom: .5rem; }
.artha-cf-head h4 { margin: 0; font-size: .78rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-subtle); }
.artha-cf-head button { margin-left: auto; }
.artha-cf-summary { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: .5rem 1.5rem; }
.artha-cf-summary > div { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }
/* The label is a `.muted.small` span and the value a `<b>` — no extra markup to hook, so
   the layout is driven off the element types the helper already emits. */
.artha-cf-summary > div > span { font-size: .66rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-faint); }
.artha-cf-summary > div > b { font-size: .9rem; font-weight: 550; overflow-wrap: anywhere; }

/* ═══════════════════════════════════════════════════════════════════════════════
   MODULE LIST TABLES — the same surface as Kriya's task list.

   The reference is `/kriya?section=task-board&view=list`: `.wk-tbl` + `.wk-thead__cell` +
   `.wk-trow__cell` (a CSS grid, because that list needs a pinned first column and grouped
   sections; these are real <table>s, so the rules differ but every VALUE below is copied
   from it). Not `.wk-ltable` — that is the item-library table, which is a different surface
   with a dark header, and matching it is what made these tables look like a third thing.

   What Kriya's list actually is: a bordered, 10px-radius card on `--bg-surface`; a header
   band on that SAME surface with small uppercase muted ink; hairline column separators; row
   hover on `--bg-hover`; and NO zebra striping — the separators already do that work, and
   striping on top of them reads as two competing grids.

   Seventeen modules had each pasted their own five declarations into their partial, ID-scoped
   (`#pimly-tab .pimly-table th`) so nothing at theme level could outrank them. Those copies
   are gone; this is the single source, and it uses the tokens Kriya uses, so both themes
   follow without a separate dark patch.

   NO `:is()` here, deliberately: WebKit ships it with specificity ZERO (see the rail-ink note
   above), which would drop every selector to element specificity in Safari.

   `.ws-table` is the Settings ▸ Org ▸ Orgs list (workspace controller, 2026-08-20) — a
   settings-hosted list adopting the same skin, swept by the same tableSkin.spec. `.oa-table` is
   Org Admin ▸ People (2026-08-21), which became a real table the same way. That console injects
   ~50 rules of its own chrome scoped to `#orgadmin-tab`, and NONE of them may name a table cell:
   an ID-scoped copy is exactly how seventeen modules kept a private look while this block said
   otherwise.

   `.party-table` is Customers & Vendors (2026-09-10). Its partial had said since the day it
   shipped that "the table skin is the app-wide MODULE LIST TABLES block (`.party-table`)" — and
   the class was in NO selector list here, so all SEVEN of that module's tables (the company list
   plus roles, people, places, banking, history and activity on the record) rendered as bare
   browser-default tables: no card, no header band, no separators, no hover. Nothing failed,
   because `tableSkin.spec.js`'s sweep is a NAMED list and the module was not on it. It is now.

   `.sch-ltable` is Schpec's PRODUCT LIBRARY (2026-09-10) — the same story one module over, with a
   different ending. That list shared `.sch-table` with Schpec's editable schedule grid, and
   `.sch-table` was in the SUPERSEDED "App-wide table treatment" earlier in this file: a dark head
   band and zebra striping, i.e. the `.wk-ltable` look this block was written to stop these tables
   copying. So it was not unstyled like Customers & Vendors — it was styled as the one thing this
   block forbids. The GRID keeps `.sch-table` and takes Kriya's ink at its own (editor) density,
   because the padding in here IS the contract and a row where every cell is an input cannot carry
   12px of cell padding; the LIST is `.sch-ltable` and is swept with everything else.
   ═══════════════════════════════════════════════════════════════════════════════ */

.pimly-table,
.orbit-table,
.iam-table,
.inv-table,
.ord-table,
.prc-table,
.sv-table,
.kv-table,
.nir-table,
.sm-table,
.gd-table,
.sg-table,
.su-table,
.vk-table,
.drv-table,
.pul-table,
.bz-table,
.ck-table,
.py-table,
.pt-table,
.shop-table,
.oa-table,
.sch-ltable,
.party-table,
.ws-table {
  width: 100%;
  /* `separate` + zero spacing rather than `collapse`: a collapsed table cannot round its
     corners, and the card edge is half of what makes Kriya's list read as one object. */
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  font-size: 12.5px;
}

.pimly-table thead th,
.orbit-table thead th,
.iam-table thead th,
.inv-table thead th,
.ord-table thead th,
.prc-table thead th,
.sv-table thead th,
.kv-table thead th,
.nir-table thead th,
.sm-table thead th,
.gd-table thead th,
.sg-table thead th,
.su-table thead th,
.vk-table thead th,
.drv-table thead th,
.pul-table thead th,
.bz-table thead th,
.ck-table thead th,
.py-table thead th,
.pt-table thead th,
.shop-table thead th,
.oa-table thead th,
.sch-ltable thead th,
.party-table thead th,
.ws-table thead th {
  position: sticky;
  top: 0;
  z-index: 3;
  text-align: left;
  padding: 9px 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-muted, #64748b);
  /* The header sits on the SAME surface as the rows — Kriya's list has no dark head band.
     An opaque background is still required, or rows show through as they scroll under it. */
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border);
  border-left: 1px solid var(--border-muted);
}

.pimly-table thead th:first-child,
.orbit-table thead th:first-child,
.iam-table thead th:first-child,
.inv-table thead th:first-child,
.ord-table thead th:first-child,
.prc-table thead th:first-child,
.sv-table thead th:first-child,
.kv-table thead th:first-child,
.nir-table thead th:first-child,
.sm-table thead th:first-child,
.gd-table thead th:first-child,
.sg-table thead th:first-child,
.su-table thead th:first-child,
.vk-table thead th:first-child,
.drv-table thead th:first-child,
.pul-table thead th:first-child,
.bz-table thead th:first-child,
.ck-table thead th:first-child,
.py-table thead th:first-child,
.pt-table thead th:first-child,
.shop-table thead th:first-child,
.oa-table thead th:first-child,
.sch-ltable thead th:first-child,
.party-table thead th:first-child,
.ws-table thead th:first-child {
  border-left: 0;
}

.pimly-table tbody td,
.orbit-table tbody td,
.iam-table tbody td,
.inv-table tbody td,
.ord-table tbody td,
.prc-table tbody td,
.sv-table tbody td,
.kv-table tbody td,
.nir-table tbody td,
.sm-table tbody td,
.gd-table tbody td,
.sg-table tbody td,
.su-table tbody td,
.vk-table tbody td,
.drv-table tbody td,
.pul-table tbody td,
.bz-table tbody td,
.ck-table tbody td,
.py-table tbody td,
.pt-table tbody td,
.shop-table tbody td,
.oa-table tbody td,
.sch-ltable tbody td,
.party-table tbody td,
.ws-table tbody td {
  padding: 7px 12px;
  font-size: 12.5px;
  color: var(--text);
  vertical-align: middle;
  border-bottom: 1px solid var(--border-muted);
  border-left: 1px solid var(--border-muted);
}

.pimly-table tbody td:first-child,
.orbit-table tbody td:first-child,
.iam-table tbody td:first-child,
.inv-table tbody td:first-child,
.ord-table tbody td:first-child,
.prc-table tbody td:first-child,
.sv-table tbody td:first-child,
.kv-table tbody td:first-child,
.nir-table tbody td:first-child,
.sm-table tbody td:first-child,
.gd-table tbody td:first-child,
.sg-table tbody td:first-child,
.su-table tbody td:first-child,
.vk-table tbody td:first-child,
.drv-table tbody td:first-child,
.pul-table tbody td:first-child,
.bz-table tbody td:first-child,
.ck-table tbody td:first-child,
.py-table tbody td:first-child,
.pt-table tbody td:first-child,
.shop-table tbody td:first-child,
.oa-table tbody td:first-child,
.sch-ltable tbody td:first-child,
.party-table tbody td:first-child,
.ws-table tbody td:first-child {
  border-left: 0;
}

.pimly-table tbody tr:last-child td,
.orbit-table tbody tr:last-child td,
.iam-table tbody tr:last-child td,
.inv-table tbody tr:last-child td,
.ord-table tbody tr:last-child td,
.prc-table tbody tr:last-child td,
.sv-table tbody tr:last-child td,
.kv-table tbody tr:last-child td,
.nir-table tbody tr:last-child td,
.sm-table tbody tr:last-child td,
.gd-table tbody tr:last-child td,
.sg-table tbody tr:last-child td,
.su-table tbody tr:last-child td,
.vk-table tbody tr:last-child td,
.drv-table tbody tr:last-child td,
.pul-table tbody tr:last-child td,
.bz-table tbody tr:last-child td,
.ck-table tbody tr:last-child td,
.py-table tbody tr:last-child td,
.pt-table tbody tr:last-child td,
.shop-table tbody tr:last-child td,
.oa-table tbody tr:last-child td,
.sch-ltable tbody tr:last-child td,
.party-table tbody tr:last-child td,
.ws-table tbody tr:last-child td {
  border-bottom: 0;
}

/* ── STACKED TABLES MUST LINE UP WITH EACH OTHER ─────────────────────────────
   2026-09-18, user report against a screenshot of Settings > Forms > Form layouts, circling the
   ragged right-hand column: *"fix these alignment issues."*

   Three Patra surfaces stack several `.pt-table`s down one page — one card per module on Form
   layouts and on Where they are used, and two headed lists on Templates. A table sizes its own
   columns from its own content, so each card was laid out independently and every boundary
   wandered. Measured at 1600px through `probe/settings.html?forms=1`:

     card                 Form   Using   Last changed   actions
     Finance (module on)   265     541            932   1264  (299px wide)
     Projects (off)        265     596           1072   1478  ( 85px wide)
     CRM (off)             265     603           1076   1478  ( 85px wide)

   `Last changed` drifts 144px between cards, and the action column is 299px where the Arrange
   button renders and 85px where the module is switched off and it does not — which is the step
   the report circled. Even the two switched-off cards disagree by 7px, because their labels are
   different lengths.

   `table-layout: fixed` + a `<colgroup>` in each table is the whole fix: the geometry stops being
   a function of the content, so N cards lay out identically BY CONSTRUCTION rather than by
   coincidence of what happens to be in them. The widths live in the markup, beside the headers
   they belong to, because a column set declared in CSS by `nth-child` breaks silently the day a
   column is added.

   THE WIDTHS ARE PERCENTAGES, INCLUDING THE ACTION COLUMN, and that is measured rather than
   tidy. A px action column wide enough for its buttons (Templates needs ~328px for three) starves
   the first column on a narrow pane — the same "a 1fr track beside an unshrinkable one silently
   starves" failure the role editor hit the same day. `width: max(360px, 26%)` in a `<col>` would
   have been the answer and is NOT honoured: measured at 327px against its own 360px floor.

   `overflow-wrap` comes with it, and is not optional: under `fixed`, a cell no longer widens for
   a long word, so without it a long record key would overflow its cell and print across the one
   beside it. */
/* A `<td>` THAT IS A FLEX CONTAINER IS NOT A TABLE CELL, and that is the other half of the
   report. `.pt-scope .pt-actions { display: flex }` (in `partials/patra.html`) was written for the
   four `<div class="pt-actions">` toolbars it also styles, and applied to the three `<td>`s as
   well — where `display: flex` replaces `display: table-cell`, so the browser takes the cell OUT
   of the row and generates an anonymous one around it. The column then stops tracking its
   neighbours and each row's action cell is drawn at its own offset: the staircase of stray box
   edges down the right-hand side of Form layouts that the report circled.

   THE FIX IS THE MARKUP, not a `display` override. `<td class="pt-actionscell"><div
   class="pt-actions">` keeps the cell a cell and gives the flex box its own element — so the
   buttons keep `gap` in BOTH axes, which an override to `display: table-cell` would have taken
   away (a td cannot use `gap`, and margins between inline-blocks give no row gap when they wrap).

   Its own class rather than a bare `td`: `.pt-actions` still means "a row of buttons" everywhere
   else in Patra, and narrowing the shared rule to `div.pt-actions` would leave the next `<td
   class="pt-actions">` silently unstyled instead of visibly wrong. */
.pt-scope td.pt-actionscell { vertical-align: middle; }

.pt-table--fixed { table-layout: fixed; }
.pt-table--fixed thead th,
.pt-table--fixed tbody td { overflow-wrap: anywhere; }

.pimly-table tbody tr:hover td,
.orbit-table tbody tr:hover td,
.iam-table tbody tr:hover td,
.inv-table tbody tr:hover td,
.ord-table tbody tr:hover td,
.prc-table tbody tr:hover td,
.sv-table tbody tr:hover td,
.kv-table tbody tr:hover td,
.nir-table tbody tr:hover td,
.sm-table tbody tr:hover td,
.gd-table tbody tr:hover td,
.sg-table tbody tr:hover td,
.su-table tbody tr:hover td,
.vk-table tbody tr:hover td,
.drv-table tbody tr:hover td,
.pul-table tbody tr:hover td,
.bz-table tbody tr:hover td,
.ck-table tbody tr:hover td,
.py-table tbody tr:hover td,
.pt-table tbody tr:hover td,
.shop-table tbody tr:hover td,
.oa-table tbody tr:hover td,
.sch-ltable tbody tr:hover td,
.party-table tbody tr:hover td,
.ws-table tbody tr:hover td {
  /* Kriya hovers the row on --bg-hover and stops there. No zebra: the hairline separators
     already group the eye, and striping on top of them reads as two competing grids. */
  background: var(--bg-hover);
}

.pimly-table thead th:first-child,
.orbit-table thead th:first-child,
.iam-table thead th:first-child,
.inv-table thead th:first-child,
.ord-table thead th:first-child,
.prc-table thead th:first-child,
.sv-table thead th:first-child,
.kv-table thead th:first-child,
.nir-table thead th:first-child,
.sm-table thead th:first-child,
.gd-table thead th:first-child,
.sg-table thead th:first-child,
.su-table thead th:first-child,
.vk-table thead th:first-child,
.drv-table thead th:first-child,
.pul-table thead th:first-child,
.bz-table thead th:first-child,
.ck-table thead th:first-child,
.py-table thead th:first-child,
.pt-table thead th:first-child,
.shop-table thead th:first-child,
.oa-table thead th:first-child,
.party-table thead th:first-child,
.ws-table thead th:first-child {
  border-top-left-radius: 9px;
}

.pimly-table thead th:last-child,
.orbit-table thead th:last-child,
.iam-table thead th:last-child,
.inv-table thead th:last-child,
.ord-table thead th:last-child,
.prc-table thead th:last-child,
.sv-table thead th:last-child,
.kv-table thead th:last-child,
.nir-table thead th:last-child,
.sm-table thead th:last-child,
.gd-table thead th:last-child,
.sg-table thead th:last-child,
.su-table thead th:last-child,
.vk-table thead th:last-child,
.drv-table thead th:last-child,
.pul-table thead th:last-child,
.bz-table thead th:last-child,
.ck-table thead th:last-child,
.py-table thead th:last-child,
.pt-table thead th:last-child,
.shop-table thead th:last-child,
.oa-table thead th:last-child,
.party-table thead th:last-child,
.ws-table thead th:last-child {
  border-top-right-radius: 9px;
}

.pimly-table tbody tr:last-child td:first-child,
.orbit-table tbody tr:last-child td:first-child,
.iam-table tbody tr:last-child td:first-child,
.inv-table tbody tr:last-child td:first-child,
.ord-table tbody tr:last-child td:first-child,
.prc-table tbody tr:last-child td:first-child,
.sv-table tbody tr:last-child td:first-child,
.kv-table tbody tr:last-child td:first-child,
.nir-table tbody tr:last-child td:first-child,
.sm-table tbody tr:last-child td:first-child,
.gd-table tbody tr:last-child td:first-child,
.sg-table tbody tr:last-child td:first-child,
.su-table tbody tr:last-child td:first-child,
.vk-table tbody tr:last-child td:first-child,
.drv-table tbody tr:last-child td:first-child,
.pul-table tbody tr:last-child td:first-child,
.bz-table tbody tr:last-child td:first-child,
.ck-table tbody tr:last-child td:first-child,
.py-table tbody tr:last-child td:first-child,
.pt-table tbody tr:last-child td:first-child,
.shop-table tbody tr:last-child td:first-child,
.oa-table tbody tr:last-child td:first-child,
.party-table tbody tr:last-child td:first-child,
.ws-table tbody tr:last-child td:first-child {
  border-bottom-left-radius: 9px;
}

.pimly-table tbody tr:last-child td:last-child,
.orbit-table tbody tr:last-child td:last-child,
.iam-table tbody tr:last-child td:last-child,
.inv-table tbody tr:last-child td:last-child,
.ord-table tbody tr:last-child td:last-child,
.prc-table tbody tr:last-child td:last-child,
.sv-table tbody tr:last-child td:last-child,
.kv-table tbody tr:last-child td:last-child,
.nir-table tbody tr:last-child td:last-child,
.sm-table tbody tr:last-child td:last-child,
.gd-table tbody tr:last-child td:last-child,
.sg-table tbody tr:last-child td:last-child,
.su-table tbody tr:last-child td:last-child,
.vk-table tbody tr:last-child td:last-child,
.drv-table tbody tr:last-child td:last-child,
.pul-table tbody tr:last-child td:last-child,
.bz-table tbody tr:last-child td:last-child,
.ck-table tbody tr:last-child td:last-child,
.py-table tbody tr:last-child td:last-child,
.pt-table tbody tr:last-child td:last-child,
.shop-table tbody tr:last-child td:last-child,
.oa-table tbody tr:last-child td:last-child,
.party-table tbody tr:last-child td:last-child,
.ws-table tbody tr:last-child td:last-child {
  border-bottom-right-radius: 9px;
}

.pimly-table thead th input[type="checkbox"],
.orbit-table thead th input[type="checkbox"],
.iam-table thead th input[type="checkbox"],
.inv-table thead th input[type="checkbox"],
.ord-table thead th input[type="checkbox"],
.prc-table thead th input[type="checkbox"],
.sv-table thead th input[type="checkbox"],
.kv-table thead th input[type="checkbox"],
.nir-table thead th input[type="checkbox"],
.sm-table thead th input[type="checkbox"],
.gd-table thead th input[type="checkbox"],
.sg-table thead th input[type="checkbox"],
.su-table thead th input[type="checkbox"],
.vk-table thead th input[type="checkbox"],
.drv-table thead th input[type="checkbox"],
.pul-table thead th input[type="checkbox"],
.bz-table thead th input[type="checkbox"],
.ck-table thead th input[type="checkbox"],
.py-table thead th input[type="checkbox"],
.pt-table thead th input[type="checkbox"],
.shop-table thead th input[type="checkbox"],
.oa-table thead th input[type="checkbox"],
.party-table thead th input[type="checkbox"],
.ws-table thead th input[type="checkbox"] {
  /* A select-all box in the header band. Tinted so it is findable in both themes. */
  accent-color: var(--primary, #2563eb);
  vertical-align: middle;
}

.pimly-table tbody td.pimly-empty,
.orbit-table tbody td.orbit-empty,
.iam-table tbody td.iam-empty,
.inv-table tbody td.inv-empty,
.ord-table tbody td.ord-empty,
.prc-table tbody td.prc-empty,
.sv-table tbody td.sv-empty,
.kv-table tbody td.kv-empty,
.nir-table tbody td.nir-empty,
.sm-table tbody td.sm-empty,
.gd-table tbody td.gd-empty,
.sg-table tbody td.sg-empty,
.su-table tbody td.su-empty,
.vk-table tbody td.vk-empty,
.drv-table tbody td.drv-empty,
.pul-table tbody td.pul-empty,
.bz-table tbody td.bz-empty,
.ck-table tbody td.ck-empty,
.py-table tbody td.py-empty,
.pt-table tbody td.pt-empty,
.shop-table tbody td.shop-empty,
.oa-table tbody td.shop-empty,
.party-table tbody td.shop-empty,
.ws-table tbody td.shop-empty {
  /* Not a record — centred, so an empty list does not read as one blank row. The modules'
     own `#x-tab .x-empty` rules are ID-scoped and still own its colour and padding. */
  text-align: center;
  border-left: 0;
}

/* ── A NUMERIC COLUMN IS RIGHT-ALIGNED (ui/tableKit.js `align: 'right'`) ───────
   `tkHead`/`tkRows` have emitted `class="tk-num"` for a right-aligned column since the
   toolkit shipped and NOTHING anywhere declared the class, so every money and count column
   in every adopter rendered left-aligned — the declaration was made and had no effect.
   `tabular-nums` is the other half: proportional digits make a column of figures ragged even
   once it is flush right, which is the whole reason a ledger column is right-aligned.

   Element-qualified AND declared after the `tbody td` rule above, which is (0,1,1): a bare
   `.tk-num` is (0,1,0) and would lose to it. The unqualified selector below is for the
   handful of callers that put the class on something other than a cell. */
td.tk-num,
th.tk-num,
.tk-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The sort arrow `tkHead` puts beside the label of the column currently sorted. Also declared
   nowhere until 2026-09-11, so it rendered at full body size jammed against the heading — the
   same class of gap as `tk-num` beside it, found by the sweep written for that one. It marks
   which column is sorted, so it is deliberately quieter than the label it follows rather than
   another thing competing for the eye. */
.tk-sort {
  margin-left: 4px;
  font-size: 9px;
  line-height: 1;
  opacity: 0.75;
}

/* ── Grouped list rows (ui/tableKit.js `tkGroupedRows`) ────────────────────────
   Kriya's task list bands its groups with `.wk-tgroup__head`: a full-width separator row
   on --bg-muted carrying the name and a count. Same band, as a real <tr>. */
tr.tk-group > .tk-group__cell {
  background: var(--bg-muted);
  padding: 8px 14px;
  border-bottom: 1px solid var(--border-muted);
  border-left: 0;
}
.tk-group__name { font-size: 13px; font-weight: 700; color: var(--text); }
.tk-group__count {
  margin-left: 8px; font-size: 11px; font-weight: 600; color: var(--text-muted);
  background: var(--bg-surface); border: 1px solid var(--border-muted);
  border-radius: 999px; padding: 1px 7px;
}
/* A group band is structure, not a record: it must not light up on hover like a row. */
tr.tk-group:hover > .tk-group__cell { background: var(--bg-muted); }

/* ── Bulk-selection bar above a list ───────────────────────────────────────────
   Appears only once something is ticked, so the toolbar is not permanently carrying a
   control for an action nobody is taking. */
.tk-bulkbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 8px; padding: 7px 12px;
  border: 1px solid var(--border); border-radius: 8px;
  background: color-mix(in srgb, var(--primary, #2563eb) 7%, var(--bg-surface));
  font-size: 12.5px;
}
.tk-bulkbar__n { font-weight: 700; }
.tk-bulkbar__sp { margin-left: auto; }
/* The Group-by picker sits in the list toolbar beside Columns — a control, not a filter. */
.iam-groupby {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  color: var(--text-muted); white-space: nowrap;
}
.iam-groupby select {
  width: auto; font: inherit; font-size: 12px; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text);
  padding: 5px 8px; border: 1px solid var(--border); border-radius: 7px;
  background: var(--bg-surface);
}

/* ── Access grants: the holder-kind tabs ───────────────────────────────────────
   People / Groups / Agents / Service accounts. These sit ABOVE the list toolbar, because
   they choose WHICH list you are reading — the search, the filters and the "N of M" chip
   below them all speak about the one you picked. The underline shape is the app's existing
   sub-tab (`.ws-subtab`, workspace); repeated here in module-neutral tokens rather than by
   reusing that class, which is scoped to `.workspace-view`.
   The count chip is the point of the strip: it is what tells a reader who searched a name
   and found nothing on People that the matches are two tabs along. */
/* Focus must be visible on a control that is also the page's navigation. */
.iam-tab.is-active .iam-tab__n {
  background: color-mix(in srgb, var(--primary, #2563eb) 14%, var(--bg-surface));
  color: var(--primary);
}

/* Roles: the expanded permission panel is a full-width row INSIDE the table, not a block
   after it — a role's permissions have to read as belonging to that role, and a panel
   printed under the whole table belongs to nothing. It carries no hover and no column
   separators, so it never reads as another role. */
.iam-table tbody tr.iam-perm-row > td {
  padding: 2px 12px 12px;
  background: var(--bg-surface);
}
.iam-table tbody tr.iam-perm-row:hover > td { background: var(--bg-surface); }
.iam-table tbody tr.iam-perm-row .iam-subrows { margin-left: 4px; }
/* The role KEY is an identifier, not prose: the API, an import and a vertical pack all name
   the role by it, so it is set in the face that makes a character-for-character copy safe. */
.iam-key {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px; padding: 1px 5px; border-radius: 5px;
  background: var(--bg-muted); color: var(--text-muted);
}

/* An org unit's modules, under its group (moved here 2026-08-22 when Settings ▸ Org units was
   retired). A wrapping grid rather than a column: eleven modules down the page pushes the
   members below it off screen, and these are ticked in a glance, not read one at a time. */
.iam-modgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 2px 10px;
}
.iam-modchk {
  display: flex; align-items: center; gap: 6px;
  font-size: 12.5px; color: var(--text); cursor: pointer;
  padding: 2px 0; margin: 0;
}
.iam-modchk input { margin: 0; flex: 0 0 auto; }

/* ── THE SIX GATES, on every Access page (2026-09-15) ──────────────────────────
   One muted line at the top of the page BODY — content about the page, not a second chrome
   band, which is why it carries no fill, no border and no padding of its own. Its whole job is
   to say that these five screens are one model read in one order, and to be a door to the two
   gates that are not in this console at all.
   A segment for a gate THIS page decides is not a button: a control that navigates you to where
   you already are is the mistake recorded against the party record. */
.iam-gates { display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
  margin: 0 0 12px; font-size: 11px; color: var(--text-muted); }
.iam-gates__sep { color: var(--text-faint); }
.iam-gate { display: inline-flex; align-items: center; gap: 5px; padding: 2px 7px;
  border: 0; border-radius: 999px; background: none; font: inherit; font-size: 11px;
  color: var(--text-muted); letter-spacing: .02em; }
/* THE SEGMENTS ARE SPANS SINCE 2026-09-18 — the chain illustrates an order on the page that
   explains it, and navigates nowhere. `button.iam-gate` and `.is-locked` went with the ribbon
   that used to carry them onto every Access page; `.is-on` stays because the reference marks no
   gate as owned and a later caller might. */
.iam-gate.is-on { background: var(--bg-muted); color: var(--text); font-weight: 700; }
.iam-gate__n { display: inline-grid; place-items: center; width: 14px; height: 14px;
  border-radius: 999px; background: var(--bg-muted); color: var(--text-subtle);
  font-size: 9px; font-weight: 700; }
.iam-gate.is-on .iam-gate__n { background: var(--primary); color: var(--primary-ink, #fff); }
/* `.iam-gates__howto` WENT WITH THE RIBBON (2026-09-18). It styled the "Check access" and "How
   access works" doors that sat after the six gates on every Access page; both are rail items, so
   the strip was repeating navigation over every working list. */

/* ── "How access works" — the reference panel (2026-09-17) ─────────────────────
   What the Permissions and Scopes tabs became. Three collapsible sections in the shared drawer,
   the one a deep link named already open. `<details>` rather than a tab strip on purpose: these
   are things you read, and a reader comparing the ladder against the scope list wants both open
   at once — which a tab strip is precisely the control for refusing. */
.iam-howto__sec { border-bottom: 1px solid var(--border-muted); padding: 0 0 10px; margin: 0 0 10px; }
.iam-howto__sec:last-child { border-bottom: 0; }
.iam-howto__sec > summary { cursor: pointer; font-weight: 600; font-size: .9rem; padding: 6px 0; }
.iam-howto__body { padding: 4px 0 0; }
.iam-howto__body > p { margin: 0 0 10px; line-height: 1.5; }
/* The gate list, spelled out under the ribbon — the ribbon says the ORDER, this says what each
   one asks. Numbers come from the same `_GATES` array the ribbon draws. */
.iam-gatelist { margin: 10px 0 0; padding-left: 18px; }
.iam-gatelist li { margin: 0 0 6px; line-height: 1.5; font-size: .82rem; color: var(--text-muted); }
.iam-gatelist strong { color: var(--text); }

/* ── THE AUDIT LOG'S before/after DIFF (2026-09-17) ────────────────────────────
   `IamAuditLog` has stored both snapshots since the log shipped and no screen had ever rendered
   one, so the trail said that something changed and never what. A quiet table, indented under its
   row — not a card: it is the row's own detail, and boxing it would make a timeline of entries
   read as a stack of panels. */
.iam-diff { margin: 2px 0 10px 26px; }
.iam-diff__t { width: 100%; border-collapse: collapse; font-size: .78rem; }
.iam-diff__t th[scope="col"] { text-align: left; font-size: .68rem; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-subtle); font-weight: 700;
  padding: 0 10px 4px 0; border-bottom: 1px solid var(--border-muted); }
.iam-diff__t th[scope="row"] { text-align: left; font-weight: 600; color: var(--text);
  padding: 4px 10px 4px 0; white-space: nowrap; }
.iam-diff__t td { padding: 4px 10px 4px 0; color: var(--text-muted); vertical-align: top; }
/* An UNCHANGED field is kept and dimmed rather than dropped: an audit entry is read to answer
   "what did this touch", and a diff with the context removed makes the changed line unreadable. */
.iam-diff__t tr.is-same th[scope="row"] { font-weight: 400; color: var(--text-muted); }
.iam-diff__t tr.is-same td { color: var(--text-subtle); }
/* `--success` / `--danger` carry their own ink in both themes; no hardcoded fill, so nothing to
   go white-on-white in dark mode. */
.iam-diff__in { color: var(--success, #188038); white-space: nowrap; margin-right: 8px; }
.iam-diff__out { color: var(--danger, #d93025); white-space: nowrap; margin-right: 8px; }

/* ── THE FIRST-RUN WIZARD on Access ▸ By module (2026-09-17) ───────────────────
   Shown only while the org holds no grants at all, and gone by itself once it does. A tinted
   block rather than a bordered card: it is the one thing on the page worth doing, so it reads as
   content, and it is temporary — a card would leave a hole in the layout the day it goes.
   `--accent-soft` with a left rule is the shape this app uses elsewhere for "start here". */
.iam-setup { margin: 0 0 16px; padding: 14px 16px; border-left: 3px solid var(--primary);
  border-radius: 0 10px 10px 0; background: var(--accent-soft, var(--bg-muted)); }
.iam-setup__h { margin: 0 0 10px; font-size: .95rem; font-weight: 600; }
.iam-setup__steps { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 12px; padding: 0;
  list-style: none; font-size: .78rem; color: var(--text-muted); }
.iam-setup__step { display: flex; align-items: center; gap: 6px; }
.iam-setup__step.is-on { color: var(--text); font-weight: 600; }
/* The step marker. `--primary-ink` on the filled one, never white: `--primary` is a LIGHT blue in
   dark mode and white on it fails. */
.iam-setup__n { display: inline-grid; place-items: center; width: 17px; height: 17px;
  border-radius: 999px; background: var(--bg-surface); color: var(--text-subtle);
  font-size: 10px; font-weight: 700; }
.iam-setup__step.is-on .iam-setup__n { background: var(--primary); color: var(--primary-ink, #fff); }
.iam-setup__step.is-done .iam-setup__n { background: var(--success, #188038); color: #fff; }
/* CAPPED, like every other block of prose in this app. Measured at 1440px the intro ran the full
   1100px of the pane — about 170 characters, roughly three times the line length anybody reads
   comfortably, and this is the one paragraph on the page a first-time admin is meant to read. */
.iam-setup__body > p { margin: 0 0 10px; line-height: 1.5; max-width: 68ch; }
.iam-setup__f { display: flex; flex-direction: column; gap: 4px; margin: 0 0 10px;
  max-width: 26rem; font-size: .82rem; }
.iam-setup__who { font-size: .82rem; }
.iam-setup__boxes { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: 4px; margin: 0 0 12px; }
.iam-setup__act { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.iam-setup__done { margin: 0 0 10px; line-height: 1.5; }

/* The rare escape hatch at the foot of a module's pane — "these three levels cannot say what I
   mean, build a role". A rule and a quiet line, NOT a button: it is the last thing on the page
   for a reason, and a primary control there would compete with "+ Give access" at the top, which
   is what almost everybody actually wants. */
.ma-escape { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 18px 0 0; padding: 10px 0 0; border-top: 1px solid var(--border-muted); }

/* ── Access ▸ Module access (2026-08-22; tabular 2026-08-24) ───────────────────
   EVERY module in one table, each row opening in place: who, how much, which parts.
   It was a grid of cards that navigated INTO one module, so comparing who reaches the CRM with
   who reaches Finance meant opening one, reading it, going back and opening the other. The card
   rules are gone with the grid — a class kept alive for markup nobody emits is the next
   reader's dead end. */
.ma-search { max-width: 320px; margin: 0 0 12px; }
/* ── TWO COLUMNS: the modules, and the one you picked (2026-09-15) ─────────────
   The shape Business & Modules §3 uses. The module column stays on screen the whole time, which
   is what the 2026-08-24 note above was actually protecting — its objection was to a grid that
   NAVIGATED INTO a module and took the list with it, not to master-detail as such.
   `.ma-modcol` is `max-content` up to a cap rather than `1fr`: a list of short module names
   stretched across a wide screen puts the name and its count at opposite ends of the monitor. */
.ma-cols { display: grid; grid-template-columns: minmax(11rem, 15rem) minmax(0, 1fr);
  gap: 14px; align-items: start; }
.ma-modcol { min-width: 0; border-right: 1px solid var(--border-muted); padding-right: 10px; }
.ma-modcol__head { display: flex; align-items: baseline; gap: 6px; margin: 0 4px 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-muted); }
.ma-modcol__hint { margin-left: auto; font-size: 10px; font-weight: 600; letter-spacing: 0;
  text-transform: none; color: var(--text-faint); }
/* Scrolls after a height rather than running the page long — 38 modules beside a pane that is
   often two rows tall would otherwise set the whole page's height. */
.ma-modcol__list { display: flex; flex-direction: column; gap: 1px;
  max-height: 60vh; overflow-y: auto; overscroll-behavior: contain; }
.ma-modrow { display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 8px; border: 0; border-radius: 7px; background: none; cursor: pointer;
  font: inherit; font-size: 12.5px; color: var(--text); text-align: left; }
.ma-modrow:hover { background: var(--bg-hover); }
/* ONE filled row, and it is the module you are reading — the rail's rule. */
.ma-modrow.is-on { background: var(--bg-muted); font-weight: 600; }
.ma-modrow__n { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ma-modrow__c { margin-left: auto; flex: 0 0 auto; font-size: 11px; font-weight: 700;
  color: var(--text-muted); font-variant-numeric: tabular-nums; }
/* A zero is a real answer here — nobody has been given this module — and it should read as one
   rather than as the same figure as 12. */
.ma-modrow__c[data-zero] { color: var(--text-faint); font-weight: 500; }
.ma-pane { min-width: 0; }
.ma-pane--empty { padding-top: 6px; }
.ma-pane__head { display: flex; align-items: center; gap: 10px; margin: 0 0 6px; }
.ma-pane__t { margin: 0; font-size: .95rem; font-weight: 700; }
.ma-pane__head .primary-button { margin-left: auto; margin-top: 0; }
/* THE CASCADE, stated. The six gates only narrow, and an admin cannot hold that chain in their
   head across four screens — so the module says its own chain on one line. */
.ma-cascade { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 0 0 10px;
  font-size: 11.5px; color: var(--text-muted); }
.ma-cascade__i { border-bottom: 1px dotted var(--border); cursor: help; }
.ma-cascade__sep { color: var(--text-faint); }
.ma-subhead {
  margin: 12px 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
}
/* The editor opens as a full-width row INSIDE the table, under the person it belongs to —
   a panel printed after the table would belong to nobody. */
.iam-table tbody tr.ma-editrow > td { padding: 4px 14px 16px; background: var(--bg-surface); }
.iam-table tbody tr.ma-editrow:hover > td { background: var(--bg-surface); }
.ma-level {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase;
  color: var(--text-muted);
}
.ma-level select {
  width: auto; font: inherit; font-size: 12.5px; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text);
  padding: 5px 8px; border: 1px solid var(--border); border-radius: 7px; background: var(--bg-surface);
}
.ma-itemgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 2px 12px;
}
.ma-item {
  display: flex; align-items: center; gap: 7px; padding: 3px 0; margin: 0;
  font-size: 12.5px; color: var(--text); cursor: pointer;
}
.ma-item input { margin: 0; flex: 0 0 auto; }
/* A tick box that CANNOT do anything is visibly off and carries the level it would need.
   Hiding it would be worse — a reader would not know the part exists at all. */
.ma-item.is-off { color: var(--text-muted); cursor: not-allowed; }
.ma-item__why {
  margin-left: auto; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; color: var(--text-muted);
}
.ma-editor__act { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 14px; }
.ma-fields { margin-top: 22px; }
/* The parts picker's own header: a count on the left, the two shortcuts on the right. Quiet on
   purpose — it is a tool over a list, not a second toolbar. */
.ma-pickhead { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 6px; }
.ma-pickhead__act { display: inline-flex; gap: 10px; }
.ma-pickhead__act button[disabled] { opacity: .45; cursor: default; }
/* The parts a higher level would unlock. Folded rather than greyed inline: they are the answer to
   "where is Journal", and inline they were ten more dead boxes in a grid of twenty-five. */
.ma-locked { margin-top: 10px; }
.ma-locked > summary { cursor: pointer; color: var(--text-muted); font-size: 12.5px; }
.ma-locked > summary:hover { color: var(--text); }
/* Said once, where a whole grid of disabled boxes used to be. */
.ma-note { margin: 0 0 8px; font-size: 12.5px; color: var(--text-muted); }
/* The named fields under gate 6 in the Why? chain. It reuses `.ma-fieldrow` and `.ma-fp` rather
   than growing a parallel set of chip styles: it is the SAME fact — this field, this restriction —
   read for one person instead of for the module, and two skins would be two answers that have to
   be kept looking alike by hand. Indented and un-ruled so it reads as detail belonging to the step
   above it, not as a list of its own. */
.ma-explain__fields { margin: 6px 0 2px; }
.ma-explain__fields .ma-fieldrow { border-top: 0; padding: 3px 0; gap: 10px; }
.ma-fieldrow {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  padding: 7px 0; border-top: 1px solid var(--border-muted, var(--border));
  font-size: 12.5px;
}

/* ── A FIELD YOU MAY NOT SEE (field-level security, 2026-09-16) ────────────────
   It must not look like an empty field, which is exactly what it looked like before: the server
   nulled the value and the client rendered a blank, so a boundary read as missing data.
   Muted and quiet on purpose — this is a statement of fact, not a warning. A red or bold
   treatment would make every restricted field shout on a page that may carry several. */
.orbit-field-rest { display: inline-flex; align-items: center; gap: 5px;
  color: var(--text-faint); font-style: italic; cursor: help; }
.orbit-field-rest > span { font-style: normal; }
/* The read-only lock rides the LABEL, because that field still shows its value. */
.orbit-field-lock { margin-left: 5px; font-size: .78em; opacity: .65; cursor: help; }
/* A restricted field is never collapsed behind "Show N empty" — it HAS a value and the reader
   is simply not its audience. The class is what says so if `filled` is ever reworked. */
.orbit-field.is-restricted { opacity: 1; }

/* ── Module access ▸ Fields (gate 6, authored here) ────────────────────────────
   A restriction is THREE facts — the field, what they may do, and who it applies to — so it is
   one chip carrying all three rather than `field: access` with the holder left out. */
.ma-fields__head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 8px; }
.ma-fields__head p { margin: 0; flex: 1 1 auto; }
.ma-fields__head .ghost-button { flex: 0 0 auto; margin-top: 0; }
.ma-fp { display: inline-flex; align-items: center; gap: 6px; margin: 0 4px 3px 0;
  padding: 2px 4px 2px 8px; border-radius: 999px; background: var(--bg-muted);
  font-size: 11.5px; color: var(--text); }
.ma-fp__f { font-weight: 600; }
.ma-fp__a { color: var(--text-muted); }
.ma-fp__w { color: var(--text-subtle); border-left: 1px solid var(--border); padding-left: 6px; }
/* The remove control is inside the chip it removes — a row of chips with a shared Delete would
   make the reader aim. It keeps a real hit target despite the chip's size. */
.ma-fp__x { display: inline-grid; place-items: center; width: 18px; height: 18px;
  border: 0; border-radius: 999px; background: none; color: var(--text-muted);
  font: inherit; font-size: 13px; line-height: 1; cursor: pointer; }
.ma-fp__x:hover { background: var(--danger-soft, rgba(220,38,38,.12)); color: var(--danger, #b91c1c); }

/* ── Module access ▸ Why? (the six gates, run for one person) ──────────────────
   A numbered chain, because the ORDER is the product rule and the reader has to see that a
   refusal stops it. The refusing step is the only coloured thing on the panel. */
.ma-explain__pick { display: flex; gap: 8px; margin: 0 0 10px; }
.ma-explain__pick .ghost-button { margin-top: 0; }
.ma-explain__head { display: flex; align-items: center; gap: 10px; margin: 0 0 8px;
  font-size: 13px; font-weight: 600; }
.ma-verdict { padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: 700;
  letter-spacing: .02em; }
.ma-verdict.is-yes { background: var(--ok-soft, rgba(22,163,74,.12)); color: var(--ok, #15803d); }
.ma-verdict.is-no { background: var(--danger-soft, rgba(220,38,38,.12)); color: var(--danger, #b91c1c); }
.ma-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.ma-step { display: flex; align-items: flex-start; gap: 9px; padding: 5px 0; font-size: 12.5px; }
.ma-step__n { flex: 0 0 auto; display: inline-grid; place-items: center; width: 17px; height: 17px;
  margin-top: 1px; border-radius: 999px; background: var(--bg-muted); color: var(--text-subtle);
  font-size: 10px; font-weight: 700; }
.ma-step.is-no .ma-step__n { background: var(--danger, #b91c1c); color: var(--primary-ink, #fff); }
.ma-step__b { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ma-step__t { color: var(--text); }
.ma-step.is-no .ma-step__t { font-weight: 600; }
/* The DETAIL is the actionable half — "turn it on at Business & Modules" — so it is muted but
   never hidden behind a hover: the whole point of the panel is that the answer is readable. */
.ma-step__d { color: var(--text-muted); font-size: 11.5px; }

/* ── Module access ▸ Screens (gate 2, read-only) ───────────────────────────────
   A checklist the org authored elsewhere, so it reads as a STATEMENT rather than a form: no
   tick boxes, no controls, a mark and a label. A hidden screen is shown rather than omitted —
   the whole value is seeing what the organisation switched off. */
.ma-screens { font-size: 12.5px; }
.ma-scrgrp { margin-bottom: 10px; }
.ma-scrlist { list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 1px 12px; }
.ma-scr { display: flex; align-items: center; gap: 7px; padding: 2px 0; color: var(--text); }
.ma-scr__m { flex: 0 0 auto; width: 12px; text-align: center; color: var(--ok, #15803d); font-weight: 700; }
.ma-scr__l { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ma-scr.is-off { color: var(--text-muted); }
.ma-scr.is-off .ma-scr__m { color: var(--text-faint); }
.ma-scr__w { margin-left: auto; font-size: 10px; font-weight: 600; text-transform: uppercase;
  letter-spacing: .03em; color: var(--text-faint); white-space: nowrap; }
/* A link that lives inside a sentence — it must not be a button-shaped thing mid-paragraph. */
.iam-linkbtn { border: 0; background: none; padding: 0; font: inherit; color: var(--primary);
  text-decoration: underline; cursor: pointer; }

/* ── App mark in the header (→ Central Home) ───────────────────────────────────
   The other half of the org brand on the left: that one opens the ORG's default
   dashboard, this one opens the app's own home. It lives in the right cluster because
   `.tb-lead` is relocated over the rails and collapses to zero width when a second rail
   is on, so a mark placed there would vanish exactly when the rails appear. */
/* The click target over the painted corner mark (see index.html). Fixed to the same box the
   background-image occupies: the rail-1 column width by the topbar height. Transparent — the
   PAINTED mark is the visual; this only makes it behave like a logo. z-index 31 sits above the
   topbar (30) and the relocated rail cluster (29). */
/* THE BRAND LOCKUP — mark then wordmark, top-left (2026-09-14, user direction).
   Sized to its CONTENT (`--brand-w`), not to a rail. It was `var(--rail1-w)` — 58px, the width of
   the first rail, which was the right answer while a 58px icon column ran below it and is an
   arbitrary number now that the navigation is one 248px column. The band pays for exactly this
   width as `padding-left`, so the hamburger starts on the lockup's right edge; the two are the
   same token for that reason, and separating them is how a control ends up underneath the logo —
   rendering, looking live, and unclickable. */
.tb-corner-home {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--brand-w);
  height: var(--topbar-h, 56px);
  z-index: 31;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}
.tb-corner-home:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 8px; }
/* The mark is a flat glyph, so it needs no hover chip of its own — the shared
   .topbar-iconbtn hover already supplies one. */

/* ── Settings ▸ Setup ▸ Currency: the traded-currency picker ───────────────────
   A tick grid rather than a multi-select: 150-odd codes in a multi-select is a scroll
   race, and the whole point of the setting is to make choosing a currency quicker. */
.cur-picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 4px 10px; }
.cur-pick { display: flex; align-items: center; gap: 8px; padding: 5px 8px; border-radius: 7px;
  font-size: .84rem; cursor: pointer; border: 1px solid transparent; }
.cur-pick:hover { background: var(--bg-hover); }
.cur-pick.is-on { border-color: var(--border-muted); background: var(--bg-app, var(--bg-muted)); }
.cur-pick input { flex: none; accent-color: var(--primary, #2563eb); }
.cur-pick__code { font-weight: 700; font-variant-numeric: tabular-nums; }
.cur-pick__sym { color: var(--text-subtle); min-width: 1.6em; }
.cur-pick__name { color: var(--text-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cur-pick__lock { margin-left: auto; font-size: .68rem; color: var(--text-faint, var(--text-subtle));
  border: 1px solid var(--border-muted); border-radius: 999px; padding: 0 .45rem; }

/* ── Settings ▸ Setup ▸ Date, time & timezone ─────────────────────────────────── */
.cur-row { display: grid; grid-template-columns: 12rem minmax(0, 22rem) 1fr; align-items: center;
  gap: .5rem .8rem; margin: .4rem 0; font-size: .86rem; }
.cur-row__label { color: var(--text-subtle); }
.cur-row__help { font-size: .76rem; color: var(--text-faint, var(--text-subtle)); }
.cur-row select { width: 100%; }
.cur-preview { margin-top: .7rem; padding: .55rem .7rem; border: 1px solid var(--border-muted);
  border-radius: 8px; background: var(--bg-app, var(--bg-muted)); }
.cur-actions { display: flex; align-items: center; gap: .7rem; margin-top: .6rem; }
@media (max-width: 720px) { .cur-row { grid-template-columns: 1fr; } }

/* ── Org Admin ▸ Platform & data ▸ Data import (cutover console) ──────────────────
   Two columns: the cutover ORDER on the left (the spine — a migration is a sequence, and the
   page has to say so before it offers an upload box) and the active step on the right. The
   checklist stays visible while you work a step, because "what have I already loaded" is the
   question you ask most during a cutover. */
.imp-bar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: .6rem 0 .9rem; }
.imp-spacer { flex: 1; }
.imp-lbl { display: flex; flex-direction: column; gap: .2rem; font-size: .78rem; color: var(--text-subtle); }
.imp-lbl select, .imp-lbl input { font-size: .86rem; }
.imp-chip { font-size: .72rem; color: var(--text-subtle); border: 1px solid var(--border-muted);
  border-radius: 999px; padding: .1rem .5rem; white-space: nowrap; }
.imp-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 1.1rem; align-items: start; }
@media (max-width: 980px) { .imp-grid { grid-template-columns: 1fr; } }
.imp-h { font-size: .98rem; margin: .2rem 0 .3rem; }
.imp-h4 { font-size: .84rem; margin: .9rem 0 .3rem; color: var(--text-subtle); }
.imp-desc { font-size: .78rem; color: var(--text-faint, var(--text-subtle)); margin: .15rem 0; }
/* Same bordered card + muted header band as every other list table in the app. */
.imp-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .84rem;
  background: var(--bg-surface); border: 1px solid var(--border-muted); border-radius: 10px; overflow: hidden; }
.imp-table th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: var(--text-muted); background: var(--bg-surface);
  padding: .5rem .6rem; border-bottom: 1px solid var(--border-muted); }
.imp-table td { padding: .5rem .6rem; border-bottom: 1px solid var(--border-muted); vertical-align: top; }
.imp-table tr:last-child td { border-bottom: 0; }
.imp-step { cursor: pointer; }
.imp-step:hover { background: var(--bg-hover); }
.imp-step.is-on { background: color-mix(in srgb, var(--primary, #2563eb) 8%, var(--bg-surface)); }
.imp-num { color: var(--text-faint, var(--text-subtle)); font-variant-numeric: tabular-nums; width: 2rem; }
.imp-mod { font-size: .68rem; color: var(--text-faint, var(--text-subtle)); margin-left: .35rem; }
/* "posts" marks a step that writes to the ledger — re-running it is not a free retry. */
.imp-warn { font-size: .66rem; margin-left: .35rem; padding: 0 .35rem; border-radius: 999px;
  color: #92400e; background: #fef3c7; }
.imp-st { white-space: nowrap; font-size: .78rem; }
.imp-done { color: #15803d; }
.imp-bad, .imp-count.is-bad { color: #b91c1c; }
.imp-todo { color: var(--text-faint, var(--text-subtle)); }
.imp-req { font-size: .66rem; color: #b91c1c; margin-left: .35rem; }
.imp-drop { display: flex; align-items: flex-end; gap: .7rem; flex-wrap: wrap; margin: .7rem 0;
  padding: .7rem; border: 1px dashed var(--border); border-radius: 10px; }
.imp-actions { display: flex; gap: .5rem; margin: .8rem 0 .4rem; }
.imp-counts { display: flex; gap: .8rem; flex-wrap: wrap; align-items: center; margin: .6rem 0 .4rem;
  font-size: .82rem; }
.imp-count b { font-variant-numeric: tabular-nums; }
.imp-errs { margin-top: .4rem; }
.imp-recon { margin-top: 1.4rem; padding-top: .9rem; border-top: 1px solid var(--border-muted); }
.imp-recon-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: .6rem; margin: .6rem 0 .7rem; }
.imp-blockers { margin: .5rem 0; padding-left: 1.1rem; font-size: .8rem; color: #b91c1c; }
@media (prefers-color-scheme: dark) {
  .imp-warn { color: #fcd34d; background: color-mix(in srgb, #f59e0b 22%, transparent); }
  .imp-done { color: #4ade80; }
  .imp-bad, .imp-count.is-bad, .imp-blockers, .imp-req { color: #f87171; }
}

/* ── Artha ▸ Setup ▸ Customers & Vendors ───────────────────────────────────────────
   Three settings on one page: the vocabulary, the numbering and the field catalogue. The field
   table is the substance and gets the room — it is grouped exactly the way the form is grouped,
   so what an admin arranges here reads as the form they are arranging. */
.ps-page { display: flex; flex-direction: column; gap: 1.4rem; max-width: 62rem; }
.ps-block { border: 1px solid var(--border-muted); border-radius: 10px; padding: .9rem 1rem;
  background: var(--bg-surface); }
.ps-h { font-size: .95rem; margin: 0 0 .25rem; }
.ps-desc { font-size: .78rem; color: var(--text-faint, var(--text-subtle)); margin: .2rem 0 .6rem; }
.ps-lbl { display: flex; flex-direction: column; gap: .2rem; font-size: .76rem;
  color: var(--text-subtle); }
.ps-check { display: flex; align-items: center; gap: .4rem; font-size: .84rem; }
.ps-vocab { padding: .6rem 0; border-top: 1px solid var(--border-muted); }
.ps-vocab:first-of-type { border-top: 0; }
.ps-vocab__head { font-size: .8rem; font-weight: 600; margin-bottom: .35rem; }
.ps-vocab__inputs { display: flex; gap: .8rem; flex-wrap: wrap; }
.ps-vocab__inputs input { min-width: 12rem; }
/* Presets are the fast path — a dozen words covering most of the world's bookkeeping
   vocabulary. They are CHOICES, not actions, so they read as chips and the one in use is filled
   and ticked: "what are we called right now" should be answerable by looking. */
.ps-presets { display: flex; gap: .4rem; flex-wrap: wrap; margin: .45rem 0 .6rem; }
.ps-chip { display: inline-flex; align-items: baseline; gap: .35rem; cursor: pointer;
  font: inherit; font-size: .82rem; line-height: 1.2; padding: .38rem .7rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg-surface); color: var(--text);
  transition: background .12s ease, border-color .12s ease, transform .06s ease; }
.ps-chip:hover { background: var(--bg-hover); border-color: var(--border-strong); }
.ps-chip:active { transform: translateY(1px); }
.ps-chip.is-on { background: var(--primary, #2563eb); border-color: var(--primary, #2563eb);
  color: var(--primary-ink, #fff); font-weight: 600; }
.ps-chip__tick { font-size: .8em; }
.ps-chip__note { font-size: .68rem; opacity: .62; white-space: nowrap; }
.ps-chip.is-on .ps-chip__note { opacity: .82; }
.ps-egs { font-size: .74rem; color: var(--text-faint, var(--text-subtle)); align-self: flex-end;
  padding-bottom: .35rem; font-style: italic; }
/* Buttons that COMMIT something get real weight; the quiet one beside them does not compete. */
.ps-actions { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; margin-top: .7rem; }
.ps-btn { font: inherit; font-size: .85rem; font-weight: 600; line-height: 1.2; cursor: pointer;
  padding: .5rem 1.1rem; border-radius: 8px; border: 1px solid var(--border);
  background: var(--bg-surface); color: var(--text);
  transition: background .12s ease, box-shadow .12s ease, transform .06s ease; }
.ps-btn:hover { background: var(--bg-hover); }
.ps-btn:active { transform: translateY(1px); }
.ps-btn:focus-visible { outline: 2px solid var(--primary, #2563eb); outline-offset: 2px; }
.ps-btn--primary { background: var(--primary, #2563eb); border-color: var(--primary, #2563eb);
  color: var(--primary-ink, #fff); box-shadow: 0 1px 2px rgba(16,24,40,.14); }
.ps-btn--primary:hover { background: color-mix(in srgb, var(--primary, #2563eb) 88%, #000); }
.ps-btn--quiet { color: var(--text-subtle); border-style: dashed; font-weight: 500; }
.ps-btn--quiet:hover { color: var(--text); }
/* "Unsaved changes" earns its place: the wording and the numbering are edit-then-save, unlike
   the field toggles which save on the spot, and the two must not look the same. */
.ps-dirty { font-size: .76rem; font-weight: 600; color: #92400e; background: #fef3c7;
  border-radius: 999px; padding: .15rem .6rem; }
.ps-tab { font: inherit; font-size: .82rem; font-weight: 600; cursor: pointer; padding: .35rem .9rem;
  border-radius: 8px; border: 1px solid var(--border); background: var(--bg-surface);
  color: var(--text-subtle); }
.ps-tab:hover { background: var(--bg-hover); color: var(--text); }
.ps-tab.is-on { background: var(--primary, #2563eb); border-color: var(--primary, #2563eb);
  color: var(--primary-ink, #fff); }
.ps-num { display: flex; gap: .8rem; align-items: flex-end; flex-wrap: wrap; padding: .5rem 0;
  border-top: 1px solid var(--border-muted); }
.ps-num:first-of-type { border-top: 0; }
.ps-num input[type="text"] { width: 7rem; }
.ps-num input[type="number"] { width: 6rem; }
.ps-preview { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .82rem;
  color: var(--text-subtle); border: 1px dashed var(--border); border-radius: 6px;
  padding: .2rem .5rem; }
.ps-fields-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  flex-wrap: wrap; }
.ps-tabs { display: flex; gap: .35rem; }
.ps-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .84rem;
  background: var(--bg-surface); border: 1px solid var(--border-muted); border-radius: 10px;
  overflow: hidden; margin: .5rem 0 .7rem; }
.ps-table th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: var(--text-muted); background: var(--bg-surface);
  padding: .5rem .6rem; border-bottom: 1px solid var(--border-muted); }
.ps-table td { padding: .45rem .6rem; border-bottom: 1px solid var(--border-muted);
  vertical-align: top; }
.ps-table tbody:last-child tr:last-child td { border-bottom: 0; }
.ps-group__head td { font-size: 11px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: var(--text-muted); background: var(--bg-app, var(--bg-muted)); }
/* A switched-off field stays legible rather than disappearing — you are choosing from a list,
   and a row you cannot read is a row you cannot choose. */
.ps-table tr.is-off td { opacity: .55; }
.ps-mid { text-align: center; }
.ps-type { color: var(--text-faint, var(--text-subtle)); font-size: .76rem; }
.ps-label-input { width: min(18rem, 100%); font-size: .84rem; }
.ps-flag { font-size: .66rem; margin-left: .35rem; padding: 0 .35rem; border-radius: 999px;
  border: 1px solid var(--border-muted); color: var(--text-subtle); white-space: nowrap; }
.ps-flag--geo { border-color: transparent; background: color-mix(in srgb, var(--primary, #2563eb) 14%, transparent); }
@media (prefers-color-scheme: dark) {
  .ps-dirty { color: #fcd34d; background: color-mix(in srgb, #f59e0b 22%, transparent); }
}
:root[data-theme="dark"] .ps-dirty { color: #fcd34d; background: color-mix(in srgb, #f59e0b 22%, transparent); }
:root[data-theme="light"] .ps-dirty { color: #92400e; background: #fef3c7; }

/* ── Artha ▸ item record ───────────────────────────────────────────────────────────
   The page that makes "shown, not stored" real. Two sections earn bespoke treatment: POSTING,
   which has to say where each account came from (an inherited account rendered as a bare name is
   indistinguishable from one someone chose deliberately), and the MIRROR, which must read as
   another module's data — visibly not editable here. */
.artha-item-head { display: flex; align-items: flex-start; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: .8rem; }
.artha-item-id h2 { margin: 0 0 .3rem; font-size: 1.15rem; }
.artha-item-codes { display: flex; gap: .35rem; flex-wrap: wrap; }
.art-chip { font-size: .72rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  border: 1px solid var(--border-muted); border-radius: 999px; padding: .1rem .55rem;
  color: var(--text-subtle); }
.art-chip--off { color: #b91c1c; border-color: currentColor; }
/* A chip that GOES somewhere — the catalogue link on an item, which says the name and SKU
   above it are the catalogue's and offers the way to change them (2026-08-29). A button, not
   a span, because it is a control: `art-chip` alone would look identical and do nothing. */
.art-chip--link { color: var(--primary); border-color: currentColor; cursor: pointer;
  background: none; font: inherit; font-size: .72rem; }
.art-chip--link:hover { background: var(--bg-hover); }
.art-tags { display: inline-flex; gap: .3rem; flex-wrap: wrap; }
.art-tag { font-size: .72rem; border-radius: 999px; padding: .05rem .5rem;
  background: var(--bg-app, var(--bg-muted)); color: var(--text-subtle); }
.art-hint { font-size: .76rem; color: var(--text-faint, var(--text-subtle)); margin: .5rem 0 0; }
.art-muted { color: var(--text-faint, var(--text-subtle)); }
/* Posting */
.art-posting-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .84rem; }
.art-posting-table th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: .03em;
  text-transform: uppercase; color: var(--text-muted); padding: .4rem .6rem;
  border-bottom: 1px solid var(--border-muted); }
.art-posting-table td { padding: .45rem .6rem; border-bottom: 1px solid var(--border-muted); }
.art-posting-table tr:last-child td { border-bottom: 0; }
/* An account set HERE reads differently from one inherited: the first is a decision on this
   record, the second is a consequence of the class, and confusing them is how someone "fixes" a
   class-wide default by editing one item. */
.art-src { font-size: .72rem; color: var(--text-subtle); border: 1px dashed var(--border-muted);
  border-radius: 999px; padding: .08rem .5rem; white-space: nowrap; }
.art-src--own { border-style: solid; border-color: transparent;
  background: color-mix(in srgb, var(--primary, #2563eb) 14%, transparent); color: var(--text); }
.art-src--none { color: var(--text-faint, var(--text-subtle)); border-style: dotted; }
/* The mirror — another module's data, and it should look borrowed. */
.art-mirror .orbit-dg-body { background: var(--bg-app, var(--bg-muted)); border-radius: 8px;
  padding: .7rem .8rem; }
.art-mirror-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .5rem .9rem; }
.art-mirror-f { display: flex; flex-direction: column; gap: .1rem; }
.art-mirror-l { font-size: .72rem; color: var(--text-faint, var(--text-subtle)); }
.art-mirror-v { font-size: .86rem; }
/* A list row's name is the way into the record; an overflow menu as the only route hides it. */
.artha-linkbtn { background: none; border: 0; padding: 0; font: inherit; color: var(--text);
  text-align: left; cursor: pointer; }
.artha-linkbtn:hover { color: var(--primary, #2563eb); text-decoration: underline; }

/* ── "+ From catalogue" — the product picker, in BOTH modules ────────────────
   Artha picks Pimly products to give them a POSTING profile; Inventra picks them to give
   them a LOGISTICS profile (2026-09-05). Same shape, one block: a scrolling list of choices
   in a drawer, so the list owns the overflow and the drawer's own actions stay put at the
   foot. The decisions behind it are shared too — `ui/catalogPick.js`.
   Selector lists are written out LONGHAND rather than with `:is()`, which WebKit prices at
   specificity zero (see the rail-ink note in this file). */
.artha-cp, .inv-cp { display: flex; flex-direction: column; gap: 10px; }
.artha-cp input[type="search"], .inv-cp input[type="search"] { width: 100%; }
.artha-cp__bar, .inv-cp__bar { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: .82rem; color: var(--text-muted); }
.artha-cp__all, .inv-cp__all { display: flex; align-items: center; gap: 6px; }
.artha-cp__list, .inv-cp__list { display: flex; flex-direction: column; max-height: 48vh; overflow-y: auto;
  border: 1px solid var(--border-muted, var(--border)); border-radius: 8px; }
.artha-cp__row, .inv-cp__row { display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border-bottom: 1px solid var(--border-muted, var(--border)); cursor: pointer; }
.artha-cp__row:last-child, .inv-cp__row:last-child { border-bottom: 0; }
.artha-cp__row:hover, .inv-cp__row:hover { background: var(--bg-hover); }
/* A product that already HAS a profile is shown and not selectable — hiding it reads as the
   catalogue being incomplete to somebody hunting for it. */
.artha-cp__row.is-done, .inv-cp__row.is-done { cursor: default; color: var(--text-muted); }
.artha-cp__row.is-done:hover, .inv-cp__row.is-done:hover { background: transparent; }
.artha-cp__tick, .inv-cp__tick { width: 13px; text-align: center; color: var(--success, #16a34a); }
.artha-cp__main, .inv-cp__main { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.artha-cp__name, .inv-cp__name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.artha-cp__sub, .inv-cp__sub { font-size: .78rem; color: var(--text-muted); }
.artha-cp__tag, .inv-cp__tag { font-size: .74rem; color: var(--text-muted); white-space: nowrap; }

/* ── PUBLIC SITE PAGES: header mega-menu + every standalone marketing page ─────
   The signed-out site is no longer one page. Every header destination is a real
   URL with its own tab — /why-smaiviz, /platform, /pricing and /solutions (whose
   mega-menu opens a two-column panel, By role | By vertical, over 20 pages at
   /solutions/<slug>). All four tab roots carry `.smz-sitepage` and share the
   `.sol-*` vocabulary below, and all four are driven by ONE controller
   (features/sitepages/controller.js) rather than a fourth copy of the
   Legal/Blog standalone trick.

   Signed-out, the page is slotted INTO
   #home-landing between the sticky nav and the footer — so it inherits the
   landing's light palette. The `--sol-*` fallbacks below exist because the same
   tab can also be opened inside the (themed) console, where the landing's
   `--home-*` variables are not in scope.

   Selector lists are written out rather than using `:is()` — WebKit ships
   `:is()` with specificity ZERO, which is how earlier shared treatments in this
   file silently lost the cascade. */

/* ---- Header mega-menu ---- */
.home-nav-menu {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.home-nav-menu-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  background: none;
  border: 0;
  color: #475569;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: pointer;
}

.home-nav-menu-btn:hover,
.home-nav-menu.is-open .home-nav-menu-btn {
  color: #0f172a;
}

.home-nav-menu-btn:focus-visible {
  outline: 2px solid var(--home-primary, #1d4ed8);
  outline-offset: 3px;
  border-radius: 4px;
}

.home-nav-caret {
  transition: transform 0.15s ease;
}

.home-nav-menu.is-open .home-nav-caret {
  transform: rotate(180deg);
}

.home-mega {
  position: absolute;
  top: calc(100% + 14px);
  left: -16px;
  z-index: 40;
  width: min(760px, calc(100vw - 32px));
  /* The panel hangs off a STICKY header inside `.home-landing`, which is the scroll
     container. So anything that overflows the viewport is not merely below the fold —
     it is unreachable: scrolling the page moves the content and leaves the header, and
     the panel with it, exactly where it was. Two columns of eleven already overflow a
     laptop viewport once browser chrome is counted, and every entry added makes it
     worse. Bound it to the viewport and let the panel itself scroll.
     `overscroll-behavior` keeps that scroll from chaining into the page behind it, so
     reaching the last vertical doesn't also scroll the page out from under the menu. */
  max-height: calc(100vh - 92px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 20px 20px 8px;
  background: #ffffff;
  border: 1px solid rgba(15, 23, 42, 0.10);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(15, 23, 42, 0.14);
}

.home-mega[hidden] {
  display: none;
}

.home-mega-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 22px;
}

.home-mega-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.home-mega-title {
  padding: 0 10px 8px;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--home-primary, #1d4ed8);
}

.home-mega-link {
  display: block;
  padding: 7px 10px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
}

.home-mega-link:hover,
.home-mega-link:focus-visible {
  background: rgba(37, 99, 235, 0.07);
  outline: none;
}

.home-mega-link strong {
  display: block;
  font-size: 0.88rem;
  font-weight: 600;
  color: #0f172a;
}

.home-mega-link span {
  display: block;
  margin-top: 1px;
  font-size: 0.78rem;
  line-height: 1.35;
  color: #64748b;
}

.home-mega-foot {
  margin-top: 6px;
  padding: 10px 10px 6px;
  border-top: 1px solid rgba(15, 23, 42, 0.08);
}

.home-mega-all {
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--home-primary, #1d4ed8);
  text-decoration: none;
}

.home-mega-all:hover {
  text-decoration: underline;
}

/* ---- Standalone signed-out site page ----
   Unlike the Legal Center and the Blog, the marketing nav links and the footer STAY:
   this IS a marketing page and both are part of it. Only the landing's own body is
   hidden, and the page flows between the (non-sticky) header and the footer. */
.home-landing.is-sitepage .home-main,
.home-landing.is-sitepage .docs-chat-launcher { display: none; }

body.public-sitepage { overflow-x: hidden; overflow-y: auto; }
body.public-sitepage #home-landing { min-height: 100vh; }
body.public-sitepage .home-nav { position: sticky; }
body.public-sitepage .smz-sitepage.tab-content.active {
  padding: 0;
  min-height: calc(100vh - 60px);
  height: auto;
  overflow: visible;
}

/* ---- The pages themselves ---- */
.smz-sitepage {
  --sol-ink: #0f172a;
  --sol-body: #45565a;
  --sol-muted: #64748b;
  --sol-line: rgba(15, 23, 42, 0.10);
  --sol-card: #ffffff;
  --sol-tint: rgba(37, 99, 235, 0.045);
  --sol-brand: var(--home-primary, #1d4ed8);
  color: var(--sol-body);
  /* These are marketing pages: a self-contained LIGHT surface, like the landing.
     Signed-out they sit inside #home-landing and inherit it anyway; signed-in the
     same tab can open inside the themed console, where half-inheriting a dark theme
     would give white cards on a dark canvas. Pin it instead of half-theming it. */
  background: #f5f8f7;
  color-scheme: light;
}

.smz-sitepage .sol-index,
.smz-sitepage .sol-page {
  max-width: 1080px;
  margin: 0 auto;
  padding: 40px 28px 72px;
}

.smz-sitepage .sol-page[hidden] { display: none; }

.smz-sitepage .sol-eyebrow {
  display: inline-block;
  margin-bottom: 12px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sol-brand);
}

.smz-sitepage h1 {
  margin: 0 0 14px;
  font-size: clamp(1.85rem, 4vw, 2.7rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--sol-ink);
}

.smz-sitepage .sol-lede,
.smz-sitepage .sol-index-hero p {
  max-width: 760px;
  margin: 0;
  font-size: 1.03rem;
  line-height: 1.65;
  color: var(--sol-body);
}

/* Breadcrumb */
.smz-sitepage .sol-crumb {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 22px;
  font-size: 0.8rem;
  color: var(--sol-muted);
}

.smz-sitepage .sol-crumb a {
  color: var(--sol-brand);
  text-decoration: none;
  font-weight: 600;
}

.smz-sitepage .sol-crumb a:hover { text-decoration: underline; }

/* Hero */
.smz-sitepage .sol-hero { padding-bottom: 8px; }

.smz-sitepage .sol-hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 26px;
}

.smz-sitepage .sol-hero-note {
  margin: 14px 0 0;
  font-size: 0.82rem;
  color: var(--sol-muted);
}

/* Section shell */
.smz-sitepage .sol-sec {
  margin-top: 46px;
  padding-top: 38px;
  border-top: 1px solid var(--sol-line);
}

.smz-sitepage .sol-sec--tint {
  padding: 34px 26px 38px;
  border: 1px solid var(--sol-line);
  border-radius: 16px;
  background: var(--sol-tint);
}

.smz-sitepage .sol-h2 {
  margin: 0 0 8px;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--sol-ink);
}

.smz-sitepage .sol-sec-sub {
  max-width: 720px;
  margin: 0 0 22px;
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--sol-muted);
}

/* Pain cards — the emotional hook, so they read as quotes not as features. */
.smz-sitepage .sol-pains {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(268px, 1fr));
  gap: 14px;
  margin-top: 22px;
}

.smz-sitepage .sol-pain {
  padding: 18px 20px;
  border: 1px solid var(--sol-line);
  border-left: 3px solid rgba(37, 99, 235, 0.45);
  border-radius: 12px;
  background: var(--sol-card);
}

.smz-sitepage .sol-pain h3 {
  margin: 0 0 7px;
  font-size: 0.98rem;
  line-height: 1.35;
  color: var(--sol-ink);
}

.smz-sitepage .sol-pain p {
  margin: 0;
  font-size: 0.89rem;
  line-height: 1.55;
  color: var(--sol-body);
}

/* Today → Instead */
.smz-sitepage .sol-shift {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 20px;
}

.smz-sitepage .sol-shift-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.smz-sitepage .sol-shift-from,
.smz-sitepage .sol-shift-to {
  margin: 0;
  padding: 14px 16px;
  border: 1px solid var(--sol-line);
  border-radius: 12px;
  background: var(--sol-card);
  font-size: 0.9rem;
  line-height: 1.5;
}

.smz-sitepage .sol-shift-from { color: var(--sol-muted); }

.smz-sitepage .sol-shift-to {
  border-color: rgba(14, 116, 144, 0.35);
  color: var(--sol-ink);
}

.smz-sitepage .sol-shift-from > span,
.smz-sitepage .sol-shift-to > span {
  display: block;
  margin-bottom: 4px;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.smz-sitepage .sol-shift-from > span { color: #94a3b8; }
.smz-sitepage .sol-shift-to > span { color: var(--home-accent, #0e7490); }

/* Capability cards */
.smz-sitepage .sol-caps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(262px, 1fr));
  gap: 14px;
  margin-top: 22px;
}

.smz-sitepage .sol-cap {
  padding: 18px 20px;
  border: 1px solid var(--sol-line);
  border-radius: 12px;
  background: var(--sol-card);
  box-shadow: 0 3px 12px rgba(15, 23, 42, 0.05);
}

.smz-sitepage .sol-cap h3 {
  margin: 0 0 7px;
  font-size: 0.98rem;
  color: var(--sol-ink);
}

.smz-sitepage .sol-cap p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--sol-body);
}

/* Numbered flow */
.smz-sitepage .sol-flow {
  list-style: none;
  margin: 22px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  counter-reset: none;
}

.smz-sitepage .sol-flow > li {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 16px 18px;
  border: 1px solid var(--sol-line);
  border-radius: 12px;
  background: var(--sol-card);
}

.smz-sitepage .sol-flow-step {
  flex: 0 0 28px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--sol-brand);
  color: #fff;
  font-size: 0.82rem;
  font-weight: 700;
}

.smz-sitepage .sol-flow h3 {
  margin: 2px 0 5px;
  font-size: 0.96rem;
  color: var(--sol-ink);
}

.smz-sitepage .sol-flow p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--sol-body);
}

/* Trust strip */
.smz-sitepage .sol-trust {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(262px, 1fr));
  gap: 14px;
  margin-top: 22px;
}

.smz-sitepage .sol-trust-item {
  padding: 18px 20px;
  border: 1px solid rgba(14, 116, 144, 0.28);
  border-radius: 12px;
  background: rgba(14, 116, 144, 0.045);
}

.smz-sitepage .sol-trust-item h3 {
  margin: 0 0 6px;
  font-size: 0.95rem;
  color: var(--sol-ink);
}

.smz-sitepage .sol-trust-item p {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--sol-body);
}

/* FAQ */
.smz-sitepage .sol-faq {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 20px;
}

.smz-sitepage .sol-faq-item {
  border: 1px solid var(--sol-line);
  border-radius: 12px;
  background: var(--sol-card);
}

.smz-sitepage .sol-faq-item > summary {
  padding: 14px 18px;
  font-size: 0.94rem;
  font-weight: 600;
  color: var(--sol-ink);
  cursor: pointer;
  list-style: none;
}

.smz-sitepage .sol-faq-item > summary::-webkit-details-marker { display: none; }

.smz-sitepage .sol-faq-item > summary::after {
  content: '+';
  float: right;
  margin-left: 12px;
  color: var(--sol-brand);
  font-weight: 700;
}

.smz-sitepage .sol-faq-item[open] > summary::after { content: '\2212'; }

.smz-sitepage .sol-faq-item > p {
  margin: 0;
  padding: 0 18px 16px;
  font-size: 0.89rem;
  line-height: 1.6;
  color: var(--sol-body);
}

/* Final CTA + related */
.smz-sitepage .sol-final {
  margin-top: 46px;
  padding: 38px 28px;
  border-radius: 18px;
  background: linear-gradient(135deg, rgba(37, 99, 235, 0.09), rgba(14, 116, 144, 0.09));
  text-align: center;
}

.smz-sitepage .sol-final h2 {
  margin: 0 0 8px;
  font-size: clamp(1.3rem, 2.6vw, 1.75rem);
  color: var(--sol-ink);
}

.smz-sitepage .sol-final p {
  max-width: 620px;
  margin: 0 auto;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--sol-body);
}

.smz-sitepage .sol-final .sol-hero-cta { justify-content: center; }

.smz-sitepage .sol-related {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--sol-line);
}

.smz-sitepage .sol-related-title {
  margin-right: 4px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sol-muted);
}

.smz-sitepage .sol-related a {
  padding: 7px 14px;
  border: 1px solid var(--sol-line);
  border-radius: 999px;
  background: var(--sol-card);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--sol-brand);
  text-decoration: none;
}

.smz-sitepage .sol-related a:hover {
  border-color: var(--sol-brand);
  background: rgba(37, 99, 235, 0.07);
}

/* ---- The hub (/solutions) ---- */
.smz-sitepage .sol-index-hero { padding-bottom: 8px; }

.smz-sitepage .sol-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-top: 34px;
}

.smz-sitepage .sol-col {
  padding: 24px 22px 12px;
  border: 1px solid var(--sol-line);
  border-radius: 16px;
  background: var(--sol-card);
  box-shadow: 0 4px 16px rgba(15, 23, 42, 0.05);
}

.smz-sitepage .sol-col-title {
  margin: 0 0 4px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sol-brand);
}

.smz-sitepage .sol-col-sub {
  margin: 0 0 14px;
  font-size: 0.87rem;
  line-height: 1.5;
  color: var(--sol-muted);
}

.smz-sitepage .sol-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.smz-sitepage .sol-link {
  display: block;
  padding: 11px 12px;
  margin: 0 -12px;
  border-radius: 10px;
  text-decoration: none;
}

.smz-sitepage .sol-link:hover,
.smz-sitepage .sol-link:focus-visible {
  background: rgba(37, 99, 235, 0.07);
  outline: none;
}

.smz-sitepage .sol-link-title {
  display: block;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--sol-ink);
}

.smz-sitepage .sol-link-sub {
  display: block;
  margin-top: 2px;
  font-size: 0.83rem;
  line-height: 1.4;
  color: var(--sol-muted);
}

.smz-sitepage .sol-index-band {
  margin-top: 46px;
  padding-top: 38px;
  border-top: 1px solid var(--sol-line);
}

/* ---- Responsive ---- */
@media (max-width: 900px) {
  .smz-sitepage .sol-cols,
  .smz-sitepage .sol-shift-row { grid-template-columns: 1fr; }
  /* The panel is width-capped at `100vw - 32px`, so at 900px it is still ~730px wide —
     wide enough for two columns of ~340px. Collapsing to one here stacked all 21 entries
     into a single column twice the viewport's height, which is the shape that was
     unreachable. One column starts at 720px, where the panel also goes fixed + scrollable. */
  .home-mega { left: auto; right: -8px; }
}

@media (max-width: 720px) {
  /* The landing hides its in-page anchor links at this width, but Solutions is a real
     destination — keep the menu reachable rather than stranding it behind a link set
     that isn't shown. */
  .home-nav-links { display: flex; }
  .home-nav-links > a { display: none; }
  .home-nav-links .home-nav-menu { display: inline-flex; }
  .home-mega-cols { grid-template-columns: 1fr; }
  .home-mega {
    position: fixed;
    top: 62px;
    left: 12px;
    right: 12px;
    width: auto;
    max-height: calc(100vh - 84px);
    overflow-y: auto;
  }
  .smz-sitepage .sol-index,
  .smz-sitepage .sol-page { padding: 28px 18px 56px; }
  .smz-sitepage .sol-sec--tint { padding: 26px 18px 30px; }
  .smz-sitepage .sol-final { padding: 30px 20px; }
}

/* ── RECORD FIELD CATALOGUE (ui/recordFields.js) ──────────────────────────────
   The "fields collected" panel, shared by every module that adopts the field catalogue
   (platformkit/recordspec). Deliberately the same shape as Artha's Setup ▸ Customers & vendors
   field list — an admin who has configured one should recognise the other immediately — but
   themed rather than light-only, because unlike the marketing pages this lives inside the
   console and has to work in both themes.

   Selector lists are written out rather than using `:is()`: WebKit ships it with specificity
   ZERO, which is how earlier shared treatments in this file silently lost the cascade. */

.rf-block { display: block; }

.rf-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }

.rf-h {
  margin: 0 0 4px;
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--text);
}

.rf-desc {
  margin: 0 0 12px;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 70ch;
}

.rf-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border-muted, var(--border));
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-surface);
}

.rf-table thead th {
  padding: 9px 12px;
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-muted, var(--border));
}

.rf-group__head td {
  padding: 9px 12px 5px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-muted, var(--bg-surface));
  border-top: 1px solid var(--border-muted, var(--border));
}

.rf-row > td {
  padding: 8px 12px;
  font-size: 0.86rem;
  color: var(--text);
  border-top: 1px solid var(--border-muted, var(--border));
  vertical-align: top;
}

.rf-row:hover > td { background: var(--bg-hover, transparent); }
.rf-row { cursor: pointer; }

/* A field that is off stays legible — it is a state to read, not a row to lose. */
.rf-row.is-off > td { opacity: 0.55; }

.rf-c { width: 1%; white-space: nowrap; text-align: center; }

.rf-type {
  width: 1%;
  white-space: nowrap;
  font-size: 0.76rem;
  color: var(--text-muted);
}

.rf-label {
  width: min(22rem, 100%);
  padding: 4px 8px;
  border: 1px solid transparent;
  border-radius: 6px;
  background: transparent;
  font: inherit;
  font-size: 0.86rem;
  color: var(--text);
}

/* The rename box only looks like a box once you are in it — a table of thirty input borders
   reads as a form to fill in rather than a list to scan. */
.rf-label:hover { border-color: var(--border-muted, var(--border)); }
.rf-label:focus {
  border-color: var(--primary, #2563eb);
  background: var(--bg-surface);
  outline: none;
}

.rf-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--bg-muted, rgba(127, 127, 127, 0.12));
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--text-muted);
  vertical-align: middle;
}

.rf-tag--renamed { color: var(--primary, #2563eb); }
.rf-tag--dep { font-weight: 500; }

.rf-help {
  display: block;
  margin: 2px 0 0 8px;
  font-size: 0.76rem;
  line-height: 1.4;
  color: var(--text-muted);
}

.rf-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
}

.rf-actions .rf-desc { margin: 0; }

.rf-btn {
  padding: 6px 13px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font: inherit;
  font-size: 0.82rem;
  color: var(--text);
  cursor: pointer;
}

.rf-btn:hover { background: var(--bg-hover, var(--bg-muted)); }

@media (max-width: 720px) {
  .rf-help { display: none; }
  .rf-label { width: 100%; }
}

/* The record's own name, above its field list (ui/recordFields.js). Two short inputs rather than
   a form: renaming a record is a five-second decision, and a Save button would make it feel like
   a commitment. Saves on blur, like the field labels below it. */
.rf-vocab {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 12px;
  margin: 0 0 14px;
  padding: 12px 14px;
  border: 1px solid var(--border-muted, var(--border));
  border-radius: 10px;
  background: var(--bg-surface);
}

.rf-vocab label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-muted);
}

.rf-vocab input {
  width: 12rem;
  padding: 6px 9px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--bg-surface);
  font: inherit;
  font-size: 0.88rem;
  color: var(--text);
}

.rf-vocab input:focus {
  border-color: var(--primary, #2563eb);
  outline: none;
}

.rf-vocab .rf-tag { margin-left: 0; align-self: center; }

@media (max-width: 720px) {
  .rf-vocab input { width: 100%; }
  .rf-vocab label { flex: 1 1 100%; }
}

/* ── Custom fields Setup panel (ui/customFieldsSetup.js) ────────────────────
   Sibling to the .rf-* catalogue panel and deliberately the same surface: the two
   sit on one Setup screen, and giving them different chrome would read as two
   unrelated features rather than "our fields" and "your fields". */
.cfs-desc {
  margin: 0 0 12px;
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 70ch;
}

.cfs-bar {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 10px;
}

.cfs-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--border-muted, var(--border));
  border-radius: 10px;
  overflow: hidden;
  background: var(--bg-surface);
}

.cfs-table thead th {
  padding: 9px 12px;
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  background: var(--bg-surface);
  border-bottom: 1px solid var(--border-muted, var(--border));
}

.cfs-row > td {
  padding: 8px 12px;
  font-size: 0.86rem;
  color: var(--text);
  border-top: 1px solid var(--border-muted, var(--border));
  vertical-align: top;
}

.cfs-row:hover > td { background: var(--bg-hover, transparent); }

.cfs-name { font-weight: 600; }

.cfs-type { color: var(--text-muted); white-space: nowrap; }

.cfs-c { text-align: center; width: 88px; }

.cfs-actions { text-align: right; white-space: nowrap; }

.cfs-help {
  display: block;
  margin-top: 2px;
  font-size: 0.78rem;
  color: var(--text-muted);
}

.cfs-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  background: var(--bg-muted, var(--bg-hover, transparent));
}

.cfs-empty {
  margin: 0;
  padding: 18px 12px;
  font-size: 0.86rem;
  color: var(--text-muted);
  text-align: center;
  border: 1px dashed var(--border-muted, var(--border));
  border-radius: 10px;
}

/* ── Shop-floor station key ────────────────────────────────────────────────────
   The registration secret is shown ONCE and only its sha256 is stored, so there is
   nothing to read back later — not even for us. It therefore stays on screen until the
   admin dismisses it, rather than in a toast that vanishes while they are walking to the
   tablet with it. Styled as something to copy, not something to skim. */
#config-tab .sf-adm-key {
  border: 1px solid var(--warn, #b45309);
  border-left-width: 3px;
  border-radius: 10px;
  background: var(--bg-muted);
  padding: 12px 14px;
  margin-bottom: 12px;
}
#config-tab .sf-adm-key p { margin: 0 0 8px; font-size: .86rem; }
#config-tab .sf-adm-key code {
  display: block;
  padding: 10px 12px;
  margin-bottom: 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-surface);
  font-size: .9rem;
  word-break: break-all;
  /* Selectable on purpose — copying by hand must work when the clipboard API is blocked. */
  user-select: all;
}
#config-tab .sf-adm-key div { display: flex; gap: .4rem; }
#config-tab #shopfloor-stations tr.is-off td { opacity: .55; }

/* ── IN-MODULE MANUAL: the chapter index ────────────────────────────────────────────
   A module's user manual used to put one rail entry per chapter — sixteen in Inventra,
   fourteen in Artha — beside the working pages, which is a large part of why the modules
   read as "too many screens". The rail now carries ONE "Help" entry and the chapters are
   listed here, on the Help page itself. Chapters keep their section keys and stay
   reachable by URL and by search; this changed where they are LISTED, not whether they
   can be OPENED. Shared by every module (ui/manualIndex.js), so it is styled once. */
.smz-manual-index {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: .5rem;
  margin: 0 0 1.5rem;
}
.smz-manual-index__item {
  display: flex;
  align-items: center;
  gap: .55rem;
  padding: .65rem .75rem;
  text-align: left;
  font: inherit;
  font-size: .875rem;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 10px;
  cursor: pointer;
}
.smz-manual-index__item:hover { background: var(--bg-hover); }
.smz-manual-index__item .hicon-wrap { flex: 0 0 auto; opacity: .75; }
.smz-manual-back {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin: 0 0 1rem;
  padding: .35rem .6rem .35rem .45rem;
  font: inherit;
  font-size: .8125rem;
  color: var(--text-muted);
  background: transparent;
  border: 1px solid var(--border-muted);
  border-radius: 999px;
  cursor: pointer;
}
.smz-manual-back:hover { color: var(--text-primary); background: var(--bg-hover); }

/* ── CENTRAL: the quiet-day screen ─────────────────────────────────────────────────
   Central lands on the Today feed now. A brand-new org has no approvals, no tasks and no
   alerts, so the ordinary feed would greet every new customer with six grey "nothing
   waiting" boxes — worse than the tile grid it replaced. Quiet gets its own screen: what
   this business does, offered as its OWN modules, plus the industry picker if it has not
   chosen one yet. */
.central-quiet { padding: 1.5rem 0 2rem; max-width: 720px; }
.central-quiet__h { margin: 1rem 0 .25rem; font-size: 1.125rem; font-weight: 650; }
.central-quiet__sub { margin: 0 0 1.25rem; font-size: .875rem; }
.central-quiet__cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: .75rem;
}
.central-quiet__card {
  display: flex;
  flex-direction: column;
  gap: .3rem;
  padding: 1rem;
  text-decoration: none;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 12px;
}
.central-quiet__card:hover { background: var(--bg-hover); }
.central-quiet__card-name { font-weight: 600; font-size: .9375rem; }
.central-quiet__card-act { font-size: .8125rem; }
/* Always present, on a busy day as well as a quiet one — the tile grid holds the module
   enable/disable menu, so the feed must never be a dead end. */
.central-quiet__more { margin: 1.25rem 0 0; font-size: .8125rem; }

/* ── SIMPLIFIED MENUS: the "show all screens" escape hatch ─────────────────────────
   Rendered INTO the rail it expands, never on a settings page somewhere else. That is
   structural, not stylistic: it means no state can exist where a reader can tell screens
   are hidden but cannot reach them. Kriya's rail toggle and Artha's both shipped as a
   persisted flag whose control was hidden app-wide, so the setting could never be undone.
   Here the control and the thing it reveals are the same element. */
.smz-rail-showall {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .5rem;
  width: 100%;
  margin-top: .5rem;
  padding: .5rem .6rem;
  font: inherit;
  font-size: .8125rem;
  color: var(--text-muted);
  background: transparent;
  border: 1px dashed var(--border-muted);
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
}
.smz-rail-showall:hover { color: var(--text-primary); background: var(--bg-hover); border-style: solid; }
.smz-rail-showall__n {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-size: .75rem;
  padding: .05rem .4rem;
  border-radius: 999px;
  background: var(--bg-hover);
}

/* Settings ▸ General ▸ Menus — the single org-wide switch behind simplified menus. */
.nav-mode__row { display: flex; align-items: flex-start; gap: .6rem; cursor: pointer; }
.nav-mode__row input[disabled] { cursor: not-allowed; }
.nav-mode__body { display: flex; flex-direction: column; gap: .2rem; }
.nav-mode__title { font-weight: 600; font-size: .9375rem; }
.nav-mode__desc { font-size: .8125rem; line-height: 1.45; }

/* ── MODULE HOME: the workspace block ──────────────────────────────────────────────
   Above the brochure. The one action this module is for, what needs doing, and the
   figures — each of them a link, because a number nobody can click is a poster.
   Every block renders nothing when it has nothing to say, so the page a business sees
   in week one and the page it sees in year two are the same code. */
.mod-work { display: flex; flex-direction: column; gap: .75rem; margin: 0 0 1.5rem; }
.mod-work__primary {
  align-self: flex-start;
  padding: .6rem 1.1rem;
  font-size: .9375rem;
  font-weight: 600;
  text-decoration: none;
  color: #fff;
  background: var(--accent, #4f46e5);
  border-radius: 10px;
}
.mod-work__primary:hover { filter: brightness(1.08); }
.mod-work__atts { display: flex; flex-direction: column; gap: .4rem; }
.mod-work__att {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: .6rem .8rem;
  font-size: .875rem;
  text-decoration: none;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-left-width: 3px;
  border-radius: 8px;
}
.mod-work__att:hover { background: var(--bg-hover); }
.mod-work__att.is-bad { border-left-color: #dc2626; }
.mod-work__att.is-warn { border-left-color: #d97706; }
.mod-work__att-go { color: var(--text-muted); }
.mod-work__tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: .6rem;
}
.mod-work__tile {
  display: flex;
  flex-direction: column;
  gap: .15rem;
  padding: .8rem .9rem;
  text-decoration: none;
  color: var(--text-primary);
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 10px;
}
.mod-work__tile:hover { background: var(--bg-hover); }
.mod-work__tile-v { font-size: 1.25rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.mod-work__tile-v.is-bad { color: #dc2626; }
.mod-work__tile-l { font-size: .75rem; color: var(--text-muted); }

/* ── CENTRAL: the tidy-up card ─────────────────────────────────────────────────────
   Evidence, never advice: what the usage numbers say about enabled-but-unopened modules,
   shown only to admins and only once enough days were actually observed to make the
   sentence a fact. It links to the existing module-management flow (which carries its own
   30-day archive warning) and never offers to disable anything itself. */
.central-tidy {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 1rem;
  padding: .8rem 1rem;
}
.central-tidy__txt { margin: 0; font-size: .875rem; line-height: 1.5; }
.central-tidy__acts { flex: 0 0 auto; }

/* ── CENTRAL TODAY: summary on top, tiles below, every count a door ─────────────────
   The feed used to be full-width sections stacked to whatever height their rows made —
   reading it meant scrolling past everything to see anything. Now: a KPI strip answers
   "how much is waiting" at a glance, the sections sit as a grid of tiles that scroll
   INSIDE themselves, and each count/Open drills into that section's own page — single
   column, full height, quick filters, and a Back button. */
.central-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: .6rem;
  margin: 0 0 16px;
}
.central-kpi {
  display: flex; flex-direction: column; gap: .1rem;
  padding: .75rem .9rem; text-align: left; font: inherit;
  color: var(--text);
  background: var(--bg-surface);
  border: 1px solid var(--border-muted);
  border-radius: 12px;
  cursor: pointer;
}
.central-kpi:hover { background: var(--bg-hover); }
.central-kpi__n { font-size: 1.35rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.central-kpi__l { font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.central-kpi.is-warn .central-kpi__n { color: #d97706; }
/* Zero stays visible but quiet — "nothing waiting" is information, not absence. */
.central-kpi.is-zero .central-kpi__n { color: var(--text-faint); }

/* Today: the sections as a responsive tile grid, each scrolling inside itself.
   `max-width: none` is load-bearing — the base `.central-feed` carries an 880px reading
   column from its stacked-list days, which is right for a single-section DRILL page (a
   list reads better narrow, so flt-* pages keep it) and wrong for a tile dashboard, which
   squeezed into 880px on every monitor. `min(340px, 100%)` collapses cleanly to one
   column on narrow screens instead of overflowing. */
.central-feed.is-today {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 14px;
  max-width: none;
}
.central-feed.is-today > .central-kpis,
.central-feed.is-today > .central-backbar,
.central-feed.is-today > .central-quiet,
.central-feed.is-today > .central-quiet__more,
.central-feed.is-today > .empty-state { grid-column: 1 / -1; }
/* Equal-height cards, aligned rows at every width: the grid stretches each row's cards to
   the same height, the card is a column, and the BODY flexes and scrolls inside it. A
   max-height on the body alone (the first version) left cards ragged — each stopped at its
   own content and the rows never lined up. */
.central-feed.is-today .central-feed-section {
  border: 1px solid var(--border-muted);
  border-radius: 12px;
  background: var(--bg-surface);
  padding: 12px 14px;
  margin: 0;
  display: flex;
  flex-direction: column;
  height: 340px;
}
.central-feed.is-today .central-feed-section-head { flex: 0 0 auto; }
.central-feed.is-today .central-feed-section-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* A section's own page: single column, full height, no cap. */
.central-backbar { display: none; margin: 0 0 12px; }
.central-feed[class*="flt-"] .central-backbar { display: flex; }
.central-feed[class*="flt-"] .central-kpis { display: none; }
.central-backbar__btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .4rem .8rem; font: inherit; font-size: .8125rem; font-weight: 600;
  color: var(--text-muted); background: transparent;
  border: 1px solid var(--border-muted); border-radius: 999px; cursor: pointer;
}
.central-backbar__btn:hover { color: var(--text); background: var(--bg-hover); }

/* Quick filters — rendered in every section, SHOWN only on that section's own page. */
.central-secfilters { display: none; align-items: center; gap: 14px; margin: 4px 0 10px; }
.central-feed.flt-tasks .central-feed-section[data-kind='tasks'] .central-secfilters,
.central-feed.flt-notifications .central-feed-section[data-kind='notifications'] .central-secfilters,
.central-feed.flt-due .central-feed-section[data-kind='due'] .central-secfilters,
.central-feed.flt-outputs .central-feed-section[data-kind='outputs'] .central-secfilters { display: flex; }
.central-secfilters__q {
  width: 240px; max-width: 100%;
  padding: 7px 12px; font: inherit; font-size: .8125rem;
  color: var(--text); background: var(--bg-muted);
  border: 1px solid transparent; border-radius: 999px;
}
.central-secfilters__q::placeholder { color: var(--text-subtle); }
.central-secfilters__q:focus { outline: none; border-color: var(--primary); background: var(--bg-surface); }
.central-secfilters__chk { display: inline-flex; align-items: center; gap: 6px; font-size: .8125rem; color: var(--text-muted); cursor: pointer; user-select: none; }

/* Tiny slider switch — the ONE on/off control on Business & Modules tiles (both columns).
   A real <button role="switch">; .is-on slides the knob and colours the track. */
.mini-switch { position: relative; width: 30px; height: 17px; border-radius: 999px; border: 1px solid var(--border-muted); background: var(--bg-hover); cursor: pointer; padding: 0; flex: 0 0 auto; transition: background .15s ease, border-color .15s ease; }
.mini-switch::after { content: ''; position: absolute; top: 1px; left: 1px; width: 13px; height: 13px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, .3); transition: left .15s ease; }
.mini-switch.is-on { background: var(--success, #1e8e3e); border-color: var(--success, #1e8e3e); }
.mini-switch.is-on::after { left: 14px; }
.mini-switch:disabled { opacity: .45; cursor: default; }

/* ── Business & Modules (Settings ▸ Company Setup) ─────────────────────────────
   Three PANES, each a bordered card with a fixed head and its own scrolling body — the head
   never scrolls away, so you always know which column you are in. Module tiles are one per
   row with the old tile grid's detail (icon · name · one-line description · worded On/Off
   badge): in a column, detail beats density. Below 1100px the panes stack and the page
   scrolls normally — three side-scrolling slivers on a laptop is worse than one long page. */
/* THE PAGE IS STACKED, NOT COLUMNED (2026-09-04, user direction). Three side-by-side panes each
   scrolling in a third of the screen made the page a fixed-height instrument with nowhere to put
   a fourth thing; sections read top to bottom in the order the decisions are made, each as tall
   as it needs to be, and the window does the scrolling. */
.bizmod { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
/* `.bizmod__sec*`, `.bizmod__type*`, `.bizmod--pick`, `.bizmod__back` and `.bizmod__pick-head`
   ARE DELETED (2026-09-19). They dressed the three tab panels, the business-type header and the
   first-run picker page — none of which this page has any more: the tabs became one screen, the
   business type became the org's Industry field, and the page's controls are the CONSOLE's own
   row 2 (`renderShell`'s `.app-shell__tools`), declared through `settingsChrome`. A stylesheet rule whose builder is gone is the next
   reader's evidence that the surface still exists. */

.bizmod-block__head { font-size: .78rem; font-weight: 700; color: var(--text-muted); margin: 14px 0 6px; }
.bizmod-block__head:first-child { margin-top: 0; }
.bizmod-cat { margin-bottom: 10px; }
.bizmod-cat__label { display: block; font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--text-subtle); margin: 0 0 5px 2px; }
/* `.bizmod-head*` IS DELETED (2026-09-19). This page built its own head row — the Orgs page's
   shape, which was right — and then the console learned to carry any settings page's controls in
   `renderShell`'s row 2, which is where every module in the app puts them. A page-shaped header
   beside a console-shaped one is two standards; the rules for the row live with the row.
   The count and the Apply cluster are styled below, as contents of that row. */
.bizmod-count { font-size: .74rem; color: var(--text-subtle); }

/* TWO COLUMNS: every module on the left, the selected module's screens on the right
   (2026-09-19, user direction: "left side, all the modules with a single list one below other …
   right side keep sections and screens, based on selection of module").

   It was THREE columns of module tiles and a separate tab for the screens. `minmax(0, 1fr)` on
   the right track rather than `1fr`: a grid track's default minimum is its CONTENT, so one long
   screen name would push the panel wider and squeeze the module list — its own ellipsis never
   gets a chance to fire. The left track has a fixed range because a module list that grows with
   the longest name is a column that moves every time an org renames something.

   Stacked below 1000px, where two columns would each be too narrow to read a name in and the
   page would become an exercise in horizontal scrolling. */
.bizmod-two { display: grid; grid-template-columns: minmax(15rem, 20rem) minmax(0, 1fr);
  gap: var(--space-4); align-items: start; }
@media (max-width: 1000px) { .bizmod-two { grid-template-columns: 1fr; } }
/* The list scrolls on its own so the screens panel beside it keeps its place: forty modules is
   taller than the pane, and a page that scrolls as a whole puts the thing you just ticked and
   the screens it brought on two different screenfuls. */
.bizmod-two__left { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.bizmod-list { display: flex; flex-direction: column; gap: var(--space-4);
  max-height: 44rem; overflow-y: auto; overscroll-behavior: contain; padding-right: var(--space-1); }
/* Where the order comes from, and the way back to following the launcher. A line of prose, not a
   box: it is a footnote to the list under it. */
.bizmod-src { margin: 0 0 var(--space-2); font-size: .78rem; line-height: var(--lh); }
.bizmod-grp__head { display: flex; align-items: center; gap: 6px; margin: 0 0 var(--space-2);
  font-size: .76rem; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--text-muted); }
.bizmod-grp__rows { display: flex; flex-direction: column; gap: var(--space-1); }
.bizmod-grp__none { margin: 0; }

/* THE CHANGE LOG SCROLLS AFTER A SCREENFUL (2026-09-14, user direction: "make scrollable list of
   info after some height"). An org that has been running a while has hundreds of these, and an
   unbounded list pushed the page's own end out of reach — you scrolled past the thing you came
   for to find out the page had nothing else on it. `overflow-y` clips BOTH axes, so the chips
   inside get their own horizontal room by wrapping rather than by overflowing. */
.bizmod-history__scroll { max-height: 26rem; overflow-y: auto; overscroll-behavior: contain;
  padding-right: 4px; }
/* ONE ROW PER MODULE: grip · checkbox · the row button · ⓘ · ⋯ — a flex line rather than the
   absolutely-positioned cluster the tiles used, because a row has room to lay its controls out
   in the order they are read and a drag handle has to be the first thing under the pointer. */
.bizmod-row { position: relative; display: flex; align-items: center; gap: var(--space-2);
  padding: 5px 6px; border: 1px solid transparent; border-radius: var(--radius-md); }
.bizmod-row:hover { background: var(--bg-hover); }
.bizmod-row.is-selected { border-color: var(--primary); background: var(--bg-hover); }
.bizmod-row.is-dragging { opacity: .45; }
/* The grip is a HINT, not a control: the whole row is `draggable`, so a pointer anywhere on it
   starts the drag and the ⋯ carries the keyboard's version of the same move. A blank of the same
   width sits in the other two groups so every row's checkbox lines up. */
.bizmod-row__grip { flex: 0 0 auto; width: 12px; color: var(--text-subtle); font-size: .7rem;
  line-height: 1; letter-spacing: -1px; cursor: grab; user-select: none; }
.bizmod-row__grip.is-blank { cursor: default; }
/* THE CONTROL IS A CHECKBOX (2026-09-14, user direction: "i want to have them as check boxes to
   enable or disable multiple at a time"). Sized up from the browser default, which is a 13px
   target on a 40-row page. */
.bizmod-row__chk { flex: 0 0 auto; width: 17px; height: 17px; margin: 0; accent-color: var(--primary); cursor: pointer; }
.bizmod-row__chk:disabled { cursor: default; opacity: .5; }
.bizmod-row__chk.is-blank { display: inline-block; }
.bizmod-row__btn { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: var(--space-2);
  padding: 4px 2px; border: 0; background: none; font: inherit; text-align: left; color: var(--text-normal); cursor: pointer; }
.bizmod-row__btn.is-off { opacity: .6; }
.bizmod-row__btn.is-off .bizmod-row__ico { filter: grayscale(1); }
.bizmod-row__ico { width: 18px; height: 18px; color: var(--text-muted); flex: 0 0 auto; }
/* The name ENDS rather than wrapping: a wrapped name makes a two-line row in a list whose whole
   argument is that you can run your eye down it. */
.bizmod-row__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: .8125rem; font-weight: 600; line-height: var(--lh-tight); }
/* "3 off" — how many of this module's screens are switched off, from the editor's own draft. */
.bizmod-row__n { flex: 0 0 auto; padding: 1px 7px; border-radius: var(--radius-pill);
  background: var(--bg-muted, var(--bg-surface)); color: var(--text-muted); font-size: .68rem; font-weight: 700; }
/* "Not available" sits where the checkbox would act. A word, not a dead control: a disabled
   switch reads as a permission the reader could go and get, and this is not that. */
.bizmod-row__soon { flex: 0 0 auto; font-size: .68rem; font-weight: 600; letter-spacing: .02em;
  color: var(--text-subtle); white-space: nowrap; }
.bizmod-row .ws-tip-i { flex: 0 0 auto; }
/* The pending classes mark a row whose box disagrees with the server — the row's own on/off
   styling keeps showing what IS, because a module is not off until it is off. */
.bizmod-row.is-pending-on, .bizmod-row.is-pending-off { border-style: dashed; border-color: var(--primary); }
.bizmod-row.is-pending-off { border-color: var(--danger, #c5221f); }
/* EVERY PER-MODULE ACTION IS A ⋯ ON THE ROW — the shape this app already uses for per-record
   actions (the quote row, the dashboard rail, the bug list), rather than a detail column a
   screen away from the module it acts on. */
.bizmod-row__more { flex: 0 0 auto; width: 24px; height: 24px; display: grid; place-items: center;
  border: 1px solid transparent; border-radius: 7px; background: none; color: var(--text-muted);
  font-size: 1rem; line-height: 1; cursor: pointer; }
.bizmod-row__more:hover, .bizmod-row__more[aria-expanded="true"] { border-color: var(--border); background: var(--bg-hover); color: var(--text-normal); }
/* The change log is a disclosure at the foot of the list now, not a tab: it is read when
   something is being explained, and a tab spends its life announcing a question nobody asked. */
.bizmod-hist > summary { cursor: pointer; font-size: .78rem; font-weight: 600; color: var(--text-muted); padding: var(--space-1) 0; }
/* The module the panel on the right is showing, named above it. */
.secpick__mod { margin: 0 0 var(--space-2); font-size: .9375rem; font-weight: 700; color: var(--text-normal); }

/* What turns a set of ticks into one decision. It was a bordered, shadowed CARD in a sticky band
   of its own; it is the head row's right cluster now — the row already provides the frame, and
   the page it sits on is the reference (`.ws-org-detail__head`).

   `margin-top: 0` on the buttons: `.ghost-button` carries 12px for the stacked forms it was
   written for, which in a row drops Discard off the line Apply sits on — the same reset `.stb`,
   `.lk-bar__row` and `.orbit-detail-actions` all make. */
.bizmod-apply { display: flex; align-items: center; gap: var(--space-3); min-width: 0; font-size: .8rem; }
.bizmod-apply__msg { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text); }
.bizmod-apply .ghost-button, .bizmod-apply .primary-button { margin-top: 0; flex: 0 0 auto; }

.bizmod-preview-note { margin: 0 0 12px; padding: 9px 12px; border: 1px solid var(--primary); border-radius: 10px; font-size: .78rem; color: var(--text-normal); background: var(--bg-body); }
.bizmod-menu { position: absolute; right: 8px; top: calc(100% - 2px); z-index: 6; min-width: 15rem;
  display: flex; flex-direction: column; padding: 5px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-surface); box-shadow: 0 10px 28px rgba(0,0,0,.18); }
.bizmod-menu__i { display: flex; flex-direction: column; gap: 1px; padding: 7px 9px; border: 0; border-radius: 7px;
  background: none; font: inherit; text-align: left; color: var(--text-normal); cursor: pointer; }
.bizmod-menu__i:hover { background: var(--bg-hover); }
.bizmod-menu__hint { font-size: .72rem; }
.bizmod-fields { padding: 2px 0 4px; }

/* ── Screens & menus (the sections editor) ────────────────────────────────────
   Three exclusive modes, then — under the third only — a module tab list beside the screens it
   offers. Nothing here writes until Save, so the bar at the foot is the commit and says what it
   is about to do before it does it. */
.secpick__modes { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 10px; margin-bottom: 14px; }
.secpick__mode { display: flex; gap: 9px; padding: 11px 12px; border: 1px solid var(--border-muted); border-radius: 10px;
  background: var(--bg-body); cursor: pointer; align-items: flex-start; }
.secpick__mode.is-on { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
.secpick__mode input { margin-top: 2px; flex: 0 0 auto; }
.secpick__mode-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.secpick__mode-title { font-size: .8437rem; font-weight: 700; color: var(--text-normal); }
.secpick__mode-desc { font-size: .74rem; line-height: 1.35; }

.secpick__editor { display: grid; grid-template-columns: minmax(11rem, 14rem) minmax(0, 1fr); gap: 14px; align-items: start; }

.secpick__panel { min-width: 0; border: 1px solid var(--border-muted); border-radius: 10px; padding: 12px; background: var(--bg-body); }
/* The preview answers "what did that just do" by looking, rather than by navigating away. */
.secpick__preview { margin-bottom: 12px; padding: 9px 11px; border: 1px solid var(--border-muted); border-radius: 9px; background: var(--bg-surface); }
.secpick__preview-lbl { display: block; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-subtle); margin-bottom: 6px; }
.secpick__preview-rail { display: flex; flex-wrap: wrap; gap: 6px; }
.secpick__pill { display: inline-flex; align-items: center; gap: 5px; padding: 4px 9px; border: 1px solid var(--border); border-radius: 999px; font-size: .74rem; color: var(--text-normal); }
.secpick__pill .hicon-wrap { width: 14px; height: 14px; color: var(--text-muted); }
.secpick__pill-n { color: var(--text-subtle); font-size: .68rem; font-weight: 700; }
.secpick__preview-note { display: block; margin-top: 6px; }
/* What the counts say, offered as one gesture. Tinted as INFORMATION rather than a warning:
   nothing here is wrong, and a red panel over "you have not opened these" would read as a
   problem the reader has to solve. */
.secpick__evidence { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between;
  margin-bottom: 12px; padding: 10px 12px; border: 1px solid var(--border-muted); border-left: 3px solid var(--primary);
  border-radius: 9px; background: var(--bg-surface); }
.secpick__evidence-txt { margin: 0; flex: 1 1 20rem; font-size: .8125rem; line-height: 1.45; color: var(--text-normal); }

/* WHO the menus are for. Above the editor rather than inside a module's panel, because it scopes
   every module at once — beside one module's screens it reads as a per-module setting.
   (Shipped in c26a0091 with its markup and WITHOUT these rules: the write that added them failed
   silently, so the selector rendered unstyled. Caught adding the builder's CSS below.) */
.secpick__scope { margin-bottom: 12px; padding: 10px 12px; border: 1px solid var(--border-muted); border-radius: 9px; background: var(--bg-surface); }
.secpick__scope-pick { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: .8437rem; font-weight: 600; color: var(--text-normal); }
.secpick__scope-pick select { width: auto; min-width: 16rem; }
.secpick__scope-note { margin: 6px 0 0; font-size: .78rem; line-height: 1.4; }
.secpick__telemetry { display: flex; gap: 9px; align-items: flex-start; margin-top: 12px; padding: 11px 12px;
  border: 1px solid var(--border-muted); border-radius: 10px; background: var(--bg-body); cursor: pointer; }
.secpick__telemetry input { margin-top: 2px; flex: 0 0 auto; }

/* ── THE FORM CANVAS (ui/layoutCanvas.js) ─────────────────────────────────────
   ONE look for every module's layout editor. Orbit, Pimly and Artha each drew their own
   (`olc-` / `plc-` / `lb-`), so the same job looked like three products — the field's key
   printed on one and nowhere on the others, the palette left in one and right in another
   (user report 2026-09-08, with the two pages side by side).

   `fc-` and NOT `lc-`: the licence checkout owns that namespace (`.lc-page`, `.lc-steps`, and
   22 `data-lc-*` attributes in `features/billing/licenceConfigurator.js`). Two delegated
   listeners reading each other's attributes is a failure that shows up as one screen's clicks
   silently doing nothing on another. Unscoped, for the reason the builder's block gave: a
   shared canvas cannot be styled from one module's partial. */
.fc { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* PALETTE LEFT, canvas right — the eye starts at the palette, and it is where Orbit's already
   was. `minmax(0,1fr)` on the canvas, or one wide tile stops the whole grid from shrinking. */
.fc-body { display: grid; grid-template-columns: 17.5rem minmax(0, 1fr); gap: 14px; align-items: start; }
/* NO PANEL, NO COLUMN FOR ONE (2026-09-20, user direction: "remove left side element panel").
   `ui/layoutBuilder.js` renders no tray now, and a grid whose first track is a fixed 17.5rem
   reserves that width whether or not anything is in it — so the form sat pushed 17.5rem to the
   right behind a strip of nothing, with no element in the DOM to explain why.

   A MODIFIER CLASS RATHER THAN `:has()`, and the reason is this file's own law: WebKit prices
   `:is()` at specificity ZERO and that includes inside `:has()`, so a rule that must WIN is not
   written that way. `canvasHtml` knows whether it emitted a tray — it is the one place that
   knows — so it says so in a class and the cascade is ordinary. `features/orbit/layoutCanvas.js`
   still renders a tray and still gets two columns. */
.fc-body--solo { grid-template-columns: minmax(0, 1fr); }
/* THE PREVIEW'S CSS IS DELETED WITH THE VIEW (2026-09-20). `.fc-body--preview`, `.fc-preview`,
   `.fc-preview__note`, `.fc-preview__form` and `.fc-views` styled the second view and its
   Arrange / Preview switch; the canvas IS the form now, so nothing emits any of that markup —
   swept, in `src/`, before they went. Dead style is worse than dead code here: it is invisible to
   `ui/deadControls.spec.js`, and the next person adding a preview finds a ready-made look for a
   view this page deliberately does not have. */
.fc-canvas { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.fc-empty, .fc-readonly { margin: 0; padding: 14px; border: 1px dashed var(--border-muted);
  border-radius: 10px; color: var(--text-muted); font-size: .8125rem; }

/* the bar */
/* CHROME OVER CONTENT ENDS IN A HAIRLINE, IT IS NOT A CARD (2026-09-13, user report: the page "is so
   overwelming because of so many borders lines"). This sat as a bordered, radiused box INSIDE the
   setup card, so the toolbar was the second box you met before any content and the buttons in it were
   the third — the app-wide rule for a toolbar is one row that ends in a rule (see "THE SHELL" and
   `.smz-has-listbar`), which is what it is now. It still STICKS, so it still needs an opaque
   background: a transparent sticky bar lets the tiles scroll through it. */
/* ── THE TOOLBAR IS THE HOUSE BAR ────────────────────────────────────────────
   2026-09-19, user direction: *"toolbar content should be like in org page"*, against a screenshot
   of Settings > Org > Orgs.

   The row is `lkBar` now — `.lk-bar__left` / `.lk-bar__right`, `wk-iconbtn` tiles with an icon over
   a one-word caption — so the sizing, the states, the focus ring and the responsive behaviour all
   come from the one place every other list page in the app gets them. THE BESPOKE `.fc-btn` BLOCK
   THAT STOOD HERE IS DELETED: it was written the day before and was a 107th retyping of a shape
   `frontend-ux` already names a law, with `ui/listKit.js` as its single declaration. Keeping it
   "because it works" is how 106 call sites drifted.

   WHAT IS LEFT HERE is what is genuinely this page's: the form picker's width, the two status
   spans, and the ⋯ anchor. */
.fc-barwrap { position: sticky; top: 0; z-index: 5; background: var(--bg-surface);
  border-bottom: 1px solid var(--border-muted); }
.fc-barwrap .lk-bar__row { margin-bottom: 0; }
/* The unsaved state colours that one rule rather than adding a second line. */
.fc-barwrap.is-dirty { border-bottom-color: var(--primary); }
.fc-barwrap .lk-bar__left select { width: auto; min-width: 11rem; min-height: 28px;
  font-size: var(--fs-md); font-weight: 600; border-radius: var(--radius-sm); margin: 0; }
.fc-bar__txt { font-size: var(--fs-sm); color: var(--text-muted); white-space: nowrap; }
.fc-bar__dirty { font-size: var(--fs-sm); font-weight: 600; color: var(--primary); white-space: nowrap; }
/* THE COUNT IS THE FIRST THING TO GO when the row runs out of width; "Not saved" is a WARNING and
   is never dropped. A MEDIA QUERY, and a container query would have been better — the canvas is a
   pane, not the viewport, so this is a proxy (the pane runs about 300px narrower than the window).
   It cannot be a container query: `container-type` makes an element the containing block for
   `position: fixed` DESCENDANTS, and this toolbar contains one — the ⋯ overflow menu, which
   `_pinPopovers` places in VIEWPORT coordinates. */
@media (max-width: 1320px) { .fc-bar__txt { display: none; } }
.fc-bar__more { position: relative; display: inline-flex; }
/* The way back is HOST markup (`ui/formLayoutsPage.js` hands it in as part of `picker`), so it
   arrives as a `ghost-button btn-sm` carrying `margin-top: 12px` — a control sitting below its
   neighbours in a row that spaces itself with `gap`. A toolbar owns its own spacing. */
.fc-barwrap .lk-bar__left > button.ghost-button { margin: 0; }

/* the section */
/* FEWER BORDERS — ONLY WHERE ONE DOES SOMETHING (2026-09-12, user direction: "use less borders in
   pages, use only when needed … across app follow global standards").

   A section was a bordered, radiused, TINTED card holding tiles that are themselves bordered and
   radiused: a box inside a box, which is the "stack of cards" this app already removed from every
   record page ("A RECORD IS A PAGE, NOT A STACK OF CARDS", the block at the end of this file). The
   same rule applies here — WHITESPACE SEPARATES SECTIONS, CHROME DOES NOT — and the one line the
   design keeps is under the HEADING, because with the box gone something has to say "a new part
   starts here", and a rule the eye reads left-to-right does that with one line where a border does
   it with four.
   A TILE KEEPS ITS BORDER, deliberately: it is a thing you GRAB and drop, so it has to read as a
   discrete object. That is the rule — a border earns its place around something you press, drag or
   type into, and around nothing else. */
.fc-sec { margin-bottom: 4px; }
.fc-sec__head { display: flex; align-items: center; gap: 8px; padding: 7px 2px;
  border-bottom: 1px solid var(--border-muted); }
/* The title is a CONTROL where the module renames inline, and must not read as a form box in a
   header — no border until it is reached for. */
.fc-sec__title { flex: 1 1 auto; min-width: 0; margin: 0; font-size: .875rem; font-weight: 700; }
input.fc-sec__title { width: auto; padding: 3px 6px; border: 1px solid transparent;
  border-radius: 6px; background: none; color: var(--text); font: inherit; }
input.fc-sec__title:hover { border-color: var(--border-muted); }
input.fc-sec__title:focus { border-color: var(--primary); background: var(--bg-surface); }
.fc-sec__count { flex: 0 0 auto; font-size: .72rem; color: var(--text-muted); }
.fc-sec__acts { display: flex; gap: 4px; align-items: center; }

/* THE TAB STRIP — which page of the form you are arranging. Rendered only where a form HAS tabs
   (or the reader may make one), so every module that has never used them looks exactly as it
   did. A PANEL tab is the module's own — a list of contacts, addresses, bank accounts — and is
   shown disabled with its reason rather than hidden: an admin told a tab is not arranged here
   goes and finds it, while one shown nothing adds a second tab with the same name. */
/* THE CANVAS'S OWN TAB PILLS ARE DELETED (2026-09-20, user direction: "make sure tabs look like
   exactly like in real form"). `.fc-tab` was a rounded, bordered pill; the record page this canvas
   is supposed to BE draws `ui/tabKit.js`'s underlined `.smz-tab` strip. Two skins for one control
   on two surfaces that must look identical — so the strip renders through `tabsHtml` now and every
   rule below it (`is-on`, `[disabled]`, `__n`, the hover and the focus ring) is the real one.

   WHAT IS LEFT HERE IS ONLY WHAT THE CANVAS ADDS: a row to sit the strip's two extra controls
   beside it. `.fc-tabs` itself keeps the strip's bottom margin off, because the row owns the
   spacing now — restating the strip's own geometry is the "cancel what that one does" trap. */
.fc-tabsrow { display: flex; align-items: flex-end; gap: 6px; margin: 0 0 10px; }
/* THE STRIP WRAPS HERE, AND ONLY HERE (2026-09-20, user direction: "keep tabs and all
   accurate"). `.smz-tabs` is `nowrap` + `overflow-x: auto`, which is right on a RECORD page: a
   strip of six sits on one line and scrolling is the honest answer to a narrow window. A party's
   form declares TEN, and this canvas hangs two more controls off the end — so the last tab
   arrived sliced mid-word ("A…" for Activity) with nothing to say more existed. A truncated
   label is not a smaller list, it is a wrong one.

   IT IS SCOPED TO `.fc-tabsrow`, never to `.smz-tabs`: this is a design surface where the strip
   is a control being READ, and two rows cost nothing. Changing the shared rule would rewrap
   every record page in the app to fix one settings page. */
.fc-tabsrow > .fc-tabs { flex: 1 1 auto; min-width: 0; margin-bottom: 0;
  flex-wrap: wrap; overflow-x: visible; row-gap: 0; }
/* A PANEL TAB IS NOT ARRANGED HERE, AND HAS TO LOOK IT. `.smz-tab[disabled]` alone is one step of
   grey — legible as "off" on a record page where nothing else is, and not enough in a strip of
   ten where four are panels and six are not. The italic is what the canvas's own pill skin used
   to carry, and it is the one thing worth keeping from it: it says "drawn elsewhere" rather than
   "temporarily unavailable", which is the actual difference. */
.fc-tabsrow .smz-tab[disabled] { font-style: italic; font-weight: 500; }
/* The ⋯ is positioned FROM here, because the menu inside it is absolute and an anchor that is not
   a containing block sends it to the page corner. */
.fc-tabs__more { position: relative; flex: 0 0 auto; }
.fc-tabs__add { flex: 0 0 auto; border: 1px dashed var(--border); border-radius: 6px;
  color: var(--primary); padding: 3px 8px; }
/* The per-section tab picker sits between the field count and the section's own controls. Sized
   to its content rather than the app-wide `width: 100%` form rule, which in a header row would
   push every control off the end. */
select.fc-sec__tab { flex: 0 0 auto; width: auto; padding: 2px 6px; font-size: .72rem;
  border-radius: 6px; }
.fc-menu__note { display: block; padding: 6px 10px; font-size: .72rem; color: var(--text-muted); }
/* NO BORDER UNTIL IT IS REACHED FOR — the rule `input.fc-sec__title` a few lines down already states
   for the section title, applied to the small controls beside it. A section header carried ↑ ↓ ⋯ ✕ and
   a count, which was four more outlines per section on a page that already had too many. */
.fc-mini { padding: 3px 7px; border: 1px solid transparent; border-radius: 6px;
  background: none; color: var(--primary); font: inherit; font-size: .78rem; line-height: 1;
  cursor: pointer; }
.fc-mini:hover { background: var(--bg-hover); border-color: var(--border-muted); }
.fc-mini:hover { background: var(--bg-hover); }
.fc-mini[disabled] { opacity: .35; cursor: default; }
.fc-mini--danger { color: var(--danger, #b42318); }
/* A real grid WITH a gap — the `dropIndex` case: a pointer between two tiles must resolve to a
   position rather than fall through to "append". */
.fc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 6px;
  padding: 10px 11px; min-height: 3rem; }
.fc-sec__empty { grid-column: 1 / -1; margin: 0; font-size: .78rem; color: var(--text-muted); }

/* ── THE DOTTED BUTTONS (2026-09-20) ──────────────────────────────────────────────────────────
   User direction: "add sections, or add new elements in each section should show as dotted
   buttons, when user click on new element then open popup with all element list".

   A DASHED OUTLINE IS THE ONE PLACE THIS APP DRAWS A BORDER AROUND EMPTY SPACE, and it is earned:
   the repo's law is that a border belongs around something you press, drag or type into, and this
   IS pressable — the dash is what says the space below it is not finished yet, which a solid box
   would claim was a panel. Full width is still right for "+ Section", which IS the end of the
   canvas; "+ Element" is sized like one more field in the grid instead — see the override below,
   reversed 2026-09-22 from what this paragraph originally said of both.

   IT OWES SIX STATES like every other control here, `:focus-visible` included — it is the only
   way onto this page from a keyboard now that the drag sources are gone. */
.fc-dashed { display: block; width: 100%; box-sizing: border-box; margin: 0; padding: 9px 12px;
  border: 1px dashed var(--border); border-radius: 8px; background: transparent;
  color: var(--text-muted); font: inherit; font-size: .8rem; text-align: center; cursor: pointer;
  min-height: 34px; transition: border-color .12s, color .12s, background .12s; }
.fc-dashed:hover { border-color: var(--primary); color: var(--primary);
  background: var(--surface-subtle, rgba(0, 0, 0, .02)); }
.fc-dashed:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px;
  border-color: var(--primary); color: var(--primary); }
.fc-dashed:active { background: var(--surface-subtle, rgba(0, 0, 0, .04)); }
/* REVERSED AGAIN 2026-09-24, user direction: "+new element, always keep just next to existing
   elements, not as new row. and also color code it to slight orange or something to highlight."

   BOTH EARLIER DECISIONS ARE RECORDED HERE RATHER THAN DROPPED, because each was right about its
   own complaint. The FIRST made it a full-width bar ("it reads as the end of the section rather
   than as one more field in the grid"). The SECOND (2026-09-22, "make +new element size as other
   elements item size") gave it one column's WIDTH through `.fc-addrow`, a separate one-row grid
   repeating `.orbit-detail-grid`'s track — sized like a field, still on a row of its own. That
   separate grid is what this direction is about, so the button is now a CELL OF THE FIELD GRID
   itself (`addTileHtml`, handed to `dGroup` through the same `wrap` that builds every tile). It
   does not match the track any more; it IS the track, and it wraps to a new row only when the row
   beside it is full.

   THE TINT IS A TOKEN PAIR, NEVER A LITERAL. `--warn`/`--warn-bg` are declared in both themes, so
   the one cell in this grid that is not a field reads as an offer in dark mode too — a hardcoded
   fill with no ink is exactly what `ui/injectedThemeSafety.spec.js` sweeps for. Slight: a wash at
   low alpha under a dashed border in the same hue, not a filled amber button competing with Save.

   IT STILL OWES SIX STATES, `:focus-visible` included — it is the only way onto this page from a
   keyboard now that the drag sources are gone — so the tinted hover/active/focus are declared
   here rather than inheriting `.fc-dashed`'s, whose `--primary` would fight the hue. */
/* ALIGNED WITH THE CONTROLS, NOT WITH THE LABELS (2026-09-24, user report with the misalignment
   circled: "new element should aligned properly similar to other elements").

   EVERY CELL IN THIS GRID IS `label` OVER `value`. The first cut put the button in a bare cell, so
   it began where its neighbours' LABELS begin and sat a label's height above every control on the
   row — the column was right and the vertical was not.

   THE LABEL ROW IS RESERVED BY A `::before`, whose height is the LABEL's own font-size and
   line-height. That is the point of doing it here rather than with a `&nbsp;` in the markup or a
   `padding-top` in pixels: "geometry defined as cancel-what-that-one-does must READ the value,
   never restate it" — `.orbit-detail-grid .orbit-field-label` may change its size, and this
   follows it.

   THE HEIGHT COMES FROM THE ROW. `.orbit-detail-grid` is `align-items: start`, so a cell is its
   own content's height; `align-self: stretch` opts this ONE cell back in, and the value slot then
   fills whatever the label row leaves — which is exactly the height of the controls beside it,
   whatever type they are. A pinned figure would be correct next to a text box and wrong next to a
   select or a date, and it would be wrong silently. */
.fc-addcell { align-self: stretch; }
.fc-addcell > .orbit-field-label::before { content: "\00a0"; }
.fc-addcell > .orbit-field-value { display: flex; flex: 1 1 auto; }
.fc-addcell > .orbit-field-value > .fc-dashed--add {
  flex: 1 1 auto; width: 100%; margin: 0;
  /* NO PADDING AND NO HEIGHT OF ITS OWN — the ROW gives it both, and that is the only way this
     stays aligned without a constant. MEASURED: `.fc-dashed`'s own `padding: 9px 12px` and
     `min-height: 34px` made the button 39px tall, which made the add CELL the tallest item in the
     row, which made the row stretch to IT — so the button ran 5px past every control beside it
     and no amount of stretching could pull it back. A flex item cannot be shorter than the box
     it insists on. With both cleared, the neighbours set the row and the button fills what the
     label leaves, whatever type of control is beside it.
     THE 24px FLOOR IS THE ONE CONSTANT, and it is a WCAG 2.2 AA target size rather than a
     geometry: it binds only in a section with nothing else in it, where there is nothing to align
     to and a bare line of text would be a 19px target. */
  min-height: 24px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  border-color: color-mix(in srgb, var(--warn) 45%, transparent);
  background: color-mix(in srgb, var(--warn-bg) 55%, transparent);
  color: var(--warn);
}
.fc-dashed--add:hover {
  border-color: var(--warn); color: var(--warn);
  background: var(--warn-bg);
}
.fc-dashed--add:focus-visible {
  outline: 2px solid var(--warn); outline-offset: 2px;
  border-color: var(--warn); color: var(--warn);
}
.fc-dashed--add:active { background: color-mix(in srgb, var(--warn-bg) 85%, var(--warn)); }
/* ══ THE FORM, OR WHAT HAS BEEN DONE TO IT (2026-09-24) ════════════════════════════════════════
   User direction: "1st modified and all its activity history. in activity tab of form layout."

   THE SWITCH sits in row 2's LEFT cluster, which is the app's law rather than a preference — the
   left changes WHAT you are looking at. It is two `lkScope` chips, so it inherits every state
   `.wk-iconbtn` already owes (hover, :focus-visible, the pressed look) and this rule only has to
   keep the pair together. */
/* `.fc-vswitch`, NOT `.fc-views` — the name is retired (2026-09-24). `.fc-views` styled the
   Arrange / Preview switch that was DELETED on 2026-09-20 when the canvas became the form, and
   `ui/canvasSoloColumn.spec.js` sweeps the sheet to prove that styling did not outlive it. This is a
   different switch for a different pair of views, and reusing the old class would have made that
   sweep unanswerable for ever: a reader finding `.fc-views` styled could no longer tell whether the
   deleted preview's look was back. A new name keeps the guard meaningful and costs one word. */
.fc-vswitch { display: inline-flex; align-items: center; gap: 2px; }

/* A CHANGE LOG IS A LIST OF EVENTS, so it is an `<ol>` and it is dated newest first. NO CARDS:
   "a bordered box whose children are bordered boxes is the stack of cards this app removes on
   sight" — the rows are separated by a hairline under each, which is what a run of records earns.
   The last one has none, or the list ends on a line under nothing. */
.fc-act { list-style: none; margin: 0; padding: 0; max-width: 780px; }
.fc-act__row { padding: 10px 0; border-bottom: 1px solid var(--border); }
.fc-act__row:last-child { border-bottom: 0; }
.fc-act__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
/* TABULAR FIGURES so a column of dates lines up under itself — the one place in this app where a
   reader scans vertically rather than reads. */
.fc-act__when { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.fc-act__who { font-size: 12px; color: var(--text-muted); }
.fc-act__what { margin: 2px 0 0; font-size: 13px; }
.fc-act__detail { list-style: none; margin: 6px 0 0; padding: 0; display: flex;
  flex-direction: column; gap: 4px; }
.fc-act__keys { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; font-size: 12px; }
.fc-act__keys > span { color: var(--text-muted); }
.fc-act__keys code {
  padding: 1px 6px; border-radius: 4px; font: inherit;
  background: var(--bg-subtle, rgba(127, 127, 127, .12));
}
.fc-act__note { margin: 12px 0; font-size: 13px; color: var(--text-muted); }
/* AN EMPTY STATE SAYS WHAT THE SCREEN IS FOR — what / why — never just "none yet". This one also
   has to say WHY it is empty for an org that has been arranging forms for a year, which is the
   case that would otherwise read as a broken page. */
.fc-act__none { max-width: 560px; margin: 24px 0; }
.fc-act__none h3 { margin: 0 0 6px; font-size: 15px; }
.fc-act__none p { margin: 0; font-size: 13px; color: var(--text-muted); line-height: 1.5; }

/* + Section is the LAST thing on the canvas and separates from the form above it — a different
   control (it adds a SECTION, not an element) and untouched by the row above, which is scoped to
   `.fc-sec--form`. */
.fc-dashed--sec { margin: 10px 0 4px; }

/* ══ THE CANVAS IS THE FORM (2026-09-20) ═══════════════════════════════════════════════════════
   User direction: "redesign canvas to match exactly to the actual form … i want form layout also
   same as second image, exactly like actual form."

   THE FIELDS AND THE SECTIONS ARE THE RECORD PAGE'S OWN — `orbit-field` inside
   `orbit-detail-grid` inside `orbit-dgroup`, drawn by `ui/detailKit.js`, which is what every
   record and create page in the app draws. Nothing here restyles any of that; every rule below is
   about getting the ARRANGING chrome out of its way.

   `.fc-ftile` IS THE GRID ITEM. It wraps one `orbit-field`, so it has to behave like the field
   did — `display: contents` would drop the wrapper from the grid and take the drag target with
   it, so it is a grid item that passes its own width through and lets the field fill it. */
.fc-ftile { position: relative; display: block; min-width: 0; border-radius: 6px; }
.fc-ftile--wide { grid-column: 1 / -1; }
/* A CELL IN THIS GRID IS A COLUMN, AND THE ADD CELL IS ONE TOO (2026-09-24). `.fc-addcell` rides
   here rather than declaring its own `display: flex`, because the two must not drift: a field cell
   and the `+ Element` cell beside it are laid out by the same rule or they stop lining up.
   MEASURED, NOT ASSUMED. The add cell shipped without this for an hour and `.orbit-field`'s column
   layout comes from HERE, not from the base rule — so it computed `display: block`, its label span
   stayed inline at 12px against a real label's 16, and the button ran 7px past the controls beside
   it. Every jsdom assertion was green: the structure was right and the cascade was not, which is
   this sheet's own law — only real Chrome reports which rule won. */
.fc-ftile > .orbit-field,
.fc-addcell { display: flex; }
.fc-ftile:hover { background: var(--bg-muted); box-shadow: 0 0 0 4px var(--bg-muted); }
.fc-ftile:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.fc-ftile.is-picked { box-shadow: 0 0 0 2px var(--primary); }
.fc-ftile.is-unknown { opacity: .7; }
/* AN OPEN MENU HAS TO WIN, AND ON A FIELD TILE IT NEVER DID (2026-09-21, user report: the ⋯ popup
   "is getting hiding", and "when we click on properties or duplicate or delete buttons they are
   not working" — one defect, not two: the menu was painted UNDER the next section's header and
   the dotted + Element, so the clicks landed on those).
   `.fc-tile` — Pimly's tray tile — has had this pair since the same bug was found there. The
   FORM tile, which is what this canvas draws, had the `is-menu` class emitted and no rule for it.
   THE OPACITY HALF IS NOT OPTIONAL: `.fc-ftile.is-unknown` is `opacity: .7`, and anything under 1
   makes the element its OWN STACKING CONTEXT — so a `z-index` on it is measured against its
   siblings inside that context and the menu stays sealed however high it goes. A field the
   palette does not know ("Missing") is exactly the tile somebody opens the ⋯ on. */
.fc-ftile.is-menu { z-index: 20; }
.fc-ftile.is-unknown.is-menu { opacity: 1; }
.fc-ftile.is-off .orbit-field-value { opacity: .55; font-style: italic; }
/* THE CONTROL IS INERT. It is the page's real control — that is the whole point — and a real
   `<select>` swallows the pointer, so the row could not be dragged and the ⋯ not reached. */
.fc-ftile .orbit-field-value { pointer-events: none; }
/* THE GRIP RIDES IN THE GUTTER so the field starts where it starts on the page. */
.fc-ftile .fc-tile__grip {
  position: absolute; left: -14px; top: 6px; opacity: 0; cursor: grab;
  transition: opacity .12s ease; font-size: .8rem;
}
.fc-ftile:hover .fc-tile__grip, .fc-ftile:focus-within .fc-tile__grip { opacity: .5; }
.fc-ftile .fc-tile__grip:hover { opacity: 1; }
.fc-ftile .fc-tile__lock { position: absolute; left: -14px; top: 22px; font-size: .68rem; }
/* THE CHIPS AND THE ⋯ float at the row's top-right, quiet until it is hovered — except anything
   saying a field is OFF or MISSING, which must be findable without hunting. */
.fc-ftile .fc-tile__chips {
  position: absolute; right: 22px; top: -2px; z-index: 2; display: flex; gap: 4px;
  opacity: 0; transition: opacity .12s ease; pointer-events: none;
}
.fc-ftile:hover .fc-tile__chips, .fc-ftile:focus-within .fc-tile__chips { opacity: 1; }
.fc-ftile .fc-tile__chips:has(.fc-chip--off) { opacity: 1; }
.fc-ftile .fc-tile__more {
  position: absolute; right: 0; top: -4px; z-index: 2; opacity: 0;
  border: 0; background: none; cursor: pointer; color: var(--text-muted); padding: 2px 4px;
  transition: opacity .12s ease;
}
.fc-ftile:hover .fc-tile__more, .fc-ftile:focus-within .fc-tile__more { opacity: 1; }
.fc-ftile .fc-tile__from { position: absolute; right: 22px; bottom: 0; font-size: .64rem; color: var(--text-faint); }

/* WHAT IS ALREADY TRUE OF THIS FIELD, under the actions (2026-09-20, user direction). Numbered,
   because the reader asked for a numbered list and because a number is the cheapest way to say
   "these are several separate facts" without a bullet's extra ink. Quieter and smaller than the
   items above it: this is state, and everything above it is a thing you can press. */
.fc-menu__props {
  margin: 4px 0 2px; padding: 0 12px 4px 28px; list-style: decimal;
  font-size: .7rem; line-height: 1.5; color: var(--text-muted);
}
.fc-menu__props li { padding: 1px 0; }
.fc-menu__props li::marker { color: var(--text-faint); }

/* A FIELD NOBODY ARRANGED. Same body as every other field — it is on the form, so it looks like
   it — and no grip, because there is no entry to drag. Its ⋯ is always visible rather than fading
   in: these are the fields a reader came down here to DO something about. */
.fc-ftile--tail .fc-tile__more { opacity: .6; }
.fc-ftile--tail:hover .fc-tile__more { opacity: 1; }
.fc-tail { margin-top: 18px; padding-top: 14px; border-top: 1px dashed var(--border); }
.fc-tail__note { margin: 0 0 12px; font-size: .74rem; color: var(--text-muted); max-width: 60ch; }
/* CLEAR sits INSIDE the note's sentence, not above it as a toolbar. It acts on exactly what that
   sentence describes, and a block this size does not earn a control row of its own. Danger ink
   because one half of what it does cannot be undone. */
.fc-tail__clear { border: 0; background: none; padding: 0 2px; font: inherit; font-size: inherit;
  color: var(--danger, #b42318); text-decoration: underline; cursor: pointer; }
.fc-tail__clear:hover { text-decoration: none; }
.fc-tail__clear:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px;
  border-radius: 4px; }
.fc-tail .orbit-dg-head { display: none; }   /* it has no title — the note above is its heading */

/* THE SECTION'S ARRANGING STRIP LIES **OVER** THE GROUP'S OWN HEAD, not above it. Rendered above,
   the title appeared twice — my rename box and then the form's `IDENTITY & CODES` under it, which
   is the repetition this redesign keeps removing. The group's `h3` is hidden rather than dropped
   so its box still sets the head's height and the rule under it stays exactly where the record
   page puts it; the rename input takes its place and is styled to match it character for
   character, so at rest the heading IS the form's and hovering turns it into a field. */
.fc-sec--form { position: relative; }
.fc-sec--form .orbit-dg-head h3 { visibility: hidden; }
/* AN EMPTY SECTION'S HEAD IS AN ORDINARY BLOCK (2026-09-20, user report: "lot of issues with
   alignments"). The rule below takes the head OUT OF FLOW so it can sit on the group's own
   heading — and `dGroup` renders nothing at all for a group with no fields, so an empty section
   had an out-of-flow head over an empty box: zero height, and several in a row printed their
   titles on top of one another at the same y. There is no heading to align to here, so it simply
   stays in flow; `position: static` beats the `absolute` below on ORDER at equal weight, and the
   selector is one class more specific so it does not rely on that. */
.fc-sec--form.fc-sec--empty .fc-sec__head {
  position: static; padding: 0 11px .45rem; min-height: 28px;
}
/* Its title is not sitting on a uppercase heading any more, so it wears the heading's own look
   rather than borrowing it by overlay. */
.fc-sec--form.fc-sec--empty { padding-top: 6px; }
/* AN EMPTY SECTION'S DROP ZONE NEEDS A TARGET (2026-09-20). `[data-fc-drop]` wraps the rendered
   group, and `dGroup` renders NOTHING for a group with no fields — so the zone was a box of zero
   height and `e.target.closest('[data-fc-drop]')` could never resolve to it. Dragging a field
   into a new section silently did nothing, on every canvas: the reader could create a section
   they had no way to fill. The dotted button sits OUTSIDE this div, so it gave the section height
   without giving the DROP a target, which is why this is not covered by it. */
.fc-sec--form.fc-sec--empty [data-fc-drop] {
  min-height: 3rem; display: flex; align-items: center;
  border: 1px dashed transparent; border-radius: 8px; margin: 0 11px;
}
.fc-sec--form.fc-sec--empty .fc-sec__empty { margin: 0; padding: 0 2px; }
.fc-sec--form.fc-sec--empty .fc-sec__count,
.fc-sec--form.fc-sec--empty .fc-sec__acts,
.fc-sec--form.fc-sec--empty .fc-sec__tab { opacity: 1; }
.fc-sec--form .fc-sec__head {
  position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  display: flex; align-items: center; gap: 6px; min-height: 28px;
  border: 0; background: none; padding: 0 0 .45rem; pointer-events: none;
}
.fc-sec--form .fc-sec__head > * { pointer-events: auto; }
.fc-sec--form input.fc-sec__title {
  flex: 0 0 auto;   /* `.fc-sec__title` is `flex: 1 1 auto` for the tiles canvas — here it must be
                       only as wide as the heading, or the box spans the whole rule beneath it. */
  border: 1px solid transparent; background: none; padding: 0 4px; border-radius: 6px;
  font-size: .76rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--text-subtle); width: auto; min-width: 12rem; font-family: inherit;
}
.fc-sec--form .fc-sec__head:hover input.fc-sec__title, .fc-sec--form input.fc-sec__title:focus {
  border-color: var(--border); background: var(--bg-surface); color: var(--text);
}
.fc-sec--form .fc-sec__count, .fc-sec--form .fc-sec__acts, .fc-sec--form .fc-sec__tab {
  opacity: 0; transition: opacity .12s ease;
}
.fc-sec--form .fc-sec__head:hover .fc-sec__count, .fc-sec--form .fc-sec__head:hover .fc-sec__acts,
.fc-sec--form .fc-sec__head:hover .fc-sec__tab, .fc-sec--form .fc-sec__head:focus-within .fc-sec__count,
.fc-sec--form .fc-sec__head:focus-within .fc-sec__acts, .fc-sec--form .fc-sec__head:focus-within .fc-sec__tab { opacity: 1; }

/* the tile */
/* A TILE IS STILL A THING YOU GRAB — AND 29 OUTLINES IS A WALL. The rule this file states is that a
   border earns its place around something you press, drag or type into; what it does not say is that
   the border has to be a LINE. A lifted surface carries the same "this is a discrete object you can
   pick up" affordance, and a grid of 29 of them then reads as blocks rather than as a page of boxes —
   which is what the user was looking at. The hover, the picked ring and the unknown/dashed state all
   survive below, so nothing that the outline was TELLING you is lost. */
.fc-tile { position: relative; display: flex; align-items: flex-start; gap: 7px;
  padding: 8px 30px 8px 9px; border: 1px solid transparent; border-radius: 8px;
  background: var(--bg-muted); cursor: grab; }
/* THE 1px TRANSPARENT BORDER IS DELIBERATE, not a leftover: the hover and picked states colour it in,
   and going 0 -> 1px instead would shift every tile in the grid by a pixel as the pointer crosses it. */
.fc-tile:hover { background: var(--bg-hover); border-color: var(--border-muted); }
.fc-tile:active { cursor: grabbing; }
.fc-tile.is-unknown { border-color: var(--border-muted); border-style: dashed; opacity: .75; }
.fc-tile.is-inherited { background: var(--bg-muted); }
.fc-tile.is-picked { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
/* THE TILE WHOSE MENU IS OPEN IS RAISED, and its dimming comes off while it is. The menu lives
   inside the tile, so a tile that is its own stacking context (any `opacity < 1` makes one)
   seals the menu underneath every tile after it — and a menu painted at .75 is legible enough
   to look deliberate and too faint to use. Both halves, or the fix is half a fix. */
.fc-tile.is-menu { z-index: 20; }
.fc-tile.is-unknown.is-menu { opacity: 1; }
.fc-tile__grip { flex: 0 0 auto; color: var(--text-muted); font-size: .8rem; line-height: 1.35; cursor: grab; }
.fc-tile__tick { flex: 0 0 auto; }
.fc-tile__body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.fc-tile__name { font-size: .8125rem; font-weight: 600; }
.fc-tile__lock { font-size: .72rem; }
/* THE KEY, under the label. The label alone is not enough to act on — two fields can read
   "Name", and the export, the API and every rule name the key. */
.fc-tile__key { font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: .68rem; color: var(--text-muted); }
.fc-tile__from { font-size: .68rem; color: var(--text-muted); font-style: italic; }
.fc-tile__chips { display: flex; flex-wrap: wrap; gap: 3px; align-items: center; }
.fc-tile__more { position: absolute; top: 4px; right: 4px; padding: 2px 5px; border: 0;
  border-radius: 6px; background: none; color: var(--text-muted); font: inherit; cursor: pointer; }
.fc-tile__more:hover { background: var(--bg-hover); color: var(--text); }

/* A CHIP INSIDE A TILE WAS A THIRD BOX. It says one word; a tint says "this is a mark, not a field"
   perfectly well, and the required one keeps its own colour, which is what actually distinguishes it. */
.fc-chip { padding: 1px 7px; border: 0; border-radius: 999px;
  background: var(--track, var(--bg-hover)); font-size: .66rem; color: var(--text-muted); white-space: nowrap; }
.fc-chip--req { background: color-mix(in srgb, var(--primary) 16%, transparent); color: var(--primary); }
.fc-chip--off { opacity: .8; }

/* the palette */
/* The tray is a REGION rather than a card — the palette on one side, the canvas on the other — so
   what it needs is the one line that says where it ends, not a box around a column of boxes. Its
   items are bordered because each is a button and a drag source. `padding-right` keeps the hairline
   off the text; `overflow-y` stays, and the sticky offset with it. */
.fc-tray { border-right: 1px solid var(--border-muted); padding: 0 14px 0 0;
  position: sticky; top: 58px; max-height: calc(100vh - 9rem); overflow-y: auto; }
.fc-tray__n { font-size: .72rem; color: var(--text-muted); }
.fc-tray__note { margin: 6px 0 8px; font-size: .72rem; color: var(--text-muted); }
.fc-tray__group { margin-top: 10px; }
.fc-tray__glabel { display: block; font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted); margin-bottom: 4px; }
.fc-tray__sub { display: block; font-size: .68rem; color: var(--text-muted); margin: 6px 0 3px; }
.fc-tray__new { border-top: 1px solid var(--border-muted); margin-top: 12px; padding-top: 10px; }
/* A BUTTON as well as a drag source: click appends, drag aims. It was a `div` with only a
   `dragstart` handler, which no keyboard could reach. */
.fc-spare { flex: 1 1 auto; display: flex; align-items: center; gap: 6px; padding: 6px 8px;
  border: 1px solid transparent; border-radius: 7px; background: var(--bg-muted);
  color: var(--text); font: inherit; font-size: .78rem; text-align: left; cursor: pointer; }
.fc-spare:hover { background: var(--bg-hover); border-color: var(--primary); }
.fc-spare--new { width: 100%; margin-bottom: 4px; border-style: dashed; color: var(--primary); }
.fc-spare--new:hover { border-style: solid; }
/* The ⋯ sits immediately beside the item it belongs to; boxing it made one row read as two controls. */

/* bulk */
.fc-bulk { position: sticky; top: 52px; z-index: 6; display: flex; flex-wrap: wrap; gap: 8px;
  align-items: center; justify-content: space-between; padding: 7px 11px;
  border: 1px solid var(--primary); border-radius: 9px; background: var(--bg-surface); }
.fc-bulk__n { font-size: .78rem; font-weight: 600; }
.fc-bulk__acts { display: flex; flex-wrap: wrap; gap: 5px; }

/* menus */
.fc-menu { position: absolute; right: 4px; top: calc(100% - 2px); z-index: 6; min-width: 13rem;
  display: flex; flex-direction: column; gap: 2px; padding: 5px; border: 1px solid var(--border-muted);
  border-radius: 9px; background: var(--bg-surface); box-shadow: 0 8px 22px rgba(0,0,0,.14); }
.fc-menu button { padding: 6px 9px; border: 0; border-radius: 6px; background: none; font: inherit;
  font-size: .8125rem; text-align: left; color: var(--text); cursor: pointer; }
.fc-menu button:hover { background: var(--bg-hover); }
.fc-menu__danger { color: var(--danger, #b42318); }
.fc-menu__note { padding: 6px 9px; margin: 0; font-size: .72rem; color: var(--text-muted); }
/* THE RULE EDITOR IS IN THE PROPERTIES PANEL, not floating off a tile (2026-09-21). It gave up
   `.fc-menu` with its position and its 13rem floor; this gave up its own 17rem one. A minimum
   wider than the panel is the whole of how it came to be drawn outside it. */
.fc-rule { min-width: 0; max-width: 100%; padding: 4px 0; }
.fc-rule__note { margin: 0 0 6px; font-size: .72rem; color: var(--text-muted); }
/* STACKED AT PANEL WIDTH. Three controls at `1 1 7rem` in ~300px wrap to two lines with one
   stranded; one per line reads as the sentence it is — field, then operator, then value. */
.fc-props .fc-rule__acts { padding: 8px 0 0; }
.fc-rule__row { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 9px; }
.fc-rule__row select, .fc-rule__row input { flex: 1 1 7rem; min-width: 0; font-size: .8125rem; }
/* STACKED AT PANEL WIDTH — AND THE BASIS RESET WITH IT (2026-09-21, user report: "when this
   section expanded, scrolls and buttons, everything stopped working").
   `flex-direction: column` alone is the bug: the row's children are `flex: 1 1 7rem`, and in a
   COLUMN that 7rem is a HEIGHT. Each select became 112px tall and grew to fill, the section
   pushed the panel's footer and its scroll off the bottom, and the whole popup read as frozen.
   A `flex-basis` means the main axis, so turning the axis without restating the basis is never
   half a change — it is a different one. */
.fc-props .fc-rule__row { flex-direction: column; padding: 0; }
.fc-props .fc-rule__row > select,
.fc-props .fc-rule__row > input { flex: 0 0 auto; width: 100%; }
.fc-rule__acts { display: flex; gap: 5px; padding: 7px 9px 2px; }

/* what a scope inherits — read-only, so drawn as text rather than as tiles inviting a drag */
.fc-inherit { margin-bottom: 12px; padding: 10px 12px; border: 1px dashed var(--border-muted);
  border-radius: 10px; background: var(--bg-muted); }
.fc-inherit__head { display: flex; align-items: baseline; gap: 8px; }
.fc-inherit__head h4 { margin: 0; font-size: .8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted); }
.fc-inherit__lead { margin: 4px 0 8px; font-size: .72rem; color: var(--text-muted); }
.fc-inherit__sec { margin-top: 6px; }
.fc-inherit__tiles { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px; }
.fc-inherit__tile { padding: 2px 8px; border: 1px solid var(--border-muted); border-radius: 999px;
  background: var(--bg-surface); font-size: .75rem; color: var(--text-muted); }

/* Narrow: the palette goes back above the canvas rather than squeezing both. */
@media (max-width: 900px) {
  .fc-body { grid-template-columns: minmax(0, 1fr); }
  .fc-tray { position: static; max-height: none; }
}

/* ── THE FORM BUILDER (ui/layoutBuilder.js) ───────────────────────────────────
   One canvas for any module's record. Unscoped and `lb-` prefixed, unlike Pimly's `plc-` rules,
   which live inside its own partial and are therefore reachable only from `#pimly-tab` — a
   shared builder cannot be styled from one module's partial. */
.lb { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.lb-body { display: grid; grid-template-columns: minmax(0, 1fr) 17.5rem; gap: 14px; align-items: start; }
.lb-canvas { display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.lb-sec { border: 1px solid var(--border-muted); border-radius: 10px; background: var(--bg-surface); }
.lb-sec__head { display: flex; align-items: center; gap: 8px; padding: 8px 11px; border-bottom: 1px solid var(--border-muted); }
.lb-sec__head h4 { margin: 0; font-size: .8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.lb-sec__n { flex: 1 1 auto; font-size: .72rem; }
/* A real grid WITH a gap — exactly the case `dropIndex` was corrected for: a pointer landing
   between two tiles must resolve to a position rather than fall through to "append". */
.lb-sec__drop { display: grid; grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr)); gap: 6px; padding: 10px 11px; min-height: 3rem; }
.lb-sec__empty { grid-column: 1 / -1; margin: 0; font-size: .78rem; }

.lb-tile { position: relative; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 7px 30px 7px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-body); cursor: grab; }
.lb-tile:active { cursor: grabbing; }
.lb-tile.is-unknown { border-style: dashed; opacity: .75; }
.lb-tile__name { font-size: .8125rem; color: var(--text-normal); }
.lb-tile__more { position: absolute; right: 5px; top: 50%; transform: translateY(-50%); width: 22px; height: 22px;
  display: grid; place-items: center; border: 1px solid transparent; border-radius: 6px; background: none;
  color: var(--text-muted); cursor: pointer; }
.lb-tile__more:hover { border-color: var(--border); background: var(--bg-hover); }

.lb-chip { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 6px; border-radius: 999px; border: 1px solid currentColor; }
.lb-chip--req { color: var(--primary); }
.lb-chip--lock { color: var(--text-subtle); }
.lb-chip--off { color: var(--warning, #b26a00); }

.lb-menu { position: absolute; right: 4px; top: calc(100% - 2px); z-index: 6; min-width: 13rem;
  display: flex; flex-direction: column; padding: 5px; border: 1px solid var(--border); border-radius: 9px;
  background: var(--bg-surface); box-shadow: 0 10px 26px rgba(0,0,0,.18); }
.lb-menu button { padding: 6px 9px; border: 0; border-radius: 6px; background: none; font: inherit;
  font-size: .8125rem; text-align: left; color: var(--text-normal); cursor: pointer; }
.lb-menu button:hover { background: var(--bg-hover); }

/* THE FORM BUILDER, part two (2026-09-06) — scopes, moving without dragging, inventing a field.
   Kept beside the rest of `.lb-*` rather than in a module sheet: one canvas serves every
   module, so a rule that lived in Artha's injected styles would style it there and nowhere
   else. */

/* "Move to…" is a select inside the tile menu; the app-wide `width: 100%` on a form control is
   right in a form and wrong in a 13rem popup. */
.lb-menu__moveto { display: flex; flex-direction: column; gap: 3px; padding: 6px 9px;
  font-size: .75rem; color: var(--text-muted); }
.lb-menu__moveto select { width: 100%; font-size: .8125rem; }

/* The section's ↑ ↓ sit before Rename, and a disabled one stays visible rather than moving the
   other controls when it reaches an end — a row whose buttons shift as you use them is the
   thing that makes repeated moves feel unreliable. */
.lb-sec__head [data-lb-act="sec-up"],
.lb-sec__head [data-lb-act="sec-down"] { padding-inline: 7px; line-height: 1; }
.lb-sec__head [data-lb-act="sec-up"][disabled],
.lb-sec__head [data-lb-act="sec-down"][disabled] { opacity: .35; cursor: default; }

/* WHAT A GROUP INHERITS. Read-only by design, so it is drawn as text rather than as tiles that
   invite a drag they will not accept. */
.lb-inherit { margin-bottom: 12px; padding: 10px 12px; border: 1px dashed var(--border-muted);
  border-radius: 10px; background: var(--bg-muted); }
.lb-inherit__head { display: flex; align-items: baseline; gap: 8px; }
.lb-inherit__head h4 { margin: 0; font-size: .8125rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted); }
.lb-inherit__lead { margin: 4px 0 8px; }
.lb-inherit__sec { margin-top: 6px; }
.lb-inherit__title { font-size: .75rem; font-weight: 700; color: var(--text-muted); }
.lb-inherit__tiles { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px; }
.lb-inherit__tile { padding: 2px 8px; border: 1px solid var(--border-muted); border-radius: 999px;
  background: var(--bg-surface); font-size: .75rem; color: var(--text-muted); }

/* Inventing a field: the tiles read as ACTIONS, not as draggable spares, because they are. */
.lb-tray__new { border-top: 1px solid var(--border-muted); margin-top: 10px; padding-top: 10px; }
.lb-tray__new .muted { margin: 2px 0 6px; }
.lb-spare--new { display: block; width: 100%; margin-bottom: 4px; text-align: left; cursor: pointer;
  border: 1px dashed var(--border-muted); background: none; color: var(--primary); font: inherit;
  padding: 5px 9px; border-radius: 7px; }
.lb-spare--new:hover { background: var(--bg-hover); border-style: solid; }

/* USER-FRIENDLINESS PASS (2026-09-07, user: "interface is not user friendly yet").

   A TRAY FIELD IS A BUTTON. It was a `div` that answered only `dragstart` — not focusable, so
   unreachable from a keyboard, and fiddly on a trackpad. It reads as a row you can click, with
   the `+` carrying that meaning rather than a sentence having to. */
.lb-spare { display: flex; align-items: center; gap: 7px; width: 100%; margin-bottom: 4px;
  padding: 6px 9px; border: 1px solid var(--border-muted); border-radius: 7px;
  background: var(--bg-surface); color: var(--text); font: inherit; font-size: .8125rem;
  text-align: left; cursor: grab; }
.lb-spare:hover { border-color: var(--primary); background: var(--bg-hover); }
.lb-spare:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.lb-spare:active { cursor: grabbing; }
.lb-spare__plus { flex: 0 0 auto; width: 1rem; text-align: center; color: var(--primary);
  font-weight: 700; }

/* THE SECTION HEADER CARRIED FOUR BUTTONS and the name had to compete with all of them — eight
   controls on a two-section form. The two MOVES stay (they are one click and used in runs); the
   two that open a dialog moved into a ⋯, the same control the tiles already have. */
.lb-sec__head { position: relative; }
.lb-sec__mv { padding-inline: 7px; line-height: 1; }
.lb-menu--sec { right: 8px; top: calc(100% - 4px); }

/* A tile is draggable and looked like a label. The grip says so without taking a column. */
.lb-tile { cursor: grab; }
.lb-tile::before { content: '⠿'; flex: 0 0 auto; margin-right: 2px; color: var(--text-muted);
  font-size: .7rem; line-height: 1; opacity: .5; }
.lb-tile:hover::before { opacity: 1; }
.lb-menu__danger { color: var(--danger, #c0392b); }
.lb-menu__why { padding: 6px 9px; font-size: .72rem; color: var(--text-muted); }

.lb-rule { position: absolute; left: 0; top: calc(100% + 2px); z-index: 7; width: max(100%, 22rem);
  padding: 10px; border: 1px solid var(--primary); border-radius: 9px; background: var(--bg-surface);
  box-shadow: 0 10px 26px rgba(0,0,0,.18); cursor: default; }
.lb-rule__lead { margin: 0 0 7px; font-size: .74rem; }
.lb-rule__row { display: grid; grid-template-columns: 1fr auto 1fr; gap: 6px; }
.lb-rule__row select, .lb-rule__row input { width: 100%; }
.lb-rule__acts { display: flex; gap: 6px; justify-content: flex-end; margin-top: 8px; }

.lb-tray { position: sticky; top: 8px; border: 1px solid var(--border-muted); border-radius: 10px;
  padding: 11px; background: var(--bg-body); max-height: 34rem; overflow-y: auto; }
.lb-tray__head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.lb-tray__head h4 { margin: 0; font-size: .8125rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.lb-tray__q { width: 100%; margin: 7px 0 4px; }
.lb-tray__lead { margin: 0 0 9px; line-height: 1.35; }
.lb-tray__group { margin-bottom: 10px; }
.lb-tray__glabel { display: block; font-size: .68rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-subtle); margin-bottom: 4px; }
.lb-spare { display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 6px 9px; margin-bottom: 3px; border: 1px solid var(--border-muted); border-radius: 7px;
  background: var(--bg-surface); font-size: .78rem; color: var(--text-normal); cursor: grab; }
.lb-spare:hover { border-color: var(--primary); }

/* Sticky at the top: the sections scroll under it, so what is about to be committed stays on
   screen — the same reasoning as the menus editor's save bar. */
.lb-bar { position: sticky; top: 0; z-index: 5; display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; justify-content: space-between; padding: 9px 12px;
  border: 1px solid var(--border-muted); border-radius: 10px; background: var(--bg-surface); }
.lb-bar.is-dirty { border-color: var(--primary); }
.lb-bar__txt { font-size: .8125rem; color: var(--text-normal); }
.lb-bar__acts { display: flex; flex-wrap: wrap; gap: 7px; }

/* Setup ▸ Item posting ▸ Posting classes — the account set an item inherits. */
.ic-tbl { width: 100%; }
.ic-tbl .ic-mid { text-align: center; white-space: nowrap; }
.ic-tbl .ic-act { text-align: right; white-space: nowrap; }
.ic-chip { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  padding: 1px 6px; border-radius: 999px; border: 1px solid currentColor; color: var(--primary); }
.ic-foot { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

@media (max-width: 900px) {
  .lb-body { grid-template-columns: 1fr; }
  .lb-tray { position: static; max-height: none; }
}
.secpick__bar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.secpick__presets { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.secpick__search input { width: auto; min-width: 13rem; }
.secpick__count { margin: 0 0 10px; }
.secpick__dest { margin-bottom: 12px; }
.secpick__dest-head { display: flex; align-items: center; gap: 7px; margin-bottom: 4px; }
.secpick__dest-head h5 { margin: 0; font-size: .78rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); flex: 1 1 auto; }
.secpick__dest-head .hicon-wrap { width: 15px; height: 15px; color: var(--text-subtle); }
.secpick__dest-tip { margin: 0 0 6px; }
.secpick__rows { display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 3px; }
/* A row is the LABEL plus its Rename, side by side. The rename sits outside the <label> because
   a button inside one is activated by clicking the label — it would toggle the screen as well as
   open the prompt — and it appears on hover or focus so a column of forty screens is a column of
   names rather than a column of buttons. */
.secpick__rowwrap { display: flex; align-items: flex-start; gap: 4px; }
.secpick__rowwrap > .secpick__row { flex: 1 1 auto; min-width: 0; }
.secpick__rename { flex: 0 0 auto; align-self: center; opacity: 0; transition: opacity 120ms ease; }
.secpick__rowwrap:hover .secpick__rename,
.secpick__rename:focus-visible { opacity: 1; }
/* …but never hidden from a keyboard or a screen reader: `opacity` keeps it focusable, where
   `display: none` would take it out of the tab order entirely. */
.secpick__scope { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 8px; }
.secpick__scope-n { font-size: .72rem; color: var(--text-muted); }
.secpick__evidence-txt { margin: 0; font-size: .8rem; }
/* The tree: a disclosure chevron and one indent, which is all the left rail uses to say that a
   screen belongs to the section above it. */
.secpick__chev { flex: 0 0 10px; font-size: .7rem; line-height: 1; color: var(--text-subtle); }
.secpick__colrow.is-on .secpick__chev { color: #fff; }
.secpick__branch + .secpick__branch { margin-top: 1px; }
.secpick__branch .secpick__rows { margin: 1px 0 6px 22px; padding-left: 8px;
  border-left: 1px solid var(--border-muted); }
.secpick__renamed { margin-left: 6px; padding: 0 6px; border-radius: 999px; font-size: .64rem;
  font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--primary); border: 1px solid currentColor; vertical-align: 1px; }
.secpick__row { display: flex; align-items: flex-start; gap: 8px; padding: 6px 8px; border-radius: 7px; cursor: pointer; }
.secpick__row:hover { background: var(--bg-hover); }
.secpick__row input { margin-top: 2px; flex: 0 0 auto; }
.secpick__row.is-locked { cursor: default; opacity: .8; }
/* NO STRIKE-THROUGH ON AN UNTICKED SCREEN (2026-09-14, user report). It was legible while OFF
   was the exception — a handful of screens an org had switched off, struck out among the ones it
   kept. The editor now opens with everything off, so a whole column rendered as crossed-out text,
   which reads as DELETED rather than as not-yet-chosen: struck-through is the mark for something
   that was taken away, and none of these has been. Muted ink says "not on" without saying that. */
.secpick__row.is-off .secpick__row-name { color: var(--text-subtle); }
.secpick__row-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.secpick__row-name { font-size: .8125rem; color: var(--text-normal); }
.secpick__row-why { font-size: .7rem; line-height: 1.3; }
.secpick__gated { display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 999px; border: 1px solid currentColor; color: var(--warning, #b26a00); font-size: .62rem; font-weight: 700; text-transform: uppercase; }
/* The save bar. Sticky at the foot of the page, because the ticks are above it and the reader
   must be able to see what they are committing without scrolling back. */
.secpick__save { position: sticky; bottom: 0; z-index: 4; display: flex; flex-wrap: wrap; gap: 10px;
  align-items: center; justify-content: space-between; margin-top: 14px; padding: 10px 12px;
  border: 1px solid var(--border-muted); border-radius: 10px; background: var(--bg-surface); }
.secpick__save.is-dirty { border-color: var(--primary); }
.secpick__save-txt { font-size: .8125rem; color: var(--text-normal); }
.secpick__save-acts { display: flex; gap: 8px; }

/* A screen this org switched off, opened by address. Placed at the TOP of the module's own tab
   rather than replacing it: the rails and every other screen still work, so the reader is one
   click from somewhere useful instead of at a dead end. */
.secblock { margin: 0 0 14px; padding: 16px 18px; border: 1px solid var(--border); border-left: 3px solid var(--primary);
  border-radius: 10px; background: var(--bg-surface); }
.secblock__h { margin: 0 0 5px; font-size: .95rem; font-weight: 700; color: var(--text-normal); }
.secblock__p { margin: 0 0 8px; font-size: .8437rem; line-height: 1.45; color: var(--text-normal); max-width: 44rem; }
.secblock__p:last-child { margin-bottom: 0; }
/* "Module changes", under the tiles it describes (it was on the org detail page, where the log
   sat two screens away from the switch it is about). Separated by a rule rather than boxed: it
   is a footnote to the column, not a fourth thing competing with the modules for attention. */
.bizmod-history { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border-muted); }
.bizmod-history__h { margin: 0 0 2px; font-size: .78rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted); }
@media (max-width: 900px) {
  /* The module tab list becomes a scrolling row above the panel rather than a column beside it:
     at this width a 14rem column leaves the checkboxes nothing to sit in. */
  .secpick__editor { grid-template-columns: 1fr; }
}

/* THE HOME RAIL / MODULE ORDER PAGE IS GONE (2026-09-19, user direction: "Remove module order
   page, instead in this same page under selected modules, user can drag and change the order of
   modules"). Its `.hrail-*` rules went with the markup that used them — a stylesheet rule whose
   builder was deleted is the next reader's evidence that a page still exists. The order is the
   Selected group of Module selections now: `.bizmod-row__grip` and `.bizmod-row.is-dragging`. */

/* =========================================================================
   THE CHROME FOLLOWS THE PAGE — header and FIRST RAIL, one lifted surface
   =========================================================================
   (2026-08-20, user direction.) Light chrome on a light page, dark on a dark one — but never
   the SAME tone as the page: the band and the first rail sit one clear step off it, so they
   read as chrome that is highlighted rather than as more page. This is the third position
   this app has held (inverted → following → inverted → following-but-distinct); each swing
   costs nothing now because the mechanism below is TOKENS, not re-skinned components.

   `--chrome-*` is a full palette stated per theme; `.topbar` and the first-rail cluster
   re-declare the standard names FROM it, so every control inside them follows by inheritance.
   It is worth keeping that indirection even now that the chrome agrees with the theme: it is
   what makes a change of mind a values edit, and it is what the popover restores hang off.

   The popovers living in the chrome's DOM but opening OVER the page (profile and notification
   menus, launcher, search panel, centre-nav dropdown, rail fly-outs) are still restored
   through the `--page-*` aliases — near-no-ops while the palettes agree, and the one thing
   that would have to be re-derived on the next swing. `ui/headerTheme.spec.js` holds the
   scope list and the restore list in step as sets.

   THE RAMP, in both themes: page → second rail → first rail + band, each a step further from
   the content. Chrome is the outermost and most distinct; the second rail sits between.

   NO `:is()` anywhere in this block — WebKit ships it with specificity ZERO, and the
   rail-popup restores must out-rank the cluster's own token scope. */
:root {
  /* THE BAND IS THE APP'S OWN GROUND (2026-09-19, user direction: "make page header background
     color and whole app background color same"). It was ONE STEP OFF the content — slate-200 on
     a white page, so the chrome read as a lifted edge — and that was the right answer while the
     content ran edge-to-edge underneath it. It stopped being right the day the content became a
     rounded PANEL on that ground: the band, the seams around all three panels and the strip
     behind the rails were then three tones of almost-the-same, which reads as a rendering
     accident rather than as chrome. One ground, three panels floating on it.
     WRITTEN ON `--chrome-bg` RATHER THAN `--header-bg`, which is what `.topbar` paints: the
     first-rail cluster and the `.canvas` frame read the same token, so this is one edit instead
     of three — and `--chrome-bg` keeps a consumer, which pointing `--header-bg` elsewhere would
     have taken from it. `--chrome-bg-muted` (the search pill) is a SEPARATE token and is
     untouched: it is the one thing in the band that still has to be a step off it. */
  --chrome-bg: var(--zone-content);
  --chrome-divider: #cbd5e1;
  --chrome-text: #0f172a;
  --chrome-text-muted: #475569;
  --chrome-text-subtle: #64748b;
  --chrome-bg-muted: rgba(15, 23, 42, 0.08);
  --chrome-wash: rgba(15, 23, 42, 0.08);
  --chrome-wash-strong: rgba(15, 23, 42, 0.14);
  --chrome-border: #cbd5e1;
  --chrome-border-muted: #dbe3ec;
  --chrome-primary: #1d4ed8;
  --chrome-wash-primary: rgba(29, 78, 216, 0.12);
  --chrome-orgbrand: #6d28d9;   /* AA on --chrome-bg */
  /* The SECOND rail sits BETWEEN the chrome and the page — one step back toward the content,
     so the three surfaces read as depth. A token because several surfaces must agree to the
     pixel: the rail, the first-rail active chip, and the hamburger's tongue. */
  --rail2-bg: #eef2f7;
  --rail2-text: #0f172a;
  --rail2-text-muted: #475569;
  --rail2-text-subtle: #64748b;
  --rail2-border: #d7dfe9;
  --rail2-wash: rgba(15, 23, 42, 0.07);
  --rail2-wash-primary: rgba(29, 78, 216, 0.12);
  --rail2-wash-strong: rgba(15, 23, 42, 0.12);
  --rail2-primary: #1d4ed8;
  /* The rail's selection accent. Deeper than the dark theme's cyan, which is near-invisible
     on a light rail. */
  --rail-accent: #0891b2;
  --rail2-green: #15803d;
  /* The tone of the page the active TAB opens into — the first pixel BELOW the band. */
  --chrome-open-page: #ffffff;
  /* ONE token because THREE elements paint the header surface and they must agree to the
     pixel: the header box itself, the org-brand band fixed over the second rail, and the
     top-left corner of the first rail — which is a background layer on `.canvas`, not on any
     header element, so it cannot read a token scoped to `.topbar`. That is why it lives at
     the root. Moved here 2026-09-24 from the token block at the top of the sheet, which was
     declaring it a SECOND time and losing to this line in silence. */
  --header-bg: var(--chrome-bg);
}
/* Stated per theme — `html[data-theme]` is (0,1,1) against `:root`'s (0,1,0), so a value left
   to the :root line would win the wrong way on a dark page. */
html[data-theme='dark'] {
  /* The same ground as the light theme's — see the note there. */
  --chrome-bg: var(--zone-content);
  --chrome-divider: rgba(255, 255, 255, 0.10);
  --chrome-text: #e2e8f0;
  --chrome-text-muted: #b9c5d4;
  --chrome-text-subtle: #94a3b8;
  --chrome-bg-muted: rgba(255, 255, 255, 0.10);
  --chrome-wash: rgba(255, 255, 255, 0.10);
  --chrome-wash-strong: rgba(255, 255, 255, 0.16);
  --chrome-border: #475569;
  --chrome-border-muted: rgba(255, 255, 255, 0.10);
  --chrome-primary: #8ab4f8;
  --chrome-wash-primary: rgba(138, 180, 248, 0.16);
  --chrome-orgbrand: #a78bfa;
  --rail2-bg: #172032;
  --rail2-text: #e2e8f0;
  --rail2-text-muted: #b9c5d4;
  --rail2-text-subtle: #94a3b8;
  --rail2-border: rgba(226, 232, 240, 0.16);
  --rail2-wash: rgba(255, 255, 255, 0.09);
  --rail2-wash-primary: rgba(138, 180, 248, 0.18);
  --rail2-wash-strong: rgba(255, 255, 255, 0.15);
  --rail2-primary: #8ab4f8;
  --rail-accent: #22d3ee;
  --rail2-green: #4ade80;
  --chrome-open-page: #0f172a;
  --header-bg: var(--chrome-bg);
}

/* The header's surface and its ink. border-bottom: 0, not transparent (2026-08-20, user
   direction): the base rule's 1px border is inside the band's border-box, the chrome
   background paints THROUGH a transparent border (background-clip defaults to border-box),
   and the active tab — a flex child — stops at the CONTENT box. That 1px of chrome under the
   tab is exactly the "bottom edge" that kept the tab from opening into the page. */
.topbar {
  background: var(--header-bg);
  border-bottom: 0;
  --text: var(--chrome-text);
  --text-muted: var(--chrome-text-muted);
  --text-subtle: var(--chrome-text-subtle);
  --text-faint: var(--chrome-text-subtle);
  --bg-muted: var(--chrome-bg-muted);
  --bg-hover: var(--chrome-wash);
  --wash-ink: var(--chrome-wash);
  --wash-ink-strong: var(--chrome-wash-strong);
  --border: var(--chrome-border);
  --border-muted: var(--chrome-border-muted);
  --primary: var(--chrome-primary);
  --wash-primary: var(--chrome-wash-primary);
  --orgbrand-ink: var(--chrome-orgbrand);
  --zone-divider: var(--chrome-divider);
}.topbar .tb-menu,
.topbar .app-launcher-panel,
.topbar .topbar-search-wrap,
.canvas .tab-content.active .app-shell__actions [role="menu"],
.canvas .tab-content.active .app-shell__actions .wk-newmenu__pop,
.canvas .tab-content.active .app-shell__actions .wk-secmenu__pop,
.canvas .tab-content.active .app-shell__actions .wk-menu,
.canvas .tab-content.active .app-shell__actions .wk-dropdown,
.canvas .tab-content.active .app-shell__actions .menu-pop,
.canvas .tab-content.active .app-shell__actions .tb-menu,
.canvas .tab-content.active .wk-hdr-actions [role="menu"],
.canvas .tab-content.active .wk-hdr-actions .wk-newmenu__pop,
.canvas .tab-content.active .wk-hdr-actions .wk-secmenu__pop,
.canvas .tab-content.active .wk-hdr-actions .wk-menu,
.canvas .tab-content.active .wk-hdr-actions .wk-dropdown,
.canvas .tab-content.active .wk-hdr-actions .menu-pop,
.canvas .tab-content.active .wk-hdr-actions .tb-menu{
  --text: var(--page-text);
  --text-muted: var(--page-text-muted);
  --text-subtle: var(--page-text-subtle);
  --text-faint: var(--page-text-faint);
  --bg-muted: var(--page-bg-muted);
  --bg-hover: var(--page-bg-hover);
  --wash-ink: var(--page-wash-ink);
  --wash-ink-strong: var(--page-wash-ink-strong);
  --border: var(--page-border);
  --border-muted: var(--page-border-muted);
  --primary: var(--page-primary);
  --wash-primary: var(--page-wash-primary);
  --zone-divider: var(--page-border-muted);
  color: var(--text);
}

/* ── THE ONE PRINT CONTROL (ui/docPrint.js) ────────────────────────────────
   A split button: Print, plus a caret for Download PDF and the template chooser. Shared by every
   module that prints a business document, so the verb and the menu are the same everywhere —
   six hand-rolled print buttons is six different words for one action. */
.dp-wrap { position: relative; display: inline-flex; align-items: stretch; }
.dp-wrap > .btn:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.dp-wrap > .dp-caret { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; padding-inline: .45rem; }
/* The split button reads like every other action beside it (2026-08-24, user direction): the
   app-wide `.ghost-button` treatment — primary ink on a primary-tinted outline over the page's
   own surface. Stated in TOKENS, never literals, which is what makes it follow day/night; it
   used to take `.btn`'s neutral grey and sat among primary-outlined siblings looking like a
   different kind of control, and washed out in dark mode. Scoped to `.dp-wrap` so no other
   module's `.btn` moves. */
.dp-wrap > .btn { background: var(--bg-surface); color: var(--primary); border-color: color-mix(in srgb, var(--primary) 40%, var(--border)); }
.dp-wrap > .btn:hover { background: color-mix(in srgb, var(--primary) 8%, var(--bg-surface)); border-color: var(--primary); }
.dp-wrap > .btn:disabled { color: var(--text-muted); border-color: var(--border); background: var(--bg-surface); }
/* FIXED, and placed by JS (ui/docPrint.js) — never `absolute` (2026-08-24, user direction:
   "always on top, never hide under anything"). The control renders inside table cells whose
   scroll container is `overflow-x: auto`, and an overflow container CLIPS BOTH AXES: an
   absolutely-positioned menu is cut off by the box, which no z-index can lift it out of. The
   same reason the rails float their tooltips on <body> and the insights engine portals its row
   menus. `top`/`left` are written by `_placeMenu`; the z-index sits above the app's chrome
   layers (header 32, corner mark 33) so it also clears a sticky toolbar. */
.dp-menu {
  position: fixed; z-index: 4000; min-width: 190px;
  display: flex; flex-direction: column; padding: 4px;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
}
.dp-menu[hidden] { display: none; }
.dp-menu > * {
  text-align: left; border: 0; background: none; font: inherit; font-size: .84rem;
  color: var(--text); padding: 6px 9px; border-radius: 6px; cursor: pointer; text-decoration: none;
}
.dp-menu > *:hover { background: var(--bg-hover, var(--bg-muted)); }
.dp-menu > a { border-top: 1px solid var(--border); border-radius: 0; margin-top: 3px; padding-top: 8px; color: var(--text-muted); }


/* ═══ A THINNER HEADER, AND A SEARCH BOX THAT IS JUST A SEARCH BOX ═══════════════════════════
   Appended last so it wins on order without raising specificity — every rule here is a SIZE,
   never a colour. The header's own surface rule ("THE TOP HEADER FOLLOWS THE PAGE THEME") is
   untouched, and `ui/headerTheme.spec.js` still checks that nothing here re-declares a token.

   Two changes, one reason: the header was 56px of chrome above every page, and the widest thing
   in it was a scope dropdown offering nine module names — a decision to make BEFORE typing, on a
   bar most people reach by pressing the key and typing a name. The scope filter belongs on the
   results page, where you can see what you are narrowing. */

/* 34: the 28px band with 2px more air above and below its 24px controls — the controls did not
   grow, the margins did, which is what keeps the extra height reading as breathing room rather
   than as bigger chrome. Every consumer (rails, body, corner mark, the module-header band's
   `top`) measures the token, so the number changes HERE and nowhere else. */
:root { --topbar-h: 44px; }   /* 44, up from 38 (2026-08-20, user direction): 30px controls with 7px of air a side */

/* Half the band. Everything inside has to come down with it or the controls clip: the header is
   `align-items: center` with `min-height`, so an oversized child silently grows the bar back and
   the number above becomes decorative.

   Written `header.topbar`, NOT `.topbar`. The inversion block above is found by
   `ui/headerTheme.spec.js` as the rules whose selector is exactly `.topbar` — a bare
   `.topbar { }` appended here becomes that rule, and the guard then reads a block with no
   palette in it — which, while the header still inverted, reported the whole inversion as
   missing. The inversion is gone but the separation is worth keeping: a bare `.topbar` block is
   the header describing its own SURFACE, and sizes go on `header.topbar`, the same element one
   specificity step up. Two selectors is what makes either safe to edit alone. */
header.topbar {
  gap: 4px;
  padding: 0 16px 0 4px;
}
/* One cap for every interactive child rather than a rule per control — the header holds a dozen
   of them (report · appearance · AI · upgrade · alerts · waffle · profile · hamburger), and a
   list that has to be kept in step with the markup is a list that will not be. */
.topbar > button,
.topbar > a:not(.tb-corner-home),
.topbar .topbar-iconbtn,
.topbar .tb-hamburger,
.topbar .topbar-toggle,
.topbar .topbar-help,
.topbar .topbar-panel-toggle {
  height: 30px;
  min-height: 0;
  padding-top: 0;
  padding-bottom: 0;
}
.topbar .topbar-iconbtn { width: 30px; }
.topbar .topbar-iconbtn svg,
.topbar .topbar-iconbtn__ico,
.topbar .tb-hamburger svg { width: 21px; height: 21px; }
.topbar .tb-avatar { width: 28px; height: 28px; font-size: .78rem; }
/* The org brand rode at 18px/29px to fill a 56px band; sized to the 44px one now. */
.tb-orgbrand { font-size: 15px; }

/* The search box: icon, then text. No scope select, no trailing button, no pill chrome fighting
   the bar it sits in. */
.topbar-search {
  height: 28px;
  max-width: 460px;
  background: var(--bg-muted);
}
.topbar-search__input {
  font-size: .76rem;
  padding: 0 10px 0 0;   /* the icon owns the left inset */
}
/* The submit button IS the leading icon now, so it leads: `order` rather than moving it in the
   markup would have left the tab order wrong — it is first in the DOM as well. */
.topbar-search__go { padding: 0 6px 0 9px; }
.topbar-search__go svg { width: 13px; height: 13px; }
/* Gone from the markup; these keep a cached page from painting a stray fragment of it. */
.topbar-search__scope { display: none; }

/* ── THINNER STILL: the padding, the type and the icons ──────────────────────────────────────
   The band came down to 28px and the CONTENTS were still sized for 56 — a control can only be
   as short as its own padding lets it, so tightening the box without tightening what is in it
   just moves the overflow. Type down a step with it: 0.8rem in a 22px pill reads as text
   crammed into a box, and the fix for that is not a taller box. */
/* Icons evenly spaced across the band — ~20px apart on a wide screen, closing to 4px as the
   window narrows so the cluster never crowds the centred org brand. `clamp` rather than a media
   query: the crowding is continuous, and a step change at one width leaves the layout at its
   worst just before it. */
/* TIGHTER (2026-09-14, user direction: "in header right side icons too, reduce the gap a little").
   The ceiling was 20px and a 1440px window sat at 15.8 — eight controls spread across 330px, which
   reads as eight separate one-off actions rather than one cluster. Halved to a 4-10px clamp; the
   right PADDING is untouched, because that is what keeps the avatar off the window edge and is a
   different measurement. Still a clamp, not a fixed number: the band has to survive a narrow
   window, and that is the half of this rule that was always right. */
header.topbar { gap: clamp(3px, 0.55vw, 10px); padding: 0 clamp(16px, 2vw, 30px) 0 2px; }
/* EVERY control in the band, by POSITION rather than by class name.
   The notification bell, the waffle and the profile avatar sit inside wrapper divs
   (`.tb-control`, `.tb-applauncher-wrap`), so they are not `.topbar > button` and they carry
   none of the classes the first list enumerates — which is why they stayed full size while
   everything around them shrank. That is the failure the comment on that list predicted, so
   this matches one level down instead: `.topbar > div > button` reaches the wrapped controls
   and the hamburger, and cannot reach the menus, which are deeper (`.tb-control > .tb-menu >
   button`). Shrinking a dropdown's own buttons would be the obvious next bug. */
.topbar > button,
.topbar > a:not(.tb-corner-home),
.topbar > div > button {
  box-sizing: border-box;
  width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0;
  margin: 0;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.topbar > button svg,
.topbar > div > button svg,
.topbar .topbar-iconbtn__ico,
.topbar .topbar-iconbtn__ico svg { width: 21px; height: 21px; }
/* The bell is a glyph, not an SVG — it sizes off the font. */
/* The bell and the profile button, BY NAME. The positional rule above (`.topbar > div >
   button`, (0,1,2)) reaches them in the DOM but LOSES the cascade: the Gmail-skin block sizes
   header icons to 40x40 circles through `.topbar :is(.topbar-iconbtn, .tb-notif-button, …)`,
   and `:is()` weighs its most specific argument — (0,2,0) beats (0,1,2). Every other icon was
   rescued by a LATER (0,2,0) rule that happens to name its class (`.topbar .topbar-iconbtn`,
   `.topbar .tb-applauncher`); these two were the only ones nobody re-named, which is exactly
   why their hover circles sat 40px wide in a row of 24s. */
.topbar .tb-notif-button,
.topbar .tb-profile-button {
  width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0;
  margin: 0;
  border: 0;
  border-radius: 8px;
}
.topbar .tb-notif-button { font-size: 18px; line-height: 1; }
.topbar .tb-avatar { width: 28px; height: 28px; font-size: .78rem; }
/* The unread dot rode on a 32px button; on a 21px one it drifts off the corner. */
.topbar .tb-notif-badge { transform: scale(.8); transform-origin: top right; }
.topbar .topbar-search-wrap { padding: 0 6px; }
.tb-orgbrand { font-size: 15px; gap: 5px; }

/* 18px, and BORDER-BOX. There is no global `* { box-sizing: border-box }` in this sheet, so the
   pill's 1px transparent border was being ADDED to its height — a 20px box measuring 22px, which
   is why it kept reading thicker than the number said. The border earns nothing here (the block
   above already states "no border line — only the caret shows on focus"), so it goes. */
.topbar-search {
  box-sizing: border-box;
  border: 0;
  height: 18px;
  max-width: 400px;
}
.topbar-search__input { font-size: .7rem; padding: 0 8px 0 0; }
.topbar-search__go { padding: 0 4px 0 7px; }
.topbar-search__go svg { width: 12px; height: 12px; }
.topbar-search__suggest { top: calc(100% + 4px); }

/* ── SEARCH IS AN ICON, AND ITS BAR IS THE POPUP ─────────────────────────────────────────────
   It was a bar permanently across the middle of the header: the widest thing in the chrome, for
   a control most people reach by pressing a key. As an icon it reads like the seven beside it
   and costs nothing until it is wanted.

   The panel opens at the TOP-CENTRE of the page over a dimmed backdrop (user direction) — the
   Spotlight/command-palette shape — rather than hung off the icon's corner of the band. It
   lives in the header's DOM and opens over the page like the profile and launcher menus, so it
   inherits the page's palette through the header. */
.tb-searchbtn { position: relative; display: inline-flex; align-items: center; }
/* Scoped through `.tb-searchbtn`, which is (0,3,0): two earlier rules style the wrap as a flex
   item at `.topbar .topbar-search-wrap` (0,2,0), from when it was a bar in the middle of the
   band, and a bare `.topbar-search-wrap` here would lose its padding to both of them. */
.topbar .tb-searchbtn .topbar-search-wrap {
  /* Top-CENTRE of the page (user direction), not hung off the icon: a lookup panel in the
     middle of the screen over a dimmed page — the Spotlight/command-palette shape. FIXED, so
     the centre is the viewport's, not the icon cluster's corner of the band.
     ONE surface, deliberately: padding 0 + overflow hidden, the field and the results run
     edge-to-edge inside it. The old in-band bar rules still reach in here (grey pill, 24px
     radius, Gmail focus shadow) and every one re-zeroed below — each survivor is one more
     visual "layer" inside what should read as a single flat panel. */
  position: fixed;
  top: calc(var(--chrome-h, 34px) + 10vh);
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  width: min(620px, 92vw);
  padding: 0;
  overflow: hidden;
  display: block;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  /* Above its own backdrop (1000); the pair sits over every band, rail and popover. */
  z-index: 1001;
}
/* ── ORBIT'S SELL PAGES LINE UP UNDER THE TOOLBAR ───────────────────────────────────────────
   The toolbar's bottom divider bleeds to the page edges (`.lk-bar__row` pulls out by
   `--page-pad-x` and pads the same value back), so whatever sits under it has to start at the
   same left edge and run the same width — on every page, or the pages look misaligned against
   each other even though each is internally consistent. Three things broke that under Sell:

   (1) Accounts and Contacts carry an ALWAYS-PRESENT bulk-action strip (`#orbit-*-bulk`) that is
       EMPTY until rows are selected — but `.orbit-tools-wrap` keeps its `margin-bottom: .5rem`
       while empty, so those two pages opened with 8px of dead space under the divider that
       Deals and Leads do not have. An empty box must take no room.
   (2) The list CARD is a flex ITEM on Deals (`.orbit-board` is a flex row) and a block on
       Leads (`.orbit-list-mode`), so one shrink-to-fits its content and the other fills the
       column — the same table, two widths, two right edges under the same full-bleed divider.
   (3) `.orbit-list-wrap` never declared `box-sizing`, and this sheet has no global one, so its
       1px border was width nobody typed. */
#orbit-tab .orbit-tools-wrap:empty { display: none; margin: 0; }
/* In LIST mode the board is a block and its card fills the column — Deals, Leads and the
   Accounts/Contacts list all start and end on the same two lines. */
#orbit-tab .orbit-board:has(.orbit-list-wrap),
#orbit-tab .orbit-stage-board:has(.orbit-list-wrap) { display: block; }
#orbit-tab [data-deals-grid] { width: 100%; }
#orbit-tab .orbit-list-wrap { width: 100%; box-sizing: border-box; }

/* ── ORBIT'S SELL LISTS READ AS ONE TABLE ────────────────────────────────────────────────────
   Under Sell, Deals and Leads render a real `.orbit-table` (a bordered 10px card, hairline
   dividers, hover on --bg-hover) while Accounts and Contacts render `.orbit-row` BUTTONS — a
   stack of separately-bordered 8px cards with their own hover and a blue "active" fill. Same
   module, same rail, same kind of list, three different-looking pages.

   The rows stay BUTTONS (their click, their checkbox, their bulk-select and every delegated
   handler key off that markup — rewriting them into a table for a visual match would be a
   rewrite of the behaviour to change a border). What changes is only the treatment: the LIST
   becomes the card, and each row becomes a row inside it — exactly what `.orbit-table` draws.

   Scoped to `#orbit-tab` because the partial's own `.orbit-row` rules are inline in
   partials/orbit.html at (0,1,0); this is (0,1,1) and appended last, so it wins on both counts
   without `!important`. */
#orbit-tab .orbit-list {
  gap: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  overflow: hidden;
}
#orbit-tab .orbit-list:empty { display: none; }
#orbit-tab .orbit-list .orbit-li-wrap {
  gap: .5rem;
  padding: 0 10px;
  border-bottom: 1px solid var(--border-muted);
}
#orbit-tab .orbit-list .orbit-li-wrap:last-child { border-bottom: 0; }
#orbit-tab .orbit-list .orbit-row {
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 7px 2px;
  font-size: 12.5px;
}
#orbit-tab .orbit-list .orbit-li-wrap:hover { background: var(--bg-hover); }
#orbit-tab .orbit-list .orbit-row:hover { background: transparent; }
/* The open record is marked by a LEADING accent, not a filled blue card — a table row that
   turns into a tinted box is the thing that made these pages look unlike Deals. */
#orbit-tab .orbit-list .orbit-row.is-active {
  border: 0;
  background: transparent;
  box-shadow: inset 3px 0 0 var(--primary);
}
#orbit-tab .orbit-list .orbit-row-name { font-weight: 600; font-size: 12.5px; }
#orbit-tab .orbit-list .orbit-row-sub { font-size: 11.5px; }
/* The card is a card only when it holds ROWS. Unlike Deals and Leads — whose empty state is a
   `<td colspan>` INSIDE the table, so the card is always a table — Accounts and Contacts render
   their loading line, their empty state (with its CTA) and their error into this same
   container. A border around "Loading…" is a box around a sentence, and an empty state framed
   like a table is a table with no columns. Those states render plain, exactly as they did. */
#orbit-tab .orbit-list:not(:has(> .orbit-li-wrap)) {
  border: 0;
  background: transparent;
  border-radius: 0;
  overflow: visible;
}
/* The truncation note is a CAPTION about the list, not a row in it — but it is prepended
   INSIDE the container, so inside the card it has to read as one: full-bleed, on the hairline,
   with the `<p>` margins that would otherwise push the first row down taken off. */
#orbit-tab .orbit-list > .orbit-list-note {
  margin: 0;
  padding: 7px 12px;
  border-bottom: 1px solid var(--border-muted);
}

/* ── "+ NEW" RIDES THE BREADCRUMB LINE ───────────────────────────────────────────────────────
   `controllers/rails.js::promoteNewButton` MOVES the list toolbar's primary action up into the
   shell bar's right cluster, so the page's most important button sits on the module's own
   identity line instead of mid-page in a second row. Here it stops being a stacked rail-style
   tile and becomes a COMPACT PILL: icon and label on ONE line at toolbar type size, matched to
   the 30px controls beside it.

   Written out longhand and DOUBLED: the tile shape comes from `.wk-iconbtn:has(> .wk-iconbtn__cap)`
   — `:has()` is (0,2,0) by spec and ZERO in WebKit, so a (0,2,0) override here would win in one
   engine and tie in the other. (0,3,0) is unambiguous in both. */
.app-shell__bar-right .lk-new-promoted.lk-new-promoted {
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: auto;
  min-width: 0;
  height: 26px;
  padding: 0 11px 0 9px;
  border-radius: 13px;
  font-size: .72rem;
  font-weight: 600;
  line-height: 1;
}
/* A PROMOTED WRAPPER PASSES THE PILL ON TO ITS BUTTON (2026-09-15, user report: "button is flat
   and text in single line, not in two lines").
   A `+ New` that opens a MENU promotes its wrapper, because `promoteNewButton` only moves a direct
   child of the toolbar's right cluster. The pill rules above then styled the WRAPPER — a plain
   div — while the `wk-iconbtn` inside it kept the rail tile's icon-over-caption COLUMN, so the
   crumb row showed a two-line tile where the reference has a flat one-line pill. */
.app-shell__bar-right .lk-new-promoted.lk-new-promoted > .wk-iconbtn {
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  gap: 5px;
  width: auto;
  min-width: 0;
  height: 26px;
  padding: 0 11px 0 9px;
  border-radius: 13px;
  font-size: .72rem;
  font-weight: 600;
  line-height: 1;
}
/* …and the wrapper itself stops being a button-shaped box: it is only a positioning anchor for
   the menu now. */
.app-shell__bar-right .lk-new-promoted.ws-menu {
  height: auto; padding: 0; border: 0; background: none; border-radius: 0;
}
.app-shell__bar-right .lk-new-promoted.lk-new-promoted .hicon-wrap,
.app-shell__bar-right .lk-new-promoted.lk-new-promoted .hicon {
  width: 13px;
  height: 13px;
  flex: none;
}
/* A module-wide "+ New" MENU (Orbit's, whose dropdown lists every record the module creates)
   takes the SAME pill on the same line — one + on the page, never two. Its old home was the
   actions cluster with a `position: fixed` pin at a hardcoded top/right; moving it here makes
   those selectors stop matching, which is the pin's whole removal. The popup needs no rule: the
   wrapper is `position: relative` and the pop already hangs `top: 100% + 6px; right: 0`. */
.app-shell__bar-right .lk-newmenu-promoted.lk-newmenu-promoted { order: -1; flex: none; }
.app-shell__bar-right .lk-newmenu-promoted.lk-newmenu-promoted > .wk-iconbtn--primary {
  flex-direction: row;
  align-items: center;
  gap: 5px;
  width: auto;
  min-width: 0;
  height: 26px;
  padding: 0 11px 0 9px;
  border-radius: 13px;
  font-size: .72rem;
  font-weight: 600;
  line-height: 1;
}
.app-shell__bar-right .lk-newmenu-promoted.lk-newmenu-promoted .hicon-wrap,
.app-shell__bar-right .lk-newmenu-promoted.lk-newmenu-promoted .hicon {
  width: 13px;
  height: 13px;
  flex: none;
}
.app-shell__bar-right .lk-newmenu-promoted.lk-newmenu-promoted .wk-iconbtn__cap {
  font-size: .72rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  color: inherit;
}

/* The caption is the LABEL now, not a tile's 8px under-text. */
.app-shell__bar-right .lk-new-promoted.lk-new-promoted .wk-iconbtn__cap {
  font-size: .72rem;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
  color: inherit;
}

/* ── THE HOME BOARD'S KPI STRIP (feed:kpis) ─────────────────────────────────────────────────
   The counts row the old Today page led with, as a dashboard widget: five figures, read left
   to right, zeros muted rather than missing (a hidden zero reads as "not measured"). */
.iq-kpistrip { display: flex; align-items: stretch; gap: 10px; height: 100%; }
.iq-kpistrip__cell {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 2px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-surface); padding: 8px 6px;
}
.iq-kpistrip__n { font-size: 1.35rem; font-weight: 800; line-height: 1; color: var(--text); }
.iq-kpistrip__l { font-size: .68rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.iq-kpistrip__cell.is-zero .iq-kpistrip__n { color: var(--text-faint); font-weight: 700; }

/* The dulled page behind the panel — created/removed with the panel by _openSearch
   (features/search/header.js), the drill-backdrop pattern: REMOVED on close, never hidden, so
   no stale full-screen layer sits over the page eating clicks. Clicking it closes the search. */
.topbar-search-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.45);
  /* BELOW the header band (z 32), never above it: the panel lives INSIDE the header's stacking
     context, so its own z can never leave that layer — a backdrop above 32 dims the panel it
     exists to frame, which is exactly the "search is not clearly visible" bug. 31 covers the
     page, the rails (10) and every page popover; the band and its panel stay crisp. */
  z-index: 31;
}
/* Closed is the default now — the opposite of what this rule used to say, when `is-open` was
   hardcoded in the markup and the toggle was hidden. */
.topbar .tb-searchbtn .topbar-search-wrap:not(.is-open) { display: none; }
.topbar .tb-searchbtn .topbar-search-wrap.is-open { display: block; }

/* Inside the panel the field is a ROW of the panel, not a box of its own: transparent, square,
   full-width — the pill fill, the 24px radius and the focus shadow are the in-band bar's, and
   any of them surviving here puts a second surface inside the panel. */
.topbar .tb-searchbtn .topbar-search {
  box-sizing: border-box;
  border: 0;
  border-radius: 0;
  height: 46px;
  max-width: none;
  width: 100%;
  background: transparent;
  box-shadow: none;
}
.topbar .tb-searchbtn .topbar-search:hover,
.topbar .tb-searchbtn .topbar-search:focus-within,
.topbar .tb-searchbtn .topbar-search:focus-within:hover {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
/* A hairline between the field and the results — only while there ARE results, or the panel's
   last pixel is a line above nothing. */
.topbar .tb-searchbtn .topbar-search-wrap:has(.topbar-search__suggest:not([hidden])) .topbar-search {
  border-bottom: 1px solid var(--border);
  border-radius: 0;
}
/* The app-global form-input rule (`input:not([type=checkbox])…`, (0,2,1)) BEATS the component's
   own low-specificity resets — inside the panel the input drew its own bordered, rounded,
   surface-filled box floating over the row. Re-zeroed here at (0,3,0)+, focus ring included. */
.topbar .tb-searchbtn .topbar-search__input {
  font-size: .95rem;
  line-height: 1;
  padding: 0 12px 0 0;
  height: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  appearance: none;
}
.topbar .tb-searchbtn .topbar-search__input:focus,
.topbar .tb-searchbtn .topbar-search__input:focus-visible {
  border: 0;
  outline: none;
  box-shadow: none;
  background: transparent;
}
.topbar .tb-searchbtn .topbar-search__go { padding: 0 10px 0 14px; }
.topbar .tb-searchbtn .topbar-search__go svg { width: 15px; height: 15px; }

/* Results live IN the panel — in flow under the field, not as a second popover floating beside
   it. That is the whole point: a query typed in the header is a lookup, and one that navigates
   you away from what you were doing is what people close and retype somewhere else. */
.topbar .tb-searchbtn .topbar-search__suggest {
  position: static;
  transform: none;
  width: auto;
  margin: 0;
  padding: 6px;
  max-height: min(60vh, 420px);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: transparent;
}
.topbar .tb-searchbtn .topbar-search__suggest[hidden] { display: none; }

/* ── THE HEADER'S THREE ZONES ────────────────────────────────────────────────────────────────
   [ wordmark · hamburger · org name ] ————— [ nav · waffle, centred ] ————— [ icons ]

   The middle was the search bar, and it was what pushed the icon cluster to the right edge:
   `flex: 1 1 auto` on the wrap. Turning search into a popup took that away, and with nothing
   flexible left in the row every icon collapsed against the left. The lead zone takes the
   growth now, so the icons sit right whatever else changes.

   The centre CLUSTER (nav + waffle) is CENTRED ON THE HEADER rather than laid out in the middle
   of the flow, for the same reason the search box was when it lived there: the two ends are
   different widths — a wordmark, a hamburger and the org name against seven icons — so anything
   centred by flex is centred on nothing in particular and drifts when either end changes. */
.canvas:has(.topbar) .topbar .tb-lead {
  flex: 1 1 auto;
  min-width: 0;
  justify-content: flex-start;
}
/* ── THE ORG BRAND ───────────────────────────────────────────────────────────────────────────
   In the LEAD zone beside the hamburger (2026-08-20, user direction), name shown.

   The single authority on this now. It used to be five blocks: a base `display: none`, one that
   showed it only while the search bar was collapsed (from when it lived inside the search wrap),
   two that fixed it into a rail's top band, and two more that hid its NAME unless a rail was in
   a particular state. Everything a reader could see about the brand depended on which rail was
   open, which is why it kept disappearing.

   `!important` on `display` because the legacy base rule still hides it and is only reachable
   by out-specifying — the honest alternative to a sixth override chain. */
/* The CLUSTER is what is centred — Central's rail items, then the waffle. The brand does not
   ride here any more; centring the cluster as one unit keeps the nav and the launcher from
   fighting the end clusters for leftover space, which is the drift this block exists to
   prevent. */
.canvas:has(.topbar) .topbar .tb-center {
  position: absolute;
  left: 50%;
  top: 0;
  height: 100%;
  /* Plain centring. This carried `+ rail1-w / 2` to cancel the band's negative pull — 50% of a box
     that started 58px off-screen was not 50% of the screen. There is no pull now, so the
     correction would be the thing knocking it off centre. */
  transform: translateX(-50%);
  max-width: 52vw;
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 1;
}
/* THE ORG ANSWERS "WHOSE DATA IS THIS" AT A GLANCE — every page in this app shows one org's
   data and nothing else on screen says WHOSE, so the name must be readable where the eye
   starts the row, not found.

   The trial/grace chip goes on the NAME, never on the container. `.tb-orgbrand--trial #tb-orgbrand-name`
   paints the name itself with the orange trial/grace chip, and that rule carries an id — a
   background here would be a second one fighting it, and the one a reader must not miss is the
   one saying their trial is ending. On the container the two stack: chip inside pill.

   Sizes are the band's ceiling, not a preference. `.topbar` is `height: var(--topbar-h)` with
   `min-height: 0`, so this band does NOT grow to its content — 22px of mark plus 3px of padding
   a side is 28px in a 34px band, and there is no global `border-box` here, so every px of
   padding and border is height nobody typed. */
/* ── WHERE IT SITS: the LEAD ZONE, beside the hamburger ──────────────────────────────────────
   (2026-08-20, user direction.) It led the centre cluster for a day; next to the hamburger it
   anchors the row the eye starts on — [app] · [menu] · [whose data] — and the centre cluster
   is left to the destinations. It is a plain flex child of `.tb-lead` (gap 8px), so nothing is
   `position: absolute` and there is no containing-block dependency to undo on a narrow screen. */
.canvas:has(.topbar) .topbar .tb-lead > .tb-orgbrand.is-shown {
  position: static;
  max-width: 22vw;
  /* The lead zone's own 8px gap separates it from the hamburger; the 2px left nudge keeps the
     name's ink from starting flush against the button's hover square. */
  margin: 0 8px 0 2px;
  /* NO CONTAINER (2026-08-19, customer direction). It was a tinted capsule with a ring, and a
     rounded tinted capsule is the silhouette this app already spends on buttons and status
     chips — so the one thing in the header that is deliberately inert wore the costume of a
     control. The emphasis is carried by INK and WEIGHT instead, which nothing else here can be
     confused with, and it costs no geometry in a 34px band.

     `cursor: default` stays, and there is deliberately no hover and no underline: the brand is a
     LABEL (2026-08-18) — it says whose data you are looking at and goes nowhere. Bold coloured
     text is the one treatment here that could be read as a link, so every other link affordance
     is withheld on purpose. */
  padding: 0;
  background: none;
  display: inline-flex !important;
  align-items: center;
  flex: none;
  cursor: default;
}
/* A STEP BIGGER THAN THE WORDMARK, IN COLOUR — 13px/700 against the mark's 11px/600
   (2026-08-20, user direction: "bigger and colourful"). The two spent a day as an equal pair;
   the customer's name now leads the app's, which is the right hierarchy — every page shows one
   org's data, and the corner mark is deliberately a watermark. Capped well short of the
   15px/800 this once was, which made it the loudest thing in the band by some margin.
   `ui/headerCompact.spec.js` asserts the ORDER (name above mark, under the cap) rather than
   pinning either number. */
.canvas:has(.topbar) .topbar .tb-lead .tb-orgbrand__name {
  display: block !important;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  /* VIOLET, per theme (2026-08-20, user direction) — `--orgbrand-ink`, stated beside
     `--zone-rail` in both theme blocks because AA is measured against that band: #6d28d9 is
     6.67:1 on the light rail, #a78bfa 5.92:1 on the dark one. Deliberately NOT `--primary`:
     the link blue on the one inert label in the header would dress it as a control, and blue
     already failed AA on this band once (4.23:1). The trial/grace chip still wins — it paints
     the name white on orange through an id-carrying rule. */
  color: var(--orgbrand-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Narrow: the two clusters close in on the middle, and a centred brand would sit under them.
   Hidden rather than shuffled — the org's name is not what you are trying to read on a phone,
   and an overlapping brand is worse than an absent one. */
/* Narrow: back into the FLOW between the clusters, name hidden, nav and waffle kept — the
   destinations and the launcher are what a phone user is here for, the org's name is not. An
   absolutely-centred cluster would sit under the end clusters as they close in. */
@media (max-width: 900px) {
  .canvas:has(.topbar) .topbar .tb-center {
    position: static;
    transform: none;
    max-width: none;
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;
  }
  .canvas:has(.topbar) .topbar .tb-lead .tb-orgbrand__name { display: none !important; }
  /* The name IS the brand now, so hiding it has to take the container with it — an empty
     inline-flex box still eats the lead zone's gap. Both halves are stated: the name rule
     above is what the wide-screen `display: block` is traded against, and this is what stops
     an empty span shipping to every phone. On a phone the lead zone is the hamburger's; the
     org's name is not what you are trying to read there. */
  .canvas:has(.topbar) .topbar .tb-lead > .tb-orgbrand.is-shown {
    display: none !important;
    margin: 0;
  }
}

/* ── THE BRAND IS TEXT ───────────────────────────────────────────────────────────────────────
   It was a 32px SVG badge painted as a background layer on `.canvas`, with this anchor laid
   over it as a transparent click target — so the thing you saw and the thing you clicked were
   two different objects, and the badge overflowed the moment the band came down to 28px.

   Real text in the markup, not a `content:` string: a wordmark is the app's NAME, and a name
   that lives in a stylesheet cannot be selected, copied, translated or read out. The AI in the
   middle takes the accent, the same treatment the sign-in card's mark already uses.

   It has 58px of rail-corner to sit in, which is what sets the size — 12px with the tracking
   pulled in (~52px of word; 13 touches the divider). Sized to the corner rather than the corner to the wordmark, because that column
   width is the first rail's and every icon in it is aligned to it. */
.tb-corner-home {
  text-decoration: none;
  color: var(--text);
}
/* (The narrow-width override that dropped the wordmark is gone with the wordmark itself: the
   corner is the mark alone at every width now.) */
/* The mark leads the lockup; the wordmark follows it. Sized to the band, not to the corner. */
.tb-corner-logo { width: 26px; height: 26px; display: block; flex: 0 0 26px; }
.tb-corner-home:hover .tb-corner-logo { transform: scale(1.06); }

/* ── THE MARK ON A DARK GROUND ──────────────────────────────────────────────────────────────
   2026-09-19. The logo is one flat navy (#03356E, sampled from the artwork) with everything
   else knocked out transparent — which reads beautifully on the light theme's #f1f3f4 and is
   very nearly INVISIBLE on the dark theme's #0f172a: navy on near-black is about 1.2:1.

   THE ARTWORK IS NOT CHANGED — the PNG shipped is the supplied logo, untouched, and this is the
   dark-mode treatment browsers and design systems apply to any monochrome mark: lift its
   lightness where the ground is dark. `brightness(0) invert(1)` would flatten it to pure white;
   this keeps it blue by lifting rather than replacing, so it still reads as the brand's colour.
   Delete these two rules and the mark renders exactly as supplied in both themes — dark included,
   where it will not be visible. */
html[data-theme='dark'] .brand-logo,
html[data-theme='dark'] .tb-corner-logo {
  filter: brightness(2.6) saturate(1.15);
}

/* (The rail-foot brand is DELETED, 2026-09-14, user direction: the wordmark is back in the
   top-left lockup beside the mark. It had been pinned to `--rail1-w` — the retired first rail's
   58px — at z-index 30, which put it ON TOP of the navigation column that replaced that rail. A
   fixed element sized to a surface that no longer exists does not disappear; it lands somewhere.) */

/* ── THE HAMBURGER'S TONGUE (2026-08-20, user direction — "just a little around it") ─────────
   Not a full-width strip: a small slate tongue wrapping the hamburger, rounded on top, square
   at the base, running into the second rail below — the toggle visibly grows out of the thing
   it toggles. z -1 inside the band's stacking context (above the navy, below the button), and
   gated by the rail's own body classes so a rail-less page shows no tongue. Geometry: the
   hamburger box sits at rail1-w + 12px (the rail items' start line); the tongue wraps it with
   4px of air a side. */
.topbar::before {
  content: '';
  position: absolute;
  /* A contained chip behind the button — same colour as the rail it toggles, but no longer
     RUNNING into it (2026-08-20 follow-up: the connection went, the colour kinship stays). */
  left: calc(var(--brand-w) + 10px);
  top: 7px;
  bottom: 7px;
  width: 0;
  background: var(--rail2-bg);
  border-radius: 8px;
  z-index: -1;
}/* The hamburger's hover is a plain square chip on the tongue — the generic wrapped-control cap
   gives it an 8px radius box; restated by class so nothing re-shapes it into a pill. */
.topbar .tb-hamburger { width: 30px; height: 30px; border-radius: 6px; }
.topbar .tb-hamburger:hover { background: var(--chrome-wash); color: var(--chrome-text); }
/* (The SMAIVIZ WORDMARK is deleted, 2026-09-14, user direction: "remove smaiviz word after logo".
   The corner is the mark alone. Its styling goes with it rather than being left behind — a rule
   for an element nobody renders reads as covered, and would silently re-style the word the day
   anybody put it back. The reasoning it carried is not lost: that the ORG's name leads the APP's
   is recorded against `.tb-orgbrand`, which is where it now has to live.) */






/* ═══ THE SUB-HEADER ═════════════════════════════════════════════════════════════════════════
   A second band under the app header. The waffle lives here: switching module is the most-used
   control in the chrome, and in the top bar it was the ninth icon in a row of nine — the same
   size and shape as eight one-off account actions, so the one thing people reach for constantly
   looked like the things they touch once a month.

   It declares no palette of its own — nor does the header above it any more (see "THE TOP
   HEADER FOLLOWS THE PAGE THEME"). Both take the page's, which is what keeps two bands reading
   as two bands rather than as one thick header of a different colour.

   Sticky under the header, not fixed: it shares the rail shift with `.topbar`, so it starts
   where the module body starts instead of running under the rails. */

/* The second band is DELETED for good — the breadcrumb rides the module toolbar's left cluster
   now (ui/appShell.js: one row, crumbs left, every module control right). The token went WITH
   its consumers in the same edit: a deleted declaration with surviving `var(--subbar-h, 30px)`
   consumers means every FALLBACK quietly re-adds a band that does not exist — the trap this
   sheet has now met from three directions. `--chrome-h` collapses to the one band left. */

/* The band itself is gone — its only tenant (the waffle) lives in the header's centre cluster
   beside the Central rail items, and a 30px stripe with nothing in it is not chrome. The token
   above is 0 so the geometry below the header closed up without touching any other rule. */

/* THE LAUNCHER IS ONE OF THE ICONS NOW — same 30px box, same 21px glyph as every control beside
   it (2026-09-14, user direction: "reduce the size of waffle menu icon, similar to other icons").
   It sat in the CENTRE cluster until the waffle moved next to the account icon, and this block was
   scoped `.tb-center .tb-applauncher` — so the move silently orphaned it and the button fell back
   to the Gmail-skin block's 40x40 circle, a fifth larger than its neighbours.
   The note further up this sheet names `.tb-center .tb-applauncher` as one of the two RESCUES that
   keep these buttons off that 40px default; moving an element out of the scope that rescues it is
   exactly how it lands back on the default, and nothing errors. Scoped to `.topbar` now — (0,2,0),
   the same weight as `.topbar .topbar-iconbtn`, which is what rescues the rest. */
.topbar .tb-applauncher-wrap { position: relative; display: inline-flex; align-items: center; }
.topbar .tb-applauncher {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  color: var(--text-muted);
}
.topbar .tb-applauncher svg { width: 21px; height: 21px; }
.topbar .tb-applauncher:hover { background: var(--bg-muted); color: var(--text); }
.topbar .tb-applauncher[aria-expanded="true"] { background: var(--bg-muted); color: var(--primary); }
/* The panel hangs off the centre cluster's waffle. `left: 50%` + translate would re-centre it on
   the button; anchored left so a long grid never hangs off the window's right edge. */
.tb-center .app-launcher-panel { top: calc(100% + 8px); left: 0; right: auto; }

/* (The rails' band padding is set once, further down, where they sit at --chrome-h — the
   two-band padding that stood here died with the second band.) */


/* ═══ THE RAILS START BELOW THE CHROME ═══════════════════════════════════════════════════════
   Until now it was the other way round: both rails ran full height from `top: 0` and the HEADER
   was shifted right of them (`margin-left: var(--rail2-w)`), so the top-left corner belonged to
   the rails and the header began part-way across the screen. That shape needed three separate
   surfaces to fake one continuous band — the header box, a background layer painting the first
   rail's corner in the header tone, and the org brand relocated by `position: fixed` into the
   second rail's top strip — and each had to be kept in step by hand.

   The chrome spans the full width now and the rails hang below it, which is what every app shell
   this one is measured against does (Gmail, Jira, GitHub). One band, one tone, no corner to
   match, and the org brand stays where it is written instead of being teleported.

   `--chrome-h` is the one number: both bands, resolved once. Everything that used to add
   `--topbar-h` to something reads this instead, so adding a third band is a single edit. */

:root { --chrome-h: var(--topbar-h, 34px); }.canvas .tab-content.active .app-shell__actions,
.canvas .tab-content.active .wk-hdr-actions{
  top: var(--chrome-h);
}/* The painted rail column starts below the chrome too, and the corner square that used to carry
   the header tone is gone — that corner IS the header now. Both stacks are rewritten whole,
   because `background-size` and `background-position` are positional lists: dropping a layer
   without re-writing them silently shifts the wrong one. */
/* ONE layer now (2026-08-20, user direction: colour flows navy → slate → page, no seams).
   The divider layer went, and the size/position lists are rewritten WHOLE with it — they are
   positional, and dropping a layer without rewriting them silently shifts the wrong one. */
/* (The chrome-toned column painted under the band was the FIRST RAIL's surface. It is gone with
   the rail: `.smz-nav` paints itself from the same `--chrome-bg`, at its own width, and a second
   painted column behind it is what produced the strip beside the collapsed rail.) */

/* HISTORY, kept because it is the reasoning that was finally acted on. `.canvas:has(.topbar)` used
   to carry `padding-left: var(--rail1-w)` to reserve the FIRST rail's gutter, and the band was
   pulled back out over it rather than the padding being removed — on the argument that the padding
   reserved the gutter "for the body, the second rail, the fixed clusters and every module's own
   layout", so taking it off would move the whole app 58px left.

   With ONE navigation column that argument inverted: the body is inset by `--nav-w`, the column
   paints itself, and the 58px was reserving nothing — it was ADDING 58px to the inset, and leaving
   a painted strip behind when the column collapsed. Removing it is exactly the "move the whole app
   58px left" that was once the thing to avoid, and is now the fix. */
/* The header bands own the full width, in EVERY rail state — one rule rather than a cancel per
   state. `.canvas:has(.topbar) .topbar` is (0,3,0) and every rail-state shift written earlier is
   (0,2,1) or less, so this lands whatever the rails are doing; a per-state cancel beside it
   would be a second rule claiming the same thing and one more to keep in step.

   Descendant, never `>`: `#app-tabs` is `display: contents`, so the bands are LAID OUT as
   children of `.canvas` while remaining its grandchildren in the DOM — and a child combinator
   reads the DOM, not the layout. `.canvas:has(.topbar) > .topbar` matches nothing at all. */
.canvas:has(.topbar) .topbar {
  /* NO PULL. The negative margin existed to drag the band back over the canvas's 58px rail
     gutter; with the gutter gone the band already starts at the screen edge, and pulling it
     further would hang it off-screen by exactly that much. */
  margin-left: 0;
  /* Widened by what it was pulled left by. A negative margin MOVES a box without resizing it,
     so without this the band is 58px short on the right and stops before the scrollbar. */
  width: 100%;
  /* …and the gutter comes back as PADDING, because `.tb-corner-home` is a fixed box over that
     corner carrying the brand lockup. Reaching the edge without this puts the hamburger
     underneath it — a control that renders, looks live, and cannot be clicked. It reserves the
     LOCKUP's width (`--brand-w`), which is what is actually there; the negative margin above stays
     zero, because the canvas has no gutter of its own any more. */
  padding-left: var(--brand-w);
}

/* ── THE TWO BANDS ARE THE TOP LAYER ─────────────────────────────────────────────────────────
   Done by RAISING the band, never by lowering anything beneath it. Lowering is what broke the old
   first rail: its icon cluster was drawn TWICE — the real one inside the module's
   `.app-shell__header`, a stacking context at `z-index: 25`, and a mirror outside it, both
   `position: fixed; left: 0` in the same place, so the one you SAW was whichever won the stack.
   The mirror carried 29 and won; dropped to 23 it lost to the in-tab copy and the rail went EMPTY.
   THE ONE RAIL cannot repeat it — the original is parked thousands of pixels to the left rather
   than stacked underneath the copy — but the rule stands for anything else added to this shell:
   every pre-existing z-index stays as it was, and only the chrome moves.

   The module BODY needs nothing at all. `.canvas` is a flex column and both bands are `sticky`,
   so they occupy flow space and `.canvas-body` already begins after them; sticky is what keeps
   them on top when the body scrolls under. Only the FIXED things — outside that flow by
   definition — had to be told where the chrome ends. A `padding-top` here would double it. */
.canvas:has(.topbar) .topbar { z-index: 32; }
/* ── THE TOKENS ARE THE HEIGHTS, NOT FLOORS ──────────────────────────────────────────────────
   Both bands were `min-height`, which lets them GROW past the number — and everything below
   them is positioned at `--chrome-h`, a value computed from those numbers. So a band one or two
   pixels taller than its token (a control's line-height, a border, a label that wraps) is a rail
   sliding that far under it, with nothing in the CSS to point at: the arithmetic is right and the
   input to it is wrong.

   Stated as a real `height` so the token IS the measurement. NOT paired with `overflow: hidden`,
   tempting as that is: every popover in the chrome — the profile and notification menus, the app
   launcher, the search suggestions — lives in these bands' DOM and opens over the page, and
   clipping them to a 30px strip would be a far worse bug than the one being fixed. */
.canvas:has(.topbar) .topbar { height: var(--topbar-h, 28px); min-height: 0; }

/* The click target over the corner mark is header-height, and the header owns that corner.
   Above both bands, because it is the one thing that sits ON them. */
.tb-corner-home { height: var(--topbar-h, 28px); z-index: 33; }/* ── THE SECOND BAND IS DELETED; THE CRUMBS RIDE THE TOOLBAR ────────────────────────────────
   What stood here fixed each module's header into a 30px band under the app header. The shell
   emits no header row at all now (ui/appShell.js): ONE toolbar row, breadcrumb in its left
   cluster at toolbar type size, every module control in the right cluster — the module's old
   `toolbarLeft` lands there BEFORE its `toolbarRight`, and + New leads whatever else is in the
   row. Kriya still hand-rolls its `.wk-board-header`; it is thinned to the same row rather than
   deleted, because its breadcrumb and its board controls already share that bar. */
.app-shell__bar .wk-bc,
#kriya-tab .wk-board-header .wk-bc { font-size: .72rem; }
/* The container size alone is DECORATIVE: `.wk-bc__crumb` (14.5px) and `.wk-bc__sep` (15px) set
   their own sizes on the children, and a child's own declaration beats anything inherited. Name
   the children at (0,3,0), or the crumbs quietly render two sizes up from the toolbar text
   beside them. */
.app-shell__bar .wk-bc .wk-bc__crumb,
.app-shell__bar .wk-bc .wk-bc__crumb--link,
.app-shell__bar .wk-bc .wk-bc__crumb--current,
.app-shell__bar .wk-bc .wk-bc__sep,
#kriya-tab .wk-board-header .wk-bc .wk-bc__crumb,
#kriya-tab .wk-board-header .wk-bc .wk-bc__crumb--link,
#kriya-tab .wk-board-header .wk-bc .wk-bc__crumb--current,
#kriya-tab .wk-board-header .wk-bc .wk-bc__sep { font-size: .72rem; }
.app-shell__bar .app-shell__crumbs { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
/* + New LEADS the right cluster, wherever a module declared it. `order` only reaches flex
   CHILDREN, so a + New nested inside a module's own wrapper stays where the module put it —
   best-effort by design; the shared `lkNewButton` and the primary icon-button cover the
   overwhelming case. */
.app-shell__bar-right { display: flex; align-items: center; gap: 6px; min-width: 0; }
.app-shell__bar-right > .lk-new,
.app-shell__bar-right > .wk-iconbtn--primary,
.app-shell__bar-right > .primary-button { order: -1; }
/* Kriya's hand-rolled header: the same thin row, not a 60px band. */
#kriya-tab .wk-board-header { min-height: 32px; }
/* The row must SPAN the band, or "right" means nothing: `.wk-board-header` is itself a flex
   container and the row was a content-sized child, so the promoted + New's `margin-left: auto`
   had no width to push against and the pill sat glued to the breadcrumb (2026-08-20, user
   direction: it belongs at the row's right edge, where every promoted + New sits). Safe to
   grow — the only sibling, .wk-hdr-actions, is position:fixed in the rail column, out of flow. */
.wk-board-header > .wk-board-header__row { flex: 1 1 auto; }
.canvas .tab-content.active .app-shell .app-shell__header .central-hello,
.app-shell__bar-right .central-hello { font-size: .82rem; }
/* In the RIGHT cluster now (user direction): last in the row — order 1 keeps it PAST + New's
   order:-1 and every unordered control — with a margin pushing it against the row's end.
   The rest is RESTATED here, not inherited: the greeting's original styles are scoped to
   `.app-shell__header`, a wrapper the shell no longer emits, so in this home they match
   nothing — without these lines the name reads regular and the wave never waves. */
.app-shell__bar-right .central-hello {
  order: 1; margin-left: 8px; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px;
  font-weight: 800; letter-spacing: -0.01em; color: var(--text, #111827);
}
.app-shell__bar-right .central-hello__lead { font-weight: 400; color: var(--text-muted, #64748b); }
.app-shell__bar-right .central-hello__wave {
  font-size: 1.1rem; display: inline-block;
  transform-origin: 72% 78%; animation: central-wave 2.6s ease-in-out 0.6s 2;
}
@media (prefers-reduced-motion: reduce) { .app-shell__bar-right .central-hello__wave { animation: none; } }
/* ── CENTRAL'S DRILL POPUP ───────────────────────────────────────────────────────────────────
   A KPI on Today opens its section as a CARD over the feed, not as a page. The drilled element
   is the SAME element it always was — the feed CSS-filtered to one section, or the approvals
   panel — so the filters, the rows and the back bar all keep their wiring; only the box moved.
   Closing (backdrop · Escape · the back bar) lands exactly where you were, because you never
   left. Below the chrome, above the page: the popup must clear the header bands (z 32) is
   wrong the other way — it opens OVER page content only, so 60/61 like every page popover. */
#central-tab .central-drill-backdrop {
  position: fixed;
  inset: var(--chrome-h, 34px) 0 0 0;   /* the chrome stays interactive above it */
  background: rgba(15, 23, 42, 0.45);
  z-index: 60;
}
#central-tab #central-list.is-drill,
#central-tab #central-approvals.is-drill {
  position: fixed;
  z-index: 61;
  left: 50%;
  top: calc(var(--chrome-h, 34px) + 4vh);
  transform: translateX(-50%);
  width: min(920px, 94vw);
  max-height: calc(100vh - var(--chrome-h, 34px) - 8vh);
  overflow-y: auto;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 12px 16px 16px;
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE FILTER SURFACE IS ORANGE, AND THE BAND IS TWO LINES
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Filtering is a STATE you are in, not a control you pressed — a list showing 12 of 400 rows
   looks exactly like a list of 12 unless the chrome says otherwise. It said so in neutral grey,
   which is the colour of everything else in the toolbar, so the one control whose job is "you
   are not seeing everything" read as furniture.

   Orange, in two strengths (user direction, 2026-08-19): LIGHT while the band is collapsed (the
   filters are there, nothing is open), FULL when it is expanded. The v connector between the
   button and the band follows the same two steps, so button and band read as one object.

   And the band itself loses its FILL and keeps two orange RULES — top and bottom. A tinted slab
   competes with the rows under it and, at .05 alpha, was closer to "slightly dirty white" than
   to a signal; two lines mark exactly the same region and cost the page no contrast.

   Every value is a token so the two themes state their own, and the caret — a data-URI, which
   cannot read a custom property — is the one part that has to repeat the hex, once per state.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
:root {
  --filter-accent: #ea580c;        /* the expanded state, and both rules on the band */
  --filter-accent-line: #fdba74;   /* the collapsed button's border + the collapsed caret */
  --filter-accent-soft: #fff7ed;   /* the collapsed button's fill */
  --filter-accent-fill: #ffedd5;   /* the expanded button's fill */
  --filter-accent-ink: #c2410c;    /* the label, on either fill */
}
html[data-theme='dark'] {
  /* Washes rather than literals: an orange-50 slab on a #202124 page is a light box, not an
     accent. Mixed from the same hue so the two themes agree about which colour this is. */
  --filter-accent: #fb923c;
  --filter-accent-line: rgba(251, 146, 60, 0.55);
  --filter-accent-soft: rgba(251, 146, 60, 0.12);
  --filter-accent-fill: rgba(251, 146, 60, 0.22);
  --filter-accent-ink: #fdba74;
}

/* ── the Filters button ──────────────────────────────────────────────────────────────────────
   Doubled (`.wk-fp.wk-fp`) and written out longhand: the greys it replaces are stated at
   (0,5,0) under `.app-shell .app-shell__bar …` and again per module, so a plain (0,2,0) rule
   here would lose in exactly the places the button is most visible. */
.wk-fp.wk-fp .wk-iconbtn,
.app-shell .app-shell__bar .wk-fp.wk-fp .wk-iconbtn {
  background: var(--filter-accent-soft);
  border-color: var(--filter-accent-line);
  color: var(--filter-accent-ink);
}
.wk-fp.wk-fp .wk-iconbtn:hover,
.app-shell .app-shell__bar .wk-fp.wk-fp .wk-iconbtn:hover {
  background: var(--filter-accent-fill);
  border-color: var(--filter-accent);
  color: var(--filter-accent-ink);
}
.wk-fp.wk-fp .wk-iconbtn.is-on,
.wk-fp.wk-fp .wk-iconbtn.is-on:hover,
.app-shell .app-shell__bar .wk-fp.wk-fp .wk-iconbtn.is-on,
.app-shell .app-shell__bar .wk-fp.wk-fp .wk-iconbtn.is-on:hover {
  background: var(--filter-accent-fill);
  border-color: var(--filter-accent);
  color: var(--filter-accent-ink);
}
.wk-fp.wk-fp .wk-iconbtn .wk-iconbtn__cap { color: inherit; }
/* The count badge sits ON the button — it must not stay slate grey against orange. */
.wk-fp.wk-fp .wk-fp__badge { background: var(--filter-accent); color: #fff; }

/* ── the v connector ─────────────────────────────────────────────────────────────────────────
   A data-URI cannot read a custom property, so this is the one part of the surface whose colour
   is stated as a literal — twice per theme, once per state. Keep these in step with the tokens
   above; `ui/filterAccent.spec.js` fails if the caret drifts back to a grey. */
.wk-fp.wk-fp::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23fff7ed' stroke='%23fdba74' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.wk-fp.wk-fp.is-open::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23ffedd5' stroke='%23ea580c' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
html[data-theme='dark'] .wk-fp.wk-fp::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23332216' stroke='%23a8562a' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}
html[data-theme='dark'] .wk-fp.wk-fp.is-open::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 7'%3E%3Cpath d='M0.5 0.5L11 6L21.5 0.5' fill='%23482c18' stroke='%23fb923c' stroke-width='1' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ── the EXPANDED band: no fill, two rules, and only while it is open ────────────────────────
   `.wk-filters` is NOT this band — it is the class on the TOOLBAR ROW (`.lk-bar__row wk-bar
   wk-filters`, and Kriya's `wk-bar wk-bar--tasks wk-filters`), so colouring it put an orange
   rule across a bar that is always on screen and never expands. The band people mean is
   `.wk-filterbar` / `.lk-filterbar` / `.app-shell__filterbar` — the row of selects that drops
   open under the toolbar, and which carries `hidden` while closed.

   `:not([hidden])` is therefore belt AND braces: `[hidden]` already sets `display: none`, so a
   closed band paints nothing either way, but stating it makes "orange means OPEN" a property of
   the selector rather than a side effect of another rule. Nothing here is orange while
   collapsed — that is the whole distinction the colour is carrying.

   The always-visible page filter zones (`.app-page-filters` and friends) are deliberately NOT
   in this list: they never expand or collapse, so an "open" accent on them would be a colour
   that never changes and therefore says nothing. */
#kriya-tab .wk-filterbar:not([hidden]),
.lk-filterbar:not([hidden]),
.app-shell .app-shell__filterbar:not([hidden]) {
  background: transparent;
  border-top: 1px solid var(--filter-accent);
  border-bottom: 1px solid var(--filter-accent);
  border-left: 0;
  border-right: 0;
  border-radius: 0;
}


/* ── THE LIST TOOLBAR IS ONE ROW ─────────────────────────────────────────────────────────────
   `.lk-bar__row` is a flex container with exactly two children — the left cluster (view
   switches, the module's own controls) and the right one (search · count · Filters · + New) —
   and it was `flex-wrap: wrap`. So the moment the left cluster grew past the space available,
   the RIGHT cluster dropped onto a second line and the bar became two rows tall.

   That is why Leads sat lower than Deals: Leads' left carries TWO segmented groups (Board|List
   AND Active|Converted) where Deals carries one, so it wrapped at a much wider viewport, and
   the wrapped first row read as "extra space above the toolbar".

   Wrapping now happens INSIDE each cluster (both are already `flex-wrap: wrap`), never BETWEEN
   them: the left cluster may shrink and re-flow its own controls, and search/Filters/+ New stay
   on the toolbar's first line where every other module puts them. `min-width: 0` is what makes
   the shrink legal — without it a flex item refuses to go below its content width and the
   nowrap simply overflows. */
.lk-bar__row { flex-wrap: nowrap; }
.lk-bar__left { flex: 0 1 auto; min-width: 0; }
.lk-bar__right { flex: 0 0 auto; }


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE BREADCRUMB ROW AND THE TOOLBAR ARE ONE CHROME ZONE — EVERY PAGE, EVERY MODULE
   ═══════════════════════════════════════════════════════════════════════════════════════════
   Orbit's Deals page was the only screen that read right, and the reason was a class:
   `#orbit-shell.is-listpage .app-shell__bar { margin-bottom: 0; border-bottom: 0 }`, applied to
   four view names. Everywhere else the breadcrumb row kept `margin-bottom: 14px` AND its own
   hairline — so between the crumbs and the toolbar under them sat a 14px gap and a divider,
   and the two rows read as two separate strips of chrome instead of one band.

   That treatment is the DEFAULT now. The divider under the whole zone is the toolbar's own
   `border-bottom`; a page with no toolbar simply ends the zone at the crumbs, which is exactly
   what Deals looks like today.

   The residual gap is the two rows' own vertical padding — 6px under the crumbs plus 6px over
   the toolbar's icons. Both come down to 3px, which keeps the text off the icons without
   reading as a space between two things. Kriya's hand-rolled bar is named beside the shared one
   for the same reason it always is: it is the one toolbar the toolkit does not render.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
.app-shell .app-shell__bar {
  margin-bottom: 0;
  padding-top: 3px;
  padding-bottom: 3px;
  /* THE ZONE ALWAYS ENDS IN A HAIRLINE — carried by whichever row is LAST.
     The rule above used to be a flat `border-bottom: 0`, on the reading that "the divider
     under the whole zone is the toolbar's own". That holds for a page like Deals, where an
     `lkBar` sits under the crumbs and carries it. It does NOT hold for the third case, which
     is most of this app: a page whose SHELL BAR IS the toolbar, crumbs and tools in one row,
     with no `lkBar` anywhere. Every `VIEW_DEFS`-driven page in Artha (~100) and the rest of
     Orbit's is that shape, as are the twenty-five modules that call `renderShell` and paint
     no shared bar — and on all of them the chrome ran straight into the content with nothing
     between them. Measured in a real engine: Deals' zone ended in a 1px hairline, Artha's in
     `0px none`.
     `.smz-has-listbar` is the existing marker for "this page rendered a shared list toolbar"
     (`lkWire` sets it, `renderShell` clears it on every view switch), and the block that
     introduced it already stated this rule in prose — "a page with no list toolbar keeps its
     divider — there it separates chrome from content, which is what it is for". It only ever
     applied it to `.app-shell__header`, a band that has since been deleted.

     The TONE is `--border`, not `--border-muted`: the shared list toolbar declares
     `--border-muted` and is overridden to `--border` further down, so what Deals -- the
     standard -- actually paints is `--border`. Two tones for one job is how the same
     hairline reads as two different lines on two pages of the same app. */
  border-bottom: 1px solid var(--border);
}
/* A page that DOES carry a list toolbar hands the hairline down to it, so the crumbs and the
   tools stay one band, and gives back the 3px it does not need above that toolbar.
   (0,4,0) against the rule above's (0,2,0). */
.tab-content.smz-has-listbar .app-shell .app-shell__bar {
  border-bottom: 0;
  padding-bottom: 3px;
}
/* When the shell bar IS the toolbar it takes the toolbar's own bottom padding — 6px, the
   same as `.lk-bar__row` — so the hairline sits the same distance under the icons on an
   Artha page as it does on Deals. 3px is right only for a crumbs row with a real toolbar
   under it; measured against Deals, the bar was 37px where its toolbar is 40. */
.app-shell .app-shell__bar { padding-bottom: 6px; }
.lk-bar__row,
#kriya-tab .wk-bar--tasks { padding-top: 3px; }


/* ── ORBIT'S OWN TOOLBAR CONTROLS JOIN THE 30px GRID ─────────────────────────────────────────
   The shared normalisation inside a list toolbar names the shared CLASSES — `.ghost-button`,
   `.primary-button`, `.wk-iconbtn`, `.wk-toggle`, `.wk-search` — and sets them all to 30px.
   Kriya's toolbar is built from exactly those. Orbit's is built from its OWN: `.orbit-seg`,
   `.orbit-inline-select` and `.orbit-sort`, none of which that rule can see, so their height is
   whatever their padding makes it.

   The `<label>` half of that — Orbit's `.orbit-sort` — is NOT fixed here, because it was never
   Orbit's to fix: it is the global `label { margin: 12px 0 6px }` landing on a toolbar control
   wrapper, and the answer is beside the height normalisation itself ("A <label> in a TOOLBAR is
   a control WRAPPER"). This block only sizes what is genuinely Orbit's own.

   `.orbit-search` is deliberately absent: `.orbit-search-wrap` is `display: none !important`
   app-wide (the header bar is the app-wide search), so a rule here could only ever style a box
   that is not laid out. Rules were once written for it on the theory that a greedy search wrapped
   the left cluster onto a second line; it cannot, because it is not in the flow at all.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */
#orbit-tab .lk-bar__row .orbit-inline-select,
#orbit-tab .lk-bar__row .orbit-sort .orbit-inline-select {
  height: 30px;
  box-sizing: border-box;
  padding-top: 0;
  padding-bottom: 0;
}
/* The segmented switches sit just inside the 30px so their border reads as a group, not as a
   control taller than the buttons beside it. */
#orbit-tab .lk-bar__row .orbit-seg { align-self: center; }
#orbit-tab .lk-bar__row .orbit-seg-btn {
  height: 28px;
  box-sizing: border-box;
  padding-top: 0;
  padding-bottom: 0;
}


/* ═══ MODULE SETUP PAGES ═════════════════════════════════════════════════════════════════════
   The look of a configuration screen, extracted from Kriya's so there is ONE of it.

   Kriya's settings have carried this treatment for a long time — a soft-bordered, gently
   lifted card with an uppercase caption and a sentence under it — but it lives in a stylesheet
   `features/work/settings.js` INJECTS, so it exists only while Kriya's settings are open and
   no other module can reach it. Orbit's twelve Setup pages sat in `.orbit-panel` instead: a
   1px `--border` box at 10px radius with no shadow and no room to breathe. Close enough to
   look like an accident rather than a decision, and different enough that the two modules
   configured the same kind of thing in two visual languages.

   These are Kriya's exact values under a shared, module-neutral name. `ui/setupSkin.spec.js`
   resolves both against the real sheet and fails the build if they drift, the same way
   `ui/tableSkin.spec.js` holds every module list table to Kriya's task list.

   NOT `:is()` — WebKit ships it at specificity ZERO (see the rail-ink note above), so the
   selector lists here are written out longhand. */
.smz-setup {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  align-items: start;
}
.smz-set-card {
  /* NO OUTLINE, AND THE SURFACE DOES THE SEPARATING (2026-09-13, user report: a Settings page "is so
     overwelming because of so many borders lines"). This was a box around a whole page's content, and
     every page that puts a toolbar and a grid inside it then had three boxes before the reader
     reached anything — the nesting this app already removed from record pages. The lifted background
     and the gap between cards still say where one ends; the shadow carries the lift on its own.
     Moved in step with Kriya's `.wk-set-card`, which `ui/setupSkin.spec.js` compares this against. */
  background: var(--bg-surface);
  border: 0;
  border-radius: 12px;
  padding: 18px 20px;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 3px 8px rgba(16, 24, 40, .05);
  display: flex;
  flex-direction: column;
  margin: 0;
  min-width: 0;   /* a grid item defaults to min-content, so a wide table inside overflows the card */
}
.smz-set-card > h3 {
  margin: 0 0 12px;
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted, #64748b);
  display: flex;
  align-items: center;
  gap: 5px;
}
/* The sentence under the caption. It is the whole reason a reader can tell Layouts from Deal
   line items without opening both — a Setup page named only by its title makes you click to
   find out what it configures, and there are twelve of them. */
.smz-set-help {
  margin: -4px 0 12px;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--text-muted, #64748b);
  max-width: 78ch;   /* a caption running the full width of a 1600px screen is not read */
}


/* ═══ THE DASHBOARD PREVIEW STRIP ════════════════════════════════════════════════════════════
   What is on screen is not saved. It reads as a state, not as a toolbar — a tinted band with
   the two ways out of it — because the one thing it must never do is look like the board's own
   controls, which is exactly what somebody would click by mistake. */
.iq-preview {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  margin: 0 0 10px;
  border: 1px solid color-mix(in srgb, var(--primary) 34%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--primary) 10%, var(--bg-surface, #fff));
}
.iq-preview__txt { font-size: 12.5px; color: var(--text); }
/* Nothing on a preview is editable, because there is nothing to edit until it exists. */
.iq-root:has(.iq-preview) .iq-widget__tools { opacity: .35; pointer-events: none; }
/* A template row is an OFFER, not a board — dashed, so the rail reads it as something that is
   not there yet rather than one more dashboard among the saved ones. */
.iq-rail__row--tpl .iq-rail__item { border: 1px dashed var(--border); border-radius: 8px; opacity: .85; }
.iq-rail__row--tpl .iq-rail__item:hover { opacity: 1; border-color: var(--primary); }


/* A KPI that has crossed a threshold its reader set. Colour ONLY — the number is the number,
   so nothing here can make a card say something different from the query behind it. */
.iq-kpi--warn .iq-kpi__v { color: var(--warn, #b45309); }
.iq-kpi--alert .iq-kpi__v { color: var(--danger, #dc2626); }

/* ── EMPTY-STATE TODO TAIL (2026-08-19) ─────────────────────────────────────────────────────
   The app-wide twin of orbit's .orbit-empty-todo: the why/cta tail `emptyState()` renders after
   the fact ("No campaigns yet." + what the screen is for + the next step). Muted on purpose —
   the fact leads, the guidance follows. */
.empty-state__todo { color: var(--text-subtle); }.canvas .tab-content.active .app-shell__actions .wk-iconbtn.is-on,
.canvas .tab-content.active .wk-hdr-actions .wk-iconbtn.is-on{
  position: relative;
  /* !important, matching the Safari-hardening block above: that block re-states the active
     chip's wash with `!important` (twice-doubled selectors), so a clean-cascade rule here
     loses in every browser — the tab never appeared, the wash-box stayed. Same weight,
     later in sheet, wins the tie everywhere. */
  background: var(--rail2-bg) !important;
  color: var(--chrome-primary, #8ab4f8) !important;
  /* Contained chip (2026-08-20 follow-up): the reach into the second rail and the wing curves
     went the way the other connections did — the slate fill and the cyan edge carry the state. */
  border-radius: 10px;
  box-shadow: inset 2px 0 0 var(--rail-accent), inset 8px 0 11px -7px color-mix(in srgb, var(--rail-accent) 50%, transparent);
  z-index: 1;
}.canvas .tab-content.active .app-shell__actions .wk-iconbtn.is-on:hover,
.canvas .tab-content.active .wk-hdr-actions .wk-iconbtn.is-on:hover{
  background: var(--rail2-bg) !important;
  color: var(--chrome-primary, #8ab4f8) !important;
}
/* ═══ THE CHROME CLOSES THE FRAME — 2px on the bottom and right ══════════════════════════════
   (2026-08-20, user direction.) The band caps the top and the rails hold the left, so the
   content sat in an open corner: chrome on two sides, raw viewport edge on the other two. A
   2px rule in the header's own tone closes it, and `--header-bg` is the SAME token the band,
   the first-rail cluster and the `.canvas` rail column paint with — so the frame follows the
   theme (and any future swing of it) with nothing to keep in step.

   On `.canvas`, which is the app's own box: a border there cannot scroll away with the
   content the way one on a scrolling child would, and `.canvas` already owns the rail gutter,
   so the left side needs nothing. `box-sizing: border-box` so the 2px comes out of the box
   rather than widening it into a horizontal scrollbar. */
.canvas:has(.topbar) {
  box-sizing: border-box;
  border-right: 2px solid var(--header-bg);
  border-bottom: 2px solid var(--header-bg);
}/* ── REQUIRED FIELDS SAY SO ────────────────────────────────────────────────────
   Every form in this app renders through one of two field renderers (ui/drawer.js
   and features/orbit/pages/form.js) and NEITHER printed a mark for a required
   field — only the bare HTML `required` attribute, which is announced to a screen
   reader and shown to nobody else. So a CRM lead form with a mandatory Last name
   looked exactly like thirty optional inputs, and the reader learned which field
   was mandatory by being refused at save.

   The mark is on the LABEL, never a border or a background on the control: an
   empty required field is not an error until somebody tries to save it, and
   colouring it red on arrival tells every new record it is already wrong. The
   invalid state below is scoped to :user-invalid (not :invalid) for exactly that
   reason — it fires after the reader has interacted, not on first paint. */
.app-req {
  color: var(--danger, #dc2626);
  font-weight: 700;
  margin-left: 1px;
  cursor: help;
}
.app-add-form label.is-required,
.app-form label.is-required {
  /* no layout change — the mark is inline in the label text */
}
.app-add-form input:user-invalid,
.app-add-form select:user-invalid,
.app-add-form textarea:user-invalid,
.app-form input:user-invalid,
.app-form select:user-invalid,
.app-form textarea:user-invalid {
  border-color: var(--danger, #dc2626);
}

/* ── A BUTTON SHOULD LOOK LIKE A BUTTON ────────────────────────────────────────
   (2026-08-21, user direction.)

   `.primary-button` is solid blue and always has been. `.ghost-button` — the other
   half of nearly every action pair in this app, and the ONLY button on plenty of
   screens — used the neutral border and body-text colour, so "Restore defaults",
   "+ Add stage", "Make default" and "← Back" read as labels somebody had boxed
   rather than as controls. On a settings page whose content is mostly grey text in
   grey boxes, that is a real cost: the reader has to hover to find out what is
   clickable.

   So a ghost button carries the PRIMARY colour in its ink and its border, and keeps
   the surface background. That is the standard outline-secondary treatment and it
   preserves the hierarchy the solid primary depends on — one blue-filled button
   leads, the blue-outlined ones follow. Making every button solid would remove the
   distinction entirely, which is the opposite of easy to recognise.

   NOT touched:
   * `.ghost-button.danger` — declared later at (0,2,0), so it still wins and a
     destructive control stays red. Turning a delete button blue would be the worst
     possible outcome of a legibility change.
   * `.wk-iconbtn` (Refresh / Search / Filters) — module chrome that repeats on every
     page. Colouring those competes with the page's own actions for the same signal.

   `--primary` is stated per theme, so this follows dark mode without a second rule. */
.ghost-button {
  color: var(--primary);
  border-color: color-mix(in srgb, var(--primary) 38%, var(--border));
}
.ghost-button:hover {
  background: color-mix(in srgb, var(--primary) 8%, var(--bg-surface));
  border-color: var(--primary);
}
/* A ghost button that is disabled must stop looking clickable — inheriting the
   primary ink would make a dead control the most eye-catching thing on the row. */
.ghost-button:disabled,
.ghost-button[aria-disabled='true'] {
  color: var(--text-subtle);
  border-color: var(--border);
}

/* ═══ A DROPDOWN LOOKS LIKE A DROPDOWN, WHEREVER IT IS ═══════════════════════════════════════
   The base rule above gives every single `<select>` a chevron, because `appearance: none` took
   the native one away. Any rule that then sets the `background` SHORTHAND on that select resets
   `background-image` and the caret silently disappears — the control still works, it just stops
   announcing that it is a control, which is what "the dropdowns don't look like dropdowns" was
   (2026-08-24, user report: every module's record page except Kriya, which paints its own).

   A dozen module partials write `background: var(--bg-surface)` on rules that match a select,
   and the next one written will too — the shorthand is the natural thing to type and nothing
   about it says "this erases the caret". So the invariant is restated here rather than left to
   every author to remember, and it is `!important` deliberately: this is the one declaration
   that must survive whatever a module scopes on top of it, including the id-scoped rules
   (`#kavach-tab .kv-fld select`) that out-specify anything reasonable.

   `padding-right` rides with it — restoring the image without the room only moves the problem,
   putting the caret on top of the longest option's text.

   Multi-selects are excluded: they render as list boxes and have never had a chevron. */
select:not([multiple]) {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 9px center !important;
  padding-right: 26px !important;
}

/* A COMBOBOX IS A DROPDOWN, SO IT WEARS THE SAME CHEVRON (2026-09-06, user report: the Category
   and Sub-category boxes "are not looking like dropdown until i do mouse hover on them").

   `<input list="…">` over a `<datalist>` is what this app uses wherever a list should SUGGEST
   without restricting — a state under a country (`ui/addressKit.js`), a party's category under
   the catalogue's taxonomy (`features/artha/categoryKit.js`). It genuinely drops down. Chrome
   draws no affordance until the pointer is over it; Safari and Firefox draw none at all. So it
   read as a plain text box and nobody found the list.

   This does NOT contradict "the 'v' affordance belongs to dropdowns ONLY... a text box must
   never look like a dropdown" (the early rule this one restates). That rule is about what the
   control DOES, and this one does drop down. A datalist input with no chevron is the same
   defect facing the other way: a dropdown that does not look like one.

   It sits HERE, beside the authoritative select declaration rather than beside the early one,
   and shares its geometry to the pixel — a combobox 8px out of step with the select above it is
   the drift this block exists to stop. `!important` for the same stated reason: module-scoped
   input rules (`.wk-filters input`, `#kriya-tab … input`, and a dozen more) out-specify anything
   reasonable, and stripping the chevron here would silently turn the control back into a text
   box on exactly those screens.

   Written out longhand rather than with `:is()`, which WebKit prices at ZERO. */
input[list]:not([type="checkbox"]):not([type="radio"]),
.aa-colsbtn {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8L9.5 4.5' stroke='%2364748b' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
  background-repeat: no-repeat !important;
  background-position: right 9px center !important;
  padding-right: 26px !important;
}
/* Chrome draws its OWN arrow inside a datalist input on hover. With ours always present that is
   two chevrons side by side the moment the pointer arrives — which is what "it only looks like a
   dropdown on hover" would become if this were left out. */
input[list]::-webkit-calendar-picker-indicator {
  display: none !important;
}

/* ═══ KRIYA — A RECORD'S SECTIONS AS ONE TAB STRIP ═══════════════════════════════════════════
   Details, Custom fields and Description lead the page; everything else is a tab (2026-08-24,
   user direction). They were collapsible tiles stacked down the page with a "+ Section" picker
   deciding which existed at all, so reading a project meant scrolling past a dozen headers and
   finding a section you had never added meant knowing the picker was there. Every section is
   present now and one shows at a time — which is what removed the need to curate them: the
   reason to hide a section was the length of the page, and a strip has no length.

   The tiles inside are UNCHANGED `_entTile` output, so every delegated handler still finds what
   it expects. Their own header is hidden HERE rather than in the renderer: the tab already
   names the section, and a tile that lost its header in the renderer would lose it on the
   pages that still stack tiles. */
.wk-sectabs { margin-top: .9rem; }
.wk-sectabs__strip {
  display: flex; flex-wrap: wrap; gap: 3px; align-items: flex-end;
  padding: 5px 6px 0; background: var(--bg-muted); border: 1px solid var(--border);
  border-bottom: 0; border-radius: 10px 10px 0 0;
}
.wk-sectab {
  border: 0; border-bottom: 1px solid transparent; background: color-mix(in srgb, var(--text-muted) 10%, var(--bg-surface));
  color: var(--text-muted); font: inherit; font-size: .78rem; font-weight: 700;
  padding: 7px 12px; border-radius: 8px 8px 0 0; cursor: pointer; margin-bottom: -1px;
}
.wk-sectab:hover:not(.is-on) { background: color-mix(in srgb, var(--primary) 12%, var(--bg-surface)); color: var(--primary); }
/* The active tab is a light tint of the primary with its own bottom border painted to match,
   so it erases its stretch of the strip's divider and opens onto the panel below — which is
   what reads as a tab rather than a button that happens to sit in a row. */
.wk-sectab.is-on {
  background: var(--bg-surface); color: var(--primary);
  border-bottom-color: var(--bg-surface);
  box-shadow: inset 0 2px 0 var(--primary);
}
.wk-sectab__panel {
  border: 1px solid var(--border); border-top: 0; border-radius: 0 0 10px 10px;
  background: var(--bg-surface); padding: .2rem .1rem;
}
.wk-sectab__panel[hidden] { display: none; }
/* Inside a panel the tab is the heading, so the tile's own chrome (title, grip, chevron, ×) is
   redundant — and its collapse state is meaningless when only one panel shows at a time. */
.wk-sectab__panel > .wk-tile > .wk-tile__head { display: none; }
.wk-sectab__panel > .wk-tile { border: 0; box-shadow: none; margin: 0; background: transparent; }
.wk-sectab__panel > .wk-tile.is-collapsed > .wk-tile__body { display: block; }

/* ═══ CRM — A RECORD'S SECTIONS AS TABS, EACH WITH ITS OWN HISTORY RAIL ══════════════════════
   A deal stacked eleven sections down one column — fields, people, line items, quotes, orders,
   tags, timeline, files, related records and two kinds of history. Reading what was quoted
   meant scrolling past everything the record knows, and both histories sat at the bottom where
   neither was beside the thing it describes (2026-08-24, user direction; the Kriya
   detail-page shape brought to the CRM).

   The rail is per TAB, which is the point of the split: the field history beside the fields,
   the activity beside the quotes. */
.orbit-rtabs { margin-top: 1rem; }
.orbit-rtabs__strip {
  display: flex; flex-wrap: wrap; gap: 3px; align-items: flex-end;
  padding: 5px 6px 0; background: var(--bg-muted); border: 1px solid var(--border);
  border-bottom: 0; border-radius: 10px 10px 0 0;
}
.orbit-rtab {
  border: 0; border-bottom: 1px solid transparent;
  background: color-mix(in srgb, var(--text-muted) 10%, var(--bg-surface));
  color: var(--text-muted); font: inherit; font-size: .8rem; font-weight: 700;
  padding: 8px 14px; border-radius: 8px 8px 0 0; cursor: pointer; margin-bottom: -1px;
}
.orbit-rtab:hover:not(.is-on) { background: color-mix(in srgb, var(--primary) 12%, var(--bg-surface)); color: var(--primary); }
/* The active tab paints its own bottom border to match its fill, erasing its stretch of the
   strip's divider so it opens onto the panel — what reads as a tab rather than a button. */
.orbit-rtab.is-on {
  background: var(--bg-surface); color: var(--primary);
  border-bottom-color: var(--bg-surface); box-shadow: inset 0 2px 0 var(--primary);
}
.orbit-rtab__panel {
  border: 1px solid var(--border); border-top: 0; border-radius: 0 0 10px 10px;
  background: var(--bg-surface); padding: 1rem;
}
.orbit-rtab__panel[hidden] { display: none; }
/* ONE COLUMN, HISTORY UNDERNEATH (2026-08-25, user direction). This was a two-column grid with
   the trails in a 280-360px right rail — which is what made both of them scroll sideways to stay
   readable, a tabular thing in a column too narrow for its own columns. Full width under the
   content they belong to, they fit; the narrow-screen media query that used to stack them goes
   with the second column, because there is no longer a width at which anything squeezes. */
.orbit-rtab__cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; align-items: start; }
.orbit-rtab__main { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.orbit-rtab__after { display: flex; flex-direction: column; gap: 1rem; min-width: 0;
  margin-top: .35rem; padding-top: 1rem; border-top: 1px solid var(--border-muted); }

/* ═══ THE SHARED IMPORT WIZARD ═══════════════════════════════════════════════════════════════
 *
 * `ui/importWizard.js` — upload a spreadsheet, correct the column mapping, import. Here rather
 * than in a module partial because it is used from more than one module (2026-08-26): styles in
 * `partials/orbit.html` reach Orbit's tab and nowhere else, which is part of why the CRM's own
 * version could not be lifted out as it stood.
 *
 * It deliberately borrows the app's existing surfaces — the card, the hairline, the muted ink —
 * rather than inventing a look, so an import screen is recognisably the same product as the list
 * it was opened from.
 */
.imp { max-width: 900px; }
.imp__head { display: flex; align-items: center; gap: .75rem; margin-bottom: .25rem; }
.imp__head h2 { margin: 0; font-size: 1.05rem; }
.imp__file { display: block; margin: .8rem 0; font-weight: 600; }
.imp__opts { margin: 1rem 0; padding: .8rem; border: 1px solid var(--border); border-radius: 8px; }
.imp__opts legend { padding: 0 .35rem; font-size: .8rem; font-weight: 700; color: var(--text-subtle); }
.imp__radio { display: flex; align-items: flex-start; gap: .5rem; margin: .35rem 0; cursor: pointer; }
.imp__radio input { margin: .2rem 0 0; flex: 0 0 auto; }
.imp__radio-t { display: block; font-size: .86rem; font-weight: 600; color: var(--text); }
.imp__radio-n { display: block; font-size: .76rem; color: var(--text-muted); line-height: 1.35; }
.imp__actions { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1rem; }
.imp__file-name { font-size: .84rem; margin: .6rem 0; }
/* The row cap. A warning, not a refusal: the file is still importable, in pieces. */
.imp__warn { font-size: .82rem; color: var(--warning, #b45309); margin: .5rem 0; }
.imp__map { width: 100%; border-collapse: collapse; margin-top: .5rem; }
.imp__map th, .imp__map td { padding: .4rem .5rem; border-bottom: 1px solid var(--border-muted); text-align: left; font-size: .84rem; }
.imp__map thead th { font-size: .74rem; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); }
.imp__map select { width: 100%; max-width: 260px; }
.imp__sample { max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Every row that did not import, with its number and its reason — the whole value of the
   report, and the reason it is a list rather than a count. */
.imp__errors { margin: .6rem 0 0; padding-left: 1.1rem; font-size: .82rem; color: var(--text-muted); }
.imp__errors li { margin: .15rem 0; }

/* ── A ROW OF ACTIONS IS ONE ROW ───────────────────────────────────────────────
   2026-08-30, user report: "buttons on the top toolbar are not having consistent size when
   viewing product details".

   A record page's action cluster mixes all three button classes — `editToggleHtml()` emits
   `.ghost-button.btn-sm`, a module's own controls are `.btn.btn-sm`, the primary action is
   `.primary-button.btn-sm` — and `.btn-sm` was not making them agree.

   `.btn-sm` is declared at ~10841, and BOTH `.btn` (12294, `font-size: .82rem`) and
   `.primary-button` (12267, `font: inherit` — a shorthand, so it resets font-size) are declared
   AFTER it at the same specificity. Later wins: the compact modifier was inert on two of the
   three classes it exists for, and a primary button rendered at the INHERITED size, which is
   why Publish sat visibly larger than Enrich and Delete beside it.

   Written at (0,2,0) so it beats each base class on weight as well as on order — relying on
   "it is last in the file" is what made `.btn-sm` inert in the first place.

   Locked by `ui/actionRowSkin.spec.js`, which resolves the real sheet: the three classes
   agreeing is not something you can see by reading the markup. */
.btn.btn-sm,
.ghost-button.btn-sm,
.danger-button.btn-sm,
.primary-button.btn-sm {
  padding: 5px 10px;
  font-size: 0.8rem;
  border-radius: 7px;
  /* LINE-HEIGHT IS PART OF THE SIZE, and leaving it out is why this was reported twice
     (2026-08-30). `.primary-button` declares `font: inherit` — a SHORTHAND, so it sets
     line-height too — and `body` is `line-height: 1.5`, while a `<button>` with no declaration
     takes the UA default of roughly `normal` (~1.2). Same padding, same font-size, different
     LINE BOX: at .8rem that is ~19px against ~15px, so the primary action rendered several
     pixels taller than the outline buttons beside it and its label sat on a different baseline.
     Equalising font-size alone made them the same TYPE and not the same BUTTON. */
  line-height: 1.35;
}

/* `.ghost-button` carries `margin-top: 12px` for the stacked forms it was written for, and in a
   toolbar row that drops it off the line its siblings sit on. `.lk-bar__row` already zeroes it
   (see the note at ~18236, which describes this same problem); a record page's action cluster is
   the same row under a different name, and had no rule at all. */
.orbit-detail-actions .ghost-button,
.orbit-detail-actions .danger-button,
.orbit-detail-actions .primary-button,
.orbit-editbar .ghost-button,
.orbit-editbar .danger-button {
  margin-top: 0;
}

/* ── A VARIANT SITS UNDER ITS PARENT ───────────────────────────────────────────
   2026-08-30, user report: "heirarchial view of products not available". The product list was
   flat, so a T-shirt's eight colours scattered across the table with nothing saying which parent
   they belonged to. `_familyOrder` keeps a family together; this is what makes the relationship
   visible rather than merely adjacent.

   An indent on the FIRST cell only — a row-wide inset would misalign every other column against
   its own header, and the name is the cell the eye follows down. The rule is a left border
   rather than padding alone so the group reads as a group at a glance, and it is deliberately
   `--border-muted`: it is a hint about structure, not a division between records. */
#pimly-tab .pimly-table tbody tr.pimly-row--variant > td:first-child,
#pimly-tab .pimly-table tbody tr.pimly-row--variant > th:first-child {
  padding-left: 30px;
  box-shadow: inset 12px 0 0 -11px var(--border-muted);
}

/* ── A DRAFT RECORD IS HELD UNTIL SAVE ─────────────────────────────────────────
   2026-08-30, user direction: nothing is written until Save is pressed. `ui/draftRecord.js`
   paints the record's own field groups into a create page, so almost everything here is already
   styled by the record-page rules — what is left is the bar's own state and the child panels.

   The panels are hidden with the `hidden` attribute rather than being re-rendered, which is what
   makes "the form holds data between tabs" true with nothing to save and restore. `[hidden]` is
   overridden by `display` on some elements, so it is stated. */
.drf__panel[hidden],
.drf [data-draft-panel][hidden] { display: none; }

/* ── SAVE AND CANCEL STAY PUT FOR THE LENGTH OF THE SCROLL ─────────────────────
   2026-08-30, user direction: "in new or edit product page top save or cancel buttons toolbar
   always stay at top while scrolling".

   A create page is thirty-odd fields deep, so a bar that scrolls away means scrolling back up to
   commit — and back up again to abandon. Orbit's record pages have had this since 2026-08-22
   (`#orbit-tab.is-record-page .orbit-detail-toolbar`), but that rule is scoped to Orbit's own tab
   and lives in Orbit's partial, so the shared draft host never got it however many of Orbit's
   class names it borrowed.

   Which container this sticks TO is the part worth stating, because guessing it is what made the
   first attempt at Orbit's version fail: sticky resolves against the nearest SCROLLING ancestor,
   and a module's body is the one that scrolls (`.pimly-main { overflow-y: auto }` and its
   equivalents). The draft renders inside it, so `top: 0` pins to the top of the module body — not
   the viewport, and not the app shell, which is why this needs no coordinate and no offset for
   the 44px header band.

   An opaque BACKGROUND is not decoration: without it the fields scroll visibly through the bar. */
.drf__bar {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--bg-app);
  padding-top: 6px;
  padding-bottom: 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border-muted);
}

/* The RECORD page's action row, same reason: on a product deep enough to scroll, Edit — and once
   editing, ✓ Save and Cancel — went off the top. Scoped to a detail page, because `.pimly-head`
   is also the heading row of several LIST screens, where there is nothing to pin. */
#pimly-tab .smz-detail-page > .pimly-head {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--bg-app);
  padding-top: 6px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border-muted);
}

.drf__title { margin: 0; font-size: 1rem; font-weight: 600; }

/* The one thing a reader must not have to guess: this record does not exist yet. */
.drf__unsaved {
  margin-left: 8px;
  padding: 2px 8px;
  border: 1px solid var(--warn-border, var(--border));
  border-radius: 999px;
  background: var(--warn-bg, var(--bg-muted));
  color: var(--text-muted);
  font-size: 0.72rem;
  white-space: nowrap;
}

.pimly-draft-files { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 8px; }
.pimly-draft-files li { display: flex; align-items: center; gap: 10px; }
.pimly-draft-files img { width: 40px; height: 40px; object-fit: cover; border-radius: 6px; }

/* A draft's row-collection panel: the add-form above the rows it has collected. Two columns on
   anything but a phone, because these are short fields (an option name and its value, a vendor
   and a cost) and one per line turns six of them into a scroll. */
.drf-rowform {
  display: grid;
  gap: 10px 14px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  margin: 12px 0;
}
.drf-rowform label { display: flex; flex-direction: column; gap: 4px; font-size: 0.82rem; }
.drf-rowform label.drf-check { flex-direction: row; align-items: center; gap: 8px; }

/* ── A SETTINGS PAGE SAVES WHEN YOU PRESS SAVE ─────────────────────────────────
   2026-08-30, user direction: "Settings too — add Save buttons". Five pages wrote on every
   `change` and carried no Save button at all; `ui/settingsBuffer.js` holds the change and this is
   the bar that commits it.

   STICKY to the bottom of the pane, because these pages scroll: a Save button below four cards is
   one somebody scrolls past, and the whole point of it is being reachable at the moment they
   decide. It is rendered even when nothing is pending — a bar that appears on first edit shifts
   the page under the pointer, and a reader who cannot see the button until they have already
   changed something has no way to know the page has one. */
.stb {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg-surface);
  box-shadow: 0 -2px 10px rgb(0 0 0 / 6%);
}
.stb__msg { flex: 1 1 auto; min-width: 0; color: var(--text-muted); font-size: 0.82rem; }

/* Pending is the state worth SEEING — a count in muted grey reads as a caption. */
/* THE SAVE BAR AT THE TOP, AND ALWAYS THERE (2026-09-14, user direction: "move save and other
   buttons to top of the page body, and keep always on like other modules tools area").
   SCOPED TO A WRAPPER, not applied to `.stb` itself: a dozen settings pages render that bar and
   they still foot their forms, which is right for a long form you fill downwards. These two pages
   are not forms — they are a list you rearrange and a set of boxes you tick, where the row of
   tools belongs where every module's toolbar is, at the top, in the same place before and after
   you touch anything.
   `bottom: auto` is stated: `position: sticky` honours BOTH offsets, and a box with `top: 0` and
   an inherited `bottom: 0` sticks to whichever edge it reaches first — which reads as a bar that
   works on a short page and not on a long one. */
/* IN THE CHROME'S TOOLS ROW, the bar is not a bar any more — it is the row's right cluster, so
   it drops the panel look entirely: no border, no card background, no shadow, no sticky offsets.
   Two nested surfaces (a bordered box inside a bordered toolbar) is the stack of frames this app
   removes on sight, and the row already provides the frame. */
.app-shell__tools .stb,
.lk-bar__right .stb {
  position: static;
  bottom: auto;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  gap: 8px;
}
/* The "N unsaved changes" line reads as a status beside the buttons here, not as a panel's
   caption — so it takes the toolbar's own muted voice and gets out of the way on a narrow row. */
.app-shell__tools .stb__msg,
.lk-bar__right .stb__msg { font-size: .78rem; color: var(--text-muted); white-space: nowrap; }

/* THREE COLUMNS: modules · sections · sub-sections (2026-09-14, user direction). `minmax(0, …)`
   on every track — a grid track's default minimum is its CONTENT, so one long screen name would
   push its column wider and squeeze the other two before any ellipsis could fire.
   Each column scrolls on its own and they share one height, so a module with forty screens does
   not make the page taller than the two columns beside it. */
/* EACH COLUMN IS ITS OWN HEIGHT, AND EVERY COLUMN SHOWS ITS WHOLE LIST (2026-09-14, user report:
   "3rd column knocked down, 2nd column and 3rd columns are not showing full list").
   Two faults, one cause. `align-items: stretch` made all three tracks as tall as the tallest —
   the module column, which had no cap — while columns 2 and 3 were capped at 26rem inside it, so
   they were clipped AND floating in a taller box: clipped content in a column with empty space
   below it, which is exactly what "knocked down" looks like. `start` gives each column its own
   height, and the caps are gone: a list of screens is read by scrolling the PAGE, the way the
/* Settings ▸ Help ▸ Guides & help — an index of the platform manual (2026-09-15). */
.helpdocs__group + .helpdocs__group { margin-top: 18px; }
.helpdocs__head { display: flex; align-items: center; gap: 8px; margin: 0 0 8px;
  font-size: .82rem; font-weight: 700; color: var(--text); }
.helpdocs__rows { display: flex; flex-direction: column; gap: 3px; }
.helpdocs__row { display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border: 1px solid var(--border-muted); border-radius: 9px; background: var(--bg-surface);
  color: var(--text); text-decoration: none; font-size: .84rem; }
.helpdocs__row:hover { background: var(--bg-hover); border-color: var(--border); }
.helpdocs__row-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* THE ORGS LIST — one vertical list, grouped by what each org can do right now (2026-09-15). */
.ws-orggroup + .ws-orggroup { margin-top: 18px; }
.ws-orggroup__head { display: flex; align-items: center; gap: 6px; margin: 0 0 8px;
  font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.ws-orggroup__rows { display: flex; flex-direction: column; gap: 4px; }
.ws-orgrow { position: relative; display: flex; align-items: center; }
.ws-orgrow__main { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px;
  padding: 8px 46px 8px 10px; border: 1px solid var(--border-muted); border-radius: 10px;
  background: var(--bg-surface); font: inherit; text-align: left; color: var(--text); cursor: pointer; }
.ws-orgrow__main:hover { background: var(--bg-hover); }
.ws-orgrow.is-current .ws-orgrow__main { border-color: var(--primary); }
.ws-orgrow__icon { flex: 0 0 auto; display: inline-flex; }
.ws-orgrow__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ws-orgrow__name { font-size: .86rem; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-orgrow__sub { font-size: .76rem; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The chip carries the WORD and the NUMBER: "trial" and "trial - 3 days left" are different
   facts, and the number is the whole point of a trial or a grace period. */
.ws-orgrow__chip { flex: 0 0 auto; padding: 2px 9px; border-radius: 999px; font-size: .7rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em; border: 1px solid currentColor; white-space: nowrap; }
.ws-orgrow__chip.is-trial { color: var(--primary); }
.ws-orgrow__chip.is-grace { color: var(--warning, #b26a00); }
.ws-orgrow__chip.is-disabled { color: var(--danger, #c5221f); }
/* CENTRED WITHOUT A TRANSFORM, AND THAT IS LOAD-BEARING (2026-09-15, user report: "now these
   ... not showing popup at all").
   This was `top: 50%; transform: translateY(-50%)`. A non-`none` transform makes an element the
   CONTAINING BLOCK for `position: fixed` descendants — so the moment the ⋯ popover became fixed
   to escape the settings pane's scroller, its viewport coordinates were resolved inside this
   34px box at the row's right edge instead, and the menu was placed far off-screen. It did not
   look clipped; it looked like the button had stopped working.
   Same family as "a sticky element travels only inside its containing block": the property that
   breaks it is nowhere near the element that misbehaves, and nothing warns. Zero-inset + flex
   centres it identically with no transform. */
.ws-orgrow .ws-menu, .ws-orgrow [data-menu] {
  position: absolute; right: 8px; top: 0; bottom: 0;
  display: flex; align-items: center;
}
/* THE ⋯ MENU WAS BEING CLIPPED (2026-09-15, user report: "... items are getting hiding"). The row
   is a positioned box and the popover hangs below it, so anything with `overflow` between the two
   cuts it off — and a row is exactly the kind of box that gets `overflow: hidden` to make an
   ellipsis work. The row keeps its ellipsis on the NAME (where the text actually is) and the row
   itself stays visible; the open menu is lifted above its neighbours so the row beneath cannot
   paint over it. */
/* THESE THREE RULES WERE DELETED (2026-09-15) AND THE STORY IS WHY THE REPORT CAME BACK.
   They set `overflow: visible` on the org row, the row group and the group, to stop a ⋯ menu
   being cropped by its own row — except NOTHING here sets `overflow` on any of them, so
   `visible` was already in force and the rules changed nothing at all. They read as a fix, they
   passed a spec asserting they existed, and the menu was still clipped.
   Resolving the whole ancestor chain named the real clipper: `.settings-section`, the settings
   pane's SCROLLER (`overflow-y: auto`), which cannot be made visible because the pane has to
   scroll. The menu is `position: fixed` and placed by `ui/menuPlacement.js` now, which escapes
   overflow clipping entirely — see `_placeMenu` in features/workspace/controller.js.
   A rule that changes nothing is worse than a missing rule: the sheet reads as covered. */
.ws-menu__popover { z-index: 60; }

/* (A comment opener was lost here at some point before 2026-09-15; the orphaned tail below was
   left parsing as a SELECTOR, which consumes everything up to the next `{...}` — silently eating
   the rule that followed it. Restored as a comment rather than deleted, because the sentence is
   the surviving half of a real note about where the module-changes list scrolls.)
   A reader should find that list by the scrollbar arriving where the
   forty module rows above it are, not by finding a scrollbar inside a box inside a section. */
/* TWO COLUMNS: the modules, and one TREE of that module's sections with their screens nested
   under the open one (2026-09-15, user direction: "show sections and subsections as single list as
   tabed similar to left rail"). It was three; side by side the reader had to hold the
   section→screen relationship in their head, and nested the relationship IS the layout — the same
   shape the navigation column uses, which is what they will see afterwards. */
/* TIGHT, AND NOT FULL-WIDTH (2026-09-15, user direction: "Kepp spaces tight and don't drag the
   things to full width"). The tree column is `max-content` up to a cap rather than `1fr`: a list
   of short labels stretched across a 1600px screen puts its checkbox and its name at opposite
   ends of the monitor, and every row becomes a journey for the eye. `justify-content: start`
   keeps the pair together at the left instead of spreading them. */
/* THE BREAKPOINT WATCHED THE WRONG BOX (2026-09-15, user report: "still it is showing then one
   below other, i want them side by side").
   `@media (max-width: …)` asks how wide the WINDOW is. This grid lives inside the settings pane,
   which sits inside a 248px navigation column and its own rail — so on a perfectly wide screen
   the pane can be narrow, the media query never fires, and the columns stack anyway when their
   own minimums no longer fit. A CONTAINER query asks the only question that matters: how much
   room does THIS element have.
   The viewport rule stays underneath as the fallback for a browser without container queries,
   and it is written so that not supporting them degrades to two columns rather than to one. */
/* `.secpick__cols` IS DELETED (2026-09-19). It was the grid holding this editor's own module
   column beside the tree; the page's left list is that column now, and the tree has the whole
   right pane. The container-query reasoning it carried lives on in `.bizmod-two`, which is the
   grid that actually has two tracks to stack. */
/* A row is one line high. The description that used to sit under it is an ⓘ beside the name now,
   so nothing here needs to grow to two. */
.secpick__rowwrap { align-items: center; }
.secpick__row { align-items: center; padding: 3px 6px; }
.secpick__colbtn { padding: 3px 2px; }
.secpick__colrow { padding: 1px 3px; }
.secpick__col { min-width: 0;
  border: 1px solid var(--border-muted); border-radius: 10px; padding: 6px; background: var(--bg-surface); }
.secpick__col-head { margin: 2px 4px 6px; font-size: .7rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted); }
.secpick__colrow { display: flex; align-items: center; gap: 8px; border-radius: 8px; padding: 2px 4px; }
.secpick__colrow.is-on { background: var(--bg-hover, var(--bg-muted)); }
.secpick__colrow input[type="checkbox"] { flex: 0 0 auto; width: 16px; height: 16px; margin: 0;
  accent-color: var(--primary); }
.secpick__colbtn { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  border: 0; background: none; font: inherit; font-size: .82rem; color: var(--text); text-align: left;
  padding: 5px 2px; cursor: pointer; }
.secpick__colbtn-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.secpick__col-n { flex: 0 0 auto; font-size: .72rem; }
/* The module column is one of the three now, so it drops the tab strip's own frame — a bordered
   list inside a bordered column is the stack of frames this app removes on sight. */
/* The module column is one of the three now: same box, same rules, no cap and no inner scroller
   — three columns that each scrolled differently was half of what made this read as broken. */

/* WHICH ROW FEEDS THE NEXT COLUMN (2026-09-14, user direction: "show exact connections from col1,
   to col2 to col3"). Three lists side by side are three lists until something says how they
   relate, and a highlight cannot: every column has one of its own. The chevron points AT the
   column it fills, so the eye follows module ▸ section ▸ sub-sections unaided. */
/* ONE SELECTED LOOK ACROSS BOTH COLUMNS. They had two — the module column filled its row with
   primary, the section column tinted its with the hover wash — so the two ends of the same chain
   did not look like the same kind of answer. */
.secpick__tree .secpick__colrow.is-on { background: var(--primary); }
.secpick__tree .secpick__colrow.is-on .secpick__colbtn,
.secpick__tree .secpick__colrow.is-on .secpick__col-n { color: var(--primary-ink, #fff); }
.secpick__tree .secpick__colrow.is-on .secpick__col-n { opacity: .85; }

/* The path in words, for anyone scrolled past the chevrons — and the one line that answers
   "which list am I looking at" without comparing three highlights. */

.settings-topbar { position: sticky; top: 0; z-index: 4; margin: 0 0 14px; }
/* The save bar is ABSENT until something is pending (2026-09-15) and its HOST is what stays, so
   the host's own bottom margin would otherwise hold 14px of empty page open above every settings
   section for the 99% of the time there is nothing to save. */
.settings-topbar:empty { margin: 0; }
.settings-topbar .stb {
  position: static;
  bottom: auto;
  margin: 0;
  box-shadow: 0 2px 10px rgb(0 0 0 / 6%);
}
.stb[data-stb-dirty] { border-color: var(--primary); }
.stb[data-stb-dirty] .stb__msg { color: var(--text); font-weight: 500; }

/* `.ghost-button` carries `margin-top: 12px` for the stacked forms it was written for, which in
   a row drops Discard off the line Save sits on — the same reset `.lk-bar__row` and
   `.orbit-detail-actions` already make. */
.stb .ghost-button, .stb .primary-button { margin-top: 0; }

/* ── THE SHARED DRIVE PICKER (ui/drivePicker.js, 2026-09-11) ─────────────────────────────────
   A drill-down list, not a grid: a folder tree is read top to bottom.

   APP-LEVEL, deliberately. These rules were `#pimly-tab .pim-dp__…` in that module's partial,
   while the picker opens in the shared drawer — which `ui/drawer.js` appends to `document.body`,
   outside every module's tab. The selector never matched and the picker had rendered unstyled
   since it shipped; nobody reported it, because nothing looked broken enough to name. Any module
   can open this picker now, so its styles have to live where every one of them can reach. */
.smz-dp__crumbs { font-size: 12px; color: var(--text-muted); margin-bottom: 8px; }
.smz-dp__list { display: grid; gap: 2px; max-height: 46vh; overflow-y: auto; }
.smz-dp__row { display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 9px; border: 1px solid transparent; border-radius: 8px; background: none;
  font: inherit; color: inherit; text-align: left; cursor: pointer; }
.smz-dp__row:hover { background: var(--bg-hover); border-color: var(--border-muted); }
/* A file the caller cannot take is drawn, not hidden — a reader hunting for one needs to see
   they are in the right folder, and a silently absent file reads as a broken list. */
.smz-dp__row.is-off { color: var(--text-muted); cursor: default; }
.smz-dp__row.is-off:hover { background: none; border-color: transparent; }
.smz-dp__ico { flex: none; }

/* A NOTE IS A DATED REMARK, so it reads as one — a byline, a date, then the text. A table would
   put somebody's paragraph in a cell and wrap it to a column width nobody chose. */
.pty-note { border: 1px solid var(--border-muted); border-radius: 8px; padding: 10px 12px; margin: 0 0 8px; background: var(--bg-surface); }
.pty-note.is-pinned { border-color: var(--primary); }
.pty-note__head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.pty-note__by { font-weight: 600; font-size: .82rem; }
.pty-note__head /* The child-field block on a SAVED record's People / Places / Banking tabs (2026-09-24, user
   direction: the fields are drawn upfront, not behind a ＋). Separated from the list ABOVE it by
   a rule and space rather than boxed: a bordered box whose children are bordered controls is the
   stack of cards this app removes on sight, and the two halves are one subject — what is there,
   and the next one. */
.pty-childadd { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.pty-childadd__head { margin: 0 0 10px; font-size: 13px; font-weight: 700; color: var(--text-subtle); }
.pty-childadd .pty-rowacts { margin-top: 12px; }

.pty-rowacts { margin-left: auto; }
.pty-note__body { margin: 0; white-space: pre-wrap; font-size: .88rem; line-height: 1.5; }

/* ─── FILES ON A COMPANY ──────────────────────────────────────────────────────────────────────
   2026-09-23, with the Attachments tab.

   A LIST OF ROWS, NOT A GRID OF CARDS. Every file here is a document rather than a picture — a
   signed PDF, a tax certificate — so the thing a reader scans is the NAME, and a thumbnail grid
   would give equal width to a dozen identical page icons. One row, one filename, the facts after
   it: *a bordered box whose children are bordered boxes is the stack of cards this app removes on
   sight*, and the row needs no border of its own because the hairline between rows separates
   them.

   THE NAME IS THE LINK and carries the whole target, so the press target is the word rather than
   a separate ⤓ beside it — and it is the only thing here at full ink, because it is the only
   thing anybody is looking for. */
.pty-files { list-style: none; margin: 0; padding: 0; }
.pty-file {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 8px 0; border-bottom: 1px solid var(--border-muted);
}
.pty-file:last-child { border-bottom: 0; }
.pty-file__name { font-weight: 500; font-size: .9rem; word-break: break-all; }
/* What the uploader said it IS, beside a filename like `scan_0042.pdf`. */
.pty-file__cap { font-size: .84rem; color: var(--text-subtle); }
/* The size, who, when — pushed right so the names stay in one readable column on the left. */
.pty-file .pty-sub { margin-left: auto; white-space: nowrap; }
.pty-file .ghost-button { flex: none; }
/* THE LABEL IS THE BUTTON and the input inside it is hidden, so the label has to LOOK pressable
   on its own — `ghost-button` gives it the skin, this gives it the cursor and stops the global
   `label { display: block; margin: 12px 0 6px }` making it a full-width block with air above it. */
.pty-upload { display: inline-flex; align-items: center; cursor: pointer; }

/* ─── A ROW OF ADD BUTTONS IS ONE ROW ─────────────────────────────────────────────────────────
   2026-09-24, user report with a screenshot of ＋ File / ＋ From URL / ＋ From Drive: *"buttons in
   the attachments are not aligned properly"*.

   THE LABEL WAS A BUTTON BY APPEARANCE AND NOT BY BOX, and there were three ways for that to show:

     · `box-sizing`. `.ghost-button` is `border-box` (the app-wide reset reaches `button`); a
       `<label>` is `content-box`. With `height: auto` the two agree exactly, which is why this
       measured 0px apart in a headless browser and still looked wrong on a real one — the moment
       ANY rule gives the row a height, the label is border+padding = 12px taller than its
       siblings and nothing in the markup says why.
     · `margin-top`. `.ghost-button` carries `margin-top: 12px` for a button standing alone under
       a block of text. `.pty-upload` cancelled it — for the label ONLY, so the label sat 12px
       above two siblings that kept it. They coincide only while something else centres them.
     · The row itself was whatever `display` it inherited, so which of those differences showed
       depended on the container each surface happened to put it in — the record page and the
       draft use different ones.

   SO THE ROW IS DECLARED, AND EVERY CHILD IS THE SAME BOX. Cancelling the margin on ALL of them
   rather than on the label is the half that was wrong before: a rule that special-cases one child
   of three to look like the other two is the thing that breaks the day a fourth arrives.

   MEASURED BOTH SURFACES, RECORD AND DRAFT: same top, same height, 0px spread. */
.pty-rowacts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.pty-rowacts > .ghost-button,
.pty-rowacts > .danger-button,
.pty-rowacts > .pty-upload {
  box-sizing: border-box;
  margin: 0;
}

/* ═══ A RECORD IS A PAGE, NOT A STACK OF CARDS ═══════════════════════════════════════════════
   (2026-09-11, user direction: forms "should not look like cards … clean, neat and organised
   web page forms".)

   Every record page, create draft and edit form in this app is built from `dGroup` / `dSection`
   in the shared `ui/detailKit.js` — 81 call sites across 25 files and ~20 modules. Each group
   was a bordered, radiused, tinted CARD with a tinted head band and a hairline under it, and a
   record stacks four to ten of them, so opening a customer showed eleven rectangles and the
   reader had to find the form inside the boxes.

   THE SHAPE NOW: a section heading · ONE hairline under it · space · the fields · a generous
   gap · the next heading. Whitespace separates sections; chrome does not.

   THREE THINGS THIS BLOCK IS CAREFUL ABOUT.

   ONE HAIRLINE, AND IT IS UNDER THE HEADING. With the box gone something has to say "a new
   section starts here", and a rule the eye reads left-to-right under a title does that with one
   line where a border does it with four. Nothing else inside a group draws one.

   ALIGNMENT CARRIES THE STRUCTURE. A border held a group together; with none, the fields must
   sit on a real grid or the page reads as a list. `.orbit-detail-grid` keeps its column track
   and gains a row gap and `align-items: start`, so labels and values line up across the row
   whatever the cells contain — which is what makes it read as a form rather than as prose.

   THE SPACING HAS ONE OWNER. A group carries its own `margin-bottom`, and the three stacks that
   hold groups (`.orbit-lead-main`, `.orbit-pageform--grouped`, and a plain container in the
   draft / workspace / Artha item hosts) contribute NOTHING — flex and grid `gap` ADD to a
   margin rather than absorbing it, so leaving the gaps would give three different distances
   between two groups depending on which surface you opened, which is exactly the uniformity
   this change exists to buy. Every other child of those stacks self-spaces already
   (`.orbit-kpis`, `.orbit-nextstep`, `.orbit-subsection` all carry their own margin).

   THIS BLOCK IS THE SHARED TOOLKIT'S SKIN AND LIVES WITH THE SHEET, not in a module partial.
   It was in `partials/orbit.html` — global only because that partial happens to be INLINED into
   the shell, so the day it is fetched like `docs.html` every module's record page loses its
   styling at once. Same reasoning as "MODULE SETUP PAGES" above. Every one of these selectors
   is declared HERE and nowhere else; moving rather than overriding is deliberate, because the
   partials are inlined AFTER this sheet and would otherwise win every tie.

   Locked by `ui/detailKit.spec.js` (the cascade is resolved against the real sheet + the real
   partials, the `ui/tableSkin.spec.js` shape — whether an override wins is not a question
   source text can answer). */

/* THE STACK — see "THE SPACING HAS ONE OWNER" above. */
.orbit-lead-main { display: flex; flex-direction: column; gap: 0; min-width: 0; }
/* `.orbit-pageform--grouped`'s own `gap: 0` is in `partials/orbit.html`, beside the rule that
   declares that grid. A copy here would be a SAME-WEIGHT duplicate that LOSES — the partials are
   inlined after this sheet — and a losing rule is worse than an absent one, because it reads as
   covered. That one was caught by measuring the real gap in real Chrome, not by reading. */

/* A field group (`dGroup`) and a content section (`dSection`) are ONE shape: the reader must
   not be able to tell which of the two a block of a record came from. */
.orbit-dgroup,
.orbit-lead-sec {
  border: 0;
  border-radius: 0;
  background: none;
  padding: 0;
  margin: 0 0 1.9rem;
  scroll-margin-top: 1rem;
}
.orbit-dgroup:last-child,
.orbit-lead-sec:last-child { margin-bottom: 0; }

/* The heading row, and the one hairline. `min-height` keeps two headings level whether or not
   the group carries a pencil (26px) — without it a group with an Edit button stands taller than
   the one above it and the headings stop reading as one column. */
.orbit-dg-head,
.orbit-lead-sec-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  min-height: 28px;
  padding: 0 0 .45rem;
  margin: 0 0 .9rem;
  border-bottom: 1px solid var(--border-muted);
}
.orbit-dg-head h3,
.orbit-lead-sec-head h3 {
  margin: 0;
  font-size: .76rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--text-subtle);
}
.orbit-dg-note { font-size: .7rem; color: var(--text-faint); font-style: italic; }
.orbit-dg-tools { margin-left: auto; display: inline-flex; align-items: center; gap: .35rem; }
.orbit-dg-toggle { border: 1px solid var(--border); background: var(--bg-surface); color: var(--text-subtle);
  font: inherit; font-size: .7rem; padding: .2rem .5rem; border-radius: 999px; cursor: pointer; white-space: nowrap; }
.orbit-dg-toggle:hover { border-color: var(--primary); color: var(--primary); }
.orbit-dg-edit { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg-surface); color: var(--text-subtle); cursor: pointer; font-size: .8rem; }
.orbit-dg-edit:hover { border-color: var(--primary); color: var(--primary); }

/* The body owns no padding: with no border to inset from, the fields sit on the page's own
   left edge, which is what puts every group's first column on ONE line down the page. */
.orbit-dg-body,
.orbit-lead-sec-body { padding: 0; }

/* NO DIVIDER ABOVE THE REVEALED BLANKS (2026-08-25, user report, circled). It was a dashed
   hairline so "these are the blanks" read structurally — which held while the reader had chosen
   to reveal them, and does not hold in EDIT mode, where `.is-editing` shows every field and the
   line then separates "happened to have a value" from "did not": a distinction the reader
   cannot act on and did not ask to see. The `Show N empty` control already says where the
   blanks begin. The spacing stays; only the line goes. */
.orbit-dg-empties { margin-top: .35rem; padding-top: .35rem; }
.orbit-dg-empties[hidden] { display: none; }

/* ALIGNMENT CARRIES THE STRUCTURE. */
.orbit-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: .45rem 2rem;
  align-items: start;
}
/* NO HAIRLINE UNDER A FIELD CELL (2026-08-25, user report, with the stray line circled). The
   base `.orbit-field` rule is a flat label-left / value-right ROW, where a bottom border
   separates one row from the next and reads correctly — Channels' connection panel and Hive's
   profile are that shape. A `.orbit-detail-grid` cell is not: the label sits ABOVE the value,
   so the same border draws a line under every cell, at a different length per cell, and under
   an EMPTY one it is a mark with nothing above it. Zeroed here rather than deleted from the
   base rule, so the flat lists keep their separator. */
.orbit-detail-grid .orbit-field { flex-direction: column; align-items: stretch; gap: .2rem; padding: .3rem 0 .45rem; border-bottom: 0; }
/* `wide` SPANS THE GROUP, which is what `dField`'s own documentation has promised since it shipped
   — "spans the field across the whole group grid (descriptions, addresses)" — and what NOTHING
   implemented (2026-09-17, user report: the Hidden-fields tick boxes rendering one character per
   line).

   `dField({ wide: true })` emitted `.orbit-field--wide` and no rule anywhere read it, so a field
   asking to span sat in ONE ~200px column instead. 29 files pass the flag: every long description
   and address in the app has been in a narrow column, and anything that puts a GRID inside such a
   field — the field picker does — then asks for more width than the track has and squeezes its
   own rows to nothing.

   A flag the API documents, the markup carries and the stylesheet ignores is the layout form of
   *a control whose value is dropped on the way to the server*: every caller believes it worked. */
.orbit-detail-grid .orbit-field--wide { grid-column: 1 / -1; }
.orbit-detail-grid .orbit-field-label { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }
.orbit-detail-grid .orbit-field-value { text-align: left; font-size: .9rem; }
.orbit-detail-grid .orbit-inline-select { width: 100%; }

/* ═══ ONE TAB STRIP, EVERY PAGE ══════════════════════════════════════════════════════════════
   (2026-09-11, user direction: tabs must read as *"one calm strip"* — a clear active state that
   does not shout, comfortable hit targets, keyboard-reachable and arrow-navigable, the panel
   below separated by SPACE rather than by a box, and no second row of chrome under the strip.)

   This app had SEVEN tab implementations. The five a reader meets daily were four different
   products on four screens: `.pimly-tabs` (the product record and the price-list picker),
   `.artha-formtabs` (every tabbed form in Finance), `.artha-tabs` (Finance's document page and
   the dunning console), `.drf__tabs` (the create draft, which borrowed Pimly's classes), and
   `.pty-tabs` (the party record — PILLS, where the other four drew an underline). They are one
   strip now. `frontend/src/ui/tabKit.js` is the markup, the active state and the keyboard;
   this is the skin.

   THE SHAPE: a row of quiet labels, ONE hairline under the whole strip, a 2px primary underline
   on the one that is on, then SPACE before the panel. The same reasoning as the record-page
   block above — the hairline is the "where one genuinely helps the eye" case, because it is what
   the active underline sits on and what separates the strip from the content it names. Nothing
   else here draws a line: the panel has no border, no tint and no second bar under the strip.

   AN UNDERLINE, NOT A PILL, and the call is written down because two of the five were pills. A
   pill is already this app's shape for a FILTER CHIP (`.lk-*`, `.fc-tab`), so tabs wearing one
   would mean two different controls with one silhouette; and a filled chip shouts where an
   underline states. Usability beats symmetry with the module the pills came from.

   44px HIT TARGETS. Every one of the five was 27-29px tall. `min-height` with `inline-flex`
   centring rather than padding, so a tab carrying a count badge does not stand taller than one
   beside it.

   TWO MECHANISMS, ONE APPEARANCE, ONE DECLARATION — read the active rule below carefully.
   Four hosts switch tabs in JavaScript and mark the live one `.is-on`; the party record switches
   with NO JavaScript at all (radio inputs and a sibling selector, because `registerDetail`
   repaints that record whole on every in-place save and a tab in module state would snap back to
   the first one every time somebody pressed Save). CSS cannot say "the Nth checked input marks
   the Nth label", so the party needs one selector per index — and if those lived in a rule of
   their own, the day this strip is restyled the party record would keep the old look with
   nothing to say so. They are selectors on the SAME declaration instead. `views.js::_TAB_RULES`
   is the matching cap; anything past it is folded into the last panel rather than drawn as a
   label whose content could never appear.

   Locked by `ui/tabKit.spec.js`, which DRIVES the strip (clicks, ArrowLeft/Right, Home/End) and
   resolves this block against the real sheet — every failure here renders perfectly. */

.smz-tabs {
  display: flex;
  flex-wrap: nowrap;
  align-items: stretch;
  gap: 2px;
  margin: 0 0 1.35rem;
  padding: 0;
  border-bottom: 1px solid var(--border-muted);
  /* Wide content scrolls in its OWN container so the page never scrolls sideways. */
  overflow-x: auto;
  scrollbar-width: thin;
}
.smz-tab {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  flex: 0 0 auto;
  min-height: 44px;
  padding: 0 .9rem;
  /* TWO DECLARATIONS THE PARTY RECORD'S TABS CANNOT DO WITHOUT, and neither is visible in jsdom.
     Its tabs are `<label>`s (the CSS-only mechanism below), and this sheet has a global
     `label { display: block; margin: 12px 0 6px }` — an element rule this class beats on every
     property it STATES and loses on every property it does not, so the labels arrived 12px lower
     than the buttons two screens away and their strip stood 58px against 44. And the sheet has
     no global `* { box-sizing: border-box }`: a <button> gets it from a rule of its own and a
     <label> does not, so `min-height: 44px` meant 44 on one and 46 on the other. Measured in
     real Chrome; both read as correct in every source and in every jsdom assertion. */
  box-sizing: border-box;
  /* Sit ON the strip's hairline rather than above it, so the active underline replaces the rule
     instead of doubling it. */
  margin: 0 0 -1px;
  appearance: none;
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-muted);
  white-space: nowrap;
  cursor: pointer;
  transition: color .12s ease, border-color .12s ease;
}
.smz-tab:hover { color: var(--text); }
.smz-tab:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }

/* THE ACTIVE TAB — ONE declaration for both mechanisms. See the note above: the party selectors
   are here, and not in a block of their own, so the two cannot drift apart. */
.smz-tab.is-on,
.pty-tabs > .pty-tabs__in:nth-of-type(1):checked ~ .smz-tabs > .smz-tab:nth-of-type(1),
.pty-tabs > .pty-tabs__in:nth-of-type(2):checked ~ .smz-tabs > .smz-tab:nth-of-type(2),
.pty-tabs > .pty-tabs__in:nth-of-type(3):checked ~ .smz-tabs > .smz-tab:nth-of-type(3),
.pty-tabs > .pty-tabs__in:nth-of-type(4):checked ~ .smz-tabs > .smz-tab:nth-of-type(4),
.pty-tabs > .pty-tabs__in:nth-of-type(5):checked ~ .smz-tabs > .smz-tab:nth-of-type(5),
.pty-tabs > .pty-tabs__in:nth-of-type(6):checked ~ .smz-tabs > .smz-tab:nth-of-type(6),
.pty-tabs > .pty-tabs__in:nth-of-type(7):checked ~ .smz-tabs > .smz-tab:nth-of-type(7),
.pty-tabs > .pty-tabs__in:nth-of-type(8):checked ~ .smz-tabs > .smz-tab:nth-of-type(8),
.pty-tabs > .pty-tabs__in:nth-of-type(9):checked ~ .smz-tabs > .smz-tab:nth-of-type(9),
.pty-tabs > .pty-tabs__in:nth-of-type(10):checked ~ .smz-tabs > .smz-tab:nth-of-type(10),
.pty-tabs > .pty-tabs__in:nth-of-type(11):checked ~ .smz-tabs > .smz-tab:nth-of-type(11),
.pty-tabs > .pty-tabs__in:nth-of-type(13):checked ~ .smz-tabs > .smz-tab:nth-of-type(13),
.pty-tabs > .pty-tabs__in:nth-of-type(14):checked ~ .smz-tabs > .smz-tab:nth-of-type(14),
.pty-tabs > .pty-tabs__in:nth-of-type(15):checked ~ .smz-tabs > .smz-tab:nth-of-type(15),
.pty-tabs > .pty-tabs__in:nth-of-type(16):checked ~ .smz-tabs > .smz-tab:nth-of-type(16),
.pty-tabs > .pty-tabs__in:nth-of-type(12):checked ~ .smz-tabs > .smz-tab:nth-of-type(12) {
  color: var(--primary);
  border-bottom-color: var(--primary);
}

/* A tab that cannot be opened is disabled and keeps its reason in `title` — never live-looking
   with a refusal as its only outcome. */
.smz-tab[disabled] { color: var(--text-faint); cursor: not-allowed; }
.smz-tab[disabled]:hover { color: var(--text-faint); }

.smz-tab__n { font-size: .72rem; font-weight: 600; color: var(--text-faint); }
/* A count that has not arrived yet must not reserve the gap before it — Finance's document
   page paints its badge spans empty and fills them from four best-effort reads. */
.smz-tab__n:empty { display: none; }
.smz-tab.is-on .smz-tab__n { color: inherit; }

/* THE PANEL IS SEPARATED BY SPACE — no border, no tint, no card. The strip's own margin is the
   separation; a panel that added one of its own would put two gaps between one pair of things. */
.smz-tabpanel { min-width: 0; }
/* An author rule setting `display` outranks the user agent's `[hidden]` — origin beats
   specificity — so a panel that is a grid or a flex column shows through it. Third instance in
   this app (`.app-page[hidden]`, `.app-add-form [hidden]`). Any module whose panel carries a
   display inside an ID scope must restate this at its own weight; Artha does. */
.smz-tabpanel[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .smz-tab { transition: none; }
}

/* THE PARTY RECORD'S MECHANISM — the CSS-only half, and only the half the shared rules cannot
   express. The inputs are SIBLINGS of the panels because the general-sibling combinator is the
   only thing that can reveal one; an input nested in the strip can reach nothing. */
.pty-tabs { margin-top: 14px; }
/* ─── A TAB NOBODY CAN REACH IS A TAB NOBODY HAS ──────────────────────────────────────────────
   2026-09-24, user report, THE THIRD TIME: *"why am i still unable to see attachments tab in
   module form"* — after two rounds of me proving from the code that it was there.

   IT WAS THERE. It was CLIPPED. `.smz-tabs` is `flex-wrap: nowrap` + `overflow-x: auto`, which is
   right for a strip of four and silently wrong for one of eleven — and `scrollbar-width: thin`
   over macOS overlay scrollbars means there is NO affordance at all: no arrow, no fade, no
   scrollbar until you are already scrolling. Measured in real Chrome, one strip, three widths:

       1400px   scroll=1400  overflow=  0px   off-screen: none
       1100px   scroll=1137  overflow= 37px   off-screen: Attachments
        900px   scroll=1137  overflow=237px   off-screen: Activity, Notes, Attachments

   ATTACHMENTS IS THE ELEVENTH AND LAST, so it is the first to go and the last to come back. That
   is why every check I ran said the feature worked: it did, in the DOM, off the right-hand edge.

   THE LESSON, because it will happen again to whoever adds a twelfth tab: a horizontal scroll
   container is an answer to content that is WIDE, not to a set of controls that must all be
   FINDABLE. Nothing in the markup, the stylesheet or a jsdom test can tell you a control is off
   the edge — only a measurement in a real browser at a real width.

   THIS EXACT DEFECT WAS FOUND FOUR DAYS EARLIER, ON THE OTHER SURFACE, and fixed only there —
   see `.fc-tabsrow > .fc-tabs` above (2026-09-20: *"the last tab arrived sliced mid-word ('A…'
   for Activity) with nothing to say more existed"*). The canvas learned to wrap; the RECORD page,
   which draws the same eleven tabs through the same `.smz-tabs`, was left with the bug. That is
   why the reader could see every tab in Settings ▸ Layouts and not in the module, and why three
   rounds of checking the code found nothing: the two surfaces genuinely did differ, in the one
   place neither the markup nor a jsdom test can see.

   SO IT WRAPS. Two short rows of tabs, every one visible at any width, no gesture to discover.
   SCOPED to this module's CSS-only strip rather than changed app-wide, for the reason the canvas
   rule states for itself: `.smz-tabs` is the shared strip on every page in the app and most carry
   three or four, where nowrap is right and a second row would be a worse answer. **The same
   clipping is still latent for any OTHER module that grows past ~8 tabs** — named here rather
   than fixed blind, because it needs the same measurement per surface.

   WEIGHT, NOT ORDER: (0,2,0) against the base rule's (0,1,0), so it wins wherever the two sheets
   end up relative to each other. */
.pty-tabs > .smz-tabs {
  flex-wrap: wrap;
  /* With wrapping there is nothing to scroll, and `visible` says so rather than leaving a
     scroll container that can never scroll — the shape that hid a tab in the first place. */
  overflow-x: visible;
}

.pty-tabs__in { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
.pty-tabs > [data-pty-panel] { display: none; }
.pty-tabs > .pty-tabs__in:nth-of-type(1):checked ~ [data-pty-panel="0"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(2):checked ~ [data-pty-panel="1"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(3):checked ~ [data-pty-panel="2"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(4):checked ~ [data-pty-panel="3"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(5):checked ~ [data-pty-panel="4"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(6):checked ~ [data-pty-panel="5"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(7):checked ~ [data-pty-panel="6"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(8):checked ~ [data-pty-panel="7"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(9):checked ~ [data-pty-panel="8"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(10):checked ~ [data-pty-panel="9"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(11):checked ~ [data-pty-panel="10"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(12):checked ~ [data-pty-panel="11"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(13):checked ~ [data-pty-panel="12"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(14):checked ~ [data-pty-panel="13"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(15):checked ~ [data-pty-panel="14"] { display: block; }
.pty-tabs > .pty-tabs__in:nth-of-type(16):checked ~ [data-pty-panel="15"] { display: block; }
/* Keyboard focus must be visible: the radio it belongs to is off-screen by design, and a native
   radio group already moves AND checks on ←/→, which is why `ui/tabKit.js` deliberately ignores
   this strip. Matched per index rather than `~ .smz-tabs .smz-tab`, which outlined EVERY tab at
   once — what the rule it replaces actually did. */
.pty-tabs > .pty-tabs__in:nth-of-type(1):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(1) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(2):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(2) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(3):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(3) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(4):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(4) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(5):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(5) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(6):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(6) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(7):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(7) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(8):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(8) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(9):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(9) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(10):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(10) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(11):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(11) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(12):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(12) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(13):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(13) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(14):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(14) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(15):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(15) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }
.pty-tabs > .pty-tabs__in:nth-of-type(16):focus-visible ~ .smz-tabs > .smz-tab:nth-of-type(16) { outline: 2px solid var(--primary); outline-offset: -2px; border-radius: 6px; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE ONE RAIL — a single accordion navigation column (2026-09-13, user direction)
   ═══════════════════════════════════════════════════════════════════════════════════════════

   Replaces the header waffle + icon rail + page rail: every module is a vertical tab, one open
   at a time; the open module's destinations are the main sections, one expanded at a time; that
   section's pages are the selectable rows beneath it. Built by `ui/navRail.js`.

   THE RAMP COLLAPSES. It was page -> second rail -> first rail + band, three surfaces reading as
   depth. With ONE rail there are two: page -> rail + band. So `.smz-nav` re-declares the standard
   names FROM the `--chrome-*` palette, exactly as `.topbar` does — never from literals, which is
   what would strand the rail dark on a light page. `--rail2-*` is no longer consumed here.

   NO `:is()` in this block. WebKit ships it with specificity ZERO, so a rule written with one
   loses ties it should win — the same finding recorded against the first rail's ink.

   The module's OWN rail is PARKED, not hidden: `display: none` is how a MODULE stands its own
   rail down, and overwriting it would erase the one signal `activeModuleRail` reads. So the
   source nodes are moved off-screen and left laid out. `.sidebar` is the precedent — a whole
   module rail kept in the markup purely as a data source.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

:root {
  --nav-w: 248px;           /* dragged by .rail-resizer, persisted; see controllers/rails.js */
  --nav-indent: 14px;       /* one level of nesting */
  /* The row's own furniture, named so the levels can line up against each other rather than
     against a number somebody measured once. A page row has no chevron, so it has to pay for the
     column its parent's chevron occupies or it reads as the OUTDENTED one. */
  --nav-chev: 13px;
  --nav-gap: 7px;
  /* THE THREE LEVELS STEP DOWN — 34 / 30 / 28. A tree of one row height asks the reader to find
     the hierarchy in the indent alone; a size step states it before they read a word, which is
     what Fiori, VS Code and every mature admin shell do. The floor is WCAG 2.2 AA Target Size
     (Minimum), 24x24 CSS px: the smallest row here is 28px tall and full-width, well clear. */
  --nav-row-mod: 34px;
  --nav-row-sec: 30px;
  --nav-row-page: 28px;
  --nav-radius: 8px;
}

/* ── The source nodes: read, never seen ─────────────────────────────────────────────────── */
.smz-nav-src {
  position: fixed !important;
  left: -12000px !important;
  top: 0 !important;
  width: var(--nav-w) !important;
  max-height: 100vh !important;
  margin: 0 !important;
  opacity: 0 !important;
  pointer-events: none !important;
}

/* ── The column ─────────────────────────────────────────────────────────────────────────── */
/* A SURFACE OF ITS OWN (2026-09-14, user direction: "make left Menu feel like separate box, so
   create some border line and little different color for it").

   THE RAMP GOES BACK TO THREE STEPS: page -> column -> band. It was collapsed to two when the two
   rails became one, on the reasoning that with a single rail there was a single step to make — and
   that produced a column the same tone as the header, which reads as one L-shaped piece of chrome
   wrapping the page rather than as a menu beside it.

   `--rail2-*` is that middle step, and it is not a new palette: it was written for exactly this
   surface (the second rail, one step back toward the content) and survived the rails' retirement
   with both themes intact. The column re-declares the STANDARD names from it, never from literals
   — the thing that would otherwise strand it light on a dark page.

   The right edge is `--rail2-border` rather than the chrome's divider: a box needs an edge that
   reads as its own, and the divider was chosen to be invisible between two surfaces of one tone. */
.smz-nav {
  position: fixed;
  /* IT OPENS FROM ITS OWN SIDE (2026-09-19, user direction: "left menu left edge and right menu
     right edge should not be there and user should feel like they are opened from their sides").
     So the OUTER edge is gone — no gap, no border, no corners — and the panel runs off the side
     of the screen; only the edge facing the content is drawn and rounded. A panel inset and
     bordered on all four sides reads as something floating in the middle of the page that
     happens to be near the edge; anchored, it reads as something the edge opened. */
  left: 0;
  /* NO GAP AT THE TOP (2026-09-19, user direction: "move all 3 parts up close to header"). The
     seam is left on the other three sides, where it is what makes the curves read against the
     ground; above, the thing behind the panel is the header BAND — its own surface, its own
     colour — so a strip of ground between the two was dead space, not a seam. The height gives
     back ONE gap now, not two. */
  top: var(--chrome-h, 56px);
  /* RESTATED rather than inherited: 100vh from an offset overflows by exactly the offset. */
  height: calc(100vh - var(--chrome-h, 56px) - var(--panel-gap));
  box-sizing: border-box;
  width: var(--nav-w);
  z-index: 10;
  display: flex;
  flex-direction: column;
  background: var(--rail2-bg);
  /* A FULL BORDER, not the single right edge it used to carry. A rounded box with one border
     side draws a line that curves into a corner and stops, which reads as a rendering fault
     rather than a panel. Nothing inside this column paints its own background at an edge — every
     row is transparent until it is hovered, and the hover wash is inset by the column's padding —
     so the curve is clean with no child needing a radius of its own. */
  border: 1px solid var(--rail2-border);
  border-left: 0;                                             /* the screen's edge is this edge */
  border-radius: 0 var(--panel-radius) var(--panel-radius) 0;
  /* The column's own ink, from the column's own palette. */
  --text: var(--rail2-text);
  --text-muted: var(--rail2-text-muted);
  --text-subtle: var(--rail2-text-subtle);
  --text-faint: var(--rail2-text-subtle);
  --bg-muted: var(--rail2-wash);
  --bg-hover: var(--rail2-wash);
  --wash-ink: var(--rail2-wash);
  --wash-ink-strong: var(--rail2-wash-strong);
  --border: var(--rail2-border);
  --border-muted: var(--rail2-border);
  --primary: var(--rail2-primary);
  color: var(--rail2-text);
}
body.smz-nav-off .smz-nav { display: none; }

/* Scrolling is a CLASS, applied only when the column genuinely overflows: `overflow-y` clips in
   BOTH axes, and this column's tooltips and fly-outs go to the RIGHT. A short rail keeps them. */
.smz-nav__scroll {
  flex: 1 1 auto;
  min-height: 0;
  /* The brand is PINNED over the column's bottom edge, so the scroller pays for it here —
     otherwise the last module row sits underneath it and the list looks like it simply stops. */
  padding: 6px 8px 8px;
}
.smz-nav.is-scrolling .smz-nav__scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ── THE SCROLLBAR IS VISIBLE WHENEVER THE COLUMN OVERFLOWS ──────────────────────────────────
   User direction 2026-09-14: "display scrollbar in left menu by default, if it has more items."

   It was not. Measured in Chrome on macOS: `offsetWidth - clientWidth` was **0**, meaning an
   OVERLAY scrollbar — one that takes no layout space and, on macOS, is invisible until you are
   already scrolling. A reader with 41 modules saw a list that simply stopped, with nothing to say
   there was more below it.

   The cause was `scrollbar-width: thin` sitting beside the `::-webkit-scrollbar` rules. Chrome
   (121+) honours the STANDARD properties when they are present and IGNORES the webkit
   pseudo-elements entirely — so declaring both does not mean "whichever the browser supports", it
   means the standard one wins and the styling below never applied. Removing it hands the element
   back to `::-webkit-scrollbar`, which is what forces a classic, space-taking, always-drawn
   scrollbar in Chrome and Safari.

   FIREFOX ONLY sees the standard properties, and there `thin` is already persistent rather than
   overlay — so they are kept for it, behind a feature query that is false in the engines where
   they would undo the rules above. Two declarations for one intent, each scoped to the engine
   that needs it, rather than both fighting in every engine. */
@supports not selector(::-webkit-scrollbar) {
  .smz-nav.is-scrolling .smz-nav__scroll {
    scrollbar-width: thin;
    scrollbar-color: var(--rail2-wash-strong) transparent;
  }
}
/* A thin, self-effacing scrollbar. The default is a 15px light-grey slab that reads as a second
   border down the middle of the chrome. */
/* THE MODULE MENU SCROLLS THE SAME WAY THE COLUMN DOES (2026-09-21). It is capped to fifteen
   rows and an org runs forty modules, so it overflows for nearly everybody — and a webkit
   scrollbar is the only kind that takes layout space and is drawn before you touch it. Declared
   unconditionally rather than behind `is-scrolling`: the menu is open only while somebody is
   using it, so there is no resting state for a bar to intrude on. */
.smz-nav__modmenu::-webkit-scrollbar { width: 8px; }
.smz-nav__modmenu::-webkit-scrollbar-track { background: transparent; }
.smz-nav__modmenu::-webkit-scrollbar-thumb {
  background: var(--border); border-radius: 4px; border: 2px solid transparent;
  background-clip: content-box;
}
.smz-nav__modmenu::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
.smz-nav.is-scrolling .smz-nav__scroll::-webkit-scrollbar { width: 8px; }
.smz-nav.is-scrolling .smz-nav__scroll::-webkit-scrollbar-track { background: transparent; }
.smz-nav.is-scrolling .smz-nav__scroll::-webkit-scrollbar-thumb {
  /* Definite enough to READ at rest, which is the whole point of showing it by default — at
     `--rail2-wash-strong` (0.14 alpha) the thumb was a suggestion. Mixed from the chrome's own
     subtle ink so it follows both themes with nothing here to keep in step. */
  background: color-mix(in srgb, var(--rail2-text-subtle) 45%, transparent);
  border: 2px solid transparent;
  background-clip: content-box;
  border-radius: 99px;
}
.smz-nav.is-scrolling .smz-nav__scroll::-webkit-scrollbar-thumb:hover {
  background: color-mix(in srgb, var(--rail2-text-subtle) 72%, transparent);
  background-clip: content-box;
}

/* The separator survives for ONE case: the open pinned module (Settings) taking its place at the
   end of the scrolling list, where it needs a boundary the foot's own border would have given it.
   It is no longer drawn under Home — see navRail.js. */
.smz-nav__sep { height: 1px; background: var(--rail2-border); margin: 6px 6px; }
.smz-nav__group { display: block; }
/* Home reads as pinned rather than as the first module in the list. 4px, not 10 — at 10 it read as
   a gap somebody had left rather than as a grouping (2026-09-14, user report). */
.smz-nav__group + .smz-nav__group { margin-top: 4px; }
/* A tree with nothing in it still carried a margin, a padding and an indent guide — a small
   bordered blank between two rows. The JS drops it now; this is the belt for anything that slips
   through, because an empty box is indistinguishable from a layout bug. */
.smz-nav__tree:empty, .smz-nav__pages:empty { display: none; }

/* ── A module row ───────────────────────────────────────────────────────────────────────── */
/* One selector list for the button and the open row's <div>, because they must be the same row
   at two different jobs. The open one is NOT a button: expansion is derived, so it has nothing
   to toggle, and a button that does nothing is invisible to every other check. */
.smz-nav__mod {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;      /* no global `* { box-sizing }` in this sheet */
  min-height: var(--nav-row-mod);
  padding: 4px 8px 4px 4px;
  border: 0;
  border-radius: var(--nav-radius);
  background: transparent;
  color: var(--rail2-text);
  font: inherit;
  font-size: .815rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  /* Colour only — never a transition on a box property, which would animate the whole column
     every time a row is added. 120ms is below the threshold where a hover feels laggy. */
  transition: background-color 120ms ease, color 120ms ease;
}
.smz-nav__mod:hover { background: var(--rail2-wash); }
/* THE MODULE SELECTOR (2026-09-19, user direction). A BUTTON and a listbox rather than a native
   `<select>`: the browser places a select's popup itself, anchoring the selected option over the
   control, so the list opened upward whenever the selection sat far down it — "dropdown should
   always go down, it should not go up".

   The button takes the column's own rhythm: the module row's height and inset, so the sections
   below line up under it, and the column's ink rather than a form surface's — this is chrome, not
   a field on a page. */
.smz-nav__modsel { position: relative; padding: 2px 4px 6px; }
.smz-nav__modbtn {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;
  min-height: var(--nav-row-mod);
  padding: 4px 8px;
  border: 1px solid var(--rail2-border);
  border-radius: var(--nav-radius);
  background: transparent;
  color: var(--rail2-text);
  font: inherit;
  font-size: .815rem;
  font-weight: 640;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
}
.smz-nav__modbtn-lbl { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.smz-nav__modbtn-chev { flex: 0 0 auto; font-size: .7rem; opacity: .8; }
.smz-nav__modbtn:hover { background: var(--rail2-wash); }
.smz-nav .smz-nav__modbtn:focus-visible { outline: 2px solid var(--rail2-primary); outline-offset: -2px; }

/* THE LIST. `position: fixed`, because the column scrolls and `overflow-y` clips in BOTH axes —
   a menu laid out inside the scroller is cut off by it, which is this app's oldest overlay
   defect. Its coordinates are arithmetic (`ui/menuPlacement.js`, `noFlip`), never CSS. */
/* THE ADMINISTRATION FOLD (2026-09-21, user direction: "settings and app admin in left menu
   should have collapse button"). Only the two pinned foot rows carry it — Settings unfolds ~20
   sections under the module somebody is actually working in, and there was no way back short of
   navigating away. The triangle sits at the row's end, after the label, so the icon and the name
   still line up with every other row in the column. */
.smz-nav__mod--fold { justify-content: flex-start; }
.smz-nav__mod--fold .smz-nav__lbl { flex: 1 1 auto; min-width: 0; }
.smz-nav__fold {
  flex: 0 0 auto; margin-left: auto; padding-left: 6px;
  font-size: .7rem; line-height: 1; color: var(--text-faint);
}
.smz-nav__mod--fold:hover .smz-nav__fold { color: var(--text); }

.smz-nav__modmenu {
  position: fixed;
  z-index: 60;
  overflow-y: auto;
  /* A VISIBLE SCROLLBAR, THE WAY THIS COLUMN ALREADY DOES IT (2026-09-20, user report: "when it
     has big list user not able scroll it"; corrected 2026-09-21). The list is capped to fifteen
     rows, so with forty modules there is always more below the fold — and an overflow with no
     visible bar is a list that looks complete and is not.

     NOT `scrollbar-width`, WHICH IS WHAT THIS FIRST SHIPPED AS AND IS THE OPPOSITE OF THE FIX.
     Chrome (121+) honours the STANDARD scrollbar properties when they are present and ignores
     `::-webkit-scrollbar` entirely — so declaring it here would have turned the bar into an
     OVERLAY: invisible until you are already scrolling, on the exact control somebody reported
     as unscrollable. `.smz-nav__scroll` beside it learnt this, `ui/navScrollBoundary.spec.js`
     wrote the rule down, and that test is what caught this.

     `overscroll-behavior: contain` keeps the scroll inside it, so reaching the end does not
     start moving the page behind. */
  overscroll-behavior: contain;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--nav-radius);
  background: var(--bg-surface);
  box-shadow: 0 10px 28px rgb(0 0 0 / 18%);
}
.smz-nav__modopt {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;
  min-height: 30px;
  padding: 4px 8px;
  border: 0;
  border-radius: var(--nav-radius);
  background: transparent;
  /* The page's ink, not the column's: this surface is drawn on `--bg-surface`, so a dark column's
     white text would land on a light menu. */
  color: var(--text-normal);
  font: inherit;
  font-size: .8125rem;
  text-align: left;
  cursor: pointer;
}
.smz-nav__modopt:hover { background: var(--bg-hover); }
.smz-nav__modopt.is-on { font-weight: 700; }
.smz-nav .smz-nav__modopt:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.smz-nav__modopt .smz-nav__ico { color: var(--text-muted); }
/* "◂ All modules" — the way back from the module you drilled into (2026-09-19, user direction:
   the column is two views now). Deliberately the module row's own shape and NOT a module: it is
   the same height and the same hit target, so the column keeps one rhythm, and it is muted and
   uppercase-free because it names an ACTION rather than a place you can be. */
.smz-nav__back {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;
  min-height: var(--nav-row-mod);
  padding: 4px 8px 4px 4px;
  margin-bottom: 2px;
  border: 0;
  border-radius: var(--nav-radius);
  background: transparent;
  color: var(--rail2-muted, var(--text-muted));
  font: inherit;
  font-size: .78rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.smz-nav__back:hover { background: var(--rail2-wash); color: var(--rail2-text); }
.smz-nav .smz-nav__back:focus-visible {
  outline: 2px solid var(--rail2-primary);
  outline-offset: -2px;
  border-radius: var(--nav-radius);
}
/* A focus ring that is VISIBLE ON THE CHROME, not the page. `outline-offset: -2px` keeps it inside
   the row so it cannot be clipped by the scroller. */
.smz-nav .smz-nav__mod:focus-visible,
.smz-nav .smz-nav__dest:focus-visible,
.smz-nav .smz-nav__pagesrc button:focus-visible,
.smz-nav .smz-nav__pagesrc a:focus-visible {
  outline: 2px solid var(--rail2-primary);
  outline-offset: -2px;
  border-radius: var(--nav-radius);
}

/* THE OPEN MODULE IS A HEADING, NOT A SELECTION. It used to wear the same wash as a hover, which
   made the row you are IN look like the row you are OVER — and put a second filled block in a
   column that already fills the active PAGE. Weight and ink carry it instead, which is what the
   record-page pass settled for section headings: whitespace and weight separate, chrome does not. */
.smz-nav__mod.is-open {
  cursor: default;
  font-weight: 680;
  color: var(--rail2-text);
  background: transparent;
}
.smz-nav__mod.is-open:hover { background: transparent; }

/* The disclosure mark. At .58rem the glyphs rendered as full stops — a triangle small enough to
   read as punctuation says nothing about whether a row opens. */
/* The disclosure mark sits TIGHT AGAINST the icon, not adrift at the row's edge. At the chevron's
   old position the eye had to cross 24px of nothing to get from the triangle to the thing it
   discloses, and the two read as unrelated controls. */
.smz-nav__chev {
  flex: 0 0 var(--nav-chev);
  width: var(--nav-chev);
  margin-right: -2px;
  font-size: .72rem;
  line-height: 1;
  text-align: center;
  color: var(--rail2-text-subtle);
  transition: transform 140ms ease, color 120ms ease;
}
.smz-nav__mod:hover > .smz-nav__chev,
.smz-nav .smz-nav__dest:hover > .smz-nav__chev { color: var(--rail2-text); }
.smz-nav__mod.is-open > .smz-nav__chev,
.smz-nav .smz-nav__dest[aria-expanded='true'] > .smz-nav__chev { color: var(--rail2-text); }
.smz-nav__ico { flex: 0 0 17px; width: 17px; height: 17px; display: inline-flex; align-items: center; justify-content: center; }
.smz-nav__ico svg { width: 16px; height: 16px; }
.smz-nav__lbl { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── EACH MODULE IS A TAB, AND ITS CONTENTS SIT INSIDE IT ────────────────────────────────────
   User direction 2026-09-14: "make each module as tab in left menu, when we click on that tab
   that should expand and collapse. when expanded all module related content should be like inside
   that tab box."

   So the open row and its tree are ONE box: the row is the tab's header, the tree is its body, and
   they share a border and a surface. The row keeps its own radius at the top, the body picks it up
   at the bottom, and nothing is drawn between them — a line there would cut the tab in half, which
   is the opposite of "inside".

   The body sits a step FURTHER from the column than the column is from the page. That is the same
   ramp reasoning one level down: a panel inside a panel has to be distinguishable from it, and the
   cheapest honest way is another step in the same direction rather than a second border. */
/* THE OPEN MODULE IS MARKED BY A SPINE, INK AND A FADE — NOT BY A FILLED RECTANGLE (2026-09-14,
   user direction, in three steps: "keep only border for tabbed box", then "Module name should have
   highlighted background", then "we may need better highlight option rather than box").
   A flat wash across the row is a grey rectangle sitting on a grey column: it is the weakest
   contrast the column can offer AND the most furniture, which is why it read as a box rather than
   as emphasis. Three cheap signals replace it, and none of them draws an edge:
     - a 3px PRIMARY SPINE down the left of the open tab, carried by the border the box already
       has, so the header and its body share one continuous line and the tab has a side to hang
       from. This is a real BORDER, not the inset bar the active page was refused: a border follows
       the radius instead of being clipped into a stubby arc by it (see the note at the active-page
       rule), and it is on the tab EDGE where the fill would have been on the row.
     - the ink at full strength and 640, where every shut module sits at 500. Weight is the signal
       people actually read a list by.
     - a wash that FADES OUT to the right. A gradient has no right edge, so it lifts the name
       without enclosing it — the thing that made a flat fill look like a container.
   Still neutral, never primary: the primary FILL belongs to the active page, and it stays the only
   filled row in the column. The spine is primary because it is a line, not a fill, so the two
   cannot be mistaken for each other at a glance. */
/* NO BOX AROUND THE OPEN MODULE (2026-09-19, user direction: "Remove box selection Highlighter
   for selected module concept, no need to box highlighter across leftmenu").
   The border, the primary spine and the fading wash are all gone; WEIGHT and INK carry it, which
   is what the paragraph above already said people actually read a list by. The box only ever had
   a job while the open module sat among forty others — the selector names it now, and a frame
   around the one thing on screen encloses nothing. */
.smz-nav__mod.is-open {
  background: transparent;
  color: var(--rail2-text);
  font-weight: 640;
}
/* `[data-nav-toggle]` and `.is-shut` ARE DELETED with the collapse (2026-09-19, user direction:
   "no need to collapse or expand the module names"). */

/* The tree's own half of the box went with it (2026-09-19). The INDENT GUIDES below stay — they
   are what tells a section's pages from the section below it, which is a different job from
   enclosing the module. */
.smz-nav__tree { background: transparent; }

/* ── The open module's tree ─────────────────────────────────────────────────────────────── */
/* ── INDENT GUIDES ───────────────────────────────────────────────────────────────────────────
   A hairline down each level of nesting. At three levels an indent alone stops being readable —
   the eye cannot tell, four rows down a long list, whether a row belongs to the section above it
   or to the module above that. A guide answers it without a word, which is why every mature tree
   in this class of software draws one (VS Code, Notion, Linear, Fiori).

   Drawn with a border on the CONTAINER rather than a pseudo-element on each row, so it is one
   continuous line rather than a dotted column of segments, and it costs no extra node per row. */
.smz-nav__tree {
  display: block;
  /* Flush with the selector above it. */
  margin: 0 0 4px;
  padding: 3px 0 5px var(--nav-indent);
}
.smz-nav__destrow { display: block; }
.smz-nav__tree > .smz-nav__destrow { border-left: 1px solid var(--rail2-border); padding-left: 6px; }
.smz-nav__pages {
  margin-left: calc(4px + var(--nav-chev) / 2);
  padding-left: calc(var(--nav-indent) + var(--nav-gap));
  border-left: 1px solid var(--rail2-border);
}

/* A mirrored DESTINATION. The clone keeps the module's own classes (`.wk-iconbtn` and whatever
   else it carried), so everything below re-states the geometry it needs and nothing relies on
   the module's icon-tile rules — which were written for a 58px column and do not apply here.
   Doubled class for weight, longhand rather than `:is()`. */
.smz-nav .smz-nav__dest.smz-nav__dest {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;
  min-height: var(--nav-row-sec);
  padding: 3px 8px 3px 4px;   /* the INDENT is on the tree container, not here */
  border: 0;
  border-radius: var(--nav-radius);
  background: transparent;
  color: var(--rail2-text-muted);
  font: inherit;
  font-size: .8rem;
  font-weight: 500;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
  /* The icon-rail shape has to be undone explicitly: those buttons stack an icon over a caption
     in a fixed square, and a leftover `flex-direction: column` reads as a broken row. */
  flex-direction: row;
  height: auto;
  width: 100%;
}
.smz-nav .smz-nav__dest.smz-nav__dest:hover { background: var(--rail2-wash); color: var(--rail2-text); }
/* The OPEN section is a heading too — ink and weight, no fill. The only filled row in the column
   is the page you are on, so "where am I" has exactly one answer on screen at a time. */
.smz-nav .smz-nav__dest.smz-nav__dest[aria-expanded='true'],
.smz-nav .smz-nav__dest.smz-nav__dest.is-on { color: var(--rail2-text); font-weight: 640; }
/* The caption `destRailHtml` renders UNDER the icon for a narrow column is the row's label here,
   so it stops being a caption and becomes the text. */
.smz-nav .smz-nav__dest .wk-iconbtn__cap {
  font-size: .8rem;
  line-height: 1.3;
  letter-spacing: 0;
  text-transform: none;
  max-width: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1 1 auto;
  min-width: 0;
}
.smz-nav .smz-nav__dest .hicon-wrap { flex: 0 0 17px; width: 17px; height: 17px; }
.smz-nav .smz-nav__dest .hicon-wrap svg { width: 15px; height: 15px; }

/* ── The pages under the open destination ───────────────────────────────────────────────── */
/* The whole rail is cloned as ONE block (five different rail shapes in this app; a reader per
   shape would stop matching the day its module changed). So the wrapper neutralises whatever
   positioning the rail carried and the rows inside are styled by their own classes below. */
.smz-nav__pages { display: block; padding-bottom: 4px; }
.smz-nav .smz-nav__pagesrc {
  position: static !important;
  left: auto !important;
  top: auto !important;
  width: 100% !important;
  height: auto !important;
  max-height: none !important;
  min-height: 0 !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  background: transparent !important;
  overflow: visible !important;
  flex: none !important;
  display: block !important;
  box-shadow: none !important;
  z-index: auto !important;
}

/* A MISSING ICON STILL RESERVES ITS COLUMN. An icon name this app has no glyph for renders an
   EMPTY span, and an empty span with no width collapses — so "Leads" sat 19px left of "Accounts"
   in the same list, measured in Chrome. The box is fixed, so a row without a glyph still lines up
   with the rows that have one, and a newly-added icon shifts nothing. */
.smz-nav .smz-nav__pagesrc .hicon-wrap {
  flex: 0 0 17px;
  width: 17px;
  height: 17px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.smz-nav .smz-nav__pagesrc .hicon-wrap svg { width: 15px; height: 15px; }

/* Every page row, whichever rail shape it came from.
   THE NAMED LIST IS NOW A FLOOR, NOT THE RULE (2026-09-14, user report: "items are congested",
   with a screenshot of SETTINGS' page list — rows jammed together at browser defaults, no
   padding, no gap between an icon and its label, and the group head not in caption voice, while
   Kriya's list two rows above was perfect). Settings' rail emits `.set-nav-btn`, which was in no
   list here — and the sweep that was supposed to catch that reads the shared TOOLKITS' source,
   so a rail a MODULE renders by hand is invisible to it. That is "the sweep is only as wide as
   its own array" for the third time in this repo (Customers & Vendors' seven unskinned tables,
   tableKit's undeclared `tk-num`), and the answer is to stop keeping a list of who is covered:
   a plain `button`/`a[href]` selector cannot omit anybody. The named classes stay because they
   carry more specificity, which is what beats a module's own rail rules; the generic pair is
   what catches the shape nobody registered. */
.smz-nav .smz-nav__pagesrc button,
.smz-nav .smz-nav__pagesrc a[href],
.smz-nav .smz-nav__pagesrc .smz-rail-btn,
.smz-nav .smz-nav__pagesrc .wk-rail__item,
.smz-nav .smz-nav__pagesrc .wk-rail__sub,
.smz-nav .smz-nav__pagesrc .iq-rail__item,
.smz-nav .smz-nav__pagesrc .kriya-rail-btn,
.smz-nav .smz-nav__pagesrc .forge-nav-btn,
.smz-nav .smz-nav__pagesrc .appadmin-nav-btn,
.smz-nav .smz-nav__pagesrc .hive-subnav-btn,
.smz-nav .smz-nav__pagesrc .orbit-rail__item {
  display: flex;
  align-items: center;
  gap: var(--nav-gap);
  width: 100%;
  box-sizing: border-box;
  min-height: var(--nav-row-page);
  padding: 3px 8px 3px 4px;   /* the INDENT is on .smz-nav__pages */
  border: 0;
  border-radius: var(--nav-radius);
  background: transparent;
  box-shadow: none;
  color: var(--rail2-text-muted);
  font: inherit;
  font-size: .79rem;
  font-weight: 450;
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}
.smz-nav .smz-nav__pagesrc button:hover,
.smz-nav .smz-nav__pagesrc a[href]:hover,
.smz-nav .smz-nav__pagesrc .smz-rail-btn:hover,
.smz-nav .smz-nav__pagesrc .wk-rail__item:hover,
.smz-nav .smz-nav__pagesrc .wk-rail__sub:hover,
.smz-nav .smz-nav__pagesrc .iq-rail__item:hover,
.smz-nav .smz-nav__pagesrc .kriya-rail-btn:hover,
.smz-nav .smz-nav__pagesrc .forge-nav-btn:hover,
.smz-nav .smz-nav__pagesrc .appadmin-nav-btn:hover,
.smz-nav .smz-nav__pagesrc .hive-subnav-btn:hover,
.smz-nav .smz-nav__pagesrc .orbit-rail__item:hover {
  background: var(--rail2-wash);
  color: var(--rail2-text);
}
/* THE ACTIVE PAGE. An inset bar plus weight, never a filled pill: a filled chip is this app's
   shape for a FILTER, so a page wearing one would be two controls with one silhouette — the
   same call `ui/tabKit.js` records for tabs. */
.smz-nav .smz-nav__pagesrc .smz-rail-btn.is-active,
.smz-nav .smz-nav__pagesrc .wk-rail__item.is-active,
.smz-nav .smz-nav__pagesrc .wk-rail__sub.is-active,
.smz-nav .smz-nav__pagesrc .iq-rail__item.is-active,
.smz-nav .smz-nav__pagesrc .kriya-rail-btn.is-active,
.smz-nav .smz-nav__pagesrc .forge-nav-btn.is-active,
.smz-nav .smz-nav__pagesrc .appadmin-nav-btn.is-active,
.smz-nav .smz-nav__pagesrc .hive-subnav-btn.is-active,
.smz-nav .smz-nav__pagesrc .orbit-rail__item.is-active,
.smz-nav .smz-nav__pagesrc [aria-current='page'] {
  color: var(--rail2-primary);
  font-weight: 640;
  background: var(--rail2-wash-primary);
}
/* NO INSET ACCENT BAR. A 2px bar drawn inside an 8px-radiused row is clipped into a stubby arc at
   both ends — it reads as a rendering fault rather than as an accent, and it is a SECOND signal
   for a state the fill already states. One filled row is the whole answer to "where am I", and it
   is the only fill in the column. (A bar is the right shape for a SQUARE row, which is what this
   was copied from.) */
/* A group HEADING inside a rail is prose about a set of pages, not a control — it keeps its
   caption voice and never grows a hover state. */
.smz-nav .smz-nav__pagesrc .smz-rail-head,
.smz-nav .smz-nav__pagesrc .wk-rail__head,
.smz-nav .smz-nav__pagesrc .kriya-rail-label,
.smz-nav .smz-nav__pagesrc .settings-rail__head,
.smz-nav .smz-nav__pagesrc .oa-nav__head,
.smz-nav .smz-nav__pagesrc .iq-rail__head {
  padding: 9px 8px 3px 4px;   /* the INDENT is on .smz-nav__pages */
  margin: 0;
  font-size: .625rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--rail2-text-subtle);
  background: transparent;
  /* A caption, not a control: it must never look pressable. */
  pointer-events: none;
}
/* AN IN-ROW CONTROL IS NOT A ROW. The generic `button` selector above reaches a ⋯ kebab and any
   other button a rail nests INSIDE a row, and a full-width flex item there would push the label
   out. Anything that sits in a row-wrapper opts back out; the row itself never matches this. */
.smz-nav .smz-nav__pagesrc .iq-rail__row button:not(.iq-rail__item),
.smz-nav .smz-nav__pagesrc .wk-rail__row button:not(.wk-rail__item) {
  width: auto;
  min-height: 0;
}
/* A per-row ⋯ stays where its rail put it — at the row's end, revealed on hover. Its menu is
   positioned through `railAnchorFor`, because the button it was opened from is the parked
   original, thousands of pixels to the left. */
.smz-nav .smz-nav__pagesrc .wk-rail__row,
.smz-nav .smz-nav__pagesrc .iq-rail__row { display: flex; align-items: center; }
.smz-nav .smz-nav__pagesrc .wk-rail__kebab,
.smz-nav .smz-nav__pagesrc .iq-rail__kebab { flex: 0 0 auto; opacity: 0; }
.smz-nav .smz-nav__pagesrc .wk-rail__row:hover .wk-rail__kebab,
.smz-nav .smz-nav__pagesrc .iq-rail__row:hover .iq-rail__kebab { opacity: 1; }

/* "Show all screens" — the escape hatch renders INTO the rail it expands, so no state exists
   where a reader can tell screens are hidden and cannot reach them. */
.smz-nav .smz-nav__pagesrc .smz-rail-showall {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  box-sizing: border-box;
  margin-top: 4px;
  padding: 5px 10px 5px 6px;   /* the INDENT is on .smz-nav__pages */
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--rail2-primary);
  font: inherit;
  font-size: .74rem;
  cursor: pointer;
}
.smz-nav .smz-nav__pagesrc .smz-rail-showall:hover { background: var(--rail2-wash-primary); }

/* ── SETTINGS IS ALWAYS ONE CLICK AWAY ───────────────────────────────────────────────────────
   A flat list of every enabled module runs to ~41 rows, so administration sits below the fold on a
   laptop and is reached by scrolling past the whole estate. The foot is a region that DOES NOT
   SCROLL, above the brand.

   `position: sticky` was tried first and read wrong: the pinned block floats over a list that
   carries on behind AND below it (the scroller's own bottom padding showed a row underneath the
   thing pinned to the bottom), so the column appeared to end twice. A region outside the scroller
   is the honest version, and it is what every shell of this shape does. */
/* THE FOOT SCROLLS, SO ITS ROWS NEVER HAVE TO MOVE (2026-09-15, user report: "when app admin or
   settings opened it is going top … they should expand only from bottom to top").
   An open pinned row used to be relocated into the scrolling list, because the foot could not
   scroll and Settings open is sixteen destinations deep. That solved a SCROLLING problem by
   changing POSITION, so pressing Settings made Settings jump to the top of the column. Capping
   the foot and letting it scroll keeps the row where it is and lets its tree open upward from the
   edge it is anchored to.
   `flex-shrink: 1` with the cap: the foot may take up to 60% of the column when a pinned module
   is open, and the module list above it gives that space up rather than the two fighting. */
.smz-nav__foot {
  /* THE FOOT NEVER SHRINKS — `0 0 auto`, and the `0` for shrink is the whole fix (2026-09-15,
     user report: "when there more left menu items, it is reducing settings and app admin space").

     It was `0 1 auto`, which reads as "do not grow, but give way under pressure" and is exactly
     wrong here: the foot exists so administration is NOT below the fold behind 41 modules, and an
     org with a long module list is precisely when that matters. With 26 modules the foot was
     squeezed to about one row — App Admin gone, Settings clipped.

     AND IT COULD SHRINK TO NOTHING BECAUSE OF THE LINE BELOW IT. A flex item's automatic minimum
     size is its CONTENT — which would have stopped this on its own — except that an item which is
     itself a scroll container has an automatic minimum of ZERO. So adding `overflow-y: auto` (for
     the case where App Admin's tree is expanded) silently removed the floor that was protecting
     the foot, and the two declarations are only wrong TOGETHER. That is why the shrink factor is
     stated rather than left at its default.

     The scroller absorbs all of it instead: it is `flex: 1 1 auto` with `min-height: 0`, which is
     what makes it shrinkable and scrollable. The `max-height` still caps the foot when its own
     tree is open, so a foot cannot eat the module list either. */
  flex: 0 0 auto;
  max-height: 60vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 8px;
  border-top: 1px solid var(--rail2-border);
}
.smz-nav__foot:empty { display: none; }

/* A SCOPE SELECT IN THE SHELL'S TOOLS ROW (2026-09-20). Row 2's left cluster is "which subset am
   I looking at", and `lkScope` answers that with a BUTTON — right for All/Mine/Recent, wrong for a
   list of twelve modules. Kriya's `.wk-groupsel` is the same control and is scoped to `#kriya-tab`,
   so this is the unscoped version of it for a page that declares its toolbar through
   `settingsChrome`. Same height as `wk-iconbtn` so the row does not grow around it. */
/* NO BORDER, NO FILL, NO BOX (2026-09-24, user direction: *"it is showing with extra borders.
   remove all its borders, keep it simple drop down, just UI change"* — reported against Settings
   ▸ Numbering and Settings ▸ Forms, which share this shape).
   It was a bordered, filled, 8px-radiused box sized to match `wk-iconbtn`, on the reasoning that
   a bare select "is a word floating on the background". That reasoning is kept above rather than
   deleted because it is the argument somebody will make again — and what it missed is the law one
   level up: *a border earns its place around something you press, drag or type into, and around
   nothing else*, inside a toolbar that is ALREADY a bordered band. Two boxes, one control.
   THE PADDING GOES WITH THE BORDER. With no box to inset from, `padding: 0 10px` only pushes the
   word 10px right of the breadcrumb above it, which reads as a misaligned control rather than as
   a padded one. The HEIGHT stays: 30px is the row's rhythm and clears the 24px target floor. */
.pt-toolsel {
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0;
  color: var(--text); font-size: 12px;
}
/* `.pt-toolsel__cap` was the caption beside the select and is DELETED, not left unused
   (2026-09-21, user direction: "Just keep only drop down, nothing else required"). A rule with
   no emitter is what the next person styling this row finds and copies. */
/* IT LOOKS LIKE A DROPDOWN AGAIN, BECAUSE IT IS THE APP'S DROPDOWN (2026-09-24, user report with
   the control circled: *"the dropdown filter should look like exactly like dropdown item"*).

   THIS NARROWS THIS MORNING'S CHANGE RATHER THAN REVERSING IT, and the difference is the whole of
   why both notes stay. That one answered *"it is showing with extra borders"* — and the finding
   was real: `.wk-filters select` is (0,1,1) and beat the component's one-class `border: 0`, so the
   select kept the shared box INSIDE a wrapper that drew its own. Two boxes ~8px apart, reading as
   one thick edge. The fix took the wrapper's box away, which was right, and ALSO forced the
   select's off, which went one step further than the report asked: with neither, the control is a
   bold word with a chevron, indistinguishable from the tab strip beside it.

   SO THE WRAPPER STAYS BOXLESS AND THE SELECT STOPS FIGHTING THE SHARED RULE. What is left is
   exactly one border — the same 1px, 8px radius and surface fill `.wk-filters select` gives every
   other filter dropdown in row 2, which is what "exactly like dropdown item" means and is a
   convention this app already has rather than a better one invented here.

   WHAT SURVIVES OF THE OVERRIDE IS WHAT IS NOT ABOUT FRAMING: a width cap, so a long module name
   cannot push the row's right cluster off the end, and the focus ring below. Nothing here
   declares a border, a radius or a background any more — the shared rule is meant to win. */
.pt-toolsel .pt-toolsel__sel { max-width: 16rem; cursor: pointer; }
.pt-toolsel .pt-toolsel__sel:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.pt-toolsel__n { margin-left: 10px; font-size: 12px; color: var(--text-muted); }

/* ── STANDARD AND CUSTOMIZED LISTS ──────────────────────────────────────────────────────────
   2026-09-21, user direction: "there should be two types of lists, 1. Standard 2. Customized".
   Two groups on one page, so the heading is what separates them — no border, no card: a border
   earns its place around something you press, drag or type into, and the one line a design keeps
   is under a HEADING. The table under each already carries its own rules. */
/* ANOTHER ONE OF THESE (2026-09-24, user direction: "in the same tab, should be able to add any
   number of contacts or addresses"). A repeated block needs ONE thing the first does not: a line
   saying where the previous person ended and the next begins. A hairline above each copy, never a
   box around every block — a bordered box whose children are bordered boxes is the stack of cards
   this app removes on sight, and the FIRST block takes no rule at all because there is nothing
   above it to separate from. */
.pty-childset + .pty-childset,
.pty-childmore + .pty-childset { border-top: 1px solid var(--border); padding-top: 12px; margin-top: 4px; }
.pty-childmore { margin: 4px 0 14px; }

.pt-listgrp + .pt-listgrp { margin-top: 28px; }

/* ── THE TAB STRIP, IN A TOOLBAR ────────────────────────────────────────────────────────────
   2026-09-21, user direction: "Move Tabs Forms, and Lists into toolbar at stating, then +new
   related buttons and other filters and other controlls similar to org page or something like
   that to make standard at app level".

   `.smz-tabs` is sized for a PAGE — 44px tall with a bottom rule and 1.35rem under it, which is
   right above a record and half again the height of row 2. This is the same strip at toolbar
   scale: no rule of its own (the toolbar already ends in one), no margin, and the 30px the
   selects and `wk-iconbtn` in that row stand at, so the bar does not grow around it.

   A MODIFIER ON THE SHARED STRIP, not a rule scoped to one page: the next console to put tabs in
   its toolbar gets this rather than writing a second one. */
.smz-tabs.smz-tabs--bar { margin: 0; border-bottom: 0; gap: 0; }
.smz-tabs--bar > .smz-tab { min-height: 30px; padding: 0 12px; font-size: 12px; }
.pt-listgrp__h {
  display: flex; align-items: center; gap: 6px;
  margin: 0 0 8px; padding-bottom: 6px;
  border-bottom: 1px solid var(--border-muted);
  font-size: 13px; font-weight: 600; letter-spacing: .02em; color: var(--text);
}
/* A standard list this org has not installed: present, and visibly not yet real. Tone only —
   never `opacity`, which would take the Add button down with it. */
.pt-table tr.is-offer > td { color: var(--text-muted); }
/* THE SECOND ROW OF A NESTED LIST — the child level. It is a continuation of the row above, so it
   gives up that row's top border and indents its first cell; `is-sub` is never a row you act on,
   which is why it carries no controls. */
.pt-table tr.is-sub > td { border-top: 0; padding-top: 0; }
.pt-table tr.is-sub > td.pt-sub { padding-left: 22px; }

/* ── THE ELEMENT PROPERTIES PANEL ───────────────────────────────────────────────────────────
   2026-09-21, user direction: "when click on properties should Open rightside panel for that
   element", then "Properties should be like this image, very organized and all".

   A COLUMN OF `.fc-body`, beside the canvas rather than over it: you read the form and the
   panel together, and a modal would hide the thing being described. It scrolls on its own, so a
   long field's settings never push the canvas around.

   A FORM, NOT A MENU IN A PANEL — labelled controls in titled groups. The groups carry the only
   rules in it: a border earns its place around something you press or type into, and the one
   line a design keeps is under a heading. */
.fc-props {
  /* A POPUP ON THE RIGHT, NOT A COLUMN (2026-09-21, user report: "now i am seeing them inside page
     below the form. instead we need it as popup").
     THE FIRST CUT ASSUMED A FLEX ROW. `.fc-body` is a GRID — `17.5rem minmax(0,1fr)`, and
     `--solo` collapses it to one track now that the canvas draws no tray — so a third child
     became a second ROW and the panel rendered under the form. `flex: 0 0 300px` on a grid item
     does nothing at all, which is why it looked like the panel had no styling rather than the
     wrong kind.
     FIXED TO THE VIEWPORT, so it stays beside whatever part of a long form you have scrolled to.
     Checked before relying on it: nothing on `.canvas` or `.tab-content` sets `transform`,
     `filter` or `perspective`, any of which would make this resolve against an ancestor instead
     — the trap the rail already records.
     CLEAR OF THE RIGHT RAIL, which is itself fixed at `right: 0` and `--rail-right-w` wide.
     Landing on top of it would bury the one control that is on every screen — and that variable
     is the rail's LIVE width (`--rail-right-rest-w` at rest, wider with the chat dock open), so
     this follows it rather than restating 48px, which is the *geometry defined as "cancel what
     that one does" must READ the value* rule. Both are `:root`, so the fallbacks never fire in
     the app and only cover a fixture that loads this sheet alone. */
  position: fixed; z-index: 40;
  top: var(--topbar-h, 56px); bottom: 0;
  right: var(--rail-right-w, 0px);
  width: min(340px, 92vw);
  display: flex; flex-direction: column;
  border-left: 1px solid var(--border-muted); background: var(--bg-surface);
  box-shadow: -8px 0 24px rgb(0 0 0 / 12%);
}
.fc-props__head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border-bottom: 1px solid var(--border-muted);
}
.fc-props__head h4 { margin: 0; font-size: 13px; font-weight: 600; }
.fc-props__x {
  border: 0; background: transparent; color: var(--text-muted);
  font-size: 18px; line-height: 1; min-width: 24px; min-height: 24px; cursor: pointer;
}
.fc-props__x:hover { color: var(--text); }
.fc-props__x:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; border-radius: 4px; }
/* `overflow-x: hidden` IS PART OF THIS RULE, not a second one further down — how a box scrolls
   is one decision, and two rules for it is the split that drifts (the *cancel what that one does*
   law, applied to one element). It is a FLOOR: a child that somehow computes wider than the panel
   is clipped here rather than drawn over the form, which is what the rule editor did while it
   still carried `.fc-menu`. */
.fc-props__body {
  flex: 1; min-height: 0; padding: 4px 12px 12px;
  overflow-y: auto; overflow-x: hidden;
}
/* COLLAPSIBLE GROUPS (2026-09-21, user direction: "if required use, collapse, expand sections in
   popup"). A `<details>`, so the keyboard and the screen reader come free — the marker is the
   native one, restyled only enough to sit on the line rather than hang off it. */
.fc-props__grp { padding: 6px 0; border-bottom: 1px solid var(--border-muted); }
.fc-props__grp:last-child { border-bottom: 0; }
.fc-props__grp > summary {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 0; min-height: 28px; cursor: pointer; list-style: none;
  font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-muted);
}
.fc-props__grp > summary::-webkit-details-marker { display: none; }
/* The caret is drawn rather than inherited, so it turns with the group in both engines. */
.fc-props__grp > summary::before {
  content: '\25b8'; font-size: 10px; line-height: 1;
  transition: transform .12s ease;
}
.fc-props__grp[open] > summary::before { transform: rotate(90deg); }
.fc-props__grp > summary:hover { color: var(--text); }
.fc-props__grp > summary:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }
/* THE RULE EDITOR SITS IN THE PANEL NOW, not as a second floating surface off the tile — so it
   gives up the menu's card: no shadow, no border, no absolute placement. */
/* NOTHING ESCAPES THE PANEL SIDEWAYS. The override that used to stand here pinned `position`,
   `min-width`, the border and the shadow back one property at a time — a list whoever adds the
   next rule to `.fc-menu` would have had to extend. The editor no longer carries that class at
   all, so what is left is one floor, on the body's own rule above. */
.fc-props__body > * { max-width: 100%; }
.fc-props__f { display: block; margin-bottom: 8px; }
.fc-props__f > span { display: block; margin-bottom: 4px; font-size: 12px; color: var(--text-muted); }
.fc-props__f > input {
  width: 100%; box-sizing: border-box; min-height: 30px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-input, var(--bg-surface)); color: inherit; font: inherit;
}
.fc-props__f > input:disabled { color: var(--text-muted); background: transparent; }
.fc-props__f > input:focus-visible { outline: 2px solid var(--primary); outline-offset: 0; }
/* WHAT A FIELD ALREADY OFFERS, WHEN IT IS NOT THE ORG'S TO CHANGE (2026-09-24, user direction:
   "when element has its own choices, display them in properties, even if they are non editable,
   just show"). A LIST, not a disabled textarea: a box that cannot be typed into is read as broken
   or as "not yet", where words on a page are read as a fact. No bullets — the `border earns its
   place around something you press` rule applies to list markers too: these are a run of values,
   and the indent alone separates them from the label above. */
.fc-props__opts {
  list-style: none; margin: 0; padding: 4px 8px;
  border-left: 2px solid var(--border); border-radius: 0;
  font-size: 13px; color: var(--text);
  display: flex; flex-direction: column; gap: 2px;
  max-height: 180px; overflow: auto;
}
/* The share of records holding a value, beside the counts rather than instead of them: a reader
   scans the percentage and checks the numbers. Muted, because it is derived from the sentence it
   sits in and must not out-weigh it. */
.fc-props__pct {
  margin-left: 6px; padding: 1px 6px; border-radius: 10px;
  background: var(--bg-subtle, rgba(127, 127, 127, .12)); color: var(--text-muted);
  font-size: 12px; font-variant-numeric: tabular-nums;
}
/* THE FORMAT EDITOR, IN THE SECTION (2026-09-24, user direction: "instead of separate popup for
   format, can we add all that popup content into this format area only"). It is a form of ordinary
   panel controls — `.fc-props__f` does the work — so this only has to do the two things a column
   of boxes cannot: put the paired bounds on one line, and make a refusal visible.

   NO BOX AROUND IT. The section's own `<details>` already separates it, and a border earns its
   place around something you press, drag or type into rather than around a group of such things. */
.fc-shape__row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 10px; }
/* A REFUSAL HAS TO LOOK LIKE ONE. It is the only thing in this panel that says "what you typed was
   not stored", so it takes the app's danger ink rather than the muted note colour every other line
   here uses — a rule that silently vanishes is the defect this line exists to prevent. */
.fc-props__bad {
  /* THE TOKEN WITH NO FALLBACK. `--danger` is declared in BOTH themes (`:root` and the dark
     block), so a literal beside it would be a second answer that only ever wins when the first
     is missing — and if it ever were missing, a hardcoded red is exactly the light-only ink that
     reads as invisible in dark mode. */
  margin: 4px 0 8px; font-size: 12px; color: var(--danger);
}
.fc-props__tick { display: flex; align-items: center; gap: 8px; min-height: 28px; font-size: 13px; }
.fc-props__tick.is-off { color: var(--text-muted); }
/* Each control on its own line: they are settings, not a row of buttons. */
.fc-props__act {
  display: block; width: 100%; text-align: left; margin-bottom: 4px;
  min-height: 30px; padding: 4px 8px;
  border: 1px solid var(--border); border-radius: 6px;
  background: transparent; color: inherit; font: inherit; cursor: pointer;
}
.fc-props__act:hover { background: var(--bg-subtle, transparent); }
.fc-props__act:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.fc-props__danger { color: var(--danger, #b42318); border-color: currentColor; }
.fc-props__note, .fc-props__sub { margin: 4px 0 0; font-size: 12px; color: var(--text-muted); }
.fc-props__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 10px 12px; border-top: 1px solid var(--border-muted);
}
.fc-props__hint { font-size: 11px; color: var(--text-muted); }

/* ── THE ADD ROW ────────────────────────────────────────────────────────────────────────────
   2026-09-21, user direction: "adding list items, should be simple, instead of separate right
   popup and all. it should be a simple row at top or bottom of the table list".

   It reads as PART OF THE TABLE, not as a form stapled under it: the inputs give up their own
   box and sit on the cell, so the row's only frame is the table's own separator. A bordered
   control in every cell would be three boxes inside a box, which is the stack this design
   removes on sight. The focus ring is the one thing kept — nothing here is hover-only, and a
   borderless input with no visible focus is unusable from a keyboard. */
.pt-table tr.pt-addrow > td { background: var(--bg-subtle, transparent); }
/* The add row is ONE BOX PER COLUMN since 2026-09-22, so each cell holds a single input that
   fills it — the parent picker that used to sit beside the box is gone with the tree. */
.pt-addrow__cell { padding-left: 0; padding-right: 0; }

/* ── A LIST IS A TABLE OF ROWS AND NAMED COLUMNS ────────────────────────────────────────────
   2026-09-22, user direction: "i would like to add lists as tabular rows and columns ... each
   column will have column name". The indented tree and its disclosure (`.pt-tw`) went with the
   change — a grid has no branches to open, because a row is a whole path and every level is a
   column on it.

   A CELL IS A CONTROL, and it has to READ as the word it holds. An ancestor has no row of its
   own — it IS the repeated cell — so pressing it is the only way to reach one, and a cell that
   looked like a button would put 568 buttons on the Country/State table. So it is the text, with
   the affordance kept for hover and focus only. Nothing here is hover-only: `:focus-visible`
   carries the same underline, which is what makes the grid usable from a keyboard.

   A REPEATED ANCESTOR IS MUTED, NEVER BLANK. Blanking is how a grouped table usually reads and it
   is wrong here: these rows get filtered and read out of order, and a blank cell three rows down
   cannot say what it inherited. Muted keeps it scannable AND keeps it true. */
.pt-grid td { vertical-align: top; }
.pt-grid__cell {
  border: 0; background: transparent; color: inherit; font: inherit; text-align: left;
  padding: 0; min-height: 24px; cursor: pointer; border-radius: 4px;
}
.pt-grid__cell:hover { text-decoration: underline; text-underline-offset: 2px; }
.pt-grid__cell:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.pt-grid__rep .pt-grid__cell,
.pt-grid__rep { color: var(--text-muted); }
/* The name of a LEVEL is a heading that can be pressed, so it keeps the heading's weight and
   gains the same hover the cells have rather than a second button shape in the header row. */
.pt-grid__colname {
  border: 0; background: transparent; color: inherit; font: inherit; text-align: left;
  padding: 0; min-height: 24px; cursor: pointer; border-radius: 4px;
}
.pt-grid__colname:hover { text-decoration: underline; text-underline-offset: 2px; }
.pt-grid__colname:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* A column a row does not reach — a country with no state — is empty rather than dashed: the row
   is complete, it simply stops sooner, and a dash would read as a missing value. */
.pt-grid__pad { background: transparent; }
.pt-addrow__in {
  width: 100%; box-sizing: border-box;
  border: 0; background: transparent; color: inherit; font: inherit;
  padding: 4px 2px; min-height: 28px;
}
.pt-addrow__in::placeholder { color: var(--text-muted); }
.pt-addrow__in:focus-visible { outline: 2px solid var(--primary); outline-offset: 0; border-radius: 4px; }

/* ── THE RIGHT RAIL ─────────────────────────────────────────────────────────────────────────
   2026-09-19, user direction: "create thin rail at right side. move beta agent at the center of
   right side rail."

   It is the navigation column's mirror: fixed, full height under the 44px band, the same surface
   and the same hairline — turned around. Thin, because it holds ONE control and a column sized
   for a label it does not have would be a margin pretending to be chrome.

   A RAIL, NOT A FLOATING BUBBLE. A bubble hovers over whatever is beneath it — a table's last
   column, a form's Save — and this app's overlay rules say anything that sits over the page must
   be dismissible. A rail reserves its width (`.canvas-body` is inset below), so nothing is ever
   underneath it and nothing has to be dismissed.

   ITS CONTROL IS CENTRED VERTICALLY, which is the direction's own word and the right one for a
   single item: at the top it reads as the first of a list that never arrives; in the middle it
   reads as the rail's whole purpose, and it is the shortest travel from anywhere on screen. */
:root {
  /* THE PANEL SHAPE, shared by all three (2026-09-19, user direction). One radius and one gap,
     because three panels of the same app rounded by three different numbers is the drift this
     sheet already has 117 font sizes' worth of. The GAP is not decoration: a curve needs ground
     behind it, and flush against the viewport edge there is none to show. */
  --panel-radius: 12px;
  --panel-gap: 8px;

  /* TWO TOKENS, and the second READS the first: `--rail-right-rest-w` is how wide the rail is
     when it is only a rail, `--rail-right-w` is how wide it is RIGHT NOW. Everything that has to
     get out of its way reads the second one, so expanding the panel moves the page by itself. */
  --rail-right-rest-w: 48px;
  --rail-right-w: var(--rail-right-rest-w);
  --rail-right-head-h: 40px;
}
.smz-rail-right {
  position: fixed;
  right: 0;                     /* anchored to its own side — see the note on `.smz-nav` */
  top: var(--chrome-h, 56px);   /* flush under the band — same note */
  /* RESTATED rather than inherited, the same trap the left column records: 100vh from an offset
     overflows by exactly the offset. */
  height: calc(100vh - var(--chrome-h, 56px) - var(--panel-gap));
  box-sizing: border-box;
  width: var(--rail-right-w);
  z-index: 10;
  display: flex;
  flex-direction: column;
  justify-content: center;   /* the one control, in the middle */
  align-items: center;
  background: var(--rail2-bg);
  /* The navigation column's border and radius, mirrored — see the note there for why a rounded
     panel takes a full border rather than the one edge that faces the content. */
  border: 1px solid var(--rail2-border);
  border-right: 0;
  border-radius: var(--panel-radius) 0 0 var(--panel-radius);
}
.smz-rail-right__mid { display: flex; flex-direction: column; align-items: center; gap: 8px; }
/* THE BUTTON KEEPS ITS OWN SHAPE and takes the rail's hover. `.topbar-iconbtn`'s base already
   draws a 999px border circle around the mark — the header CANCELLED that with
   `.topbar .tb-smai { border: 0 !important }`, and outside the header it comes back, which is
   what the rule at `.topbar .topbar-iconbtn.tb-smai` says it wanted all along. A LITTLE LARGER
   than the 30px header copy: in the band it sat in a row of six and had to match them; here it is
   the only thing in a 48px column, and 30px inside 48 reads as a mark that drifted in. The hover
   is the rail's own wash — the header's `--bg-muted` is mixed against the BAND's surface. */
.smz-rail-right .topbar-iconbtn { width: 34px; height: 34px; }
.smz-rail-right .topbar-iconbtn:hover { background: var(--rail2-wash); border-color: var(--border-strong); }
.smz-rail-right .tb-smai .tb-smai__ico svg { width: 26px; height: 26px; }

/* ITS NAME, ON HOVER AND ON FOCUS. `data-rail-tip` is styled per HOST — `.topbar [data-rail-tip]`,
   `.wk-bar [data-rail-tip]`, and so on — so a button that moves out of its host keeps the
   attribute and silently loses the tooltip. This one flies LEFT, over the content: the rail is
   against the right edge of the screen and the header's downward-centred tip would hang off it.
   THE RAIL MUST NOT TAKE `overflow`, for the reason `.smz-rail1` must not — it would clip this. */
.smz-rail-right [data-rail-tip] { position: relative; }
.smz-rail-right [data-rail-tip]::after {
  content: attr(data-rail-tip);
  position: absolute; right: calc(100% + 8px); top: 50%; transform: translateY(-50%);
  white-space: nowrap; color: var(--text); background: var(--bg-surface);
  border: 1px solid var(--border); border-radius: 6px;
  font-size: 0.585rem; font-weight: 500; padding: 5px 9px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  opacity: 0; visibility: hidden; pointer-events: none; z-index: 1000;
}
.smz-rail-right [data-rail-tip]:hover::after,
.smz-rail-right [data-rail-tip]:focus-visible::after { opacity: 1; visibility: visible; }

/* ── EXPANDED: the rail IS the assistant panel ──────────────────────────────────────────────
   2026-09-19, user direction: "when user click on beta agent, it should expand right panel to
   open chat page, to chat and get answers and all."

   THE RAIL DOES NOT OPEN SOMETHING — IT GETS WIDER. `--rail-right-w` is what the content's inset
   reads, so moving that one token from 48px to the panel width shrinks the page beside the panel
   with no second rule to keep in step. That is the difference between this and the floating dock
   it replaces: you keep the record you were looking at while you ask about it, instead of the
   answer sitting on top of the question.

   `body.chat-dock-open` IS THE EXISTING STATE, not a new one. `chatDock.js` has driven a
   min/open/max body class since the dock shipped; the panel reuses it, which is why Escape,
   the focus-the-composer step and the consent-gated persistence all already work here. */
body.chat-dock-open { --rail-right-w: min(420px, 92vw); }
body.chat-dock-open .smz-rail-right { justify-content: flex-start; }
/* The button's job is done once the panel is open, and ✕ is what closes it. Two controls that
   both mean "the assistant" is how a reader ends up pressing the wrong one. */
body.chat-dock-open .smz-rail-right__mid { display: none; }

.smz-rail-right__head { display: none; }
body.chat-dock-open .smz-rail-right__head {
  display: flex;
  align-items: center;
  gap: 2px;
  width: 100%;
  box-sizing: border-box;
  height: var(--rail-right-head-h);
  padding: 0 4px 0 10px;
  border-bottom: 1px solid var(--rail2-border);
}
.smz-rail-right__mark { display: inline-flex; flex: none; }
.smz-rail-right__title {
  flex: 1; min-width: 0; margin-left: 6px;
  font-size: 13px; font-weight: 650; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.smz-rail-right__act {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px;            /* inside a 40px row; the target is the row's full height */
  border: 0; border-radius: 7px; background: none; cursor: pointer;
  color: var(--text-subtle); font: inherit; font-size: 13px; line-height: 1;
}
.smz-rail-right__act:hover { background: var(--rail2-wash); color: var(--text); }
.smz-rail-right__act:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

/* THE CONVERSATION FILLS THE PANEL, and `#chat-tab` is NOT moved to get there. It is a
   `.tab-content` the router and `switchTab` own — relocating it out of `.canvas-body` is the
   "moving a nav button out of its tab" defect wearing different clothes — so it is POSITIONED
   into the panel instead, fixed over the aside below the header.

   Specificity, because this has to beat `.tab-content { display: none }` without beating
   `.canvas #chat-tab.chat-page.tab-content.active`, which is the FULL PAGE: (1,2,1) against
   (0,1,0) and (1,3,1). The panel and the page are one element and can never both be on, which
   is why `tab-shown` on the chat tab closes the panel. */
body.chat-dock-open #chat-tab.chat-page {
  display: flex;
  position: fixed;
  right: 0;
  top: calc(var(--chrome-h, 56px) + var(--rail-right-head-h));
  bottom: var(--panel-gap);
  width: var(--rail-right-w);
  box-sizing: border-box;
  z-index: 11;                 /* over the rail's own surface, under every menu and the drawer */
  padding: 0;
  overflow: hidden;
  background: var(--bg-surface);
  /* It fills the rail's box below the header, so it continues the rail's border and finishes the
     rail's own bottom corners — NO top border and NO top radius, because the header above it is
     still the rail's and already carries both. */
  border: 1px solid var(--rail2-border);
  border-top: 0;
  border-right: 0;                                   /* it is the rail's own edge, and the rail
                                                        does not draw one on the screen's side */
  border-bottom-left-radius: var(--panel-radius);
}
body.chat-dock-open #chat-tab .chat-page__body { flex: 1; min-width: 0; min-height: 0; }
/* The chat's own history rail is ~200px of a 420px panel, which leaves the conversation 220px.
   It stays on the FULL PAGE — ⤢ in the header is the door to it. */
body.chat-dock-open #chat-tab #chat-dock-rail { display: none; }

/* THE CONTENT IS INSET BY IT, always — the rail is not conditional the way the navigation column
   is (`body.smz-nav-on`), because nothing switches it off. TWICE THE GAP: one to clear the rail's
   own inset from the viewport edge, one to leave the seam between the two panels. The left inset
   is the same arithmetic over `--nav-w`, under `body.smz-nav-on` below. */
.canvas-body {
  /* ONE gap per side, not two: the rails are anchored to the screen's edges now, so the only
     space to leave is the seam BETWEEN each rail and this panel. */
  margin-right: calc(var(--rail-right-w) + var(--panel-gap));
  margin-left: var(--panel-gap);            /* with no navigation column, just the seam */
  /* TOP: A HAIRLINE SEAM, 2px (2026-09-19, user direction: "add 2px margin at page body's
     upside"). DELIBERATELY NOT `--panel-gap` and deliberately not shared with the rails: the
     full 8px above this panel was dead space and was removed the same day, but hard against the
     band the white surface and the band's own edge read as one block. 2px is enough to separate
     them and too little to read as a gap. The rails keep 0 — they are the band's own tone, so
     they have nothing to separate from.
     BOTTOM: the full seam, the same one both rails end on. */
  margin-block: 2px var(--panel-gap);
}
/* THE CENTRE PANEL'S OWN CORNERS. `.canvas-body` is not the surface a reader sees — the ACTIVE
   TAB is, and it paints `--bg-surface` over its parent with `!important`, so a radius on the
   parent would be squared off by the child. No `overflow` is added to clip to the curve: this
   element is ALREADY a scroll container (`.canvas .tab-content.active { overflow-y: auto }`), so
   it already clips, and adding `hidden` where it is overridden away would un-stick every sticky
   header inside the page.

   SCOPED TO `.canvas-body >`, not every `.tab-content` in `.canvas`: the signed-out marketing
   tabs are siblings of this box, full-bleed by design, and a rounded landing page is not what
   was asked for. */
.canvas-body > .tab-content.active {
  border-radius: var(--panel-radius);
  /* AND THE SAME HAIRLINE THE TWO RAILS CARRY. Both of them state an edge in `--rail2-border`;
     a centre panel with none reads as a hole between them rather than the third panel — and in
     DARK MODE it is not visible at all, because `html[data-theme='dark'] .canvas .tab-content
     .active` deliberately paints it `--zone-content`, the ground's own colour. Tone alone then
     says nothing about where the panel starts, and the curve it was given is invisible.
     AN OUTLINE, NOT A BORDER: there is no global `box-sizing` reset in this sheet, so a border
     here would add 2px to a box whose width is already spoken for. `outline` follows
     `border-radius`, costs no layout, and at `-1px` keeps the panel's footprint exact. It
     cannot collide with a focus ring — `.tab-content` is not focusable. */
  outline: 1px solid var(--rail2-border);
  outline-offset: -1px;
}

/* NARROW: THINNER, NEVER HIDDEN. The navigation column floats over the page at this width
   because there is a button in the band that brings it back. This rail has no such partner —
   `#topbar-chat` is the ONLY thing anywhere that calls `openChatDock()` (chatDock.js), and
   `#chat-dock-launcher` in the footer is `display:none !important` — so hiding the rail does not
   move the agent somewhere else, it takes the agent away. It gives back the 12px it can. */
@media (max-width: 720px) {
  :root { --rail-right-rest-w: 36px; }
  .smz-rail-right .topbar-iconbtn { width: 28px; height: 28px; }
  .smz-rail-right .tb-smai .tb-smai__ico svg { width: 22px; height: 22px; }
  /* EXPANDED here, the panel FLOATS over the page — the same call the navigation column makes at
     this width. Insetting the content by a 420px panel on a 400px screen leaves the page a
     column it cannot use. The inset falls back to the rail's resting width, READ rather than
     restated, so the ✕ is never underneath the panel it closes. */
  body.chat-dock-open { --rail-right-w: min(420px, 100vw); }
  body.chat-dock-open .canvas-body { margin-right: calc(var(--rail-right-rest-w) + var(--panel-gap)); }
}

/* EMBED MODE shows a module's BODY alone — no band, no navigation column, no chat launcher. The
   rail is chrome by the same definition, so it goes with them and gives its width back. */
html.embed-mode .smz-rail-right { display: none !important; }
/* Embed shows a module's body alone — no rails, so no seams to leave for them, and no rounded
   frame: the host page decides what shape the iframe is. */
html.embed-mode .canvas-body { margin: 0 !important; }
html.embed-mode .canvas-body > .tab-content.active { border-radius: 0; }

/* ── The content beside it ──────────────────────────────────────────────────────────────── */
/* Open: the page starts at the rail's right edge. Collapsed: it takes the full width. The band
   spans the top in both states — with one rail there is no corner box to reserve. */
body.smz-nav-on .canvas-body { margin-left: calc(var(--nav-w) + var(--panel-gap)); }
body.smz-nav-on .topbar { margin-left: 0; }

/* NARROW: the rail floats OVER the page and dismisses on pick, rather than squeezing a column
   that has nothing left to give. A shadow rather than a border, because an overlay has to read
   as lifted off the thing behind it. */
@media (max-width: 1100px) {
  body.smz-nav-on .canvas-body { margin-left: var(--panel-gap); }
  /* The one place the column is allowed over the page — and it must clear the scrim under it. */
  body.smz-nav-on .smz-nav { z-index: 70; box-shadow: 0 8px 28px rgba(15, 23, 42, .22); }
  body.smz-nav-on .smz-nav-scrim {
    position: fixed;
    inset: var(--chrome-h, 56px) 0 0 0;
    z-index: 69;
    background: rgba(15, 23, 42, .28);
  }
}
@media (min-width: 1101px) {
  .smz-nav-scrim { display: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE HEADER SEARCH IS AN OPEN BAR, GMAIL-SHAPED (2026-09-14, user direction)
   ═══════════════════════════════════════════════════════════════════════════════════════════
   One wide filled pill centred in the band: magnifier on the left, no border at rest, lifting to
   a white card with a shadow on focus. It was an ICON with the bar as a popup behind it; the
   trade is recorded beside the markup in index.html.

   Appended at the END of the sheet deliberately — the `.tb-searchbtn`-scoped rules further up were
   written for a POPUP hanging off an icon (absolute, shadowed, pinned under the button), and these
   have to win on order rather than by out-specifying each of them one at a time. No `:is()`:
   WebKit prices it at specificity zero.
   ═══════════════════════════════════════════════════════════════════════════════════════════ */

:root { --tb-search-h: 34px; --tb-search-max: 440px; }

/* THE TOGGLE IS GONE, AND `hidden` ALONE DOES NOT DO IT. An author `display` un-hides `[hidden]`
   at ANY specificity — origin settles before specificity — and `.topbar > button` sets
   `display: inline-flex` on every control in the band. So the magnifier BUTTON went on being
   drawn above the open bar, overflowing the 44px band, while its attribute said hidden. Ask "does
   this rule set `display`", never "is mine more specific". */
.canvas:has(.topbar) .topbar .tb-center #header-search-toggle[hidden] { display: none !important; }

/* The centre cluster is the bar's container now, so it has to be wide rather than hug an icon. */
.canvas:has(.topbar) .topbar .tb-center {
  width: min(var(--tb-search-max), 46vw);
  max-width: none;
}
.canvas:has(.topbar) .topbar .tb-center .tb-searchbtn {
  display: flex;
  align-items: center;
  width: 100%;
  height: 100%;
  position: static;
}

/* IN FLOW, not a popup. The wrap was `position: absolute` under the icon with its own shadow and
   radius — every one of those is wrong for a bar that is simply there. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search-wrap.topbar-search-wrap {
  position: static;
  display: block;
  width: 100%;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transform: none;
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  /* AND IT MUST NOT CLIP. The wrap carries `overflow: hidden` from its popup life, where it was
     what made the panel collapse to nothing; in flow it is 34px tall — the height of the bar — so
     it cut the suggestion list off after two rows. Measured: a 287px card inside a 34px clipper.
     A container that clips its own dropdown is the same shape of bug as a rail that clips its own
     fly-outs, and it fails the same way: the list is there, correct, and mostly invisible. */
  overflow: visible;
}

.canvas:has(.topbar) .topbar .tb-center .topbar-search {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  box-sizing: border-box;
  height: var(--tb-search-h);
  padding: 0 8px 0 6px;
  /* Gmail's resting shape: a filled pill with no border at all. A border plus a fill is two
     boundaries for one control, and it is what makes a search bar read as a form field rather
     than as a place to type. */
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--chrome-bg-muted);
  box-shadow: none;
  transition: background-color 120ms ease, box-shadow 120ms ease, border-color 120ms ease;
}
/* FOCUS LIFTS IT OFF THE CHROME — the one state change Gmail makes, and the thing that says the
   keystrokes are going here. Focus-within, so it holds while the caret is in the input. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search:focus-within {
  background: var(--chrome-open-page);
  border-color: var(--chrome-border);
  box-shadow: 0 1px 6px rgba(15, 23, 42, .18);
}

.canvas:has(.topbar) .topbar .tb-center .topbar-search__go {
  flex: 0 0 26px;
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--chrome-text-muted);
  cursor: pointer;
}
.canvas:has(.topbar) .topbar .tb-center .topbar-search__go:hover { color: var(--chrome-text); }
.canvas:has(.topbar) .topbar .tb-center .topbar-search__go svg { width: 17px; height: 17px; }

.canvas:has(.topbar) .topbar .tb-center .topbar-search__input {
  flex: 1 1 auto;
  min-width: 0;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: transparent;
  box-shadow: none;
  outline: none;
  color: var(--chrome-text);
  font: inherit;
  font-size: .85rem;
}
.canvas:has(.topbar) .topbar .tb-center .topbar-search__input::placeholder { color: var(--chrome-text-subtle); }
/* The browser's own clear affordance is a second, differently-styled control inside ours. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search__input::-webkit-search-decoration,
.canvas:has(.topbar) .topbar .tb-center .topbar-search__input::-webkit-search-cancel-button { display: none; }

/* THE SUGGESTION LIST IS A CARD AGAIN, hanging off the bar at its full width.
 *
 * It was transparent, borderless and shadowless — words floating on the page with nothing behind
 * them. `.topbar .tb-searchbtn .topbar-search__suggest` strips exactly those four properties, and
 * correctly so for the shape it was written for: back then the list lived INSIDE the search POPUP,
 * which was itself the card, and a card inside a card is two borders around one thing. With the
 * bar open in the band there is no popup any more, so the list has to carry the surface itself.
 *
 * Restating every property that rule sets, not just the ones that looked wrong: it wins on
 * specificity for what it declares and stays in force for everything else, so a partial override
 * leaves a panel that is half one shape and half the other.
 *
 * The standard names resolve to PAGE values in here — `.topbar .topbar-search-wrap` is in the
 * chrome block's popover restore list — so this reads as the page it opens over rather than as
 * more chrome, with nothing to keep in step across themes. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search__suggest {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(100% + 6px);
  width: auto;
  transform: none;
  margin: 0;
  padding: 6px;
  z-index: 60;
  max-height: min(60vh, 460px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
}
/* `hidden` is not enough on its own here: the rules above set `display` on this element, and an
   author `display` un-hides `[hidden]` at ANY specificity — origin settles before specificity. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search__suggest[hidden] { display: none; }
/* The bar keeps its own shape while the list is open — they are two surfaces, one under the
   other, not one panel that grows. The popup-era rule squared the field's bottom corners to weld
   it to the list below; there is nothing to weld to now. */
.canvas:has(.topbar) .topbar .tb-center .topbar-search-wrap:has(.topbar-search__suggest:not([hidden])) .topbar-search {
  border-radius: 999px;
}

/* NARROW: the bar gives way before the controls do. Below 900px `.tb-center` returns to the flow
   (a rule further up), and a 640px pill in a flowed band would push the right cluster off. */
@media (max-width: 900px) {
  .canvas:has(.topbar) .topbar .tb-center { width: auto; }
  .canvas:has(.topbar) .topbar .tb-center .topbar-search { max-width: 240px; }
}
@media (max-width: 700px) {
  .canvas:has(.topbar) .topbar .tb-center { display: none; }
}

/* ── Settings ▸ People ▸ Agents: the agent that cannot act ─────────────────────
   The row reports an agent with no service account. Until 2026-09-17 that state was
   merely informational ("ungoverned" — it ran with its owner's access), so the pill
   beside it needed no treatment and got none: `.oa-pill` has never carried a rule.
   The IAM gate now fails CLOSED, so the same cell reports a BROKEN agent, and this is
   the one pill in that table that must not read as a neutral note. Only the --warn
   variant is styled — giving `.oa-pill` a base here would restyle every pill in the
   directory (Org default, disabled, External) as a side effect of this fix. */
.oa-pill--warn {
  display: inline-block;
  padding: 1px 7px;
  border: 1px solid var(--warn, #b06000);
  border-radius: 999px;
  background: var(--warn-bg, #fef3c7);
  color: var(--warn-fg, #b06000);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}

/* ── Access ▸ How far it reaches — the reference as a PAGE ─────────────────────
   It was a drawer until 2026-09-17 and inherited the drawer's width. On a page the
   sections must not run the full width of a wide monitor: a reference is read, and a
   90-character measure is the difference between reading it and scanning past it.
   The permission matrix is the exception — it is a table and gets the whole width. */
.iam-howto--page { max-width: 68rem; }
.iam-howto--page .iam-howto__sec > summary { font-size: .95rem; padding: 10px 0; }
.iam-howto--page .iam-howto__body > p { max-width: 62rem; }

/* The ladder. Numbered, because "each step includes everything below it" is the one thing
   readers get wrong about it, and a plain list of four phrases does not say there is an
   order. No border per step: whitespace separates, and a bordered box whose children are
   bordered boxes is the stack of cards this app removes on sight. */
.iam-ladder { list-style: none; margin: 0 0 12px; padding: 0; counter-reset: iam-rung; }
.iam-ladder__step {
  counter-increment: iam-rung;
  display: grid;
  grid-template-columns: 1.6rem minmax(7rem, max-content) 1fr;
  gap: 4px 10px;
  align-items: baseline;
  padding: 7px 0;
}
.iam-ladder__step::before {
  content: counter(iam-rung);
  color: var(--text-muted);
  font-size: .75rem;
  font-variant-numeric: tabular-nums;
}
.iam-ladder__step + .iam-ladder__step { border-top: 1px solid var(--border-muted); }
@media (max-width: 640px) {
  .iam-ladder__step { grid-template-columns: 1.6rem 1fr; }
  .iam-ladder__step > .small { grid-column: 2; }
}

/* The matrix section's own search + module filter. It has never carried a rule — as a drawer
   section the two controls stacked and it read as a form; on a page they are a control row and
   must sit on one line, wrapping on a narrow window rather than overflowing it. */
.iam-howto__filters { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.iam-howto__filters > [data-iam-howto-q] { flex: 1 1 18rem; min-width: 0; }
.iam-howto__filters > [data-iam-howto-mod] { flex: 0 1 14rem; }

/* A wide table inside a section scrolls ITSELF; the page body must never scroll sideways. */
.iam-howto__body { overflow-x: auto; }

/* ── Access ▸ Hidden fields — the field picker on the create PAGE ──────────────
   Was a `<select multiple>` in a 380px drawer: the control people get wrong, because
   clicking a second option silently drops the first unless you hold a modifier nobody
   advertises. A page has room for the whole list with its labels showing.

   No border on a box — whitespace separates. The one line here is under the quick-pick
   row, which is a different KIND of control (it acts now) and earns the separation. */
.iam-fpick__quick {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding: 0 0 8px;
  margin: 0 0 10px;
  border-bottom: 1px solid var(--border-muted);
}
.iam-fpick__grid {
  display: grid;
  /* `min(13rem, 100%)` RATHER THAN `13rem`: a track whose MINIMUM is wider than the container
     cannot shrink, so the grid overflows and every row inside it is squeezed — which is what put
     a checkbox and a column of single letters on the Hidden-fields page. The field spans its
     group again (above), so this should never be reached; it is here because a grid that can
     demand more width than it has is one bad ancestor away from doing it again, and the failure
     is silent and looks like a broken control rather than a narrow one. */
  grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr));
  gap: 2px 16px;
}
.iam-fpick__box {
  display: flex;
  align-items: baseline;
  gap: 7px;
  padding: 5px 0;
  cursor: pointer;
  font-size: .85rem;
  line-height: 1.35;
}
.iam-fpick__box input { margin: 0; flex: none; }
/* The label wraps at WORDS; it never becomes a column of letters. Stated rather than left to the
   default because the default is only safe while the box is wide enough, and this is the line a
   reader of the bug report will look for. */
.iam-fpick__box > span { min-width: 0; overflow-wrap: break-word; }
.iam-fpick__box input:disabled + span { opacity: .55; }

/* ── Directory ▸ People — the provenance trace, as the row's own panel ─────────
   It was a drawer until 2026-09-17. On a PAGE the panel is a table row spanning every
   column, so it reads as belonging to the row above it rather than as a second table.
   No border around it — whitespace and the tint separate; the one line it keeps is the
   hairline under the row it explains, which is the row's own. */
.oa-trace-row > td {
  padding: 10px 14px 14px;
  background: var(--bg-muted, rgba(127, 127, 127, .06));
}
.oa-caret {
  display: inline-block;
  width: .8em;
  color: var(--text-muted);
  font-size: .8em;
}
/* The trace itself is shared with the IAM console; on a row panel it must not run the full
   width of a wide monitor, for the same reason the reference page caps its measure. */
.oa-trace-row .iam-trace { max-width: 62rem; }

/* ── Access ▸ Give access — the module rows (plan step 13) ─────────────────────
   One row per module in the grant: the module, how much, and its parts beneath. A
   border here earns its place — these are repeatable blocks and the reader has to see
   where one module's parts end and the next begins, which whitespace alone stops doing
   at three rows. Nothing INSIDE a row is bordered. */
.iam-gmrow + .iam-gmrow { border-top: 1px solid var(--border-muted); margin-top: 14px; padding-top: 14px; }
.iam-gmrow__head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 10px; }
.iam-gmrow__head > select { flex: 0 1 15rem; min-width: 0; }
.iam-gmrow__head > [data-iam-gm-del] { margin-left: auto; }

/* ── Access ▸ Check access (plan step 18) ──────────────────────────────────────
   One question on one line: "Can [somebody] use [module] ?". A form layout would make
   it read as something to fill in and submit; it is a sentence with two blanks. */
.iam-check__ask {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 16px;
  font-size: .95rem;
}
.iam-check__ask > select { flex: 0 1 18rem; min-width: 0; }

/* ── Access ▸ Give access — "What this will change" (plan step 19) ─────────────
   A tinted band at the foot, not a bordered card: it is a SUMMARY of the form above it,
   and a box around it would read as a sixth question. The one line it keeps is the rule
   under its heading, which is what this app allows a border for. */
.iam-preview {
  margin: 18px 0 0;
  padding: 14px 0 0;
  border-top: 2px solid var(--border-muted);
}
.iam-preview__h { font-weight: 600; font-size: .82rem; letter-spacing: .04em; text-transform: uppercase; color: var(--text-muted); }
.iam-preview__l { margin: 8px 0 8px; padding-left: 18px; }
.iam-preview__l li { margin: 0 0 4px; line-height: 1.5; font-size: .88rem; }

/* ── Access ▸ Access reviews — what is actually being certified (plan step 20) ──
   The row was a tier pill and nothing else. The entitlements sit UNDER their row, indented
   to the person's name, so a reviewer scanning twenty rows reads names first and detail
   only where they stop. No border: the rows are already separated. */
.iam-recert-row + .iam-recert-row { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border-muted); }
.iam-recert-ent { margin: 4px 0 0 1.6rem; padding-left: 14px; font-size: .84rem; line-height: 1.5; }
.iam-recert-ent li { margin: 0 0 2px; }

/* ── Access ▸ By module — "Needs attention" (plan step 22) ─────────────────────
   A strip, not a stack of cards: every line is one number and one phrase, and the whole
   thing is gone when there is nothing outstanding. Tinted rather than bordered — it is a
   band across the top of the page, and a box would make it a permanent fixture of the
   layout, which is the opposite of a strip that disappears. */
.iam-posture {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 18px;
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: 6px;
  background: var(--warn-bg, #fef3c7);
  color: var(--warn-fg, #b06000);
}
.iam-posture__h {
  font-weight: 600;
  font-size: .72rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.iam-posture__i { font-size: .85rem; color: inherit; }
.iam-posture__i strong { font-variant-numeric: tabular-nums; }

/* ─── A CHOICE DRAWN AS A GROUP — radio buttons or tick boxes ─────────────────────────────────
   2026-09-23, user direction: a fixed-choice element, and a display picker on it and on the
   two-choice element. Rendered by 'ui/fieldControl.js::choiceGroupHtml' on ALL FIVE form
   surfaces — the drawer 39 modules open, the three module page hosts, and the record page's
   in-place editor — so it is styled HERE, in the sheet every one of them loads, rather than in
   any one surface's own block.

   THE BOX AND ITS TEXT ARE ONE LINE, which is the whole of the report that produced this
   ("check boxes or radio buttons are being at one place and the text related to that being in
   another place"). Measured in real Chrome before and after, the 'jsdom's cascade is not the
   browser's' rule: the radio was 727px wide and its label sat 15px lower, because
   'ui/scaffold.js' sets 'display: block; width: 100%' on every input in a drawer form.

   SPECIFICITY IS (0,2,1) AND THAT IS DELIBERATE. The rule it has to beat is (0,1,1) and lives in
   an INJECTED sheet, so it is appended to the head after this file and would win any tie on
   order. Weight rather than order is what makes this correct wherever the two end up.

   NO ':is()' — WebKit prices it at specificity ZERO, so a selector list that must win is written
   out. The selectors below are longhand for exactly that reason.

   A FIELDSET IS THE SEMANTIC GROUPING and carries a browser border, padding, margin and a
   'min-width: min-content' quirk that stops it shrinking inside a grid; all four are reset. It is
   a fieldset rather than a div because it is what associates the field's LABEL with the whole set
   for a screen reader, where an 'id' can only ever point at one box. */
.smz-choice {
  border: 0; margin: 0; padding: 0; min-width: 0;
  display: flex; flex-wrap: wrap; gap: .3rem 1.1rem;
}
/* WRAPS RATHER THAN STACKING. Two or three short options read as one row; a long label or a
   narrow column pushes the next option onto its own line on its own, which is the behaviour a
   fixed column count cannot have. */
.smz-choice .smz-choice__opt {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: .88rem; font-weight: 400; margin: 0; cursor: pointer;
}
/* 'width: auto' IS THE FIX; 'flex: none' is what stops a flex parent stretching the box back.
   'margin: 0' clears the '.25rem' top margin the same injected rule adds for text boxes, which
   on a box beside text reads as the pair being out of line by a few pixels. */
.smz-choice .smz-choice__opt input[type="checkbox"],
.smz-choice .smz-choice__opt input[type="radio"] {
  width: auto; flex: none; margin: 0; accent-color: var(--primary, #2563eb); cursor: pointer;
}
/* The words stay with their box when the row wraps mid-option. */
.smz-choice .smz-choice__opt > span { line-height: 1.3; }
/* A GROUP THE READER MAY NOT EDIT still has to read as text rather than as a broken control —
   'setDetailEditing' paints the whole fieldset disabled on every record page. */
.smz-choice[disabled] .smz-choice__opt { cursor: default; opacity: .75; }

/* ─── A FIELD WHOSE CONTROL IS A TICK BOX ─────────────────────────────────────────────────────
   2026-09-23, user report with a screenshot: *"the check boxes in all the sections of all forms
   are still mis-aligned"* — the box sat under a wide uppercase heading with nothing beside it.

   THE CAUSE IS THE CELL, NOT THE BOX. '.orbit-detail-grid .orbit-field' is a COLUMN, so every
   field puts its label above its control; a tick box is the one control smaller than its own
   label, so it reads as stranded. The box itself was already fine — '.orbit-ef-check' has been an
   inline-flex row all along, and it had nothing to sit beside because a boolean field carries no
   'checkLabel'.

   ONLY WHILE EDITING, and that is the whole reason this is two rules rather than one. In READ mode
   the value is the word "Yes" or "No", and a row would render "No  ENFORCE THE CREDIT LIMIT" —
   the label reading as though it were the value's unit. In EDIT mode the value IS the box, and
   'box then label' is the only arrangement a tick box has ever had. A create form is permanently
   in edit mode ('setDetailEditing(root, true)'), so it gets the row without asking.

   ORDER RATHER THAN MARKUP. The label is emitted first because it is first for every other field
   and for a screen reader; 'order' moves the BOX in front of it visually without changing the
   document, so what is read aloud is unchanged.

   EVERY RULE BELOW CARRIES A SECOND PREFIX, '.fc-canvas' — the form-layout canvas under Settings,
   which is this same renderer with neither of the hooks these rules hang on. The measurements and
   the reason are at the FOOT of this block, where they can be read after the rules they explain. */
.is-editing .orbit-detail-grid .orbit-field--check,
.fc-canvas .orbit-detail-grid .orbit-field--check {
  /* `justify-content: flex-start` IS THE ONE THAT MATTERS, and leaving it out is what produced
     the second screenshot: the box at the far left of the cell and its words at the far right.
     `.orbit-field` sets `justify-content: space-between` for the READ layout — a label on the
     left and its value right-aligned across the row — and the grid variant resets
     `flex-direction`, `align-items`, `gap`, `padding` and `border` but NOT that. In a COLUMN it
     is inert, which is why it survived unnoticed; the moment this cell became a ROW it did
     exactly what it says and pushed the pair to opposite ends.

     THE LESSON, because it will happen again to whoever flips another cell to a row: changing
     `flex-direction` inherits every other flex property the base rule set for the old direction.
     List them, do not assume the variant above reset them. */
  flex-direction: row; justify-content: flex-start; align-items: center;
  gap: .5rem; flex-wrap: nowrap;
}
.is-editing .orbit-detail-grid .orbit-field--check .orbit-field-value,
.fc-canvas .orbit-detail-grid .orbit-field--check .orbit-field-value {
  order: -1; flex: none; text-align: left;
}
/* The heading stops shouting once it is a label beside its box: sentence height, normal weight,
   and the reader's own ink rather than the faint uppercase a column header needs. */
.is-editing .orbit-detail-grid .orbit-field--check .orbit-field-label,
.fc-canvas .orbit-detail-grid .orbit-field--check .orbit-field-label {
  font-size: .8rem; font-weight: 400; text-transform: none; letter-spacing: 0;
  /* `flex: 0 1 auto` keeps the words against the box. A label allowed to GROW takes the rest of
     the cell and the text drifts right again — the same symptom by a different mechanism, which
     is worth closing here rather than discovering twice. */
  color: var(--text); min-width: 0; flex: 0 1 auto; text-align: left;
}

/* THE BOX ON THE TEXT'S CENTRE LINE, not on its baseline (2026-09-23, third screenshot in the
   same report: *"these check boxes are looking mis-aligned like up and down with the text"*).
   Measured before this: the box's centre sat exactly 3.0px BELOW the label's, in every cell.

   TWO CAUSES, AND 'align-items: center' ON THE CELL CANNOT REACH EITHER. That centres the cell's
   own flex ITEMS — the label span and the value span — while the box is three levels further in:
   value > '.orbit-fv-edit' (a BLOCK while editing) > '.orbit-ef-check' (INLINE-flex) > input. An
   inline-flex box sits on the baseline of the text around it, so it hangs below a centred line by
   the font's descender; and the input still carried the browser's own 'margin: 3px 3px 3px 4px'.

   SO THE CHAIN IS MADE FLEX THE WHOLE WAY DOWN and the default margin is cleared. Specificity is
   (0,4,0) against the (0,3,0) of '.smz-detail-page.is-editing .orbit-fv-edit { display: block }'
   it has to beat — weight, not order, because that rule lives in a partial whose <style> is
   appended after this file.

   CLEARING THE MARGIN ALSO TIGHTENS THE GAP by the 4px left margin the box was carrying, which is
   why the spacing is the cell's own 'gap' and nothing else. */
.is-editing .orbit-detail-grid .orbit-field--check .orbit-field-value,
.is-editing .orbit-detail-grid .orbit-field--check .orbit-fv-edit,
.is-editing .orbit-detail-grid .orbit-field--check .orbit-ef-check,
/* NO '.orbit-fv-edit' ON THIS SIDE: the canvas has no view/edit pair to switch between — its
   control IS the value — so the chain is one level shorter and a selector for it could never
   match. Listed as what exists rather than mirrored for symmetry. */
.fc-canvas .orbit-detail-grid .orbit-field--check .orbit-field-value,
.fc-canvas .orbit-detail-grid .orbit-field--check .orbit-ef-check {
  display: flex; align-items: center; min-width: 0;
  /* AND THE GLOBAL `label { margin: 12px 0 6px }` IS NEUTRALISED, which is what the 3.0px
     actually was. `.orbit-ef-check` is a LABEL, so it inherits that rule — written for stacked
     forms, where air above a caption and less below it is right — and the margin is ASYMMETRIC:
     centring a box whose margin is 12 above and 6 below puts its border box (12-6)/2 = 3px low.
     Three other places in this sheet already neutralise the same rule by name, and
     `.smz-choice__opt` sets `margin: 0` for exactly this reason, which is why the radio buttons
     this report started with were already right.

     MEASURED, NOT REASONED: the box's centre sat 3.0px below the label's in every cell, and the
     chain read `val cy=20.3, fve cy=20.3, chk cy=23.3` — every ancestor centred and the label
     itself three pixels low, which only an asymmetric margin can do. */
  margin: 0;
}
.is-editing .orbit-detail-grid .orbit-field--check .orbit-ef-check .orbit-ef,
.fc-canvas .orbit-detail-grid .orbit-field--check .orbit-ef-check .orbit-ef {
  margin: 0; flex: none;
}
/* ─── AND THE SAME CELL ON THE FORM-LAYOUT CANVAS ─────────────────────────────────────────────
   2026-09-23, user direction: *"same alignment should apply to forms in layouts of settings"*.

   THE CANVAS IS THE SAME RENDERER WITH NEITHER OF THE TWO HOOKS THE RULES ABOVE HANG ON.
   Settings > Layouts draws its fields with the record page's own 'dGroup'/'dField' — deliberately,
   so the canvas cannot drift from the form it is a canvas for — but it passes each control in as
   the field's VALUE rather than as an 'edit' spec (see 'ui/layoutBuilder.js', which records why:
   an editor there would be painted 'disabled' and revealed by an edit mode this page never
   enters). So the cell had no 'edit' to read its type from, and there is no '.is-editing'
   ancestor for these rules to match under.

   MEASURED IN REAL CHROME, both surfaces side by side, before this existed:

     CANVAS  SINGLE Enforce the credit l  dir=column  V-OFFSET=32.6px  sameLine=no
     RECORD  SINGLE Enforce the credit l  dir=row     V-OFFSET= 0.0px  sameLine=YES

   A RADIO OR TICK-BOX **GROUP** NEEDED NOTHING, and that is worth recording so nobody adds a rule
   for it: '.smz-choice' above is unscoped, so a group measured identically on both surfaces
   (V-OFFSET 0.0px, gap 6.4px) all along. The lone tick box was the whole defect — the one control
   with no text of its own, whose text IS the cell's label.

   '.fc-canvas' RATHER THAN A SECOND CLASS ON THE CELL: the canvas is permanently showing its
   controls, which is what '.is-editing' means on a record page, so the container is the honest
   place to say it. Weight matches the rules above at (0,4,0) for the same reason they carry it. */

/* ── A RECORD'S PICTURES, BESIDE ITS FIELDS ───────────────────────────────────────────────────
   2026-09-25, user direction: *"in each tab, next to elements left side, need to display images and
   where user can add, something like linkedin profile, photos can slide one after the other with
   small dotted slider below them."*

   THE STRIP IS A COLUMN, and `.smz-withphotos` is the row that holds it beside the fields. A grid
   rather than a float, so the fields' own grid keeps its full track width in the second column
   instead of flowing around an image — `minmax(0, 1fr)` is what stops a wide photo pushing the form
   off its edge, the "one track, not a strip of nothing and then the form" rule this sheet already
   states for the canvas.

   IT STACKS UNDER ~640px. A 160px picture beside a form is two columns of nothing on a phone; the
   strip goes full width above the fields, which is the same order a reader scans in anyway.

   TOKENS, not literals, for spacing and radii — this is new work, and new work takes its type,
   spacing, radii and colour from the tokens (`design-system`). */
.smz-withphotos {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: var(--space-4, 16px);
  align-items: start;
}
@media (max-width: 640px) {
  .smz-withphotos { grid-template-columns: minmax(0, 1fr); }
}

.smz-photos { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

/* THE STAGE IS A FIXED SQUARE so a gallery of mixed orientations does not make the form jump by a
   hundred pixels every time somebody presses a dot. `object-fit: cover` is what lets a portrait and
   a landscape share one frame without either being distorted. */
.smz-photos__stage {
  position: relative;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 8px);
  overflow: hidden;
  background: var(--surface-2, #f4f5f7);
}
/* A PICTURE THAT DOES NOT LOAD IS AN ORDINARY STATE HERE, AND IT ARRIVED WITH THE PRODUCT OWNER
   (2026-09-25). A party photo is always a blob we hold, so a broken one means something is wrong;
   a PIMLY asset may be an `external_url` on somebody else's CDN, which can 404, move or be taken
   down without anything on our side changing. Chrome then renders the `alt` as ordinary inline text
   at body size — so the org's own description of the photograph wrapped to three lines inside a 64px
   frame and shoved the row's own text sideways, which reads as a broken LAYOUT rather than a missing
   image.

   THE ALT STAYS. It is what a screen reader reads and, when the picture is gone, the only thing left
   that says what it was — it is merely sized to the frame holding it. `overflow: hidden` on the
   stage does the clipping; these three make what is clipped legible rather than truncated mid-word. */
/* THE STAGE IS A BUTTON (it opens the full photograph) AND MUST NOT LOOK LIKE ONE — no chrome of
   its own, the picture is the control. `zoom-in` is the cursor that says what the press does, and
   the focus ring is non-negotiable: *every control owes six states*, and `:focus-visible` is the one
   a keyboard reader depends on. */
.smz-photos__open {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}
.smz-photos__open:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }

.smz-photos__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  font-size: var(--fs-xs);
  line-height: var(--lh-tight);
  color: var(--text-faint);
}

/* THE ARROWS ARE HOVER-VISIBLE AND FOCUS-VISIBLE, never hover-ONLY: *nothing that matters is
   hover-only*, and a carousel a keyboard cannot advance is a carousel half the readers cannot use.
   24x24 minimum, which is the target size this sheet owes every control. */
.smz-photos__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  min-width: 24px;
  min-height: 24px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 999px;
  background: rgb(0 0 0 / 45%);
  color: #fff;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 120ms ease;
}
.smz-photos__nav--prev { left: 4px; }
.smz-photos__nav--next { right: 4px; }
.smz-photos:hover .smz-photos__nav,
.smz-photos__nav:focus-visible { opacity: 1; }
.smz-photos__nav:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }

.smz-photos__badge {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-start: 4px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-ink);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* THE DOTTED SLIDER the direction asks for. Buttons, not spans: pressing one is how you reach a
   slide, and a span is unreachable from a keyboard. */
.smz-photos__dots { display: flex; justify-content: center; gap: 4px; min-height: 12px; }
.smz-photos__dot {
  width: 6px;
  height: 6px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: var(--border);
  cursor: pointer;
}
.smz-photos__dot.is-on { background: var(--primary); }
/* A 6px target is far under 24px, so the HIT AREA is grown without growing the dot — the control
   somebody presses is bigger than the thing they see, which is the only way a row of dots can be
   both discreet and reachable. */
.smz-photos__dot::before { content: ''; position: absolute; inset: -9px; }
.smz-photos__dot { position: relative; }
.smz-photos__dot:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.smz-photos__acts { display: flex; gap: 2px; justify-content: center; }
.smz-photos__act {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  min-height: 24px;
  padding: 2px 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm, 6px);
  background: transparent;
  color: var(--text-2, #5b6472);
  font-size: 11px;
  cursor: pointer;
  position: relative;
}
.smz-photos__act:hover { background: var(--surface-2, #f4f5f7); color: var(--text); }
.smz-photos__act:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.smz-photos__act--del:hover { color: var(--danger, #b42318); }
.smz-photos__actlabel { font-size: 11px; }
/* THE FILE INPUT IS THE LABEL'S OWN and is visually hidden rather than `display: none` — a hidden
   input is not focusable, so the keyboard route to "add a photo" would disappear with it. */
.smz-photos__file {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

/* THE EMPTY STATE — a dashed target that says what it is for, the shape `+ Element` uses on the
   canvas. Not a bordered box around a bordered box: one dashed edge, nothing inside it framed. */
.smz-photos--empty .smz-photos__drop {
  /* A COMPONENT BOX, NOT A FORM LABEL. It is a `<label>` (that is what makes the whole square open
     the file picker), so the sheet's label spacing was landing on it — `margin: 12px 0 6px`,
     measured — which made an EMPTY strip 82px tall where a filled one is 64 and pushed the dashed
     square 12px below the top of its own strip. The visible effect was a name that lined up with
     the picture on rows that had one and sat 12px high on rows that did not. */
  margin: 0;
  display: grid;
  place-items: center;
  gap: 4px;
  aspect-ratio: 1 / 1;
  position: relative;
  border: 1px dashed var(--border);
  border-radius: var(--radius-md, 8px);
  color: var(--text-2, #5b6472);
  font-size: 11px;
  text-align: center;
  cursor: pointer;
}
.smz-photos--empty .smz-photos__drop:hover { border-color: var(--primary); color: var(--text); }
.smz-photos--empty .smz-photos__drop:focus-within { outline: 2px solid var(--primary); outline-offset: 1px; }
.smz-photos__dropicon { font-size: 20px; line-height: 1; }
.smz-photos__none {
  margin: 0;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  border-radius: var(--radius-md, 8px);
  color: var(--text-2, #5b6472);
  font-size: 11px;
}

/* A CELL THAT LEADS WITH A PICTURE — the strip and the text on one line, the picture never
   stretching the row. `align-items: center` rather than baseline: an image aligned on a text
   baseline sits visibly low.

   PIMLY'S VARIANT ROWS ARE THE ONE CALLER SINCE 2026-09-26 — the party master's people and places
   carried strips for a day and the option was removed. The rule stays because the defect below is
   about a strip in ANY crowded row, and pimly's is as crowded as party's was. */
/* THE NAME LINES UP WITH THE PICTURE, NOT WITH THE WHOLE STRIP (2026-09-26, user report:
   *"alignment of the photos are not good"*, with a screenshot of a name floating level with the
   dots).

   THE MEASUREMENT, taken in real Chrome through `frontend/probe/photos.html`: the picture's centre
   sat at y=39 and the name's at y=16 — 23px apart — because the strip is a COLUMN (picture, dots,
   three buttons, ~124px) and `align-items: center` centres the name against all of it. The taller
   the strip, the further the name drifts, so a row with a photo was visibly worse than one without.

   TOP-ALIGNED, THEN THE NAME IS PUSHED DOWN BY HALF THE PICTURE. That keeps the two centres
   together whatever is underneath the picture — add a fourth button tomorrow and the name does not
   move, which restating the height as a magic number would not survive. */
.smz-cellwithpic { --smz-pic: 64px; display: flex; align-items: flex-start; gap: 8px; }
/* THE NAME OCCUPIES EXACTLY THE PICTURE'S HEIGHT AND CENTRES ITSELF INSIDE IT.
   
   The first attempt pushed the text down by `calc((var(--smz-pic) - 1.35em) / 2)` — half the
   picture less half a line — which is arithmetic that has to know the line height, and measuring it
   showed it landing differently on a one-line name than a two-line one. A box of the picture's
   height that centres its own content needs to know neither: one line or three, with a picture or
   with the dashed target, the text sits on the picture's midline. */
.smz-cellwithpic > span {
  min-height: var(--smz-pic);
  display: flex;
  align-items: center;
}
/* THE PICTURE NEVER SHRINKS; THE TEXT DOES (2026-09-25, user report: *"still i am not seeing option
   to upload and see customer photos next to customers and address photos next to addresses. only
   Party details photo is working good."*).

   THE BUG, AND IT IS A ONE-DECLARATION BUG. The cell is a flex ROW, so everything in it is a flex
   ITEM with the default `flex: 0 1 auto` — shrinkable — and `.smz-photos` carries `min-width: 0`,
   which is correct for the record page's grid track and is an explicit PERMISSION TO SHRINK TO ZERO
   here. A row with six other columns and its actions has a narrow first cell, so the 64px strip was
   squeezed to nothing. Present in the DOM, addressed correctly, zero pixels wide.

   THE AVATAR IT REPLACED DID NOT HAVE THE BUG, because that rule carried `flex: none` — the markup
   was swapped and that one declaration was not carried over with it. (Both the avatar and the party
   rows it served are gone; the lesson is why this declaration is here.)

   AND jsdom COULD NOT HAVE CAUGHT IT: it resolves no layout, so every add control was present and
   every test was green. `ui/photoInCell.spec.js` asserts the DECLARATION that wins, which is what
   `ui/cssCascade.js` exists for. */
.smz-cellwithpic > .smz-photos { flex: none; }
/* …and the words give way instead, so a long name wraps rather than crushing the picture. */
.smz-cellwithpic > span { min-width: 0; }

/* THE SAME STRIP, SMALLER — for a row in a list (2026-09-25, user report: *"for each contact and
   each address, we should able to add photos"*).

   `--compact` IS A SIZE, NOT A REDUCED FEATURE SET. The first version of those rows drew a read-only
   avatar, on the argument that twelve stages and twelve dot strips is not a list any more — which was
   about crowding and answered the wrong question: what was asked for was a way to ADD a picture to
   each person and each place, and an avatar is a way to look at one. Everything is here; it is 64px
   instead of 9.5rem.

   THE ACTION LABELS GO, not the actions: at this size the words wrap and the icons alone are the
   control, each keeping its `title` so a hover still says what it does. The buttons keep their 24px
   target, which is the one thing that must not shrink. */
.smz-photos--compact { width: var(--smz-pic, 64px); gap: 3px; }
.smz-photos--compact .smz-photos__stage { border-radius: var(--radius-sm, 6px); }
.smz-photos--compact .smz-photos__badge { font-size: 8px; padding: 0 4px; inset-block-start: 2px; inset-inline-start: 2px; }
.smz-photos--compact .smz-photos__nav { min-width: 18px; min-height: 18px; font-size: 12px; }
.smz-photos--compact .smz-photos__dot { width: 5px; height: 5px; }
.smz-photos--compact .smz-photos__dots { min-height: 8px; gap: 3px; }
.smz-photos--compact .smz-photos__actlabel { display: none; }
.smz-photos--compact .smz-photos__act { padding: 1px 3px; font-size: 10px; }
.smz-photos--compact.smz-photos--empty .smz-photos__drop { gap: 1px; }
.smz-photos--compact .smz-photos__dropicon { font-size: 14px; }
.smz-photos--compact .smz-photos__droptext { font-size: 9px; line-height: 1.2; }

/* "ADD ANOTHER", under a preview. The empty box IS the dashed target; once a file is chosen the
   picture takes the frame and the target becomes a line beneath it — so there is always exactly one
   way in, and it never covers the thing the reader asked to see. */
.smz-photos__drop--more {
  aspect-ratio: auto;
  min-height: 20px;
  padding: 1px 4px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm, 6px);
  font-size: 10px;
  text-align: center;
  position: relative;
  display: grid;
  place-items: center;
  color: var(--text-2, #5b6472);
  cursor: pointer;
}
.smz-photos__drop--more:hover { border-color: var(--primary); color: var(--text); }
.smz-photos__drop--more:focus-within { outline: 2px solid var(--primary); outline-offset: 1px; }


/* THE FULL-SIZE PHOTO VIEWER (2026-09-26, user report: *"when i click on any photo, i am unable to
   see those photos"*). It renders inside the shared overlay, so this styles the CONTENT only — the
   surface, its heading and its close are the drawer's.

   `max-height` IN VIEWPORT UNITS, because the point is to see the whole photograph: a portrait
   picture at natural size would run off the bottom of a laptop screen and the reader would be
   scrolling an image inside a scrolling panel to find its middle. */
.smz-photoview { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.smz-photoview__img {
  max-width: 100%;
  max-height: 72vh;
  object-fit: contain;
  border-radius: var(--radius-md, 8px);
  background: var(--surface-2, #f4f5f7);
}
.smz-photoview__cap { margin: 0; color: var(--text-subtle); font-size: var(--fs-sm, 13px); text-align: center; }
/* ── mail.smaiviz.com boot + sign-in ──────────────────────────────────────────
   Every screen a person meets at mail before the inbox paints: signing in, the
   second factor, "no mailbox yet", "mail is unavailable".

   THESE CLASSES HAD NO RULES AT ALL until 2026-09-26. `mail.html` shipped with the
   markup and nothing styled it, so the first screen of the product rendered as
   browser defaults flush against x=0 — an unstyled h1, an unstyled link, no
   centring, no page padding. It looked exactly like a broken page, and no sweep
   could see it: the markup is correct, the controller is correct, and jsdom
   resolves no layout. Measured in real Chrome at 1440px before the fix
   (.mailboot-host at x=0, w=1440, no padding).

   Sized off the token ramp and the `px` rule (`1rem` is 16px here while body is
   14px), and every control owes its six states — the input and the button both
   carry :focus-visible. */
/* THE RESTORING RULE, IN THE SAME BLOCK AS THE `display` THAT NEEDS IT. `mailBoot` hands over
   with `HOST.hidden = true`, and an author `display` UN-HIDES `[hidden]` at any specificity —
   origin settles before specificity, so `display: flex` below beats the UA's `display: none`.
   Without this the boot host stays a 100vh flex box with no content and pushes the ENTIRE app one
   full viewport down: every element measured at y=813 in an 813px viewport, a screen that is blank
   while the DOM is complete and every request returned 200. Shipped and found by driving the page;
   `bootHidden: true` in a DOM read looks perfectly correct. */
.mailboot-host[hidden] { display: none; }

.mailboot-host {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--space-6) var(--space-4);
  background: var(--bg-muted);
  box-sizing: border-box;
}
.mailboot {
  width: 100%;
  max-width: 380px;
  padding: var(--space-6);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.mailboot__title {
  margin: 0 0 var(--space-2);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--text);
}
.mailboot__body {
  margin: 0 0 var(--space-5);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-muted);
}
.mailboot__form { display: flex; flex-direction: column; gap: var(--space-1); }
.mailboot__label {
  margin-top: var(--space-3);
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}
.mailboot__input {
  /* 40px tall — comfortably past the 24x24 floor, and the height the app's own
     inputs use, so somebody arriving here from the console meets one control. */
  min-height: 40px;
  padding: var(--space-2) var(--space-3);
  font: inherit;
  font-size: 14px;
  color: var(--text);
  background: var(--bg-surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  box-sizing: border-box;
}
.mailboot__input:hover { border-color: var(--text-muted); }
.mailboot__input:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 1px;
  border-color: var(--primary);
}
.mailboot__error {
  margin: var(--space-3) 0 0;
  font-size: 13px;
  line-height: 1.4;
  color: var(--danger);
}
.mailboot__btn {
  display: inline-block;
  margin-top: var(--space-4);
  min-height: 40px;
  padding: var(--space-2) var(--space-4);
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  text-align: center;
  text-decoration: none;
  /* --primary-ink, never a white literal: the dark-mode --primary is a light blue
     and white on it is invisible with nothing failing. */
  color: var(--primary-ink);
  background: var(--primary);
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.mailboot__btn:hover { background: var(--primary-hover); }
.mailboot__btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.mailboot__btn:active { background: var(--primary-hover); }
.mailboot__btn:disabled { opacity: 0.6; cursor: default; }
.mailboot__alt {
  margin: var(--space-4) 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text-muted);
}
.mailboot__alt a { color: var(--primary); }
