/* =============================================================================
   Clipper Kit — shared component CSS
   -----------------------------------------------------------------------------
   SINGLE SOURCE OF TRUTH for the components below. Every flow links this file;
   clipper-molecules-v2.html links it too, so the Kit sheet and the flows render
   from the same rules. Edit here and the change lands everywhere on next load —
   that is the whole point of the file.

   Requires design-system.css (tokens) to be linked first.

   Clipper nomenclature (--background/--foreground/--muted-foreground/--font-sans/--input) is
   used by these rules. Pages that link design-system.css alone need the alias
   layer at the bottom of this file; it is inert when a page already defines the
   Clipper names in its own :root.

   Contents
     1. Header bar organism  — .header-bar / .header-left / .header-right
     2. Header icon button   — .icon-btn (+ notification badge, info tooltip)
     3. Breadcrumb trail     — .breadcrumbs / .breadcrumb-item / .breadcrumb-sep
   ============================================================================= */

/* ── TYPE UTILITY — .ck-text ───────────────────────────────────────────────
   The section's own rule is that every piece of text in a flow must carry a
   token. Until now the kit gave nothing to carry one WITH: there was no type
   class anywhere, so a flow that wanted 18px bold wrote `font:700 18px` and
   the scale went unreferenced. The kit page's own weight specimen did exactly
   that — `font:700 …` as a literal.

   One class, two axes, and both are the kit's existing scales rather than new
   ones: `.size-*` names the step in the type scale, `.weight-*` the weight.
   Line height and colour come from the base so a bare .ck-text is already
   correct; override either the normal way.

   Every step is here, not just the ones in use today, because a scale you can
   only reach half of is a scale people go around. */
.ck-text{
  font-family:var(--font-sans);
  font-size:var(--text-base);
  font-weight:var(--font-weight-normal);
  line-height:1.5;color:var(--foreground)}
/* Rule 65, for the text primitive too: a heading wearing .ck-text keeps no
   browser margin. The Email Templates list heading, an <h2>, had ~10px above
   and below it, which made a 16px-padded head 71px tall. */
:is(h1,h2,h3,h4,h5,h6).ck-text{margin:0}
/* ── COLOUR ROLES — .ck-text.is-* ───────────────────────────────────────
   .ck-text had a size axis and a weight axis and no colour axis, so a
   secondary line — the one under a title, the caption under a field, the
   count beside a name — had nowhere to go and every screen wrote
   `color: var(--muted-foreground)` by hand. Thirty-four of those across three
   screens, each one a local rule painting a kit component, and each one a
   place the role could drift.

   These are ROLES, not colours: is-muted is "secondary to what it sits
   beside", and what that resolves to is the token set's business in each
   theme. The semantic tones are the -foreground halves of their pairs, so a
   tone reads on the page ground rather than only on its own tint. */
.ck-text.is-muted{color:var(--muted-foreground)}
.ck-text.is-primary{color:var(--primary)}
.ck-text.is-destructive{color:var(--destructive-soft-foreground)}
.ck-text.is-success{color:var(--success-soft-foreground)}
.ck-text.is-warning{color:var(--warning-soft-foreground)}
.ck-text.is-info{color:var(--info-soft-foreground)}
.ck-text.size-3xs{font-size:var(--text-3xs)}
.ck-text.size-2xs{font-size:var(--text-2xs)}
.ck-text.size-xs{font-size:var(--text-xs)}
.ck-text.size-sm{font-size:var(--text-sm)}
.ck-text.size-md{font-size:var(--text-md)}
.ck-text.size-base{font-size:var(--text-base)}
.ck-text.size-lg{font-size:var(--text-lg)}
.ck-text.size-xl{font-size:var(--text-xl)}
.ck-text.size-2xl{font-size:var(--text-2xl)}
.ck-text.size-3xl{font-size:var(--text-3xl)}
.ck-text.size-4xl{font-size:var(--text-4xl)}
.ck-text.size-5xl{font-size:var(--text-5xl)}
.ck-text.size-6xl{font-size:var(--text-6xl)}
.ck-text.size-7xl{font-size:var(--text-7xl)}
.ck-text.weight-normal{font-weight:var(--font-weight-normal)}
.ck-text.weight-medium{font-weight:var(--font-weight-medium)}
.ck-text.weight-semibold{font-weight:var(--font-weight-semibold)}
.ck-text.weight-bold{font-weight:var(--font-weight-bold)}
/* Digits that sit in a column line up; the type scale does not decide that,
   the content does, so it is opt-in. */
.ck-text.is-tabular{font-variant-numeric:tabular-nums}

/* ── ONE FAMILY MEANS FORM CONTROLS TOO ───────────────────────────────────
   A <button>, <input>, <select> or <textarea> does not inherit font-family: it
   takes the UA's, which is Arial here. Every kit control sets its own font, so
   nothing on the page renders in it today — but the moment a control gets a
   text label without an explicit rule, it is Arial, and that is a second
   typeface arriving by omission rather than by decision. Now that --font-mono
   is gone and the product is one family, this closes the only way a second one
   could get in. */
/* The root carries the family, so a page that links the kit is in Noto Sans
   before it writes a single rule. Neither file set one until now — the base
   font lived in each consumer's own <style>, which is why the token file's own
   note says a standalone prototype "silently rendered in -apple-system and the
   token was unenforceable". A token nothing applies is a suggestion. */
html{font-family:var(--font-sans)}
/* The browser's 8px body margin framed every page built on the kit alone —
   a gap along the top and right of the shell that no rule asked for. */
body{margin:0}
button,input,select,textarea,optgroup{font-family:inherit}
/* A <button> also arrives with the UA's own padding — 1px 6px in Chrome. On an
   icon-only button that is not cosmetic: the 24px box becomes a 12px content
   box, the 16px glyph no longer fits it, and place-items:center then centres
   the glyph in the CONTENT box rather than the border box. The result is a
   glyph 6px from one edge and 2px from the other — off-centre, consistently,
   in whichever direction the padding is asymmetric against the overflow.
   .ck-icon-btn always declared padding:0 and was always right; four clear
   buttons and the rail never did. Every icon-only button in the kit now
   declares its own padding. */

/* The legacy-name alias layer that used to sit here is gone. Every rule in
   this file, and every page that links it, speaks the shadcn dialect
   directly, so the indirection has no callers. */

/* ─── HEADER BAR (Organism) ─── */
/* 56px, down from 64px. The height was set by the 48px icon frames inside it,
   not by anything the bar itself needed: a 48px frame around a 16px glyph is
   16px of padding a side, which made the bar a third taller than the content
   asked for. The frames are now 40px — still well clear of the 24px target
   floor, and on the 4px grid — so the bar closes to 56px with the same 8px of
   breathing room above and below its controls. */
/* THE BAR HAS NO BACKGROUND, IN ANY MODE. It is transparent, so whatever the
   page is standing on runs behind it — the canvas in a shell, a panel in a
   drawer or dialog header. A --background fill made the bar a white band laid
   ON the page, which is wrong twice: it read as a floating object rather than
   the page's own top edge, and on --canvas it drew a hard seam the first row
   of panels then had to meet with no gap.

   Its bottom rule is what separates it. That is the whole chrome. */
/* THE BAR HAS NO CHROME AT ALL — no background and no border. It is a
   transparent strip that positions its contents; whatever the page stands on
   runs straight through it, and nothing draws a line under it. What separates
   it from the content below is the content's own top edge: in a shell, the
   first row of panels, which is exactly why those meet the header with a 0
   gap. A rule here would double up with that edge and read as a seam.

   SIDE GUTTERS ARE --canvas-gap, 12px, always. Not scaled by size — it is
   the same gap the canvas puts either side of its panels, so the header's
   content lines up with the panel edges directly beneath it. At 8px the
   breadcrumb sat half a gutter inboard of the panels and the whole page read
   as slightly out of register. Vertical padding stays 0: the bar's height is
   its own, set above. */
.header-bar{display:flex;align-items:center;justify-content:space-between;height:48px;padding:0 var(--canvas-gap);background:transparent;border-bottom:0}
/* ── Plain header — .header-bar.is-plain ──
   A title and the trailing controls, no breadcrumb trail. For a page that IS
   the destination: a dashboard, a settings root, a single-level view. Reach
   for the trail only when a user can be somewhere three levels deep and needs
   to know where; on a flat page it is chrome that says nothing.
   Shorter again at 48px, because a single line of title needs no more. */
/* Both variants are 48px now; .is-plain differs only in its title size and
   its icon frames. */
.header-bar.is-plain .header-title{font-size:var(--text-xl)}
/* Leading run — panel toggle, trail, info — on the shared 8px token.
   .header-right keeps its own spacing and is untouched. */
.header-left{display:flex;align-items:center;gap:var(--header-control-gap);flex:1;min-width:0}
/* The trail wraps by default; inside a fixed-height bar it must not. */
.header-left .breadcrumbs{flex-wrap:nowrap;overflow:hidden}
.header-title{font:var(--font-weight-semibold) var(--text-2xl) var(--font-sans);color:var(--foreground);white-space:nowrap}
/* ── THE TWO RUNS ARE FIXED — ORDER, SIZE AND GAP ─────────────────────────
   The leading run, left to right:   panel toggle · breadcrumb or title · info
   The trailing run, left to right:  search · settings · notifications · avatar

   Every control in either run is a 24px frame around a 16px glyph — the pair
   § 7c gives every icon outside the main rail — and every gap between them is
   --header-control-gap, 8px. The avatar closes the trailing run at the same
   24px, its default size, so the run ends on the same rhythm it keeps.

   .header-right used to be gap:0, so each flow spaced its own controls by
   hand and no two headers matched. The geometry is stated on the RUN rather
   than on each control, so a flow that forgets .size-xs still gets it: the
   header cannot drift by omission, only by someone overriding it on purpose.

   The class is doubled to clear .ck-icon-btn.size-xs, which is (0,2,0) and
   declared later in this file. */
.header-right{display:flex;align-items:center;gap:var(--header-control-gap)}
/* The gear's settings menu. Portalled and placed by ckPlaceOverlay, so it is
   fixed to the viewport; its width holds the longest label on one line. */
.ck-menu.is-settings{position:fixed;z-index:var(--z-overlay);min-width:208px}
.ck-menu.is-settings[hidden]{display:none}
:is(.header-left.header-left,.header-right.header-right) > :is(.ck-icon-btn,.ck-ico,.ck-avatar){
  width:var(--icon-box);height:var(--icon-box);flex:none}
:is(.header-left.header-left,.header-right.header-right) > :is(.ck-icon-btn,.ck-ico) > svg{
  width:var(--icon-size);height:var(--icon-size)}
/* 40px, down from 48px — see .header-bar. It is what set the bar's height. */
/* The header bar's private .icon-btn is gone. It drew a 40px frame around a
   20px glyph, which is the MAIN LEFT NAVIGATION's pair (--icon-box-nav /
   --icon-size-nav) and nowhere else's. Every icon outside that rail is a 16px
   glyph in a 24px box (§ 7c), and the header is not the rail.

   The header now composes .ck-icon-btn.size-xs — 24px frame, 16px glyph, and
   every state the atom already has. */
/* .header-bar.is-plain .icon-btn had a 36px override. The header composes
   .ck-icon-btn now and both runs are 24px, so it described nothing. */
/* Flows predating the .ico helper put a bare <svg> in the button. Both markup
   styles are supported so linking this file is a no-op visually; the two sizes
   are a divergence to reconcile, not a new one being introduced. */

/* A count on the corner of a 24px button, so it is 16px and overhangs rather
   than sitting inside and swallowing the glyph. */
/* The count bubble is defined once, with the icon — see .ck-ico-count. */
/* Tooltip below (header info icon) */
.hb-tooltip{position:absolute;top:calc(100% + 8px);left:50%;transform:translateX(-50%);padding:8px 12px;border-radius:var(--radius-2xl);background:var(--foreground);color:var(--background);font:var(--font-weight-normal) var(--text-md)/1.5 var(--font-sans);white-space:normal;width:max-content;max-width:280px;opacity:0;pointer-events:none;transition:opacity var(--duration-base);z-index:var(--z-dropdown)/* no box-shadow, ever: a tooltip is a label, not a surface */}
.hb-tooltip::after{content:'';position:absolute;top:-4px;left:50%;transform:translateX(-50%) rotate(45deg);width:8px;height:8px;background:var(--foreground)}
/* The tooltip is absolute, so its button has to be the containing block.
   Without this it resolved against the initial containing block: parked at the
   document's bottom edge at opacity 0, it still extended the scrollable area
   and gave every screen using data-info a spurious vertical scrollbar. */
.hb-info-btn{position:relative}
.hb-info-btn:hover .hb-tooltip{opacity:1}
/* The header bar's own .avatar is gone. It was a second avatar — 32px, a
   --primary plate, its own font — living inside the header block while
   .ck-avatar was the atom the rest of the product used. An organism composes
   the atoms the kit already ships (§ 14.3 check 40); it does not keep a
   private copy that drifts.

   It takes the atom at its DEFAULT size, which is --icon-box: the same 24px
   frame as the .ck-icon-btn.size-xs beside it, so the whole trailing run is one
   box size. It was .size-lg (32px) for one release — carried over from the
   fork's own 32px, and correct while the header's buttons were still 40px.
   Once those came down to 24 the avatar was the only thing left at 32 and it
   overhung the row by 4px top and bottom. A run of controls shares one box
   (§ 14.3 check 8). */

/* ─── BREADCRUMBS ───────────────────────────────────────────────────────────
   A standalone component as of v3.2. It had lived only inside the header bar,
   on unprefixed classes, which § 12.4 counts as flow CSS leaking into the kit.
   .ck-breadcrumb* are now the canonical names; the unprefixed ones stay as
   aliases because pages use them, the same arrangement as .switch/.ck-switch.

   The trail is 16px MEDIUM and the current crumb steps up to 18px SEMIBOLD,
   keyed to aria-current="page" so the drawing cannot drift from what is
   announced. Two axes separate them, weight and size — the inactive crumbs
   were semibold too, which left only 2px of size between "where you are" and
   "where you came from", and at a glance the trail read as five titles.

   Spec: references/components/breadcrumb.md */
.ck-breadcrumbs,.breadcrumbs{
  display:flex;align-items:center;gap:var(--breadcrumb-gap,4px);
  font:var(--font-weight-medium) var(--breadcrumb-size,16px) var(--font-sans);
  flex-wrap:wrap}
/* Compact trail, for a breadcrumb inside a dialog, drawer or panel header,
   where the page-level 16/18px would out-shout the panel's own title. */
.ck-breadcrumbs.size-sm{--breadcrumb-size:var(--text-base)}
.ck-breadcrumbs.size-sm .ck-breadcrumb-item[aria-current="page"],
.ck-breadcrumbs.size-sm .ck-breadcrumb-item.current{font-size:var(--text-lg)}

/* A CRUMB NEVER WRAPS. It is one name, and a name broken across two lines
   stops looking like a step in a path — it also makes the crumb taller than
   the bar holding it, which is how a 48px header ends up with 52px of
   breadcrumb inside it. The trail already truncates as a whole
   (.header-left .breadcrumbs is nowrap + overflow:hidden); this is the same
   rule one level down, where the text actually breaks. */
.ck-breadcrumb-item,.breadcrumb-item{
  display:inline-flex;align-items:center;gap:var(--space-xs);
  color:var(--muted-foreground);text-decoration:none;
  padding:4px 6px;border-radius:var(--radius-sm);cursor:pointer;
  white-space:nowrap;min-width:0;
  transition:color var(--duration-fast),background var(--duration-fast)}
.ck-breadcrumb-item:is(:hover,[data-force~="hover"]),.breadcrumb-item:is(:hover,[data-force~="hover"]){
  color:var(--foreground);background:var(--hover-bg)}
.ck-breadcrumb-item:is(:active,[data-force~="active"]),.breadcrumb-item:is(:active,[data-force~="active"]){background:var(--selected-bg)}
/* A crumb is interactive, so it owes a focus ring. It had none — the browser
   default does not follow the theme, and § 9 makes that a Sev-1. */
.ck-breadcrumb-item:is(:focus-visible,[data-force~="focus"]),.breadcrumb-item:is(:focus-visible,[data-force~="focus"]){
  outline:0;box-shadow:var(--focus-ring)}
/* Active crumb steps up in size AND weight: 18px semibold against the trail's
   16px medium. .current stays as a transitional alias. */
.ck-breadcrumb-item[aria-current="page"],.ck-breadcrumb-item.current,
.breadcrumb-item[aria-current="page"],.breadcrumb-item.current{
  color:var(--foreground);font-size:var(--text-2xl);
  font-weight:var(--font-weight-semibold);cursor:default}
.ck-breadcrumb-item[aria-current="page"]:hover,.ck-breadcrumb-item.current:hover,
.breadcrumb-item[aria-current="page"]:hover,.breadcrumb-item.current:hover{
  background:transparent}
.ck-breadcrumb-item svg,.breadcrumb-item svg{
  width:var(--icon-size);height:var(--icon-size);flex:none;display:block}

/* The separator reads the trail's own size token, so changing one size moves
   the chevrons with the text instead of leaving them behind. */
.ck-breadcrumb-sep,.breadcrumb-sep{
  color:var(--muted-foreground);font-size:var(--breadcrumb-size,16px);
  flex:none;user-select:none}
.ck-breadcrumb-sep svg,.breadcrumb-sep svg{
  width:1em;height:1em;display:block}

/* Collapsed middle. A trail deeper than about four levels stops being
   scannable, so the middle collapses behind one control that reveals the rest.
   It is a real <button> with an aria-label, not an inert ellipsis. */
.ck-breadcrumb-more{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:24px;height:24px;padding:0 var(--space-xs);
  border:0;border-radius:var(--radius-sm);background:0;cursor:pointer;
  color:var(--muted-foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  transition:color var(--duration-fast),background var(--duration-fast)}
.ck-breadcrumb-more:hover{color:var(--foreground);background:var(--hover-bg)}
.ck-breadcrumb-more:focus-visible{outline:0;box-shadow:var(--focus-ring)}

/* ─── SETTINGS PANEL (Organism) ───────────────────────────────────────────
   Section card + field grid + controls, as used by folder settings and any
   other settings surface. Prefixed ck- so these can never collide with a
   flow's own class names. Edit here and every flow follows on next load. */
/* Light teal, not grey: --primary is so dark and desaturated that a 4% mix
   reads as neutral. --teal carries actual hue at the same lightness. */
/* ─── CARD / SETTINGS CARD / TOOLBAR (Molecules) ──────────────────────────
   .ck-card had no base rule at all — only a descendant selector for an icon
   inside its title. Anything using it was inheriting nothing.

   Both card surfaces read --card / --accent rather than a color-mix of
   --teal (Rulebook §10).

   Spec: references/components/surfaces.md */
/* The stroke is --panel-stroke, not --panel-border, and the difference is
   whether the card has an edge at all: --panel-border measures about 1.02:1
   against --canvas, so a card resting on the app ground was being read by its
   white fill alone. --panel-stroke is 1.63:1 against the panel and 1.44:1
   against the canvas — visible, without becoming a hard rule around every
   region. .ck-panel already used it; a card and a panel both rest on the
   canvas and must not disagree about their own edge. */
.ck-card{background:var(--card);color:var(--card-foreground);
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);padding:var(--container-inset)}
.ck-card.size-sm{padding:var(--container-inset-sm)}
/* The secondary card — the tinted surface option, --card-secondary with its
   own foreground and a stroke one step up from the fill. Every other part of
   the card (inset, radius, sizes) is unchanged. */
.ck-card.is-secondary{
  background:var(--card-secondary);color:var(--card-secondary-foreground);
  border-color:var(--card-secondary-border)}
.ck-card.size-lg{padding:var(--container-inset-lg)}
/* A card the reader picks from a list — .ck-card.is-selectable / .is-selected.
   A linking set, a saved view, a template: one card of the list is the one in
   use. Hover firms the stroke to --input, the chosen card takes a --primary
   stroke thickened by a .5px ring to 1.5px, so the choice reads at a glance, and focus is
   the kit's ring. Found on the DES-667 linking-set list, which drew all three
   by hand. */
.ck-card.is-selectable{cursor:pointer;transition:border-color var(--duration-base),box-shadow var(--duration-base)}
.ck-card.is-selectable:is(:hover,[data-force~="hover"]){border-color:var(--input)}
.ck-card.is-selectable:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-card.is-selectable:is(.is-selected,[aria-selected="true"],[aria-current="true"]){
  border-color:var(--primary);box-shadow:0 0 0 .5px var(--primary)}
.ck-card-title{font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--card-foreground);
  display:flex;align-items:center;gap:var(--space-xs);
  /* Rule 65. A card title is reachable as a heading, and as an <h2> it kept the
     browser's ~0.83em above and below — Smart Values' heading sat 29px into a
     card padded 16, and 24px above a list the card gaps at 12. */
  margin:0}
.ck-card-title .ck-info{width:var(--icon-size);height:var(--icon-size);color:var(--muted-foreground);cursor:help;display:inline-flex}
/* A CARD TITLE'S ACTION SITS ON ITS TRAILING EDGE, ON THE TITLE'S LINE. A
   button parked on a row of its own under the title spends a whole row on
   one control and reads as belonging to the content below. The first button
   in the title takes the slack; it is size-sm (28px) so the title keeps its
   own line. */
.ck-card-title > .ck-btn:first-of-type{margin-inline-start:auto}
.ck-card-sub{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);margin-top:2px}

/* ─── PAGE HEAD — .ck-page-head ────────────────────────────────────────────
   The screen's own title, one line of what the screen is for, and the actions
   that act on the screen. It was specified and never built, so every flow
   invented one: 16px semibold on one screen, 18px medium on the next, the
   action row drifting between the two edges. .ck-page-heading stacks the title
   over .ck-page-sub; .ck-page-actions.is-end sends the actions to the trailing
   edge, on the title's line.

   Spec: references/components/page-head.md */
.ck-page-head{display:flex;align-items:flex-start;gap:var(--space-base);flex-wrap:wrap;margin-block-end:var(--space-xl)}
.ck-page-heading{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-page-title{margin:0;display:flex;align-items:center;gap:var(--space-sm);
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--foreground)}
.ck-page-sub{margin:0;font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-page-actions{display:flex;align-items:center;gap:var(--space-sm);flex-wrap:wrap}
.ck-page-actions.is-end{margin-inline-start:auto}

.ck-settings-card{background:var(--accent);color:var(--accent-foreground);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);padding:var(--container-inset)}
.ck-settings-card > .ck-card-title{font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--accent-foreground);
  padding-bottom:16px;border-bottom:1px solid var(--panel-border)}

.ck-toolbar{display:flex;align-items:center;gap:var(--space-base);margin-bottom:16px;flex-wrap:wrap}
.ck-toolbar .grow{flex:1}
.ck-toolbar.size-sm{gap:var(--space-sm);margin-bottom:12px}
/* IN A STACK THAT SPACES WITH GAP, THE TOOLBAR BRINGS NO MARGIN. Its 16px
   bottom margin is for a toolbar in plain flow. Inside .ck-form or a dialog
   or drawer body, the parent's gap already separates the rows, and the two
   added up: 28px under a toolbar in a 12px rhythm. The gap is the spacing. */
:is(.ck-form,.ck-dialog-body,.ck-drawer-body) > .ck-toolbar{margin-bottom:0}
/* In a row of controls — an action bar's group — a toolbar is one item on the
   row's centre line; a bottom margin lifts it off that line (6px on Edit
   Document's linking-set chips beside the status pill, check 79). */
.ck-action-bar-group > .ck-toolbar{margin-bottom:0}

/* ─── FIELD GRID (Molecule) ─── */
/* ─── INPUT / FIELD (Atom + Molecule) ─────────────────────────────────────
   .ck-field is the labelled wrapper; .ck-input / .ck-select / .ck-textarea are
   the controls. .ck-field-grid lays fields out in 1-3 columns.

   Sizes share the control height scale (Rulebook §7a): 28 / 34 / 40. The
   height is explicit and the padding is inline only — an input sized by
   padding alone lands on whatever its font metrics produce, which is how
   input (35) and select (37) drifted away from button (34).

   A textarea has no fixed height, so its sizes change padding and min-height
   instead.

   No :active state, deliberately: pressing a text field puts the caret in it,
   which IS the focus state. A separate pressed treatment would flash for one
   frame and mean nothing. Buttons and triggers do get :active.

   Spec: references/components/input.md */
.ck-field-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-base)}
.ck-field-grid.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.ck-field-grid.cols-1{grid-template-columns:minmax(0,1fr)}
/* A TALL FIELD SPANS TWO ROWS. A textarea in a row of single-line fields made
   the whole row as tall as itself, so the next row started a textarea's
   height below its neighbours. .span-rows-2 lets it take two rows and the
   short fields beside it stack at the grid's own gap. */
:is(.ck-field-grid,.ck-form-grid) > .span-rows-2{grid-row:span 2}
.ck-field-grid.cols-1 > .span-rows-2{grid-row:auto}
/* A field that takes the whole row, like .ck-form-grid's. */
.ck-field-grid > .span-all{grid-column:1 / -1}
/* A field that starts a new row and keeps its one-column width: a short
   field alone on its line, or a pair, without stretching across the grid. */
.ck-field-grid > .is-row-start{grid-column-start:1}

/* ── FORM — .ck-form ───────────────────────────────────────────────────────
   The container the field atoms were always meant to sit in. Three variants,
   and the only thing that changes between them is the column count: one, two
   or three. Everything else — the row rhythm, the label treatment, where the
   buttons go, what happens when a field fails — is identical, which is the
   point of having the component at all.

   Spec: references/components/form.md */
.ck-form{
  display:flex;flex-direction:column;gap:var(--space-lg);
  min-width:0;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  color:var(--foreground)}
/* On a panel it brings the panel's own frame; bare, it is just layout. */
.ck-form.size-sm{gap:var(--space-md)}
.ck-form.is-carded{
  padding:var(--container-inset);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground)}

.ck-form-head{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-form-title{
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);
  color:var(--foreground);margin:0}
.ck-form-sub{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground);margin:0}
/* Rule 65: a form title is a heading (<h2>/<h3>) and its sub a <p>; both kept
   the browser's margins, so a carded section's title sat a line below its
   inset and pushed the fields down. */

/* A titled run of fields. <fieldset> so the legend is a real legend and the
   grouping survives with stylesheets off. */
.ck-form-section{
  display:flex;flex-direction:column;gap:var(--space-md);
  min-width:0;border:0;padding:0;margin:0}
.ck-form-legend{
  padding:0;
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);
  color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.04em}

/* THE THREE VARIANTS. align-items:start is load-bearing: without it every
   field in a row stretches to the tallest, so the one field showing an error
   drags its neighbours' controls out of line with it. */
.ck-form-grid{
  display:grid;gap:var(--space-md) var(--space-base);
  align-items:start;min-width:0;
  grid-template-columns:minmax(0,1fr)}
.ck-form-grid.cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.ck-form-grid.cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
/* A field that needs the whole row — a textarea, an address, a long note. */
.ck-form-grid > .span-2{grid-column:span 2}
.ck-form-grid > .span-all{grid-column:1 / -1}
.ck-form-grid.cols-1 > :is(.span-2,.span-all){grid-column:auto}

/* Columns are a wide-window luxury. Below these widths a multi-column form is
   a row of 90px inputs, so it folds — three to two, then everything to one.
   Container queries would be better but the grid has no container to query on
   a bare form, and a viewport query is honest about what it measures. */
@media (max-width:900px){
  .ck-form-grid.cols-3{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:620px){
  .ck-form-grid.cols-2,.ck-form-grid.cols-3{grid-template-columns:minmax(0,1fr)}
  .ck-form-grid > :is(.span-2,.span-all){grid-column:auto}
}

/* The action run. Right-aligned because the primary action is the last thing
   read before it is pressed; .is-split when there is a destructive action that
   must not sit next to Save. */
.ck-form-foot{
  display:flex;justify-content:flex-end;align-items:center;
  gap:var(--space-sm);flex-wrap:wrap;
  padding-block-start:var(--space-md);
  border-block-start:1px solid var(--border)}
.ck-form-foot.is-split{justify-content:space-between}
.ck-form-foot-actions{display:flex;align-items:center;gap:var(--space-sm);flex-wrap:wrap}
.ck-form.is-carded > .ck-form-foot{
  margin:0 calc(var(--space-lg) * -1) calc(var(--space-lg) * -1);
  padding:var(--space-md) var(--space-lg)}
.ck-field{display:flex;flex-direction:column;gap:var(--space-xs);min-width:0}
.ck-field .ck-field-hint{margin-top:-2px}
/* A label is read at a glance, so it truncates instead of wrapping. Put the
   full text in a title attribute wherever it can run long. */
.ck-field-label{font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--foreground);display:flex;align-items:center;gap:var(--space-xs);min-width:0}
.ck-field-label > .ck-field-label-text{overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-field-hint{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
/* A hint written as rules ("8 characters, 1 number…") is a list under the
   field: no browser margins, the alert's 20px indent. */
.ck-field-hint :is(ul,ol){margin:0;padding-inline-start:var(--space-lg)}
/* A hint that warns without blocking: the value is allowed, but the reader
   should know something about it. Not .ck-field-error, which blocks. */
.ck-field-hint.is-warning{color:var(--warning-soft-foreground)}
/* ── ERROR MESSAGES ARE HIDDEN UNTIL SOMETHING ACTUALLY FAILS ──────────────
   The message is written into the markup at build time and stays display:none
   until the control it belongs to is marked invalid. It is NOT conditionally
   rendered by the page, and it is NOT visible by default: a form that opens
   with its errors already showing is telling the reader they got something
   wrong before they typed anything.

   The trigger is the control's own aria-invalid, so the thing that reveals the
   message is the same thing a screen reader reads — they cannot disagree. The
   .is-invalid escape hatch is for a group (a radio set, a fieldset) that has
   no single control to carry the attribute.

   Three selectors, because the message is not always reachable the same way:
   :has() covers a control nested in .ck-input-wrap, the sibling combinator
   covers the flat case, and .is-invalid covers a group. Rulebook 7d. */
.ck-field-error{
  display:none;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--destructive)}
.ck-field:has(:is([aria-invalid="true"],[data-invalid="true"])) > .ck-field-error,
:is([aria-invalid="true"],[data-invalid="true"]) ~ .ck-field-error,
.ck-field.is-invalid > .ck-field-error,
.ck-field-error.is-shown{display:block}
/* A field carrying a live error suppresses its hint: two lines of small print
   under one control, one grey and one red, is noise at the moment the reader
   most needs a single instruction. */
.ck-field:has(:is([aria-invalid="true"],[data-invalid="true"])) > .ck-field-hint,
.ck-field.is-invalid > .ck-field-hint{display:none}

/* The form-level summary follows the same law: present in the markup, silent
   until submission actually failed. */
/* The form's summary banner IS the kit alert — .ck-alert.is-error.size-sm
   carries every colour, the stroke, the radius and the inset. This rule only
   decides when it shows. It used to paint its own copy of an error alert,
   which is how a form drifts from the atoms it is built on. */
.ck-form-error,.ck-alert.ck-form-error{display:none}
.ck-form.is-invalid > .ck-form-error,.ck-form-error.is-shown,
.ck-form.is-invalid > .ck-alert.ck-form-error,.ck-alert.ck-form-error.is-shown{display:flex}
.ck-form-error > svg{
  width:var(--icon-size);height:var(--icon-size);flex:none;
  stroke-width:var(--icon-stroke);margin-block-start:1px}

/* .ck-select needs appearance:none. With the UA appearance active, Chrome
   paints the option text inside its own inner box on top of our padding, so a
   select's text sat further right than the input and dropdown trigger stacked
   above it in the same field grid — a visible misalignment with identical CSS.
   The caret is drawn with two gradients so its colour stays a token; a
   data-URI SVG would hardcode a hex and stop following the theme. */
.ck-select{appearance:none;-webkit-appearance:none}
.ck-select::-ms-expand{display:none}

.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger,.ck-combo-control{
  box-sizing:border-box;width:100%;height:36px;padding:0 11px;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground);background:var(--background);
  border:1px solid var(--field-border);border-radius:var(--radius-md);
  transition:border-color var(--duration-base),box-shadow var(--duration-base);
  /* Responsive by default: the field takes its parent's width and can shrink
     inside a flex or grid track instead of forcing that track wider. */
  max-width:100%;min-width:0
}
/* Long text. A single-line field scrolls sideways as it fills, so the caret
   stays visible and nothing the user typed hides behind an ellipsis. */
.ck-input,.ck-select{text-overflow:clip}

/* ── AI FIELD — .is-ai ──
   A field whose content came from, or is going to, the model: the AI feedback
   box. It reads --purple-soft-foreground on its stroke so it is legible at a
   glance as "this one is the AI's", without changing its shape, height or
   type — it is the SAME control, marked, not a different control.

   Purple and not a semantic tone on purpose. Success, warning and destructive
   all mean something about the VALUE; this means something about where the
   value came from, which is an orthogonal fact. Borrowing a semantic tone for
   it would make an AI field look like a passing or failing one.

   The mark is a stroke and a ring, never a fill: a tinted field reads as
   read-only or disabled, and this one is fully editable.

   It rides on the field family, so it works on .ck-input, .ck-select,
   .ck-textarea and .ck-dd-trigger with no extra rules — and every state below
   is the base state's structure with the purple substituted, so nothing about
   focus, invalid or disabled behaviour changes.

   Spec: references/components/input.md § AI field */
:is(.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger).is-ai{
  border-color:var(--ai-border)}
:is(.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger).is-ai:is(:hover,[data-force~="hover"]){
  border-color:var(--purple)}
:is(.ck-input,.ck-select,.ck-textarea).is-ai:is(:focus,[data-force~="focus"]),
.ck-dd-trigger.is-ai:is(:focus-visible,[data-force~="focus"]),
.ck-dd.open .ck-dd-trigger.is-ai{
  outline:0;border-color:var(--ai-border);box-shadow:var(--focus-ring-ai)}
/* Invalid still wins over the AI mark. A field that is wrong has to say wrong
   first — where it came from is the lesser fact, and the destructive stroke is
   the one a user must not miss. */
:is(.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger).is-ai[aria-invalid="true"]{
  border-color:var(--destructive)}
:is(.ck-input,.ck-select,.ck-textarea).is-ai[aria-invalid="true"]:is(:focus,[data-force~="focus"]){
  border-color:var(--destructive);box-shadow:var(--focus-ring-error)}
/* Disabled and read-only drop the mark entirely: an inactive field is not
   offering the user anything, so advertising its provenance is noise. */
:is(.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger).is-ai:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  border-color:var(--field-border)}
:is(.ck-input,.ck-textarea).is-ai:read-only:not(:disabled){border-color:var(--field-border)}
/* ── LINKED — .is-linked ──
   The field whose value is being traced to where it was read on the page:
   the review screen draws a --success connector from the field to its OCR
   box, and the field takes the same colour so the two ends read as one link.
   A state, not a focus: it holds while focus moves into the AI editor the
   link opened. Declared after .is-ai so a trained field that is linked shows
   the link. */
:is(.ck-input,.ck-textarea).is-linked,
:is(.ck-input,.ck-textarea).is-linked:is(:hover,:focus,[data-force~="focus"]){
  border-color:var(--success);box-shadow:var(--focus-ring-success)}
/* The label that names it. --purple-soft-foreground clears 4.5:1 on the page,
   so it is a real label and not a decoration. */
.ck-field-ai{
  display:inline-flex;align-items:center;gap:var(--space-xs);
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--purple-soft-foreground)}
.ck-field-ai > svg{width:var(--icon-size);height:var(--icon-size);flex:none}
/* Vertical only. A field's WIDTH belongs to its .ck-field-grid column, so a
   horizontal handle would let one field break the grid's alignment and every
   field beside it. Dragging cannot go under the size class's min-height, so a
   textarea can never be collapsed to nothing. */
/* A TEXTAREA STARTS AT ITS FIELD'S HEIGHT — 36px, like the input beside it
   (28 / 44 at size-sm / size-lg) — and the reader drags it taller. At 76px it
   set every row it sat in two inputs tall before anyone had typed a word.
   The height is set, not rows-driven, so a rows="" attribute cannot undo it;
   resize:vertical is the way to more room. */
.ck-textarea{height:36px;min-height:36px;padding:8px 11px;resize:vertical;
  /* A text area grows downward, so its long text scrolls VERTICALLY. */
  overflow-y:auto;overflow-x:hidden}
/* Where the row height is fixed — an editable table cell — suppress it. */
.ck-textarea.no-resize{resize:none}
/* Declared AFTER the shared rule: its `background:` shorthand resets
   background-image, so the caret has to come later or it never paints. */
.ck-select{
  padding-inline-end:30px;
  background-image:
    linear-gradient(45deg,transparent 50%,var(--muted-foreground) 50%),
    linear-gradient(135deg,var(--muted-foreground) 50%,transparent 50%);
  background-position:calc(100% - 16px) center,calc(100% - 11px) center;
  background-size:5px 5px,5px 5px;
  background-repeat:no-repeat
}
.ck-select.size-sm{padding-inline-end:26px;background-position:calc(100% - 14px) center,calc(100% - 9px) center;background-size:4px 4px,4px 4px}
.ck-select.size-lg{padding-inline-end:36px;background-position:calc(100% - 18px) center,calc(100% - 13px) center;background-size:6px 6px,6px 6px}
.ck-select:disabled,.ck-field.is-locked .ck-select{background-image:none}

/* Sizes */
.ck-input.size-sm,.ck-select.size-sm{height:28px;padding:0 9px;font-size:var(--text-md)}
.ck-input.size-lg,.ck-select.size-lg{height:44px;padding:0 13px}
.ck-textarea.size-sm{height:28px;min-height:28px;padding:6px 9px;font-size:var(--text-md)}
.ck-textarea.size-lg{height:44px;min-height:44px;padding:10px 13px}

:is(.ck-input,.ck-select,.ck-textarea,.ck-dd-trigger):is(:hover,[data-force~="hover"]){border-color:var(--input-border-hover)}
:is(.ck-input,.ck-select,.ck-textarea):is(:focus,[data-force~="focus"]){outline:0;border-color:var(--primary);box-shadow:var(--focus-ring)}
.ck-input::placeholder,.ck-textarea::placeholder{color:var(--muted-foreground)}

/* Invalid */
.ck-input[aria-invalid="true"],.ck-select[aria-invalid="true"],.ck-textarea[aria-invalid="true"],.ck-dd-trigger[aria-invalid="true"]{border-color:var(--destructive)}
.ck-input[aria-invalid="true"]:focus,.ck-select[aria-invalid="true"]:focus,
.ck-textarea[aria-invalid="true"]:focus{border-color:var(--destructive);box-shadow:var(--focus-ring-error)}

/* Disabled and read-only. Locked keeps its own class for the field-level
   treatment that predates this. */
.ck-input:disabled,.ck-select:disabled,.ck-textarea:disabled,
.ck-field.is-locked .ck-input,.ck-field.is-locked .ck-select{
  background:var(--muted);color:var(--muted-foreground);border-color:var(--field-border);cursor:not-allowed
}
.ck-input:read-only:not(:disabled),.ck-textarea:read-only:not(:disabled){background:var(--muted)/* pairing-exempt: re-asserts the surface only; the base rule owns the colour */}
/* A figure field: an amount, a count. Right-aligned, tabular, so a column of
   them compares down the column the way .num does in a table. */
.ck-input.is-num{text-align:end;font-variant-numeric:tabular-nums}

/* ── INPUT GROUP — .ck-input-group > .ck-input-addon + .ck-input ──────────
   A field whose value starts with a fixed part the user does not type: a
   country prefix on a VAT ID, a scheme code on a network ID. The fixed part is
   an addon joined to the field's leading edge, so it reads as part of the
   value and cannot be edited or deleted. It takes the field's own height,
   stroke and radius, so the pair is one control; the field keeps its focus
   ring and every state. Disabled or read-only mutes the addon with it.

   Spec: references/components/input.md § Input group */
.ck-input-group{display:flex;align-items:stretch;min-width:0}
.ck-input-addon{display:inline-flex;align-items:center;flex:none;box-sizing:border-box;height:36px;padding:0 11px;
  border:1px solid var(--field-border);border-inline-end:0;
  border-start-start-radius:var(--radius-md);border-end-start-radius:var(--radius-md);
  background:var(--muted);color:var(--foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);font-variant-numeric:tabular-nums;user-select:none}
.ck-input-group > .ck-input{flex:1;border-start-start-radius:0;border-end-start-radius:0}
.ck-input-group:has(> .ck-input:is(:disabled,:read-only)) > .ck-input-addon{color:var(--muted-foreground)}
.ck-input-group.size-sm > .ck-input-addon{height:28px;padding:0 9px;font-size:var(--text-md)}

/* ── The field clear ─────────────────────────────────────────────────────
   One control for the whole family: an input, a search box, a textarea and a
   dropdown all clear the same way and it looks identical in each.

   It is REVEALED, not resident: hidden until the field is hovered or holds
   focus, so a row of filled fields is not a row of crosses. Visibility is
   driven by the WRAPPER, because a control hidden on its own cannot be
   hovered into view. :focus-within as well as :hover — hover alone would make
   it unreachable by keyboard (§ 14.4 check 4).

   The glyph is --icon-size like every other icon in the product; the 24px box
   around it is the target-size floor. */
.ck-input-wrap{position:relative}
/* One rule, not two. The geometry and the reveal used to be declared
   separately, which left width/height/radius stated twice and the second copy
   silently winning. */
.ck-input-clear{padding:0;
  position:absolute;inset-inline-end:8px;inset-block:0;margin-block:auto;
  width:24px;height:24px;border:0;background:0;border-radius:var(--radius-sm);
  color:var(--muted-foreground);cursor:pointer;
  /* NOT DISPLAYED until there is something to clear. It used to sit at
     opacity:0 with pointer-events:none, which stops the mouse and does
     NOTHING to the keyboard: the button kept tabIndex 0, so tabbing through
     an empty field landed on an invisible control that would not have done
     anything anyway. Check 74 — a focusable control at opacity:0. Measured on
     the recon folder listing: two of them.

     display:none is the kit's own answer, not a new one — .ck-search-clear
     has always been display:none until .has-value, and was the only one of
     the three clears that got this right. */
  display:none;place-items:center;
  transition:opacity var(--duration-base)}
.ck-input-clear svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-input-clear:hover{background:var(--hover-bg);color:var(--foreground)}
.ck-input-clear:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* A SECOND CLEAR. type="search" gives WebKit its own cancel button, which
   lands beside the kit's clear — two crosses in one field, only one of them
   wired to anything. The kit's clear is the one that exists in every theme
   and every browser, so the native one goes. */
.ck-input[type="search"]::-webkit-search-cancel-button,
.ck-input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;appearance:none;display:none}
/* On a textarea the field is tall, so the clear anchors to the top-right
   corner instead of the vertical centre. */
/* A text area's cross sits at its top, beside the first line. The base cross is
   centred with inset-block:0 and margin-block:auto, so moving only `top` left
   the bottom inset and the auto margins in place and it still centred. */
.ck-input-wrap.is-textarea > .ck-input-clear{inset-block:var(--space-sm) auto;margin-block:0;transform:none}
/* Present once there is a value — and only then does it enter the tab order.
   Visible once the field is hovered or in edit mode. */
.ck-input-wrap.has-value > .ck-input-clear{
  display:grid;opacity:0;pointer-events:none}
.ck-input-wrap.has-value:hover > .ck-input-clear,
.ck-input-wrap.has-value:focus-within > .ck-input-clear,
.ck-input-wrap.has-value[data-force~="hover"] > .ck-input-clear{
  opacity:1;pointer-events:auto}
/* ── THE VALUE STOPS BEFORE THE CLEAR ─────────────────────────────────────
   A control drawn INSIDE a field does not push the text out of the way: the
   input's trailing padding stays where it was, so the text box runs on
   underneath the button and a long enough value collides with it. On Profile
   Settings the final "h" of "Branch" sat on top of the cross.

   .ck-search already solved this — it goes to 60px of trailing padding once
   there is a value — and the fix here is the same idea with this field's own
   arithmetic: the clear sits 8px from the edge and is 24px wide, so the text
   must stop 8px before it starts. 8 + 24 + 8 = 40.

   It is bound to .has-value, not declared flat, because an empty field has
   nothing to clear and no reason to carry a 40px gutter. */
.ck-input-wrap.has-value > :is(.ck-input,.ck-textarea){
  padding-inline-end:calc(8px + 24px + var(--space-sm))}
/* The textarea's clear is pinned top-right rather than centred, but the lane
   it occupies is the same, so the reservation is the same. */
.ck-input-wrap.is-textarea.has-value > .ck-textarea{
  padding-inline-end:calc(8px + 24px + var(--space-sm))}
/* ── MORE THAN ONE CONTROL IN A FIELD — .ck-input-tools ──────────────────
   A review field can hold a clear, an AI-feedback button and a source picker
   at once. .ck-input-clear owns the one trailing slot, so a second control
   used to be absolutely placed by hand at right:28px, right:30px, right:52px —
   one magic number per combination. The tools go in one trailing run instead:

     .ck-input-wrap > .ck-input + .ck-input-tools > .ck-icon-btn.size-xs …

   The field reserves room for however many there are — 8 + n × 24 + 8, the
   same arithmetic as the clear — counted with :has(), so nothing is set by
   hand. .is-reveal tools appear on hover and focus-within, the way the clear
   does; one that is on (.is-on, aria-expanded) stays. On a textarea the run
   pins to the top edge. */
.ck-input-tools{
  position:absolute;inset-inline-end:var(--space-xs);inset-block:0;margin-block:auto;
  height:24px;display:flex;align-items:center}
.ck-input-wrap.is-textarea > .ck-input-tools{inset-block:var(--space-xs) auto;margin-block:0}
.ck-input-wrap:has(> .ck-input-tools) > :is(.ck-input,.ck-textarea){
  padding-inline-end:calc(var(--space-xs) + var(--ck-input-tools,1) * 24px + var(--space-sm))}
.ck-input-wrap:has(> .ck-input-tools > :nth-child(2)){--ck-input-tools:2}
.ck-input-wrap:has(> .ck-input-tools > :nth-child(3)){--ck-input-tools:3}
.ck-input-tools > .is-reveal{opacity:0;transition:opacity var(--duration-base)}
.ck-input-wrap:is(:hover,:focus-within,[data-force~="hover"]) > .ck-input-tools > .is-reveal,
.ck-input-tools > .is-reveal:is(.is-on,[aria-pressed="true"],[aria-expanded="true"]){opacity:1}
/* A picker's chevron turns up while its list is open (§ 14.9). */
.ck-input-tools > .ck-icon-btn[aria-expanded="true"] > svg{transform:rotate(180deg)}

/* ─── SWITCH (Atom) ─── */
/* ─── SWITCH (Atom) ────────────────────────────────────────────────────────
   shadcn component name: Switch · Clipper legacy name: Switcher.

   A Switch commits a binary choice the moment it is flipped — there is no Save
   step. Use it for settings that take effect immediately; for a mode that a
   button holds, use .ck-toggle.

   CANONICAL CLASS IS .switch. State is expressed the shadcn way with
   data-state="checked" / "unchecked", which is what the React kit's
   switch.tsx keys on (data-[state=checked]) — so the two halves of the kit now
   agree. The legacy .ck-switch class, the .on flag and aria-checked are all
   still honoured, so shipped flows keep rendering unchanged.

   role="switch" + aria-checked are still REQUIRED for accessibility:
   data-state drives the paint, aria-checked drives the announcement, and a
   <button> with neither announces no state at all.

   GEOMETRY is --switch-w / --switch-h / --switch-thumb / --switch-inset, and
   thumb travel is DERIVED from them rather than hard-coded, so a size variant
   is a token change and never a new rule.

   The track IS the affordance — there is no label inside it — so it must clear
   3:1 against the page ground unchecked as well as checked (WCAG 1.4.11).
   That is why --input was corrected; the unchecked track reads from it.

   data-force~="hover|active|focus|disabled" paints a state without the user
   being in it. It exists so a spec sheet can render the full states matrix
   from the kit's OWN rules instead of re-declaring them in page CSS, which
   § 12.1 warns goes stale. Documentation only — never in product markup.

   Spec: references/components/switcher.md */
.switch,.ck-switch{
  --switch-w:40px; --switch-h:24px; --switch-thumb:16px; --switch-inset:4px;
  width:var(--switch-w);height:var(--switch-h);flex-shrink:0;box-sizing:border-box;
  border-radius:var(--radius-full);border:0;padding:0;
  background:var(--toggle-track);/* pairing-exempt: track and thumb carry no text */
  position:relative;cursor:pointer;
  transition:background var(--duration-moderate)
}
.switch::after,.ck-switch::after{
  content:'';position:absolute;
  top:var(--switch-inset);left:var(--switch-inset);
  width:var(--switch-thumb);height:var(--switch-thumb);
  border-radius:var(--radius-full);
  background:var(--background);/* pairing-exempt: track and thumb carry no text */
  transition:transform var(--duration-moderate)
}

/* Checked — data-state first, then the two legacy forms. */
:is(.switch,.ck-switch):is([data-state="checked"],[aria-checked="true"],.on){
  background:var(--primary)/* pairing-exempt: track and thumb carry no text */}
:is(.switch,.ck-switch):is([data-state="checked"],[aria-checked="true"],.on)::after{
  transform:translateX(calc(var(--switch-w) - var(--switch-thumb) - (2 * var(--switch-inset))))
}

/* Sizes. Travel derives from the three metrics, so a size needs no separate
   translate value and cannot fall out of sync with its geometry. */
/* Every size keeps the SAME 4px inset, so a small switch does not look
   tighter than a large one — only smaller. A 20px knob in a 24px track left
   2px of track showing and read as a disc jammed into a slot; at 16px the
   knob is plainly a knob sitting in a groove. */
:is(.switch,.ck-switch).size-sm{--switch-w:32px;--switch-h:20px;--switch-thumb:12px;--switch-inset:4px}
/* 20px tall is under the 24px target floor, so the small switch grows its hit
   area without moving the track. ::after is the thumb; ::before is free. */
:is(.switch,.ck-switch).size-sm::before{content:'';position:absolute;inset:-2px 0}
:is(.switch,.ck-switch).size-lg{--switch-w:48px;--switch-h:28px;--switch-thumb:20px;--switch-inset:4px}

/* Hover */
:is(.switch,.ck-switch):is(:hover,[data-force~="hover"]){
  background:var(--toggle-track-hover)/* pairing-exempt: track and thumb carry no text */}
:is(.switch,.ck-switch):is([data-state="checked"],[aria-checked="true"],.on):is(:hover,[data-force~="hover"]){
  background:var(--primary-hover)/* pairing-exempt: track and thumb carry no text */}

/* Pressed */
:is(.switch,.ck-switch):is(:active,[data-force~="active"]){
  background:var(--toggle-track-hover)/* pairing-exempt: track and thumb carry no text */}
:is(.switch,.ck-switch):is([data-state="checked"],[aria-checked="true"],.on):is(:active,[data-force~="active"]){
  background:var(--primary-active)/* pairing-exempt: track and thumb carry no text */}

/* Disabled — a FADED version of whatever the switch is actually showing, which
   is the v2.0 treatment and the one the product's users already know. The
   track keeps its real colour (--toggle-track off, --primary on) and the knob
   keeps its white, and the whole control drops to --disabled-opacity.

   This is the kit's one opacity-based disabled state, and it is a declared
   exception rather than a lapse. The no-opacity rule exists because opacity
   dims a control's LABEL along with its box, so the text reads as a rendering
   fault instead of an unavailable action. A switch has no label and no glyph:
   it is a track and a circle. There is nothing for opacity to damage, and
   fading is exactly the message — this control still says what it says, you
   just cannot change it.

   The alternative was re-colouring: --muted track with a --muted-foreground
   knob. It measured better (4.41:1) but it changed what the control looked
   like rather than dimming it, so a disabled OFF and a disabled ON stopped
   being recognisable as the same component in two states.

   Position still carries on/off either way, and inactive controls are exempt
   from 1.4.11, so the low track-vs-page contrast here is allowed. */
:is(.switch,.ck-switch):is(:disabled,.is-disabled,[aria-disabled="true"],[data-force~="disabled"]){
  opacity:var(--disabled-opacity);cursor:not-allowed}
:is(.switch,.ck-switch):is(:disabled,.is-disabled,[aria-disabled="true"]){pointer-events:none}

/* Focus */
:is(.switch,.ck-switch):is(:focus-visible,[data-force~="focus"]){outline:0;box-shadow:var(--focus-ring)}
/* A 3-column 1fr grid pushed each toggle to the far right of its column,
   landing it nearer the NEXT label than its own. Gestalt: the gap inside a
   pair must be smaller than the gap between pairs. So the row is capped at a
   readable width and the column gap is widened well past it. */
.ck-switch-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,240px));gap:var(--space-base) 56px;justify-content:start}
@media (max-width:760px){.ck-switch-grid{grid-template-columns:minmax(0,1fr);gap:var(--space-2xs) 0}}
/* space-between pushed the toggle to the far edge: 203px from its own label,
   24px from the next one. The label now has a bounded min-width so toggles
   still line up, but the switch sits right after it. */
.ck-switch-row{display:flex;align-items:center;justify-content:flex-start;gap:var(--space-md);max-width:240px;padding:7px 0}
.ck-switch-row > span{min-width:112px;flex:none}
.ck-switch-row > span{font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground)}

/* ─── DATA TABLE (Organism) ─── */
/* ─── TABLE (Molecule) ─────────────────────────────────────────────────────
   Sizes are row density, which is what a table's size actually means.
   .is-sticky pins the header when the table scrolls inside a container.

   The --muted header band with --foreground text is a declared pairing
   exemption: 15.87:1 where the nominal --muted-foreground partner is 3.16:1.

   Spec: references/components/table.md */
.ck-table{
  --table-row-height:40px;
  /* The widest a cell's text runs before it truncates in the middle. */
  --table-cell-max:240px;
  width:100%;border-collapse:collapse;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  background:var(--card);color:var(--card-foreground);
  border-radius:var(--radius-2xl);overflow:hidden}
.ck-table th{/* pairing-exempt: --muted is a header band, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */
  text-align:start;padding:10px 14px;background:var(--muted);
  font:var(--font-weight-medium) var(--text-sm) var(--font-sans);color:var(--foreground);white-space:nowrap;
  height:var(--table-header-height)}
/* Row height is EXPLICIT, not emergent from font metrics. A data row and a
   loading row have different content — text versus a 12px bar — so left to
   padding alone they land on different heights and the table jumps when the
   real rows arrive (§ 14.10 check 17). One property drives both. */
/* The separator is DOTTED, and it reads --panel-border rather than --border.
   A dotted rule shows less ink than a solid one of the same colour, so at
   --border (#f5f5f5, 1.09:1) it disappeared entirely; --panel-border is the
   token whose job is a visible stroke. */
/* box-sizing is what makes --table-row-height MEAN the row height. Without it
   `height` sizes the CONTENT box and the padding is added outside, so the
   documented 40px row rendered at 40 + 22 = 62px, size-sm's 32px at 46px, and
   the value in table.md — "row height is set, not emergent" — was not the
   value on screen. The token is now the row. */
.ck-table :is(th,td){box-sizing:border-box}
/* THE ROW HEIGHT IS FIXED, AND A CELL HOLDS IT EXACTLY. The cell carries no
   block padding and centres what it holds, so a 24px badge or a 28px button
   sits inside a 32px size-sm row instead of pushing it to 42. With 7px of
   padding a size-sm row had 18px for content, which is less than every
   control the kit puts in a table — so no two rows with different contents
   came out the same height. A cell is one line: text does not wrap. A long
   value runs to --table-cell-max and is then cut in the middle by
   .ck-mid-trunc, with the whole of it in the tooltip. */
.ck-table td{height:var(--table-row-height);padding:0 14px;vertical-align:middle;white-space:nowrap;
  border-top:1px dotted var(--panel-border);color:var(--card-foreground)}
.ck-table tbody tr:hover td{background:var(--hover-bg)}
/* A row that only holds an empty state is not a row you can choose (check 59). */
.ck-table tbody tr:has(> td > .ck-empty):hover td{background:transparent}
.ck-table tbody tr[aria-selected="true"] td,.ck-table tbody tr.is-selected td{background:var(--selected-bg);color:var(--selected-fg)}

/* ── Errored row ──
   The soft-semantic triplet at rest, and hover deepens to the destructive
   HOVER tint rather than the neutral --hover-bg. A row that is a problem must
   not stop looking like one the moment the cursor lands on it. */
.ck-table tbody tr.is-error td{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);
  border-top-color:var(--destructive-border)}
/* Hover deepens the STROKE, not the fill, and that is forced rather than
   chosen. § 8's derivation rule says a hover fill must still clear 4.5:1
   against its family foreground — and on a soft tint there is no headroom:
   the rest state is already 4.58:1 in light, so any deepening drops under AA,
   and reusing --destructive-border as a fill measures 3.57:1 in light and
   1.00:1 in high contrast, where the text would vanish outright. So the
   hover "shifts the other way": the rules go solid --destructive and a 2px
   rail lands on the leading edge. Text contrast is untouched in all three
   themes, and the row still answers the cursor destructively instead of
   reverting to the neutral --hover-bg. */
.ck-table tbody tr.is-error:hover td,
.ck-table tbody tr.is-error[data-force~="hover"] td{
  border-top-color:var(--destructive)}
.ck-table tbody tr.is-error:hover td:first-child,
.ck-table tbody tr.is-error[data-force~="hover"] td:first-child{
  box-shadow:inset 2px 0 0 var(--destructive)}

/* ── Sortable column header ──
   Three states, cycled in one order: unsorted -> ascending -> descending ->
   unsorted. aria-sort is the state, so a screen reader is told the same thing
   the glyph shows.

   THE GLYPH IS REVEALED ON HOVER WHILE UNSORTED, AND RESIDENT ONCE SORTED. A
   header row of permanent arrows is noise, but a column that IS sorted has to
   say so whether the cursor is there or not — the indicator is the only record
   of why the rows are in this order.

   THE TOOLTIP NAMES THE NEXT ACTION, NOT THE CURRENT STATE: "Sort A to Z",
   then "Sort Z to A", then "Clear sorting". A label that describes what you
   are already looking at tells you nothing you cannot see. ckTableSort() in
   clipper-kit.js keeps the label and aria-label in step with aria-sort.

   ONE GLYPH, THREE READINGS. It is always the same chevron pair — an up
   chevron above a down chevron — and the STATE IS WHICH HALF IS LIT:

     unsorted     both halves --muted-foreground   "either direction is available"
     ascending    up lit --primary, down --input    "sorted up; down is available"
     descending   down lit --primary, up --input    "sorted down; up is available"

   Swapping the glyph for a different one per state was wrong twice over: the
   mark moved under the cursor between states, and the lit half is what says
   which direction you are IN while the faded half says which you can go to.
   The fade is a token change, never opacity — a half-transparent stroke over
   a --muted header band is a different colour, not a dimmer one. */
.ck-table th.is-sortable{
  cursor:pointer;user-select:none;
  padding-inline-end:calc(14px + var(--icon-size) + var(--space-xs))}
.ck-table th.is-sortable:is(:hover,[data-force~="hover"]){background:var(--hover-bg)/* pairing-exempt: re-asserts the surface only; the base th rule owns the colour */}
.ck-table th.is-sortable:focus-visible{outline:0;box-shadow:inset 0 0 0 2px var(--primary)}
.ck-table th.is-sortable > .ck-th{display:inline-flex;align-items:center;gap:var(--space-xs);position:relative}
/* THE SORT CONTROL IS ALWAYS VISIBLE. A sortable column says so at rest — a
   header whose sort appears only on hover hides the fact that the column can
   be sorted, and a keyboard or touch user never finds it. Unsorted it rests
   in --muted-foreground; sorted, the lit half is --primary (below). Only the
   column's DELETE control is revealed on approach (.ck-th-delete). */
.ck-th-sort{
  width:var(--icon-size);height:var(--icon-size);flex:none;display:grid;place-items:center;
  color:var(--muted-foreground)}
.ck-th-sort svg{width:var(--icon-size);height:var(--icon-size);display:block}


/* ── A DELETE CONTROL ON EVERY COLUMN — .ck-th-delete ─────────────────────
   Revealed on approach — hover, and focus-within so a keyboard reaches it too.
   The sort control beside it does NOT hide: it is always visible. It removes the COLUMN, not the data, which is
   why it is destructive in tone but not in weight — a filled red button on
   every header would make a table look like a minefield.

   It sits inboard of the resize handle, so dragging the edge never lands on
   it by mistake. */
.ck-th-delete{
  position:absolute;inset-inline-end:10px;top:50%;transform:translateY(-50%);
  width:var(--icon-box);height:var(--icon-box);flex:none;
  display:grid;place-items:center;padding:0;border:0;
  border-radius:var(--radius-sm);
  background:transparent;color:var(--icon-color-destructive);cursor:pointer;
  opacity:0;pointer-events:none;
  transition:opacity var(--duration-base),background var(--duration-base),color var(--duration-base)}
.ck-th-delete svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-th-delete:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-th-delete:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground)}
.ck-th-delete:focus-visible{outline:0;box-shadow:var(--focus-ring-error);border-radius:var(--radius-xs)}
.ck-table th:is(:hover,:focus-within,[data-force~="hover"]) .ck-th-delete{opacity:1;pointer-events:auto}

/* ── RESIZABLE COLUMNS — .ck-th-resize ────────────────────────────────────
   A 6px grab strip on the trailing edge of the header, drawn as a 1px rule so
   the target is bigger than the thing you can see. The column's own min-width
   comes from the shortest data it holds and is set per column with
   --ck-col-min; the ceiling is 400px for every column, because past that a
   line of text is harder to scan, not easier.

   The table keeps table-layout:fixed while resizing so the other columns do
   not redistribute under the pointer — a drag that moves two edges at once is
   impossible to aim. */
.ck-table th{position:relative}
.ck-th-resize{
  position:absolute;inset-block:0;inset-inline-end:-3px;width:6px;
  cursor:col-resize;background:0;border:0;padding:0;
  touch-action:none;user-select:none;z-index:1}
.ck-th-resize::before{
  content:'';position:absolute;inset-block:4px;inset-inline-start:2px;width:1px;
  background:var(--input);opacity:0;transition:opacity var(--duration-base)}
.ck-table th:is(:hover,[data-force~="hover"]) .ck-th-resize::before,
.ck-th-resize:focus-visible::before{opacity:1}
.ck-th-resize:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* While a drag is live the whole table shows the resize cursor, so the pointer
   does not flicker between cells as it crosses them. */
.ck-table.is-resizing{cursor:col-resize;user-select:none}
.ck-table.is-resizing .ck-th-resize::before{opacity:1;background:var(--primary)/* pairing-exempt: a 1px drag indicator, carries no text */}
/* Once a column has been sized by hand the cells must be able to give way, or
   `white-space: nowrap` holds the column open at its own text width and the
   declared minimum can never be reached — the handle appears to stop early for
   no visible reason. .is-sized is added the moment a width is set. */
.ck-table.is-sized th,.ck-table.is-sized td{overflow:hidden;text-overflow:clip}
.ck-table.is-sized td{white-space:nowrap}
/* Sorted: resident, and in --primary so the active column is unmistakable. */
.ck-table th[aria-sort="ascending"] .ck-th-sort,
.ck-table th[aria-sort="descending"] .ck-th-sort{color:var(--primary)}
/* Unsorted shows BOTH halves at equal weight: neither direction is in force,
   and both are one click away. */
.ck-table th[aria-sort="none"] .ck-th-sort,
.ck-table th.is-sortable:not([aria-sort]) .ck-th-sort{color:var(--muted-foreground)}
/* Which half is lit. The whole glyph takes the state colour and the INACTIVE
   half is overridden back to --input, so there is one place per state to
   change and the two halves can never both claim to be active. */
.ck-table th[aria-sort="ascending"] .ck-th-sort .ck-sort-down,
.ck-table th[aria-sort="descending"] .ck-th-sort .ck-sort-up{stroke:var(--input)}

/* ── Row actions ──
   Revealed on row hover and on focus-within, like every other hover-revealed
   control in the kit (§ 14.3 check 31). The cell keeps its width whether the
   actions show or not, so a row never reflows under the cursor. */
.ck-table-actions{
  display:inline-flex;align-items:center;gap:var(--space-2xs);
  opacity:0;pointer-events:none;transition:opacity var(--duration-base)}
.ck-table tbody tr:hover .ck-table-actions,
.ck-table tbody tr:focus-within .ck-table-actions,
.ck-table tbody tr[data-force~="hover"] .ck-table-actions{opacity:1;pointer-events:auto}
.ck-table td.ck-cell-actions{width:1%;white-space:nowrap;text-align:end}

/* ── WHY A STICKY CELL NEEDS THIS FIRST ───────────────────────────────────
   .ck-table carries `overflow:hidden` to clip its own rounded corners, and
   that one property makes EVERY sticky cell in the kit inert. An element with
   a clipping overflow is itself a scroll container, so a sticky cell inside
   the table sticks to the TABLE — which never scrolls — instead of to the
   wrapper that does.

   That is not theoretical and it is not new. Measured against this file
   before the rule below existed: .ck-table.is-sticky's header sat 199px above
   its scrollport after a 200px scroll, and .ck-table.is-matrix's pinned first
   column sat 299px to the left of its scrollport after a 300px scroll. Both
   had scrolled clean away. Two documented features, one cause.

   The clipping is not lost: .ck-table-panel already carries the same radius
   and the same overflow:hidden, so a table inside the wrapper the kit tells
   you to use is clipped by the wrapper. Only the tables that ask for a sticky
   cell opt out here, so a plain table is untouched. */
.ck-table.is-sticky,
.ck-table.is-matrix,
.ck-table:has(:is(th,td).ck-cell-actions.is-pinned){overflow:visible}

/* ── A PINNED ACTION COLUMN — .ck-cell-actions.is-pinned ──────────────────
   A wide table scrolls sideways and carries the row's actions off screen with
   it. Pinning the cell keeps them reachable at every scroll position, which
   is the whole of what this variant does.

   The run of controls inside is a .ck-action-bar.is-cell, not a new object. A
   row's actions are the same component whether they sit in a cell, a document
   toolbar or a floating selection bar, and the bar already carries the
   spacing, the grouping and the divider rules for a run of icon buttons.

   EVERY ROW STATE RESTATES ITS SURFACE HERE, and that is not duplication. A
   sticky cell is composited over whatever scrolls behind it, so a transparent
   one shows the passing columns straight through the icons. Each surface
   below is the same declared pair the row itself uses, re-asserted on a cell
   that has no choice but to be opaque.

   The leading hairline is a rule rather than a shadow because it has to read
   at scroll position 0 as well, where a shadow falling on nothing looks like
   a stray line under the table.

   The cell is --card, the table's own surface. It was --background, which is
   the same white in light but a shade darker than the table in dark, so the
   pinned column read as a separate strip.

   Spec: references/components/table.md § Pinned actions */
.ck-table :is(th,td).ck-cell-actions.is-pinned{
  position:sticky;inset-inline-end:0;z-index:var(--z-base);
  background:var(--card);color:var(--card-foreground)}
.ck-table :is(th,td).ck-cell-actions.is-pinned::before{
  content:'';position:absolute;inset-block:0;inset-inline-start:0;width:1px;
  background:var(--panel-border)/* pairing-exempt: a 1px rule, carries no text */}
/* The header keeps its own band, and outranks the body cells so a row
   scrolling beneath a pinned header never rides over it. */
.ck-table thead th.ck-cell-actions.is-pinned{
  background:var(--muted)/* pairing-exempt: --muted is a header band, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 — the same pairing .ck-table th already carries */;
  color:var(--foreground);z-index:calc(var(--z-sticky) + 1)}
.ck-table tbody tr:is(:hover,[data-force~="hover"]) td.ck-cell-actions.is-pinned{
  background:var(--hover-bg)/* pairing-exempt: re-asserts the surface only; the base cell rule owns the colour */}
.ck-table tbody tr:is([aria-selected="true"],.is-selected) td.ck-cell-actions.is-pinned{
  background:var(--selected-bg);color:var(--selected-fg)}
/* The errored row's fill carries through the pinned cell, and so does its
   hover — which deepens the STROKE, not the fill. See the note on .is-error
   above for why that direction is forced rather than chosen. */
.ck-table tbody tr.is-error td.ck-cell-actions.is-pinned{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);
  border-top-color:var(--destructive-border)}
.ck-table tbody tr.is-error:is(:hover,[data-force~="hover"]) td.ck-cell-actions.is-pinned{
  border-top-color:var(--destructive)}
/* ── A PINNED LEADING COLUMN — .ck-cell-pin-start ─────────────────────────
   The mirror of the pinned actions: a wide table keeps the row's NAME (File
   Name, the field a matrix row is about) on screen while the values scroll
   under it. Same surfaces per row state, same hairline, on the trailing edge
   this time. Below 720px it lets go: a pinned 240px name in a 360px scroller
   would leave no room for the values it is meant to label. */
.ck-table:has(:is(th,td).ck-cell-pin-start){overflow:visible}
.ck-table :is(th,td).ck-cell-pin-start{
  position:sticky;inset-inline-start:0;z-index:var(--z-base);
  background:var(--card);color:var(--card-foreground)}
.ck-table :is(th,td).ck-cell-pin-start::after{
  content:'';position:absolute;inset-block:0;inset-inline-end:0;width:1px;
  background:var(--panel-border)/* pairing-exempt: a 1px rule, carries no text */}
.ck-table thead th.ck-cell-pin-start{
  background:var(--muted)/* pairing-exempt: the header band, the same declared pair .ck-table th carries */;
  color:var(--foreground);z-index:calc(var(--z-sticky) + 1)}
.ck-table tbody tr:is(:hover,[data-force~="hover"]) td.ck-cell-pin-start{
  background:var(--hover-bg)/* pairing-exempt: re-asserts the surface only; the base cell rule owns the colour */}
.ck-table tbody tr:is([aria-selected="true"],.is-selected) td.ck-cell-pin-start{
  background:var(--selected-bg);color:var(--selected-fg)}
.ck-table tbody tr.is-error td.ck-cell-pin-start{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
@media (max-width:720px){.ck-table :is(th,td).ck-cell-pin-start{position:static}}

/* ─── TABLE CELL DATA TYPES — .ck-table.is-data ────────────────────────────
   The default table holds text. This variant is the same table carrying every
   kind of value the product actually shows, one per column, so a screen can be
   assembled from named patterns instead of hand-rolled cells.

   Nothing here restyles a component. A badge in a cell is a badge, a dropdown
   is a dropdown, a switch is a switch. These classes only handle the two
   things a CELL owns: how the value is aligned, and how it behaves when it is
   longer than its column.

   It is wide by design — put it in a .ck-table-panel > .ck-table-scroll with
   .is-sticky on the table, and it scrolls with its header pinned.

   Spec: references/components/table.md § Data types */
.ck-table.is-data td{vertical-align:middle}

/* Flag — a tone rail on the leading edge of the row. It is the row's status at
   a glance, and it is never the only carrier of that status: a flagged row
   also says why in words somewhere.

   IT IS FLUSH TO THE WALL AND FULL HEIGHT. A 4px cell with no padding, and the
   rail absolutely positioned across the whole block axis — so it butts the
   table's left edge and runs the row's entire height whatever that row's
   density is. It was a floating 20px capsule inset by 10px, which read as a
   dot in a margin rather than as the row's own edge.

   Square ends, not --radius-xs: a rail that meets the wall and both
   separators has no ends to round, and rounding them reopens the gap the
   flush edge exists to close. .ck-table's overflow:hidden clips it to the
   panel's corner radius on the first and last row, which is what should
   happen.

   The dotted separator is dropped on this cell only, so the rule does not cut
   across the rail — the rails then read as a continuous edge segmented by
   colour, which is the point of them. */
.ck-table.is-data td.ck-cell-flag{
  position:relative;width:4px;min-width:4px;padding:0;border-top:0}
.ck-flag{
  position:absolute;inset-block:0;inset-inline-start:0;width:4px;
  background:var(--input)/* pairing-exempt: a rail, carries no text */}
.ck-flag.is-success{background:var(--success)/* pairing-exempt: carries no text */}
.ck-flag.is-warning{background:var(--warning)/* pairing-exempt: carries no text */}
.ck-flag.is-error{background:var(--destructive)/* pairing-exempt: carries no text */}
.ck-flag.is-info{background:var(--info)/* pairing-exempt: carries no text */}

/* Expander chevron. Same clockwise-both-ways rotation as the accordion and the
   dropdown, from the same keyframes — a disclosure is a disclosure. */
.ck-table.is-data td.ck-cell-chev{width:1%;padding-inline-end:0}
/* A trailing chevron — the cell is the row's last — sits on the row's end
   edge: the end inset is the cell's own, the start inset goes. */
.ck-table.is-data td.ck-cell-chev:last-child{padding-inline:0 14px}
.ck-table.is-data td.ck-cell-chev:last-child > .ck-row-chev{margin-inline-start:auto}
.ck-row-chev{
  width:var(--icon-size);height:var(--icon-size);display:grid;place-items:center;
  border:0;background:0;padding:0;cursor:pointer;
  color:var(--muted-foreground);transform:rotate(0deg)}
.ck-row-chev svg{width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-row-chev:focus-visible{outline:0;box-shadow:var(--focus-ring);border-radius:var(--radius-xs)}
tr[aria-expanded="true"] > td > .ck-row-chev{
  transform:rotate(180deg);
  transition:transform var(--duration-chevron) var(--ease-default)}
tr.is-collapsing > td > .ck-row-chev{
  animation:ck-chev-close var(--duration-chevron) var(--ease-default) forwards}

/* Name with avatar */
.ck-cell-name{display:inline-flex;align-items:center;gap:var(--space-sm);min-width:0}
.ck-cell-name > span:last-child{overflow:hidden;text-overflow:clip;white-space:nowrap}

/* Email — a real link, so it takes the link token and the link underline. */
.ck-cell-email{
  color:var(--link);text-decoration:underline;text-underline-offset:2px;
  overflow:hidden;text-overflow:clip;white-space:nowrap;display:block;max-width:220px}
/* No --link-hover exists, and none should be minted: the link family signals
   hover by THICKENING THE UNDERLINE, which is exactly what .ck-btn.link does.
   A labelled link and an email in a cell must not answer the cursor
   differently. */
.ck-cell-email{text-decoration-thickness:1px}
.ck-cell-email:is(:hover,[data-force~="hover"]){color:var(--link);text-decoration-thickness:2px}
.ck-cell-email:focus-visible{outline:0;box-shadow:var(--focus-ring);border-radius:var(--radius-xs)}

/* RECORD NAME — .ck-cell-record
   The cell whose text opens the row's own record: a filename, a document
   number, an invoice reference. It is clickable, so it is an <a>, but it is
   not a link AWAY — it is the row's identity, and the row is already the
   subject of the page. .ck-cell-email was standing in for it, which painted
   every filename column in --link blue and made a table of records read as a
   table of outbound links.

   So: --foreground and semibold carry it, not the link colour, and it rests
   WITHOUT an underline — a column of underlined names read as a column of
   links, which is the thing this cell exists not to be. The underline
   arrives on hover and focus, the cursor's answer that the text is the
   control. Same ellipsis and same 220px cap as .ck-cell-email, so the two
   columns truncate alike. */
.ck-cell-record{
  color:var(--foreground);font-weight:var(--font-weight-semibold);
  text-decoration:none;text-underline-offset:2px;text-decoration-thickness:1px;
  overflow:hidden;text-overflow:clip;white-space:nowrap;display:block;max-width:220px;
  line-height:24px/* the cell's only link, so a 24px target; fits the 40px row */}
.ck-cell-record:is(:hover,:focus-visible,[data-force~="hover"]){color:var(--foreground);text-decoration:underline;text-decoration-thickness:1px}
.ck-cell-record:focus-visible{outline:0;box-shadow:var(--focus-ring);border-radius:var(--radius-xs)}

/* Numeric, amount, date, counter — all right-aligned with tabular figures, so
   a column of them lines up on the decimal and stepping 9 -> 10 shifts
   nothing. An amount additionally carries its currency in the header, never
   repeated per row. */
.ck-table.is-data td.ck-cell-num,
.ck-table.is-data td.ck-cell-amount,
.ck-table.is-data th.ck-cell-num,
.ck-table.is-data th.ck-cell-amount{text-align:end;font-variant-numeric:tabular-nums}
.ck-table.is-data td.ck-cell-amount{font-weight:var(--font-weight-semibold)}
.ck-table.is-data td.ck-cell-date{white-space:nowrap;font-variant-numeric:tabular-nums}
.ck-table.is-data td.ck-cell-counter{text-align:center;width:1%}
/* The column that takes the rest of the row — .ck-cell-fill. Every other
   column shrinks to its content, so a run of check and figure columns packs
   against the trailing edge with one cell padding between them. */
.ck-table.is-data :is(td,th).ck-cell-fill{width:100%}

/* Long text, two behaviours and they are NOT interchangeable.
   .ck-cell-multi WRAPS — for a value the user has to read in full, capped at
   three lines so one long note cannot make the row twice the height of its
   neighbours.
   .ck-cell-trunc TRUNCATES and hands the full text to a tooltip — for a value
   that is scanned, where the column has to stay narrow. Wrap it in
   .ck-tip-host and the tooltip appears on hover AND on keyboard focus. */
.ck-table.is-data td.ck-cell-multi{
  white-space:normal;min-width:220px;max-width:320px;
  display:table-cell}
.ck-cell-multi > span{
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;
  overflow:hidden;line-height:1.45}
.ck-table.is-data td.ck-cell-trunc{max-width:180px}
.ck-cell-trunc .ck-tip-host{max-width:100%}
.ck-cell-trunc .ck-tip-host > span:first-child{
  overflow:hidden;text-overflow:clip;white-space:nowrap;display:block}

/* Editable cell — it reads as text until the row is hovered or the field takes
   focus, then it becomes visibly a field. A grid of permanent input boxes is
   unreadable as data, and a value that never shows it is editable is
   undiscoverable; revealing the stroke on approach is the middle. */
/* The cell editor sizes its input with the ATOM's own size class in the
   markup (.ck-input.size-sm), not by overriding height here — an organism
   places an atom, it does not re-measure it (§ 14 check 40). */
.ck-cell-edit > .ck-input{padding:0 var(--space-xs);
  border-color:transparent;background:transparent}
.ck-table tbody tr:hover .ck-cell-edit > .ck-input,
.ck-table tbody tr[data-force~="hover"] /* The cell editor sizes its input with the ATOM's own size class in the
   markup (.ck-input.size-sm), not by overriding height here — an organism
   places an atom, it does not re-measure it (§ 14 check 40). */
.ck-cell-edit > .ck-input{
  border-color:var(--field-border);background:var(--background);color:var(--foreground)}
.ck-cell-edit > .ck-input:focus{
  border-color:var(--primary);background:var(--background);color:var(--foreground)}
/* A cell editor in a table that lays itself out — .ck-cell-edit.is-fit.
   The input is width:100%, and a percentage width gives an auto-layout table
   nothing to measure, so every column collapsed to its header and the values
   inside were cut to "1…5". Sized to its own value instead, the input sets the
   column, still fills a column that is wider, and stops at --table-cell-max,
   where a long value scrolls inside the field. Found on the DES-667 document
   review, whose extracted tables are built from data and cannot know their
   widths in advance. */
.ck-cell-edit.is-fit > .ck-input{
  field-sizing:content;width:auto;min-width:100%;max-width:var(--table-cell-max)}
/* A number you can edit — .ck-cell-edit.ck-cell-num. The figure lines up with
   the read-only figures above and below it and with its right-aligned header,
   so a threshold column reads as one column whether a row is editable or not. */
.ck-table.is-data :is(td,th).ck-cell-edit.ck-cell-num > .ck-input{text-align:end;font-variant-numeric:tabular-nums}
/* A figure field keeps to a figure's width, and its column shrinks to it, so
   the name column takes the rest of the row. 80px holds "0.00" at every size
   with room for the caret. The same field can sit in the header band as the
   column's bulk setter, beside its label. */
.ck-table.is-data :is(td,th).ck-cell-edit.ck-cell-num{width:1%;white-space:nowrap}
.ck-table.is-data :is(td,th).ck-cell-edit.ck-cell-num > .ck-input{width:80px;vertical-align:middle}
.ck-table.is-data th.ck-cell-edit.ck-cell-num > .ck-input{margin-inline-start:var(--space-sm)}
/* A field the user is expected to fill in every row — .ck-cell-edit.is-boxed.
   It shows its stroke at rest, so a column of thresholds reads as a column of
   inputs rather than text. Focus and invalid keep their own strokes. */
.ck-table .ck-cell-edit.is-boxed > .ck-input:not(:focus):not([aria-invalid="true"]){
  border-color:var(--field-border);background:var(--background);color:var(--foreground)}
/* An invalid cell editor keeps its red stroke at rest. The editor's own
   transparent and hover strokes are more specific than the atom's invalid
   rule, so without this the error showed only while the field had focus. */
.ck-table .ck-cell-edit > .ck-input[aria-invalid="true"]{border-color:var(--destructive)}

/* Progress in a cell — the bar plus its number, because a bar alone is not a
   label (§ 14.3 check 25). */
.ck-cell-progress{display:flex;align-items:center;gap:var(--space-sm);min-width:140px}
.ck-cell-progress > .ck-progress{flex:1}
.ck-cell-progress > span{
  flex:none;font-variant-numeric:tabular-nums;color:var(--muted-foreground);
  font-size:var(--text-sm)}

/* Switch and CTA cells shrink to their content and never set the row height —
   the controls inside are already on the control scale. */
.ck-table.is-data td.ck-cell-switch,
.ck-table.is-data td.ck-cell-cta,
.ck-table.is-data td.ck-cell-bar{width:1%;white-space:nowrap}

/* ── AVATAR (Atom) ──
   Initials on a tinted plate, or an image. Round, on the icon-box scale so it
   lines up with a glyph in the same column. */
.ck-avatar{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  width:var(--icon-box);height:var(--icon-box);border-radius:var(--radius-full);
  background:var(--group);color:var(--group-foreground);
  border:1px solid var(--group-border);
  font:var(--font-weight-semibold) var(--text-2xs) var(--font-sans);
  text-transform:uppercase;overflow:hidden;user-select:none}
.ck-avatar.size-sm{width:20px;height:20px;font-size:var(--text-3xs)}
.ck-avatar.size-lg{width:32px;height:32px;font-size:var(--text-xs)}
.ck-avatar > img{width:100%;height:100%;object-fit:cover;display:block}
/* AN AVATAR THAT OPENS SOMETHING IS A <button class="ck-avatar">. The one that
   closes the header's trailing run opens the account menu, and ClipperHeader
   used to build it as a <span> — so the way out of the product could not be
   tabbed to or pressed, and every flow that needed it working wrapped it in a
   button of its own or restyled one. A button carrying the class needs only
   what a button brings: no padding, a pointer. It answers the cursor on its
   edge, the way a field does, because a fill change would read as a change of
   identity, not of state. */
button.ck-avatar{padding:0;cursor:pointer;transition:border-color var(--duration-base)}
button.ck-avatar:is(:hover,[data-force~="hover"]){border-color:var(--input-border-hover)}
button.ck-avatar:focus-visible{outline:0;box-shadow:var(--focus-ring)}
button.ck-avatar:disabled{cursor:not-allowed;opacity:var(--disabled-opacity)}
/* A filled plate rather than a tinted one. The header bar wanted this and kept
   its own class to get it; it is a tone on the atom now, so the next organism
   that wants a solid avatar composes one instead of minting a second. */
.ck-avatar.is-primary{
  background:var(--primary);color:var(--primary-foreground);border-color:var(--primary)}
/* A TONED AVATAR takes the pill palette, so a person can carry the colour of
   what they are — a member list paints each avatar in its role's tone, the
   same tint, stroke and foreground as the role chip beside it. Identity, not
   state: no hover or active change comes with it. */
.ck-avatar.teal{background:var(--teal-bg);color:var(--teal-soft-foreground);border-color:var(--teal-border)}
.ck-avatar.cyan{background:var(--cyan-bg);color:var(--cyan-soft-foreground);border-color:var(--cyan-border)}
.ck-avatar.indigo{background:var(--indigo-bg);color:var(--indigo-soft-foreground);border-color:var(--indigo-border)}
.ck-avatar.pink{background:var(--pink-bg);color:var(--pink-soft-foreground);border-color:var(--pink-border)}
.ck-avatar.rose{background:var(--rose-bg);color:var(--rose-soft-foreground);border-color:var(--rose-border)}
.ck-avatar.amber{background:var(--amber-bg);color:var(--amber-soft-foreground);border-color:var(--amber-border)}
.ck-avatar.lime{background:var(--lime-bg);color:var(--lime-soft-foreground);border-color:var(--lime-border)}
.ck-avatar.emerald{background:var(--emerald-bg);color:var(--emerald-soft-foreground);border-color:var(--emerald-border)}
.ck-avatar.violet{background:var(--violet-bg);color:var(--violet-soft-foreground);border-color:var(--violet-border)}
.ck-avatar.fuchsia{background:var(--fuchsia-bg);color:var(--fuchsia-soft-foreground);border-color:var(--fuchsia-border)}
.ck-avatar.sky{background:var(--sky-bg);color:var(--sky-soft-foreground);border-color:var(--sky-border)}
.ck-avatar.slate{background:var(--slate-bg);color:var(--slate-soft-foreground);border-color:var(--slate-border)}
.ck-avatar.gold{background:var(--gold-bg);color:var(--gold-soft-foreground);border-color:var(--gold-border)}
.ck-avatar.crimson{background:var(--crimson-bg);color:var(--crimson-soft-foreground);border-color:var(--crimson-border)}
.ck-avatar.sage{background:var(--sage-bg);color:var(--sage-soft-foreground);border-color:var(--sage-border)}
.ck-avatar.purple{background:var(--purple-bg);color:var(--purple-soft-foreground);border-color:var(--purple-border)}
.ck-avatar.orange{background:var(--orange-bg);color:var(--orange-soft-foreground);border-color:var(--orange-border)}
.ck-avatar.blue{background:var(--blue-bg);color:var(--blue-soft-foreground);border-color:var(--blue-border)}






/* ── AVATAR EDITOR (Molecule) — .ck-avatar-edit ──
   The signed-in user's own photo, with the control that changes it. Distinct
   from .ck-avatar, which identifies someone in a list at 20-32px and is not
   editable; this is the one place a face is shown large enough to judge, so it
   sits off the avatar scale on purpose and that literal is its identity.

   The ring is --card, not --background: it is a cut-out of the surface the
   photo sits on, and on a card those are different colours in dark. */
.ck-avatar-edit{position:relative;width:104px;height:104px;flex:none}
.ck-avatar-edit > img{
  width:100%;height:100%;border-radius:var(--radius-full);object-fit:cover;
  border:4px solid var(--card);display:block;
  background:var(--muted)/* pairing-exempt: an <img> renders no text of its own; this is the tint behind a photo that has not loaded */}
/* No picture yet — .ck-avatar-edit-initial. The first letter of the person's
   or company's name on the same --group plate a list .ck-avatar uses, at the
   editor's size, with the same --card ring as a photo. 40px is the letter's
   size at a 104px plate and, like the 104px, is this component's identity.
   The photo and the initial swap with [hidden], which has to beat the img's
   display:block above. */
.ck-avatar-edit-initial{
  display:grid;place-items:center;width:100%;height:100%;box-sizing:border-box;
  border-radius:var(--radius-full);border:4px solid var(--card);
  background:var(--group);color:var(--group-foreground);
  font:var(--font-weight-semibold) 40px/1 var(--font-sans);text-transform:uppercase;user-select:none}
.ck-avatar-edit > [hidden]{display:none}
/* An organisation's logo — .ck-avatar-edit.is-logo. A logo is not a face: it
   is shown whole on a framed square tile, never cropped to a circle, and it
   is changed by the Replace and Remove buttons beside it, so it carries no
   camera button. */
.ck-avatar-edit.is-logo > img{
  border-radius:var(--radius-xl);object-fit:contain;padding:var(--space-sm);box-sizing:border-box;
  border:1px solid var(--panel-border);background:var(--card)/* pairing-exempt: an <img> renders no text of its own */}
/* 28px — size-sm on the control scale (section 6). It is a real button and has
   to stay hittable without covering the face. */
.ck-avatar-edit-btn{
  position:absolute;right:0;bottom:2px;width:28px;height:28px;padding:0;
  display:grid;place-items:center;border-radius:var(--radius-full);
  background:var(--primary);color:var(--primary-foreground);
  border:2px solid var(--card);cursor:pointer;
  transition:background var(--duration-base)}
.ck-avatar-edit-btn svg{
  width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke)}
.ck-avatar-edit-btn:is(:hover,[data-force~="hover"]){background:var(--primary-hover)}
.ck-avatar-edit-btn:is(:active,[data-force~="active"]){background:var(--primary-active)}
.ck-avatar-edit-btn:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-avatar-edit-btn:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:var(--muted);color:var(--muted-foreground);cursor:not-allowed}



/* ── MATRIX TABLE — .ck-table.is-matrix ──
   Row labels down the left, entities across the top, a mark at each crossing.
   A permission grid, a plan comparison, a feature matrix.

   It differs from a data table in one structural way, and everything here
   follows from it: a matrix grows PAST its container instead of fitting it,
   because a column per entity has no upper bound. So the table is max-content
   inside a scrolling parent, and both the header row and the label column stay
   pinned — a matrix you have scrolled into the middle of, with neither, is a
   grid of ticks belonging to nothing.

   border-collapse goes to `separate`, which is not a style choice: a collapsed
   table drops a sticky cell's borders as it scrolls out. */
.ck-table.is-matrix{
  width:max-content;min-width:100%;
  border-collapse:separate;border-spacing:0}
.ck-table.is-matrix :is(th,td){white-space:nowrap;vertical-align:middle}
/* Row height comes from the table, not from whatever the mark happens to
   be — a matrix row and a data row are the same row. */
.ck-table.is-matrix tbody td{height:var(--table-row-height)}
.ck-table.is-matrix thead th{
  position:sticky;top:0;z-index:var(--z-sticky);
  border-bottom:1px solid var(--panel-border)}
/* The label column: wide enough to read a permission name without wrapping. */
.ck-table.is-matrix :is(th,td):first-child{
  position:sticky;left:0;min-width:230px;
  border-right:1px solid var(--panel-border)}
.ck-table.is-matrix thead th:first-child{z-index:calc(var(--z-sticky) + 1)}
.ck-table.is-matrix tbody td:first-child{
  background:var(--card);color:var(--card-foreground)}
/* Dashed, not dotted: a matrix row is read ACROSS, and a dashed rule carries
   the eye along it where the data table's dotted rule only separates. */
.ck-table.is-matrix tbody td{border-top:0;border-bottom:1px dashed var(--input)}
/* A band naming the group of rows beneath it. Title Case as written (check
   62): the group name is a label, so it is never set in capitals. */
.ck-table.is-matrix tbody tr.is-group td{
  background:var(--accent);color:var(--muted-foreground)/* pairing-exempt: a group band is a label strip, not an accent surface carrying content; --muted-foreground on --accent is the quieter pair by design */;
  font:var(--font-weight-semibold) var(--text-sm) var(--font-sans)}

/* The crossing. It is a real .ck-check — rule 40: an atom used inside an
   organism brings its behaviour with it, so the matrix PLACES a checkbox and
   does not draw one. The retired .ck-matrix-mark was a second checkbox with
   its own tick geometry, which is the drift that rule exists to stop.

   A comparison grid is READ-ONLY: the boxes state what a role has, they are
   not offers. They carry `disabled` so they cannot be clicked — but disabled
   here means "not editable", not "unavailable", so a ticked box keeps
   --primary rather than dropping to --input and greying the grid into
   illegibility. */
.ck-matrix-cell{text-align:center;vertical-align:middle}
/* The entity header sits OVER its column of marks. The marks were already
   centred in their cells, but the headers kept the table's start alignment,
   so every label stood ~50px left of the checkbox beneath it and the grid
   read as misaligned even though no mark had moved. A matrix is read down a
   column — "what can an Admin do" — so the label and its marks share one
   centre line. The first column is the row labels and stays at the start. */
.ck-table.is-matrix thead th:not(:first-child){text-align:center}
.ck-matrix-cell > .ck-check{display:inline-block;vertical-align:middle}
.ck-table.is-matrix .ck-check:disabled{cursor:default;pointer-events:none}
.ck-table.is-matrix .ck-check:disabled:checked{
  background:var(--primary);border-color:var(--primary)/* pairing-exempt: carries no text; read-only, not unavailable */}
.ck-table.is-matrix .ck-check:disabled:not(:checked){
  background:var(--background)/* pairing-exempt: a checkbox carries no text; the border is the affordance, same contract as the .ck-check base */;
  border-color:var(--input)}
/* ── A READ-ONLY MATRIX — .ck-table.is-matrix.is-readonly ──────────────────
   A comparison grid states what each entity HAS; nothing in it can be
   changed there. Even disabled, a checkbox reads as a form control, so a
   reader goes looking for how to edit it. The read-only grid shows a mark
   instead: a tick in the success tone for allowed, a dash in muted ink for
   not allowed. The two differ in SHAPE as well as colour, so the grid still
   reads without colour. Each mark is role="img" with an aria-label
   ("Allowed" / "Not Allowed"), so a screen reader hears a state, not a
   checkbox. No hover, no focus, no pointer: nothing here is interactive.
   An EDITABLE grid keeps the .ck-check above.

     td.ck-matrix-cell > span.ck-matrix-icon.is-yes|.is-no[role=img] > svg
     .ck-matrix-legend > span > .ck-matrix-icon + text      (under the title)
*/
.ck-matrix-icon{
  display:inline-grid;place-items:center;vertical-align:middle;
  width:var(--icon-box);height:var(--icon-box);cursor:default}
.ck-matrix-icon > svg{
  width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
/* The tick is the mark a reader looks for, so it is drawn heavier (stroke 3);
   the dash is the absence of one, so it steps back: the lighter --input ink
   and the 12px small glyph. Shape still tells them apart without colour. */
.ck-matrix-icon.is-yes{color:var(--success-soft-foreground)}
.ck-matrix-icon.is-yes > svg{stroke-width:3}
.ck-matrix-icon.is-no{color:var(--input)}
.ck-matrix-icon.is-no > svg{width:var(--icon-size-sm);height:var(--icon-size-sm)}
/* The row keeps its rest surface under the pointer: a hover fill offers an
   action, and a read-only row has none. */
.ck-table.is-matrix.is-readonly tbody tr:hover td{background:transparent}
.ck-table.is-matrix.is-readonly tbody tr:hover td:first-child{background:var(--card);color:var(--card-foreground)}
.ck-table.is-matrix.is-readonly tbody tr.is-group:hover td{
  background:var(--accent);color:var(--muted-foreground)/* pairing-exempt: the group band's own pair, restated over the hover */}
.ck-table.is-matrix.is-readonly .ck-matrix-cell{cursor:default}
.ck-matrix-legend{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-md);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-matrix-legend > span{display:inline-flex;align-items:center;gap:var(--space-2xs)}

/* ── EDITOR TOOLBAR — .ck-action-bar.is-toolbar ──
   The action bar welded to the top of an editing surface rather than floating
   over content. Same component, three differences: it spans the surface
   instead of hugging its buttons, it wraps rather than scrolls because a
   formatting run has no natural place to cut, and its bottom corners are
   square because the surface continues underneath.

   align-items:center is the fix for the thing that looked wrong: a tool
   carrying a word and a chevron is taller than a bare glyph, and with the row
   stretching they sat on different baselines and the band grew. Every tool is
   28px now — size-sm on the control scale — and the row centres them.

   Separators are .ck-divider.is-vertical, the same atom the base bar uses. */
.ck-action-bar.is-toolbar{
  display:flex;width:100%;flex-wrap:wrap;align-items:center;
  gap:var(--space-2xs);padding:var(--space-xs) var(--space-sm);
  background:var(--muted);color:var(--foreground)/* pairing-exempt: --muted is a toolbar band, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */;
  border:0;border-bottom:1px solid var(--panel-border);border-radius:0}
/* Every tool is one height whatever it carries — a glyph, a word, or a word
   and a chevron. A toolbar whose buttons differ in height has no baseline. */
.ck-action-bar.is-toolbar > :is(.ck-icon-btn,.ck-btn,.ck-toggle){
  height:28px;min-width:28px;width:auto;
  padding:0 var(--space-xs);gap:var(--space-2xs);flex:none}
.ck-action-bar.is-toolbar .ck-divider.is-vertical{
  height:var(--icon-size);align-self:center;margin-inline:var(--space-2xs)}
/* A tool that is switched on is a .ck-toggle and says so with aria-pressed —
   bold and italic are modes, not one-shot actions. */
.ck-action-bar.is-toolbar .ck-toggle[aria-pressed="true"]{
  background:var(--selected-bg);color:var(--selected-fg);border-color:transparent}

/* ── EDITOR — .ck-editor ──
   The surface an .is-toolbar belongs to: a bordered field with a toolbar band
   at the top and an editable area beneath. Minted as the toolbar's child so
   the pair travels together — a toolbar with no surface under it is an action
   bar, and this is the thing that makes it a toolbar.

   It is a FIELD, so it takes the field family's states rather than inventing
   its own: --input at rest, --input-border-hover on hover, --ring plus
   --focus-ring on focus, --destructive plus --focus-ring-error when invalid,
   --muted when disabled. */
.ck-editor{
  border:1px solid var(--field-border);border-radius:var(--radius-md);overflow:hidden;
  background:var(--background);color:var(--foreground);
  transition:border-color var(--duration-base),box-shadow var(--duration-base)}
.ck-editor:is(:hover,[data-force~="hover"]){border-color:var(--input-border-hover)}
/* focus-within, not focus: what takes focus is the area inside, and the stroke
   belongs to the whole field — the same contract .ck-input-wrap has. */
.ck-editor:is(:focus-within,[data-force~="focus"]){
  border-color:var(--ring);box-shadow:var(--focus-ring)}
.ck-editor[aria-invalid="true"]{border-color:var(--destructive)}
.ck-editor[aria-invalid="true"]:is(:focus-within,[data-force~="focus"]){
  box-shadow:var(--focus-ring-error)}
.ck-editor-area{
  min-height:150px;padding:var(--space-md) var(--space-base);
  font:var(--font-weight-normal) var(--text-base)/1.6 var(--font-sans);
  color:var(--foreground);outline:none}
.ck-editor-area:empty::before{
  content:attr(data-placeholder);color:var(--muted-foreground)}
/* ── .ck-editor-block — something the editor holds but does not edit ────
   A merge table, a signature, an inserted component: a block that sits in the
   flow of the text, is `contenteditable="false"`, and is removed as a unit
   rather than character by character. It has to read as NOT TYPEABLE, which
   is what the dashed stroke says — the same thing a dropzone's dashed stroke
   says about an area waiting for content. --accent is the ground, so the
   block separates from the white editing surface without becoming a panel.

   The dismiss is `.ck-icon-btn.size-xs.tone-danger` placed in the -block's
   trailing corner. It is not a part of its own: removing a block is the same
   act as removing anything else, and check 71 says the kit does not build a
   second dismiss.

   Spec: references/components/editor.md § Embedded blocks */
.ck-editor-block{
  position:relative;margin-block:var(--space-md);
  padding:var(--space-md) var(--space-base);
  border:1px dashed var(--info);border-radius:var(--radius-lg);
  background:var(--accent);color:var(--accent-foreground)}
.ck-editor-block-head{
  display:flex;align-items:center;gap:var(--space-sm);
  margin-bottom:var(--space-sm);padding-inline-end:var(--space-lg)}
.ck-editor-block-body{display:flex;flex-wrap:wrap;gap:var(--space-xs)}
.ck-editor-block > .ck-icon-btn{
  position:absolute;inset-block-start:var(--space-sm);inset-inline-end:var(--space-sm)}

/* A count sits under the area, inside the field, so it moves with it. */
.ck-editor-foot{
  display:flex;align-items:center;justify-content:flex-end;
  padding:var(--space-xs) var(--space-base);
  border-top:1px solid var(--panel-border);
  background:var(--card);color:var(--card-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  font-variant-numeric:tabular-nums}
.ck-editor-foot.is-over{color:var(--destructive)}
.ck-editor:is([aria-disabled="true"],.is-disabled){
  background:var(--muted);color:var(--muted-foreground);cursor:not-allowed}
.ck-editor:is([aria-disabled="true"],.is-disabled) .ck-action-bar.is-toolbar{
  opacity:1;pointer-events:none}
.ck-editor:is([aria-disabled="true"],.is-disabled) .ck-editor-area{
  color:var(--muted-foreground)}

/* ── MEDALLION CARD — .ck-card.is-medallion ──
   A card introduced by an icon sitting astride its top edge. Used where a
   short list of destinations has to be chosen between at a glance — pick a
   support topic, pick a connector — and the icon is doing the identifying
   before the words are read.

   Two things follow from the medallion and are not free choices: the card
   cannot clip its overflow, and its top padding is 52px rather than a step on
   the spacing scale, because it is the medallion's radius plus a gap. Both are
   recorded in the spec sheet.

   The whole card is the target, so it takes hover, focus and press — not the
   title inside it. */
.ck-card.is-medallion{
  position:relative;overflow:visible;
  display:flex;flex-direction:column;align-items:center;text-align:center;
  gap:var(--space-2xs);
  /* The top padding is DERIVED, not chosen: half the medallion plus a normal
     gap. Written as the sum so it moves if either does, instead of being a 52
     that nobody can explain later. */
  padding:calc((var(--icon-box) * 2 + var(--space-sm)) / 2 + var(--space-xl))
          var(--container-inset-lg) var(--container-inset-lg);
  background:var(--accent);color:var(--accent-foreground);
  border:1px solid var(--panel-border);
  cursor:pointer;
  transition:transform var(--duration-base),box-shadow var(--duration-base)}
.ck-card.is-medallion:is(:hover,[data-force~="hover"]){
  transform:translateY(-2px);box-shadow:var(--shadow-md)}
.ck-card.is-medallion:is(:active,[data-force~="active"]){transform:translateY(0)}
.ck-card.is-medallion:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-card.is-medallion .ck-card-title{font-size:var(--text-xl)}
.ck-card.is-medallion .ck-card-sub{font-size:var(--text-base);margin-top:0}
/* The medallion is two icon boxes plus a gap — 56px — so it scales with the
   icon system rather than being its own number, and it holds a --icon-box
   glyph with a full box of clearance all round. */
.ck-card-medallion{
  position:absolute;left:50%;
  width:calc(var(--icon-box) * 2 + var(--space-sm));
  height:calc(var(--icon-box) * 2 + var(--space-sm));
  top:calc((var(--icon-box) * 2 + var(--space-sm)) / -2);
  transform:translateX(-50%);
  border-radius:var(--radius-full);
  display:grid;place-items:center;
  background:var(--primary);color:var(--primary-foreground);
  box-shadow:var(--shadow-sm)}
.ck-card-medallion svg{
  width:var(--icon-box);height:var(--icon-box);
  stroke-width:var(--icon-stroke);display:block}
@media (prefers-reduced-motion:reduce){
  .ck-card.is-medallion{transition:none}
  .ck-card.is-medallion:is(:hover,[data-force~="hover"]){transform:none}}

/* ── COMBOBOX — .ck-combo ──
   A field you type into that filters a list, and which can accept a value that
   is not in the list at all. That last part is why it is not a [[dropdown]]:
   .ck-dd chooses among known options, and a combobox is for the case where
   "Auditor" might be a role that already exists or one you are inventing as
   you type it.

   The control IS the field base — it joins .ck-input's selector above rather
   than restating height, padding, stroke and states — and the list it drops is
   the dropdown's own panel and options. What is local to this component is the
   typing: an input that carries no chrome of its own, and the create row.

   `open` rather than `:focus-within` carries the focus stroke, because the
   field stays focused while the pointer is in the panel. */
.ck-combo{position:relative;width:100%}
.ck-combo-control{display:flex;align-items:center;gap:var(--space-sm);cursor:text}
.ck-combo.open .ck-combo-control{
  outline:0;border-color:var(--ring);box-shadow:var(--focus-ring)}
.ck-combo.has-error .ck-combo-control{border-color:var(--destructive)}
.ck-combo.has-error.open .ck-combo-control{box-shadow:var(--focus-ring-error)}
/* The input wears nothing: the control around it is the field. */
.ck-combo-input{
  flex:1;min-width:0;padding:0;border:0;outline:0;background:0;
  font:inherit;color:inherit}
.ck-combo-input::placeholder{color:var(--muted-foreground)}
.ck-combo-arrow{
  width:var(--icon-size);height:var(--icon-size);flex:none;
  display:grid;place-items:center;color:var(--muted-foreground);
  transform:rotate(0deg)}
.ck-combo-arrow svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-combo.open .ck-combo-arrow{
  animation:ck-chev-open var(--duration-chevron) var(--ease-default) forwards}
.ck-combo.is-closing .ck-combo-arrow{
  animation:ck-chev-close var(--duration-chevron) var(--ease-default) forwards}
.ck-combo .ck-dd-panel{top:calc(100% + 4px)}
.ck-combo.open .ck-dd-panel{display:flex}
/* The create row. --primary and medium, so "Add ..." reads as an action among
   a list of nouns rather than as one more thing to pick. */
.ck-dd-opt.is-add{color:var(--primary);font-weight:var(--font-weight-medium)}
@media (prefers-reduced-motion:reduce){
  .ck-combo.open .ck-combo-arrow,.ck-combo.is-closing .ck-combo-arrow{animation-duration:1ms}}

/* ── NAVIGATION LIST — .ck-navlist ──
   A vertical list of destinations INSIDE a page: the sections of Settings, the
   chapters of a guide.

   Not the [[sidebar-nav]], and the distinction matters because both get called
   "the nav". That one is the app's own edge — icon-only, global, exactly one
   of it. This is a panel in the content area, it carries labels, and a screen
   may hold more than one.

   THE INSET IS ON THE LIST, NOT THE CHOSEN ROW. Insetting only the active item
   with a margin made it a different width from its neighbours, so the lozenge
   jumped sideways as the selection moved and every row's label shifted with
   it. The list pads itself instead and every row is the same box; the chosen
   one only changes colour.

   Rows are 44px — size-lg on the control scale (section 6). A nav row is a
   primary target, often the first thing touched on the screen, and it holds an
   icon box, a label and a chevron on one line.

   Spec: references/components/navlist.md */
.ck-navlist{
  width:100%;box-sizing:border-box;
  background:var(--card);color:var(--card-foreground);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  padding:var(--space-sm);
  display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-navlist-item{
  display:flex;align-items:center;gap:var(--space-md);width:100%;
  box-sizing:border-box;min-height:44px;padding:0 var(--space-md);
  border:0;background:transparent;cursor:pointer;text-align:start;
  border-radius:var(--radius-md);
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);
  color:var(--card-foreground);
  transition:background var(--duration-base),color var(--duration-base)}

/* Six states, and the chosen row keeps its own on top of them: a selected row
   that turns grey on hover reads as deselecting. */
.ck-navlist-item:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
.ck-navlist-item:is(:active,[data-force~="active"]){background:var(--selected-bg)}
.ck-navlist-item:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-navlist-item:is(.is-active,[aria-current="page"],[data-force~="selected"]){
  background:var(--primary);color:var(--primary-foreground)}
/* Check 52 — every mark on a selected surface reads from THAT surface's
   foreground. The item flips to --primary and sets --primary-foreground on
   itself, but .ck-text asserts --foreground, which is the PAGE's, so a text
   component inside a selected row rendered dark on dark and simply vanished.
   Inheriting is the only correct answer: the row decides, the marks follow.
   The muted role keeps its rank by stepping back in opacity rather than in
   colour, because --muted-foreground on --primary is 1.4:1. */
.ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-text{color:inherit}
/* The same rule for a CONTROL rather than a mark. A tone-* icon button inside
   a selected row asserts its tone against the PAGE's ground: a destructive
   glyph on the row's own --primary measures 2.27:1, under the 3:1 a non-text
   mark needs. It inherits at rest, and takes its tone back on hover, where
   the tint supplies a ground of its own to read against. */
.ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-icon-btn{color:inherit}
.ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-icon-btn:is(:hover,[data-force~="hover"]){
  background:var(--ck-icon-hover);color:var(--ck-icon-hover-fg)}
.ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-text.is-muted{color:inherit;opacity:.72}
.ck-navlist-item:is(.is-active,[aria-current="page"]):is(:hover,[data-force~="hover"]){
  background:var(--primary-hover);color:var(--primary-foreground)}
.ck-navlist-item:is(.is-active,[aria-current="page"]):active{background:var(--primary-active)}
.ck-navlist-item:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:transparent;color:var(--muted-foreground);cursor:not-allowed}

/* Both glyphs ARE the icon atom. .ck-ico is already the 24px box holding a
   16px glyph at --icon-stroke, so the row places it and adds nothing — a
   .ck-navlist-icon wrapper around it was a box inside a box, and the inner one
   then fought the atom for the size and won at 18px. Rule 40: reach for the
   real class and let the organism place it. */
.ck-navlist-item > .ck-ico{flex:none;color:var(--icon-color)}
/* The glyph is stated at the row's own specificity on purpose. A page that
   runs its own icon renderer alongside the kit ends up with hosts carrying
   both classes, and a local `.ico svg{width:100%}` ties with the kit's rule
   and wins on order — the glyph then fills the 24px box instead of sitting
   16px inside it. This settles it for the row without reaching into whatever
   the page does elsewhere. */
.ck-navlist-item > .ck-ico > svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
.ck-navlist-chevron{margin-inline-start:auto}
/* A row label that takes the free width, so a count or a pill after it sits
   on the trailing edge and a long label clips instead of pushing them out. */
.ck-navlist-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap}
/* On the chosen row both glyphs take the fill's partner, or they stay grey on
   a --primary lozenge and drop under AA. */
.ck-navlist-item:is(.is-active,[aria-current="page"]) > .ck-ico{
  color:var(--primary-foreground)}
.ck-navlist-item:is(:disabled,[aria-disabled="true"]) > .ck-ico{
  color:var(--muted-foreground)}


/* ── RATING (Atom) ──
   Read-only by default. The score is stated in text beside the stars, because
   counting glyphs is not reading a value — and a screen reader gets the number
   from the text, not from five identical icons. */
.ck-rating{display:inline-flex;align-items:center;gap:var(--space-2xs)}
.ck-rating > svg{
  width:var(--icon-size);height:var(--icon-size);flex:none;
  color:var(--input);fill:none}
.ck-rating > svg.is-on{color:var(--warning);fill:var(--warning)}
.ck-rating > .ck-rating-value{
  margin-inline-start:var(--space-xs);
  font-variant-numeric:tabular-nums;color:var(--muted-foreground);
  font-size:var(--text-sm)}

/* Density */
.ck-table.size-sm{--table-row-height:32px}
.ck-table.size-lg{--table-row-height:48px}
.ck-table.size-sm th{padding:7px 10px}
.ck-table.size-sm td{padding:0 10px;font-size:var(--text-md)}
.ck-table.size-lg th{padding:13px 16px}
.ck-table.size-lg td{padding:0 16px}
.ck-table td .ck-mid-trunc{max-width:var(--table-cell-max)}

/* Sticky header, for a table scrolling inside a fixed-height container. */
.ck-table.is-sticky th{position:sticky;top:0;z-index:var(--z-sticky)}

/* ── TWO-TIER HEADER — thead tr.ck-th-groups ─────────────────────────────────
   A row of column groups (Invoice, Purchase Order, System data) above the row
   of column names. Each group is one <th colspan> over the columns it owns —
   the checkbox column included, when the first group starts the table — and
   its label is a sky pill that fills the whole span, so the groups read as
   bands, not badges. The 200px badge cap does not apply here: a band shorter
   than its columns stops pointing at them.

     <tr class="ck-th-groups">
       <th colspan="4"><span class="ck-pill sky size-sm">Invoice
         <span class="ck-th-group-tag">Alignment field</span></span></th>
       <th colspan="2"><span class="ck-pill sky size-sm">System data</span></th>
     </tr>
     <tr> …column names… </tr>

   The group row sits on --background so the grey column row below it stays
   the header proper; a 1px --panel-border separates them. The column row keeps
   the kit's header text, the same as a one-row header, with a firmer --input
   rule under it. When the table is
   .is-sticky the column row pins below the group row, not under it.

   Spec: references/components/table.md § Two-tier header */
.ck-table thead tr.ck-th-groups > th{
  height:var(--ck-th-groups-h,36px);box-sizing:border-box;padding:5px 3px;
  background:var(--background);color:var(--foreground);
  border-bottom:1px solid var(--panel-border);
  text-align:center;vertical-align:middle}
.ck-table thead tr.ck-th-groups > th > .ck-pill{width:100%;max-width:none;min-width:0}
.ck-table thead tr.ck-th-groups + tr > th{border-bottom:1px solid var(--input)}
.ck-table.is-sticky thead tr.ck-th-groups + tr > th{top:var(--ck-th-groups-h,36px)}
/* A tag inside a group band ("Alignment field"): 16px inside the 20px band,
   so the band keeps a 2px buffer round it. */
.ck-th-group-tag{
  display:inline-flex;align-items:center;height:16px;padding:0 6px;box-sizing:border-box;
  background:var(--background);
  color:var(--muted-foreground)/* pairing-exempt: --muted-foreground on --background is de-emphasised secondary copy, the .ck-empty pattern (§ 13) */;
  border:1px solid var(--field-border);border-radius:var(--radius-full);
  font:var(--font-weight-medium) var(--text-2xs) var(--font-sans);line-height:1}

/* Numeric columns read faster right-aligned and tabular. */
.ck-table .num{text-align:end;font-variant-numeric:tabular-nums}
.ck-table-empty{padding:36px 16px;text-align:center;color:var(--muted-foreground);font:var(--font-weight-normal) var(--text-md) var(--font-sans)}

/* ─── TOOLBAR (Molecule) ─── */
.ck-toolbar{display:flex;align-items:center;gap:var(--space-base);margin-bottom:16px}
.ck-toolbar .grow{flex:1}

/* ─── BUTTON (Atom) ────────────────────────────────────────────────────────
   Four variants x three sizes, plus hover / active / disabled / focus.

   THE DEFAULT IS THE SECOND-TIER BUTTON. There is no separate grey
   `secondary` variant, and there should not be one: the outlined default was
   always doing that job, and having both meant every screen had to choose
   between two things that meant the same thing. The bare class is the button
   you reach for beside a primary.

   Base is the outline variant: a --background surface carrying a --primary
   label and border. That is the recognised "outline" pairing (Rulebook §3) —
   label and border share one token, and primary on background is 12.01:1.
   Hover is --hover-bg, the product's own tint. NOTHING in the kit hovers to
   grey; a grey-hovering button read as a different system.

   Filled variants darken their fill on hover via the minted --*-hover tokens.
   They must never use opacity, which dims the label with the fill and reads
   as disabled (Rulebook §8).

   Spec: references/components/button.md */
.ck-btn{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-xs);
  height:36px;padding:0 14px;box-sizing:border-box;
  border-radius:var(--radius-md);
  border:var(--btn-border-width) solid var(--btn-border);
  background:var(--background);color:var(--primary)/* pairing-exempt: the outline pattern — label and border share --primary so the two cannot drift; 12.01:1 light, 15.06:1 dark, 12.74:1 high contrast */;
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);
  white-space:nowrap;cursor:pointer;
  transition:background var(--duration-base),border-color var(--duration-base),color var(--duration-base)
}
.ck-btn svg{width:var(--icon-size);height:var(--icon-size);flex:none}

/* Sizes — height and padding only; the label stays 12.5px/600 at every size
   so a row of mixed-size buttons shares one baseline. */
.ck-btn.size-sm{height:28px;padding:0 10px;gap:var(--space-xs)}
.ck-btn.size-lg{height:44px;padding:0 18px;gap:var(--space-sm)}

/* Variants */
.ck-btn.primary{background:var(--primary);color:var(--primary-foreground);border-color:var(--primary)}
/* Success — the one action that CONFIRMS a completed result: the "Done" on an
   import-complete dialog. Not a general affirmative; a green Save is a primary
   action wearing the wrong colour, and there is one --primary per view for a
   reason.

   IT IS THE SOFT TRIPLET, NOT A SOLID GREEN FILL, and that is measurement not
   taste. A solid --success with its nominal white partner is 3.43:1 in light —
   under AA for a button label, and the standing Sev-4 in § 11 for "any solid
   success fill with white text". Dark ink on it is no better across themes.
   The soft triplet passes everywhere: 4.72:1 light, 4.54:1 dark, 10.79:1 high
   contrast. It also sits at the right volume — a solid green CTA shouts louder
   than the primary action it is standing in for. */
.ck-btn.success{
  background:var(--success-bg);color:var(--success-soft-foreground);
  border-color:var(--success-border)}
.ck-btn.destructive{background:var(--destructive);color:var(--destructive-foreground);border-color:var(--destructive)}
.ck-btn.ghost{background:transparent;color:var(--foreground);border-color:transparent}
/* ── .ck-swatch — a colour you can pick ─────────────────────────────────
   A colour picker's chip: the whole control IS the colour, so it carries no
   label and no glyph, and the colour arrives as an inline background because
   it is the one piece of genuinely per-instance content a component can have.
   Everything else is the kit's — a 24px target (§ 6), --radius-sm (§ 7b), the
   stroke that keeps a pale swatch visible against a pale panel, and the same
   focus ring as every other control.

   It is a BUTTON, not a div with a handler. The email composer's palette was
   ten <div>s wearing .switch — the toggle — so the palette could not be
   reached from the keyboard and the markup claimed a component it was not.

   Spec: references/components/swatch.md */
.ck-swatch{
  width:24px;height:24px;flex:none;box-sizing:border-box;padding:0;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  cursor:pointer;
  transition:transform var(--duration-fast) ease
  /* pairing-exempt: the control's fill IS the content; it carries no text */}
.ck-swatch:is(:hover,[data-force~="hover"]){transform:scale(1.08)}
.ck-swatch:is(:focus-visible,[data-force~="focus"]){outline:0;box-shadow:var(--focus-ring)}
.ck-swatch:is(:active,[data-force~="active"]){transform:scale(.96)}
.ck-swatch[aria-pressed="true"],.ck-swatch.selected{
  box-shadow:0 0 0 2px var(--popover),0 0 0 4px var(--primary)}
.ck-swatch:is(:disabled,[aria-disabled="true"]){cursor:not-allowed;opacity:var(--disabled-opacity)}
.ck-swatch-grid{display:grid;grid-template-columns:repeat(5,24px);gap:var(--space-sm)}
@media (prefers-reduced-motion: reduce){ .ck-swatch{transition:none} }

/* ── .ck-btn.is-block — the button fills its container ───────────────────
   A form's primary action, a panel's single action, a button in a narrow
   rail: all of them want the full width, and the label stays centred. It is
   a WIDTH, not a new tone, so it composes with every variant and size.
   Without it a screen writes `width:100%` onto .ck-btn itself, which is a
   page setting a kit component's own geometry — and the variant set is fixed
   (check 45), so a page may not invent one. */
.ck-btn.is-block{width:100%;justify-content:center}

/* Hover — solid fills darken; outline and ghost take the product hover tint.
   [data-force~="hover"] rides along with every one of these. 91 rules in this
   file already carry that companion and the BUTTON did not, so the kit's own
   documentation mechanism — and any QA pass that uses it, which is the
   sanctioned way to read a state without synthesising a pointer — was blind on
   the most common control in the system. A probe cannot report what it cannot
   force, and a specimen page cannot show a hover it cannot hold. */
.ck-btn:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
/* The outline button rests on the lighter --btn-border and takes the full
   --primary edge on hover and press, so the stroke strengthens as it is used. */
.ck-btn:not(.primary):not(.success):not(.destructive):not(.ghost):not(.link):not(:disabled):is(:hover,:active,[data-force~="hover"],[data-force~="active"]){border-color:var(--primary)}
.ck-btn.primary:is(:hover,[data-force~="hover"]){background:var(--primary-hover);border-color:var(--primary-hover)}
/* Hover deepens the STROKE, not the fill — § 8's derivation rule again. On a
   soft tint there is no headroom: the rest state is 4.72:1 and darkening the
   fill drops the label under AA, exactly as it does on an errored table row. */
.ck-btn.success:is(:hover,[data-force~="hover"]){background:var(--success-bg);border-color:var(--success)}
.ck-btn.destructive:is(:hover,[data-force~="hover"]){background:var(--destructive-hover);border-color:var(--destructive-hover)}
.ck-btn.ghost:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--primary)}
/* .tone-primary — a ghost whose label is --primary at rest, for a quiet
   action that should still read as an action (Edit Document's Reset). Hover
   and press are the ghost's own. */
.ck-btn.ghost.tone-primary{color:var(--primary)}
/* Destructive ghost. .ck-btn.ghost is declared after .ck-btn.destructive, so at
   equal specificity ghost wins and the pair rendered as a neutral ghost. Two
   classes lifts this to 0-3-0. Same treatment .ck-icon-btn.tone-danger carries:
   destructive label at rest, soft destructive fill on hover, solid on press —
   so a labelled and an unlabelled destructive action agree.

   This replaces the retired .ck-clear-btn, which was a whole component for
   what is one variant combination. */
/* Rest text is the soft foreground, not --destructive: dark --destructive is
   4.26:1 on --popover, the drawer and menu surface; the soft one is 4.82:1
   there and 5.29:1 in light. */
.ck-btn.ghost.destructive{background:transparent;color:var(--destructive-soft-foreground);border-color:transparent}
.ck-btn.ghost.destructive:hover{background:var(--destructive-bg);color:var(--destructive-soft-foreground);border-color:transparent}
.ck-btn.ghost.destructive:active{background:var(--destructive);color:var(--destructive-foreground);border-color:transparent}
.ck-btn.ghost.destructive:focus-visible{box-shadow:var(--focus-ring-error)}
.ck-btn.ghost.destructive:disabled,.ck-btn.ghost.destructive[aria-disabled="true"]{
  background:transparent;color:var(--muted-foreground);border-color:transparent}

/* Active */
.ck-btn:active{background:var(--selected-bg)}
.ck-btn.primary:active{background:var(--primary-active);border-color:var(--primary-active)}
/* Pressed doubles the stroke rather than deepening the fill. It used to fill
   with --success-border, which stopped working the moment that token became a
   real green: a solid --success-soft-foreground fill under
   --success-soft-foreground text is 1.00:1. The fill has no headroom to move
   (§ 8), so the press moves the edge. */
.ck-btn.success:active{
  background:var(--success-bg);border-color:var(--success);
  box-shadow:inset 0 0 0 1px var(--success)}
.ck-btn.destructive:active{background:var(--destructive-active);border-color:var(--destructive-active)}
.ck-btn.ghost:active{background:var(--selected-bg)}

/* Disabled — one treatment for every variant, so a disabled row reads as one
   thing. Covers the attribute and the class the reference pages use. */
.ck-btn:disabled,.ck-btn.disabled,.ck-btn[aria-disabled="true"]{
  background:var(--muted);color:var(--muted-foreground);border-color:var(--input);
  cursor:not-allowed;pointer-events:none
}
/* A GHOST stays a ghost when disabled: no fill, no stroke, only the muted
   label. The shared disabled plate made it look like an outlined button the
   moment it was switched off. */
.ck-btn.ghost:is(:disabled,.disabled,[aria-disabled="true"]){background:transparent;border-color:transparent;color:var(--input)}

/* Focus — visible ring on every variant; destructive gets the error ring. */
.ck-btn:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-btn.destructive:focus-visible{box-shadow:var(--focus-ring-error)}
/* Destructive outline. The base .ck-btn already IS the outline pattern — page
   surface, --primary label, --primary stroke — so this is that same pattern in
   the destructive family, and nothing more. It exists for a labelled
   destructive action that has to be findable at rest: ghost gives a red word
   with no edge, which reads as a link in a row of buttons, and the solid fill
   shouts. Hover and press follow .ck-btn.ghost.destructive exactly, so the
   three destructive variants agree on what pressing one feels like. */
.ck-btn.destructive.is-outlined{
  background:var(--background);color:var(--destructive);
  border-color:var(--destructive)/* pairing-exempt: the outline pattern — label and border share --destructive so the two cannot drift */}
.ck-btn.destructive.is-outlined:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);
  border-color:var(--destructive)}
.ck-btn.destructive.is-outlined:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground);
  border-color:var(--destructive)}
.ck-btn.destructive.is-outlined:focus-visible{box-shadow:var(--focus-ring-error)}

/* ─── BADGE / CHIP / PILL (Atom) ───────────────────────────────────────────
   One component, three names. `.ck-badge` and `.ck-chip` are aliases of
   `.ck-pill`; use whichever reads better in context.

   Shape is always --radius-full. There is no rounded-rectangle variant and
   there must not be one: two page-local implementations had border-radius:6px
   and both are gone.

   Text colour is always --*-soft-foreground, never the solid token. The solid
   as text on its own -bg tint fails 4.5:1 in 12 of 66 family/theme pairs.

   Spec: references/components/pill.md */
.ck-pill,.ck-badge,.ck-chip{
  /* The label is CENTRED, always — horizontally as well as vertically. A pill
     hugs its content, so the two only diverge when something fixes the width:
     the 150px cap with a long label, a column of pills sharing a width, a
     truncated label. Left-aligned there reads as a mis-set label rather than a
     narrow one, and a column of them shows every text start at a different
     offset from the pill's own centre. */
  display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  gap:var(--ck-pill-gap);height:var(--ck-pill-h);padding:var(--ck-pill-pad);
  max-width:var(--ck-pill-max);
  /* A badge with no tone IS the neutral badge, so the base is the neutral tone
     rather than a borrowed surface. It used to take --muted with --border, and
     in the light theme those are the same hex, so every untoned badge in the
     kit — 277 of them on this page alone — drew its stroke in its own fill and
     had no border at all. --neutral-* is a real pair, so the cross-pairing
     exemption this line used to carry is gone with it. .is-neutral below
     restates the default for markup that needs to say the tone out loud. */
  border-radius:var(--radius-full);border:1px solid var(--neutral-border);
  background:var(--neutral-bg);color:var(--neutral-soft-foreground);
  font:var(--font-weight-medium) var(--ck-pill-fs) var(--font-sans);
  white-space:nowrap;vertical-align:middle;
  transition:background var(--duration-base),border-color var(--duration-base)
}

/* ── Sizes ──
   Height is fixed per size so a row of pills never staggers.

   Width HUGS THE TEXT, up to --badge-max-width (200px), the same at every size
   and in every context — a table cell included. Past the cap the LABEL
   truncates head…tail ("abc…apso", ckTruncate); the pill never grows past it
   and is never stretched to a fixed width. A container holding pills sizes to
   the pill, not the reverse. */
/* ── TYPE IS THE SAME AT EVERY SIZE ──
   A badge is a status label, and the label is the thing being read: it is
   MEDIUM at 12px whatever the badge's height. The three sizes used to carry
   three type sizes — 11, 12 and 13 — which made the small one harder to read
   than the data it sat next to and the large one compete with body copy, for
   no gain: the height and padding already tell the sizes apart, and the token
   scale has named 12px "badge text" all along.

   --ck-pill-fs stays in all three blocks rather than moving to the base. One
   block per size is the invariant below; a var that lives in only one of them
   is exactly the bug that comment is guarding against. */
/* One var block per size, covering all three names. These were duplicated —
   .ck-pill got the counter variables and .ck-badge / .ck-chip did not, so a
   counter or dismiss inside a badge or chip collapsed to 0px. An alias must
   never be able to miss a variable, so there is one block per size now. */
.ck-pill,.ck-badge,.ck-chip{
  --ck-pill-h:24px;--ck-pill-pad:0 10px;--ck-pill-fs:var(--text-md);--ck-pill-gap:var(--space-xs);
  --ck-pill-max:var(--badge-max-width);--ck-pill-ico:var(--icon-size);
  --ck-count-h:16px;--ck-count-fs:var(--text-xs);--ck-count-pad:5px;--ck-count-inset:4px}
.ck-pill.size-sm,.ck-badge.size-sm,.ck-chip.size-sm{
  --ck-pill-h:20px;--ck-pill-pad:0 8px;--ck-pill-fs:var(--text-md);--ck-pill-gap:var(--space-xs);
  --ck-pill-max:var(--badge-max-width);--ck-pill-ico:var(--icon-size);
  --ck-count-h:12px;--ck-count-fs:var(--text-2xs);--ck-count-pad:4px;--ck-count-inset:3px}
.ck-pill.size-lg,.ck-badge.size-lg,.ck-chip.size-lg{
  --ck-pill-h:28px;--ck-pill-pad:0 12px;--ck-pill-fs:var(--text-md);--ck-pill-gap:var(--space-sm);
  --ck-pill-max:var(--badge-max-width);--ck-pill-ico:var(--icon-size);
  --ck-count-h:20px;--ck-count-fs:var(--text-sm);--ck-count-pad:6px;--ck-count-inset:5px}

/* ── Label: truncates, never wraps ──
   .ck-mid-trunc keeps the head and the tail — "Missing D…nt" — because the
   tail of a status label usually carries the distinguishing word. The full
   string goes in title= for hover; ckMiddleTruncate in clipper-kit.js sets it. */
.ck-pill-label{min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap;text-align:center}

/* ── Icons: either side, or both ── */
.ck-pill svg,.ck-badge svg,.ck-chip svg{width:var(--ck-pill-ico);height:var(--ck-pill-ico);flex:none;display:block}
.ck-pill-dot{width:6px;height:6px;flex:none;border-radius:var(--radius-full);background:currentColor}
.ck-pill.size-sm .ck-pill-dot{width:5px;height:5px}
.ck-pill.size-lg .ck-pill-dot{width:7px;height:7px}

/* ── Counter: hollow, and only hollow ──
   Transparent fill, 1px border and text both in currentColor, which is the
   tone's --*-soft-foreground. The filled and subtle forms that the pages
   shipped are discarded. --radius-full so it matches the pill it sits in. */
.ck-pill-count{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  min-width:var(--ck-pill-h);height:var(--ck-count-h);
  margin-inline-end:calc(-1 * var(--ck-count-inset));
  padding:0 var(--ck-count-pad);
  border:1px solid currentColor;border-radius:var(--radius-full);
  background:transparent;color:currentColor;
  font:var(--font-weight-semibold) var(--ck-count-fs) var(--font-sans);font-variant-numeric:tabular-nums;
  line-height:1
}
.ck-pill-count{min-width:var(--ck-count-h)}

/* ── Tones. Surface, border and text are one family; text is soft-foreground. ── */
.ck-pill.off,.ck-pill.is-neutral,.ck-badge.is-neutral,.ck-chip.is-neutral{background:var(--neutral-bg);color:var(--neutral-soft-foreground);border-color:var(--neutral-border)}
.ck-pill.ok,.ck-pill.is-success,.ck-badge.is-success,.ck-chip.is-success{background:var(--success-bg);color:var(--success-soft-foreground);border-color:var(--success-border)}
.ck-pill.warn,.ck-pill.is-warning,.ck-badge.is-warning,.ck-chip.is-warning{background:var(--warning-bg);color:var(--warning-soft-foreground);border-color:var(--warning-border)}
.ck-pill.err,.ck-pill.is-error,.ck-badge.is-error,.ck-chip.is-error{background:var(--destructive-bg);color:var(--destructive-soft-foreground);border-color:var(--destructive-border)}
.ck-pill.info,.ck-pill.is-info,.ck-badge.is-info,.ck-chip.is-info{background:var(--info-bg);color:var(--info-soft-foreground);border-color:var(--info-border)}
.ck-pill.teal,.ck-badge.teal,.ck-chip.teal,.ck-counter.teal{background:var(--teal-bg);color:var(--teal-soft-foreground);border-color:var(--teal-border)}
.ck-pill.cyan,.ck-badge.cyan,.ck-chip.cyan,.ck-counter.cyan{background:var(--cyan-bg);color:var(--cyan-soft-foreground);border-color:var(--cyan-border)}
.ck-pill.indigo,.ck-badge.indigo,.ck-chip.indigo,.ck-counter.indigo{background:var(--indigo-bg);color:var(--indigo-soft-foreground);border-color:var(--indigo-border)}
.ck-pill.pink,.ck-badge.pink,.ck-chip.pink,.ck-counter.pink{background:var(--pink-bg);color:var(--pink-soft-foreground);border-color:var(--pink-border)}
.ck-pill.rose,.ck-badge.rose,.ck-chip.rose,.ck-counter.rose{background:var(--rose-bg);color:var(--rose-soft-foreground);border-color:var(--rose-border)}
.ck-pill.amber,.ck-badge.amber,.ck-chip.amber,.ck-counter.amber{background:var(--amber-bg);color:var(--amber-soft-foreground);border-color:var(--amber-border)}
.ck-pill.lime,.ck-badge.lime,.ck-chip.lime,.ck-counter.lime{background:var(--lime-bg);color:var(--lime-soft-foreground);border-color:var(--lime-border)}
.ck-pill.emerald,.ck-badge.emerald,.ck-chip.emerald,.ck-counter.emerald{background:var(--emerald-bg);color:var(--emerald-soft-foreground);border-color:var(--emerald-border)}
.ck-pill.violet,.ck-badge.violet,.ck-chip.violet,.ck-counter.violet{background:var(--violet-bg);color:var(--violet-soft-foreground);border-color:var(--violet-border)}
.ck-pill.fuchsia,.ck-badge.fuchsia,.ck-chip.fuchsia,.ck-counter.fuchsia{background:var(--fuchsia-bg);color:var(--fuchsia-soft-foreground);border-color:var(--fuchsia-border)}
.ck-pill.sky,.ck-badge.sky,.ck-chip.sky,.ck-counter.sky{background:var(--sky-bg);color:var(--sky-soft-foreground);border-color:var(--sky-border)}
.ck-pill.slate,.ck-badge.slate,.ck-chip.slate,.ck-counter.slate{background:var(--slate-bg);color:var(--slate-soft-foreground);border-color:var(--slate-border)}
.ck-pill.gold,.ck-badge.gold,.ck-chip.gold,.ck-counter.gold{background:var(--gold-bg);color:var(--gold-soft-foreground);border-color:var(--gold-border)}
.ck-pill.crimson,.ck-badge.crimson,.ck-chip.crimson,.ck-counter.crimson{background:var(--crimson-bg);color:var(--crimson-soft-foreground);border-color:var(--crimson-border)}
.ck-pill.sage,.ck-badge.sage,.ck-chip.sage,.ck-counter.sage{background:var(--sage-bg);color:var(--sage-soft-foreground);border-color:var(--sage-border)}
.ck-pill.purple,.ck-badge.purple,.ck-chip.purple,.ck-counter.purple{background:var(--purple-bg);color:var(--purple-soft-foreground);border-color:var(--purple-border)}
.ck-pill.orange,.ck-badge.orange,.ck-chip.orange,.ck-counter.orange{background:var(--orange-bg);color:var(--orange-soft-foreground);border-color:var(--orange-border)}
.ck-pill.blue,.ck-badge.blue,.ck-chip.blue,.ck-counter.blue{background:var(--blue-bg);color:var(--blue-soft-foreground);border-color:var(--blue-border)}

/* ── States. A pill is not interactive by default; these apply once it is
      (a filter chip, a clickable status). Hover and active follow §8. ── */
.ck-pill.is-interactive,.ck-badge.is-interactive,.ck-chip.is-interactive{cursor:pointer}
/* A 20px small chip is under the 24px target floor. An interactive one grows
   its hit area without moving the mark; ::after is taken by .has-value. */
.ck-chip.size-sm.is-interactive{position:relative}
.ck-chip.size-sm.is-interactive::before{content:'';position:absolute;inset:-2px 0}
.ck-pill.is-interactive:hover,.ck-chip.is-interactive:hover{background:var(--hover-bg)}
.ck-pill.is-interactive:active,.ck-chip.is-interactive:active{background:var(--selected-bg)}
/* A chip used as a TOGGLE — a source filter, a facet — needs a state that
   survives the pointer leaving, and :active is not it. aria-pressed is the
   attribute that says so, so it is what the rule keys off: the state cannot be
   styled as pressed while reading as unpressed to a screen reader. The kit's
   selection language is --selected-bg with its own foreground, the same pair
   the tree row and the menu option use. */
/* Selection is carried by the fill, the text colour and the stroke — three
   signals. It used to bump the weight to semibold as a fourth, which broke the
   rule that badge text is medium at every size, and moved the label: 600 is
   wider than 500, so a chip grew as it was picked and the row of chips beside
   it shifted. Measured at 0.52px on "All", and it scales with the label. */
.ck-pill.is-interactive[aria-pressed="true"],
.ck-chip.is-interactive[aria-pressed="true"]{
  background:var(--selected-bg);color:var(--selected-fg);
  border-color:var(--selected-fg)}
.ck-pill.is-interactive[aria-pressed="true"]:hover,
.ck-chip.is-interactive[aria-pressed="true"]:is(:hover,[data-force~="hover"]){
  background:var(--selected-bg);color:var(--selected-fg)}
.ck-pill.is-interactive:focus-visible,.ck-chip.is-interactive:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-pill[aria-selected="true"],.ck-chip[aria-selected="true"]{background:var(--selected-bg);color:var(--selected-fg);border-color:var(--primary)}
.ck-pill.is-disabled,.ck-badge.is-disabled,.ck-chip.is-disabled,
.ck-pill:disabled,.ck-chip:disabled{
  background:var(--muted);color:var(--muted-foreground);border-color:var(--input);
  cursor:not-allowed;pointer-events:none}

/* Dismiss. .ck-icon-btn.size-xs, so it clears the 24x24 WCAG 2.5.8 floor —
   .filter-chip-x was 14px and failed it. The negative margin pulls it into the
   pill's padding so the pill does not grow to accommodate it. */
/* Dismiss inside a pill — an exception to the icon-button tone system.
   It rests in the PILL'S OWN foreground (currentColor, i.e. the tone's
   --*-soft-foreground) rather than in --destructive, so it reads as part of the
   pill instead of as a standing warning. The destructive tokens appear only on
   hover, when the action is actually imminent.

   This is why .tone-danger is wrong here: that tone is destructive at rest.
   The pill's dismiss inverts that — neutral at rest, destructive on approach. */
/* .size-xs is declared later at equal specificity, so it was winning and the
   dismiss computed to 24px inside a 22px pill — taller than the pill, with a
   6px radius inside a fully-round one. The doubled class lifts this to 0-3-0.

   The VISUAL size matches the counter (13 / 16 / 19px) so it fits the pill and
   is round like it. The HIT AREA stays 24x24 via a ::before that overflows the
   visual bounds — WCAG 2.5.8 sizes the target, not the paint, so a small glyph
   with a large target satisfies it. */
.ck-pill .ck-icon-btn,.ck-badge .ck-icon-btn,.ck-chip .ck-icon-btn,
.ck-pill .ck-icon-btn.size-xs,.ck-badge .ck-icon-btn.size-xs,.ck-chip .ck-icon-btn.size-xs{
  position:relative;
  margin-inline-end:calc(-1 * var(--ck-pill-gap));
  width:var(--ck-count-h);height:var(--ck-count-h);
  border-radius:var(--radius-sm);
  color:inherit;
  --ck-icon-hover:var(--destructive-bg);
  --ck-icon-hover-fg:var(--destructive-soft-foreground)}
.ck-pill .ck-icon-btn::before,.ck-badge .ck-icon-btn::before,.ck-chip .ck-icon-btn::before{
  content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  width:24px;height:24px;border-radius:var(--radius-sm)}
/* Hover must be declared at pill specificity. The rule above is 0-3-0
   (.ck-pill .ck-icon-btn.size-xs) and .ck-icon-btn:hover is only 0-2-0, so
   `color:inherit` was winning on hover and the destructive colour never
   appeared — resting and hover rendered identically. */
.ck-pill .ck-icon-btn:hover,.ck-badge .ck-icon-btn:hover,.ck-chip .ck-icon-btn:hover,
.ck-pill .ck-icon-btn.size-xs:hover,.ck-badge .ck-icon-btn.size-xs:hover,
.ck-chip .ck-icon-btn.size-xs:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}

/* Active goes to the SOLID destructive pair, not --destructive-border:
   in high contrast --destructive-border and --destructive-soft-foreground are
   the same colour, so the icon vanished on its own fill at 1.0:1. The solid
   pair also reads as a firmer press. */
.ck-pill .ck-icon-btn:active,.ck-badge .ck-icon-btn:active,.ck-chip .ck-icon-btn:active,
.ck-pill .ck-icon-btn.size-xs:active,.ck-badge .ck-icon-btn.size-xs:active,
.ck-chip .ck-icon-btn.size-xs:active{
  background:var(--destructive);color:var(--destructive-foreground)}
.ck-pill .ck-icon-btn:focus-visible,.ck-badge .ck-icon-btn:focus-visible,
.ck-chip .ck-icon-btn:focus-visible{box-shadow:var(--focus-ring-error)}
.ck-pill.is-disabled .ck-icon-btn,.ck-chip.is-disabled .ck-icon-btn{color:var(--muted-foreground)}
/* A dismiss inside a pill is the ONE place the 16px glyph does not apply, and
   it is derived rather than picked: the glyph is the dismiss button's own size
   less 4px, so there are exactly 2px of clear space on every side at every
   pill size — 8px in 12px, 12px in 16px, 16px in 20px.

   A flat --icon-size here put a 16px cross in a 16px button, edge to edge:
   the cross touched the button's bounds, read a size too large for a 24px
   chip, and broke § 14.3 check 11 ("icons keep their buffer"). The kit's
   "16px glyph everywhere" rule is about icons laid out in their own box; a
   glyph living inside a control takes that control's padding, and a 16px-tall
   control has none to give.

   The HIT AREA is unaffected — it stays 24x24 on the ::before, which
   overflows the paint box, because WCAG 2.5.8 sizes the target and not the
   paint. */
.ck-pill .ck-icon-btn svg,.ck-chip .ck-icon-btn svg,.ck-badge .ck-icon-btn svg,
.ck-pill .ck-icon-btn.size-xs svg,.ck-chip .ck-icon-btn.size-xs svg,
.ck-badge .ck-icon-btn.size-xs svg{
  width:calc(var(--ck-count-h) - var(--space-xs));
  height:calc(var(--ck-count-h) - var(--space-xs))}
/* A GLYPH WRAPPED IN .ck-ico TAKES THE DISMISS'S BOX, NOT ITS OWN. .ck-ico
   is a 24px frame; inside a pill's 16px dismiss it overflowed downward, so
   the cross sat below the label's line, and its --icon-color replaced the
   pill's own foreground. It now fills the dismiss and inherits its ink. */
:is(.ck-pill,.ck-badge,.ck-chip) .ck-icon-btn > .ck-ico{width:100%;height:100%;color:inherit}

/* ─── FULFILMENT STATUS (Molecule) — .ck-fulfil ────────────────────────────
   A quantity, and under it what is still true about that quantity: a delta
   counter plus a one-word state. It is the pattern a reconciliation line uses
   in the product, and it is NOT a badge with a label inside.

   THE LABEL IS PLAIN TONE-COLOURED TEXT, NOT A PILL. Wrapping the word in a
   filled pill and putting the count inside it, which is what this used to be,
   made two different facts look like one object — and a row of them read as a
   wall of tinted rectangles rather than a column of numbers you can scan. The
   number is the data; the counter is the delta; the word says which.

   FULFILLED CARRIES NO COUNTER. Its delta is zero by definition, and a chip
   reading "0" is noise the reader has to interpret. The absence of a counter
   IS the signal, and the word still says it in full.

   Tones map to the semantic families: remaining is destructive (work
   outstanding), excess is --violet (over-delivery — neither good nor an
   error, and it must not be confused with either), fulfilled is success. */
.ck-fulfil{display:inline-flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-fulfil-qty{
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);
  color:var(--foreground);font-variant-numeric:tabular-nums;line-height:1.2}
/* An unknown quantity is an em dash in --muted-foreground, never an empty
   cell: blank reads as "nothing here", the dash reads as "not known yet". */
.ck-fulfil-qty.is-unknown{color:var(--muted-foreground)}
.ck-fulfil-state{display:inline-flex;align-items:center;gap:var(--space-xs);min-width:0}
.ck-fulfil-count{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  min-width:20px;height:20px;padding:0 var(--space-xs);
  border:1px solid transparent;border-radius:var(--radius-sm);
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);
  font-variant-numeric:tabular-nums;line-height:1}
.ck-fulfil-label{
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  white-space:nowrap;overflow:hidden;text-overflow:clip}
/* --radius-sm, not --radius-full: this is a small numeric chip beside text,
   not a status pill, and a full round on a 20px box makes the digits look
   squeezed against the ends. */
.ck-fulfil.is-remaining .ck-fulfil-count{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);border-color:var(--destructive-border)}
.ck-fulfil.is-remaining .ck-fulfil-label{color:var(--destructive-soft-foreground)}
.ck-fulfil.is-excess .ck-fulfil-count{
  background:var(--violet-bg);color:var(--violet-soft-foreground);border-color:var(--violet-border)}
.ck-fulfil.is-excess .ck-fulfil-label{color:var(--violet-soft-foreground)}
.ck-fulfil.is-fulfilled .ck-fulfil-label{color:var(--success-soft-foreground)}

/* The delta chooser between two quantities — a dashed outline says "this is
   editable and not yet committed", which a solid stroke does not. */
.ck-fulfil-delta{
  display:inline-flex;align-items:center;gap:var(--space-xs);box-sizing:border-box;
  height:28px;padding:0 var(--space-sm);
  border:1px dashed var(--input);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground);
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  font-variant-numeric:tabular-nums;cursor:pointer;
  transition:border-color var(--duration-base),background var(--duration-base)}
.ck-fulfil-delta:is(:hover,[data-force~="hover"]){border-color:var(--primary);background:var(--hover-bg)}
.ck-fulfil-delta:focus-visible{outline:0;border-color:var(--primary);box-shadow:var(--focus-ring)}
.ck-fulfil-delta > svg{width:var(--icon-size);height:var(--icon-size);flex:none;color:var(--muted-foreground)}

/* ── Standalone counter and status ──
      Same protocol as the pill's own, so a counter in a nav item and a counter
      inside a badge are the same shape, height and treatment.

      A COUNTER IS BUILT LIKE A BADGE: three tokens, one per job — a
      `-bg` surface, a `-soft-foreground` numeral, a `-border` stroke. It used
      to be a transparent circle with `border: 1px solid currentColor`, which
      made one colour do all three and left the surface inherited from whatever
      was behind it. That is a Law 3 break, and it also read as an outlined
      ring rather than as the same object a badge is.

      Every tone's numeral clears AA on its own fill: success 4.72:1, warning
      4.51:1, error 4.58:1, info 4.63:1, neutral 14.56:1. */
.ck-counter{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  min-width:20px;height:20px;padding:0 6px;
  /* The stroke is --input, and that is a considered choice. A counter is a
     20px circle whose fill is --muted at 1.04:1 against a white page, so the
     EDGE is what gives the mark a shape at all. --border is #f5f5f5 in light —
     the same value as the fill, so it drew nothing — and --panel-border is
     1.16:1, barely better. --input is 3.17:1, and § 4 already assigns it to
     "form control borders and any unlabelled control surface", which is what
     this edge is. A toned counter needs none of this: it takes its own
     -border step, which is a real tint of its own tone. */
  border:1px solid var(--input);border-radius:var(--radius-full);
  background:var(--muted);color:var(--foreground)/* pairing-exempt: --muted is a counter tint, not a muted-content surface; --foreground on it is 14.56:1 where the nominal --muted-foreground partner is 3.16:1 — the same declared cross-pair .ck-pill.is-neutral carries */;
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);font-variant-numeric:tabular-nums;line-height:1}
.ck-counter.size-sm{min-width:16px;height:16px;padding:0 4px;font-size:var(--text-2xs)}
.ck-counter.size-lg{min-width:24px;height:24px;padding:0 7px;font-size:var(--text-md)}

/* ── NO FIXED BADGE FOOTPRINT — every badge hugs its text ─────────────────
   A badge, pill or chip is as wide as its label, up to --badge-max-width
   (200px), in a table cell exactly as anywhere else. It used to be a fixed
   150 x 24 inside a table so a column of badges shared one edge; the design
   owner's rule now is that a badge fits its text, and only a label longer
   than the cap is cut.

   Past the cap the label truncates head…tail — "abc…apso" — the kit's one
   truncation format (ckTruncate), because the tail of a status is as likely
   to distinguish it as the start. The full text is kept for the tooltip.
   .is-fixed is kept only as an alias for the cap. */
/* ── A CONTROL THAT HOLDS A VALUE — .has-value ─────────────────────────────
   "Configured" is not "selected", and the kit had only one of the two. A
   tolerance button, a filter, a column picker — anything that opens a popover
   and comes back with something set — was reaching for .is-selected because it
   was the nearest state, and .is-selected means the control IS the chosen one
   among several. Painting one meaning with the other's paint is the visual
   approximation the token contract exists to stop.

   .has-value is already the kit's word for this: .ck-input-wrap, .ck-search
   and .ck-dd all use it to mean "this holds something". The only new thing is
   extending it to a control that is not a field.

   THE SIGNAL IS A DOT, not a fill and not a stroke. A fill is selection's, and
   .ck-btn is already --primary-bordered and --primary-labelled at rest, so a
   stroke change would say nothing. A dot is the kit's existing word for "there
   is something here" — notification, status, error, timeline all use one — and
   it stacks with selection rather than competing: a control can be both the
   chosen one AND carry a value, and it needs to be able to say so.

   The --background ring keeps it legible when the dot lands on a hovered or
   selected surface, the same trick the timeline dot uses on its rail. */
:is(.ck-btn,.ck-icon-btn,.ck-toggle,.ck-pill,.ck-chip).has-value{position:relative}
:is(.ck-btn,.ck-icon-btn,.ck-toggle,.ck-pill,.ck-chip).has-value::after{
  content:'';position:absolute;
  inset-block-start:-2px;inset-inline-end:-2px;
  width:8px;height:8px;border-radius:var(--radius-full);
  box-shadow:0 0 0 2px var(--background);
  background:var(--primary)
  /* pairing-exempt: a marker, carries no text */}
/* On a dark bar the ring takes that bar's own ground, or the dot reads as a
   hole punched in it. */
.ck-action-bar.on-dark :is(.ck-btn,.ck-icon-btn).has-value::after{
  box-shadow:0 0 0 2px var(--sidebar)}
/* A field already shows it holds a value by showing its clear — it does not
   also need a dot, and the two would collide in the same corner. */
.ck-input-wrap.has-value::after,.ck-search.has-value::after,.ck-dd.has-value::after{content:none}

/* ── A BADGE WITH A GLYPH ON EACH SIDE — .is-duo ───────────────────────────
   A fixed 150x24 badge carrying a leading mark and a trailing one, with the
   label between them. The status pill in a table column already has the fixed
   box; this adds the second glyph lane and, more importantly, makes the label's
   width DETERMINISTIC rather than emergent.

   It is a grid, not a flex row, and that is the whole point. Under flex the
   label is sized by its own content and the icons are pushed around by it: a
   long label squeezes them, a short one lets them drift inward, and the badge
   stops reading as one object across a column of rows. Three tracks fix both
   lanes at --ck-pill-ico and give the label exactly what is left:

     150 - 2 (border) - 16 (padding) - 8 (two gaps) - 32 (two glyphs) = 92px,

   the same 92px on every row whatever the words are. Past that it ellipsises;
   the icons never move. (The badge is border-box and carries a 1px stroke, so
   the border comes off the top — measured, not assumed.)

   THE GLYPHS ARE THE MARKUP'S. Any Lucide glyph goes in either lane and both
   inherit the badge's tone through currentColor, so a tone change recolours
   them with the label and nothing is declared twice. Swap them freely — the
   variant owns the geometry, not the meaning.

   A trailing BUTTON is a different thing: that is a dismiss, and it is
   .ck-icon-btn.size-xs inside a plain pill (section 13). This variant is for
   two marks that are read, not pressed.

   Width is a token so a consumer with a wider column can raise it once rather
   than overriding three properties. */
.ck-pill.is-duo,.ck-badge.is-duo{
  display:inline-grid;
  grid-template-columns:var(--ck-pill-ico) minmax(0,auto) var(--ck-pill-ico);
  align-items:center;
  box-sizing:border-box;
  /* Hugs its content like every badge, capped at --badge-max-width. */
  inline-size:max-content;
  max-inline-size:var(--badge-max-width);
  block-size:24px;
  gap:var(--space-xs);
  padding-inline:var(--space-sm)}
/* The label takes the middle track and nothing else, so it can never widen the
   badge or narrow a glyph lane. */
.ck-pill.is-duo > .ck-pill-label,.ck-badge.is-duo > .ck-pill-label{
  min-width:0;overflow:hidden;white-space:nowrap;text-overflow:clip;
  text-align:center}
/* Both glyphs sit centred in their own lane, so the pair is symmetrical even
   when the two icons have different optical widths. */
.ck-pill.is-duo > svg,.ck-badge.is-duo > svg{
  justify-self:center;align-self:center}
/* In a table cell the fixed pill rule above would otherwise re-impose flex. */
.ck-table td .ck-pill.is-duo,.ck-table td .ck-badge.is-duo{
  display:inline-grid;justify-content:stretch}

/* An icon-only pill is exempt. .ck-pill.is-icon is a square carrying one glyph
   and no text, and this sheet documents it as being FOR "a table's pinned
   action" — so stretching it to a 150px label footprint in the one place it was
   designed for made it unusable there, and screens hand-rolled their own square
   status mark instead. There is no text to line up, so the fixed footprint has
   nothing to do. */
/* NO FIXED WIDTH. A pill in a table cell used to be a fixed 150px, to line a
   column up; it now hugs its text like every other badge, capped at
   --badge-max-width with a head…tail cut past it. .is-fixed survives as an
   alias for that cap, so older markup keeps working. */
.ck-pill.is-fixed,
.ck-table td .ck-pill:not(.is-icon),
.ck-table td .ck-badge:not(.is-icon){
  width:auto;min-width:0;max-width:var(--badge-max-width);height:24px;
  /* Centred, like every other pill (§ 14.3 check 58). This is the case the
     label sits in the middle of whatever width the text gives the pill. */
  justify-content:center;gap:var(--space-xs);
  padding-inline:var(--space-sm)}
.ck-pill.is-fixed > .ck-pill-label,
.ck-table td .ck-pill > .ck-pill-label,
.ck-table td .ck-badge > .ck-pill-label{
  flex:0 1 auto;min-width:0;overflow:hidden;white-space:nowrap;text-overflow:clip;
  text-align:center}
/* A counter keeps its own size: it is a number, not a label. */
.ck-table td .ck-counter{width:auto;min-width:20px;max-width:none}
.ck-counter.is-success{background:var(--success-bg);color:var(--success-soft-foreground);border-color:var(--success-border)}
.ck-counter.is-warning{background:var(--warning-bg);color:var(--warning-soft-foreground);border-color:var(--warning-border)}
.ck-counter.is-error{background:var(--destructive-bg);color:var(--destructive-soft-foreground);border-color:var(--destructive-border)}
.ck-counter.is-info{background:var(--info-bg);color:var(--info-soft-foreground);border-color:var(--info-border)}
/* Solid brand fill. The only counter variant that is not a soft tint, and it is
   the one an unread count needs: a badge pinned to a bell has to read as "there
   is something here" from across the header, which a --muted chip at 1.04:1
   does not do. Surface and numeral are a declared pair. */
.ck-counter.is-primary{
  background:var(--primary);color:var(--primary-foreground);border-color:var(--primary)}

/* The one place a counter keeps a transparent surface: sitting ON a --primary
   fill, where borrowing that fill is the point. Both the numeral and the
   stroke are the fill's own partner. */
.ck-counter.on-primary{
  background:transparent;color:var(--primary-foreground);
  border-color:var(--primary-foreground)/* pairing-exempt: deliberately borrows the --primary fill it sits on as its surface; the numeral is that fill's declared partner at 12.01:1 */}

.ck-status{display:inline-flex;align-items:center;gap:var(--space-xs);font:var(--font-weight-medium) var(--text-md) var(--font-sans);color:var(--foreground)}
/* Same rule as the badge: the label is medium 12px whatever the size. Only the
   dot and the gap scale — a status sits in the same table rows as a badge and
   the two must read at one size. */
.ck-status.size-sm{gap:var(--space-xs)}
.ck-status.size-lg{gap:var(--space-sm)}
.ck-status-dot{width:8px;height:8px;flex:none;border-radius:var(--radius-full);background:var(--muted-foreground)/* pairing-exempt: carries no text */}
.ck-status.size-sm .ck-status-dot{width:6px;height:6px}
.ck-status.size-lg .ck-status-dot{width:10px;height:10px}
.ck-status.is-success .ck-status-dot{background:var(--success-soft-foreground)/* pairing-exempt: carries no text */}
.ck-status.is-warning .ck-status-dot{background:var(--warning-soft-foreground)/* pairing-exempt: carries no text */}
.ck-status.is-error .ck-status-dot{background:var(--destructive-soft-foreground)/* pairing-exempt: carries no text */}
.ck-status.is-info .ck-status-dot{background:var(--info-soft-foreground)/* pairing-exempt: carries no text */}
.ck-status.is-neutral .ck-status-dot{background:var(--muted-foreground)/* pairing-exempt: carries no text */}

/* ─── TABS (Organism) ─── */
/* Fixed height so a tab bar is the same height wherever it appears, and a
   16px gutter to match every other container edge. */
/* ─── TABS (Molecule) ──────────────────────────────────────────────────────
   A tab bar is a structural strip, not a control in a row, so it has its own
   height scale rather than the control scale (Rulebook §7a).

   The active indicator is --primary at 12:1, which is what conveys selection.
   The rail behind it is --panel-border: a visible structural stroke per §5,
   not the near-invisible --border it used to be.

   Spec: references/components/tabs.md */
.ck-tabs{
  display:flex;align-items:stretch;box-sizing:border-box;flex-shrink:0;
  height:44px;padding:0 16px;gap:0;
  /* THE RAIL IS A BACKGROUND, NOT A BORDER, and that is a containment fix
     rather than a cosmetic one. A border sits OUTSIDE the padding box, and
     the active indicator has to occupy the same 2px band, so with a border
     rail the indicator must hang at bottom:-2px — outside the padding box,
     which is exactly where `overflow` clips.

     .ck-tabs.is-scroll-x sets overflow-y:hidden, and it has no choice: a box
     that scrolls on one axis cannot keep overflow:visible on the other, the
     spec computes it to auto. So the scrolling tab bar silently lost its
     active indicator — the one affordance a tab bar cannot do without —
     and rendering the two variants side by side is the only way it showed.
     Found on the DES-667 folder listing, whose own hand-rolled
     `overflow:auto hidden` did the same thing.

     Painted as a background the rail is not clipped, and the indicator can
     sit at bottom:0 INSIDE the padding box. .is-pill and .is-segmented each
     set the `background` shorthand, which clears this image, so neither
     needs to cancel it. */
  background-image:linear-gradient(var(--panel-border),var(--panel-border));
  background-repeat:no-repeat;
  background-size:100% var(--ck-tab-rail);
  background-position:left bottom
}
/* OVERFLOW IN A TAB BAR — .ck-tabs.is-scroll-x
   The same problem .ck-action-bar.is-scroll-x already solves, and the same
   answer: a bar whose items do not fit SCROLLS, it does not wrap, because a
   second row moves every control out from under the cursor and changes the
   bar's height. .ck-tabs is flex-shrink:0, so a tab bar sharing its row with
   a button panel kept full width and ran underneath it instead. */
.ck-tabs.is-scroll-x{
  flex-wrap:nowrap;min-width:0;flex-shrink:1;
  overflow-x:auto;overflow-y:hidden;
  scrollbar-width:thin;overscroll-behavior-inline:contain}
.ck-tabs.is-scroll-x > *{flex:none}
.ck-tabs.size-sm{height:36px;padding:0 12px}
.ck-tabs.size-sm .ck-tab{padding:0 12px;font-size:var(--text-base)}
/* 13px, the button and toggle size (bible § 10 roles): at 12px the tabs read a
   size smaller than the Rescan / Filter controls on the same head. */

.ck-tab{
  display:inline-flex;align-items:center;height:100%;padding:0 16px;
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--muted-foreground);
  border:0;background:0;cursor:pointer;position:relative;
  transition:color var(--duration-base),background var(--duration-base)
}
/* A LONG TAB LABEL TRUNCATES FROM THE MIDDLE — "Newtab…showup" — and shows
   the full text on hover. A tab names a destination, and the end of that name
   is as likely to distinguish it as the start ("Invoices — Q3" against
   "Invoices — Q4"), so cutting only the tail would make two tabs identical.
   The cap is a max-width, not a fixed one: a short label still shrinks to fit
   its own text. */
/* A tab label is one line. The kit caps the tab at 180px and offers
   .ck-mid-trunc for what does not fit, which only works if the text
   cannot wrap instead — without this a tab in a narrow container silently
   becomes two lines and the 44px row grows with it. Found on the document
   editor's range popover, where Custom Ranges / Fixed Ranges each broke
   across two lines with 2px to spare. */
.ck-tab{max-width:180px;white-space:nowrap;gap:6px}
/* A tab is often a link (each tab its own page). The rail marks the active one;
   the browser's link underline is not part of the tab. */
.ck-tab{text-decoration:none}
/* A count in a tab reads from the tab's own foreground, so it tracks the
   selected colour instead of staying muted on the active tab. A toned counter
   (.is-error and the rest) keeps its own pair: this rule has the tone rules'
   specificity and comes later, so without the :not() it won and left red text
   off a red fill. */
.ck-tab > .ck-counter:not([class*="is-"]){color:inherit;border-color:currentColor}
.ck-tab > .ck-mid-trunc{display:block;min-width:0;overflow:hidden;white-space:nowrap}
.ck-tab:hover{color:var(--foreground);background:var(--hover-bg)}
.ck-tab:active{background:var(--selected-bg)}

/* Selected. Driven by aria-selected so the visual cannot drift from what a
   screen reader announces; .active is kept as a transitional alias. */
.ck-tab[aria-selected="true"],.ck-tab.active{color:var(--primary);font-weight:600}
.ck-tab[aria-selected="true"]::after,.ck-tab.active::after{
  content:'';position:absolute;left:0;right:0;bottom:-2px;height:2px;background:var(--primary)/* pairing-exempt: carries no text */}

.ck-tab:disabled,.ck-tab[aria-disabled="true"]{
  color:var(--input);cursor:not-allowed;pointer-events:none;background:0
}
.ck-tab:focus-visible{outline:0;box-shadow:var(--focus-ring)}

/* A count or badge riding along in the label. */
.ck-tab .ck-pill{margin-inline-start:6px}

/* ── Icons in a tab label — either side, or both ──
   The glyph is --icon-size like every other icon, and the gap is the label's
   own. A leading glyph classifies the tab; a trailing one points at what
   selecting it does. Put a count in a badge, never in a glyph. */
.ck-tab{gap:var(--space-sm)}
.ck-tab > svg{width:var(--icon-size);height:var(--icon-size);flex:none;stroke-width:var(--icon-stroke)}

/* ── STROKE WIDTHS, stated once ──
   underline rail        2px  --panel-border
   underline indicator   2px  --primary, sitting IN the rail's own band
   segmented outer edge  1px  --input
   segmented divider     1px  --input
   pill trough           0    a fill, no stroke
   pill active plate     1px  --panel-border, plus --shadow-raised
   Anything thicker reads as a structural rule; anything thinner disappears
   against the page. */
.ck-tabs{--ck-tab-rail:2px}
/* NO GAP between the indicator and the separator. The indicator is pulled up
   by exactly the rail's thickness, so it occupies the rail's band rather than
   sitting below it — the two are one continuous 2px line, and the active tab's
   segment of it is --primary. Offsetting by anything other than
   --ck-tab-rail leaves either a hairline of --panel-border showing through or
   a 1px step where the colours meet. */
.ck-tab[aria-selected="true"]::after,.ck-tab.active::after{
  bottom:0;height:var(--ck-tab-rail)}

/* ─── PILL TABS — .ck-tabs.is-pill ────────────────────────────────────────
   A recessed trough with the active tab raised out of it. Use it for
   switching between peer views of the same data, where none of them is a
   "page" — the underline variant implies a section, this one implies a mode.

   Selection is carried three ways, not one: the plate lifts to --background,
   the label goes from --segment-foreground to --foreground, and the weight
   goes semibold. The plate alone is only 1.13:1 against the trough, so on
   colour it would be no signal at all. */
.ck-tabs.is-pill{
  display:inline-flex;height:auto;padding:var(--space-xs);gap:var(--space-2xs);
  border:0;border-radius:var(--radius-md);
  background:var(--segment);color:var(--segment-foreground)}
.ck-tabs.is-pill .ck-tab{
  height:28px;padding:0 var(--space-md);border-radius:var(--radius-sm);
  color:var(--segment-foreground)}
.ck-tabs.is-pill.size-lg .ck-tab{height:36px;padding:0 var(--space-base)}
.ck-tabs.is-pill .ck-tab:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--foreground)}
.ck-tabs.is-pill .ck-tab:is([aria-selected="true"],.active){
  background:var(--background);color:var(--foreground);
  border:1px solid var(--panel-border);box-shadow:var(--shadow-raised);
  font-weight:var(--font-weight-semibold)}
/* The underline indicator is the other variant's mechanism; a pill must not
   also grow a bar. */
.ck-tabs.is-pill .ck-tab:is([aria-selected="true"],.active)::after{display:none}
.ck-tabs.is-pill .ck-tab:is(:disabled,[aria-disabled="true"]){background:0;color:var(--input)}

/* ─── SEGMENTED / BORDERED TABS — .ck-tabs.is-segmented ───────────────────
   One bordered block divided into segments, the active one filled solid. Use
   it for a small closed set of mutually exclusive choices — Day / Week /
   Month / Year — where the options are a scale rather than destinations.

   Selection here CAN rest on the fill, because --primary-foreground on
   --primary is 12.01:1 and the fill is a genuine surface change, not a tint. */
.ck-tabs.is-segmented{
  display:inline-flex;height:auto;padding:0;gap:0;overflow:hidden;
  border:1px solid var(--input);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground)}
.ck-tabs.is-segmented .ck-tab{
  height:36px;padding:0 var(--space-base);border-radius:0;
  color:var(--foreground)}
.ck-tabs.is-segmented.size-sm .ck-tab{height:28px;padding:0 var(--space-md);font-size:var(--text-md)}
.ck-tabs.is-segmented .ck-tab + .ck-tab{border-inline-start:1px solid var(--input)}
.ck-tabs.is-segmented .ck-tab:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--primary)}
.ck-tabs.is-segmented .ck-tab:is([aria-selected="true"],.active){
  background:var(--primary);color:var(--primary-foreground);
  font-weight:var(--font-weight-semibold)}
.ck-tabs.is-segmented .ck-tab:is([aria-selected="true"],.active)::after{display:none}
.ck-tabs.is-segmented .ck-tab:is(:disabled,[aria-disabled="true"]){background:var(--muted);color:var(--muted-foreground)}
/* Focus is inset on both of these, because the strip clips its overflow and an
   outer ring would be cut off by the rounded corner. */
.ck-tabs.is-pill .ck-tab:focus-visible,
.ck-tabs.is-segmented .ck-tab:focus-visible{
  outline:0;box-shadow:inset 0 0 0 2px var(--primary)}

/* ── Panel connected to an underline bar ──
   The panel's top edge IS the tab rail, so they must not both draw one. */
.ck-tab-panel{
  padding:var(--container-inset);box-sizing:border-box;
  border:1px solid var(--panel-border);border-top:0;
  border-end-start-radius:var(--radius-md);border-end-end-radius:var(--radius-md);
  background:var(--card);color:var(--card-foreground)}
.ck-tab-panel[hidden]{display:none}
/* ── The panel with no frame of its own — .ck-tab-panel.is-plain ──
   A tab rail inside a surface that already draws an edge — a popover, a
   dialog, a panel body — would otherwise put a second frame inside the first.
   The inner one is the visible defect: it indents every row away from the
   container's own inset, so the content no longer lines up with anything else
   in that surface. The rail keeps its underline; only the box goes. */
.ck-tab-panel.is-plain{
  border:0;background:0;color:inherit;padding:0;border-radius:0}

/* A ROW THAT OPENS SOMETHING — .ck-table tr.is-clickable. The row already
   tints on hover; this says it is a target: the pointer, and a focus ring when
   the row itself takes focus (tabindex="0", Enter opens it). Its own buttons
   (a ⋮ menu) stay separate targets. Found on E-Invoice Onboarding's list. */
.ck-table tbody tr.is-clickable{cursor:pointer}
.ck-table tbody tr.is-clickable:focus-visible{outline:2px solid var(--ring);outline-offset:-2px}
/* ─── EMPTY STATE (Molecule) ─── */
/* ─── EMPTY STATE (Molecule) ───────────────────────────────────────────────
   Structure, not just centred text: a glyph, a title, a line of explanation,
   and one action. An empty state with no action is a dead end.

   Spec: references/components/empty-state.md */
.ck-empty{/* pairing-exempt: empty-state copy is deliberately de-emphasised; --muted-foreground on --background is 3.44:1 */
  display:flex;flex-direction:column;align-items:center;gap:var(--space-sm);
  padding:36px 16px;text-align:center;
  color:var(--muted-foreground);font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  background:var(--background);border-radius:var(--radius-2xl);
  border:1px dashed var(--input)}
.ck-empty.size-sm{padding:20px 12px;gap:var(--space-xs)}
.ck-empty.size-lg{padding:56px 24px;gap:var(--space-md)}
.ck-empty-icon{width:32px;height:32px;color:var(--input)}
.ck-empty-icon svg{width:32px;height:32px;display:block}
.ck-empty-title{font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--foreground)}
.ck-empty-msg{max-width:44ch}
/* Rule 65: the title is a heading (<h2>/<h3>) and the message a <p>; both
   kept the browser's margins, which doubled the empty state's own gap. */
.ck-empty-title,.ck-empty-msg,.ck-error-state-title{margin:0}
.ck-empty-action{margin-top:6px}
/* A field the caller has already decided — shown, explained, not editable. */
.ck-field.is-locked .ck-input,.ck-field.is-locked .ck-select{background:var(--muted);color:var(--muted-foreground);cursor:not-allowed}

/* ─── SEARCHABLE DROPDOWN (Organism) ──────────────────────────────────────
   Trigger + filterable panel. Behaviour lives in clipper-dropdown.js; link
   both to use it. Replaces a native <select> wherever the option list is
   long enough that scanning it is slower than typing. */
.ck-dd{position:relative;width:100%}
/* The trigger is a variant of the field base above, not a copy of it. It
   overrides only what a button genuinely differs in: it lays its value and
   chevron out as a row, and it is clickable. Everything else — height,
   padding, type, colours, stroke, radius, transition — comes from the base,
   so the family cannot drift the way it did when .ck-search re-declared the
   input and then missed aria-invalid, :disabled and ::placeholder. */
.ck-dd-trigger{
  display:flex;align-items:center;gap:var(--space-sm);
  cursor:pointer;text-align:left}
/* data-force~="hover|active|focus|disabled" paints a state without the user
   being in it, so a spec sheet renders the full state set from the kit's OWN
   rules instead of re-declaring them in page CSS (§ 12.1). Documentation only.
   Same hook the Switch carries. */
/* Sizes — kept identical to .ck-input so a dropdown and a text field in
   the same .ck-field-grid line up. */
.ck-dd.size-sm .ck-dd-trigger{height:28px;padding:0 9px;font-size:var(--text-md)}
.ck-dd.size-lg .ck-dd-trigger{height:44px;padding:0 13px}
:is(.ck-dd-trigger:disabled,.ck-dd-trigger[aria-disabled="true"],.ck-dd-trigger[data-force~="disabled"]){
  background:var(--muted);color:var(--muted-foreground);border-color:var(--field-border);cursor:not-allowed}
.ck-dd-trigger:disabled,.ck-dd-trigger[aria-disabled="true"]{pointer-events:none}
:is(.ck-dd-trigger:disabled,.ck-dd-trigger[aria-disabled="true"],.ck-dd-trigger[data-force~="disabled"]) .ck-dd-arrow{
  color:var(--muted-foreground)}
:is(.ck-dd-trigger:active,.ck-dd-trigger[data-force~="active"]){background:var(--hover-bg)/* pairing-exempt: re-asserts the surface only; the base rule owns the colour */}
:is(.ck-dd-trigger:focus-visible,.ck-dd-trigger[data-force~="focus"]),.ck-dd.open .ck-dd-trigger{outline:0;border-color:var(--primary);box-shadow:var(--focus-ring)}
/* Invalid focus takes the error ring, like every other field control (§ 14.9). */
:is(.ck-dd-trigger[aria-invalid="true"]:focus-visible,.ck-dd-trigger[aria-invalid="true"][data-force~="focus"]){
  border-color:var(--destructive);box-shadow:var(--focus-ring-error)}
.ck-dd-value{flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap}
/* A LEADING GLYPH — .ck-dd.has-icon.
   A flag, a currency mark, an avatar: something that identifies the value
   before the value is read. It sits inboard of the trigger's own padding and
   keeps the icon box, so the label still starts on the field's text line.

   This replaces a .ck-phone molecule that existed to put a flag beside a
   dialling code. A country picker is a dropdown with a leading glyph, and so
   is a currency picker and an account picker — one variant, not one component
   each. */
.ck-dd.has-icon .ck-dd-trigger{gap:var(--space-sm)}
.ck-dd-icon{
  width:var(--icon-box);height:var(--icon-box);flex:none;
  display:grid;place-items:center;overflow:hidden}
.ck-dd-icon > img{width:20px;height:14px;object-fit:cover;border-radius:var(--radius-xs);display:block}
.ck-dd-icon > svg{width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
/* Content-width, for a picker that sits beside a field rather than filling a
   row — a dialling code next to a number. Without it the trigger inherits the
   field group's width:100% and a "+1" button renders several hundred px wide. */
.ck-dd.is-compact{width:auto;flex:none}

.ck-dd-value.is-placeholder{color:var(--muted-foreground)}
/* A dropdown's clear behaves exactly like a field's: hidden until the trigger
   is hovered or focused, and only when something is selected. It sits inboard
   of the chevron, the same order a search box uses. */
.ck-dd-clear{padding:0;
  position:absolute;z-index:1;
  /* Inboard of the chevron: 11px of trigger padding, the 16px glyph, and 2px
     so the two 24px hit targets do not overlap. It is
     a sibling of the trigger, never a child — a button inside a button is
     invalid markup, and the parser would lift it straight back out. Centred by
     inset + auto margins, never by transform, because the chevron's rotation
     already owns transform on this row. */
  inset-inline-end:29px;inset-block:0;margin-block:auto;
  /* display:none until .has-value, for the same reason as .ck-input-clear:
     opacity:0 leaves the control in the tab order. See check 74. */
  display:none;place-items:center;width:24px;height:24px;
  border:0;background:0;border-radius:var(--radius-sm);
  color:var(--muted-foreground);cursor:pointer;
  transition:opacity var(--duration-base)}
.ck-dd.has-value .ck-dd-clear{display:grid;opacity:0;pointer-events:none}
.ck-dd.size-sm .ck-dd-clear{inset-inline-end:27px}
.ck-dd.size-lg .ck-dd-clear{inset-inline-end:31px}
/* The clear's lane is held open inside the value whether the clear is showing
   or not, so a long label ellipsises short of it and revealing it on hover
   never nudges the text. */
.ck-dd.has-value .ck-dd-value{padding-inline-end:20px}
.ck-dd-clear svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-dd-clear:hover{background:var(--hover-bg);color:var(--foreground)}
.ck-dd-clear:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-dd.has-value:hover .ck-dd-clear,
.ck-dd.has-value:focus-within .ck-dd-clear,
.ck-dd.has-value[data-force~="hover"] .ck-dd-clear{opacity:1;pointer-events:auto}

/* The stroked chevron is the dropdown's one visual addition to the field
   base, so it takes the product's standard glyph size like every other icon. */
.ck-dd-arrow{width:var(--icon-size);height:var(--icon-size);flex:none;color:var(--muted-foreground)}
/* The class normally goes ON the svg. When markup wraps it in a span instead,
   the class sizes the SPAN and the svg inside inherits nothing — an unsized
   <svg> in a flex row expands to fill the line, and the chevron renders at the
   full width of the field. That is not hypothetical: it is the defect the kit
   already recorded once for the multi-select trigger, and it came back the
   moment a second author wrote the wrapper form. Sizing the child here makes
   both markup forms correct, so it cannot come back a third time. */
.ck-dd-arrow > svg{width:var(--icon-size);height:var(--icon-size);display:block}
/* The chevron turns CLOCKWISE in both directions. A two-state transition
   cannot do that: with closed at 0deg and open at 180deg, closing interpolates
   180 -> 0 and reads as counter-clockwise. So opening and closing are separate
   keyframes, and ClipperDropdown adds .is-closing for the duration of the
   close. Closing runs 180 -> 360, which is clockwise. */
/* ── THE OPEN CHEVRON RESTS, IT DOES NOT REPLAY ────────────────────────────
   The half-turn disclosure chevrons keyed to aria-expanded — accordion, table
   row, document row — used to OPEN by running ck-chev-open. An animation runs
   whenever its selector starts matching, and that includes the moment an
   element is inserted already open. So a list re-rendered with open groups spun
   every chevron at once, with nothing clicked: measured on Role Management,
   switching roles set 12 of 12 turning. First paint did the same.

   Open is now a resting rotation reached by a TRANSITION, which fires only on
   a real change of state, never on insertion. The transition sits on the open
   rule alone, deliberately: the close still runs ck-chev-close, 180 -> 360
   clockwise, and when its class drops the chevron falls from 360 to 0 — the
   same drawing — with no transition declared to animate that jump. Put the
   transition on the base rule and it unwinds a full turn backwards at exactly
   that moment.

   The navlist's quarter-turn is not covered: its close ends at 180deg, which
   is NOT the drawing it rests at, so it needs its own look. */
@media (prefers-reduced-motion: reduce){
  :is(tr[aria-expanded="true"] > td > .ck-row-chev,
      .ck-accordion-trigger[aria-expanded="true"] .ck-accordion-chev,
      .ck-doc-head[aria-expanded="true"] .ck-doc-chev){transition-duration:1ms}}
/* ── A CHEVRON TURNS ON ITS OWN AXIS ─────────────────────────────────────
   Every chevron box is --icon-size, and it rotates about its own centre. A
   flow that wraps the glyph in .ck-ico puts a 24px box (--icon-box) inside a
   16px one, anchored top-left, so the glyph sat 4px off the axis it turns
   about and ORBITED instead of spinning: measured on Role Management's
   permission groups, the glyph's centre drifted 9.6px across and down during
   each turn, sitting 4px above its label when open and 4px below when shut.
   A nested .ck-ico now fills its chevron exactly, so the glyph's centre IS the
   pivot — it turns in place, clockwise, and never moves. */
:is(.ck-accordion-chev,.ck-doc-chev,.ck-row-chev,.ck-folder-chev,.ck-dd-arrow,.ck-combo-arrow,.ck-navlist-chevron) > .ck-ico{
  width:100%;height:100%}
@keyframes ck-chev-open{from{transform:rotate(0deg)}to{transform:rotate(180deg)}}
@keyframes ck-chev-close{from{transform:rotate(180deg)}to{transform:rotate(360deg)}}
.ck-dd-arrow{transform:rotate(0deg)}
.ck-dd.open .ck-dd-arrow{animation:ck-chev-open var(--duration-chevron) var(--ease-default) forwards}
.ck-dd.is-closing .ck-dd-arrow{animation:ck-chev-close var(--duration-chevron) var(--ease-default) forwards}

.ck-dd-panel{position:absolute;z-index:var(--z-dropdown);left:0;right:0;top:calc(100% + 4px);display:none;flex-direction:column;background:var(--popover);color:var(--popover-foreground);border:1px solid var(--panel-border);border-radius:var(--radius-2xl);box-shadow:var(--shadow-panel);overflow:hidden}
/* Portalled to <body>: fixed, above dialogs and drawers, never clipped by a
   scrolling ancestor and never part of the layout.

   PLACEMENT ONLY. This rule used to set display:flex as well, which welded two
   unrelated things together: a consumer that wanted the portal's positioning —
   the document editor's select menu does — got "always visible" with it and had
   to fight that back with a rule of its own. Visibility is .is-open, so either
   can be taken without the other. */
.ck-dd-panel.is-portal{position:fixed;right:auto;top:auto;z-index:calc(var(--z-toast) + 10)}
/* A MENU OPENED FROM INSIDE AN OVERLAY — .ck-menu.is-portal
   .ck-menu is --z-dropdown (20) and a dialog or drawer scrim is --z-overlay
   (30), so a menu opened from inside one rendered BEHIND the thing it belongs
   to. .ck-dd-panel.is-portal was the kit's only surface declared above an
   overlay, which is why DES-667's folder picker had to compose both classes to
   get a working menu. Same contract, same z-index, so neither can drift. */
.ck-menu.is-portal{position:fixed;right:auto;top:auto;z-index:calc(var(--z-toast) + 10)}
/* ── A SPECIMEN STANDS IN THE PAGE, AND THE KIT SAYS HOW ───────────────────
   An overlay floats: it is placed against the viewport or its trigger, and a
   dropdown panel is not even displayed until something opens it. A page that
   wants to *show* one — a spec sheet, a states grid — has to undo that, and
   until now every such page undid it by hand, in a style attribute:
   position:static, display:flex, and a margin picked by eye. There were 24.

   Each one is a page overriding a kit component, which § 2 forbids, and each
   is a place where what the reader sees stops being what the kit does. The
   hand-picked gaps were 6px — off the 4px scale, and off it precisely because
   no rule was watching them.

   .is-static is the sanctioned way to do it, and it belongs beside data-force
   in § 12.1: a documentation-only hook. It plants the overlay in the flow of
   the page, shows it, and puts the gap on the scale. It never appears in a
   product flow — there, an overlay floats. */
:is(.ck-dd-panel,.ck-menu,.ck-card.is-dock,.ck-card.is-tour).is-static{position:static}
/* Only the panel hangs off a trigger inside its own wrapper, so only the panel
   owns the gap to it; the others sit in page layout that already has one. */
.ck-dd-panel.is-static{display:flex;margin-block-start:var(--space-xs)}
/* An in-flow panel is shown by its own wrapper; a portalled one has left that
   wrapper's subtree, so it carries the state itself. */
.ck-dd.open .ck-dd-panel,
.ck-dd-panel.is-open{display:flex}
/* The in-flow panel is as wide as its field, so only the vertical axis can
   run past the window; it scrolls rather than clipping. */
.ck-dd-panel:not(.is-portal){max-height:min(320px,calc(100vh - 2 * var(--overlay-margin)));overflow-y:auto}
.ck-dd.drop-up .ck-dd-panel{top:auto;bottom:calc(100% + 4px)}
/* ── THE MULTI PANEL HANGS OFF THE FIELD, NOT THE WHOLE CONTROL ────────────
   .ck-dd-panel anchors at top:100% of .ck-dd, and .ck-dd.is-multi is now three
   stacked parts — field, summary row, chips. 100% of that is below the chips,
   so the list opened somewhere under the selection instead of under the thing
   that opens it, and the further the selection grew the further away it went.

   It anchors to the field's own height instead, and sits ABOVE the summary and
   the chips rather than pushing them down: opening a list is a temporary act
   and it should not move the page under the reader. The three offsets are the
   three trigger heights — the trigger holds only a placeholder now, so it is
   exactly its min-height and the number is knowable. */
.ck-dd.is-multi > .ck-dd-panel{top:calc(36px + var(--space-xs));z-index:var(--z-dropdown)}
.ck-dd.is-multi.size-sm > .ck-dd-panel{top:calc(28px + var(--space-xs))}
.ck-dd.is-multi.size-lg > .ck-dd-panel{top:calc(44px + var(--space-xs))}
/* The summary and chips stay below it in the stacking order, so the panel
   covers them cleanly rather than showing through. */
.ck-dd.is-multi > .ck-dd-summary,
.ck-dd.is-multi > .ck-dd-tags{position:relative;z-index:var(--z-base)}
/* Also the .ck-search molecule; this only places it. See the note on
   .ck-menu-search above. */
.ck-dd-search{padding:8px;flex:none}
/* The leading action row (ClipperDropdown cfg.action): the kit outline button,
   start-aligned, over the search and the options. 120px is its minimum width,
   this row's own geometry (Law 2 recorded exception), so a short label still
   reads as a button, not a chip. */
.ck-dd-action{display:flex;justify-content:flex-start;padding:var(--space-sm);border-bottom:1px solid var(--border);flex:none}
.ck-dd-action > .ck-btn{min-width:120px}
/* Search and action on one row: the search takes the room, the button keeps
   its label on one line, and the panel is wide enough for both. */
.ck-dd-search.has-action{display:flex;align-items:center;gap:var(--space-sm)}
.ck-dd-search.has-action > .ck-search{flex:1 1 auto;min-width:0}
.ck-dd-search.has-action > .ck-btn{flex:none;white-space:nowrap}
.ck-dd-list{
  display:flex;flex-direction:column;min-height:0;
  max-height:260px;overflow-y:auto;padding:var(--space-xs)}
.ck-dd-search > .ck-search{display:block}
.ck-dd-opt{width:100%;box-sizing:border-box;display:flex;align-items:center;gap:var(--space-sm);padding:7px 9px;border:0;background:0;border-radius:var(--radius-sm);font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground);cursor:pointer;text-align:left}
:is(.ck-dd-opt:hover,.ck-dd-opt.active,.ck-dd-opt[data-force~="hover"]){background:var(--hover-bg)}
.ck-dd-opt.selected,
.ck-dd-opt[aria-selected="true"]{background:var(--selected-bg);color:var(--selected-fg);font-weight:var(--font-weight-semibold)}
/* The option row was missing three of its six states (§ 14.3 check 4): no
   pressed, no focus-visible and no disabled, where .ck-menu-item and .ck-pick
   — the same shape of control — carry all three. Focus is an INSET rule for
   the same reason theirs is: a 3px outer ring is clipped by the list's 4px
   padding and reads as a smudge. */
:is(.ck-dd-opt:active,.ck-dd-opt[data-force~="active"]){
  background:var(--selected-bg);color:var(--selected-fg)}
:is(.ck-dd-opt:focus-visible,.ck-dd-opt[data-force~="focus"]){
  outline:0;background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
:is(.ck-dd-opt:disabled,.ck-dd-opt[aria-disabled="true"],.ck-dd-opt[data-force~="disabled"]){
  color:var(--input);background:0;cursor:not-allowed}
:is(.ck-dd-opt:disabled,.ck-dd-opt[aria-disabled="true"]){pointer-events:none}
/* THE MATCH MARK — the text in a list row that matches what is typed in the
   search box. One look everywhere a list is searched (universal check 82):
   --highlight-bg behind the matched letters, the row's own text colour kept.
   ckHighlight() writes mark.ck-match; the kit-built dropdown and menu write a
   plain <mark>, so both are covered. */
.ck-dd-opt mark,mark.ck-match{background:var(--highlight-bg);color:inherit;border-radius:var(--radius-xs);padding:0 1px}
.ck-dd-opt-label{flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap}
/* ── AN OPTION THAT NAMES WHERE IT LIVES — .ck-dd-opt.has-meta ───────────
   A row whose name alone is ambiguous (two workspaces can each hold an
   "Invoices" folder) carries a neutral .ck-pill naming its context on the
   trailing edge, the lane the multi forms keep free for a badge:

     .ck-dd-opt.has-meta > .ck-check + .ck-ico + .ck-dd-opt-label + .ck-pill > .ck-pill-label

   A grid, so the pill sits in the same column on every row and everything is
   centred on one line. The row is 40px at least with --space-sm above and
   below, so the pill never touches its edges. The pill is a label, not a
   control: it takes no pointer of its own, but a page that must stop it
   toggling the row can catch the click on it. 160px caps it; the label cuts
   head…tail with the full name in a tooltip (ckTruncate). On a phone the pill
   drops under the name and starts at the name's edge, not the checkbox's. */
.ck-dd-opt.has-meta{
  display:grid;grid-template-columns:auto auto minmax(0,1fr) auto;align-items:center;
  column-gap:var(--space-sm);row-gap:var(--space-xs);
  padding-block:var(--space-sm);min-height:var(--space-3xl)}
.ck-dd-opt.has-meta > .ck-dd-opt-label{grid-column:3}
.ck-dd-opt.has-meta > .ck-pill{grid-column:4;justify-self:end;--ck-pill-max:160px}
@media (max-width:480px){
  .ck-dd-opt.has-meta > .ck-pill{grid-column:3;grid-row:2;justify-self:start}}
/* Without a leading .ck-ico the icon column goes, so the name sits one gap
   from the checkbox instead of two. */
.ck-dd-opt.has-meta:not(:has(> .ck-ico)){grid-template-columns:auto minmax(0,1fr) auto}
.ck-dd-opt.has-meta:not(:has(> .ck-ico)) > .ck-dd-opt-label{grid-column:2}
.ck-dd-opt.has-meta:not(:has(> .ck-ico)) > .ck-pill{grid-column:3}
@media (max-width:480px){
  .ck-dd-opt.has-meta:not(:has(> .ck-ico)) > .ck-pill{grid-column:2}}
/* ── .ck-dd-opt-text / .ck-dd-opt-desc — an option that explains itself ──
   A picker whose options need a sentence each — an event list, a permission
   list — stacks a label over a description. The option's own layout is a row,
   so the two lines go in a -text wrapper and the row keeps its checkbox,
   its glyph and its tick where they are. The description is --muted-foreground
   at --text-sm, the same pairing every secondary line in the kit uses, so a
   screen never restyles a <small> to get here. */
.ck-dd-opt-text{display:flex;flex-direction:column;gap:var(--space-2xs);flex:1;min-width:0}
.ck-dd-opt-text > .ck-dd-opt-label{flex:none;white-space:normal}
.ck-dd-opt-desc{font:var(--font-weight-normal) var(--text-sm) var(--font-sans);
  color:var(--muted-foreground);line-height:1.45}
/* ── .ck-dd-opt.is-note — a line in the list that is not an option ───────
   "No results", "Everything is already selected". It sits in the list so it
   lands where the reader is looking, but it is not choosable: no pointer, no
   hover, no tick. .ck-empty is the right answer for an empty PANEL; this is
   the right answer for an empty LIST inside a panel that still has a search
   box above it. Pairs with check 59 — a row that cannot be chosen must not
   look choosable. */
.ck-dd-opt.is-note{cursor:default;color:var(--muted-foreground);
  font:var(--font-weight-normal) var(--text-sm) var(--font-sans)}
.ck-dd-opt.is-note:is(:hover,[data-force~="hover"]){background:transparent}

/* ═══ SEARCHABLE DROPDOWN — THE TWO MULTI FORMS ════════════════════════════
   Multi-select is not a separate component. It is a .ck-dd whose options are
   checkable, and it was a separate organism only by accident of history: it
   had grown its own trigger, its own option row and its own focus ring, all
   parallel to the dropdown's and already drifting from them. One parent means
   the panel, the search row, the empty state and every field state are defined
   once.

     .ck-dd.is-multi    The FIELD form. A trigger showing what is chosen as
                        chips, a checkable list, and a footer to commit.
     .ck-dd.is-picker   The PANEL form, always open. A title, a search, the
                        chosen set as chips with Clear all beside them, a
                        select-all band over a bordered list, and a footer.
                        For when choosing IS the task rather than one field
                        in a form.

   THE OPTION ROW IS A <label>, NOT A <button>. It was a button wrapping an
   <input class="ck-check"> — invalid, because a button may not contain
   interactive content, and browsers only vary in how they cope. It also meant
   clicking the row did nothing: the row and its checkbox were two separate
   targets, and only the 16px box worked.

   A label makes the whole row the checkbox's target natively — no JS, no click
   handler, nothing that can desync from the input's real state. It also makes
   the ARIA honest: a multi-select list is a group of checkboxes ("several of
   these can be true"), not a listbox, and each row announces as
   "<name>, checkbox, checked".

   Spec: references/components/dropdown.md */

/* ── The row, shared by both forms and by the single-select list ── */
.ck-dd-opt.has-check{gap:var(--space-sm);align-items:center}
.ck-dd-opt.has-check > .ck-check{flex:none}
/* The row lays the checkbox out and sets NO width, height or border on it, so
   the Checkbox atom stays the single definition of what a checkbox looks like.
   Sizing it here is the v2.0 defect the June feedback flagged. */

/* ── .is-multi — the field form ── */
/* THE MULTI TRIGGER IS A div[role="combobox"], NOT A <button>. Its chips carry
   dismiss buttons, and a button may not contain a button — the parser closes
   the outer one at the first nested <button>, so the trigger ended after the
   first chip and every chip, glyph and arrow after it spilled onto the page
   below. ARIA 1.2 puts role="combobox" on exactly this kind of element, so the
   correct markup is also the valid one. ckMultiTrigger() gives it the keyboard
   a button would have had. */
.ck-dd.is-multi .ck-dd-trigger{height:auto;min-height:36px;padding:var(--space-2xs) var(--space-md)}
.ck-dd.is-multi .ck-dd-trigger:focus-visible{outline:0;border-color:var(--primary);box-shadow:var(--focus-ring)}
.ck-dd.is-multi.size-sm .ck-dd-trigger{min-height:28px;padding:0 var(--space-sm)}
.ck-dd.is-multi.size-lg .ck-dd-trigger{min-height:44px;padding:var(--space-xs) var(--space-md)}
.ck-dd-tags{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-xs);flex:1;min-width:0}

/* ── .is-chip — ONE VALUE, AND IT SITS IN THE FIELD ───────────────────────
   The single-select counterpart of .is-multi, for a value that IS a badge
   rather than a word: a status, a reconcile outcome, a label. The list is a
   list of badges — rows carrying nothing but a .ck-pill, no checkbox and no
   tick — picked with the same --hover-bg and --selected-bg every other list in
   the kit uses. It is .ck-menu.is-badges-only.is-select put inside a field's
   panel, and it is deliberately the same object: the row, the states and the
   keyboard are the menu's, so the two cannot drift.

   THIS IS THE ONE EXCEPTION TO CHECK 68, AND IT IS NARROW. A multi-select
   field shows its placeholder and never its selection, because chips packed
   into a trigger grow the control as the selection grows — at 37 values the
   field is a paragraph of chips, and it moves under the pointer while it is
   being used. A single select cannot do that. There is exactly one chip, its
   width is capped at 152px like every other pill, and the field's height is
   fixed. The reason for the rule is absent here, so the rule is too — and the
   status you picked belongs where you will look for it, in the field.

   The chip carries its own dismiss, so the trigger is a div[role="combobox"]
   and not a <button>: a button may not contain a button. ckChipSelect() gives
   it the keyboard a button would have had, the same way ckMultiTrigger() does
   for the multi form. */
.ck-dd.is-chip > .ck-dd-trigger{gap:var(--space-sm)}
/* The chip REPLACES the placeholder; they are never both in the field. */
.ck-dd.is-chip.has-value > .ck-dd-trigger > .ck-dd-value{display:none}
.ck-dd.is-chip:not(.has-value) > .ck-dd-trigger > .ck-dd-chip{display:none}
/* It shrinks before the chevron does, and truncates rather than pushing it
   out. The auto margin keeps the chevron pinned to the trailing edge: the
   placeholder it replaces was flex:1 and held that lane open, so without it
   the chevron walked in behind the chip. A field’s chevron is always in the
   same place, whatever the field holds. */
.ck-dd-chip{flex:0 1 auto;min-width:0;margin-inline-end:auto}
/* The dismiss rests in the chip's own foreground and turns destructive on
   hover — the same cross as every chip in the kit. */
.ck-dd.is-chip .ck-dd-chip .ck-icon-btn{color:inherit}
.ck-dd.is-chip .ck-dd-chip .ck-icon-btn:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
/* Rows are badges: the badge is the whole content, so it sits at the leading
   edge and nothing trails it, while the full row width stays the target. The
   same two lines .ck-menu.is-badges-only carries, for the same reason. */
:is(.ck-dd.is-chip,.ck-dd-panel.is-chip) .ck-dd-opt{justify-content:flex-start}
:is(.ck-dd.is-chip,.ck-dd-panel.is-chip) .ck-dd-opt > .ck-pill{pointer-events:none}

/* ── .is-picker — the panel form ── */
.ck-dd.is-picker{
  position:static;
  display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden;
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground)}
.ck-dd-picker-head{
  display:flex;align-items:center;gap:var(--space-md);flex:none;
  padding:var(--space-base) var(--space-base) var(--space-md)}
.ck-dd-picker-title{
  flex:1;min-width:0;
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);
  color:var(--card-foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-dd-picker-body{
  display:flex;flex-direction:column;gap:var(--space-md);min-height:0;
  padding:var(--container-inset)}
/* The chips row owns Clear all. It belongs beside the thing it clears, not up
   in the title: a title says what the panel IS, and an action parked there
   reads as applying to the whole panel rather than to the chosen set. It also
   only has anything to do when there are chips, so it lives where they do. */
.ck-dd-picker-chips{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-xs)}
.ck-dd-picker-chips > .ck-btn{margin-inline-start:auto;flex:none}
.ck-dd-picker-chips:empty{display:none}
/* The list is its own bordered container, so the select-all band and the rows
   read as one block rather than as items floating on the panel. */
.ck-dd-picker-list{
  display:flex;flex-direction:column;min-height:0;overflow:hidden;
  border:1px solid var(--panel-border);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground)}
.ck-dd-picker-all{/* pairing-exempt: --muted is a header band, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */
  display:flex;align-items:center;gap:var(--space-sm);flex:none;
  min-height:44px;padding:var(--space-sm) var(--space-md);
  background:var(--muted);color:var(--foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  cursor:pointer}
.ck-dd-picker-all .ck-check{flex:none}
.ck-dd-picker-scroll{overflow-y:auto;min-height:0;max-height:320px}
/* Rows sit flush and are separated by a hairline, not by a gap or a radius. */
.ck-dd-picker-list .ck-dd-opt{
  border-radius:0;min-height:44px;
  padding:var(--space-sm) var(--space-md);
  }
/* In the picker the CHECKBOX carries selection, so a chosen row is not also
   tinted and bolded. The field form keeps both, because there the row is the
   only thing that shows state. Tinting every chosen row in a long list makes
   it stripey and doubles an indicator that is already unambiguous. Hover and
   focus still respond, so the row remains legible as a target. */
.ck-dd-picker-list .ck-dd-opt:is([aria-selected="true"],.selected,:has(> .ck-check:checked)){
  background:transparent;color:var(--foreground);font-weight:var(--font-weight-normal)}
.ck-dd-picker-list .ck-dd-opt:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
/* A label cannot take :focus-visible for its own control, so the ring is drawn
   when the checkbox inside it has focus. Inset, because a 3px outer ring is
   clipped by the list's own overflow. */
.ck-dd-opt:has(> .ck-check:focus-visible){
  background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
/* .ck-dd-picker-foot is gone, and with it Cancel / Confirm. A multi-select
   saves as it is used: ticking a row IS the change, so a Confirm afterwards
   asks the reader to agree to something already visibly done, and a Cancel
   promises an undo the panel cannot honour once the list behind it has
   already moved. The panel closes on an outside click or Escape, which is
   what every other non-modal overlay in the kit does (§ 14.3 check 49).

   This is the switch-versus-checkbox rule (check 48) applied to a panel: a
   control that applies instantly must not wear the chrome of one that waits
   for Save. */
.ck-dd-none{padding:16px 10px;text-align:center;font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}

/* ─── LOADERS (Atoms) ─────────────────────────────────────────────────────
   Same look and feel as the loaders in clipper-kit-v2.0.html and
   clipper-molecules-v2.html: a border spinner and a shimmering skeleton.
   Keyframes are ck- prefixed on purpose — flows already define their own
   `spin` and `shimmer` with different directions, and an unprefixed
   keyframe in this shared file would silently retime every one of them. */
@keyframes ck-spin{to{transform:rotate(360deg)}}
@keyframes ck-shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}

/* ─── SPINNER / SKELETON (Atoms) ───────────────────────────────────────────
   Spinner track is --skeleton-sheen, the same value the skeleton sweeps
   through, so the whole loading family reads as one thing. It was --input — a
   control-border grey at 3.17:1 — which made the ring compete with the arc
   that actually carries the motion. The arc stays --primary at 12.01:1, and
   that is the affordance; the track is context, exactly as with the progress
   bar. It is deliberately quiet, not invisible.

   Spec: references/components/feedback.md */
.ck-spinner{display:inline-block;/* without this it is inline, so width/height
     are ignored and a standalone spinner renders 4px wide */
  /* Tracks --icon-box: a spinner swapped in where an icon was must not resize
     the row it sits in. */
  width:var(--icon-box);height:var(--icon-box);flex:none;box-sizing:border-box;
  border:2px solid var(--skeleton-sheen);border-top-color:var(--primary);
  border-radius:var(--radius-full);animation:ck-spin .8s linear infinite}
.ck-spinner.size-sm,.ck-spinner.sm{width:16px;height:16px;border-width:2px}
.ck-spinner.size-lg,.ck-spinner.lg{width:28px;height:28px;border-width:3px}

.ck-skeleton{/* pairing-exempt: carries no text */
  background:linear-gradient(90deg,var(--muted) 25%,var(--skeleton-sheen) 50%,var(--muted) 75%);
  background-size:200% 100%;animation:ck-shimmer 1.5s ease-in-out infinite;
  border-radius:var(--radius-sm)}
.ck-skeleton-text{height:12px;border-radius:var(--radius-sm)}
.ck-skeleton-title{height:16px;border-radius:var(--radius-sm);max-width:40%}
.ck-skeleton-circle{border-radius:var(--radius-full)}
.ck-skeleton-row{display:flex;align-items:center;gap:var(--space-sm);padding:7px 9px}

@media (prefers-reduced-motion:reduce){
  .ck-spinner{animation-duration:2.4s}
  /* The chevron still needs to END in the right place, so the animation is
     shortened rather than removed — dropping it would leave the arrow pointing
     the wrong way. */
  .ck-dd.open .ck-dd-arrow,.ck-dd.is-closing .ck-dd-arrow{animation-duration:1ms}
  .ck-skeleton{animation:none;background:var(--skeleton-sheen)/* pairing-exempt: carries no text */}
}

/* ─── SEARCH LOADER ───────────────────────────────────────────────────────
   While a search is in flight the magnifier becomes a spinner and the list
   shows skeleton rows, so the panel keeps its height instead of collapsing
   to an empty state and snapping back. */
/* Centred with inset + auto margins, NOT transform: ck-spin animates
   transform, so a translateY(-50%) here would be replaced by the
   rotation — the spinner slid downward through every revolution and
   snapped back. Leave transform free wherever it is animated. */
/* The spinner and its loading swap are the .ck-search box's own rules now —
   the row holds a .ck-search rather than being one, so the offset these
   rules compensated for (the row's 8px padding) is gone. */
.ck-dd-loading{padding:4px}
.ck-dd-loading .ck-skeleton-row .ck-skeleton-text{flex:1}
.ck-dd-loading .ck-skeleton-row:nth-child(2) .ck-skeleton-text{max-width:70%}
.ck-dd-loading .ck-skeleton-row:nth-child(3) .ck-skeleton-text{max-width:85%}
.ck-dd-loading .ck-skeleton-row:nth-child(4) .ck-skeleton-text{max-width:55%}

/* Standalone search field loader, for search inputs outside a dropdown. */
/* ─── SEARCH BOX (Molecule) ────────────────────────────────────────────────
   A wrapper around a .ck-input, not a parallel control. It used to re-declare
   the input's border, fill, type, hover and focus — 7 of 8 declarations
   duplicated — which meant every input state and size had to be added twice.
   The input inside must carry .ck-input; this file only adds the trailing
   glyph, the clear button and the loading swap.

   Sizes: none of its own. Size the .ck-input inside it.

   Spec: references/components/search.md */
.ck-search{position:relative}
.ck-search > svg{position:absolute;inset-inline-end:10px;top:50%;transform:translateY(-50%);width:var(--icon-size);height:var(--icon-size);color:var(--muted-foreground);pointer-events:none}
.ck-search > .ck-spinner{position:absolute;inset-inline-end:10px;
  inset-block:0;margin-block:auto;display:none}
.ck-search.is-loading > svg{display:none}
.ck-search.is-loading > .ck-spinner{display:block}

/* Room for the glyph. The input keeps every other declaration from .ck-input. */
.ck-search > .ck-input{padding-inline-end:36px}
/* A search in a toolbar is 200px. Nothing sized it before, so it took the
   browser's default text-field width (196px in Chrome, other widths
   elsewhere) — a number no rule chose. The field still fills it. */
.ck-toolbar > .ck-search{inline-size:200px;flex:none}
/* A dropdown in a toolbar or a drawer's tool row is a control among controls,
   the same 200px as the toolbar search, not the field-width default. */
:is(.ck-toolbar,.ck-drawer-tools) > .ck-dd{inline-size:200px;flex:none}
/* ...unless it asks to be content-width: .is-compact means "as wide as what it
   shows", and this rule came later at the same specificity, so a compact
   filter in a toolbar stayed 200px (E-Invoice's All Countries / All Statuses). */
:is(.ck-toolbar,.ck-drawer-tools) > .ck-dd.is-compact{inline-size:auto}
.ck-toolbar > .ck-search > .ck-input{inline-size:100%}
/* The wide search, 300px, for a toolbar whose search is its main control
   (a member list, an activity log) rather than a filter beside others. */
.ck-toolbar > .ck-search.is-wide{inline-size:300px}
.ck-search.has-value > .ck-input{padding-inline-end:60px}

.ck-search > .ck-search-clear{padding:0;
  /* The magnifier is the right-most icon, occupying 10-26px from the trailing
     edge. The clear ends 8px before it starts: 26 + 8 = 34. */
  position:absolute;inset-inline-end:34px;top:50%;transform:translateY(-50%);
  width:24px;height:24px;display:none;place-items:center;
  border:0;background:0;color:var(--muted-foreground);cursor:pointer;
  border-radius:var(--radius-sm)
}
.ck-search > .ck-search-clear svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-search > .ck-search-clear:hover{background:var(--hover-bg);color:var(--foreground)}
.ck-search > .ck-search-clear:active{background:var(--selected-bg);color:var(--foreground)}
.ck-search > .ck-search-clear:disabled{color:var(--icon-color-disabled);background:0;cursor:not-allowed;pointer-events:none}
.ck-search > .ck-search-clear:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* Present once there is a value, visible once the field is hovered or focused. */
.ck-search.has-value > .ck-search-clear{
  display:grid;opacity:0;pointer-events:none;transition:opacity var(--duration-base)}
.ck-search.has-value:hover > .ck-search-clear,
.ck-search.has-value:focus-within > .ck-search-clear,
.ck-search.has-value[data-force~="hover"] > .ck-search-clear{
  opacity:1;pointer-events:auto}

/* ══════════════════════════════════════════════════════════════════════
   ICON CONTROLS — shape and tone only                            (Atom)
   ══════════════════════════════════════════════════════════════════════
   Rounded-square on hover and selection — § 7b puts an icon button in the
   CONTROL tier (--radius-md), and .size-xs in the INNER tier (--radius-sm).
   The Round tier is pills, counters, avatars, switch, radio, spinner and
   status dot; an icon control is not in it. Tone comes from --ck-icon-hover /
   --ck-icon-hover-fg, so one shape reads grey in neutral chrome, accent
   blue in navigation, red on a destructive action.

   This block sets NO width, height or aspect-ratio, deliberately. An
   earlier version forced a 32px frame with and broke every
   control that sizes itself — folder-panel affordances that collapse to
   zero width at rest, the labelled Move back row, pagers, segmented
   buttons. Shape is safe to impose across a codebase; size is not. Opt
   into a standard frame with .ck-icon-btn (.sm / .lg) where you want one.
   ══════════════════════════════════════════════════════════════════════ */
/* Icon hover is --hover-bg, the product-wide hover fill. § 8 says "--hover-bg
   — hover fill on any interactive row, item, or ghost control" and an icon
   button is a ghost control, so --icon-hover-bg was a second token for a
   decision already made once. It is retired into --hover-bg (rename map, § 2).

   This drives EVERY icon control through --ck-icon-hover: .ck-icon-btn, the
   header bar's .icon-btn, .ck-ico.is-interactive and the eight retired
   one-offs. They agree because they all read the same property. */
:root{ --ck-icon-hover: var(--hover-bg); --ck-icon-hover-fg: var(--foreground); }

/* The opt-in primitive — the only place a size is declared. */
/* ─── ICON BUTTON (Atom) ───────────────────────────────────────────────────
   A button whose whole label is its glyph, so it always needs an aria-label.

   Sizes share the control height scale (Rulebook §7a): 28 / 34 / 40, square.
   They were 26 / 32 / 38, which left an icon button 2px short of the button
   beside it in every toolbar. `.sm` / `.lg` are kept as aliases because pages
   already use them.

   Tone is set by two local properties rather than a variant class per colour,
   so a new tone is two declarations and no new rules.

   Spec: references/components/icon-button.md */
.ck-icon-btn{
  /* No local hover override: it reads --ck-icon-hover from :root, which is
     --hover-bg. This used to re-point the property locally, so an icon button
     hovered blue while the header bar's .icon-btn, carrying no override,
     hovered grey. The fix is one value in :root rather than an override here,
     so every icon control agrees by construction. */
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  width:36px;height:36px;padding:0;
  border:0;border-radius:var(--radius-md);
  background:transparent;color:var(--muted-foreground);cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base)
}
.ck-icon-btn svg{width:var(--icon-size);height:var(--icon-size);display:block}

/* Sizes — square, from the control height scale. */
.ck-icon-btn.size-sm,.ck-icon-btn.sm{width:28px;height:28px}
/* ── .ck-icon-btn.is-menu — an icon control that opens a menu ───────────
   A glyph and a disclosure caret in one control. .ck-icon-btn is an
   inline-grid with place-items:center, which is right for ONE glyph and puts
   a second one directly on top of the first — two icons drawn in the same
   cell, which is what a screen gets today if it wants an icon with a caret.

   A row instead, and the box widens to hold both while the height stays on
   the control scale. The caret is a glyph like any other, so it takes
   --icon-size and the control's own colour and states; nothing here repaints
   it. A screen that wants this without the variant reaches for .ck-btn.ghost,
   and then its glyphs render at --foreground instead of --icon-color and sit
   darker than every icon control beside them. */
.ck-icon-btn.is-menu{
  display:inline-flex;align-items:center;justify-content:center;
  gap:var(--space-2xs);
  width:auto;min-width:36px;padding-inline:var(--space-xs)}
.ck-icon-btn.is-menu.size-sm,.ck-icon-btn.is-menu.sm{min-width:28px}
.ck-icon-btn.is-menu.size-lg,.ck-icon-btn.is-menu.lg{min-width:44px}
.ck-icon-btn.size-lg,.ck-icon-btn.lg{width:44px;height:44px}
/* 24px — the WCAG 2.5.8 floor, for controls that sit inside another control:
   a chip's dismiss, a dialog's close, an inline remove. Do not go smaller;
   .ck-remove used to be 16px and failed the floor. */
/* 24px frame, 16px glyph — the § 7c pair, and the size every icon control
   outside the main left navigation uses. It sits exactly on the WCAG 2.5.8
   floor. */
.ck-icon-btn.size-xs{width:24px;height:24px;border-radius:var(--radius-sm)}
/* .size-2xs — an 18px frame holding the 12px small glyph (--icon-box-sm /
   --icon-size-sm), for the flows that need an icon control smaller than the
   24px .size-xs. Every state, tone and interaction is the base icon button's
   (hover, pressed, focus, disabled, .tone-*, .is-close, .is-selected) — only
   the geometry changes. The TARGET stays 24 x 24: a transparent ::before
   overflows the 18px box, because WCAG 2.5.8 sizes the target, not the paint. */
.ck-icon-btn.size-2xs{position:relative;
  width:var(--icon-box-sm);height:var(--icon-box-sm);border-radius:var(--radius-xs);
  --ck-ico-count-dot:10px;--ck-ico-count-inset:2px}
.ck-icon-btn.size-2xs svg{width:var(--icon-size-sm);height:var(--icon-size-sm)}
.ck-icon-btn.size-2xs::before{content:'';position:absolute;left:50%;top:50%;
  width:24px;height:24px;transform:translate(-50%,-50%)}
.ck-icon-btn:has(> .notif-badge){overflow:visible;position:relative;--ck-ico-count-inset:4px;--ck-ico-count-dot:16px}
/* A 24px button is the icon atom’s box with a target around it, so its count
   takes the icon’s 12px, not the 36px button’s 16px. The header bell was the
   case that showed it: a 16px bubble on a 24px control is not a count on an
   icon, it is two marks the same size sharing a corner. */
.ck-icon-btn.size-xs:has(> .notif-badge){--ck-ico-count-dot:12px;--ck-ico-count-inset:2px}

/* Tones */
.ck-icon-btn.tone-accent{--ck-icon-hover:var(--hover-bg);--ck-icon-hover-fg:var(--primary)}
/* A neutral close. The cross is in the icon library's DESTRUCTIVE set on
   purpose — it is the remove glyph — but it is also the close glyph for
   dialogs, drawers, popovers and toasts, and a close is not a destroy. The
   library's own note says "a neutral close is a tone class at the usage
   site"; this is that class. */
.ck-icon-btn.tone-neutral{color:var(--icon-color)}
.ck-icon-btn.tone-neutral > svg,.ck-icon-btn.tone-neutral > .ck-ico{color:inherit}
/* § 7c's destructive icon column, in full. It previously set only the two
   hover properties, so the glyph rested on --muted-foreground and hovered to
   the solid --destructive: no destructive rest colour, and a hover foreground
   that § 7c gives as --destructive-soft-foreground. A cross, a trash, a
   disconnect IS a destructive icon, so it reads destructive at rest.

   .ck-alert's dismiss is listed alongside rather than duplicating the values.
   A dismiss inside a PILL is deliberately different — neutral at rest,
   destructive on approach (§ 14.6 check 10) — and keeps its own rules. */
.ck-icon-btn.tone-danger{
  color:var(--icon-color-destructive);
  --ck-icon-hover:var(--destructive-bg);
  --ck-icon-hover-fg:var(--destructive-soft-foreground)}
/* The pressed rule needs the data-force hook as much as hover does. Without
   it only the generic .ck-icon-btn active rule matched under data-force, so a
   spec sheet painted a danger control’s pressed state in the BLUE selection
   fill — the one thing § 12.1 exists to stop. Real presses were always
   right; only the documented state was wrong, which is the harder kind to
   notice. */
.ck-icon-btn.tone-danger:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground)}
/* On a surface that is ALREADY --destructive-bg, the tint hover paints the
   button the colour it is sitting on and the glyph does not move either, so
   the control has no hover at all -- measured identical, rgb(252,235,235) on
   rgb(252,235,235). There it lifts to --background instead, the same move the
   segmented control's chosen option makes out of its trough, and the pressed
   state still goes solid --destructive, so the three steps stay distinct.
   Applies to the alert too, where the defect already existed. */
.ck-icon-btn.tone-danger.is-selected{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-icon-btn.tone-danger:is(:focus-visible,[data-force~="focus"]){
  box-shadow:var(--focus-ring-error)}
/* THE OVERLAY CLOSE — .ck-icon-btn.is-close
   The ✕ that closes a dialog, a drawer, a side panel or the notification
   panel. It rests in the default icon colour, --icon-color, like every other
   glyph in the head: closing is how you leave, not a warning, and a red cross
   standing in every overlay's corner read as one. It turns destructive only
   on approach — the --destructive-bg tint and the destructive glyph on hover,
   solid --destructive when pressed, the error focus ring — the destructive
   icon steps, minus the destructive rest colour.

   It is also the DEFAULT for any Close button in an overlay's head, so an
   overlay that forgets the class still gets it; that is why the contextual
   selectors are here and not only the class. The TOAST's dismiss takes it
   too, and so does the ALERT's: every dismiss cross in the kit rests neutral.
   Not for a pill's dismiss, which rests in the pill's own foreground. */
.ck-icon-btn.is-close,
:is(.ck-dialog-head,.ck-dialog-title,.ck-drawer-head,.ck-drawer-tools,.ck-notif-head,.ck-panel-head)
  > .ck-icon-btn:is([aria-label="Close"],[aria-label^="Close "]),
.ck-action-bar .ck-icon-btn[aria-label="Clear Selection"],
.ck-toast > .ck-icon-btn,.ck-alert > .ck-icon-btn{
  color:var(--icon-color);
  --ck-icon-hover:var(--destructive-bg);
  --ck-icon-hover-fg:var(--destructive-soft-foreground)}
/* Stated outright: the contextual selector outranks the base hover rule, so
   leaning on --ck-icon-hover alone would leave the glyph grey on hover. */
.ck-icon-btn.is-close:is(:hover,[data-force~="hover"]),
:is(.ck-dialog-head,.ck-dialog-title,.ck-drawer-head,.ck-drawer-tools,.ck-notif-head,.ck-panel-head)
  > .ck-icon-btn:is([aria-label="Close"],[aria-label^="Close "]):is(:hover,[data-force~="hover"]),
.ck-action-bar .ck-icon-btn[aria-label="Clear Selection"]:is(:hover,[data-force~="hover"]),
.ck-toast > .ck-icon-btn:is(:hover,[data-force~="hover"]),
.ck-alert > .ck-icon-btn:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-icon-btn.is-close:is(:active,[data-force~="active"]),
:is(.ck-dialog-head,.ck-dialog-title,.ck-drawer-head,.ck-drawer-tools,.ck-notif-head,.ck-panel-head)
  > .ck-icon-btn:is([aria-label="Close"],[aria-label^="Close "]):is(:active,[data-force~="active"]),
.ck-action-bar .ck-icon-btn[aria-label="Clear Selection"]:is(:active,[data-force~="active"]),
.ck-toast > .ck-icon-btn:is(:active,[data-force~="active"]),
.ck-alert > .ck-icon-btn:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground)}
.ck-icon-btn.is-close:is(:focus-visible,[data-force~="focus"]),
:is(.ck-dialog-head,.ck-dialog-title,.ck-drawer-head,.ck-drawer-tools,.ck-notif-head,.ck-panel-head)
  > .ck-icon-btn:is([aria-label="Close"],[aria-label^="Close "]):is(:focus-visible,[data-force~="focus"]),
.ck-action-bar .ck-icon-btn[aria-label="Clear Selection"]:is(:focus-visible,[data-force~="focus"]),
.ck-toast > .ck-icon-btn:is(:focus-visible,[data-force~="focus"]),
.ck-alert > .ck-icon-btn:is(:focus-visible,[data-force~="focus"]){
  box-shadow:var(--focus-ring-error)}
.ck-icon-btn.tone-primary{--ck-icon-hover:var(--primary);--ck-icon-hover-fg:var(--primary-foreground)}
/* ── .ck-icon-btn.is-primary — a FILLED icon action ─────────────────────
   The tone-* modifiers colour the glyph and tint on hover; this fills the
   control at rest. It is for the one compact primary action in a panel head
   or a rail — an add, a create — where a labelled .ck-btn would crowd the
   heading beside it. It is a fill, not a tone, so it composes with every
   size.
   .ck-folder-actions > .ck-icon-btn.is-add was this pattern scoped to one
   component; screens wanting it anywhere else had nowhere to go. */
.ck-icon-btn.is-primary{background:var(--primary);color:var(--primary-foreground)}
.ck-icon-btn.is-primary:is(:hover,[data-force~="hover"]){
  background:var(--primary-hover);color:var(--primary-foreground)}
.ck-icon-btn.is-primary:is(:active,[data-force~="active"]){
  background:var(--primary-active);color:var(--primary-foreground)}
.ck-icon-btn.is-primary:is(:disabled,[aria-disabled="true"]){
  background:var(--primary);color:var(--primary-foreground);opacity:var(--disabled-opacity)}
.ck-icon-btn.on-dark{--ck-icon-hover:var(--toolbar-dark-hover);--ck-icon-hover-fg:var(--toolbar-dark-foreground)}
/* Outlined — a bordered icon control, for a run of them that must read as a
   group against a busy row (pagination's first/prev/next/last). It takes the
   field stroke set, so it stays in sync with .ck-input and .ck-dd-trigger. */
/* .is-round — a full-circle frame, for a free-standing stack of actions down a
   rail (Edit Document's right-side actions). Everywhere else an icon control is
   the rounded square of § 7b. */
.ck-icon-btn.is-round{border-radius:var(--radius-full)}
.ck-icon-btn.is-outlined{
  /* pairing-exempt: --muted-foreground on --background is 3.44:1, the same
     declared de-emphasis .ck-empty carries. A nav control in a table footer
     must read quieter than the data above it; --foreground would make a run
     of four chevrons as loud as the rows they page through. */
  background:var(--background);color:var(--muted-foreground);
  border:var(--btn-border-width) solid var(--field-border)}
.ck-icon-btn.is-outlined:is(:hover,[data-force~="hover"]){
  background:var(--ck-icon-hover);color:var(--ck-icon-hover-fg);
  border-color:var(--input-border-hover)}
.ck-icon-btn.is-outlined:is(:active,[data-force~="active"]){
  background:var(--selected-bg);border-color:var(--primary)}
.ck-icon-btn.is-outlined:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  /* The same triplet .ck-btn's disabled state uses, so an outlined icon
     control and a disabled button read as the same kind of unavailable. */
  background:var(--muted);color:var(--muted-foreground);border-color:var(--input)}

.ck-icon-btn:is(:hover,[data-force~="hover"]){
  background:var(--ck-icon-hover);color:var(--ck-icon-hover-fg)}
.ck-icon-btn:is(:active,[data-force~="active"]){
  background:var(--selected-bg);color:var(--ck-icon-hover-fg)}
.ck-icon-btn.is-selected{background:var(--selected-bg);color:var(--selected-fg)}
/* OPEN — a button whose popover or menu is showing reads selected, driven by
   the aria-expanded it already sets, so the visual cannot drift from what a
   screen reader hears. Chevron pickers inside a field are excluded: their
   open state is the turned chevron. */
.ck-icon-btn[aria-expanded="true"]:not(.ck-input-tools > *){background:var(--selected-bg);color:var(--selected-fg)}
/* .tone-ai — the AI-feedback control, in the --ai family the .is-ai field
   already uses, so the button and the field it trains are one colour. On
   (feedback given) keeps the glyph in the AI tone rather than --primary. */
.ck-icon-btn.tone-ai{--ck-icon-hover:var(--ai);--ck-icon-hover-fg:var(--ai-foreground)}
.ck-icon-btn.tone-ai:is(.is-on,[aria-pressed="true"]){color:var(--ai-foreground)}
.ck-icon-btn.tone-ai:is(.is-on,[aria-pressed="true"]):is(:hover,[data-force~="hover"]){background:var(--ai);color:var(--ai-foreground)}
/* SWITCHED ON — tints the glyph and keeps the ground.
   .is-selected fills the button, which is right for "this is the one you picked
   out of several" — a view switcher, a segment. It is wrong for a toggle that
   is simply on: a filled square in a row of unfilled ones reads as the current
   SELECTION, so a bold button and an italic button both lit look like a choice
   between them rather than two independent switches.

   On keeps the ground and takes the glyph to --primary, so several can be on at
   once without the row turning into a block of fills. Hover and press still
   come from the base button, unchanged — being on does not stop it responding.

   aria-pressed is what makes it true; the class only paints it. */
.ck-icon-btn.is-on,
.ck-icon-btn[aria-pressed="true"]{background:0;color:var(--primary)}
.ck-icon-btn:is(.is-on,[aria-pressed="true"]):is(:hover,[data-force~="hover"]){
  background:var(--hover-bg);color:var(--primary)}
.ck-icon-btn:is(.is-on,[aria-pressed="true"]):is(:active,[data-force~="active"]){
  background:var(--selected-bg);color:var(--primary)}
.ck-icon-btn.tone-danger:is(.is-on,[aria-pressed="true"]){color:var(--destructive-soft-foreground)}
.ck-icon-btn.tone-success:is(.is-on,[aria-pressed="true"]){color:var(--success-soft-foreground)}

/* Disabled — tokens, not opacity, so the glyph keeps its contrast. */
.ck-icon-btn:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:transparent;color:var(--icon-color-disabled);cursor:not-allowed;pointer-events:none
}
.ck-icon-btn:is(:focus-visible,[data-force~="focus"]){outline:0;box-shadow:var(--focus-ring)}

/* Shape, applied across the hub. Nothing else. */
.ck-icon-btn,
.ck-remove,
.act-btn,
.de-tbl-ic,
.dialog-close,
.toast-close,
.file-search-clear,
.accordion-chevron,
.pag-btn,
.panel-close-btn,
.pin-btn,
.theme-btn,
.theme-opt,
.theme-seg-btn,
.nav-btn,
.ci-row-menu-btn,
.copy-inline,
.doc-card__icon,
.doc-card__action-btn,
.toolbar__btn,
.preview-toolbar__btn,
.topbar__info,
.topbar__icon-btn,
.search-clear,
.ck-search-clear,
.ab-btn,
.dropzone-file-remove,
.filter-drawer-close,
.filter-input-clear,
.filter-chip-x,
.td-expand,
.dfp-copy,
.dfp-tool-btn,
.dfp-multi-remove,
[data-set-theme][data-set-theme]{ border-radius: var(--radius-md); }

/* Tone, only where a control had no hover of its own. */
.ck-icon-btn:hover,
.ck-remove:hover,
.act-btn:hover,
.de-tbl-ic:hover,
.dialog-close:hover,
.toast-close:hover,
.file-search-clear:hover,
.accordion-chevron:hover,
.pag-btn:hover,
.panel-close-btn:hover,
.pin-btn:hover,
.theme-btn:hover,
.theme-opt:hover,
.theme-seg-btn:hover,
.nav-btn:hover,
.ci-row-menu-btn:hover,
.copy-inline:hover,
.doc-card__icon:hover,
.doc-card__action-btn:hover,
.toolbar__btn:hover,
.preview-toolbar__btn:hover,
.topbar__info:hover,
.topbar__icon-btn:hover,
.search-clear:hover,
.ck-search-clear:hover,
.ab-btn:hover,
.dropzone-file-remove:hover,
.filter-drawer-close:hover,
.filter-input-clear:hover,
.filter-chip-x:hover,
.td-expand:hover,
.dfp-copy:hover,
.dfp-tool-btn:hover,
.dfp-multi-remove:hover,
[data-set-theme]:hover{ background:var(--ck-icon-hover); color:var(--ck-icon-hover-fg); }

/* Focus. Only .ck-icon-btn had a ring; every unprefixed icon control in the
   list above fell through to the BROWSER's default outline — a blue box that
   is not a kit value and does not follow the theme. § 9 makes a missing focus
   ring a Sev-1, so the same list gets the same ring. */
.ck-icon-btn:focus-visible,
.ck-remove:focus-visible,
.act-btn:focus-visible,
.de-tbl-ic:focus-visible,
.dialog-close:focus-visible,
.toast-close:focus-visible,
.file-search-clear:focus-visible,
.accordion-chevron:focus-visible,
.pag-btn:focus-visible,
.panel-close-btn:focus-visible,
.pin-btn:focus-visible,
.theme-btn:focus-visible,
.theme-opt:focus-visible,
.theme-seg-btn:focus-visible,
.nav-btn:focus-visible,
.ci-row-menu-btn:focus-visible,
.copy-inline:focus-visible,
.doc-card__icon:focus-visible,
.doc-card__action-btn:focus-visible,
.toolbar__btn:focus-visible,
.preview-toolbar__btn:focus-visible,
.topbar__info:focus-visible,
.topbar__icon-btn:focus-visible,
.search-clear:focus-visible,
.ck-search-clear:focus-visible,
.ab-btn:focus-visible,
.dropzone-file-remove:focus-visible,
.filter-drawer-close:focus-visible,
.filter-input-clear:focus-visible,
.filter-chip-x:focus-visible,
.td-expand:focus-visible,
.dfp-copy:focus-visible,
.dfp-tool-btn:focus-visible,
.dfp-multi-remove:focus-visible,
[data-set-theme]:focus-visible{ outline:0; box-shadow:var(--focus-ring); }

.act-btn.danger, .de-tbl-ic.danger, .ck-remove, .is-destructive,
.dropzone-file-remove, .dfp-multi-remove, .filter-chip-x, .ab-error{
  --ck-icon-hover: var(--destructive-bg); --ck-icon-hover-fg: var(--destructive); }
.nav-btn, .theme-opt, .theme-seg-btn, [data-set-theme]{
  --ck-icon-hover: var(--hover-bg); --ck-icon-hover-fg: var(--primary); }
.ck-icon-btn.is-selected, .nav-btn.active, .theme-opt.active,
.theme-seg-btn.active, [data-set-theme].active{
  background:var(--selected-bg); color:var(--selected-fg); }
.preview-toolbar__btn{ --ck-icon-hover: var(--toolbar-dark-hover); --ck-icon-hover-fg: var(--toolbar-dark-foreground); }

/* ─── MIDDLE TRUNCATION (Utility) ───────────────────────────────────────
   A filename's tail carries the meaning — the extension, a trailing index —
   so end-truncation throws away the useful half. .ck-mid-trunc marks an
   element for ckMiddleTruncate(), which measures and inserts the ellipsis in
   the middle. The element must not also be clipping with text-overflow, or
   the two fight. */
.ck-mid-trunc{
  display:block;min-width:0;max-width:100%;
  white-space:nowrap;overflow:hidden;text-overflow:clip;
}

/* ─── TOAST (Molecule) ──────────────────────────────────────────────────
   Confirmation for a completed action, success or destructive. Four tones,
   all sharing one shape: icon, title, optional detail, dismiss.

     .ck-toast.is-success   .ck-toast.is-error
     .ck-toast.is-warning   .ck-toast.is-info

   Stack them in .ck-toast-stack, which is fixed to the corner. */
/* ─── TOAST (Organism) ────────────────────────────────────────────────────
   Transient, non-blocking, self-dismissing. For anything the user must answer,
   use a dialog.

   Eight tones, taken from the v2.0 molecules reference: four FEEDBACK
   (success / error / warning / info) and four ACTION (download / upload /
   undo / default). Each is the soft-semantic pattern — an --X-bg fill inside
   an --X-border stroke with the icon on the solid --X — rather than the
   leading 3px accent bar this kit had shipped, which read as a hairline on a
   --popover card and lost the tone almost entirely.

   Three things the reference did that are NOT copied, because the kit already
   has them and § 10 rule 1 says reuse beats minting:
     · its own .toast-btn        -> .ck-btn.size-sm (primary / outline)
     · its own .toast-progress   -> .ck-progress.size-sm
     · rgba(0,59,74,.06) tint    -> --primary-bg, pre-composited per theme
   Its 10px radius is not copied either: § 7b puts a toast in the panel tier,
   which is --radius-2xl for every panel in the kit.

   The stack is pointer-events:none so it never blocks the page beneath; each
   toast re-enables them for itself.

   Sizes: none. A toast is one width, because a row of different-width toasts
   reads as broken rather than as a hierarchy.

   Spec: references/components/toast.md */
.ck-toast-stack{position:fixed;inset-block-end:24px;inset-inline-end:24px;z-index:var(--z-toast);
  display:flex;flex-direction:column;gap:var(--space-md);max-width:min(420px,calc(100vw - 48px));pointer-events:none}

/* ONE ROW, CENTRED, WORDS FROM THE LEFT (§ 10 check 72). A notification is
   read in one pass, in passing: the eye lands on the glyph and runs straight
   along the words, which it cannot do if the two sit on different baselines.
   This was flex-start, which top-aligns a 16px glyph against a 13px line and
   reads as a misprint on the one-line toasts that are almost all of them — a
   screen had already worked around it locally, which is how a kit default
   gets found. */
.ck-toast{
  display:flex;align-items:center;text-align:start;gap:var(--space-md);box-sizing:border-box;
  padding:var(--container-inset-bar);
  border:1px solid var(--input);border-radius:var(--radius-2xl);
  background:var(--muted);color:var(--foreground);
  box-shadow:var(--shadow-panel);
  pointer-events:auto;opacity:0;transform:translateY(10px);
  transition:opacity var(--duration-base) ease,transform var(--duration-base) ease
  /* pairing-exempt: --muted is a toast tint, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */}
.ck-toast.is-shown{opacity:1;transform:translateY(0)}
.ck-toast.is-leaving{opacity:0;transform:translateY(-6px)}

/* Tone = fill + stroke + icon, one family each. */
.ck-toast.is-success{background:var(--success-bg);border-color:var(--success-border)}
.ck-toast.is-error{background:var(--destructive-bg);border-color:var(--destructive-border)}
.ck-toast.is-warning{background:var(--warning-bg);border-color:var(--warning-border)}
.ck-toast.is-info{background:var(--info-bg);border-color:var(--info-border)}
.ck-toast.is-upload{background:var(--cyan-bg);border-color:var(--cyan-border)}
.ck-toast.is-undo{background:var(--violet-bg);border-color:var(--violet-border)}
/* Download is the in-progress toast, so it takes the solid --primary stroke
   rather than a soft one — it is the only tone that is still working. */
.ck-toast.is-download{background:var(--primary-bg);border-color:var(--primary)}

.ck-toast-icon{width:var(--icon-box);height:var(--icon-box);flex:none;
  /* no margin-top nudge: check 72 centres the row, and a 1px push off that
     centre is the misprint the check exists to stop. */
  display:inline-grid;place-items:center;color:var(--muted-foreground)}
.ck-toast-icon svg{width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
.ck-toast.is-success .ck-toast-icon{color:var(--success)}
.ck-toast.is-error .ck-toast-icon{color:var(--destructive)}
.ck-toast.is-warning .ck-toast-icon{color:var(--warning)}
.ck-toast.is-info .ck-toast-icon{color:var(--info)}
.ck-toast.is-upload .ck-toast-icon{color:var(--cyan)}
.ck-toast.is-undo .ck-toast-icon{color:var(--violet)}
.ck-toast.is-download .ck-toast-icon{color:var(--primary)}

.ck-toast-content{flex:1;min-width:0}
.ck-toast-title{font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  margin-bottom:var(--space-2xs)}
.ck-toast-msg{font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground);line-height:1.45}
/* ── AN ACTION SITS IN THE ROW, NEVER UNDER IT ────────────────────────────
   Actions are .ck-btn.size-sm — there is no toast-only button.

   .ck-toast-actions used to live INSIDE .ck-toast-content carrying
   margin-top, so it stacked beneath the message, and .ck-toast-action pinned
   itself to the top of the row with align-self:flex-start. Between them a
   toast came apart: the glyph and the button on one line, the words on
   another, the dismiss somewhere else again. That is check 72's failure —
   a notification is read in one pass, and it cannot be if the eye has to find
   where the row went.

   So the action is a DIRECT CHILD of the toast and a member of its row,
   between the content and the dismiss. It takes no margin and no self
   alignment: the toast centres its row and every child accepts that. A long
   message still wraps to two lines inside .ck-toast-content — the ROW does
   not, which is the part that matters. */
.ck-toast{flex-wrap:nowrap}
.ck-toast-actions{display:flex;align-items:center;gap:var(--space-sm);flex:none}
.ck-toast-action{flex:none}
/* The dismiss is a DIRECT child, which is what keeps the destructive tone off
   the buttons inside .ck-toast-actions -- an Undo is not a destructive act. */
/* Progress is .ck-progress.size-sm, spanning the toast under the copy. */
.ck-toast-progress{margin-top:var(--space-sm)}
@media (prefers-reduced-motion: reduce){
  .ck-toast{transition:none;opacity:1;transform:none}
}

/* ─── MENU (Organism) ─── */
/* A menu floats over the panel it came from, so it needs a shadow that
   actually lifts it - the ambient --shadow-lg alone reads flat against a
   light panel. Weighted downward, since it drops from its trigger. */
/* ─── MENU (Organism) ──────────────────────────────────────────────────────
   An action list anchored to a trigger. Shares its panel treatment with
   .ck-dd-panel; the searchable variant shares its search row.

   Behaviour is ClipperMenu in clipper-kit.js.

   Spec: references/components/menu.md */
.ck-menu{position:fixed;z-index:var(--z-dropdown);min-width:208px;
  background:var(--popover);color:var(--popover-foreground);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  box-shadow:var(--shadow-panel);padding:var(--container-inset-xs)}
.ck-menu.size-sm{min-width:168px}
.ck-menu.size-lg{min-width:280px}
/* ── EVERY FLOATING PANEL STAYS INSIDE THE WINDOW ──────────────────────────
   A panel wider than the viewport pushes the document sideways and the page
   grows a horizontal scrollbar — the panel is then partly unreachable AND it
   has broken the page behind it. A panel taller than the viewport is simply
   cut off at the bottom with no way to reach the rest.

   The floors are set here so containment survives even if a consumer forgets
   to call the placement helper; ckPlaceOverlay() in clipper-kit.js does the
   rest — it never covers the control that opened the panel, it flips to the
   side with the most room, and it writes the max-height for that side.

   min-width above is a MINIMUM, so it wins over max-width on a narrow screen.
   The clamp() keeps the floor from re-introducing the overflow it prevents.
   Rulebook 12a. */
.ck-menu,.ck-dd-panel.is-portal{
  box-sizing:border-box;
  min-width:min(208px,calc(100vw - 2 * var(--overlay-margin)));
  max-width:calc(100vw - 2 * var(--overlay-margin));
  max-height:calc(100vh - 2 * var(--overlay-margin));
  overflow-y:auto;overscroll-behavior:contain}
.ck-menu.size-sm{min-width:min(168px,calc(100vw - 2 * var(--overlay-margin)))}
/* A list with search and its action on one row is wider, so neither is
   cramped; after the floor above, which it outranks only by coming later. */
.ck-dd-panel.has-search-action{min-width:min(360px,calc(100vw - 2 * var(--overlay-margin)))}
.ck-menu.size-lg{min-width:min(280px,calc(100vw - 2 * var(--overlay-margin)))}

.ck-menu-item{width:100%;display:flex;align-items:center;gap:var(--space-sm);padding:9px 10px;
  border:0;background:0;border-radius:var(--radius-sm);
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--popover-foreground);
  cursor:pointer;text-align:start}
.ck-menu-item:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
.ck-menu-item:active{background:var(--selected-bg)}
/* An inset rule rather than --focus-ring: a 3px outer ring on a menu item
   would be clipped by the panel's 4px padding and read as a smudge. */
.ck-menu-item:focus-visible{outline:0;background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
.ck-menu-item[aria-selected="true"],.ck-menu-item.selected{background:var(--selected-bg);color:var(--selected-fg)}
.ck-menu-item:disabled,.ck-menu-item[aria-disabled="true"]{
  color:var(--input);cursor:not-allowed;pointer-events:none;background:0}
.ck-menu-item svg{width:16px;height:16px;flex:none;color:var(--muted-foreground)}
.ck-menu-item:is(:hover,[data-force~="hover"]) svg{color:var(--primary)}
.ck-menu-item:disabled svg,.ck-menu-item[aria-disabled="true"] svg{color:var(--icon-color-disabled)}
.ck-menu-item.is-destructive{color:var(--destructive-soft-foreground)}
.ck-menu-item.is-destructive:is(:hover,[data-force~="hover"]){background:var(--destructive-bg)}
.ck-menu-item.is-destructive svg{color:var(--destructive-soft-foreground)}

/* A header block at the top of a menu — an identity for a profile menu, or a
   title for a long one. It is NOT a menu item: no hover, no focus, not in the
   tab order, and it sits outside .ck-menu-list so the list still scrolls on
   its own. Ported from the v2.0 profile menu. */
.ck-menu-header{
  display:flex;align-items:center;gap:var(--space-sm);flex:none;
  padding:var(--space-md);
  border-bottom:1px solid var(--border);
  background:var(--menu-header);color:var(--menu-header-foreground)}
.ck-menu-header-info{display:flex;flex-direction:column;min-width:0;gap:var(--space-2xs)}
.ck-menu-header-name{
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  color:var(--menu-header-foreground);overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-menu-header-meta{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground);overflow:hidden;text-overflow:clip;white-space:nowrap}
/* A menu carrying a header drops its own padding, so the header meets the
   panel edge instead of floating inside a 4px frame. */
.ck-menu:has(.ck-menu-header){padding:0;overflow:hidden}
.ck-menu:has(.ck-menu-header) .ck-menu-list{padding:var(--space-xs)}

.ck-menu-sep{height:1px;margin:4px 0;background:var(--border)/* pairing-exempt: carries no text */}
/* Symmetric 5px, was 6px top / 4px bottom — a 2px asymmetry with no
   stated reason reads as a mistake and QA cannot tell it from one. */
.ck-menu-label{padding:5px 10px;font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);color:var(--muted-foreground)}
/* No caps: a section label is written in Title Case ("Favorites (3)", "Saved
   Filters") and shown as written — the design owner's rule for every label. */

/* ─── SELECTION MENU — .ck-menu.is-select [.is-multi] ─────────────────────
   A menu whose rows are VALUES rather than actions. This is where the
   multi-select dropdown lives: it was a second component with its own
   classes, and it was the same object — a floating panel of rows anchored to
   a trigger. One component, four orthogonal choices:

     .is-select              rows are values; picking one is a choice, not an act
     .is-multi               each row carries a checkbox; several can be true
     .ck-menu-search child   adds the search row. Markup, not a class
     .ck-menu-item-badge     adds a trailing badge to a row. Markup, not a class

   So "multi-select with search and badges" is `.is-select.is-multi` plus two
   children, not a variant with a name of its own — which is why they compose
   instead of multiplying into eight rules.

   AN ACTION MENU AND A SELECTION MENU DIFFER IN ROLE, NOT JUST IN PAINT.
   An action list is role="menu" with role="menuitem" rows: activating one
   does something and the menu closes. A selection menu is role="listbox"
   with role="option" rows and aria-selected, and a MULTI one adds
   aria-multiselectable="true" and stays open while you pick. Getting that
   wrong is the defect, not the tick.

   THERE IS NO TICK GLYPH. A single-select row used to carry a circle-check at
   its trailing edge; it is gone. The row's own surface is the mark — the same
   --selected-bg / --selected-fg pair a tree row, a dropdown option and a menu
   item already use — so selection is stated once, in the kit's one selection
   language, instead of with a glyph unique to this component. It also frees
   the trailing lane for the badge, which is real content. */
.ck-menu.is-select .ck-menu-item[aria-selected="true"],
.ck-menu.is-select .ck-menu-item.selected{
  background:var(--selected-bg);color:var(--selected-fg);
  font-weight:var(--font-weight-semibold)}
.ck-menu.is-select .ck-menu-item[aria-selected="true"]:is(:hover,[data-force~="hover"]),
.ck-menu.is-select .ck-menu-item.selected:is(:hover,[data-force~="hover"]){
  background:var(--selected-bg);color:var(--selected-fg)}
/* A leading glyph is decoration, not the mark. */
.ck-menu.is-select .ck-menu-item > svg:first-child{color:var(--muted-foreground)}
/* ...except a destructive item, whose icon stays destructive with its label. */
.ck-menu.is-select .ck-menu-item.is-destructive > svg:first-child{color:var(--destructive-soft-foreground)}

/* Multi select: a real checkbox per row, and the Checkbox atom stays the one
   definition of a checkbox — the v2.0 row sized the box itself and fought
   .ck-check's 16px. */
.ck-menu.is-select.is-multi .ck-menu-item{gap:var(--space-sm)}
/* A multi-select menu item is a <label>, so the whole row toggles its box —
   it was a <button> wrapping an <input>, which is invalid and left only the
   16px box clickable (§ 14 check 38). A label cannot take :focus-visible for
   its own control, so the ring is drawn from the checkbox inside it. */
.ck-menu-item:has(> .ck-check:focus-visible){
  background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
.ck-menu.is-select.is-multi .ck-menu-item > .ck-check{flex:none;pointer-events:none}
/* No selected fill in the multi form — the ticked box is the mark, and a
   filled row on top of it fights the hover state for the same pixels. */
.ck-menu.is-select.is-multi .ck-menu-item[aria-selected="true"]{
  background:0;color:var(--popover-foreground);font-weight:var(--font-weight-normal)}
.ck-menu.is-select.is-multi .ck-menu-item[aria-selected="true"]:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}

/* A trailing badge on a row — a count, a status, a tone. It goes at the
   trailing edge and never replaces the label: the label says what the row is
   and the badge qualifies it. */
.ck-menu-item-badge{margin-inline-start:auto;flex:none}
/* A trailing VALUE that is plain text — a dial code, a unit — reads as the
   row's secondary text rather than a second label. */
.ck-menu.is-select .ck-menu-item-badge:not(:has(*)){
  color:var(--muted-foreground);font-variant-numeric:tabular-nums}
/* A row may lead with an image instead of a glyph — a flag, a logo. Every
   image takes the same box, the icon box wide and the glyph high, cropped to
   fill: flags come in different proportions (the UK's is 2:1, Japan's 3:2),
   and at their own widths each label started somewhere different. */
.ck-menu-item > img:first-child{
  width:var(--icon-box);height:var(--icon-size);object-fit:cover;flex:none;
  border-radius:2px;box-shadow:0 0 0 1px var(--border)/* pairing-exempt: a hairline around an image, carries no text */}
/* A badge's own glyph is not a menu glyph, so the menu's icon colouring must
   not reach into it. */
.ck-menu-item .ck-menu-item-badge svg{color:inherit}
.ck-menu-item:is(:hover,[data-force~="hover"]) .ck-menu-item-badge svg{color:inherit}

/* ── .is-badges-only — the row IS the badge ──────────────────────────────
   A list whose rows carry nothing but a badge: picking a status, a label, a
   reconcile outcome. The badge is not qualifying a label here, it is the
   whole content, so it moves to the LEADING edge and the auto margin that
   normally pushes it right is dropped.

   Every other rule still applies — the row is still a .ck-menu-item, so
   hover, selection, focus, disabled and the keyboard are the ones the menu
   already has, and the multi form still uses a real .ck-check. The badge
   itself is the .ck-pill atom, untouched.

   Available in all four forms, the same as the labelled list:
     .is-select                        single, no search
     .is-select.has-search             single, with search
     .is-select.is-multi               multi,  no search
     .is-select.is-multi.has-search    multi,  with search */
.ck-menu.is-badges-only .ck-menu-item-badge{margin-inline-start:0}
/* Nothing trails the badge, so the row shrinks to its content and the whole
   width stays a target. */
.ck-menu.is-badges-only .ck-menu-item{justify-content:flex-start}
/* A selected badge row takes the kit's selection FILL, like every other
   selected row in the product. It carried an inset 2px --primary rail instead,
   on the argument that a tint behind a toned pill muddies both — but that rail
   was a selection language this one variant used and nothing else did, and one
   list reading differently from every other list costs more than the tint.
   --selected-bg is light enough that the pill's own tone still separates. */
.ck-menu.is-badges-only.is-select .ck-menu-item:is([aria-selected="true"],.selected){
  background:var(--selected-bg);color:var(--selected-fg)}
.ck-menu.is-badges-only.is-select .ck-menu-item:is([aria-selected="true"],.selected):is(:hover,[data-force~="hover"]){
  background:var(--selected-bg);color:var(--selected-fg)}

/* A select-all row above a multi list. Same band as the picker's, because it
   is the same control in a different container. */
.ck-menu-all{
  display:flex;align-items:center;gap:var(--space-md);flex:none;
  padding:var(--space-sm) var(--space-md);
  
  background:var(--muted)/* pairing-exempt: --foreground on --muted is 15.87:1, the declared header pairing */;
  color:var(--foreground);
  font:var(--font-weight-semibold) var(--text-md) var(--font-sans)}
.ck-menu:has(.ck-menu-all){padding:0;overflow:hidden}
.ck-menu:has(.ck-menu-all) .ck-menu-list{padding:var(--space-xs)}

/* ─── CENTRED DIALOG (Organism) ─── */
/* ─── DIALOG (Organism) ────────────────────────────────────────────────────
   Sizes are widths, not heights — a dialog is as tall as its content up to the
   viewport. .wide is kept as an alias for .size-lg.

   Every text part reads --popover-foreground, not --foreground. The two are
   identical in all three themes today, so the old crossed pairing showed
   nothing; it would break the moment the popover surface diverges.

   Spec: references/components/dialog.md */
.ck-dialog-scrim{position:fixed;inset:0;z-index:var(--z-overlay);display:grid;place-items:center;padding:24px}
/* Shown by removing [hidden]; the display above would otherwise beat it. */
.ck-dialog-scrim[hidden]{display:none}
/* The tint is painted on ::before with --scrim-opacity rather than as an
   alpha colour, so the token stays 6-digit hex. It cannot go on the scrim
   itself: the dialog is its child and would fade with it.

   AND THE PANEL MUST BE POSITIONED, or the tint lands on top of it. An
   absolutely-positioned ::before paints above every NON-positioned descendant
   no matter what order they are declared in, so a static .ck-dialog rendered
   as a washed-out grey card — the scrim over the dialog instead of behind it.
   position + z-index puts the panel back in front. Anything else dropped
   inside a scrim needs the same. */
.ck-dialog-scrim::before{content:'';position:absolute;inset:0;background:var(--scrim);opacity:var(--scrim-opacity)/* pairing-exempt: carries no text */}
.ck-dialog{
  position:relative;z-index:1;
  width:100%;max-width:460px;max-height:calc(100vh - 48px);overflow:auto;
  background:var(--popover);color:var(--popover-foreground);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  box-shadow:var(--shadow-overlay)
}
/* Same guard for anything else placed inside a scrim. */
.ck-dialog-scrim > *{position:relative;z-index:1}
/* A dialog is ALWAYS behind a full-screen overlay in the product. A spec page
   still has to show one, and six fixed full-window scrims stacked on a
   documentation page is not a specimen, it is six blackouts. .is-inline is the
   same scrim bounded to its own box: the dim, the centring and the padding are
   identical, only the containing block changes. Documentation and preview
   surfaces only — a product dialog uses the real thing. Rulebook 4. */
.ck-dialog-scrim.is-inline{
  position:relative;inset:auto;z-index:var(--z-base);
  padding:var(--space-lg);
  border-radius:var(--radius-2xl);overflow:hidden}
.ck-dialog.size-sm{max-width:380px}
.ck-dialog.size-lg,.ck-dialog.size-full{max-width:calc(100vw - 48px)}
/* A fourth width, between the 460px default and the full-bleed size-lg, for a
   dialog holding a LIST rather than a sentence and two buttons. Declared on
   .ck-dialog itself, not only on the popover variant: a size is a size, and a
   size that exists on one variant only is a class that silently does nothing
   everywhere else. */
.ck-dialog.size-xl{max-width:560px}
/* A fifth width, for a dialog whose SUBTITLE is one long sentence: the head
   reads as a title and a line, and at 560 that line wraps under itself. The
   webhook authentication dialog's needs 549px of text; 640 leaves it room. */
.ck-dialog.size-2xl{max-width:640px}
/* A table's width: a dialog whose body is a table of rows with controls in
   them — a name, a dropdown, a choice — needs the columns to sit side by side
   without the name truncating to nothing. */
.ck-dialog.size-3xl{max-width:820px}

/* ── A DIALOG THAT HOLDS A LIST — .ck-dialog.is-fixed ──
   A fixed height. The head and the foot stay put, and only the body scrolls,
   so the title and the actions are always on screen however long the list
   runs. A plain dialog scrolls as one piece, taking its own close button and
   its Confirm out of view with the content.

   When the body holds a table, the body does NOT scroll: the table scrolls in
   its own .ck-table-scroll, whose edges carry no padding. A sticky header
   pins to its scrollport, which includes the scroller's padding, so inside a
   padded scrolling body the rows would scroll up through the strip above the
   pinned header — the permission matrix's bug. */
.ck-dialog.is-fixed{display:flex;flex-direction:column;overflow:hidden;
  height:min(640px, calc(100vh - 48px))}
.ck-dialog.is-fixed > :is(.ck-dialog-head,.ck-dialog-foot){flex:none}
.ck-dialog.is-fixed > .ck-dialog-body{flex:1;min-height:0;overflow:auto}
.ck-dialog.is-fixed > .ck-dialog-body:has(> .ck-table-scroll){overflow:hidden}

.ck-dialog-head{display:flex;align-items:flex-start;gap:var(--space-sm);padding:var(--container-inset);border-bottom:1px solid var(--border)}
.ck-dialog-title{font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--popover-foreground);flex:1;margin:0}
.ck-dialog-sub{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);margin-top:0}
.ck-dialog-body{padding:var(--container-inset);display:flex;flex-direction:column;gap:var(--space-base)}
.ck-dialog-foot{display:flex;justify-content:flex-end;gap:var(--space-sm);padding:var(--container-inset);border-top:1px solid var(--border)}
/* A footer carrying a low-emphasis escape hatch beside its buttons — "Discard
   changes" next to Go back / Save. The link goes to the LEADING edge and the
   buttons stay trailing, so the destructive-ish escape is never the thing a
   user's hand lands on by reflex. */
.ck-dialog-foot.is-split{justify-content:space-between;align-items:center}
/* The buttons stay on the trailing edge even when the leading action is
   hidden (Change Logo with no logo to remove): space-between alone put Cancel
   and Upload on the left. */
:is(.ck-dialog-foot,.ck-form-foot,.ck-panel-foot).is-split > .ck-form-foot-actions{margin-inline-start:auto}
/* ONLY THE BODY SCROLLS. A dialog taller than the window used to scroll as a
   whole, so its title, its search and its Cancel / Update went off with the
   content — Update Allow List with 253 addresses showed neither the field nor
   the buttons once scrolled. The head and the foot stay; the body takes what
   is left and scrolls. Popovers keep their own one-box layout. */
.ck-dialog:not(.is-popover){display:flex;flex-direction:column}
.ck-dialog:not(.is-popover) > :is(.ck-dialog-head,.ck-dialog-foot){flex:none}
.ck-dialog:not(.is-popover) > .ck-dialog-body{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain}
/* ...and a toolbar that opens the body (a search, Clear All) stays at its top
   while the list under it scrolls. It reaches up over the body's top inset and
   paints the popover ground across the gap below it, so rows pass under it
   rather than showing through. */
.ck-dialog:not(.is-popover) > .ck-dialog-body:not(.is-flush) > .ck-toolbar:first-child{
  position:sticky;top:calc(-1 * var(--container-inset));z-index:2;
  margin-block-start:calc(-1 * var(--container-inset));padding-block-start:var(--container-inset);
  background:var(--popover);box-shadow:0 var(--space-base) 0 var(--popover)}
.ck-dialog-foot-actions{display:flex;align-items:center;gap:var(--space-sm)}

/* ═══ .ck-dialog.is-popover — the ANCHORED, NON-MODAL variant ═════════════
   A small panel holding controls — a filter, a quick edit, a confirmation —
   opened from a trigger rather than over the whole page. Not a tooltip (which
   holds no controls) and not a menu (which is a list).

   IT IS THE SAME COMPONENT, AND THAT IS THE POINT: the surface, the stroke,
   the radius, the title, the body, the footer and the tone plate are the
   dialog's. Only three things change — where it sits, how wide it is, and
   whether it blocks the page.

   WHAT MUST NOT BE SHARED IS THE MODALITY. A dialog is modal: it carries
   aria-modal="true", tints its scrim, traps focus, and inerts the rest of the
   page, because the user must answer it before going on. A popover is none of
   those. Its scrim is a transparent click-catcher, focus is free to leave, and
   ClipperOverlay must NOT be pointed at it — trapping focus in a non-modal
   panel strands a keyboard user in a box they never asked to enter.

   So: one component for everything you can see, two behaviours for what you
   can do. Use role="dialog" WITHOUT aria-modal on a popover.

   Spec: references/components/dialog.md § Popover */
/* Anchored, so the scrim neither centres nor tints. It exists only to catch
   the click that dismisses. */
.ck-dialog-scrim.is-popover{display:block;padding:0;background:transparent}
.ck-dialog-scrim.is-popover::before{content:none}
.ck-dialog.is-popover{
  position:fixed;width:320px;max-width:calc(100vw - 32px);max-height:none;
  box-shadow:var(--shadow-panel);
  padding:var(--container-inset);
  display:flex;flex-direction:column;gap:var(--space-md)}
.ck-dialog.is-popover.size-sm{width:240px;max-width:calc(100vw - 32px);padding:var(--container-inset-sm);gap:var(--space-sm)}
.ck-dialog.is-popover.size-lg{width:400px;max-width:calc(100vw - 32px)}
/* A fourth width, for a popover that holds a LIST rather than a field or two:
   the split instructions run to a sentence each, and at 400px every one of
   them wrapped to three lines. */
.ck-dialog.is-popover.size-xl{width:560px;max-width:calc(100vw - 32px)}
/* One padded box, not three banded ones: at this size a rule above the footer
   spends more height on the line than the row it separates. The dialog's own
   parts keep their type and their roles — only the chrome is dropped. */
.ck-dialog.is-popover .ck-dialog-head,
.ck-dialog.is-popover .ck-dialog-body,
.ck-dialog.is-popover .ck-dialog-foot{padding:0;border:0}
.ck-dialog.is-popover .ck-dialog-body{gap:var(--space-sm)}
.ck-dialog.is-popover .ck-dialog-foot{gap:var(--space-sm)}
/* A list the popover HOLDS spans its inset, so the rows read as one block
   rather than a stack floating inside a box. Only as a direct child of the
   body: nested deeper it is part of something else's layout, not the
   popover's. */
.ck-dialog.is-popover .ck-dialog-body > .ck-accordion,
.ck-dialog.is-popover .ck-dialog-body > .ck-menu{
  margin-inline:calc(var(--space-base) * -1)}
.ck-dialog.is-popover.size-sm .ck-dialog-body > .ck-accordion,
.ck-dialog.is-popover.size-sm .ck-dialog-body > .ck-menu{
  margin-inline:calc(var(--space-md) * -1)}
/* With no head band to lay the row out, the title carries the row itself, so
   a close button still lands at the trailing edge instead of beside the words.
   .grow marks whatever should take the slack. */
.ck-dialog.is-popover .ck-dialog-title{display:flex;align-items:center;gap:var(--space-sm)}
.ck-dialog.is-popover .ck-dialog-title > .grow{flex:1;min-width:0}

/* ── The dialog's tone plate — .ck-dialog-icon ──
   A tinted rounded square holding one 16px glyph, leading the title. It tells
   the user what KIND of dialog this is before they read a word: a deletion, a
   confirmation, a warning, a result, an explanation.

   Each tone is the soft-semantic triplet — a --*-bg plate with a
   --*-soft-foreground glyph — so it matches the inline alert a dialog body
   often carries. --radius-md, not --radius-full: a round plate reads as an
   avatar, and this is not a person.

   THE PLATE IS NEVER THE ONLY SIGNAL. The title still says "Delete 3
   invoices?" and the destructive button still says "Delete invoices". A user
   who cannot separate red from amber must get the same warning from the
   words. */
.ck-dialog-icon{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  width:36px;height:36px;border-radius:var(--radius-md);
  background:var(--muted);color:var(--foreground)/* pairing-exempt: --muted is a tone plate, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */}
.ck-dialog-icon svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-dialog-icon.is-destructive{background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-dialog-icon.is-warning{background:var(--warning-bg);color:var(--warning-soft-foreground)}
.ck-dialog-icon.is-success{background:var(--success-bg);color:var(--success-soft-foreground)}
.ck-dialog-icon.is-info{background:var(--info-bg);color:var(--info-soft-foreground)}
/* The head becomes a three-part row: plate, the title-and-body stack, close.
   The copy stacks under the title rather than beside the plate, so a long
   explanation does not wrap into a narrow column next to the glyph. */
.ck-dialog-head.has-icon{align-items:flex-start;gap:var(--space-md)}
.ck-dialog-head-text{display:flex;flex-direction:column;gap:var(--space-xs);flex:1;min-width:0}
/* Inside the head the flex gap spaces the title from its subtitle, and the
   head's own padding ends the run. .ck-dialog-sub zeroed only its TOP margin,
   so a subtitle written as a <p> kept the browser's 1em below it — 12px of
   dead space between the subtitle and the divider, measured on the webhook
   authentication dialog as 27px where the head pads 14. */
.ck-dialog-head-text > .ck-dialog-sub{margin-block:0}
/* A dialog whose whole message lives in the head needs no rule under it — the
   body it would separate is not there. */
.ck-dialog-head.is-only{border-bottom:0;padding-block-end:0}
/* With no body there is nothing for the foot's rule to separate either, so a
   confirmation reads as one card: message, then its actions. */
.ck-dialog-head.is-only + .ck-dialog-foot{border-top:0}

/* ── A row list inside a dialog body — .ck-dialog-rows ──
   Label on the left, its value or shortcut on the right, one per line. For a
   reference dialog: keyboard shortcuts, a summary of what an action will do.
   Rules between rows, none above the first or below the last, so the list
   does not double up on the body's own padding. */
.ck-dialog-rows{display:flex;flex-direction:column}
.ck-dialog-row{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-lg);
  padding-block:var(--space-sm);
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--popover-foreground)}
/* no line between dialog rows — a list carries no separators (check 80) */
.ck-dialog-row > .ck-kbd-combo,.ck-dialog-row > .ck-kbd{flex:none}

/* ─── SWITCH BLOCK (Molecule) ─────────────────────────────────────────────
   A toggle with a title and an explanation, laid out two-up. Used by folder
   Automation settings, where every row needs the "what does this do" line. */
/* Stacked list: rows sit on top of each other, switch pinned to the far
   right, title semi-bold with its subtext in regular directly beneath. */
.ck-switch-list{display:flex;flex-direction:column}
.ck-switch-list > .ck-switch-block{padding:16px 0}
.ck-switch-list > .ck-switch-block + .ck-switch-block{border-top:1px solid var(--border)}
.ck-switch-list > .ck-switch-block:first-child{padding-top:0}
.ck-switch-list > .ck-switch-block:last-child{padding-bottom:0}
/* .is-plain — a long list of settings on its own, not in a card: no rules
   between blocks, 24px apart, and each description runs the full width up to
   its switch. The 78ch cap is for a description beside other content; alone
   in a panel it wrapped a two-line sentence into three with half the row
   empty. */
.ck-switch-list.is-plain > .ck-switch-block{padding-block:var(--space-md)}
.ck-switch-list.is-plain > .ck-switch-block:first-child{padding-top:0}
.ck-switch-list.is-plain > .ck-switch-block:last-child{padding-bottom:0}
.ck-switch-list.is-plain > .ck-switch-block + .ck-switch-block{border-top:0}
.ck-switch-list.is-plain .ck-sb-desc{max-width:none}
/* .is-labels — switches with no descriptions, read like field labels: each
   title is a setting's name, not a heading over copy, so it drops to the
   field label's regular weight. Pairs with .is-plain inside a card. */
.ck-switch-list.is-labels .ck-sb-title{font-weight:var(--font-weight-normal)}
/* Title sits tight to its subtext: they are one unit. */
.ck-switch-block{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:var(--space-xl);row-gap:0;align-items:start;max-width:none}
.ck-switch-block .ck-sb-title{grid-column:1;font:var(--font-weight-semibold) var(--text-base) var(--font-sans);color:var(--foreground);line-height:1.45}
/* A badge beside a switch title ("New") qualifies the title, so it sits on
   the title's line, 8px after the words, centred on them, and never taller
   than the line the switch centres on. */
.ck-switch-block .ck-sb-title > :is(.ck-pill,.ck-badge){margin-inline-start:var(--space-sm);vertical-align:middle;margin-block:calc((1lh - var(--ck-pill-h)) / 2)}
/* THE SWITCH CENTRES ON THE TITLE'S LINE. It used to take margin-top:1px,
   a number that ignored that the switch (--switch-h, 24px) is taller than the
   title's line (14px x 1.45 = 20.3px): its centre landed 2.6px below the
   title's. It now takes the title's type, so 1lh IS the title's line, and is
   pulled up by half the difference — right at every switch size. The same
   negative margin below keeps it from stretching the title's row, so the
   description follows the title directly (the block's row-gap:0). */
.ck-switch-block :is(.switch,.ck-switch){grid-column:2;grid-row:1;
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);line-height:1.45;
  margin-block:calc((1lh - var(--switch-h)) / 2)}
/* AN ICON BUTTON INSIDE A LINE OF TEXT DOES NOT GROW THE LINE. An info
   button in a switch title or a table header is a 24px box on a ~20px line:
   inline, it made the line taller and pushed the words down, so the switch,
   which centres on 1lh of the title, sat 6px above the text beside it, and a
   header holding one sat 4px lower than its neighbours. Pulled in by half the
   difference, the button takes exactly one line and the words stay put. A
   field label is the same case: one with an info button was 24px against its
   neighbours' 18px, so its control started 6px lower in the row. Its
   font is the line's own, so 1lh here IS that line. */
:is(.ck-switch-block .ck-sb-title, .ck-table th, .ck-field-label, .ck-field-label > .ck-tip-host) > .ck-icon-btn{
  font:inherit;line-height:inherit;vertical-align:middle;
  margin-block:calc((1lh - var(--icon-box)) / 2)}
/* .ck-sb-action — a small labelled button BESIDE THE SWITCH (Configure): the
   block gains a third column, the button sits left of the switch on the
   title's line, centred on it the way the switch is. It is always shown and
   disabled while the switch is off — what it opens only matters when on. */
.ck-switch-block:has(> .ck-sb-action){grid-template-columns:minmax(0,1fr) auto auto}
.ck-switch-block:has(> .ck-sb-action) :is(.switch,.ck-switch){grid-column:3}
.ck-switch-block > .ck-sb-action{grid-column:2;grid-row:1;
  line-height:1.45;font-size:var(--text-base);margin-block:calc((1lh - 28px) / 2);margin-inline-end:calc(var(--space-md) - var(--space-xl))}
.ck-switch-block .ck-sb-desc{grid-column:1;font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.55;max-width:78ch}
/* .is-full: a description that runs the block's width — one short paragraph
   in a wide card, where the 78ch reading cap left half the card empty. */
.ck-switch-block .ck-sb-desc.is-full{max-width:none}

/* ─── RADIO GROUP (Molecule) ─── */
.ck-radio-set{display:flex;flex-direction:column;gap:var(--space-sm)}
.ck-radio-set + .ck-radio-set{margin-top:20px}
.ck-radio-legend{font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--foreground)}
.ck-radio-row{display:flex;align-items:center;gap:var(--space-xl);flex-wrap:wrap}
/* ─── RADIO (Atom) ─────────────────────────────────────────────────────────
   shadcn semantics: --input border at rest, --primary border and dot when
   checked, --ring for focus, --destructive for aria-invalid. 16px, matching
   .ck-check and shadcn's size-4.

   Spec: references/components/radio.md */
.ck-radio{display:inline-flex;align-items:center;gap:var(--space-sm);font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground);cursor:pointer}
.ck-radio input{appearance:none;-webkit-appearance:none;width:16px;height:16px;margin:0;flex:none;
  box-sizing:border-box;border:1.5px solid var(--input);border-radius:var(--radius-full);
  background:var(--background);position:relative;cursor:pointer;
  transition:border-color var(--duration-base),background var(--duration-base)/* pairing-exempt: carries no text */}
.ck-radio input:checked{border-color:var(--primary)}
.ck-radio input:checked::after{content:'';position:absolute;inset:3px;border-radius:var(--radius-full);background:var(--primary)/* pairing-exempt: carries no text */}
.ck-radio:is(:hover,[data-force~="hover"]) input{border-color:var(--primary)}
.ck-radio input:active{background:var(--hover-bg)/* pairing-exempt: carries no text */}
.ck-radio:is(:hover,[data-force~="hover"]) input:checked::after{background:var(--primary-hover)/* pairing-exempt: carries no text */}
.ck-radio input:checked:active::after{background:var(--primary-active)/* pairing-exempt: carries no text */}

/* Invalid */
.ck-radio input[aria-invalid="true"]{border-color:var(--destructive)/* pairing-exempt: carries no text */}
.ck-radio input[aria-invalid="true"]:checked::after{background:var(--destructive)/* pairing-exempt: carries no text */}
.ck-radio input[aria-invalid="true"]:focus-visible{box-shadow:var(--focus-ring-error)}

/* Disabled — the whole row dims via tokens, and the label goes muted so the
   control and its text read as one disabled unit. */
.ck-radio:has(input:disabled),.ck-radio.is-disabled{color:var(--muted-foreground);cursor:not-allowed}
.ck-radio input:disabled,.ck-radio input[aria-disabled="true"]{
  background:var(--muted);border-color:var(--input);cursor:not-allowed;pointer-events:none
/* pairing-exempt: carries no text */}

/* ── RADIO CARD — .ck-radio.is-card ─────────────────────────────────────────
   A choice that needs a sentence to explain it: a signature method, an
   approval route. The whole card is the label, so the hit area is the card;
   the dot stays the real input. Chosen is a --primary stroke, never a fill,
   for the same reason the dot never fills the circle. Title and description
   are .ck-radio-title over .ck-radio-desc inside .ck-radio-text. Stack them in
   a .ck-radio-set.

   Spec: references/components/radio.md § Radio card */
.ck-radio.is-card{display:flex;align-items:flex-start;gap:var(--space-md);box-sizing:border-box;width:100%;
  padding:var(--container-inset);border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  transition:border-color var(--duration-base),background var(--duration-base)}
.ck-radio.is-card:hover{background:var(--hover-bg)}
.ck-radio.is-card:has(input:checked){border-color:var(--primary)}
.ck-radio.is-card > input{margin-block-start:2px}
.ck-radio-text{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-radio-title{font:var(--font-weight-semibold) var(--text-base) var(--font-sans);color:var(--foreground)}
.ck-radio-desc{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.5}
.ck-radio.is-card:has(input:disabled){background:var(--muted);cursor:not-allowed}
.ck-radio input:disabled:checked{border-color:var(--input)/* pairing-exempt: carries no text */}
.ck-radio input:disabled:checked::after{background:var(--muted-foreground)/* pairing-exempt: carries no text */}

.ck-radio input:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-radio:is(:hover,[data-force~="hover"]) input{border-color:var(--primary)}

/* ─── MEMBER CARD (Molecule) ─── */
/* ─── MEMBER CARD (Molecule) ───────────────────────────────────────────────
   Row actions are hidden until hover, which is fine for a mouse and invisible
   to a keyboard — so they also reveal on :focus-within.

   Surface is --accent, not a color-mix of --teal. --teal is a badge palette
   colour; using it as a surface tint means a brand change to --accent would
   never reach this card (Rulebook §10).

   Spec: references/components/member-card.md */
.ck-member-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-md)}
.ck-member-grid.cols-1{grid-template-columns:minmax(0,1fr)}
.ck-member{display:flex;align-items:center;gap:var(--space-md);padding:var(--container-inset);
  border-radius:var(--radius-2xl);background:var(--accent);color:var(--accent-foreground);
  border:1px solid var(--panel-border)}
.ck-member:hover{background:var(--hover-bg)}
.ck-member-av{/* pairing-exempt: outline pattern — initials in --primary on --background, 12.01:1 */
  width:36px;height:36px;flex:none;border-radius:var(--radius-full);
  display:grid;place-items:center;font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  background:var(--background);color:var(--primary)}
.ck-member-main{flex:1;min-width:0}
.ck-member-top{display:flex;align-items:center;gap:var(--space-sm)}
.ck-member-name{font:var(--font-weight-semibold) var(--text-base) var(--font-sans);color:var(--accent-foreground);overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-member-role{white-space:nowrap;flex:none}
.ck-member-mail{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);margin:0;overflow:hidden;text-overflow:clip;white-space:nowrap}
/* margin:0, not margin-top:0 — the mail line is usually a <p>, and its UA
   bottom margin (1em) made every card 12px taller than its content and sat
   the text 6px above the card's centre. */
.ck-member .ck-icon-btn{opacity:0;transition:opacity var(--duration-base)}
.ck-member:hover .ck-icon-btn,.ck-member:focus-within .ck-icon-btn{opacity:1}

/* ─── SIDE DRAWER (Organism) ─── */
/* ─── DRAWER (Organism) ────────────────────────────────────────────────────
   A side panel for work the user does alongside the page. For a decision that
   blocks, use a dialog; for a small anchored panel, a popover.

   Sizes are widths. .from-start opens on the leading edge.

   Spec: references/components/drawer.md */
/* The tint is on ::before, not on the scrim itself. The drawer is normally a
   SIBLING of the scrim, so opacity on the element would have been safe — but
   only until someone nests it, and then group opacity fades the panel and no
   z-index can rescue it. Painting the tint on a pseudo-element makes that
   impossible by construction, the same way the dialog does it. */
.ck-drawer-scrim{position:fixed;inset:0;z-index:var(--z-overlay)}
.ck-drawer-scrim::before{content:'';position:absolute;inset:0;background:var(--scrim);opacity:var(--scrim-opacity)/* pairing-exempt: carries no text */}
.ck-drawer-scrim > *{position:relative;z-index:1}
.ck-drawer{position:fixed;inset-block:0;inset-inline-end:0;z-index:calc(var(--z-overlay) + 1);
  width:min(420px,100%);
  background:var(--popover);color:var(--popover-foreground);
  border-inline-start:1px solid var(--panel-border);
  box-shadow:var(--shadow-overlay);
  display:flex;flex-direction:column}
.ck-drawer.size-sm{width:min(320px,100%)}
.ck-drawer.size-lg{width:min(560px,100%)}
.ck-drawer.size-full{width:min(880px,100%)}
.ck-drawer.from-start{inset-inline-end:auto;inset-inline-start:0;
  border-inline-start:0;border-inline-end:1px solid var(--panel-border)}
/* IT SLIDES IN. The spec has always said the drawer "slides in from the
   trailing edge over a scrim", and nothing drew it: every flow that wanted the
   motion translated its own drawer with a local class, and a flow that used
   the kit's markup as written got a panel that simply appeared.

   A drawer is shown by removing [hidden], so the motion is an animation on
   the shown state rather than a transition between two classes — it needs no
   open class, and it runs every time the drawer is shown. The scrim fades in
   with it. Under reduced motion both finish at once rather than not at all,
   so the end state is identical. */
@keyframes ck-drawer-in{from{transform:translateX(100%)}}
@keyframes ck-drawer-in-start{from{transform:translateX(-100%)}}
@keyframes ck-scrim-in{from{opacity:0}}
.ck-drawer:not([hidden]){animation:ck-drawer-in var(--duration-moderate) var(--ease-default)}
.ck-drawer.from-start:not([hidden]),.ck-drawer:dir(rtl):not([hidden]){animation-name:ck-drawer-in-start}
.ck-drawer.from-start:dir(rtl):not([hidden]){animation-name:ck-drawer-in}
.ck-drawer-scrim:not([hidden])::before{animation:ck-scrim-in var(--duration-moderate) var(--ease-default)}
@media (prefers-reduced-motion:reduce){
  .ck-drawer:not([hidden]),.ck-drawer-scrim:not([hidden])::before{animation-duration:1ms}}

/* EVERY SPACING VALUE HERE IS A MULTIPLE OF 4. The head was 18px top and
   bottom, which is the only number in the drawer that was not — and it read
   as a head that did not line up with anything below it.

   The bigger problem was invisible: .ck-drawer-title is an <h4> and
   .ck-drawer-sub a <p>, so both carried the BROWSER'S default margins —
   21.28px around the title and 12px around the subtitle. Nobody chose those,
   they are the UA stylesheet, and they were inflating the head to 130px and
   putting a 21px gap between a title and its own subtitle. Zeroed here, with
   a designed 4px gap in their place. */
/* One named inset, all four sides (check 77). The head was on --space-lg at
   the sides while .ck-drawer-body is on --container-inset, so the heading and
   the close control sat 4px further in than everything beneath them. */
.ck-drawer-head{display:flex;align-items:center;gap:var(--drawer-gap);padding:var(--drawer-inset);border-bottom:1px solid var(--border);flex:none}
.ck-drawer-title{font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--popover-foreground);flex:1;margin:0}
.ck-drawer-sub{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);margin:0}
/* The title and its subtitle are one block: 4px apart, and the head's own
   gap keeps them clear of the close button. */
.ck-drawer-heading{display:flex;flex-direction:column;gap:var(--space-xs);flex:1;min-width:0}
/* A tools row under the head carries the inset on three sides and none below:
   the body's own top inset is the space between them, so the two never stack. */
.ck-drawer-tools{padding:var(--drawer-inset) var(--drawer-inset) 0;flex:none}
/* Inside the head, the head owns the inset. The tools run carries its own for
   the case where it sits BELOW the head as its own row; nested, that inset is
   applied twice and the close control floats in off the corner instead of
   sitting at the edge like every other dismiss in the kit. */
.ck-drawer-head > .ck-drawer-tools{padding:0}
/* .has-search — the title and the drawer's search share the head's one row,
   so a picker drawer spends a single band on its header instead of two. The
   title keeps its width; the search takes the rest. */
.ck-drawer-head.has-search > .ck-drawer-heading{flex:none}
.ck-drawer-head.has-search > .ck-drawer-tools{flex:1;min-width:0}
/* The body is a stack on one gap, so a drawer built from any mix of kit parts
   gets the same rhythm without a variant rule. Margins are zeroed on its direct
   children: in a flex column they no longer collapse, and a stray <p> or <h4>
   default would double the gap. */
.ck-drawer-body{flex:1;min-height:0;overflow-y:auto;padding:var(--drawer-inset);
  display:flex;flex-direction:column;gap:var(--drawer-gap)}
.ck-drawer-body > *{margin-block:0;flex:none}
.ck-drawer-foot{display:flex;justify-content:flex-end;gap:var(--drawer-action-gap);padding:var(--drawer-inset);border-top:1px solid var(--border);flex:none}
.ck-select-count{display:flex;align-items:center;gap:var(--space-sm);padding:6px 8px 12px;font:var(--font-weight-medium) var(--text-md) var(--font-sans);color:var(--foreground)}
/* In a drawer or dialog body the body's own gap already spaces the count from
   the list, so its 12px bottom pad stacked on top of it into a 28px hole. */
.ck-drawer-body > .ck-select-count,
.ck-dialog-body > .ck-select-count{padding-block-end:0}

/* ═══ DRAWER VARIANTS ══════════════════════════════════════════════════════
   Filters and Manage columns are not two components. They are one drawer
   wearing two bodies: same scrim, same panel, same head, same footer, same
   open/close behaviour, same focus trap. Only .ck-drawer-body's contents
   differ, and that is what .is-filters and .is-columns change.

   They were separate organisms before, and the cost showed: the filter drawer
   carried its own -head, -title, -desc and -foot that duplicated the parent's
   and had already drifted from them. One parent means a change to the drawer's
   chrome reaches both, which matters because these two are the most frequently
   opened panels in the product.

   Both are built from atoms — .ck-dd, .ck-input, .ck-search, .ck-btn,
   .ck-chip, .ck-check, .ck-pill, .ck-icon-btn. Nothing here restyles an atom;
   these rules place them and nothing more.

   Spec: references/components/drawer.md */

/* ─── Variant: .is-filters ─────────────────────────────────────────────────
   A stack of rule cards, each one a sentence: FIELD, OPERATOR, then the
   values that answer it. The drawer's head counts the rules and offers to
   clear them; the footer saves or applies. */
/* No separator under the head in this variant. The rule cards are already
   discrete blocks on their own ground, so a line above the first one draws a
   boundary where the eye does not need one — and it cut the head off from the
   content it introduces. The columns drawer keeps its rule, because there the
   body is a flat list with nothing else to separate it. */
/* ── THE WIDTH ADJUSTOR — [data-ck-resize] ────────────────────────────────
   A grab strip on the panel's own edge. 6px wide so it can be hit, drawn as a
   1px rule so it is quiet until approached. The drawer takes it on its LEADING
   edge (it grows leftwards into the page); the folder navigator takes it on
   its trailing edge.

   The bounds are declared per panel and they are the whole point: without them
   a drawer can be dragged down to 40px, which hides the data it exists to
   show, or out to the full viewport, which is a page pretending to be a
   drawer. ckPanelResize() reads them and clamps. */
.ck-drawer,.ck-folder-nav{--ck-resize-min:320px;--ck-resize-max:720px}
[data-ck-resize]{
  position:absolute;inset-block:0;width:6px;z-index:2;
  cursor:col-resize;background:0;border:0;padding:0;
  touch-action:none;user-select:none}
[data-ck-resize="start"]{inset-inline-start:-3px}
[data-ck-resize="end"]{inset-inline-end:-3px}
[data-ck-resize]::before{
  content:'';position:absolute;inset-block:var(--space-md);inset-inline-start:2px;width:1px;
  background:var(--input);opacity:0;transition:opacity var(--duration-base)}
[data-ck-resize]:is(:hover,:focus-visible)::before,
.is-resizing > [data-ck-resize]::before{opacity:1}
.is-resizing > [data-ck-resize]::before{background:var(--primary)/* pairing-exempt: a 1px drag indicator, carries no text */}
[data-ck-resize]:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* The panel must be a positioning context for the handle to sit on its edge,
   and must not clip it. */
.ck-folder-nav{
  position:relative;
  /* Just under 8 rows at the 34px folder row (measured, not assumed), so the
     eighth is cut by the fold — the partial row is the cue that the list
     continues. Raise it per navigator where a panel is genuinely taller. */
  --ck-folder-children-max:270px}

.ck-drawer.is-filters .ck-drawer-head{border-bottom:0}
.ck-drawer.is-filters{width:min(480px,100%)}

/* Clearing every rule is destructive and says so, but it is not the drawer's
   main action — so it is a link button in the destructive tone, not a filled
   red button competing with Apply. */
/* A link button in the destructive tone. Named `destructive` like every other
   tone in the family — .ck-btn.ghost.destructive already exists, and a second
   spelling for one idea is how two of them drift apart. Not scoped to the
   drawer: any link button may carry it. */
/* Check 53: the soft foreground at rest. --destructive itself is 3.43:1 on
   white and 4.26:1 on --popover in dark — a link label is body text, not a
   filled surface, so it reads the tone's text token rather than its fill. */
.ck-btn.link.destructive{color:var(--destructive-soft-foreground)}
.ck-btn.link.destructive:is(:hover,[data-force~="hover"]){
  color:var(--destructive);text-decoration-thickness:2px}

/* One rule. --accent is the kit's soft cyan attention surface, which is what
   the shipped drawer uses: the card has to read as a unit without a stroke
   loud enough to make four of them look like a grid. */
/* --filter-card, a token of its own. It is not --accent: that is the kit's
   "you are here" surface — an open row, a hovered header, the active nav item
   — and borrowing it for a dozen rule cards at once makes the whole drawer
   look selected. Nor is it a palette tint like --teal-bg, which belongs to
   teal-toned CONTENT and would drag its own foreground along with it.

   A rule card is a block you are EDITING inside a panel. That is its own role,
   so it gets its own tokens, minted in all three themes: a fill about 1.21:1
   from the panel it sits on, and a stroke about 1.10:1 from that fill. The
   stroke is a STEP UP FROM THE FILL, not a different colour — it is there to
   close the card, not to outline it. High contrast is the exception by design:
   its stroke steps to 1.56:1, because amplifying edges is what that theme is
   for. */
.ck-filter-rule{
  display:flex;flex-direction:column;gap:var(--space-sm);
  padding:var(--container-inset-sm);
  border:1px solid var(--filter-card-border);border-radius:var(--radius-2xl);
  background:var(--filter-card);color:var(--filter-card-foreground)}
/* An entered value is a WHITE tag on the tinted card. As a neutral chip its
   fill was #ebeeee on #dcecf0 — 1.04:1, the chip and the card the same weight,
   so the values dissolved into the card. Fill alone cannot part a chip from a
   pale card, so it takes the card surface with the neutral stroke: the fill
   steps up and the stroke draws the edge. --card / --card-foreground are a
   real pair, so the text holds 12:1 or better in every theme. */
.ck-filter-rule-chips .ck-chip{
  background:var(--card);color:var(--card-foreground);border-color:var(--neutral-border)}
.ck-filter-rule-head{
  display:flex;align-items:center;gap:var(--space-sm);row-gap:var(--space-sm);flex-wrap:wrap}
/* The field name reads as part of the sentence, so its trigger drops the box
   and keeps only the chevron. It is still a .ck-dd — the menu, the keyboard
   handling and the open state all come from the atom. */
/* A long field name ("Reconcile Status(INV-PO-GRN-Delivery Note-4 way Match
   (APAC Vendors))") shrinks with the card instead of running out of it: the
   value cuts head…tail (ckTruncate) and the full name shows on hover and
   focus. The chevron never shrinks, so the control still reads as a picker. */
.ck-filter-rule-field{flex:0 1 auto;width:auto;min-width:0;max-width:100%}
.ck-dd.ck-filter-rule-field > .ck-dd-trigger{
  max-width:100%;min-width:0;
  height:auto;min-height:24px;padding:0;border:0;background:transparent;
  font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);
  color:var(--foreground);gap:var(--space-xs)}
.ck-dd.ck-filter-rule-field > .ck-dd-trigger:is(:hover,[data-force~="hover"]){
  background:transparent;border:0;color:var(--primary)}
.ck-dd.ck-filter-rule-field .ck-dd-arrow{color:var(--foreground)}
/* The operator is a real control — it changes what the rule means — so it
   keeps a surface, sized down to sit under the field name.

   IT HAS ITS OWN PAIR, --filter-op, AND A STROKE. It borrowed --segment, which
   is #eef1f3 on the card's #eaf4f6 — 1.01:1, so the control dissolved into the
   card it sits on. A control's boundary needs 3:1 (WCAG 1.4.11); a fill that
   dark would outweigh the field name above it, so the fill steps down just
   enough to read as a surface and the stroke carries the 3:1. */
.ck-filter-rule-op{flex:none;width:auto;min-width:0}
.ck-dd.ck-filter-rule-op > .ck-dd-trigger{
  height:28px;padding:0 var(--space-sm);gap:var(--space-xs);
  border:1px solid var(--filter-op-border);border-radius:var(--radius-md);
  background:var(--filter-op);color:var(--filter-op-foreground);
  font:var(--font-weight-medium) var(--text-base) var(--font-sans)}
.ck-dd.ck-filter-rule-op > .ck-dd-trigger:is(:hover,[data-force~="hover"]){
  border-color:var(--input-border-hover);background:var(--filter-op);color:var(--filter-op-foreground)}
.ck-dd.ck-filter-rule-op .ck-dd-arrow{color:var(--filter-op-foreground)}
/* Open and keyboard focus keep the dropdown's own treatment: the stroke above
   is declared later at equal weight, so without this it would win. */
.ck-dd.ck-filter-rule-op.open > .ck-dd-trigger,
.ck-dd.ck-filter-rule-op > .ck-dd-trigger:is(:focus-visible,[data-force~="focus"]){
  border-color:var(--primary);box-shadow:var(--focus-ring)}
/* THE FIELD AND OPERATOR MENUS SIZE TO WHAT THEY LIST, NOT TO THEIR TRIGGER.
   .ck-dd-panel spans its trigger (left:0; right:0), which is right for a
   dropdown that is a field — the menu under a 320px select is 320px. These two
   triggers are not fields: the field trigger is the field's NAME, sized to its
   text, and the operator is a 28px pill. A menu the width of "Filename" cut
   every option to three letters and an ellipsis, and a source tag beside each
   one made it worse. So the panel starts at the trigger's left edge, is at
   least as wide as the trigger and never narrower than a readable list, and
   otherwise takes the width of its longest option, capped to the viewport. */
.ck-dd:is(.ck-filter-rule-field,.ck-filter-rule-op) > .ck-dd-panel{
  right:auto;width:max-content;min-width:max(100%, 220px);
  max-width:min(360px, calc(100vw - 2 * var(--overlay-margin)))}
/* A rule whose operator cannot vary states it in words rather than offering a
   control that does nothing. */
.ck-filter-rule-op-static{
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  color:var(--muted-foreground);flex:none}
/* Remove. A hollow ring in the destructive tone — small, because removing one
   rule is minor and reversible, and the drawer has not been applied yet. The
   ::before gives it the 24x24 target § 7c requires without a 24px circle. */
/* It is .ck-icon-btn.size-xs.tone-danger, and this class is only where it
   sits. It used to be a control of its own: a 16px CIRCLE with a 1.5px
   --destructive ring and a 10px glyph, hit-padded out to 24px with a ::before.
   Three rules broken at once — § 7b says an icon control is a rounded square
   and --radius-full is for pills, § 7c says the glyph is 16px in a 24px box,
   and the whole thing was a second icon button living beside the kit's own.
   Its hover went straight to solid --destructive, where every other
   destructive control steps through the tint first; the atom already does. */
.ck-filter-rule-remove{margin-inline-start:auto;flex:none}
/* The value row. Whatever answers the rule goes here — an input, a dropdown,
   a date field — and it always spans the card.

   THE OPERATOR DECIDES WHETHER THERE IS A VALUE AT ALL. "is empty" answers
   itself, so the row and the counter go: showing an input beside it invites an
   answer that will be ignored. "is between" takes two, side by side.
   ckFilterSchema() sets data-value-shape; these rules only respond to it. */
.ck-filter-rule-row{display:flex;align-items:center;gap:var(--space-sm)}
.ck-filter-rule-row > *{flex:1;min-width:0}
.ck-filter-rule[data-value-shape="none"] .ck-filter-rule-row,
.ck-filter-rule[data-value-shape="none"] .ck-filter-hint,
.ck-filter-rule[data-value-shape="none"] .ck-filter-rule-chips{display:none}
.ck-filter-rule[data-value-shape$="-range"] .ck-filter-rule-row > *{flex:1 1 0}
/* How many values are in, against the ceiling. It is a count, not an error,
   until it is one. */
.ck-filter-hint{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}
.ck-filter-hint.is-full{color:var(--destructive)}
.ck-filter-rule-chips{display:flex;flex-wrap:wrap;gap:var(--space-sm)}
.ck-filter-rule-chips:empty{display:none}
/* An invalid rule takes the destructive soft triplet and says what is wrong
   in words underneath — tone is redundancy, never the only signal. */
.ck-filter-rule.is-error{
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);
  border-color:var(--destructive-border)}
.ck-filter-rule.is-error .ck-dd.ck-filter-rule-field > .ck-dd-trigger{color:var(--destructive-soft-foreground)}
.ck-filter-error{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--destructive-soft-foreground)}
/* Add sits at the trailing edge under the last rule: it belongs to the stack,
   not to the footer, and the footer's two actions must stay unambiguous. */
.ck-filter-add-row{display:flex;justify-content:flex-end}

/* ─── Variant: .is-columns ─────────────────────────────────────────────────
   A reorderable checklist of the table's columns. Every row does four things —
   show, rename, reorder, and say where the column comes from — so the row is
   the component and the list is just a stack of them. */
/* ── ACTIVITY — .ck-drawer.is-activity ─────────────────────────────────────
   Live progress across the folders: a search, a status filter and a column of
   items, each a file with the folder it went to, a bar and the step it is on.
   Lifted from the document editor, where it had been built out of kit atoms
   with its arrangement kept locally; the arrangement is the kit's now, and the
   measurements are the editor's to the pixel.

   It is composed, not drawn. Nothing below restyles an atom:
     head     .ck-drawer-head / -heading / -title / -sub, .ck-icon-btn.is-close
     search   .ck-search
     filter   .ck-tabs.size-sm.is-scroll-x, each tab a .ck-counter.size-sm
     item     .ck-card.size-sm — name .ck-card-title, folder .ck-card-sub
     bar      .ck-progress.size-sm, toned .is-running / .is-info / .is-success / .is-error
     empty    .ck-empty.size-sm
   What is here is only what those atoms do not already say: the column, the
   item's own row, and which tone each status takes.

   THE WIDTH ADJUSTOR is the kit's [data-ck-resize="start"] on the drawer's
   LEFT (leading) edge. Its floor is --activity-min: the width at which all
   five status tabs show at once, two-digit counts included (537px measured,
   540 set), so the filter never scrolls sideways —
   which is also the drawer's default width. It can grow to the 720 every
   drawer carries.

   Statuses: in-progress · uploaded · scanned · failed. Uploaded documents
   are edited ones sent back, and are not scanned, so their card says
   "Uploaded" and nothing about waiting. */
.ck-drawer.is-activity{
  --activity-min:540px;
  --ck-resize-min:var(--activity-min);
  width:min(var(--activity-min),100%);min-width:min(var(--activity-min),100%)}
.ck-drawer.is-activity > .ck-drawer-body{overflow:hidden}
.ck-drawer.is-activity > .ck-drawer-body > .ck-activity-list{flex:1}
.ck-drawer.is-activity > .ck-drawer-body > :is(.ck-search,.ck-tabs){flex:none}
/* The list is the one part that scrolls: the search and the filter stay put
   while the reader moves through the files. */
.ck-activity-list{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;gap:var(--drawer-list-gap)}
/* TOP-ALIGNED TO THE TITLE. The glyph and the percent belong to the file's
   name, not to the two-line block, so they sit on the title's first line
   instead of floating between the name and the folder. The glyph's box is
   taller than that line, so it is pulled up by half the difference: it takes
   the title's font, which makes 1lh the title's line height, and the offset
   follows the font rather than a pixel guess. */
.ck-activity-head{display:flex;align-items:flex-start;gap:var(--space-sm);margin-block-end:var(--space-sm)}
.ck-activity-head > .ck-ico{
  font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);
  margin-block-start:calc((1lh - var(--icon-box)) / 2)}
.ck-activity-meta{flex:1;min-width:0}
/* A long filename truncates rather than wrapping, so every item keeps one
   height and the column scans as a list; the full name is its title=. */
.ck-activity-meta > :is(.ck-card-title,.ck-card-sub){overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-activity-head > .ck-progress-val{font:var(--font-weight-normal) var(--text-lg) var(--font-sans);flex:none}
.ck-activity-step{font:var(--font-weight-normal) var(--text-sm)/1.5 var(--font-sans);color:var(--muted-foreground)}
/* The glyph speaks the same tone as the bar under it. A running item keeps the
   plain icon colour: it is not a state yet, it is on its way to one. */
.ck-activity-item[data-status="uploaded"] > .ck-activity-head > .ck-ico{color:var(--info-soft-foreground)}
.ck-activity-item[data-status="scanned"] > .ck-activity-head > .ck-ico{color:var(--success-soft-foreground)}
.ck-activity-item[data-status="failed"] > .ck-activity-head > .ck-ico{color:var(--destructive-soft-foreground)}
.ck-activity-item[data-status="in-progress"] > .ck-activity-head > .ck-ico{color:var(--progress-running)}
.ck-drawer.is-columns{width:min(520px,100%)}

/* Search and reset share a row; the search takes the slack. */
.ck-col-tools{display:flex;align-items:center;gap:var(--space-sm)}
.ck-col-tools > .ck-search{flex:1;min-width:0}
.ck-col-tools > .ck-btn{flex:none}
/* Source filter. Chips, not tabs: these narrow a list in place, they do not
   swap panels, and calling them tabs would promise a tabpanel that is not
   there. Selection is aria-pressed on a toggle button. */
.ck-col-seg{display:flex;flex-wrap:wrap;gap:var(--space-sm)}
/* Select-all. The tri-state lives on a real .ck-check, so indeterminate is the
   browser's own property rather than a class that can fall out of step. */
.ck-col-master{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--space-xs) 0;
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  color:var(--foreground)}
.ck-col-list{display:flex;flex-direction:column;gap:var(--drawer-list-gap);list-style:none;margin:0;padding:0}
.ck-col-card{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--container-inset-sm);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  transition:border-color var(--duration-base),background var(--duration-base)}
.ck-col-card:is(:hover,[data-force~="hover"]){border-color:var(--input);background:var(--hover-bg)}
.ck-col-card:focus-within{border-color:var(--ring)}
/* A column that is off is still listed — it has to be, or you could not turn
   it back on — so it steps down in weight and colour rather than disappearing. */
.ck-col-card.is-off .ck-col-name{color:var(--muted-foreground);font-weight:var(--font-weight-medium)}
.ck-col-grip{
  flex:none;display:grid;place-items:center;
  width:24px;height:24px;padding:0;
  border:0;background:0;border-radius:var(--radius-sm);
  color:var(--input);cursor:grab}
.ck-col-grip:active{cursor:grabbing}
.ck-col-grip svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-col-grip:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--muted-foreground)}
.ck-col-grip:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-col-main{display:flex;align-items:baseline;gap:var(--space-xs);flex:1;min-width:0;flex-wrap:wrap}
.ck-col-name{font:var(--font-weight-semibold) var(--text-base) var(--font-sans);color:var(--foreground)}
.ck-col-key{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap;min-width:0}
.ck-col-key.is-link{color:var(--link);text-decoration:underline}
.ck-col-actions{display:flex;align-items:center;gap:var(--space-sm);flex:none}
/* Rename appears on approach, not at rest. A pencil on all eleven rows is
   eleven pieces of noise for an action taken once; :focus-within keeps it
   reachable from the keyboard (§ 14.3 check 31). */
.ck-col-actions > [data-ck-col-rename]{
  opacity:0;pointer-events:none;transition:opacity var(--duration-base)}
.ck-col-card:is(:hover,:focus-within,[data-force~="hover"]) > .ck-col-actions > [data-ck-col-rename]{
  opacity:1;pointer-events:auto}
/* Renaming happens in place. The card's own content is swapped out rather than
   overlaid, so the row keeps its height and the list does not jump. */
.ck-col-card.is-editing .ck-col-main,
.ck-col-card.is-editing .ck-col-actions{display:none}
.ck-col-edit{display:none;flex:1;min-width:0;align-items:center;gap:var(--space-sm)}
.ck-col-card.is-editing .ck-col-edit{display:flex}
.ck-col-edit > .ck-input{flex:1;min-width:0}
/* Drag. A 3px bar in --primary marks where the row would land — drawn on the
   card being passed, so there is exactly one indicator on screen at a time. */
.ck-col-card.is-dragging{opacity:var(--disabled-opacity);cursor:grabbing}
.ck-col-card.drop-before,.ck-col-card.drop-after{position:relative}
.ck-col-card.drop-before::before,.ck-col-card.drop-after::after{
  content:'';position:absolute;inset-inline:0;height:3px;
  background:var(--primary)/* pairing-exempt: a 3px drop indicator, carries no text */;
  border-radius:var(--radius-xs);pointer-events:none}
.ck-col-card.drop-before::before{top:-5px}
.ck-col-card.drop-after::after{bottom:-5px}
/* ADD A COLUMN. The last thing in the list, and shaped like a row that is not
   there yet: full width, the row's radius, a dashed edge — the kit's word for
   "there could be something here" (.ck-empty, the dropzone). It is a .ck-btn,
   so every state is the button's; only the edge and the ink change, and they
   go to --primary on approach like the row it will become. */
.ck-btn.ck-col-add{
  width:100%;justify-content:center;border-radius:var(--radius-2xl);
  border-style:dashed;border-color:var(--input);color:var(--muted-foreground)}
.ck-btn.ck-col-add:is(:hover,[data-force~="hover"]){border-color:var(--primary);color:var(--primary)}
.ck-col-empty{
  padding:var(--space-xl) var(--space-base);text-align:center;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}
/* 14px, rounded, drawn by the kit so it looks the same on every platform. */
/* ─── CHECKBOX (Atom) ──────────────────────────────────────────────────────
   shadcn semantics: --input border at rest, --primary fill with a
   --primary-foreground mark when checked, --ring for focus,
   --destructive for aria-invalid.

   16px to match .ck-radio and shadcn's size-4. It was 14px, which read as a
   misalignment wherever a checkbox and a radio shared a form.

   Spec: references/components/checkbox.md */
.ck-check{
  /* Geometry lives in five properties so every size shares every state rule
     below; a size only restates these. */
  --ck-check-size:16px;--ck-check-stroke:1.5px;
  --ck-tick-w:3.5px;--ck-tick-h:7px;--ck-dash-w:7px;
  appearance:none;-webkit-appearance:none;
  width:var(--ck-check-size);height:var(--ck-check-size);flex:none;margin:0;cursor:pointer;
  border:var(--ck-check-stroke) solid var(--input);border-radius:var(--radius-xs);
  background:var(--background)/* pairing-exempt: carries no text; the border is the affordance — --input at 3.17:1 clears the 3:1 floor for a non-text control (WCAG 1.4.11) */;position:relative;box-sizing:border-box;
  transition:background var(--duration-base),border-color var(--duration-base)
}
/* data-force alongside every state, so a spec sheet renders the matrix from
   the kit's OWN rules instead of re-declaring them (§ 12.1). The checkbox was
   the one control that never got the hook, so its hover and focus cells drew
   identically to rest and the documentation quietly said nothing. */
.ck-check:is(:hover,[data-force~="hover"]){border-color:var(--primary)}
.ck-check:is(:active,[data-force~="active"]){border-color:var(--primary);background:var(--hover-bg)}
.ck-check:checked{background:var(--primary);border-color:var(--primary)/* pairing-exempt: carries no text */}
/* Centred from the middle rather than offset from the corner, so the tick
   sits in the frame at any box size. Lifted off the geometric centre by
   --ck-tick-lift, because a rotated check reads low when its BOX is centred —
   see the derivation below. */
/* The tick is two borders rotated 45deg. border-radius rounds where they meet
   and both open ends, so the glyph gets the round joins and caps § 7c asks of
   every icon — a border cannot take stroke-linecap, and a data-URI SVG would
   hardcode a colour and stop following the theme.

   CENTRING THE BOX IS NOT CENTRING THE TICK, and that is the whole problem
   here. The element is a 3.5 x 7 rectangle, but the INK is only its right and
   bottom borders — an L sitting against two edges of that rectangle. So the
   ink's centre of mass is down-and-right of the rectangle's own centre, and
   rotating 45deg turns that pair of offsets into a purely VERTICAL drop. A
   plain translate(-50%,-50%) centres the rectangle perfectly and leaves the
   mark visibly low: measured +0.81px below the box centre.

   --ck-tick-lift takes it back, and the value is CALIBRATED AGAINST THE
   RENDER rather than derived on paper. The arithmetic predicts 0.86px, but
   the painted centroid only moves 0.56px for every 1px of lift — the response
   is linear and repeatable, so the zero crossing is at 1.4px, not 0.86px.
   Measured across the sweep: 0px -> +0.81, 0.8px -> +0.36, 1.3px -> +0.07,
   1.4px -> +0.009, 1.5px -> -0.05. Horizontal offset is +0.02px throughout
   and needs no correction.

   If the tick's width, height or border ever change, RE-MEASURE — this number
   does not follow from them by a formula you can reapply.

   The indeterminate minus needs none of this: it is a filled bar, so its ink
   and its box are the same rectangle. Measured +0.02px. */
.ck-check{--ck-tick-lift:1.4px}
.ck-check:checked::after{content:'';position:absolute;left:50%;top:50%;width:var(--ck-tick-w);height:var(--ck-tick-h);
  border:solid var(--primary-foreground);border-width:0 var(--ck-check-stroke) var(--ck-check-stroke) 0;
  border-radius:0 0 1px 0;
  transform:translate(-50%,calc(-50% - var(--ck-tick-lift))) rotate(45deg)}
.ck-check:checked:is(:hover,[data-force~="hover"]){background:var(--primary-hover);border-color:var(--primary-hover)/* pairing-exempt: carries no text */}
.ck-check:checked:is(:active,[data-force~="active"]){background:var(--primary-active);border-color:var(--primary-active)/* pairing-exempt: carries no text */}
.ck-check:indeterminate{background:var(--primary);border-color:var(--primary)/* pairing-exempt: carries no text */}
/* The minus is drawn as a filled bar rather than a border, so it can take a
   radius on both ends — a border-top has square caps and cannot be rounded. */
.ck-check:indeterminate::after{content:'';position:absolute;left:50%;top:50%;
  width:var(--ck-dash-w);height:var(--ck-check-stroke);
  background:var(--primary-foreground);border-radius:var(--radius-full);
  transform:translate(-50%,-50%)}

/* Invalid. shadcn pairs a --destructive border with a destructive-tinted ring;
   --focus-ring-error already exists for the second half. */
.ck-check[aria-invalid="true"]{border-color:var(--destructive)}
.ck-check[aria-invalid="true"]:is(:focus-visible,[data-force~="focus"]){box-shadow:var(--focus-ring-error)}

/* Disabled. Tokens rather than opacity, so the mark keeps its contrast
   instead of fading with the box (Rulebook §8). */
.ck-check:disabled,.ck-check[aria-disabled="true"]{
  background:var(--muted);border-color:var(--input);cursor:not-allowed;pointer-events:none
/* pairing-exempt: carries no text */}
.ck-check:disabled:checked,.ck-check:disabled:indeterminate{
  background:var(--input);border-color:var(--input)
/* pairing-exempt: carries no text */}
.ck-check:disabled:checked::after,.ck-check:disabled:indeterminate::after{
  border-color:var(--background)
/* pairing-exempt: carries no text */}

.ck-check:is(:focus-visible,[data-force~="focus"]){outline:0;box-shadow:var(--focus-ring)}

/* ── .ck-check.size-sm — 12px, the TABLE checkbox ──────────────────────────
   Same element, same states, same interaction rules; only the geometry is
   smaller. A table row is dense, and a 16px box beside 14px cell text read as
   the loudest thing in the row. Every checkbox inside a .ck-table, and every
   document checkbox in the folder navigator (every variant of it), takes it by
   default — so neither ever mixes two sizes; elsewhere, .size-sm opts in.

   Stroke 1.25px and a 2.5 x 5 tick keep the mark in proportion with the box;
   --ck-tick-lift is RE-MEASURED at this size (see the derivation above — it
   does not scale by formula): 1.3px puts the tick's painted centre within
   0.02px of where the calibrated 16px tick sits, measured against the
   indeterminate bar at 8x. The box is 12px but the TARGET stays 24px: a
   transparent ::before overflows it, because WCAG 2.5.8 sizes the target, not
   the paint. */
.ck-check.size-sm,.ck-table .ck-check,.ck-folder-nav .ck-check{
  --ck-check-size:12px;--ck-check-stroke:1.25px;
  --ck-tick-w:2.5px;--ck-tick-h:5px;--ck-dash-w:5px;--ck-tick-lift:1.3px}
.ck-check.size-sm::before,.ck-table .ck-check::before,.ck-folder-nav .ck-check::before{
  content:'';position:absolute;left:50%;top:50%;width:24px;height:24px;
  transform:translate(-50%,-50%)}
/* ─── PICK ROW (Molecule) ──────────────────────────────────────────────────
   A selectable row in a list — structurally a menu item that persists rather
   than firing and closing. Shares the menu item's focus treatment for the same
   reason: an outer ring is clipped by the list's padding.

   Spec: references/components/menu.md § Pick row */
.ck-pick{display:flex;align-items:center;gap:var(--space-md);width:100%;padding:9px 8px;box-sizing:border-box;
  border:0;background:0;border-radius:var(--radius-sm);cursor:pointer;text-align:start;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground)}
/* box-sizing above: a pick row that is a <label> around its checkbox (the
   accessible form of a multi-pick list) is content-box by default, and
   width:100% plus 16px of padding ran it 16px past its list. A <button> row
   was border-box by the UA, which is why it went unnoticed. */
/* .ck-pick-meta — trailing content (a role pill, a count) at the row's end. */
.ck-pick-meta{margin-inline-start:auto;flex:none}
.ck-pick:hover{background:var(--hover-bg)}
.ck-pick:active{background:var(--selected-bg)}
.ck-pick:focus-visible{outline:0;background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
.ck-pick[aria-selected="true"],.ck-pick.is-on{background:var(--selected-bg);color:var(--selected-fg)}
.ck-pick[aria-selected="true"]:hover,.ck-pick.is-on:hover{background:var(--hover-bg)}
.ck-pick:disabled,.ck-pick[aria-disabled="true"]{color:var(--input);cursor:not-allowed;pointer-events:none}

/* Wider dialog for two-column forms. */
.ck-field-label .ck-info{width:var(--icon-size);height:var(--icon-size);color:var(--muted-foreground);cursor:help;display:inline-flex}
.ck-dialog-note{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.55}

/* Controls stay white inside a teal tile so they keep their edge. */
.ck-settings-card .ck-input,.ck-settings-card .ck-select,.ck-settings-card .ck-textarea,
.ck-settings-card .ck-dd-trigger,.ck-settings-card .ck-table{background:var(--background)/* pairing-exempt: re-asserts the surface only; the base .ck-input rule owns the colour */}
.ck-settings-card .ck-table th{/* pairing-exempt: header band inside an --accent card; the base .ck-table th rule owns the colour */background:var(--accent)}
/* A TINTED CARD KEEPS THE PRODUCT'S HEADER BAND. --accent was right on the
   plain settings card, which IS --accent, but on --card-secondary (#eaf4f6)
   and --group the blue band landed within a shade of the card around the
   table and the header read as part of the card, not of the table. Those two
   take --muted, the header every other table in the product has. */
.ck-settings-card:is(.is-secondary,.is-group,.is-plain) .ck-table th:not(.is-sortable:hover){/* pairing-exempt: header band; the base .ck-table th rule owns the colour */background:var(--muted)}
.ck-settings-card .ck-switch-list > .ck-switch-block + .ck-switch-block{border-top-color:var(--panel-border)}
/* .ck-settings-card.is-group — the card on the --group surface, with no
   dividers: no rule under the title, none between switch blocks. The spacing
   stays; only the lines go. The surface brings its own foreground, and the
   secondary text takes it too: --muted-foreground measures 3.80:1 on --group
   in light, under the 4.5:1 body-text floor; --group-foreground is 11.81:1
   (8.36:1 dark, 14.06:1 high contrast). */
.ck-settings-card.is-group{background:var(--group);color:var(--group-foreground)}
.ck-settings-card.is-group > .ck-card-title{color:var(--group-foreground);border-bottom:0}
.ck-settings-card.is-group .ck-switch-list > .ck-switch-block + .ck-switch-block{border-top:0}
.ck-settings-card.is-group :is(.ck-sb-desc,.ck-field-hint){color:var(--group-foreground)}
/* .ck-settings-card.is-secondary — the same undivided card on the tinted
   --card-secondary surface. --muted-foreground holds AA on it (4.56 / 6.95 /
   11.37:1), so secondary text keeps its own colour here. */
.ck-settings-card.is-secondary{background:var(--card-secondary);color:var(--card-secondary-foreground);border-color:var(--card-secondary-border)}
/* .ck-settings-card.is-plain — the same undivided card on plain white: --card
   with the light --panel-border stroke. For a settings page whose sections
   should read as plain cards rather than tinted bands. */
.ck-settings-card.is-plain{background:var(--card);color:var(--card-foreground);border-color:var(--panel-border)}
.ck-settings-card.is-plain > .ck-card-title{color:var(--card-foreground);border-bottom:0}
.ck-settings-card.is-plain .ck-switch-list > .ck-switch-block + .ck-switch-block{border-top:0}
.ck-settings-card.is-secondary > .ck-card-title{color:var(--card-secondary-foreground);border-bottom:0}
.ck-settings-card.is-secondary .ck-switch-list > .ck-switch-block + .ck-switch-block{border-top:0}
/* With the dividers gone, 16px above and below each block left 32px of empty
   rhythm between switches. Without a rule to carry the separation, 8 and 8 is
   enough: 16px between blocks, the same as the field grid's gap. */
.ck-settings-card:is(.is-secondary,.is-group,.is-plain) .ck-switch-list > .ck-switch-block{padding-block:var(--space-sm)}
.ck-settings-card:is(.is-secondary,.is-group,.is-plain) .ck-switch-list > .ck-switch-block:first-child{padding-top:0}
.ck-settings-card:is(.is-secondary,.is-group,.is-plain) .ck-switch-list > .ck-switch-block:last-child{padding-bottom:0}

/* Remove action — red stroked minus in a circle, 16px. */

/* ─── TOOLTIP (Molecule) ───────────────────────────────────────────────────
   Text-only help on hover AND focus. If it holds a control it is a popover.

   TWO variants x FOUR placements = eight tooltips, ONE size:
     default   --primary fill, --primary-foreground text. The product default.
     .is-light --popover fill, --popover-foreground text, --shadow-panel.
               For a tooltip over dark or busy content, where a primary-filled
               bubble would compete.

   There is no size scale. A tooltip is as wide as its content up to 320px and
   as tall as it needs — one standard shadow, one padding. A "large tooltip"
   is a popover.

   MULTILINE is supported: the text wraps, `white-space` is normal, and
   line-height is set for two or three lines to read properly.

   TRUNCATION: the same rule as everywhere else (§ 14.9). A tooltip whose own
   text is too long truncates with .ck-mid-trunc, and anything truncated
   elsewhere shows its full value in a tooltip of this style — the truncated
   element and its tooltip are the same component, so they cannot diverge.

   Spec: references/components/tooltip.md */
.ck-tip{
  position:fixed;z-index:calc(var(--z-toast) + 20);
  /* border-box, so max-width is the width of the TOOLTIP. Without it the cap
     sized the content and the painted tip measured 342px — the stated 320
     plus its own padding and stroke. */
  box-sizing:border-box;
  max-width:320px;padding:8px 10px;
  border-radius:var(--radius-2xl);
  background:var(--primary);color:var(--primary-foreground);
  border:1px solid var(--primary);
  /* NO shadow, ever. A tooltip is a transient label, not a surface: a shadow
     under it reads as a second panel and, stacked over a bar or a card that
     already casts one, the two collide and look like a rendering fault. Its
     own fill and border carry it. Universal rule — see the sanity bible. */
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);line-height:1.5;
  white-space:normal;overflow-wrap:anywhere;
  opacity:0;pointer-events:none;
  transition:opacity var(--duration-base)}
.ck-tip.open{opacity:1;pointer-events:auto}

/* Light variant — same geometry, same shadow, inverted surface. */
.ck-tip.is-light{
  background:var(--popover);color:var(--popover-foreground);
  border-color:var(--panel-border)}

/* ── Tail ──
   An 8px square rotated 45deg with half of it protruding from the edge that
   faces the anchor. Placement names follow Radix / shadcn, where the side is
   where the TOOLTIP sits: .is-top means the tooltip is ABOVE its anchor, so
   its tail is on the BOTTOM edge pointing down.

   Four placements x two variants = the eight tooltips. Both variants get the
   same tail; it inherits the bubble's fill and stroke, so a light tooltip's
   tail carries --panel-border and a default one's is invisible against its own
   --primary edge.

   Rotating a square moves its edges: after rotate(45deg) the top border draws
   the upper-RIGHT facet, right draws lower-right, bottom draws lower-left and
   left draws upper-left. So each placement keeps the two OUTWARD facets and
   zeroes the two facing the bubble — otherwise the stroke draws a line across
   the bubble it is attached to. box-sizing keeps the diamond 8px either way. */
.ck-tip::after{
  content:'';position:absolute;width:8px;height:8px;box-sizing:border-box;
  background:var(--primary)/* pairing-exempt: the tail carries no text; it inherits the bubble's fill and stroke */;border:1px solid var(--primary);
  transform:rotate(45deg)}
.ck-tip.is-light::after{background:var(--popover);border-color:var(--panel-border)/* pairing-exempt: the tail carries no text; it inherits the bubble's fill and stroke */}
/* tail below — keeps right + bottom */
.ck-tip.is-top::after{
  inset-block-end:-4px;inset-inline-start:var(--tip-arrow-x,50%);margin-inline-start:-4px;
  border-block-start-width:0;border-inline-start-width:0}
/* tail above — keeps top + left */
.ck-tip.is-bottom::after{
  inset-block-start:-4px;inset-inline-start:var(--tip-arrow-x,50%);margin-inline-start:-4px;
  border-block-end-width:0;border-inline-end-width:0}
/* tail on the inline-end edge — keeps top + right */
.ck-tip.is-start::after{
  inset-inline-end:-4px;inset-block-start:50%;margin-block-start:-4px;
  border-block-end-width:0;border-inline-start-width:0}
/* tail on the inline-start edge — keeps bottom + left */
.ck-tip.is-end::after{
  inset-inline-start:-4px;inset-block-start:50%;margin-block-start:-4px;
  border-block-start-width:0;border-inline-end-width:0}

/* Multiline: a title line plus body reads better than one long run. */
.ck-tip-title{display:block;font:var(--font-weight-semibold) var(--text-base) var(--font-sans);margin-bottom:2px}
.ck-tip b{font-weight:600}
.ck-tip .ck-tip-eg{display:block;margin-top:6px;opacity:.82}

/* A truncated label's tooltip carries the full string, and its own text can
   truncate too — one line, middle-truncated, same style. */
.ck-tip.is-single-line{white-space:nowrap;overflow:hidden;text-overflow:clip}

.ck-tip code{display:block;margin-top:4px;padding:3px 6px;border-radius:var(--radius-xs);
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);background:var(--tip-code-bg);white-space:pre-wrap}
/* ─── MENU SEARCH ─────────────────────────────────────────────────────────
   A menu long enough to scan is a menu worth filtering. Add .has-search to
   .ck-menu and put .ck-menu-search as its first child. */
.ck-menu.has-search{min-width:248px;padding:0}
/* The search row is the .ck-search MOLECULE, not a second one. It used to be
   a parallel implementation — its own input, its own magnifier at a different
   offset, its own spinner, and no clear button at all, which is why the cross
   never appeared once you typed. An atom or molecule used inside an organism
   brings its behaviour with it (§ 14 check 40); re-styling a copy is how the
   two drift until one of them is missing a control.

   All that belongs here is where it sits. */
.ck-menu-search{padding:8px;flex:none}
.ck-menu-search > .ck-search{display:block}
.ck-menu.has-search .ck-menu-list{max-height:260px;overflow-y:auto;padding:var(--container-inset-xs)}
.ck-menu.has-search .ck-menu-item mark{background:var(--highlight-bg);color:inherit;border-radius:var(--radius-xs);padding:0 1px}
.ck-menu-empty{padding:16px 10px;text-align:center;font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}.ck-remove:focus-visible{outline:0;box-shadow:var(--focus-ring-error)}

/* ─── ICON (Atom) ──────────────────────────────────────────────────────────
   Every icon in the product is Lucide: 24x24 viewBox, fill="none",
   stroke="currentColor", stroke-width 2, round caps and joins, no transforms
   and no explicit colours. https://lucide.dev

   ONE size: a 16x16 glyph centred in a 20x20 box, so the margin is 2px on
   every side and a column of icons lines up regardless of glyph shape. The box
   is what you lay out against; the glyph is what you see.

   Two colours only — default and destructive. A tone beyond those is a defect,
   not a variant.

   Spec: references/components/icon.md */
.ck-ico{
  display:inline-grid;place-items:center;flex:none;
  width:var(--icon-box);height:var(--icon-box);
  color:var(--icon-color);
  transition:color var(--duration-base),background var(--duration-base)}
.ck-ico svg{width:var(--icon-size);height:var(--icon-size);display:block;
  stroke-width:var(--icon-stroke)}
.ck-ico.is-destructive{color:var(--icon-color-destructive)}

/* THE ONE SANCTIONED SECOND SIZE — the app's main left navigation.
   The rail's targets are 40px, and a 16px glyph inside a 40px frame leaves a
   12px margin on every side, which reads as a small mark adrift in a large
   button rather than as a navigation icon. 20px in 40px keeps the same 2:1
   feel the default pair has at 16-in-24 while clearing WCAG 2.5.8 on its own.
   It is a VARIANT, not a free size: nothing outside the left rail may use it.
   Spec: references/components/icon.md */
.ck-ico.size-nav{width:var(--icon-box-nav);height:var(--icon-box-nav)}
.ck-ico.size-nav svg{width:var(--icon-size-nav);height:var(--icon-size-nav)}

/* THE SMALL ICON — .ck-ico.size-sm, a 12px glyph in an 18px frame
   (--icon-size-sm in --icon-box-sm). The same 2:3 glyph-to-frame ratio as the
   default 16-in-24, the same --icon-color, --icon-stroke, destructive tone and
   transitions; only the geometry is smaller. For dense places a 24px frame
   crowds: a badge row, a table cell's inline mark, a compact list. It never
   replaces the default where the default fits. */
.ck-ico.size-sm{width:var(--icon-box-sm);height:var(--icon-box-sm);
  --ck-ico-count-dot:10px;--ck-ico-count-inset:2px}
.ck-ico.size-sm svg{width:var(--icon-size-sm);height:var(--icon-size-sm)}

/* ── A COUNT ON AN ICON — .ck-ico-count ───────────────────────────────────
   An unread mark that carries a NUMBER, on the icon's top trailing corner.
   One rule serves both hosts: the icon atom and the icon button, whose older
   spelling .notif-badge stays valid. A second counter is how the first one
   drifts, and the kit has paid for that three times already (check 71).

   IT IS DELIBERATELY SMALL, AND THAT IS THE POINT. The icon is the subject;
   the count qualifies it. On the 24px icon box the bubble is 12px — half the
   box, and a little over half the 16px glyph — so the glyph still reads first.
   Sized to match the glyph, which is what the icon button's roomier 36px box
   can carry, the two become marks competing for the same glance, which is
   exactly what a count on a small icon must not do. So it scales with its
   host rather than being one number everywhere.

   The ring is --background rather than a border: it is the surface behind the
   bubble, so it separates it from the glyph underneath in all three themes
   instead of being a stroke that has to be re-toned for each.

   Digits are --text-3xs, the floor of the scale and the size it exists for,
   with tabular numerals so 11 and 99 occupy the same width. Past two digits
   the bubble grows sideways into a capsule; it never grows taller. */
.ck-ico{--ck-ico-count-dot:12px;--ck-ico-count-inset:2px}
.ck-ico.size-nav{--ck-ico-count-dot:16px;--ck-ico-count-inset:2px}
.ck-ico:has(> .ck-ico-count){position:relative;overflow:visible}
.ck-ico-count,.ck-icon-btn > .notif-badge{
  position:absolute;
  inset-block-start:calc(-1 * var(--ck-ico-count-inset, 2px));
  inset-inline-end:calc(-1 * var(--ck-ico-count-inset, 2px));
  display:flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-width:var(--ck-ico-count-dot, 16px);height:var(--ck-ico-count-dot, 16px);
  padding:0 var(--space-2xs);
  border-radius:var(--radius-full);
  background:var(--primary);color:var(--primary-foreground);
  font:var(--font-weight-medium) var(--text-3xs) var(--font-sans);
  font-variant-numeric:tabular-nums;line-height:1;
  box-shadow:0 0 0 2px var(--background)}
/* Tones, for a count that means something other than "new". */
.ck-ico-count.is-error{background:var(--destructive);color:var(--destructive-foreground)}
.ck-ico-count.is-neutral{background:var(--muted-foreground);color:var(--background)}

/* Interactive icons. A bare .ck-ico is decorative and has no states; add
   .is-interactive, or use .ck-icon-btn when it is a real button. */
.ck-ico.is-interactive{cursor:pointer;border-radius:var(--radius-md)}
/* A glyph inside a control takes the CONTROL's foreground, not --icon-color.
   .ck-ico pins its own colour, so on a filled button the glyph stayed at the
   resting icon tone while the label went to --primary-foreground — the pair
   Law 3 exists to keep together. .ck-sidebar-item already did this for the
   rail; every control needs it. */
.ck-btn .ck-ico,.ck-toggle .ck-ico,.ck-menu-item .ck-ico,
/* .ck-icon-btn was missing from this list, and it is the control most likely
   to hold a .ck-ico. The box asserts --icon-color, so a glyph inside a FILLED
   icon button — .is-primary, .tone-* on a selected row — went grey instead of
   taking the control's own foreground. Check 52: a mark reads from the
   surface it sits on. */
.ck-icon-btn .ck-ico{color:inherit}
/* The field clears and the close/remove crosses were missing too. They turn
   destructive on hover (the field-clear block at the end of the file), but a
   glyph wrapped in .ck-ico kept --icon-color, so the tint went red and the
   cross stayed grey. Found on the Team Members search. */
:is(.ck-input-clear,.ck-dd-clear,.ck-search-clear,.file-search-clear,.search-clear,
    .filter-input-clear,.filter-chip-x,.dropzone-file-remove,.ck-remove,.is-close,.is-destructive) .ck-ico{color:inherit}
.ck-ico.is-interactive:hover{color:var(--foreground);background:var(--hover-bg)}
.ck-ico.is-interactive:active{background:var(--selected-bg)}
.ck-ico.is-interactive.is-selected{color:var(--primary);background:var(--selected-bg)}
.ck-ico.is-interactive:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-ico.is-destructive.is-interactive:hover{color:var(--destructive-soft-foreground);background:var(--destructive-bg)}
.ck-ico.is-destructive.is-interactive:active{background:var(--destructive);color:var(--destructive-foreground)}
.ck-ico.is-destructive.is-interactive.is-selected{color:var(--destructive-soft-foreground);background:var(--destructive-bg)}
.ck-ico.is-destructive.is-interactive:focus-visible{box-shadow:var(--focus-ring-error)}
.ck-ico.is-disabled,.ck-ico[aria-disabled="true"]{color:var(--icon-color-disabled);cursor:not-allowed;pointer-events:none}

/* Every control that holds an icon uses the same 16px glyph and 2px stroke.
   The BOX varies with the control; the glyph does not. */
.ck-icon-btn svg,.ck-btn svg,.ck-menu-item svg,
.ck-dd-opt svg,.ck-pick svg,.ck-toast-icon svg,.ck-empty-icon svg{stroke-width:var(--icon-stroke)}

/* ─── EMPTY / LOADING / SKELETON (states) ─────────────────────────────────
   Three distinct answers to "there is nothing to show yet", and they must not
   be swapped:
     loading   a shimmer in the SHAPE of the content that is coming
     empty     a dashed outline saying there is none, plus a way to change that
     error     a destructive-tinted panel with a retry

   Spec: references/components/states.md */

/* Skeletons for each primitive, so a loading state can be built to the shape
   of the thing it replaces rather than as a grey rectangle. */
.ck-skeleton-avatar{width:var(--icon-box);height:var(--icon-box);border-radius:var(--radius-full)}
.ck-skeleton-ico{width:var(--icon-box);height:var(--icon-box);border-radius:var(--radius-sm)}
.ck-skeleton-image{width:100%;aspect-ratio:16/10;border-radius:var(--radius-2xl)}
.ck-skeleton-pill{width:72px;height:24px;border-radius:var(--radius-full)}
.ck-skeleton-btn{width:88px;height:36px;border-radius:var(--radius-md)}
.ck-skeleton-input{width:100%;height:36px;border-radius:var(--radius-md)}

/* A list of skeleton rows. --ck-skel-rows sets how many. */
.ck-skeleton-list{display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-skeleton-list .ck-skeleton-row{padding:9px 10px}

/* Table loading: skeleton cells that keep the column widths, so the header
   does not jump when the real rows arrive. */
.ck-table.is-loading tbody td{padding:11px 14px}
/* nothing else: the loading row inherits --table-row-height from .ck-table, so
   it is the same height as a data row by construction rather than by luck. */
.ck-table.is-loading .ck-skeleton-text{height:12px}

/* Loading overlay for a panel that already has a size. Keeps the panel's own
   height so the page does not reflow when it resolves. */
.ck-loading{position:relative;min-height:120px}
.ck-loading-overlay{position:absolute;inset:0;display:grid;place-items:center;
  gap:var(--space-sm);grid-auto-flow:row;justify-items:center;
  background:var(--card);color:var(--card-foreground);border-radius:inherit}
.ck-loading-label{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}

/* Image placeholder — a real element rather than a background, so alt text and
   a broken-image fallback both work. */
.ck-image{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;
  border-radius:var(--radius-2xl);background:var(--muted)/* pairing-exempt: carries no text */}
/* An unsized <svg> in a grid cell stretches to fill it, which is how the
   placeholder glyph rendered at full panel height. § 7c: every glyph is 16px
   regardless of what the markup carries. */
.ck-image-empty > svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
/* A placeholder ILLUSTRATION is not a glyph — it fills the frame. Use
   .ck-image with a src for that; this keeps an <img> inside .ck-image-empty
   behaving the same way if one is passed. */
.ck-image-empty > img{
  width:100%;height:100%;object-fit:cover;border-radius:inherit;display:block}
.ck-image-empty{display:grid;place-items:center;gap:var(--space-xs);width:100%;aspect-ratio:16/10;
  border-radius:var(--radius-2xl);border:1px dashed var(--input);
  /* pairing-exempt: placeholder copy is deliberately de-emphasised; --muted-foreground on --background is 3.44:1, same as .ck-empty */
  background:var(--background);color:var(--muted-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans)}

/* Error state — the third answer, and the one most often missing. */
.ck-error-state{display:flex;flex-direction:column;align-items:center;gap:var(--space-sm);
  padding:36px 16px;text-align:center;
  border-radius:var(--radius-2xl);border:1px solid var(--destructive-border);
  background:var(--destructive-bg);color:var(--destructive-soft-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans)}
.ck-error-state .ck-ico{color:var(--icon-color-destructive)}
.ck-error-state-title{font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--destructive-soft-foreground)}

/* ═══════════════════════════════════════════════════════════════════════════
   v3.2 GAP CLOSURE — components the v2.0 kit shipped and this kit had not.
   Every rule below is tokens-only; no new token was minted, because the
   superset token set already carried a role for each value. Register rows in
   rulebook § 14.1, spec sheets in references/components/.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ─── DIVIDER (Atom) ───────────────────────────────────────────────────────
   A rule between regions. Four forms: solid, dashed, labelled, vertical.

   ONE colour for all of them: --panel-border, the § 5 "visible structural
   stroke". It was --border, which is 1.09:1 against the page — invisible by
   design, because § 5 scopes --border to hairlines INSIDE a surface (table
   rows, list separators, menu dividers), not to a rule that separates regions.
   A divider you cannot see is not doing its job. The old .is-strong variant
   existed only to reach this value, so it is retired: it is now the default and
   there is nothing to opt into.

   Retired with it: .is-accent (--primary) and .is-thick (2px). One divider,
   one colour, one weight — a rule that varies by emphasis invites a second
   decision at every call site for no gain.

   Note the ceiling: --panel-border measures 1.16:1 against the page (§ 14.5,
   open). Making a divider genuinely high-contrast means changing that token's
   VALUE, which moves every panel stroke in the product with it — a token-set
   decision, not a component one.

   .ck-menu-sep stays as it is; it is the menu's own scoped divider, is a
   hairline inside a surface, and correctly reads --border.

   Spec: references/components/divider.md */
.ck-divider{
  flex:none;border:0;height:1px;margin:0;
  background:var(--panel-border)/* pairing-exempt: carries no text */}
/* Dashed cannot be a background: it is a border style. Height goes to 0 and
   the stroke carries it, so both forms occupy the same 1px. */
.ck-divider.is-dashed{height:0;background:0;border-top:1px dashed var(--panel-border)}

.ck-divider.is-vertical{width:1px;height:auto;align-self:stretch;min-height:var(--space-base)}
.ck-divider.is-vertical.is-dashed{
  width:0;height:auto;border-top:0;border-inline-start:1px dashed var(--panel-border)}

/* Labelled — the "or" between two auth options. The rules are ::before and
   ::after so the label stays a single text node and centres itself. */
.ck-divider-label{
  display:flex;align-items:center;gap:var(--space-md);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}
.ck-divider-label::before,.ck-divider-label::after{
  content:'';flex:1;height:1px;background:var(--panel-border)}

/* ─── ALERT (Molecule) ─────────────────────────────────────────────────────
   A persistent inline banner: something the user should know while they work.
   NOT a toast (transient, self-dismissing, floats) and NOT .ck-error-state
   (a content state that replaces the content). All three exist and are not
   interchangeable — § 7d.

   Tones are the soft-semantic triplet (§ 3): --X-bg fill, --X-border stroke,
   --X-soft-foreground text. Never the solid --X as text on its own tint.

   The neutral default is a declared cross-pair: --foreground on --muted is
   15.87:1 where the nominal --muted-foreground partner is 3.16:1 and would
   fail AA for body copy. Same exemption .ck-table th and .ck-pill.is-neutral
   already carry.

   Spec: references/components/alert.md */
.ck-alert{/* pairing-exempt: --muted is an alert tint, not a muted-content surface; --foreground on it is 15.87:1 where the nominal --muted-foreground partner is 3.16:1 */
  display:flex;align-items:flex-start;gap:var(--space-md);box-sizing:border-box;
  padding:var(--container-inset-bar);
  border:1px solid var(--border);border-radius:var(--radius-2xl);
  background:var(--muted);color:var(--foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans)}
.ck-alert.size-sm{padding:var(--container-inset-bar-sm);gap:var(--space-sm)}
.ck-alert.size-lg{padding:var(--container-inset-bar-lg)}

.ck-alert-icon{
  width:var(--icon-box);height:var(--icon-box);flex:none;
  display:inline-grid;place-items:center;color:var(--icon-color)}
.ck-alert-icon svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
/* ONE LINE RHYTHM, AND THE ICON SITS ON THE FIRST LINE.
   Title and message share an 18px line (--text-md at 1.5). The icon's 24px box
   is centred on that first line rather than top-aligned to it: top-aligned, a
   24px box's centre sits 3px below an 18px line's, so every one-line alert had
   its glyph visibly low. The negative block margin is derived, not picked —
   (line - box) / 2 — so it holds if either token moves.

   And no browser margins. The message is usually written as a <p>, and the
   kit set its font but never its margin, so each carried the browser's 1em
   above and below. Measured on the webhook editor's high-volume warning: the
   text 12px below its icon and a one-line alert 68px tall. It is 44 now,
   which is on the size scale. */
.ck-alert-content{flex:1;min-width:0;line-height:1.5}
.ck-alert-icon{margin-block:calc((var(--text-md) * 1.5 - var(--icon-box)) / 2)}
.ck-alert-title{font:var(--font-weight-semibold) var(--text-md)/1.5 var(--font-sans);margin:0 0 var(--space-2xs)}
.ck-alert-msg{font:var(--font-weight-normal) var(--text-md)/1.5 var(--font-sans);margin:0}
/* LISTS AND PARAGRAPHS IN AN ALERT. Guidance written as steps ("have ready",
   "the process") carried the browser's 1em margins and 40px indent, so a
   two-list alert ran 430px tall. They take the alert's own rhythm instead. */
.ck-alert-msg :is(p,ul,ol){margin:0}
.ck-alert-msg :is(ul,ol){padding-inline-start:var(--space-lg)}
.ck-alert-msg > * + *{margin-block-start:var(--space-xs)}
.ck-alert-msg strong{font-weight:var(--font-weight-semibold)}
/* A title with nothing under it keeps no gap under it either. */
.ck-alert-title:last-child{margin-bottom:0}
.ck-alert-action{margin-top:var(--space-sm)}
/* The dismiss rests in --icon-color and turns destructive on hover — the
   overlay-close rule above; this only pulls it into the padding. */
.ck-alert > .ck-icon-btn{margin-block-start:calc(-1 * var(--space-2xs))}

/* Tones — fill, stroke, text and icon are one family. */
.ck-alert.is-info{background:var(--info-bg);color:var(--info-soft-foreground);border-color:var(--info-border)}
.ck-alert.is-success{background:var(--success-bg);color:var(--success-soft-foreground);border-color:var(--success-border)}
.ck-alert.is-warning{background:var(--warning-bg);color:var(--warning-soft-foreground);border-color:var(--warning-border)}
.ck-alert.is-error{background:var(--destructive-bg);color:var(--destructive-soft-foreground);border-color:var(--destructive-border)}
/* Amber — a caution that is not a warning state: the amber pair, 4.51:1 text
   in light, 5.61:1 dark, 9.64:1 high contrast. */
.ck-alert.is-amber{background:var(--amber-bg);color:var(--amber-soft-foreground);border-color:var(--amber-border)}
.ck-alert.is-info .ck-alert-icon,
.ck-alert.is-success .ck-alert-icon,
.ck-alert.is-warning .ck-alert-icon,
.ck-alert.is-error .ck-alert-icon,
.ck-alert.is-amber .ck-alert-icon{color:inherit}

/* ─── PROGRESS (Atom) ─────────────────────────────────────────────────────
   Determinate by default; .is-indeterminate for work of unknown length.

   The track is --muted, a light grey; the FILL is --primary. § 9 defect 0
   names "a progress fill", not a progress track, and that is the right reading:
   the fill is what conveys the value and it clears 3:1 comfortably (12.01:1),
   while the track is only its container. An earlier pass put the track on
   --input by over-reading that rule, which made the unfilled remainder a mid
   grey heavy enough to compete with the fill.

   A progress bar therefore belongs on --card or --background. On --canvas
   directly the --muted track is nearly invisible, which is a placement
   mistake rather than a token one.

   Width is set inline (style="width:62%") or via --ck-prog-val, because the
   value is data, not style.

   Spec: references/components/progress.md */
.ck-progress{
  --ck-prog-h:8px;
  position:relative;width:100%;height:var(--ck-prog-h);box-sizing:border-box;
  border-radius:var(--radius-full);overflow:hidden;
  background:var(--track)/* pairing-exempt: a track carries no text */}
.ck-progress.size-sm{--ck-prog-h:4px}
.ck-progress.size-lg{--ck-prog-h:12px}
.ck-progress-bar{
  height:100%;width:var(--ck-prog-val,0);border-radius:var(--radius-full);
  background:var(--primary);
  transition:width var(--duration-moderate) var(--ease-out)/* pairing-exempt: carries no text */}
.ck-progress.is-success .ck-progress-bar{background:var(--success)/* pairing-exempt: carries no text */}
.ck-progress.is-warning .ck-progress-bar{background:var(--warning)/* pairing-exempt: carries no text */}
.ck-progress.is-error .ck-progress-bar{background:var(--destructive)/* pairing-exempt: carries no text */}
/* The fourth of the semantic set. Without it a screen with four progress
   states — running, waiting, done, failed — has three tones and has to let
   two of them collapse onto --primary, which is exactly the distinction the
   reader needs at a glance. */
.ck-progress.is-info .ck-progress-bar{background:var(--info)/* pairing-exempt: carries no text */}
/* Running — work under way, in the lighter --progress-running. */
.ck-progress.is-running .ck-progress-bar{background:var(--progress-running)/* pairing-exempt: carries no text */}

/* Indeterminate — a fixed-width fill sliding the length of the track. */
@keyframes ck-progress-slide{
  0%{transform:translateX(-100%)}
  100%{transform:translateX(350%)}}
.ck-progress.is-indeterminate .ck-progress-bar{
  width:30%;animation:ck-progress-slide 1.4s var(--ease-default) infinite}
@media (prefers-reduced-motion:reduce){
  .ck-progress.is-indeterminate .ck-progress-bar{animation-duration:3.6s}
  .ck-progress-bar{transition:none}}

/* ── Count variant ───────────────────────────────────────────────────────
   For work measured in THINGS, not percent: "3 of 12 files uploaded".

   The fill width is DERIVED from the two counts rather than passed in as a
   separate percentage, so the bar and the readout cannot disagree — the same
   reasoning as the switch's derived thumb travel. calc() divides by a unitless
   custom property, verified: 3/12 of a 400px track measures exactly 100px.

   A failed segment abuts the completed one on the same track, so the three
   quantities — done, failed, not-yet-attempted — read as one bar.

   aria-valuetext is REQUIRED here. Without it assistive tech computes a
   percentage from min/max and announces "25%", which is the wrong unit for
   the thing being measured. Spec: references/components/progress.md */
.ck-progress.is-count{
  --ck-prog-done:0;--ck-prog-total:1;--ck-prog-failed:0;
  display:flex;align-items:stretch}
.ck-progress.is-count .ck-progress-bar{
  width:calc(var(--ck-prog-done) / var(--ck-prog-total) * 100%)}
.ck-progress.is-count .ck-progress-fail{
  flex:none;height:100%;
  width:calc(var(--ck-prog-failed) / var(--ck-prog-total) * 100%);
  border-radius:var(--radius-full);
  background:var(--destructive)/* pairing-exempt: carries no text */}
/* Where both segments are present the inner edges square off, so the two read
   as one continuous bar; the track's own overflow clip rounds the outer ends. */
.ck-progress.is-count:has(.ck-progress-fail) .ck-progress-bar{
  border-start-end-radius:0;border-end-end-radius:0}
.ck-progress.is-count:has(.ck-progress-bar) .ck-progress-fail{
  border-start-start-radius:0;border-end-start-radius:0}

/* The count readout. The completed number carries the emphasis; the total and
   the noun stay quiet, so a glance lands on what changed. */
.ck-progress-count{
  font-variant-numeric:tabular-nums;color:var(--muted-foreground)}
.ck-progress-count > strong{
  font-weight:var(--font-weight-semibold);color:var(--foreground)}
.ck-progress-count > .is-failed{color:var(--destructive-soft-foreground)}

/* The value in text beside the bar. A bar alone is not a label. */
.ck-progress-row{display:flex;flex-direction:column;gap:var(--space-xs)}
.ck-progress-meta{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-progress-val{font-variant-numeric:tabular-nums;color:var(--foreground)}

/* ─── PROGRESS RING (Atom) — .ck-progress-ring ────────────────────────────
   The linear progress bar bent into a circle, for where a bar has no room: a
   file row, a table cell, a toast, a card corner. Same contract as the bar —
   the FILL carries the value and the contrast, --track is only its container;
   the same five tones; the same indeterminate state; the same value
   transition and reduced-motion handling. Only the geometry differs.

     <span class="ck-progress-ring" role="progressbar" aria-valuenow="62"
           aria-valuemin="0" aria-valuemax="100" aria-label="Uploading"
           style="--ck-ring-val:62">
       <svg viewBox="0 0 36 36" aria-hidden="true">
         <circle class="ck-progress-ring-track" cx="18" cy="18" r="16" pathLength="100"/>
         <circle class="ck-progress-ring-bar"   cx="18" cy="18" r="16" pathLength="100"/>
       </svg>
     </span>

   SVG, not a conic-gradient, for one reason: round caps. The bar's fill has
   round ends (--radius-full) and a gradient arc cannot. pathLength="100" makes
   the dash the percentage directly, so --ck-ring-val is the value 0-100, the
   same number as aria-valuenow. The ring starts at twelve o'clock and fills
   clockwise.

   Sizes are tokens (--progress-ring-size-*, --progress-ring-stroke-*). The
   viewBox is 36 units wide whatever the size, so the stroke is converted to
   viewBox units per size — that conversion is the component's own geometry
   (Law 2's exception), stated once here: units = stroke px x 36 / size px.
   Spec: references/components/progress.md § Progress ring */
.ck-progress-ring{
  --ck-ring-size:var(--progress-ring-size);--ck-ring-sw:4.5;--ck-ring-val:0;
  position:relative;display:inline-grid;place-items:center;flex:none;
  width:var(--ck-ring-size);height:var(--ck-ring-size);
  color:var(--foreground)}
.ck-progress-ring.size-sm{--ck-ring-size:var(--progress-ring-size-sm);--ck-ring-sw:4.5}
.ck-progress-ring.size-lg{--ck-ring-size:var(--progress-ring-size-lg);--ck-ring-sw:3.6}
.ck-progress-ring.size-xl{--ck-ring-size:var(--progress-ring-size-xl);--ck-ring-sw:3.375}
.ck-progress-ring > svg{
  grid-area:1 / 1;width:100%;height:100%;display:block;overflow:visible;
  transform:rotate(-90deg)}
.ck-progress-ring-track,.ck-progress-ring-bar{fill:none;stroke-width:var(--ck-ring-sw)}
.ck-progress-ring-track{stroke:var(--track)/* pairing-exempt: a track carries no text */}
.ck-progress-ring-bar{
  stroke:var(--primary);stroke-linecap:round;
  stroke-dasharray:var(--ck-ring-val) 100;
  transition:stroke-dasharray var(--duration-moderate) var(--ease-out)/* pairing-exempt: carries no text */}
/* A round cap overhangs the arc's end by half the stroke, so at 0 an empty
   ring would still show a dot. Nothing to show means nothing is drawn. */
.ck-progress-ring[style*="--ck-ring-val:0;"] .ck-progress-ring-bar,
.ck-progress-ring[style$="--ck-ring-val:0"] .ck-progress-ring-bar{stroke-linecap:butt}
.ck-progress-ring.is-success .ck-progress-ring-bar{stroke:var(--success)/* pairing-exempt: carries no text */}
.ck-progress-ring.is-warning .ck-progress-ring-bar{stroke:var(--warning)/* pairing-exempt: carries no text */}
.ck-progress-ring.is-error .ck-progress-ring-bar{stroke:var(--destructive)/* pairing-exempt: carries no text */}
.ck-progress-ring.is-info .ck-progress-ring-bar{stroke:var(--info)/* pairing-exempt: carries no text */}
.ck-progress-ring.is-running .ck-progress-ring-bar{stroke:var(--progress-running)/* pairing-exempt: carries no text */}
/* Indeterminate — the bar's 30% fill, going round instead of along. */
@keyframes ck-progress-ring-spin{to{transform:rotate(270deg)}}
.ck-progress-ring.is-indeterminate > svg{animation:ck-progress-ring-spin 1.4s linear infinite}
.ck-progress-ring.is-indeterminate .ck-progress-ring-bar{stroke-dasharray:30 100;transition:none}
@media (prefers-reduced-motion:reduce){
  .ck-progress-ring.is-indeterminate > svg{animation-duration:3.6s}
  .ck-progress-ring-bar{transition:none}}
/* The value inside the ring — xl only; smaller rings take the value beside
   them in .ck-progress-meta, as the bar does. */
.ck-progress-ring-val{
  grid-area:1 / 1;font:var(--font-weight-semibold) var(--text-md) var(--font-sans);
  font-variant-numeric:tabular-nums;color:var(--foreground)}
.ck-progress-ring:not(.size-xl) > .ck-progress-ring-val{display:none}

/* ─── SLIDER (Atom) ───────────────────────────────────────────────────────
   A real <input type="range">. § 14.9 check 11: the event belongs on the
   native control, or keyboard users get nothing — and range gives arrow keys,
   Home/End and PageUp/Down for free.

   The filled portion is a gradient on the track driven by --ck-slider-pct,
   which ckSlider() in clipper-kit.js keeps in sync with the value. WebKit has
   no ::-moz-range-progress equivalent, so the gradient is the only way to fill
   a native range without a token-breaking image.

   Rail is --muted for the same reason as Progress: the affordance is the THUMB
   and the filled portion, both on --primary at 12.01:1, so the unfilled
   remainder is a container rather than the thing that must be perceived. That
   is a declared deviation from § 9's "slider rail" wording, stated here so the
   next reader can weigh it rather than discover it.

   The thumb is 20px with a 2px ring — it fills --icon-box exactly, so the
   whole control height is the target, and it reads as a grabbable control
   rather than a glyph. Rail is 8px, the same as Progress's default track.

   Spec: references/components/slider.md */
.ck-slider{
  --ck-slider-pct:0%;
  appearance:none;-webkit-appearance:none;
  /* 20px is the slider's OWN height — a 20px thumb with a 2px ring, which is
     this control's identity rather than an icon box. It used to read
     --icon-box; when that grew to 24 the thumb would have stayed 20 and the
     rail centring (margin-top:-6px) would have drifted 2px. */
  width:100%;height:20px;margin:0;padding:0;
  background:0;cursor:pointer}

/* Track — WebKit / Blink */
.ck-slider::-webkit-slider-runnable-track{
  height:8px;border-radius:var(--radius-full);
  background:linear-gradient(to right,
    var(--primary) 0 var(--ck-slider-pct),
    var(--track) var(--ck-slider-pct) 100%)/* pairing-exempt: rail and thumb carry no text */}
.ck-slider::-webkit-slider-thumb{
  appearance:none;-webkit-appearance:none;box-sizing:border-box;
  width:20px;height:20px;margin-top:-6px;
  border:2px solid var(--primary);border-radius:var(--radius-full);
  background:var(--background);box-shadow:var(--shadow-raised);
  transition:border-color var(--duration-base)/* pairing-exempt: carries no text */}
/* Track — Gecko. ::-moz-range-progress paints the fill natively, so the rail
   stays flat and no gradient is needed. */
.ck-slider::-moz-range-track{
  height:8px;border-radius:var(--radius-full);
  background:var(--track)/* pairing-exempt: carries no text */}
.ck-slider::-moz-range-progress{
  height:8px;border-radius:var(--radius-full);
  background:var(--primary)/* pairing-exempt: carries no text */}
.ck-slider::-moz-range-thumb{
  box-sizing:border-box;width:20px;height:20px;
  border:2px solid var(--primary);border-radius:var(--radius-full);
  background:var(--background);box-shadow:var(--shadow-raised)/* pairing-exempt: carries no text */}

.ck-slider:hover::-webkit-slider-thumb{border-color:var(--primary-hover)}
.ck-slider:hover::-moz-range-thumb{border-color:var(--primary-hover)}
.ck-slider:active::-webkit-slider-thumb{border-color:var(--primary-active)}
.ck-slider:active::-moz-range-thumb{border-color:var(--primary-active)}
.ck-slider:focus-visible{outline:0}
.ck-slider:focus-visible::-webkit-slider-thumb{box-shadow:var(--focus-ring)}
.ck-slider:focus-visible::-moz-range-thumb{box-shadow:var(--focus-ring)}
.ck-slider[aria-invalid="true"]::-webkit-slider-thumb{border-color:var(--destructive)}
.ck-slider[aria-invalid="true"]:focus-visible::-webkit-slider-thumb{box-shadow:var(--focus-ring-error)}
.ck-slider:disabled{cursor:not-allowed}
.ck-slider:disabled::-webkit-slider-runnable-track{
  background:linear-gradient(to right,
    var(--input) 0 var(--ck-slider-pct),
    var(--track) var(--ck-slider-pct) 100%)/* pairing-exempt: carries no text */}
.ck-slider:disabled::-webkit-slider-thumb{
  border-color:var(--input);background:var(--muted)/* pairing-exempt: carries no text */}
.ck-slider:disabled::-moz-range-track{background:var(--track)/* pairing-exempt: carries no text */}
.ck-slider:disabled::-moz-range-thumb{border-color:var(--input);background:var(--muted)/* pairing-exempt: carries no text */}

.ck-slider-row{display:flex;align-items:center;gap:var(--space-md)}
/* A slider on an action bar — a document's zoom — is one control among
   icon buttons, not a form row, so it takes a fixed 160px (set
   --ck-bar-slider to change it) instead of the row's full width, and packs its
   read-out at the bar's own 8px. */
.ck-action-bar .ck-slider-row{inline-size:var(--ck-bar-slider,160px);flex:none;gap:var(--space-sm)}
.ck-slider-val{
  flex:none;min-width:var(--space-3xl);text-align:end;
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);font-variant-numeric:tabular-nums;
  color:var(--foreground)}

/* ─── KBD (Atom) ──────────────────────────────────────────────────────────
   A keycap in running text. --secondary / --secondary-foreground rather than
   --muted / --muted-foreground: a keycap carries 11px text, and
   muted-foreground on muted is 3.16:1 (§ 9 defect 1). The secondary pair is a
   proper family and clears AA.

   Two literals, both this component's identity and recorded in its sheet: the
   2px bottom border that reads as a key's bevel, and the 20px cap that matches
   --icon-box so a keycap and an icon line up in the same row.

   Radius is --radius-sm, not the --radius-xs the § 7b mark tier gives inline
   code. A keycap is an object, not a code span; 2px on a 24px box reads as a
   rendering artefact.

   Spec: references/components/kbd.md */
.ck-kbd{
  display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  min-width:var(--icon-box);height:var(--icon-box);padding:0 var(--space-xs);
  border:1px solid var(--input);border-bottom-width:2px;border-radius:var(--radius-sm);
  background:var(--secondary);color:var(--secondary-foreground);
  font:var(--font-weight-medium) var(--text-sm) var(--font-sans);
  font-variant-numeric:tabular-nums;line-height:1;
  white-space:nowrap;vertical-align:middle}
.ck-kbd.size-sm{min-width:var(--space-base);height:var(--space-base);font-size:var(--text-xs)}
.ck-kbd.size-lg{min-width:var(--space-xl);height:var(--space-xl);padding:0 var(--space-sm);font-size:var(--text-md)}
/* A combination — Cmd + K. The joiner is text so it can be translated. */
.ck-kbd-combo{
  display:inline-flex;align-items:center;gap:var(--space-xs);
  font:var(--font-weight-normal) var(--text-sm) var(--font-sans);color:var(--muted-foreground)}

/* ─── TOGGLE (Atom) ───────────────────────────────────────────────────────
   A button that stays pressed. Distinct from .ck-switch: a switch commits an
   on/off setting immediately and announces as role="switch"; a toggle is a
   button whose pressed state is a mode (bold, a filter, a view). They are not
   substitutes for each other.

   On the § 7a control scale — 28 / 34 / 40 — because a toggle shares rows with
   buttons and icon buttons.

   State is driven by aria-pressed, not a class, so what is drawn cannot drift
   from what is announced (§ 14.3 check 12). Requires aria-pressed; a glyph-only
   toggle also requires aria-label.

   Spec: references/components/toggle.md */
.ck-toggle{
  display:inline-flex;align-items:center;justify-content:center;gap:var(--space-xs);
  height:36px;min-width:36px;padding:0 var(--space-md);box-sizing:border-box;
  border:var(--btn-border-width) solid transparent;border-radius:var(--radius-md);
  background:transparent;color:var(--foreground);
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);white-space:nowrap;cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base),border-color var(--duration-base)}
.ck-toggle svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);flex:none;display:block}
.ck-toggle.size-sm{height:28px;min-width:28px;padding:0 var(--space-sm);gap:var(--space-2xs)}
.ck-toggle.size-lg{height:44px;min-width:44px;padding:0 var(--space-base);gap:var(--space-sm)}

.ck-toggle:hover{background:var(--hover-bg)}
.ck-toggle:active{background:var(--selected-bg)}
.ck-toggle[aria-pressed="true"]{
  background:var(--selected-bg);color:var(--selected-fg);border-color:var(--primary)}
.ck-toggle[aria-pressed="true"]:hover{background:var(--hover-bg);color:var(--selected-fg)}
.ck-toggle:disabled,.ck-toggle[aria-disabled="true"]{
  background:var(--muted);color:var(--muted-foreground);border-color:var(--input);
  cursor:not-allowed;pointer-events:none}
.ck-toggle:focus-visible{outline:0;box-shadow:var(--focus-ring)}

/* A segmented group. Nested radii step inward (§ 14.8 check 6): the group is
   --radius-md, so its children drop to --radius-sm. */
.ck-toggle-group{
  display:inline-flex;align-items:center;gap:var(--space-2xs);box-sizing:border-box;
  padding:var(--space-2xs);
  border:var(--btn-border-width) solid var(--btn-border);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground)}
.ck-toggle-group .ck-toggle{border-radius:var(--radius-sm);border-color:transparent}
.ck-toggle-group .ck-toggle[aria-pressed="true"]{border-color:transparent}

/* The TROUGH form — .ck-toggle-group.is-segment.
   Same group, different reading. The plain group is a row of buttons resting
   on the page; the segment is a recessed trough with one option raised out of
   it, which is the shape a SETTING wants: the chosen value looks selected
   rather than merely pressed.

   --segment / --segment-foreground were minted for exactly this and had no
   component to belong to, so a parallel .ck-segment grew up beside the group
   and duplicated it — two ways to draw the same control, drifting apart. This
   is the one.

   The raised option takes --background, the page's own surface, so it reads as
   lifted out of the trough rather than tinted inside it. Hover does not fill,
   because a trough that fills on hover looks like four selected options. */
.ck-toggle-group.is-segment{
  border-color:transparent;
  background:var(--segment);color:var(--segment-foreground)}
.ck-toggle-group.is-segment .ck-toggle{color:inherit}
.ck-toggle-group.is-segment .ck-toggle:hover{background:transparent;color:var(--foreground)}
.ck-toggle-group.is-segment .ck-toggle:active{background:transparent}
.ck-toggle-group.is-segment .ck-toggle[aria-pressed="true"]{
  background:var(--background);color:var(--foreground);
  border-color:transparent;box-shadow:var(--shadow-sm)}
.ck-toggle-group.is-segment .ck-toggle[aria-pressed="true"]:hover{
  background:var(--background);color:var(--foreground)}
/* Fills its container and splits the width evenly — for a dialog header, where
   a trough narrower than the dialog reads as a stray control. */
.ck-toggle-group.is-stretch{display:flex;width:100%}
.ck-toggle-group.is-stretch .ck-toggle{flex:1}

/* STACKED — .ck-toggle.is-stacked
   An option that carries its consequence under its name, for a setting whose
   choices are not self-evident. "Exception / Must be resolved" says in two
   lines what a tooltip says in none, and it says it to everyone at once
   instead of only to whoever hovers.

   This is the one place a toggle leaves the 28 / 36 / 44 height scale, and it
   barely leaves it: 44 is the floor — the scale's own top step — and the box
   grows from there when a caption wraps. A fixed height would clip the caption
   at narrow widths, which is the failure the variant exists to avoid.

   In dark, --muted-foreground and --segment-foreground resolve to the same
   value, so an unpressed caption and its label are the same colour there. The
   hierarchy is carried by size and weight instead, which is the rule anyway:
   colour is never the only cue. Pressed, the label takes --foreground and the
   two separate in every theme.

   Spec: references/components/toggle.md section Stacked */
.ck-toggle.is-stacked{
  height:auto;min-height:44px;
  flex-direction:column;align-items:flex-start;justify-content:center;
  gap:var(--space-2xs);padding-block:var(--space-xs);
  white-space:normal;text-align:start}
.ck-toggle-title{
  display:flex;align-items:center;gap:var(--space-2xs);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans)}
.ck-toggle-title svg{width:var(--icon-size);height:var(--icon-size);flex:none}
.ck-toggle-sub{
  font:var(--font-weight-normal) var(--text-sm)/1.3 var(--font-sans);
  color:var(--muted-foreground)}

/* ─── LINK BUTTON — .ck-btn.link (Atom variant) ───────────────────────────
   v2.0 shipped "Link Button" with sm / md / lg, i.e. a button-shaped link, so
   this is a fifth .ck-btn variant rather than a new component (§ 10 rule 1:
   try harder to reuse). It keeps the control height scale, so it still aligns
   in a dialog footer beside a filled button.

   Colour is --link, which exists precisely for this and is distinct from
   --primary. The underline carries the affordance, since there is no fill and
   no border. Declared after the .ck-btn base so it wins on source order.

   Spec: references/components/button.md § Link variant */
.ck-btn.link{
  background:transparent;color:var(--link);border-color:transparent;
  text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px}
.ck-btn.link:is(:hover,[data-force~="hover"]){
  background:transparent;color:var(--link);text-decoration-thickness:2px}
.ck-btn.link:is(:active,[data-force~="active"]){background:var(--hover-bg);color:var(--link)}

/* ─── INLINE LINK — .ck-link ────────────────────────────────────────────────
   A link inside a sentence: an address to visit, a sample to download. The
   link button keeps the control height so it can sit in a footer; in running
   text that height breaks the line. This is the same affordance at the text's
   own size — --link, underlined, the underline thickening on hover (as
   .ck-cell-email does in a table) — and it inherits the sentence's font. A
   <button> can wear it too, for an action phrased as a link.

   Spec: references/components/button.md § Inline link */
.ck-link{color:var(--link);font:inherit;text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px;
  background:none;border:0;padding:0;cursor:pointer}
.ck-link:is(:hover,[data-force~="hover"]){text-decoration-thickness:2px}
.ck-link:focus-visible{outline:0;box-shadow:var(--focus-ring);border-radius:var(--radius-xs)}
.ck-btn.link:is(:disabled,.disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:transparent;color:var(--muted-foreground);border-color:transparent;
  text-decoration-color:var(--muted-foreground)}
.ck-btn.link:is(:focus-visible,[data-force~="focus"]){outline:0;box-shadow:var(--focus-ring)}
/* The neutral link: a quiet text action beside content that should stay
   louder, such as a group head's Resolve All. It rests in the text colour and
   turns --link on hover and focus, which is where it shows it is a link. The
   underline stays as the affordance. */
.ck-btn.link.is-neutral{color:var(--foreground)}
.ck-btn.link.is-neutral:is(:hover,:focus-visible,[data-force~="hover"],[data-force~="focus"]){color:var(--link)}
/* A link whose TEXT lines up with its row's edge. Its padding and border are
   the target, so they stay; the margin pulls the box out by exactly that much,
   and the label lands on the same inset as the content opposite it. Without
   it, a trailing link sits 11px further in than a leading label — the
   exception group head's Resolve All read 28px from the edge against 17px. */
.ck-btn.link.is-flush-end{margin-inline-end:calc(-1 * (14px + 1px))}
.ck-btn.link.is-flush-end.size-sm{margin-inline-end:calc(-1 * (10px + 1px))}
.ck-btn.link.is-flush-end.size-lg{margin-inline-end:calc(-1 * (18px + 1px))}
.ck-btn.link.is-flush-start{margin-inline-start:calc(-1 * (14px + 1px))}
.ck-btn.link.is-flush-start.size-sm{margin-inline-start:calc(-1 * (10px + 1px))}
.ck-btn.link.is-flush-start.size-lg{margin-inline-start:calc(-1 * (18px + 1px))}

/* ─── PAGINATION (Organism) ───────────────────────────────────────────────
   Page list, prev/next, a gap marker, a row count, and a per-page chooser.

   The numeral buttons are on the control scale so the whole bar lines up with
   the toolbar above it; the size class goes on .ck-pag and cascades, which is
   the same placement rule as .ck-dd (§ 14.1: a size on the inner element is a
   defect). Prev/next are .ck-icon-btn and the per-page chooser is .ck-dd —
   neither is reimplemented here.

   The current page is --primary / --primary-foreground rather than the
   --selected-bg pair: it is the one unambiguous position marker in the bar and
   needs to outrank hover, which owns --hover-bg.

   Spec: references/components/pagination.md */
/* ─── PAGINATION — a table footer bar ──────────────────────────────────────
   Sequential nav only: first / previous / next / last, in .ck-pag-nav. There
   is no numbered page list, because the product pages through tables one step
   at a time. .ck-pag carries no size class — the controls inside bring their
   own. Spec: references/components/pagination.md */
.ck-pag{
  display:flex;align-items:center;gap:var(--space-lg);flex-wrap:wrap;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground)}
.ck-pag-info{color:var(--muted-foreground);font-variant-numeric:tabular-nums}

/* ── Bar form — .ck-pag.is-bar ────────────────────────────────────────────
   A table footer rather than a run of page numbers: rows selected, total,
   rows-per-page, "Page X of Y", and first/prev/next/last. Ported from the
   v2.0 reference, re-expressed in v3.2 terms — its own version used dead
   token names (--bg, --fg, --border-strong), opacity for disabled, a
   scale(.95) press and off-scale 32px buttons, all of which the rulebook now
   forbids. Structure and behaviour are the reference's; the values are the
   kit's. Spec: references/components/pagination.md */
/* The bar keeps its shape when the panel gets narrow: the run scrolls
   sideways instead of stacking. Wrapping used to reflow the footer into two or
   three lines, which moved the buttons under the reader's cursor and changed
   the panel's height as the window resized. Scrolling keeps every control
   where the user last saw it. */
.ck-pag.is-bar{
  justify-content:space-between;flex-wrap:nowrap;gap:var(--space-base);width:100%;
  min-height:48px;padding:var(--space-sm) var(--space-md);box-sizing:border-box;
  border-top:1px solid var(--border);
  color:var(--muted-foreground);
  overflow-x:auto;overflow-y:hidden;overscroll-behavior-x:contain;scrollbar-width:thin}
.ck-pag.is-bar > *{flex:none}
/* Nothing selected and no count to show: the run collapses to the right. */
.ck-pag.is-bar.is-compact{justify-content:flex-end}
.ck-pag-total{white-space:nowrap;font-variant-numeric:tabular-nums}
.ck-pag-total > strong{
  margin-inline-start:var(--space-xs);
  font-weight:var(--font-weight-semibold);color:var(--foreground)}
.ck-pag-group{display:flex;align-items:center;gap:var(--space-lg);flex-wrap:nowrap}
.ck-pag-rpp{display:flex;align-items:center;gap:var(--space-sm);white-space:nowrap}
/* Documented in pagination.md's markup from the start and never actually
   declared, so every flow that followed the spec sheet got an unstyled
   label and quietly restyled it locally. It is a quiet label beside the
   control, not a field label above one. */
.ck-pag-rpp-label{color:var(--muted-foreground);white-space:nowrap}
/* Fixed width, so stepping 9 -> 10 does not shift the buttons beside it. */
.ck-pag-page{
  white-space:nowrap;text-align:center;min-width:var(--space-7xl);
  font-variant-numeric:tabular-nums;color:var(--foreground)}
.ck-pag-nav{display:flex;align-items:center;gap:var(--space-sm)}
/* A page you can type — .ck-pag-page holding a .ck-input.size-sm.is-num: the
   reader jumps straight to a position ("10 / 24.3K") instead of stepping. The
   field is 48px, wide enough for five digits, so the run beside it never
   shifts as the number grows. */
.ck-pag-page:has(> .ck-input){display:inline-flex;align-items:center;gap:var(--space-xs);min-width:0}
.ck-pag-page > .ck-input{inline-size:var(--space-4xl);text-align:center}

/* Narrow viewports tighten the spacing; they do NOT reflow the bar. The
   horizontal scroll above is what handles the squeeze. */
@media (max-width:768px){
  .ck-pag.is-bar{gap:var(--space-md);padding:var(--space-sm)}
  .ck-pag-group{gap:var(--space-md)}
}

/* ─── TABLE PANEL (Organism) ──────────────────────────────────────────────
   A table and its pagination footer in one bordered panel.

   The footer belongs to the PANEL, not to the last row. It sits on the panel's
   bottom edge whether the table holds two hundred rows, a single row, or is
   still loading — so it never floats up under a short table and the panel's
   height never jumps as rows arrive. The rows take the slack and scroll; the
   footer keeps its place.

   Spec: references/components/table.md § Panel */
.ck-table-panel{
  display:flex;flex-direction:column;width:100%;min-height:0;box-sizing:border-box;
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  overflow:hidden}
/* The rows scroll; belt and braces so the footer is last even with no wrap. */
/* ── The scroll box on its own — .ck-table-scroll ──
   For a table that sits directly in a surface which already frames it, such
   as a .ck-panel body. .ck-table-panel there would be a box inside a box
   (check 8), so the scroll box stands alone and carries no frame of its own.

   It carries no padding either, on purpose, and that is the whole point of
   it. A sticky header pins at top:0 of its SCROLLPORT, and a scrollport
   includes the scroll container's padding — so a table scrolling inside a
   padded container pins its header BELOW the padding and the rows scroll up
   through the strip above it. Measured on the permission matrix: a 20px strip
   of body rows showing above the pinned header. The pinned label column has
   the same failure sideways. Keep the inset on the parent, outside the
   scrollport, and let this box do the scrolling with its edges flush.

   Before this rule the class had rules only as a child of .ck-table-panel,
   so used alone it did nothing at all. */
.ck-table-scroll{flex:1 1 auto;min-height:0;min-width:0;overflow:auto;box-sizing:border-box}
.ck-table-panel > .ck-table-scroll{flex:1 1 auto;min-height:0;overflow:auto}
.ck-table-panel > .ck-pag{flex:none;margin-block-start:auto}
/* Given a height, the panel fills it and the footer rides the bottom edge. */
.ck-table-panel.is-filled{height:100%}
/* A filled panel under a heading and a toolbar. height:100% only works when
   the panel is the body's one child; beside a page head it overflowed, so
   screens left it off and the footer rode up under the last row. As a flex
   child the panel takes whatever room is left, so the pagination stays locked
   to the bottom edge with space above it when there are few rows, and the
   rows scroll inside when there are many. It never shrinks below six rows. */
.ck-panel-body:has(> .ck-table-panel.is-filled){display:flex;flex-direction:column}
.ck-panel-body > .ck-table-panel.is-filled{flex:1 1 auto;height:auto;
  /* --table-row-height is declared on the table, not the panel: 40px is its default */
  min-height:calc(var(--table-row-height, 40px) * 6)}

/* ── Multi-value field — .ck-multi-values ─────────────────────────────────
   The third form. Where the trigger form shows what is chosen and the picker
   form is a panel for choosing, this one lets a value be TYPED: one row per
   value, each a real input with its own remove, plus an add control.

   Use it where the set is open-ended — a document's alternate references, a
   list of email recipients — and the trigger/picker forms where the options
   are known. Ported from the v2.0 document-fields multi-value row.

   Spec: references/components/multi-select.md § Multi-value form */
.ck-multi-values{display:flex;flex-direction:column;gap:var(--space-xs);min-width:0}
.ck-multi-value{display:flex;align-items:center;gap:var(--space-xs);width:100%}
.ck-multi-value > :is(.ck-input,.ck-input-wrap){flex:1;min-width:0}
/* The remove is the kit's own destructive icon button at the field's own
   height, so the row reads as one control rather than a field plus a button. */
.ck-multi-value > .ck-icon-btn{flex:none}
.ck-multi-add{align-self:flex-start}

/* ─── FULFILMENT STATUS — retired into the pill ────────────────────────────
   v2.0's four fulfilment states were briefly their own component. They are
   not: a count beside a label in a toned pill is the pill's OWN anatomy —
   .ck-pill-label plus .ck-pill-count, which already ships. A second set of
   classes for it was exactly the duplication § 14.6 check 12 forbids.

     fulfilled     .ck-pill.is-success      remaining      .ck-pill.is-info
     excess        .ck-pill.is-warning      not fulfilled  .ck-pill.is-error

   So there is no CSS here. Spec: references/components/pill.md § Fulfilment
   status; rename map in rulebook § 2. */

/* ─── LABELLED CHECKBOX ROW — .ck-inline-check (Atom addition) ────────────
   A checkbox with its label beside it. The kit had the box and it had the
   field label, but nothing joining them — so every flow that needed "tick this
   and here is what it means" invented its own row in page CSS, which is § 12.4
   exactly. The auth card was doing it with a `.ckx-inline` that lived in the
   demo chrome.

   It is a <label> wrapping the input, so the WHOLE ROW is the hit target and
   no `for`/`id` pair can go stale. align-items:flex-start, not center, because
   the label may wrap to two lines and the box belongs beside the first one.
   The 1px nudge is optical: a 16px box against a 20px line box sits high by
   about a pixel. */
/* ─── LABELLED CONTROLS — .ck-inline-check / .ck-inline-switch ────────────
   A checkbox or a switch with its label beside it, in every state the control
   has. The bare atoms carry no label at all, which is fine inside a table row
   or a menu where something else names them — but a control standing on its
   own in a form needs a name, and that name has to answer to the pointer.

   BOTH ARE A <label>, and that is the whole mechanism. The browser forwards a
   click on the text to the label's control, so the whole thing is one target
   with no script — and it works for the switch too, even though the switch is
   a <button role="switch"> rather than an input: a button is a labelable
   element, so `label.control` resolves to it. No handler to desync, and it
   satisfies check 38 (a row with a control is clickable across its width) by
   construction rather than by wiring.

   THE LABEL FOLLOWS THE CONTROL'S STATE. Disabled greys the text and takes the
   cursor with it; invalid turns it destructive. A control that reads as
   disabled next to a label that reads as normal is the state told half-way.

   Spec: references/components/checkbox.md and switcher.md */
.ck-inline-check,.ck-inline-switch{
  display:inline-flex;align-items:flex-start;gap:var(--space-sm);
  cursor:pointer;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--foreground)}
.ck-inline-check > .ck-check{flex:none;margin-block-start:1px}
.ck-inline-switch > :is(.switch,.ck-switch){flex:none}
.ck-inline-check > span,.ck-inline-switch > span{min-width:0}
/* Disabled: the label goes with the control. */
.ck-inline-check:has(.ck-check:disabled),
.ck-inline-switch:has(:is(.switch,.ck-switch):is(:disabled,.is-disabled,[aria-disabled="true"],[data-force~="disabled"])){
  cursor:not-allowed;color:var(--muted-foreground)}
/* Invalid: so does the label. */
.ck-inline-check:has(.ck-check[aria-invalid="true"]),
.ck-inline-switch:has(:is(.switch,.ck-switch)[aria-invalid="true"]){color:var(--destructive)}
/* A hint under the label. It goes INSIDE the label's text span, not beside it:
   the wrapper is a flex row, so a third child would sit in a third column
   rather than under the text. Nested, it already starts at the label's own
   edge and needs no indent — the earlier rule added one on top of that and
   pushed the hint 24px past the label it belongs to. */
.ck-inline-check-hint,.ck-inline-hint{
  display:block;margin-block-start:var(--space-2xs);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-inline-check:has(.ck-check:disabled) .ck-inline-hint,
.ck-inline-switch:has(:is(.switch,.ck-switch):is(:disabled,.is-disabled,[aria-disabled="true"])) .ck-inline-hint{
  color:var(--input)}
/* Label first, control pushed to the trailing edge — the settings-row reading,
   where the eye scans a column of names and the controls line up on the right.
   The control still comes FIRST in the markup, so the label element keeps its
   control and nothing about the forwarding changes. */
.ck-inline-check.is-reversed,.ck-inline-switch.is-reversed{
  display:flex;align-items:center;width:100%;gap:var(--space-base)}
.ck-inline-check.is-reversed > :is(.ck-check),
.ck-inline-switch.is-reversed > :is(.switch,.ck-switch){order:2;margin-inline-start:auto}
.ck-inline-check.is-reversed > span,.ck-inline-switch.is-reversed > span{order:1}

/* ─── FIELD LABEL — required / optional (Atom addition) ───────────────────
   v2.0's Label atom had required and optional affordances; .ck-field-label
   had neither. The asterisk is a ::after because it is punctuation, not
   content; "optional" is a real span in the markup because it is a word and
   has to be translatable.

   Spec: references/components/input.md § Label */
.ck-field-label[data-required]::after{
  content:'*';margin-inline-start:var(--space-2xs);color:var(--destructive)}
/* The asterisk belongs to the label's words, so it follows the text and any
   "(optional)"; a help glyph comes after both. The label is a flex row, so
   without this the ::after landed after the info button: "VAT ID (i) *". */
.ck-field-label > :is(.ck-icon-btn,.ck-tip-host,.ck-info){order:1}
.ck-field-optional{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}/* The container. It had no rule of its own, which is why it drew as a bare
   stack: no corners and no edge to say where it began or ended. A subtle
   stroke and the panel radius; overflow:hidden so the first and last rows are
   clipped to the corners instead of squaring them off. */
.ck-accordion{
  display:flex;flex-direction:column;box-sizing:border-box;
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  overflow:hidden}
/* Two documents are two containers, not two rows of one: each has its own
   corners, its own edge and its own error state, and stacking them in a single
   box implies they share a fate. 8px between them. */
.ck-accordion + .ck-accordion{margin-top:var(--space-sm)}
.ck-accordion-stack{display:flex;flex-direction:column;gap:var(--space-sm)}
.ck-accordion-stack > .ck-accordion + .ck-accordion{margin-top:0}
/* .is-flush — the accordion IS its container's content (the sections of a
   fields panel), so it draws no box of its own: no edge, no corners, no fill.
   Put it in a flush body (.ck-panel-body.is-flush). A boxed accordion inside
   a padded panel stacked two insets and a border between the panel edge and
   the first control — 25px where the section had 12. */
.ck-accordion.is-flush{border:0;border-radius:0;background:transparent;color:inherit}
/* NO LINES BETWEEN LIST ITEMS. The rows of a list — accordion items, menu
   items, dropdown options, picker rows — are one list, told apart by their
   own padding and hover, not by a rule under each. The same goes for the
   line under a menu's or dropdown's search row and its select-all row: the
   list follows directly. (Accordion items used to carry --accordion-stroke
   between them.) Component rule in each sheet; bible check 80. */
.ck-accordion-trigger{
  display:flex;align-items:center;gap:var(--space-md);width:100%;box-sizing:border-box;
  height:44px;padding:var(--container-inset-bar);
  border:0;background:transparent;color:var(--card-foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);text-align:start;cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base)}
.ck-accordion.size-sm .ck-accordion-trigger{height:36px;padding:var(--container-inset-bar-sm);font-size:var(--text-base)}
.ck-accordion.size-lg .ck-accordion-trigger{height:52px;padding:var(--container-inset-bar-lg)}
.ck-accordion-trigger:hover{background:var(--hover-bg)}
.ck-accordion-trigger:active{background:var(--selected-bg);color:var(--selected-fg)}
.ck-accordion-trigger[aria-expanded="true"]{color:var(--primary)}
.ck-accordion-trigger:disabled,.ck-accordion-trigger[aria-disabled="true"]{
  color:var(--input);cursor:not-allowed;pointer-events:none;background:transparent}
.ck-accordion-trigger:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-accordion-label{flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap}
/* The glyph is chevron-DOWN at rest and turns UP when expanded — down means
   "there is more below", up means "collapse it". Expanding runs 0 -> 180deg,
   collapsing 180 -> 360deg rather than back through 0, so it turns CLOCKWISE
   both ways (§ 14.9 checks 5-7). A single transform would interpolate 180 -> 0
   and read as counter-clockwise, which is the defect that rule catches.
   These are ck-chev-open / ck-chev-close, the SAME keyframes ClipperDropdown
   uses, so the two disclosures animate identically. ckAccordion() carries
   .is-collapsing for the animation's duration. */
.ck-accordion-chev{
  width:var(--icon-size);height:var(--icon-size);flex:none;
  display:inline-grid;place-items:center;color:var(--muted-foreground);
  transform:rotate(0deg)}
.ck-accordion-chev svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
.ck-accordion-trigger[aria-expanded="true"] .ck-accordion-chev{
  transform:rotate(180deg);
  transition:transform var(--duration-chevron) var(--ease-default)}
.ck-accordion-item.is-collapsing .ck-accordion-chev{
  animation:ck-chev-close var(--duration-chevron) var(--ease-default) forwards}
/* Errored item — the soft-semantic triplet, so the row reads as a problem
   without the panel becoming a wall of red. The tone is redundancy: the
   trigger must still say what is wrong in words. */
/* ── ONE RULE FOR EVERY ROW'S SURFACE ──
   A row rests on WHITE — closed or open, errored or not — and tints only
   while the pointer is on it.

   OPENING IS NOT A STATE THE SURFACE REPORTS. The panel below is already the
   answer, and tinting the header as well leaves hover nothing to say: the row
   would be at its loudest before you had touched it.

   The TONE decides which tint arrives — --hover-bg for an ordinary row,
   --destructive-bg for an errored one. An errored row also carries a
   --destructive stroke AT ALL TIMES, which is what states the problem at rest
   now that the fill no longer does. Same rules in .is-document. */
.ck-accordion-item.is-error{background:var(--card);color:var(--destructive-soft-foreground)/* pairing-exempt: an errored row keeps the card surface and speaks in the destructive tone — that cross-pair IS the state. 5.29:1 on --card at rest and 4.58:1 on the --destructive-bg tint it takes on hover, both over the 4.5:1 body floor */}
.ck-accordion-item.is-error:is(:hover,[data-force~="hover"]){background:var(--destructive-bg)}
.ck-accordion-item.is-error + .ck-accordion-item,
.ck-accordion-item.is-error{border-color:var(--destructive)}
/* The container an errored document sits in wears the stroke too, or the red
   stops at the row and the tile still reads as an ordinary one. */
.ck-accordion:has(> .ck-accordion-item.is-error){border-color:var(--destructive)}
.ck-accordion-item.is-error .ck-accordion-trigger{color:var(--destructive-soft-foreground)}
.ck-accordion-item.is-error .ck-accordion-trigger[aria-expanded="true"]{
  color:var(--destructive-soft-foreground)}
.ck-accordion-item.is-error .ck-accordion-chev{color:var(--destructive-soft-foreground)}
.ck-accordion-item.is-error .ck-accordion-trigger:is(:hover,[data-force~="hover"]){
  background:transparent}
.ck-accordion-item.is-error .ck-accordion-trigger:is(:focus-visible,[data-force~="focus"]){
  box-shadow:var(--focus-ring-error)}
/* Disabled item — tokens, not opacity, so the label keeps its contrast. */
.ck-accordion-item.is-disabled{background:var(--muted);color:var(--muted-foreground)}
.ck-accordion-item.is-disabled .ck-accordion-trigger,
.ck-accordion-item.is-disabled .ck-accordion-chev{
  color:var(--muted-foreground);cursor:not-allowed;pointer-events:none}

/* ── THE HEADER ROW — .ck-accordion-head ───────────────────────────────────
   An accordion row that carries more than its trigger: a count, a switch, an
   action. The kit gave an accordion a trigger and nowhere to put anything
   beside it, so every screen that needed one built its own row and then
   flattened the trigger to fit inside it — which meant overruling the kit's
   height, padding, background and every state, per screen.

   The row owns the layout and the trigger keeps its own geometry, which is
   exactly the arrangement .ck-accordion.is-document already proved with
   .ck-doc-head. This is that pattern with the document specifics removed, so
   any accordion can use it.

   The count and the switch are SIBLINGS of the trigger, never inside it — a
   control inside a button is invalid and the parser closes the button early
   (§ 14.3 check 39). Composing them as siblings makes that hold by
   construction rather than by care.

   Spec: references/components/accordion.md */
.ck-accordion-head{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--space-md) var(--space-base);
  background:var(--accordion-bg);color:var(--card-foreground);
  transition:background var(--duration-base),color var(--duration-base)}
/* The trigger stops painting: the row does. It keeps only its flex share, so
   the label still truncates and the trailing controls stay put. */
.ck-accordion-head > .ck-accordion-trigger{
  flex:1;min-width:0;height:auto;padding:0;
  background:transparent;justify-content:flex-start}
.ck-accordion-head > .ck-accordion-trigger:is(:hover,:active,:focus-visible,[data-force~="hover"]){
  background:transparent;box-shadow:none}
/* Hover belongs to the row now, because the row is what the reader is over. */
.ck-accordion-head:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
/* And so does the focus ring: the trigger fills the row, so a ring drawn on the
   trigger alone stops short of the count and the switch and reads as a ring
   around the wrong thing. inset, or it is clipped by the accordion's radius. */
.ck-accordion-head:has(.ck-accordion-trigger:focus-visible){
  box-shadow:inset 0 0 0 2px var(--ring)}
/* The two size steps move the row's padding, not the trigger's — the trigger
   no longer has any. */
.ck-accordion.size-sm .ck-accordion-head{padding:var(--space-sm) var(--space-md)}
.ck-accordion.size-lg .ck-accordion-head{padding:var(--space-base) var(--space-lg)}
/* And the size steps must stop re-imposing a height on a trigger that is inside
   a row. `.ck-accordion.size-sm .ck-accordion-trigger` is three classes and
   beats the two-class flattening rule above, so a small accordion with a header
   row got its 36px back — the exact double inset the row exists to remove.
   Four classes, so the intent wins on specificity rather than on source order. */
.ck-accordion.size-sm .ck-accordion-head > .ck-accordion-trigger,
.ck-accordion.size-lg .ck-accordion-head > .ck-accordion-trigger{
  height:auto;padding:0}
/* An errored row keeps the rule, in its own tone. */
.ck-accordion-item.is-error > .ck-accordion-head:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-accordion-item.is-disabled > .ck-accordion-head{
  background:var(--muted);color:var(--muted-foreground)}
.ck-accordion-item.is-disabled > .ck-accordion-head:hover{background:var(--muted);color:var(--muted-foreground)}

/* The drawer takes its own surface. It shared --accordion-bg with
   .ck-accordion-head, so an open section was the same colour as the row that
   opened it and the boundary between chrome and content disappeared. */
.ck-accordion-panel{
  padding:var(--container-inset);
  background:var(--accordion-panel-bg);color:var(--card-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans)}
.ck-accordion.size-sm .ck-accordion-panel{padding:var(--container-inset-sm)}
/* A LIST OF ROWS IN A SECTION — a flush .ck-navlist as the panel's content.
   The panel's 12px plus the row's own 12px put every label 24px in, 12px right
   of the section title above it, and the trailing marks short of the head's
   chevron by the same. The panel keeps 4px either side (room for the selected
   lozenge) and the rows take 8px, so labels and trailing marks sit on the
   head's own 12px lines. Found on Edit Document's table list. */
.ck-accordion.size-sm .ck-accordion-panel:has(> .ck-navlist.is-flush){padding-inline:var(--space-xs)}
.ck-accordion.size-sm .ck-accordion-panel > .ck-navlist.is-flush > .ck-navlist-item{padding-inline:var(--space-sm)}
@media (prefers-reduced-motion:reduce){
  /* Shortened, not removed: dropping the animation would leave the chevron
     pointing the wrong way (§ 14.9 check 8). */
  .ck-accordion-trigger[aria-expanded="true"] .ck-accordion-chev,
  .ck-accordion-item.is-collapsing .ck-accordion-chev{animation-duration:1ms}}

/* ─── DOCUMENT ACCORDION (Organism) — .ck-accordion.is-document ────────────
   The Document Editor's split/page list, generalised. A file row that expands
   into a grid of page thumbnails, with a run of actions in the title area and
   a per-file error state.

   THE STRUCTURAL RULE: the actions are SIBLINGS of the trigger, never children
   of it. A <button> cannot contain other buttons — it is invalid HTML, and a
   screen reader announces one control where there are five. So the head is a
   row: the toggle is a button covering the chevron, icon, name and meta, and
   .ck-doc-actions sits beside it. This is the same restructuring member-card
   needs when a card becomes clickable.

   Spec: references/components/accordion.md § Document accordion */
/* ORDER: file icon, name, meta — all packed LEFT — then the actions, then the
   chevron LAST. The chevron is the rightmost thing in the row because it acts
   on the row as a whole; an action that acts on the file sits inboard of it.
   The name and meta pack left together so the eye reads one run of
   information rather than a name at one edge and its size at the other.

   The chevron is a SIBLING of the trigger, and it is aria-hidden with
   tabindex="-1": a pointer affordance only. The trigger already gives keyboard
   users the disclosure, and two controls in the accessibility tree for one
   action is noise, not redundancy. */
/* HOVER IS THE ROW'S, NOT THE TRIGGER'S. In this variant the actions and the
   chevron are SIBLINGS of the trigger, so tinting the trigger left the right
   end of the row — the whole icon run — untinted, and the accent stopped in
   mid-air. The head takes the tint and the trigger goes transparent, so the
   accent covers the row edge to edge, icons included. */
/* 16px on every side, and the padding is the HEAD's. The trigger used to
   bring its own 44px height and 12/16px padding, which sat inside the row on
   top of whatever the head had — so the checkbox ended up flush against the
   container edge while the filename started 24px after it. One box owns the
   inset now; the trigger just fills it. */
.ck-accordion.is-document .ck-doc-head{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--space-base);cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base)}
.ck-accordion.is-document .ck-doc-head:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
/* An errored document row follows the same rule, in its own tone. */
.ck-accordion-item.is-error .ck-doc-head:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}

.ck-accordion.is-document .ck-doc-head:has(.ck-accordion-trigger:focus-visible){
  box-shadow:inset 0 0 0 2px var(--ring)}
/* The trigger no longer paints anything: the row does. */
.ck-accordion.is-document .ck-accordion-trigger{
  flex:1;min-width:0;height:auto;padding:0;background:transparent}
.ck-accordion.is-document .ck-accordion-trigger:is(:hover,:active,:focus-visible){
  background:transparent;box-shadow:none}
/* The name takes the slack so the meta ends up beside it, not across the row. */
.ck-accordion.is-document .ck-accordion-label{flex:none;max-width:100%}
.ck-accordion.is-document .ck-doc-spacer{flex:1;min-width:var(--space-sm)}
  /* The document row's popovers — info, move, error tip — open on hover and are
     positioned inside the row, so this variant cannot clip to its radius the way
     the base accordion does. It opens the card and rounds the parts instead: the
     head takes the top corners, an open panel the bottom, and a row whose panel
     is closed takes all four. */
  .ck-accordion.is-document{overflow:visible}
  .ck-accordion.is-document > .ck-accordion-item{border-radius:inherit}
  .ck-accordion.is-document > .ck-accordion-item > .ck-doc-head{
    border-top-left-radius:inherit;border-top-right-radius:inherit}
  .ck-accordion.is-document > .ck-accordion-item:not(:has(> .ck-accordion-panel.is-open)) > .ck-doc-head{
    border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}
  .ck-accordion.is-document > .ck-accordion-item > .ck-accordion-panel.is-open{
    border-bottom-left-radius:inherit;border-bottom-right-radius:inherit}
/* Sits after .ck-doc-actions in the markup, so it is last in the row. */
.ck-doc-chev{
  flex:none;order:99;
  width:var(--icon-size);height:var(--icon-size);display:grid;place-items:center;
  border:0;background:0;padding:0;cursor:pointer;
  color:var(--muted-foreground);transform:rotate(0deg)}
.ck-doc-chev svg{width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-accordion-item[aria-expanded="true"] .ck-doc-chev,
.ck-doc-head[aria-expanded="true"] .ck-doc-chev{
  transform:rotate(180deg);
  transition:transform var(--duration-chevron) var(--ease-default)}
/* Both hosts, exactly as the open rule above takes both. The close listed
   only .ck-accordion-item, so a .ck-doc-head could animate open and then
   SNAPPED shut — the asymmetry read as a glitch rather than as a missing
   selector, which is how it survived. */
.ck-accordion-item.is-collapsing .ck-doc-chev,
.ck-doc-head.is-collapsing .ck-doc-chev{
  animation:ck-chev-close var(--duration-chevron) var(--ease-default) forwards}
/* The leading slot is a CHECKBOX, not a file glyph. Every row in this list is
   a file, so a file icon on each one said nothing; a checkbox says which files
   you are acting on, which is what the row is for.

   It is the .ck-check atom and sizes nothing itself, and it is a SIBLING of
   the trigger, never inside it — a button may not contain an input (§ 14
   check 39). Clicking it selects the file and must NOT expand the row;
   ckDocAccordion() treats it as its own control, the way it treats the
   action buttons. */
.ck-doc-check{flex:none;margin:0}
.ck-doc-meta{
  flex:none;color:var(--muted-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  font-variant-numeric:tabular-nums;white-space:nowrap}
/* THE RUN IS ALWAYS VISIBLE. It used to fade in as a block, which meant a
   reader could not tell what a row let them do without pointing at it first —
   and on touch there is no hover to point with, so the actions were reachable
   only by tabbing to something invisible.

   One action opts out: .is-hover-only. Edit is the destructive-adjacent one
   here — it changes the document rather than reading it, copying it or taking
   it away — so it stays out of the resting row and appears on approach. */
.ck-doc-actions{
  display:flex;align-items:center;gap:var(--space-2xs);flex:none;
  opacity:1}
/* Revealed on hover AND focus — hover alone is invisible to a keyboard
   (§ 14.4 check 4), and the control keeps its box either way, so the icons
   beside it never shift as it appears. */
.ck-doc-actions > .is-hover-only{
  opacity:0;transition:opacity var(--duration-base)}
.ck-doc-head:hover .ck-doc-actions > .is-hover-only,
.ck-doc-head:focus-within .ck-doc-actions > .is-hover-only,
.ck-doc-head[data-force~="hover"] .ck-doc-actions > .is-hover-only,
.ck-doc-actions > .is-hover-only:focus-visible{opacity:1}
/* An errored row shows everything: the reader is being asked to act on it. */
.ck-accordion-item.is-error .ck-doc-actions > .is-hover-only{opacity:1}

/* ── INLINE RENAME — .ck-doc-rename ───────────────────────────────────────
   The row's name turned into a field, with its confirm and cancel beside it.

   IT IS ONE BOX ON PURPOSE. The head is the accordion's click target, and a
   field and two buttons are exempt from that toggle by element type — but the
   GAPS BETWEEN THEM ARE NOT. A click landing two pixels wide of the tick
   collapsed the row instead of saving the name, which is the worst version of
   the failure: the reader thinks they saved and the editor is simply gone.
   The group is the unit the head exempts, exactly as .ck-doc-actions is.

   THE PAIR IS COLOURED, NOT JUST SHAPED (§ 14.3 check 53). Confirm takes
   .tone-success and cancel .tone-danger, each at its soft foreground at rest;
   a tick and a cross side by side in the neutral icon colour make the reader
   decode the glyph to tell saving from discarding.

     <span class="ck-doc-rename">
       <input class="ck-input size-sm" aria-label="Document name">
       <button class="ck-icon-btn size-sm tone-success" aria-label="Save Name">…
       <button class="ck-icon-btn size-sm tone-danger"  aria-label="Cancel Rename">…
     </span>

   The field takes the slack so the buttons sit against the name rather than
   drifting to the end of the row; size-sm on all three keeps the run on one
   box (§ 14.3 check 8), the same 28px the actions run uses. */
.ck-doc-rename{
  display:flex;align-items:center;gap:var(--space-2xs);
  flex:1;min-width:0}
.ck-doc-rename > .ck-input{flex:1;min-width:0}
/* .is-fit — a rename in a wide head, where the field taking the slack put the
   tick and cross a full panel-width from the name being edited. The field holds
   the width of the search beneath it (320px, still shrinking on a narrow panel)
   and the pair sits right against it. Found on Role Management. */
.ck-doc-rename.is-fit{flex:0 1 auto}
.ck-doc-rename.is-fit > .ck-input{flex:0 1 320px}

/* ── THE RENAME TRIGGER'S OWN GROUND — .ck-doc-rename-trigger ─────────────
   The pencil belongs beside the name — that is where the thing it renames is,
   and putting it out with the row actions makes the reader hunt for it. But
   the name run IS the head's click target, so the glyph is exempt by element
   type while the head's --space-sm gap either side of it is not: a click that
   misses a 24px target by two pixels collapses the row instead of opening the
   editor.

   The ground is given to the control itself, on a ::before, which is the same
   way a pill's dismiss keeps a 24x24 target inside a 16px paint box. It was a
   WRAPPER for one release and that was wrong twice over: an extra flex item
   put the head's gap on both sides of it, which added 8px to a row at rest
   that is meant to reserve nothing, and the wrapper's padding answered to
   nobody — a click there neither renamed nor toggled, a dead strip exactly
   where the reader was aiming.

   On the control there is no extra item, so layout is untouched, and the
   extended area IS the button, so every pixel of it opens the editor. */
.ck-doc-rename-trigger{position:relative}
.ck-doc-rename-trigger::before{
  content:'';position:absolute;inset-block:0;
  inset-inline:calc(var(--space-sm) * -1)}
/* A TITLE THAT CAN BE RENAMED — .has-rename. The pencil appears beside the
   name on approach (hover) and while the keyboard is in the title
   (focus-within), never at rest, the same way the column card's rename does.
   A touch screen has no approach, so there it always shows. Opening it swaps
   the name for the .ck-doc-rename group: field, tick (.tone-success), cross
   (.tone-danger). */
.has-rename > .ck-doc-rename-trigger{opacity:0;transition:opacity var(--duration-base)}
.has-rename:is(:hover,:focus-within,[data-force~="hover"]) > .ck-doc-rename-trigger{opacity:1}
@media (hover:none){.has-rename > .ck-doc-rename-trigger{opacity:1}}

/* ── Page thumbnails ── */
.ck-thumb-grid{
  display:grid;grid-template-columns:repeat(auto-fill,120px);
  gap:var(--space-md);justify-content:start}
/* ONE STROKE WEIGHT, AND IT IS 1px — rest, hover, focus, selected,
   previewing, errored, all of them. Colour is what says which state a page is
   in; the stroke only has to be visible, and varying its weight as well made
   the grid read as though some pages mattered more than others.

   box-sizing:border-box is kept regardless, so a tile can never move in the
   grid if a state ever does change the border. */
.ck-thumb{
  position:relative;overflow:hidden;aspect-ratio:3 / 4;min-width:0;box-sizing:border-box;
  border:1px solid var(--input);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground);cursor:pointer;
  transition:border-color var(--duration-base),box-shadow var(--duration-base)}
.ck-thumb:is(:hover,[data-force~="hover"]){border-color:var(--primary);box-shadow:var(--shadow-raised)}
.ck-thumb:focus-within{border-color:var(--primary);box-shadow:var(--focus-ring)}
.ck-thumb.is-selected{border-color:var(--primary);box-shadow:none}
/* A toned tile carries its state in the STROKE and keeps the page white, so
   the thumbnail still reads as a page rather than as a coloured card. The
   tint is the response to hover or selection — the tone tells you what kind
   of state, the fill tells you that you are on it. */
.ck-thumb.is-error{border-color:var(--destructive)}

/* ── REORDERING BY HAND — the two states a drag needs ──────────────────────
   .ck-thumb had hover, focus, selected, previewing and error, and nothing for
   being picked up: no state for the tile in flight, and no slot showing where
   it would land. Every page in the document workspace can be reordered, so
   both were being drawn by the page.

   TWO states, because a drag has two subjects: the thing you are moving and
   the place it is going. One class cannot be both.

   The cursor hangs off [draggable], not off a class, so it is true exactly
   when the attribute that makes the drag possible is present — a tile that
   merely looks draggable is worse than one that does not. */
.ck-thumb[draggable="true"]{cursor:grab}
.ck-thumb[draggable="true"]:active,
.ck-thumb.is-dragging{cursor:grabbing}
/* The tile in flight. It is dimmed rather than hidden: the reader needs to see
   what they are carrying, and removing it collapses the grid under the pointer.
   Opacity is the sanctioned exemption here — the tile is an image with no label
   of its own, so there is no text to drag down with it (§ 14.3 check 5). */
.ck-thumb.is-dragging{
  opacity:var(--disabled-opacity);
  border-color:var(--primary);box-shadow:var(--shadow-raised)}
/* Where it will land. A hollow dashed slot on the grid's own ground, the same
   language .ck-empty uses for "there is nothing here yet" — because that is
   exactly what this is, for as long as the drag lasts. It holds the tile's
   footprint so the grid does not reflow as the slot moves. */
.ck-thumb.is-placeholder{
  border:1px dashed var(--input);
  background:0;box-shadow:none;cursor:grabbing;
  pointer-events:none}
.ck-thumb.is-placeholder > *{visibility:hidden}
.ck-thumb.is-placeholder:is(:hover,:focus-within){
  border-color:var(--input);box-shadow:none}
/* A drop that is not allowed says so in the stroke, not by doing nothing. */
.ck-thumb.is-placeholder.is-error{border-color:var(--destructive)}
@media (prefers-reduced-motion:reduce){
  .ck-thumb.is-dragging{transition:none}
}
.ck-thumb.is-error:is(:hover,[data-force~="hover"]),
.ck-thumb.is-error.is-selected{background:var(--destructive-bg)}
/* ── PREVIEWING — .ck-thumb.is-previewing ──
   The page currently open in the preview pane. Amber, because it is neither
   selection (--primary) nor a problem (--destructive): it is "you are looking
   at this one", a third orthogonal state that has to be told apart from both.

   The stroke goes 2px so the ring is unmistakable at 120px wide, and the page
   badge turns amber with it — the state is carried TWICE, which matters
   because --warning against a white page is 2.05:1 and would be a weak
   signal on its own. The badge's own numeral is 7.38:1 on that fill, so the
   legible half of the pair is the half that carries meaning.

   Selection and previewing can both be true: a page can be ticked and open. */
.ck-thumb.is-previewing{border-color:var(--warning);box-shadow:none}
.ck-thumb.is-previewing:is(:hover,[data-force~="hover"]),
.ck-thumb.is-previewing.is-selected{background:var(--warning-bg)}
/* Selected AND previewing: amber wins the stroke, because "you are looking at
   this one" is the more perishable fact; the tick still shows selection. */
.ck-thumb.is-selected.is-previewing{border-color:var(--warning)}
.ck-thumb.is-previewing > .ck-thumb-badge{
  background:var(--warning);color:var(--warning-foreground)}
.ck-thumb-img{
  width:100%;height:100%;object-fit:cover;object-position:top center;display:block}
/* The scrim over a page image, so the preview control reads on any document.

   Two layers, because they animate to different places: the LAYER fades 0 -> 1
   so the control arrives at full strength, and the TINT lives on ::before at
   --scrim-opacity so the page behind it dims. One layer could not do both —
   fading the container to 0.45 took the preview button down with it, and a
   45%-opaque control on a document page is exactly the thing the scrim exists
   to prevent. */
.ck-thumb-overlay{
  position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity var(--duration-base);pointer-events:none}
.ck-thumb-overlay::before{
  content:'';position:absolute;inset:0;
  background:var(--scrim);opacity:var(--scrim-opacity)/* pairing-exempt: carries no text */}
.ck-thumb:hover .ck-thumb-overlay,
.ck-thumb:focus-within .ck-thumb-overlay,
.ck-thumb[data-force~="hover"] .ck-thumb-overlay{opacity:1}
.ck-thumb-overlay > *{position:relative;z-index:1;pointer-events:auto}
/* The preview control. 28px is the dense step of the control scale. */
.ck-thumb-eye{
  display:inline-grid;place-items:center;flex:none;box-sizing:border-box;
  width:28px;height:28px;padding:0;border:0;border-radius:var(--radius-md);
  /* pairing-exempt: the outline pattern — --primary on --background at
     12.01:1, the same declared exemption .ck-member-av carries. The glyph must
     read as the brand action against a scrimmed page image, and --foreground
     would make it look like part of the document. */
  background:var(--background);color:var(--primary);cursor:pointer;
  transition:background var(--duration-base)}
.ck-thumb-eye:hover{background:var(--hover-bg)}
.ck-thumb-eye:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* Disabled — tokens, not opacity, so the glyph keeps its contrast. A page that
   cannot be previewed (still uploading, or errored) must say so before the
   click, not after. */
.ck-thumb-eye:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:transparent;color:var(--input);cursor:not-allowed;pointer-events:none}
.ck-thumb-eye svg{width:var(--icon-size);height:var(--icon-size);display:block}
/* Selection sits above the overlay so it stays clickable while previewing. */
.ck-thumb-check{
  position:absolute;top:var(--space-xs);left:var(--space-xs);z-index:2;margin:0}
/* The page number reads over an image, so a SELECTED page takes the
   dark-toolbar family — minted for exactly this: chrome on content whose
   colour we do not control.

   AN UNSELECTED PAGE'S NUMBER IS QUIETER. A grid of near-black badges reads
   as fifteen selected pages; the badge should only shout when the page is
   actually chosen. So unselected drops to the muted pair on a --muted chip.
   That measures 3.16:1, which is under the 4.5:1 text floor — a deliberate
   trade, made because the number is a positional label the user is scanning
   rather than content they must read, and because the selected and previewing
   states both raise it back to full contrast. If it has to clear AA, the fill
   stays and the numeral becomes --foreground at 14.56:1. */
.ck-thumb-badge{
  position:absolute;bottom:var(--space-xs);right:var(--space-xs);z-index:2;
  display:inline-grid;place-items:center;min-width:20px;height:20px;
  padding:0 var(--space-xs);border-radius:var(--radius-full);
  background:var(--muted);color:var(--muted-foreground);
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);
  font-variant-numeric:tabular-nums}
/* A MIDTONE, not the darkest ink in the set. --toolbar-dark is the colour of
   a dark toolbar chrome; on a small badge over a white page it read as a hole
   punched in the thumbnail. --primary-active is the midtone of the primary
   ramp and still carries a white numeral at 6.51:1. */
.ck-thumb.is-selected > .ck-thumb-badge{
  background:var(--primary-active);color:var(--primary-foreground)}
.ck-thumb.is-error > .ck-thumb-badge{
  background:var(--destructive);color:var(--destructive-foreground)}

/* ─── TOOLTIP HOST — .ck-tip-host ─────────────────────────────────────────
   Wrap an icon control in this and its .ck-tip child appears on hover AND on
   keyboard focus, with no JS. The kit had the bubble but no wiring, so every
   flow was inventing its own — which is why icons shipped without tooltips
   despite the rule requiring them.

   Focus-within, not just hover: a tooltip that only appears on hover is
   invisible to a keyboard and fails WCAG 2.1.1 — the most common tooltip
   defect there is.

   The bubble is position:fixed on its own, so inside a host it switches to
   absolute and the host becomes the containing block. Placement classes keep
   working: the tail geometry is unchanged, only the offset is set here.

   Spec: references/components/tooltip.md § Attaching one */
/* A tooltip host anchors a tip and must not change how its element lays out.
   The display below is wrapped in :where() so it carries ZERO specificity: a
   bare <span class="ck-tip-host"> still gets a box to anchor against, while
   any component that brings its own layout keeps it.

   Without that guard this rule sat at 0-1-0 and, being later in the file, beat
   .ck-icon-btn's own display:inline-grid;place-items:center — so every icon
   button carrying a tooltip laid its glyph out flex-start and drew it left of
   centre. That was 20 buttons across the kit, not one. */
:where(.ck-tip-host){position:relative}
/* :where() so this is a DEFAULT, not a decree. .ck-tip-host is a utility any
   control can wear to get a tooltip, and at full specificity it silently
   outranked every component that positions itself — it sits near the end of
   the file, so it won on order too. The table's column delete lost its
   position:absolute to it three separate times, each fix correct and each
   one outranked. At zero specificity a component always wins, and an element
   with no position of its own still gets relative, which is all the tooltip
   anchoring needs. */
:where(.ck-tip-host){display:inline-flex;align-items:center}
/* Lifted out of a clipping ancestor. A tip inside a scroll container is cut
   off by it — overflow-y:auto makes the OTHER axis clip too, so no amount of
   z-index or side-switching rescues it. ckTipLift() switches these to fixed
   and positions them against the host's box. */
.ck-tip-host > .ck-tip{
  position:absolute;opacity:0;pointer-events:none;
  transition:opacity var(--duration-base) var(--ease-default);
  /* It used to say white-space:nowrap here, bundled in with the positioning,
     and at (0,2,0) that beat .ck-tip's own `normal` — so every HOSTED tooltip
     was forced onto one line and the component's max-width and overflow-wrap
     were dead. A tip with a sentence in it ran out of its own box.

     Removing it alone is NOT the fix, and measuring showed why: an absolutely
     positioned box shrink-to-fits between its min-content and max-content
     width, and overflow-wrap:anywhere makes min-content ONE CHARACTER. The
     tips collapsed to 34px wide and 3852px tall. width:max-content restores
     the intent — as wide as the text wants, then wrapped by the 320px cap.

     .ck-tip.is-single-line remains the variant for when one line IS the
     intent. */
  width:max-content}
.ck-tip-host:hover > .ck-tip,
.ck-tip-host:focus-within > .ck-tip,
.ck-tip-host[data-force~="hover"] > .ck-tip{opacity:1}
/* Offsets per placement. 8px clears the tail, which protrudes 4px. */
.ck-tip-host > .ck-tip.is-top{
  inset-block-end:calc(100% + var(--space-sm));inset-inline-start:50%;
  transform:translateX(-50%)}
.ck-tip-host > .ck-tip.is-bottom{
  inset-block-start:calc(100% + var(--space-sm));inset-inline-start:50%;
  transform:translateX(-50%)}
.ck-tip-host > .ck-tip.is-start{
  inset-inline-end:calc(100% + var(--space-sm));inset-block-start:50%;
  transform:translateY(-50%)}
.ck-tip-host > .ck-tip.is-end{
  inset-inline-start:calc(100% + var(--space-sm));inset-block-start:50%;
  transform:translateY(-50%)}
.ck-tip-host > .ck-tip.is-lifted{
  position:fixed;z-index:var(--z-tooltip);margin:0;transform:none;
  inset-block:auto;inset-inline:auto}

/* ─── ACTION BAR (Organism) ───────────────────────────────────────────────
   A floating run of icon actions over content — a document preview's tools, a
   selection's bulk actions. Two grounds:

     default   --card, for a bar over the page
     .on-dark  --toolbar-dark, for a bar over a document or an image, where a
               light bar would compete with the content. That is exactly what
               the --toolbar-dark family exists for.

   Buttons inside are .ck-icon-btn; .on-dark retunes their hover pair rather
   than restyling them, the same way .ck-icon-btn.on-dark already does.

   Spec: references/components/action-bar.md */
/* Spacing carries the grouping. 4px between icons inside a cluster, 12px
   between clusters, and a divider adds 4px either side — so a related run
   reads as one object and the next run reads as a different one. Proximity
   does the work; the divider only confirms it.

   The bar rests ON the page, so it casts nothing. Only .is-floating lifts. */
.ck-action-bar{
  display:inline-flex;align-items:center;gap:var(--space-md);box-sizing:border-box;
  padding:var(--space-sm);
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground)}
/* A bar on the canvas is a panel on the canvas, so it takes the panel's edge:
   --panel-stroke, as .ck-panel does. It drew --panel-border, which measures
   1.02:1 against --canvas, so the bar had no visible edge beside the panels
   next to it (Edit Document's toolbar beside the fields panel). Inside a panel
   it is .is-flush and draws no edge at all. */
/* AN EMPTY BAR IS NOT DRAWN. The bar carries its own stroke and radius, so with
   every control inside it hidden it still painted — an 18px ring with nothing
   in it. Found in the webhook editor's head, where Create hides both the audit
   download and the channel toggle. Children hide with [hidden]. */
.ck-action-bar:not(:has(> :not([hidden]))){display:none}
.ck-action-bar.size-sm{padding:var(--space-xs);gap:var(--space-sm)}
/* ── .is-flush — a card that is already inside one ───────────────────────
   .ck-action-bar and .ck-navlist are cards: --card ground, --panel-border
   stroke, --radius-2xl. That is right when they sit on the canvas. Inside a
   .ck-panel — a list column's head, the row of actions at the top of a detail
   pane — it is a card drawn inside a card, and the reader sees a frame around
   a frame for no reason.

   .is-flush drops the ground, the stroke and the radius and keeps everything
   else: the layout, the gaps, the states. The panel is the surface; this is
   only an arrangement on it. */
.ck-action-bar.is-flush,.ck-navlist.is-flush{
  /* and no padding. .is-flush zeroes the inset on every other container (see
     the --container-inset block); here it dropped only the frame, so a flush
     navlist inside a panel body kept its own 8px and its rows started 24px in
     while everything beside them started at 16. One modifier, one meaning:
     the container around me owns my frame and my inset both. */
  background:transparent;border-color:transparent;border-radius:0;padding:0}

.ck-action-bar.size-sm .ck-action-bar-group{gap:var(--space-2xs)}
/* ── THE BAR INSIDE A TABLE CELL — .ck-action-bar.is-cell ──
   The same run of actions with the bar's own frame taken off. In a row the
   CELL is already the container, and a second bordered capsule inside it
   reads as a control floating in a cell rather than as the row's own actions.

   Nothing is restated: the ground and the ink come from the row, so the bar
   stays correct through hover, selection and the errored state on its own. */
/* 8px between a row's actions, not 4: at 4 the icons read as one cluster
   and a hand aiming for Edit lands on Download. */
.ck-action-bar.is-cell{
  padding:0;gap:var(--space-sm);
  border:0;border-radius:0;
  background:transparent;color:inherit}
/* A row's actions led by a status read-out — .ck-action-bar.is-cell.has-lead.
   The read-out changes width row to row (a tick, "⚠ 9", "⚠ 12"), and in a flex
   run every icon after it moves with it, so the eye and the menu no longer
   form a column. The lead sits in a fixed track (--ck-bar-lead, 56px), started
   at its leading edge, and each icon after it lands on the same x in every row. */
.ck-action-bar.is-cell.has-lead{
  display:grid;grid-auto-flow:column;align-items:center;justify-items:start;
  grid-template-columns:var(--ck-bar-lead,56px);grid-auto-columns:auto}
/* Three-column split: leading run, a centred group, trailing run. A flex row
   cannot hold the middle group centred once the two sides differ in width —
   it drifts. The grid pins it regardless. */
.ck-action-bar.is-split{
  display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);
  gap:var(--space-md);width:100%}
.ck-action-bar-group{display:flex;align-items:center;gap:var(--space-xs);min-width:0}
.ck-action-bar-group.is-center{justify-content:center}
.ck-action-bar-group.is-end{justify-content:flex-end}
/* A bordered slot on the bar — a read-out like the zoom percentage, or a real
   input like the page number. One class for both, because they are the same
   thing: a value the user reads, and sometimes types into. 32px sits inside
   the bar's own 36px controls without touching them. */
.ck-action-bar-field{
  display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;
  height:32px;min-width:56px;padding:0 var(--space-md);
  border:1px solid var(--field-border);border-radius:var(--radius-md);
  background:transparent;color:var(--foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  font-variant-numeric:tabular-nums;white-space:nowrap}
input.ck-action-bar-field{width:48px;min-width:0;text-align:center;padding:0 var(--space-xs)}
/* In a .size-sm bar every control is 28px, and the field with them (check 8):
   at its own 32px it stood 4px taller than the buttons on its row. */
.ck-action-bar.size-sm .ck-action-bar-field{height:28px}
input.ck-action-bar-field:focus{outline:0;border-color:var(--primary);box-shadow:var(--focus-ring)}
.ck-action-bar.on-dark .ck-action-bar-field{
  border-color:var(--toolbar-dark-sep);color:var(--toolbar-dark-foreground)}
.ck-action-bar.on-dark input.ck-action-bar-field:focus{
  border-color:var(--toolbar-dark-foreground);box-shadow:none}
/* On the dark bar the separator takes the family's own rule colour, or it
   disappears into the surface. */
.ck-action-bar.on-dark .ck-divider.is-vertical{background:var(--toolbar-dark-sep)}
/* Floating — lifted off the page, not resting on it. --shadow-raised is the
   whole lift: x 0, y 1, blur 3. A deep multi-layer shadow made the bar read as
   a modal panel hovering over the page rather than a control attached to it. */
.ck-action-bar.is-floating{
  position:fixed;left:50%;transform:translateX(-50%);
  inset-block-end:var(--space-xl);z-index:var(--z-sticky);
  box-shadow:var(--shadow-raised)}
.ck-action-bar .ck-divider.is-vertical{
  min-height:var(--space-lg);margin-inline:var(--space-xs)}
.ck-action-bar-label{
  padding-inline:var(--space-sm);font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--muted-foreground);white-space:nowrap}

.ck-action-bar.on-dark{
  background:var(--toolbar-dark);color:var(--toolbar-dark-foreground);
  border-color:var(--toolbar-dark-sep)}
.ck-action-bar.on-dark .ck-action-bar-label{color:var(--toolbar-dark-muted)}
.ck-action-bar.on-dark .ck-icon-btn{
  color:var(--toolbar-dark-muted);
  --ck-icon-hover:var(--toolbar-dark-hover);
  --ck-icon-hover-fg:var(--toolbar-dark-foreground)}
.ck-action-bar.on-dark .ck-icon-btn.is-selected{
  background:var(--toolbar-dark-active);color:var(--toolbar-dark-foreground)}
.ck-action-bar.on-dark .ck-divider.is-vertical{
  background:var(--toolbar-dark-sep)/* pairing-exempt: carries no text */}

/* ─── AUTH CARD (Organism) ────────────────────────────────────────────────
   Sign-in / sign-up / reset. A --card panel centred on the page ground, at a
   fixed 400px because a login form wider than that reads as a settings page;
   that width is this component's identity and is recorded in its sheet.

   Everything inside is existing kit: .ck-field, .ck-input, .ck-btn.primary,
   .ck-btn.link, .ck-divider-label, .ck-alert for a failed attempt. The only
   rules here are the frame and its rhythm.

   The primary action is .size-lg — § 7a's stated case for it: a touch-first
   surface whose action is the only one on the screen.

   Spec: references/components/auth-card.md */
.ck-auth-shell{
  display:grid;place-items:center;min-height:100vh;padding:var(--space-xl);
  background:var(--background);color:var(--foreground)}
.ck-auth{
  width:100%;max-width:400px;box-sizing:border-box;
  display:flex;flex-direction:column;gap:var(--space-base);
  padding:var(--space-2xl);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground)}
.ck-auth.size-sm{max-width:340px;padding:var(--space-lg)}
.ck-auth.size-lg{max-width:480px}
.ck-auth-brand{display:flex;align-items:center;gap:var(--space-sm)}
.ck-auth-head{display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-auth-title{font:var(--font-weight-semibold) var(--text-3xl) var(--font-sans);color:var(--card-foreground)}
.ck-auth-sub{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.5}
.ck-auth-form{display:flex;flex-direction:column;gap:var(--space-md)}
.ck-auth-row{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}
.ck-auth-actions{display:flex;flex-direction:column;gap:var(--space-sm)}
.ck-auth-foot{
  text-align:center;font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}

/* ─── CONNECTOR CARD (Organism) ───────────────────────────────────────────
   One integration: its mark, its name, what it does, whether it is connected,
   and the action that changes that.

   The mark sits on --secondary rather than --background so a white vendor
   logo still has a plate under it in dark theme. Connection state is a
   .ck-pill tone plus the button's label — never the tone alone (§ 14.6
   check 13).

   Spec: references/components/connector-card.md */
.ck-connector-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));
  gap:var(--space-md)}
.ck-connector{
  display:flex;flex-direction:column;gap:var(--space-md);box-sizing:border-box;
  padding:var(--space-base);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  transition:background var(--duration-base),border-color var(--duration-base)}
.ck-connector:hover{border-color:var(--input)}
.ck-connector.is-connected{border-color:var(--success-border)}
.ck-connector-top{display:flex;align-items:flex-start;gap:var(--space-md)}
.ck-connector-mark{
  width:var(--space-3xl);height:var(--space-3xl);flex:none;box-sizing:border-box;
  display:grid;place-items:center;
  border:1px solid var(--panel-border);border-radius:var(--radius-md);
  background:var(--secondary);color:var(--secondary-foreground)}
.ck-connector-mark img{max-width:var(--space-xl);max-height:var(--space-xl);display:block}
.ck-connector-mark svg{
  width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-connector-main{flex:1;min-width:0}
.ck-connector-name{
  font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--card-foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-connector-meta{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);margin-top:var(--space-2xs)}
.ck-connector-desc{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.5;flex:1}
.ck-connector-foot{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm)}
/* .ck-connector.is-row — the connector on ONE line while it is closed: mark,
   name and meta, status pill, actions, switch. The top and foot rows dissolve
   into the card's own row (display:contents) and their children are ordered
   on it; anything else the card holds (a config form, an alert, a list) drops
   to a full-width line below. Same markup as the stacked card. */
/* A closed row is one line, so it sits on a tighter 12px inset on every side
   rather than the stacked card's 16px. */
.ck-connector.is-row{flex-direction:row;flex-wrap:wrap;align-items:center;column-gap:var(--space-md);padding:var(--space-md)}
.ck-connector.is-row :is(.ck-connector-top,.ck-connector-foot){display:contents}
.ck-connector.is-row .ck-connector-mark{order:1}
.ck-connector.is-row .ck-connector-main{order:2}
.ck-connector.is-row .ck-connector-foot > .ck-pill{order:3}
.ck-connector.is-row .ck-connector-foot > :not(.ck-pill){order:4}
.ck-connector.is-row .ck-connector-top > :is(.switch,.ck-switch){order:5}
.ck-connector.is-row > :not(.ck-connector-top):not(.ck-connector-foot){order:6;flex:1 1 100%}

/* ─── FEATURE / SUPPORT CARD (Organism) ───────────────────────────────────
   A capability and how well it is supported. The icon plate reads --accent so
   the card has a focal point without a second surface level, and the support
   level is a .ck-pill tone with its word in the label.

   Spec: references/components/feature-card.md */
.ck-feature-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));
  gap:var(--space-md)}
.ck-feature{
  display:flex;flex-direction:column;gap:var(--space-sm);box-sizing:border-box;
  padding:var(--container-inset);
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground)}
.ck-feature-icon{
  width:var(--space-2xl);height:var(--space-2xl);flex:none;
  display:grid;place-items:center;
  border-radius:var(--radius-md);
  background:var(--accent);color:var(--accent-foreground)}
.ck-feature-icon svg{
  width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-feature-title{
  display:flex;align-items:center;gap:var(--space-sm);
  font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--card-foreground)}
.ck-feature-title .ck-pill{flex:none}
.ck-feature-desc{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);line-height:1.5}
.ck-feature-list{
  display:flex;flex-direction:column;gap:var(--space-xs);
  /* It is a <ul>: without these it kept the browser's 40px indent and 1em
     margins, which pushed a popover's list far off its own title. */
  list-style:none;margin:0;padding:0;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-feature-list-item{display:flex;align-items:flex-start;gap:var(--space-sm)}
.ck-feature-list-item .ck-ico{flex:none}

/* ─── FILE UPLOAD / DROPZONE (Organism) ───────────────────────────────────
   A drop target, then a list of what was dropped.

   The dashed --input outline is the same treatment .ck-empty uses, and for the
   same reason: an outline says "there is nothing here yet" where a filled
   shimmer would say "it is coming" (§ 7d). On drag-over the target commits to
   --accent with a solid --primary edge, so the state is carried by more than
   a colour shift.

   Rows reuse .ck-progress for the transfer and .ck-icon-btn.tone-danger
   .size-xs for the remove — that control is 24px, which the old
   .dropzone-file-remove was not.

   Spec: references/components/file-upload.md */
.ck-dropzone{
  display:flex;flex-direction:column;align-items:center;gap:var(--space-sm);
  box-sizing:border-box;padding:var(--space-2xl) var(--space-base);
  text-align:center;cursor:pointer;
  border:1px dotted var(--input);border-radius:var(--radius-2xl);
  background:var(--background);color:var(--muted-foreground)/* pairing-exempt: deliberately de-emphasised placeholder copy; 5.11:1 light, 10.81:1 dark, 15.91:1 high contrast — the title above it carries --foreground */;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  transition:background var(--duration-base),border-color var(--duration-base),color var(--duration-base)}
.ck-dropzone.size-sm{padding:var(--space-base) var(--space-md);gap:var(--space-xs)}
.ck-dropzone.size-lg{padding:var(--space-4xl) var(--space-xl)}
.ck-dropzone:hover{border-color:var(--input-border-hover)}
.ck-dropzone.is-dragover{
  border-style:solid;border-color:var(--primary);
  background:var(--accent);color:var(--accent-foreground)}
.ck-dropzone[aria-invalid="true"]{border-color:var(--destructive);color:var(--destructive-soft-foreground)}
.ck-dropzone:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-dropzone.is-disabled,.ck-dropzone[aria-disabled="true"]{
  background:var(--muted);color:var(--muted-foreground);border-color:var(--input);
  cursor:not-allowed;pointer-events:none}
.ck-dropzone-icon{width:var(--space-2xl);height:var(--space-2xl);color:var(--input)}
.ck-dropzone-icon svg{
  width:var(--space-2xl);height:var(--space-2xl);
  stroke-width:var(--icon-stroke);display:block}
.ck-dropzone-title{font:var(--font-weight-semibold) var(--text-lg) var(--font-sans);color:var(--foreground)}
.ck-dropzone-hint{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}

.ck-file-list{display:flex;flex-direction:column;gap:var(--space-sm);margin-top:var(--space-md)}
.ck-file{
  display:flex;align-items:center;gap:var(--space-md);box-sizing:border-box;
  padding:var(--space-sm) var(--space-md);
  border:1px solid var(--panel-border);border-radius:var(--radius-md);
  background:var(--card);color:var(--card-foreground)}
.ck-file-icon{width:var(--icon-box);height:var(--icon-box);flex:none;
  display:inline-grid;place-items:center;color:var(--icon-color)}
.ck-file-icon svg{
  width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-file-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-file-name{font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--card-foreground)}
.ck-file-meta{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);
  font-variant-numeric:tabular-nums}
.ck-file.is-error{border-color:var(--destructive-border);background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-file.is-error .ck-file-name,.ck-file.is-error .ck-file-meta{color:inherit}
.ck-file.is-error .ck-file-icon{color:var(--icon-color-destructive)}
.ck-file.is-done{border-color:var(--success-border)}

/* ─── DOCUMENT FIELDS PANEL (Organism) ────────────────────────────────────
   Extracted fields beside the document they came from: a confidence dot, the
   label, the value, and per-row tools.

   This is what the --dfp-* tokens were minted for, and the only component
   entitled to them:

     --dfp-ok         extracted, nothing to check
     --dfp-confirmed  a person has confirmed it
     --dfp-matched    matched against another document
     --dfp-warn       needs a look

   Those four are a confidence scale, not the semantic state family, which is
   why they are their own tokens and why Fulfilment Status does NOT borrow
   them. The dot is redundancy: every row also carries its state in text or in
   its tooltip (§ 14.3 check 12).

   Spec: references/components/doc-fields-panel.md */
.ck-dfp{
  display:flex;flex-direction:column;min-height:0;box-sizing:border-box;
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);overflow:hidden}
.ck-dfp-head{
  display:flex;align-items:center;gap:var(--space-sm);flex:none;
  padding:var(--space-md) var(--space-base);
  border-bottom:1px solid var(--border)}
.ck-dfp-title{
  flex:1;min-width:0;font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--card-foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-dfp-body{flex:1;min-height:0;overflow-y:auto;padding:var(--container-inset-sm)}
.ck-dfp-group{display:flex;flex-direction:column}
.ck-dfp-group-label{
  padding:var(--space-sm) var(--space-sm) var(--space-xs);
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);color:var(--muted-foreground);
  text-transform:uppercase;letter-spacing:.07em}
.ck-dfp-row{
  display:flex;align-items:flex-start;gap:var(--space-sm);
  padding:var(--space-sm);border-radius:var(--radius-sm);
  transition:background var(--duration-base)}
.ck-dfp-row:hover{background:var(--hover-bg)}
.ck-dfp-row[aria-selected="true"],.ck-dfp-row.is-selected{
  background:var(--selected-bg);color:var(--selected-fg)}
.ck-dfp-dot{
  width:var(--space-sm);height:var(--space-sm);flex:none;margin-top:var(--space-xs);
  border-radius:var(--radius-full);
  background:var(--muted-foreground)/* pairing-exempt: carries no text */}
.ck-dfp-row.is-ok .ck-dfp-dot{background:var(--dfp-ok)/* pairing-exempt: carries no text */}
.ck-dfp-row.is-confirmed .ck-dfp-dot{background:var(--dfp-confirmed)/* pairing-exempt: carries no text */}
.ck-dfp-row.is-matched .ck-dfp-dot{background:var(--dfp-matched)/* pairing-exempt: carries no text */}
.ck-dfp-row.is-warn .ck-dfp-dot{background:var(--dfp-warn)/* pairing-exempt: carries no text */}
.ck-dfp-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-dfp-label{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-dfp-value{
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);color:var(--foreground);
  overflow-wrap:anywhere}
.ck-dfp-value.is-empty{color:var(--muted-foreground)}
/* Kept as .is-mono because that is what consumers already write, but it is
   no longer a second typeface — an ID lines up because its digits are
   tabular, not because the letters are fixed-width. */
.ck-dfp-value.is-mono{font-family:var(--font-sans);font-variant-numeric:tabular-nums}
/* Tools appear on hover AND focus-within, or a keyboard user cannot reach
   them (§ 14.4). */
.ck-dfp-tools{display:flex;align-items:center;gap:var(--space-2xs);flex:none;opacity:0;
  transition:opacity var(--duration-base)}
.ck-dfp-row:hover .ck-dfp-tools,.ck-dfp-row:focus-within .ck-dfp-tools{opacity:1}
/* Multi-value fields carry one chip per value. */
.ck-dfp-multi{display:flex;flex-wrap:wrap;gap:var(--space-xs)}
/* ── THE EDITABLE ROW — .ck-dfp-row.is-edit ──────────────────────────────
   The review screen corrects values in place, so the row is one line: the
   state control, the field's name, then its control in a fixed column. The
   column is fixed so every value down the panel starts on the same edge
   (check 79); set --ck-dfp-control to change it (default 160px).

     .ck-dfp-row.is-edit > state + .ck-dfp-label + .ck-dfp-control

   The state control is a .ck-icon-btn.size-xs — .tone-accent for a value the
   system trusts (its glyph rests in --primary here), .tone-warning for one a person should check — with a Lucide
   circle-check / circle-alert glyph. Never a hand-drawn dot: the glyph carries
   the state for a colour-blind reader, and the button opens the score.
   Inputs are .size-sm (28px), so the 24px state control centres on them.
   .is-tall pins a multi-line control's row to the top; .is-wrap lets a long
   name wrap instead of truncating. No row fill on hover: the row holds
   controls that draw their own hover, and a filled row under a focused input
   reads as a selection. */
.ck-dfp-row.is-edit{
  display:grid;grid-template-columns:auto minmax(0,1fr) minmax(0,var(--ck-dfp-control,160px));
  align-items:center;column-gap:var(--space-sm);padding-block:var(--space-xs)}
.ck-dfp-row.is-edit:hover{background:transparent}
.ck-dfp-row.is-edit.is-selected{background:transparent;color:inherit}
.ck-dfp-row.is-edit > .ck-dfp-label{
  min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap;color:var(--foreground)}
/* A field with no state (nothing was extracted, so there is no score) starts
   its name on the state column, so the controls still line up. */
.ck-dfp-row.is-edit > .ck-dfp-label:first-child{grid-column:1 / 3}
.ck-dfp-row.is-edit.is-selected > .ck-dfp-label{font-weight:var(--font-weight-semibold)}
.ck-dfp-row.is-edit.is-wrap > .ck-dfp-label{white-space:normal;overflow-wrap:anywhere}
.ck-dfp-row.is-edit.is-tall{align-items:start}
/* Tall rows line their name and state up with the FIRST 28px line. */
.ck-dfp-row.is-edit.is-tall > .ck-dfp-label{padding-block:calc((28px - 1lh) / 2)}
.ck-dfp-row.is-edit.is-tall > .ck-icon-btn{margin-block-start:2px}
.ck-dfp-row.is-edit > .ck-icon-btn.tone-accent{color:var(--primary)}
.ck-dfp-control{min-width:0;display:flex;flex-direction:column;gap:var(--space-xs)}
.ck-dfp-control > :is(.switch,.ck-switch){align-self:flex-end}
.ck-dfp-control > .ck-field-hint{text-align:end;font-variant-numeric:tabular-nums}
/* A list of values edited one per line — remove, then the value — with an
   Add More button closing the list on the trailing edge. */
.ck-dfp-multi.is-edit{flex-direction:column;flex-wrap:nowrap;align-items:stretch}
.ck-dfp-multi-row{display:flex;align-items:center;gap:var(--space-xs);min-width:0}
.ck-dfp-multi-row > .ck-input-wrap{flex:1;min-width:0}
.ck-dfp-multi.is-edit > .ck-btn{align-self:flex-end}

/* ─── NOTIFICATION PANEL (Organism) ───────────────────────────────────────
   An anchored list of what happened. --popover, because it floats out of flow
   (§ 4) — the v2.0 panel read --background, which is invisible in light theme
   and wrong in dark.

   Unread is carried by a dot AND the row's fill, so it survives a
   colour-blind reading. An empty panel uses .ck-empty; a loading one uses
   .ck-skeleton-row (§ 7d).

   Spec: references/components/notification-panel.md */
.ck-notif-panel{
  display:flex;flex-direction:column;box-sizing:border-box;
  width:380px;max-height:calc(100vh - var(--space-8xl));overflow:hidden;
  border:1px solid var(--panel-border);border-radius:var(--radius-2xl);
  background:var(--popover);color:var(--popover-foreground);
  box-shadow:var(--shadow-panel)}
.ck-notif-panel.size-sm{width:320px}
.ck-notif-panel.size-lg{width:440px}
.ck-notif-head{
  display:flex;align-items:center;gap:var(--space-sm);flex:none;
  padding:var(--container-inset-bar);
  border-bottom:1px solid var(--border)}
.ck-notif-title{flex:1;font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--popover-foreground)}
.ck-notif-list{flex:1;min-height:0;overflow-y:auto;padding:var(--space-xs)}
.ck-notif{
  display:flex;align-items:flex-start;gap:var(--space-md);width:100%;box-sizing:border-box;
  padding:var(--space-md);border:0;background:0;border-radius:var(--radius-sm);
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);color:var(--popover-foreground);
  text-align:start;cursor:pointer;
  transition:background var(--duration-base)}
.ck-notif:hover{background:var(--hover-bg)}
.ck-notif:active{background:var(--selected-bg);color:var(--selected-fg)}
.ck-notif:focus-visible{outline:0;background:var(--hover-bg);box-shadow:inset 2px 0 0 var(--primary)}
.ck-notif.is-unread{background:var(--hover-bg)}
.ck-notif-dot{
  width:var(--space-sm);height:var(--space-sm);flex:none;margin-top:var(--space-xs);
  border-radius:var(--radius-full);background:transparent}
.ck-notif.is-unread .ck-notif-dot{background:var(--info)/* pairing-exempt: carries no text */}
.ck-notif-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--space-2xs)}
.ck-notif-text{line-height:1.5}
.ck-notif-text b{font-weight:600}
.ck-notif-time{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-notif-actions{display:flex;align-items:center;gap:var(--space-sm);margin-top:var(--space-2xs)}
.ck-notif-foot{
  flex:none;padding:var(--space-sm);border-top:1px solid var(--border);
  display:flex;justify-content:center}

/* ─── SIDEBAR NAV (Organism) ──────────────────────────────────────────────
   The app's primary icon rail, with a flyout that opens on hover and can be
   pinned. It is the one component entitled to the --sidebar-* family, which
   is why those tokens exist as their own set rather than as aliases.

   The rail is icons only, so every item needs an aria-label AND a tooltip
   (§ 7c) — the flyout label is not a substitute, because it is not there when
   the flyout is shut. Hover-open also opens on :focus-within, or the flyout
   is unreachable by keyboard (§ 14.9 check 13).

   Spec: references/components/sidebar-nav.md */
/* ─── MAIN LEFT NAVIGATION — .ck-sidebar ──────────────────────────────────
   ONE VARIANT. This is the shipped rail from sidebar-nav.html and there is no
   other: no round/square switch, no expanded mode, no size scale. A product
   has exactly one main navigation, so a component with options here only
   invites two screens to disagree about what the app's edge looks like.

   Geometry is the reference's exactly: a 56px column, 16px/8px padding, 12px
   between blocks, 4px between the buttons inside a block, 40px round buttons
   holding a 20px glyph.

   The 12/4 split is the whole grouping mechanism (§ 14.3 check 12): buttons in
   one run sit 4px apart, runs sit 12px apart with a hairline between. That is
   why no run needs a label in a rail this narrow.

   TWO KINDS OF BUTTON, and the difference is in the markup, not a class:
     a plain button           a destination. Hovering shows its tooltip.
     data-section="Name"      opens the flyout with that section's pages.
   A section button shows no tooltip while its flyout is open — the flyout
   already names it, and two labels for one thing is noise.

   Spec: references/components/sidebar-nav.md */
.ck-sidebar{
  display:flex;flex-direction:column;align-items:center;gap:var(--space-md);
  box-sizing:border-box;width:56px;flex:none;
  padding:var(--space-base) var(--space-sm);
  border-inline-end:1px solid var(--sidebar-border);
  background:var(--sidebar);color:var(--sidebar-foreground);
  position:relative;z-index:var(--z-sticky)}
.ck-sidebar-logo{padding:0;
  width:var(--icon-box-nav);height:var(--icon-box-nav);flex:none;
  display:grid;place-items:center;overflow:hidden;cursor:pointer;
  border:0;border-radius:var(--radius-md);
  background:var(--sidebar-primary);color:var(--sidebar-primary-foreground);
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans)}
.ck-sidebar-logo svg{width:var(--icon-size-nav);height:var(--icon-size-nav);display:block}
.ck-sidebar-logo:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* Full-width hairline between runs. */
.ck-sidebar-sep{
  width:100%;height:1px;flex:none;
  background:var(--sidebar-border)/* pairing-exempt: a rule, carries no text */}
.ck-sidebar-items{
  display:flex;flex-direction:column;align-items:center;gap:var(--space-xs);width:100%}
/* Round, because that is what ships. The button is a .ck-tip-host so the
   kit's own tooltip hangs off it — no bespoke bubble here (§ 14.3 check 18). */
.ck-sidebar-item{padding:0;
  position:relative;
  /* The frame of the nav icon variant — same tokens .ck-ico.size-nav uses, so
     the button and the glyph inside it can never drift apart. */
  width:var(--icon-box-nav);height:var(--icon-box-nav);flex:none;box-sizing:border-box;
  display:grid;place-items:center;
  border:0;border-radius:var(--radius-md);
  background:transparent;color:var(--muted-foreground);cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base)}
/* Accepts either markup: the glyph wrapped in .ck-ico.size-nav (preferred, and
   what the kit page ships), or a bare svg child, which the rail carried before
   the variant existed. Both resolve to the same two tokens. */
.ck-sidebar-item > svg{
  width:var(--icon-size-nav);height:var(--icon-size-nav);
  stroke-width:var(--icon-stroke);display:block}
.ck-sidebar-item > .ck-ico{color:inherit}
/* --hover-bg, never --muted. The reference hovers to grey; the kit's protocol
   is that NOTHING hovers to grey (§ 14.3 check 24), and --hover-bg is the same
   tint the active state uses, so hover reads as "on the way to selected". */
.ck-sidebar-item:is(:hover,[data-force~="hover"]){
  background:var(--hover-bg);color:var(--sidebar-accent-foreground)}
.ck-sidebar-item[aria-current="page"],.ck-sidebar-item.is-active{
  background:var(--selected-bg);color:var(--sidebar-accent-foreground)}
.ck-sidebar-item:disabled,.ck-sidebar-item[aria-disabled="true"]{
  background:transparent;color:var(--input);cursor:not-allowed;pointer-events:none}
.ck-sidebar-item:focus-visible{outline:0;box-shadow:var(--focus-ring)}
/* The tooltip is the kit's .ck-tip in its .is-end placement, hung off the
   button as a .ck-tip-host — which brings :focus-within with it, so a keyboard
   gets the label too. It is suppressed while the flyout is open. */
.ck-sidebar.has-flyout .ck-sidebar-item > .ck-tip{opacity:0}
/* Bottom block — the tenant's own logo, pushed to the foot. */
.ck-sidebar-foot{
  margin-block-start:auto;flex:none;
  display:flex;flex-direction:column;align-items:center;gap:var(--space-xs);width:100%}
.ck-sidebar-foot > .ck-sidebar-sep{margin-block-end:var(--space-xs)}
/* An image we do not control, so it clips to a plate and takes NO tone of its
   own — a customer's brand colour is not ours to put on a semantic surface. */
.ck-sidebar-brand{
  width:40px;height:40px;flex:none;box-sizing:border-box;
  display:grid;place-items:center;overflow:hidden;cursor:pointer;
  border:0;border-radius:var(--radius-md);
  background:var(--muted);color:var(--foreground)/* pairing-exempt: a plate for an image we do not control, not muted content; --foreground on --muted is 14.56:1 where the nominal --muted-foreground partner is 3.16:1 */}
.ck-sidebar-brand > img,.ck-sidebar-brand > svg{width:28px;height:28px;display:block}
.ck-sidebar-brand:focus-visible{outline:0;box-shadow:var(--focus-ring)}

/* ─── FLYOUT PANEL — .ck-sidebar-flyout ───────────────────────────────────
   A full-height 200px panel beside the rail, holding the section's pages. It
   is NOT a popover: it can be PINNED, and pinning changes its layout mode from
   floating to in-flow, so the workspace resizes around it instead of being
   covered. That is the whole point of the pattern — browse by hovering, commit
   by pinning.

   Floating it sits at left:57px (the rail plus its 1px edge) with a shadow;
   locked it goes position:relative;left:0 and drops the shadow, because a
   panel that is part of the layout is not floating over anything. */
.ck-sidebar-flyout{
  position:absolute;inset-inline-start:57px;inset-block:0;z-index:var(--z-dropdown);
  width:200px;box-sizing:border-box;padding:14px 0 0;
  display:flex;flex-direction:column;
  border-inline-end:1px solid var(--border);
  background:var(--background);color:var(--foreground);
  box-shadow:var(--shadow-md);
  opacity:0;visibility:hidden;transform:translateX(-8px);
  transition:opacity var(--duration-moderate) var(--ease-default),
             visibility 0s linear var(--duration-moderate),
             transform var(--duration-moderate) var(--ease-default)}
.ck-sidebar-flyout.is-visible{
  opacity:1;visibility:visible;transform:translateX(0);
  transition:opacity var(--duration-moderate) var(--ease-default),
             transform var(--duration-moderate) var(--ease-default)}
.ck-sidebar-flyout.is-locked{
  position:relative;inset-inline-start:0;
  box-shadow:none;opacity:1;visibility:visible;transform:none}
.ck-sidebar-flyout-head{
  display:flex;align-items:center;justify-content:space-between;
  padding:0 14px 10px;margin-block-end:var(--space-sm);
  border-bottom:1px solid var(--border)}
.ck-sidebar-flyout-title{
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);
  color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.12em}
.ck-sidebar-pin{padding:0;
  width:var(--icon-box);height:var(--icon-box);flex:none;
  display:grid;place-items:center;
  border:0;border-radius:var(--radius-md);
  background:transparent;color:var(--muted-foreground);cursor:pointer;
  transition:background var(--duration-base),color var(--duration-base)}
.ck-sidebar-pin svg{width:var(--icon-size);height:var(--icon-size);display:block}
.ck-sidebar-pin:hover{background:var(--hover-bg);color:var(--primary)}
.ck-sidebar-pin:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-sidebar-pin[aria-pressed="true"]{color:var(--info);background:var(--selected-bg)}
.ck-sidebar-flyout-items{flex:1;overflow-y:auto}
.ck-sidebar-flyout-item{
  display:flex;align-items:center;gap:var(--space-sm);width:calc(100% - 12px);
  box-sizing:border-box;
  padding:var(--space-xs) 14px;margin:1px 6px;
  border:0;border-radius:var(--radius-md);
  background:transparent;color:var(--foreground);cursor:pointer;text-align:start;
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  text-decoration:none;
  transition:background var(--duration-base),color var(--duration-base)}
/* THE SUBMENU'S GLYPHS ARE THE KIT'S STANDARD ICON — a 16px glyph in a 24px
   frame (--icon-size in --icon-box), § 7c, for the items, the pin and the
   foot alike. They were drawn at 13, 12 and 11px. The rail's own 20px glyph
   is the deliberate exception and stays. The item's block padding drops from
   7 to 4px so the taller frame keeps the row at 32px. */
.ck-sidebar-flyout-item svg,.ck-sidebar-flyout-foot svg{
  box-sizing:border-box;width:var(--icon-box);height:var(--icon-box);
  padding:calc((var(--icon-box) - var(--icon-size)) / 2);flex:none}
.ck-sidebar-flyout-item svg{opacity:.7}
.ck-sidebar-flyout-item:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--primary)}
.ck-sidebar-flyout-item:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-sidebar-flyout-item[aria-current="page"],.ck-sidebar-flyout-item.is-active{
  background:var(--primary);color:var(--primary-foreground)}
.ck-sidebar-flyout-item:is([aria-current="page"],.is-active) svg{opacity:1}
.ck-sidebar-flyout-foot{
  margin-block-start:auto;
  display:flex;align-items:center;gap:var(--space-xs);
  padding:var(--space-sm) 14px;border-top:1px solid var(--border);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}
.ck-sidebar-flyout-foot.is-locked{color:var(--primary);font-weight:var(--font-weight-medium)}
@media (prefers-reduced-motion:reduce){
  .ck-sidebar-flyout{transition-duration:1ms}}

/* Two things used to live here and both are gone. A popover-style flyout — a
   220px floating card whose min-width was still quietly beating the real
   panel's width:200px, which is exactly what a leftover duplicate does. And
   .ck-sidebar-group, which existed only to group items in the expanded rail;
   with one variant there is nothing to group. */

/* ─── CANVAS (Foundation) ──────────────────────────────────────────────────
   The app ground. A fourth surface level above the three in § 4, because
   "the page an app sits on" and "the default surface a control sits in" are
   different roles that shadcn happens to give the same value.

     --canvas      the page the app sits on          (light #eef1f3)
     --background  the default surface in it         (light #ffffff)
     --card        elevated but in flow              (light #ffffff)
     --popover     floating out of flow              (light #ffffff)

   Only light diverges. Dark and high contrast already separate the ground
   from --card, so --canvas equals --background there.

   .ck-canvas is the shell: put it on the element that owns the viewport, not
   on a panel. A panel on --canvas is a defect — it would read as a hole in
   the page rather than a surface on it.

   Spec: references/components/canvas.md */
.ck-canvas{
  min-height:100vh;box-sizing:border-box;
  background:var(--canvas);color:var(--canvas-foreground)}
/* The inset variant, for a region that sits back from the surface around it —
   a preview well, an empty column. Not for a panel. */
.ck-canvas-inset{
  box-sizing:border-box;border-radius:var(--radius-2xl);
  background:var(--canvas);color:var(--canvas-foreground)}

/* ─── WORKING PANEL — .ck-panel ────────────────────────────────────────────
   The blank white panel a screen's content actually lives in, laid on the
   canvas. Everything else on a page is inside one of these.

   It is NOT .ck-card. A card is a discrete object in a list of objects, with
   a title and a purpose of its own; a panel is a REGION of the screen. Same
   surface, different job — and the difference shows in the rules below, which
   are all about how panels sit next to each other rather than what they hold.

     No shadow            it rests on the page (§ 14.3 check 21). Only an
                          overlay lifts, and a panel is not an overlay.
     --panel-stroke edge  1.63:1 against the fill, 1.44:1 against the canvas.
                          --panel-border is 1.02:1 on the canvas, which is no
                          edge at all — see the token's own note.
     --card fill          white in light, and the elevated surface in dark.
                          Hardcoding white would black out in dark theme.

   Spec: references/components/canvas.md § Working panel */
.ck-panel{
  box-sizing:border-box;min-width:0;min-height:0;
  background:var(--card);color:var(--card-foreground);
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
  overflow:hidden}
/* A panel that scrolls its own content while the page frame stays put. */
.ck-panel.is-scroll{display:flex;flex-direction:column}
/* The body's own inset. Only the scrolling variant declared anything, so a
   plain .ck-panel put its content hard against the panel stroke. */
/* ── THE CONTAINER INSET ───────────────────────────────────────────────────
   A container holds its content away from its own edge by --container-inset,
   ON ALL FOUR SIDES. One value, one property, every container.

   They each used to pick their own: 8, 18/20, 0/12/12, 12/16/16, 20 — and five
   of those were asymmetric, so content sat closer to one edge than the other
   for no stated reason. The ladder is the one .ck-card already carried:

     .size-sm   --container-inset-sm    12px
     default    --container-inset       16px
     .size-lg   --container-inset-lg    24px

   WHICH STEP IS A PER-COMPONENT DECISION, not one number everywhere. The rule
   is that a container insets on all four sides and takes a step off this
   ladder; a dense container takes a smaller one. A fields panel, a chat dock,
   a board card and a caption under an image are read close up and in bulk, so
   they take -sm; a dialog body, a drawer, a panel and a card are the primary
   surface of what you are looking at, so they take the base. The step belongs
   in the component's own sheet, with the reason.

   .is-flush zeroes it, for content that must reach the container's edge — a
   table filling a panel, an image in a media card, the two halves of a split
   dialog. It is a decision someone makes, not a default anything falls into. */
.ck-panel-body{padding:var(--container-inset)}
/* ── .ck-panel-head / .ck-panel-foot ────────────────────────────────────
   A dialog has a head, a body and a foot, and all three carry the same
   horizontal inset, so everything in it lines up on one edge. A panel had
   only a body, so screens improvised: an .ck-action-bar for the head and a
   .ck-form-foot for the foot, each bringing its own padding.

   Measured on one panel, its three regions started their content at 10px,
   17px and 0 — the heading sat left of the field below it and the buttons sat
   hard against the edge, with nothing in the markup saying why. Same token as
   the body, so a panel reads as one column. */
.ck-panel-head{display:flex;align-items:center;gap:var(--space-md);flex:none;
  padding:var(--container-inset);border-bottom:1px solid var(--border)}
/* A panel headed by its tabs — .ck-panel-head.has-tabs. The tab bar is the
   head: it sits flush on the panel's leading edge and takes the free width, the
   tools follow on the head's own trailing inset, and the head draws the tab rail
   across its whole width so the line does not stop where the tabs do. The bar
   inside is .ck-tabs.size-sm.is-scroll-x (tabs inside a panel). Found on the
   DES-667 linking-set table, where a padded row around a padded bar left the
   first tab 36px in and the rail ending at the last tab. */
.ck-panel-head.has-tabs{
  padding:0;padding-inline-end:var(--container-inset-sm);border-bottom:0;
  background-image:linear-gradient(var(--panel-border),var(--panel-border));
  background-repeat:no-repeat;background-size:100% var(--ck-tab-rail,2px);background-position:left bottom}
/* THE SAME HEIGHT AS A PLAIN HEAD. Two panels side by side (Linking Sets and
   its table) must draw their head rules on one line, so the tabbed head is at
   least as tall as a .ck-panel-head holding a 28px control: 2 × --container-inset
   + 28 + its 1px rule = 61px. */
.ck-panel-head.has-tabs{min-height:61px;box-sizing:border-box}
.ck-panel-head.has-tabs > .ck-tabs{flex:1 1 auto;min-width:0;align-self:flex-end}
/* The tools keep at least a --space-sm buffer above and below, so the search
   and the icon buttons never touch the head's top edge or the tab rail. The tab
   bar keeps its own height and sits on the rail at the head's bottom. */
.ck-panel-head.has-tabs > :not(.ck-tabs){flex:0 1 auto;min-width:0;align-self:center;margin-block:var(--space-sm)}
/* ONE LINE (check 79). Centred in the 61px head, the tools stood 12.5px above
   the tab labels, which sit on the rail at the head's foot. They share the tab
   bar's centre line instead: on the foot, lifted by half the difference between
   the 36px tab bar and the head's 28px controls. */
.ck-panel-head.has-tabs > :not(.ck-tabs){align-self:flex-end;margin-block:var(--space-sm) var(--space-xs)}
/* .is-compact — a tabbed head with no neighbouring panel to line up with drops
   the 61px floor, so the head is only as tall as its contents: 8px above the
   tools, the tab bar on the rail. Found on Edit Document, where the floor left
   29px of empty space above Rescan. */
.ck-panel-head.has-tabs.is-compact{min-height:0}
/* .ck-panel-title — the title in a panel's head. The role table puts panel
   titles at 16px semibold (--text-xl), the size .ck-dialog-title and
   .ck-drawer-title already take; panels had no class for it, so flows titled
   them with whatever label class was nearest (a 11px menu label on Role
   Management's Roles list). A count belongs in the title as "Roles(15)". */
.ck-panel-title{margin:0;flex:1;min-width:0;
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--card-foreground)}
.ck-panel-foot{display:flex;align-items:center;justify-content:flex-end;
  gap:var(--space-sm);flex:none;
  padding:var(--container-inset);border-top:1px solid var(--border)}
/* .is-split — a destructive action that must not sit beside the confirming
   one goes to the leading edge, as .ck-form-foot.is-split does. Group each end
   in a .ck-form-foot-actions. */
.ck-panel-foot.is-split{justify-content:space-between}
.ck-panel.size-sm > :is(.ck-panel-head,.ck-panel-foot){padding:var(--container-inset-sm)}
/* ...and the body, which .size-sm used to leave at the base 16 while its head
   and foot took 12, so a dense panel's content sat further in than its edges. */
.ck-panel.size-sm > .ck-panel-body{padding:var(--container-inset-sm)}
.ck-panel.size-lg > :is(.ck-panel-head,.ck-panel-foot){padding:var(--container-inset-lg)}
:is(.ck-panel-head,.ck-panel-foot).is-flush{padding:0;border:0}
/* A tabbed head keeps its own geometry in a sized panel. .size-sm / .size-lg
   re-pad every head and come later in the file, so they used to win and
   inset the tabs off the panel's edge. */
.ck-panel:is(.size-sm,.size-lg) > .ck-panel-head.has-tabs{padding:0;padding-inline-end:var(--container-inset-sm)}
/* A spacer that pushes the trailing tools to the end, as in .ck-toolbar. */
:is(.ck-panel-head,.ck-panel-foot) > .grow{flex:1;min-width:0}
:is(.ck-panel-body,.ck-dialog-body,.ck-drawer-body,.ck-accordion-panel,.ck-dock-body,
    .ck-settings-card,.ck-tab-panel,.ck-card,.ck-form.is-carded,.ck-dd-picker-body,
    .ck-member,.ck-feature,.ck-col-card,.ck-dfp-body,.ck-dialog-pane,.ck-media-body).is-flush{padding:0}
.ck-panel.is-scroll > .ck-panel-body{flex:1;min-height:0;overflow:auto}

/* ─── GRID LAYOUT — .ck-grid ─────────────────────────────────────────────
   The columns a layout sits on: .cols-4, .cols-6, .cols-8, .cols-10, .cols-12
   (12 is the default), with a --grid-gutter between them — the canvas gap, so
   a grid and the panels around it share one rhythm. A child spans columns
   with .span-1 … .span-12 (a span wider than the grid is clamped to it).

   THE LINE OVERLAY — [data-ck-grid-lines] on a .ck-grid, or on ANY container
   with --ck-grid-cols set, paints the columns (--grid-guide) and 8px
   horizontal lines (--grid-baseline) behind the content, so it is visible at
   a glance whether icons, text and buttons that should share a vertical or a
   horizontal line do. It is a checking aid — the same lines
   scripts/audit_grid_lines.py measures — never a finished look.

   Narrow screens: under 720px every grid falls to 4 columns and a span wider
   than that takes the full row, so nothing is squeezed into a sliver. */
.ck-grid{
  --ck-grid-cols:12;
  display:grid;grid-template-columns:repeat(var(--ck-grid-cols),minmax(0,1fr));
  column-gap:var(--grid-gutter);row-gap:var(--grid-gutter);
  box-sizing:border-box}
.ck-grid.cols-4{--ck-grid-cols:4}
.ck-grid.cols-6{--ck-grid-cols:6}
.ck-grid.cols-8{--ck-grid-cols:8}
.ck-grid.cols-10{--ck-grid-cols:10}
.ck-grid.cols-12{--ck-grid-cols:12}
.ck-grid > *{grid-column:span 1;min-width:0}
.ck-grid > .span-1{grid-column:span min(1, var(--ck-grid-cols))}
.ck-grid > .span-2{grid-column:span min(2, var(--ck-grid-cols))}
.ck-grid > .span-3{grid-column:span min(3, var(--ck-grid-cols))}
.ck-grid > .span-4{grid-column:span min(4, var(--ck-grid-cols))}
.ck-grid > .span-5{grid-column:span min(5, var(--ck-grid-cols))}
.ck-grid > .span-6{grid-column:span min(6, var(--ck-grid-cols))}
.ck-grid > .span-7{grid-column:span min(7, var(--ck-grid-cols))}
.ck-grid > .span-8{grid-column:span min(8, var(--ck-grid-cols))}
.ck-grid > .span-9{grid-column:span min(9, var(--ck-grid-cols))}
.ck-grid > .span-10{grid-column:span min(10, var(--ck-grid-cols))}
.ck-grid > .span-11{grid-column:span min(11, var(--ck-grid-cols))}
.ck-grid > .span-12{grid-column:span min(12, var(--ck-grid-cols))}
.ck-grid > .span-all{grid-column:1 / -1}
@media (max-width:720px){
  .ck-grid,.ck-grid[class*="cols-"]{--ck-grid-cols:4}
  .ck-grid > [class*="span-"]{grid-column:1 / -1}}
[data-ck-grid-lines]{
  /* column width = (100% - (cols - 1) x gutter) / cols; the gradient repeats a
     column of tint and a gutter of clear, and a second layer ruled every 8px */
  --ck-grid-col-w:calc((100% - (var(--ck-grid-cols, 12) - 1) * var(--grid-gutter)) / var(--ck-grid-cols, 12));
  background-image:
    repeating-linear-gradient(to bottom, var(--grid-baseline) 0 1px, transparent 1px var(--grid-baseline-step)),
    repeating-linear-gradient(to right, var(--grid-guide) 0 var(--ck-grid-col-w),
      transparent var(--ck-grid-col-w) calc(var(--ck-grid-col-w) + var(--grid-gutter)));
  background-origin:content-box;background-clip:content-box}

/* ─── CANVAS LAYOUT — .ck-canvas-area ─────────────────────────────────────
   Where the panels sit, and the four spacing rules that govern them. They are
   stated once here rather than per screen, because a screen that invents its
   own gutters is how two pages of the same product stop looking related.

     12px BETWEEN panels, always, whenever more than one is on screen
       (--canvas-gap; it was 16px).
     0 at the TOP. The first row of panels meets the header bar with no gap —
       the header is the page's own edge, not a floating object above it, and
       a strip of canvas between the two reads as a rendering gap.
     12px at the BOTTOM, above the base footer.
     12px at the SIDES, so the gutter matches the gap between panels and the
       rhythm is one number everywhere. The header bar pads its sides by the
       same --canvas-gap, so its first control sits on the panels' edge.

   The asymmetry at the top is deliberate and is the rule most likely to be
   "corrected" by someone tidying up. It is not a mistake. */
.ck-canvas-area{
  --ck-panel-gap:var(--canvas-gap);
  flex:1;min-height:0;box-sizing:border-box;
  display:flex;gap:var(--ck-panel-gap);
  padding:0 var(--ck-panel-gap) var(--ck-panel-gap);
  background:var(--canvas);color:var(--canvas-foreground)}
/* Panels stacked down the page instead of across it. */
.ck-canvas-area.is-stacked{flex-direction:column}
/* Every direct child is a panel and shares the space unless it says otherwise;
   a panel with its own width simply keeps it. min-width:0 so a panel with wide
   content shrinks with the row instead of forcing the canvas to scroll. */
.ck-canvas-area > .ck-panel{flex:1;min-width:0}
.ck-canvas-area > .ck-panel.is-fixed{flex:none}

/* ── A PANEL YOU CAN SIZE — .ck-panel.is-resizable ─────────────────────────
   Drag a panel's trailing edge and the panels beside it give up or take back
   the difference. The kit already had the grip — [data-ck-resize] — but only
   for a drawer and the folder navigator, each sized against fixed pixel bounds
   with nothing else in the row to consider. In the canvas there always is
   something else, so two things are added here:

     the panel holds a WIDTH and stops flexing, so the drag is what sets it;
     its neighbours keep flex:1 and absorb the change between them.

   --ck-panel-min is the floor EVERY panel keeps, resizable or not. It is what
   stops a drag crushing the panel next door: ckPanelResize subtracts the
   siblings' floors from the row before it clamps, so the limit is whatever the
   window can actually afford rather than a number picked once. Shrink the
   window and the panels shrink with it; nothing has to be re-dragged.

   Below --ck-canvas-stack the row stacks and widths are dropped entirely — a
   row of sized panels on a phone is a row of unreadable columns. */
/* ── .ck-panel.is-recessed — a panel that sits BELOW the surface ─────────
   Every other panel is --card on --canvas: it reads as lifted. This is the
   inverse, --canvas inside a --card region, for a container whose job is to
   hold other cards rather than to be one — a preview well, a gallery ground,
   a read-only region beside a form. Without it a screen writes
   `background: var(--canvas)` onto its own wrapper and the recessed surface
   is a local decision each time instead of one the token set makes. */
/* ── .ck-panel.is-tinted — a side panel on the blue accent tint ──────────────
   A list panel beside the main content (Linking Sets) sits on --accent so its
   white cards read as objects on it and the panel separates from the canvas.
   An empty state inside it takes the tint rather than a white box, so the
   panel reads as one surface when it is empty too. */
.ck-panel.is-tinted{background:var(--accent);color:var(--accent-foreground)}
.ck-panel.is-tinted .ck-empty{background:transparent}
.ck-panel.is-recessed{background:var(--canvas);color:var(--canvas-foreground);
  border-color:var(--border)}
.ck-canvas-area{--ck-panel-min:180px;--ck-canvas-stack:900px}
.ck-canvas-area > .ck-panel{min-inline-size:min(var(--ck-panel-min),100%)}
.ck-canvas-area > .ck-panel.is-resizable{
  flex:none;position:relative;
  --ck-resize-min:240px;--ck-resize-max:720px;
  /* a resizable column starts at a working width, not at its content's —
     --ck-resize-start, 328px (the editor's fields column) unless set */
  inline-size:var(--ck-resize-start,328px)}
/* The grip sits INSIDE the panel's trailing edge, not out in the gap.
   .ck-panel is overflow:hidden — it has to be, to clip its own rounded corners
   — so a handle hung at a negative inset is clipped away completely: invisible,
   and nothing to grab. A drawer can hang its grip outside itself because a
   drawer does not clip; a canvas panel cannot.

   The 6px strip is the target and stays inside the panel; the 1px rule it
   draws sits hard on the edge, so the affordance still reads as the boundary
   between the two panels rather than as something inset from it. */
.ck-canvas-area > .ck-panel.is-resizable > [data-ck-resize]{inset-inline-end:0}
.ck-canvas-area > .ck-panel.is-resizable > [data-ck-resize]::before{
  inset-inline-start:auto;inset-inline-end:0}
/* Revealed when the PANEL is approached, not only the 6px strip. A 6px target
   you have to find before it tells you it is there is a handle nobody uses;
   the panel is the thing the pointer is already over. */
.ck-canvas-area > .ck-panel.is-resizable:is(:hover,[data-force~="hover"]) > [data-ck-resize]::before,
.ck-canvas-area > .ck-panel.is-resizable > [data-ck-resize]:is(:hover,:focus-visible,[data-force~="hover"])::before{
  opacity:1}
/* On approach it is the full height of the panel and takes the accent, so the
   boundary is unmistakable before the drag starts. */
.ck-canvas-area > .ck-panel.is-resizable:is(:hover,[data-force~="hover"]) > [data-ck-resize]::before{
  inset-block:0;width:2px;
  background:var(--primary)/* pairing-exempt: a 2px drag indicator, carries no text */}
/* While dragging, nothing in the row selects text and the cursor does not
   flicker as the pointer crosses a child. */
.ck-canvas-area:has(> .is-resizing){user-select:none;cursor:col-resize}
/* Stacked: the widths are meaningless, and ckPanelResize clears the inline one
   so this is not fighting a style attribute. */
.ck-canvas-area.is-stacked > .ck-panel.is-resizable{flex:none;width:auto}
.ck-canvas-area.is-stacked > .ck-panel.is-resizable > [data-ck-resize]{display:none}
@media (max-width:900px){
  .ck-canvas-area{flex-direction:column}
  .ck-canvas-area > .ck-panel.is-resizable{width:auto}
  .ck-canvas-area > .ck-panel.is-resizable > [data-ck-resize]{display:none}
}

/* ─── APP SHELL — .ck-shell ───────────────────────────────────────────────
   Header bar across the top, main navigation down the left, canvas filling
   the rest. THE HEADER AND THE LEFT NAV ARE ALWAYS PRESENT unless a screen
   has an explicit reason to hide them.

   .is-immersive is that explicit reason: a screen whose whole job is one
   document — the reconciliation detail page — hides both and brings them back
   on hover from the top and the left edge. It is a deliberate mode a screen
   opts into, never a layout that happens by accident, and the chrome must
   still be reachable by keyboard, so the reveal is :focus-within as well as
   :hover. */
.ck-shell{
  display:grid;grid-template-columns:auto minmax(0,1fr);
  grid-template-rows:auto minmax(0,1fr);
  min-height:100vh;box-sizing:border-box;
  background:var(--canvas);color:var(--canvas-foreground)}
/* THE NAV RUNS THE FULL HEIGHT — top to bottom, with the header bar starting
   to its right. The rail is the app's fixed edge; the header belongs to the
   PAGE inside it and changes as you navigate, so a header that crossed over
   the rail would imply the rail was part of the page. */
/* ── .ck-shell.is-fixed — the shell IS the viewport ──────────────────────
   The default shell is min-height:100vh, so the page grows and the WINDOW
   scrolls. A screen whose panes scroll inside themselves — a two-column
   manager, an editor beside a list — needs the opposite: the frame pinned to
   the viewport so the overflow belongs to the pane and the outer page never
   scrolls at all. Screens were reaching for `height:100vh` on .ck-shell
   locally to get this, which is a page overriding a kit-owned property to
   reach a state the kit simply did not offer. Rulebook 61. */
.ck-shell.is-fixed{height:100vh;min-height:0;overflow:hidden}
.ck-shell > .ck-sidebar{grid-column:1;grid-row:1 / -1}
.ck-shell > .header-bar{grid-column:2;grid-row:1}
.ck-shell > .ck-canvas-area{grid-column:2;grid-row:2}
/* A FLYOUT THAT IS PINNED OPEN TAKES A COLUMN.
   .ck-sidebar-flyout.is-locked already goes in-flow — that is what pinning
   means — but the grid had no slot for it, so it fell to a row of its own
   BELOW the canvas and the page grew a second screen's worth of height. The
   flyout is a child of the shell, and a child of a grid that is never placed
   is placed for you, badly.

   Pinned, it sits between the rail and the page: the rail is the app's edge,
   the flyout is the section you are in, the header and canvas are the page. */
.ck-shell:has(> .ck-sidebar-flyout.is-locked){
  grid-template-columns:auto auto minmax(0,1fr)}
.ck-shell > .ck-sidebar-flyout.is-locked{grid-column:2;grid-row:1 / -1}
.ck-shell:has(> .ck-sidebar-flyout.is-locked) > .header-bar{grid-column:3}
.ck-shell:has(> .ck-sidebar-flyout.is-locked) > .ck-canvas-area{grid-column:3}
/* Unpinned it is absolute, so it is out of flow and needs no cell — but it
   still must not be auto-placed into one. */
.ck-shell > .ck-sidebar-flyout:not(.is-locked){grid-column:1;grid-row:1 / -1}
/* No background override needed here: .header-bar is transparent in every
   mode, so the canvas already runs behind it. */
/* A footer, when the screen has one. The 12px above it belongs to the canvas
   area, not to the footer, so a screen without one loses no spacing. */
.ck-shell > .ck-shell-foot{
  grid-column:1 / -1;grid-row:3;
  display:flex;align-items:center;gap:var(--space-md);
  padding:var(--space-sm) var(--space-base);box-sizing:border-box;
  border-top:1px solid var(--panel-stroke);
  background:var(--background);color:var(--foreground)}

/* Immersive: the chrome retracts and returns on approach. */
.ck-shell.is-immersive > .header-bar,
.ck-shell.is-immersive > .ck-sidebar{
  position:fixed;z-index:var(--z-sticky);
  transition:transform var(--duration-moderate) var(--ease-default)}
.ck-shell.is-immersive > .header-bar{
  inset-block-start:0;inset-inline:0;transform:translateY(-100%)}
.ck-shell.is-immersive > .ck-sidebar{
  inset-block:0;inset-inline-start:0;transform:translateX(-100%)}
.ck-shell.is-immersive > .header-bar:is(:hover,:focus-within),
.ck-shell.is-immersive > .header-bar[data-force~="hover"]{transform:translateY(0)}
.ck-shell.is-immersive > .ck-sidebar:is(:hover,:focus-within),
.ck-shell.is-immersive > .ck-sidebar[data-force~="hover"]{transform:translateX(0)}
/* The strips that catch the pointer at the two edges while the chrome is away.
   Without them the only way back is to guess where the hidden bar is. */
.ck-shell.is-immersive{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr)}
.ck-shell.is-immersive > .ck-canvas-area{grid-column:1;grid-row:1;padding-block-start:var(--ck-panel-gap)}
@media (prefers-reduced-motion:reduce){
  .ck-shell.is-immersive > :is(.header-bar,.ck-sidebar){transition-duration:1ms}}

/* ─── FOLDER NAVIGATOR (Organism) — .ck-folder-nav ────────────────────────
   A folder tree in a panel. Rebuilt from the two shipped versions: the
   document editor (document-editor-vercel) and the folder listing used by
   integrations, folder settings and the reconciliation flows.

   TWO VARIANTS, and the difference is real rather than decorative:

     .ck-folder-nav              Folders only, TWO levels. Counts are `orange`.
                                 A group heading shows no count — the number
                                 belongs to the folders inside it, not to the
                                 heading. Used everywhere except the editor.

     .ck-folder-nav.is-document  THREE levels: folder, subfolder, documents.
                                 Counts are `blue`. Every document row carries
                                 a checkbox, because the editor's tree is a
                                 multi-select of what is open in the workspace.

   Amber against blue is the product's own distinction and it is worth keeping:
   in the editor a count is "pages loaded here"; in a listing it is "folders
   needing attention". Two different facts should not wear one colour.

   EVERY PART IS AN EXISTING ATOM. The search row is .ck-search with a
   .ck-input, the document checkbox is .ck-check, the head actions are
   .ck-icon-btn carrying .ck-tip-host, and the count is .ck-counter with a
   palette tone. The tree contributes layout and nothing else. The two
   references had drifted into three different search inputs and two checkbox
   sizes between them; pointing the tree at the atoms is what stops that
   recurring.

   IT IS AN ARIA TREEVIEW, not a stack of buttons. The row is a
   div[role="treeitem"] with a roving tabindex, so the chevron and the row
   controls can be real <button>s — a button nested inside a button is invalid
   and the parser lifts it straight out of its parent, which is the same trap
   .ck-dd-clear fell into. Enter and Space are wired in JS because that is the
   cost of not using a <button> for the row itself.

   Spec: references/components/folder-nav.md */
.ck-folder-nav{
  /* Declared on the panel, not the row, because .ck-folder-children needs it
     to work out the indent. */
  --ck-row-gap:var(--space-sm);
  display:flex;flex-direction:column;min-height:0;box-sizing:border-box;
  width:300px;flex:none;
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
  background:var(--card);color:var(--card-foreground);
  overflow:hidden;
  transition:width var(--duration-moderate) var(--ease-default),
             margin var(--duration-moderate) var(--ease-default),
             opacity var(--duration-base) var(--ease-default)}
/* Collapsible, for a listing that hands the width back to its table. The
   panel stays mounted and its width animates, so the table reflows once
   instead of twice.

   Mounted means it is still a flex item, and the row's gap still sits beside
   it — so a closed panel left a 0px item AND a gap, and the table stood two
   gaps off the canvas edge instead of the one the canvas layout requires
   (.ck-canvas-area: --canvas-gap, 12px, at the sides and between panels). The closed panel
   hands its gap back with a negative margin of exactly that gap, animated with
   the width so the table slides once. --ck-panel-gap is the canvas layout's
   own; --canvas-gap is the same 12px for a row that spaces itself. */
.ck-folder-nav.is-closed{
  width:0;min-width:0;padding:0;border-width:0;
  margin-inline-end:calc(-1 * var(--ck-panel-gap, var(--canvas-gap)));
  opacity:0;pointer-events:none}
@media (prefers-reduced-motion:reduce){.ck-folder-nav{transition-duration:1ms}}
/* ANY PANEL CLOSES THE SAME WAY — .ck-panel.is-closed
   The gap hand-back above was written for the folder navigator only, so every
   other collapsible panel rolled its own close and dropped it. Measured: Profile
   Settings' sections panel left its neighbour 32px off the rail and Document
   Editor's viewer-mode panel 34px — both the 16px side inset plus the 16px gap
   still reserved beside a 0px item. The rule is the same one, for any panel.

   flex:0 0 0 and max-width:0 because a general panel may GROW, and width:0 alone
   does not stop a flex item that grows. It holds whether the closed panel is
   first, last or between two others: its negative end margin cancels exactly
   one gap, so the survivors meet the canvas edge and each other at one gap.

   No transition here on purpose. A panel with a drag handle changes width live,
   and a width transition on every .ck-panel would make the handle lag the
   pointer. A panel that should slide shut declares its own. */
.ck-panel.is-closed{
  flex:0 0 0;width:0;min-width:0;max-width:0;padding:0;border-width:0;
  margin-inline-end:calc(-1 * var(--ck-panel-gap, var(--canvas-gap)));
  opacity:0;pointer-events:none;overflow:hidden}

/* ── Head ── */
.ck-folder-head{
  display:flex;flex-direction:column;gap:var(--space-sm);flex:none;
  padding:var(--space-base) var(--space-base) var(--space-md)}
.ck-folder-head-top{display:flex;align-items:center;gap:var(--space-sm)}
.ck-folder-title{
  flex:1;min-width:0;
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap}
.ck-folder-actions{display:flex;align-items:center;gap:var(--space-sm);flex:none}
/* The one round button in the head is the primary add. Round because it is the
   head's single affirmative action and a pill reads as "new"; sort, settings
   and refresh stay plain .ck-icon-btn on the control radius. */
.ck-folder-actions > .ck-icon-btn.is-add{
  background:var(--primary);color:var(--primary-foreground)}
.ck-folder-actions > .ck-icon-btn.is-add:is(:hover,[data-force~="hover"]){
  background:var(--primary-hover);color:var(--primary-foreground)}

/* ── Tree ── */
.ck-folder-tree{
  flex:1;min-height:0;overflow-y:auto;scrollbar-width:thin;
  padding:var(--space-xs) var(--space-sm) var(--space-base)}
/* One row definition for all three levels. Weight carries the level, not a
   different box: a folder is semibold, anything nested is regular. */
.ck-folder-row{
  display:flex;align-items:center;gap:var(--ck-row-gap);
  box-sizing:border-box;width:100%;
  /* --container-inset-xs, 4px: a row inside the navigator's list (was 8px). */
  padding:var(--container-inset-xs);
  border-radius:var(--radius-md);
  background:transparent;color:var(--foreground);cursor:pointer;
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  user-select:none;
  transition:background var(--duration-base),color var(--duration-base)}
.ck-folder-row:is(:hover,[data-force~="hover"]){background:var(--hover-bg)}
.ck-folder-row:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-folder-row:is([aria-selected="true"],.is-selected){
  background:var(--selected-bg);color:var(--selected-fg)}

/* ── Where the highlight starts ──
   A row with no children still reserves the chevron's lane, or the folder
   column goes crooked. But that lane is EMPTY, and painting the highlight
   across it leaves a band of colour in front of the row with nothing in it —
   the highlight starts 23px before the first thing the reader can see.

   So the highlight begins at the row's leftmost icon instead. It cannot be
   done by moving the row: the row's edge IS the highlight's edge, so shifting
   it right would carry the icon with it and break the column. The paint is
   moved onto a pseudo-element instead, which lets the background start
   wherever we like while every child stays exactly where it was.

   The inset is one chevron plus one gap — derived, so it follows if either
   changes. What is left in front of the icon is the row's own padding, the
   same 8px every other row has before its first ink, so all the highlights
   share one rhythm. */
.ck-folder-row:has(> .ck-folder-chev.is-empty){position:relative;background:transparent}
.ck-folder-row:has(> .ck-folder-chev.is-empty)::before{
  content:'';position:absolute;inset-block:0;inset-inline-end:0;
  inset-inline-start:calc(var(--icon-size) + var(--ck-row-gap));
  border-radius:var(--radius-md);background:transparent;
  transition:background var(--duration-base)}
/* The row's children are positioned so they paint over the pseudo-element;
   without this the highlight covers the label. */
.ck-folder-row:has(> .ck-folder-chev.is-empty) > *{position:relative}
.ck-folder-row:has(> .ck-folder-chev.is-empty):is(:hover,[data-force~="hover"])::before{
  background:var(--hover-bg)}
.ck-folder-row:has(> .ck-folder-chev.is-empty):is([aria-selected="true"],.is-selected)::before{
  background:var(--selected-bg)}
/* The focus ring follows the highlight, or a keyboard user sees a ring around
   a box the selection does not fill. */
.ck-folder-row:has(> .ck-folder-chev.is-empty):focus-visible{box-shadow:none}
.ck-folder-row:has(> .ck-folder-chev.is-empty):focus-visible::before{
  box-shadow:var(--focus-ring)}
.ck-folder-row[aria-disabled="true"]{
  background:transparent;color:var(--input);cursor:not-allowed;pointer-events:none}
/* Nested rows: regular weight, indented one step. */
/* A nested row begins exactly under its parent's FOLDER ICON, not under its
   chevron — so the indent is one chevron plus one gap, derived rather than
   guessed. A flat 20px left every level a few pixels adrift of the glyph
   above it. */
/* ── A NESTED GROUP HAS A FIXED HEIGHT AND SCROLLS ────────────────────────
   A folder with sixty documents in it used to push every folder BELOW it off
   the bottom of the panel. The outer .ck-folder-tree scrolls, so nothing was
   lost — but the reader had to scroll past one folder's contents to reach the
   next one, and the tree stopped reading as a list of folders.

   The group is capped at 7 and a half rows. The half is deliberate: a row cut
   by the fold is what says there is more below it, where a clean edge at seven
   looks like the list simply ends. Short groups are untouched — a max-height
   does nothing until the content exceeds it.

   overscroll-behavior:contain is the load-bearing part. Without it, scrolling
   to the bottom of the inner list hands the gesture to the outer tree, so the
   whole panel lurches the moment a nested list runs out. */
.ck-folder-children{
  display:none;
  padding-inline-start:calc(var(--icon-size) + var(--ck-row-gap));
  max-block-size:var(--ck-folder-children-max);
  overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}
/* One scroller per branch. A group inside a group that already scrolls does
   not add a second — two nested scrollbars is a gesture nobody can aim. */
.ck-folder-children .ck-folder-children{
  max-block-size:none;overflow-y:visible}
.ck-folder-group.is-open > .ck-folder-children{display:block}
.ck-folder-children .ck-folder-row{font-weight:var(--font-weight-normal)}
.ck-folder-children .ck-folder-row:is([aria-selected="true"],.is-selected){
  font-weight:var(--font-weight-semibold)}

/* Chevron. CLOCKWISE, both directions — 0deg closed, 90deg open, and closing
   runs 90 -> 180 rather than rewinding. The integrations reference collapses
   with rotate(-90deg), which turns the opposite way to every other disclosure
   in the kit; § 14.9 check 7 calls that a defect, so it was not carried over.
   The state is .is-open on the group; the animation only decorates it. */
.ck-folder-chev{
  width:var(--icon-size);height:var(--icon-size);flex:none;padding:0;
  display:grid;place-items:center;
  border:0;background:0;cursor:pointer;
  color:var(--muted-foreground);transform:rotate(90deg)}
.ck-folder-chev svg{width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-folder-chev:focus-visible{outline:0;box-shadow:var(--focus-ring);border-radius:var(--radius-xs)}
/* CLOSED POINTS DOWN, OPEN POINTS RIGHT — the navlist's rule, so the two
   disclosure lists read the same. The glyph is chevron-right, so shut RESTS at
   90deg; opening transitions clockwise 90 -> 360 (three quarters) and closing
   runs ck-tree-chev-close clockwise 0 -> 90 (one quarter).

   Two defects this replaces. Open was an animation, so every group rendered
   open replayed its turn on load — 6 of 17 spinning in Document Editor before
   anything was clicked. And the close ran 90 -> 180, ending with the arrow
   pointing LEFT, then snapped 180deg in one frame back to its 0deg rest when
   .is-closing dropped. The close now ends on the drawing it rests at. */
.ck-folder-group.is-open > .ck-folder-row > .ck-folder-chev{
  transform:rotate(360deg);transition:transform var(--duration-chevron) var(--ease-default)}
.ck-folder-group.is-closing > .ck-folder-row > .ck-folder-chev{
  animation:ck-tree-chev-close var(--duration-chevron) var(--ease-default) forwards}
@media (prefers-reduced-motion:reduce){
  .ck-folder-group > .ck-folder-row > .ck-folder-chev{animation-duration:1ms;transition-duration:1ms}}
/* One quarter clockwise, from open (0deg = right) to shut (90deg = down). */
@keyframes ck-tree-chev-close{from{transform:rotate(0deg)}to{transform:rotate(90deg)}}
@media (prefers-reduced-motion:reduce){
  /* Shortened, not removed: dropping the animation would leave the chevron
     pointing the wrong way (§ 14.9 check 8). */
  .ck-folder-group.is-open > .ck-folder-row > .ck-folder-chev,
  .ck-folder-group.is-closing > .ck-folder-row > .ck-folder-chev{animation-duration:1ms}}
/* A row with no children still reserves the chevron's lane, or its label
   starts 16px left of its siblings' and the column goes crooked. */
.ck-folder-chev.is-empty{visibility:hidden;cursor:default}

/* Folder glyph. The two icons crossfade rather than one being swapped, so the
   row's width never changes between states. */
.ck-folder-glyph{
  position:relative;width:var(--icon-size);height:var(--icon-size);flex:none;
  display:inline-grid;place-items:center;color:var(--muted-foreground)}
.ck-folder-glyph svg{
  position:absolute;width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);
  transition:opacity var(--duration-base)}
.ck-folder-glyph .ck-folder-open{opacity:0}
/* THE STANDARD VARIANT HAS ONE FOLDER ICON, at every level and in every
   state — a folder is a folder however deep it sits. There is no workspace
   level any more, so no stacked-folders glyph, and the open / shut crossfade
   below belongs to the document variant only: a standard row carries a single
   svg, and if it ever carries two, only the shut one is drawn. */
.ck-folder-nav:not(.is-document) .ck-folder-glyph svg{position:static;opacity:1}
.ck-folder-nav:not(.is-document) .ck-folder-glyph .ck-folder-open{display:none}

/* Open OR selected shows the open glyph. Selected matters on its own: a leaf
   folder has nothing to expand, so without this rule the row a reader is
   actually looking at is the one drawn shut. */
.ck-folder-nav.is-document .ck-folder-group.is-open > .ck-folder-row > .ck-folder-glyph .ck-folder-open,
.ck-folder-nav.is-document .ck-folder-row:is([aria-selected="true"],.is-selected) > .ck-folder-glyph .ck-folder-open{opacity:1}
.ck-folder-nav.is-document .ck-folder-group.is-open > .ck-folder-row > .ck-folder-glyph .ck-folder-shut,
.ck-folder-nav.is-document .ck-folder-row:is([aria-selected="true"],.is-selected) > .ck-folder-glyph .ck-folder-shut{opacity:0}

.ck-folder-name{flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap}

/* ── Count ──
   This is the .ck-counter atom, not a copy of it: the markup is
   <span class="ck-counter blue"> in the editor and <span class="ck-counter
   orange"> in a listing. Both tones already exist in the palette, so the tree
   mints nothing.

   The count is INFORMATION, not an action, so it is always present and it
   holds the trailing edge. The controls sit BEFORE it in the markup and the
   count takes the auto margin, so the gap opens ahead of the count and the
   count itself does not move when a row is hovered. Put the controls after it
   instead and every count in the tree jogs sideways under the pointer. */
.ck-folder-row > .ck-counter{margin-inline-start:auto}
/* THE COUNT IS ALWAYS SHOWN, on every folder at every level — in the
   standard variant it is the AMBER counter (.ck-counter.amber), and it stays
   when the row is not hovered. (A parent folder's count used to be hidden.) */

/* ── Row controls, revealed on approach ──
   Width animates from 0 so the count slides rather than the row reflowing, and
   they reveal on :focus-within as well as :hover — a control a keyboard cannot
   reach is a control § 14.3 check 31 does not allow.

   NO overflow:hidden here, deliberately. opacity:0 already hides the glyph,
   and a clipping box would cut off the tooltip every icon-only control owes
   the reader (check 18). */
.ck-folder-tool{
  position:relative;
  width:0;min-width:0;height:24px;flex:none;padding:0;
  display:grid;place-items:center;
  border:0;background:0;border-radius:var(--radius-sm);
  color:var(--muted-foreground);cursor:pointer;
  opacity:0;pointer-events:none;
  margin-inline-start:calc(-1 * var(--ck-row-gap));
  transition:width var(--duration-base) var(--ease-default),
             opacity var(--duration-base) var(--ease-default),
             margin var(--duration-base) var(--ease-default)}
.ck-folder-tool svg{width:var(--icon-size);height:var(--icon-size);stroke-width:var(--icon-stroke);display:block}
.ck-folder-tool:is(:hover,[data-force~="hover"]){background:var(--hover-bg);color:var(--foreground)}
.ck-folder-tool:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-folder-row:is(:hover,:focus-within,[data-force~="hover"]) > .ck-folder-tool,
.ck-folder-row:is([aria-selected="true"],.is-selected) > .ck-folder-tool{
  opacity:1;pointer-events:auto;width:24px;
  margin-inline-start:calc(var(--space-xs) - var(--ck-row-gap))}
/* THE STANDARD ROW'S CONTROLS, left to right: the kebab (.is-more — the
   folder's actions), New Folder (.is-newfolder — a folder inside this one),
   then the amber count. The two controls reveal on hover and focus; the count
   is always there. Every folder at every level carries all three. The
   document editor's tree is a picture of what is open, not a place to build
   one, so it offers no creation at all. */
.ck-folder-nav.is-document .ck-folder-tool.is-newfolder{display:none}
/* Refresh spins while it works, on the kit's one spin keyframe. */
.ck-folder-tool.is-spinning svg{animation:ck-spin .8s linear infinite}

/* ── Document level — .ck-folder-nav.is-document ──
   Blue counts, and a checkbox on every document row. The checkbox governs
   what is OPEN in the editor; it never deletes anything, which is why it is
   a checkbox and not a dismiss. */
/* In the editor a folder's count is the pages loaded under it. */
.ck-folder-nav.is-document .ck-folder-group > .ck-folder-row > .ck-counter{display:inline-grid}
/* A document reserves NO chevron lane. A folder keeps its empty lane because
   that is what aligns it with the siblings that do expand; a document can
   never expand, so the lane is 23px of nothing in front of every row — and
   the selection highlight draws that emptiness.

   Dropping it also lands the checkbox exactly under the parent folder's icon,
   which is the indent rule stated above: children indent 46px + row padding
   8px = 54px, the same x as the glyph on the folder holding them. */
.ck-folder-doc > .ck-folder-chev{display:none}
.ck-folder-doc{font-weight:var(--font-weight-normal)}
.ck-folder-doc > .ck-check{flex:none;margin:0}
.ck-folder-doc:has(> .ck-check:checked){font-weight:var(--font-weight-medium)}
/* Documents sit one step deeper than the subfolder holding them. */
.ck-folder-children .ck-folder-children{padding-inline-start:calc(var(--icon-size) + var(--ck-row-gap))}

/* ── Empty and filtered states ── */
.ck-folder-empty{
  padding:var(--space-base) var(--space-sm);text-align:center;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}
.ck-folder-row[hidden],.ck-folder-group[hidden]{display:none}

/* ===========================================================================
   ANIMATED ALPHA RAMPS  ·  § 2a
   ---------------------------------------------------------------------------
   § 2 requires every colour to be 6-digit hex, and offers a companion
   --*-opacity token wherever alpha is genuinely needed. A ring that FADES
   cannot use either escape on its own: pre-composing the trough against the
   page ground yields an opaque ring that expands and then sits there, and
   box-shadow has no opacity channel of its own.

   So the ramp rides a ::after ring. The colour is a token; the alpha is a
   --pulse-*-opacity token; the trough is always 0. Four roles, named for what
   the pulse MEANS, never for how strong it looks:

     attention  destructive  this needs action
     activity   info         this just changed
     input      primary      this is waiting for entry
     guide      background   the guided-tour spotlight

   A host needs position:relative and its own border-radius; the ring inherits
   both. Respects prefers-reduced-motion.
   =========================================================================== */
.ck-pulse{position:relative}
.ck-pulse::after{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
}
.ck-pulse.is-attention::after{
  box-shadow:0 0 0 0 var(--destructive);opacity:var(--pulse-attention-opacity);
  animation:ck-pulse-attention 1.4s ease-in-out infinite;
}
.ck-pulse.is-activity::after{
  box-shadow:0 0 0 0 var(--info);opacity:var(--pulse-activity-opacity);
  animation:ck-pulse-activity 1.1s ease-out infinite;
}
.ck-pulse.is-input::after{
  box-shadow:0 0 0 0 var(--primary);opacity:0;
  animation:ck-pulse-input 1.2s ease-in-out infinite;
}
.ck-pulse.is-guide::after{
  box-shadow:0 0 0 2px var(--background);opacity:var(--pulse-guide-opacity);
  animation:ck-pulse-guide 2.2s ease-in-out infinite;
}
@keyframes ck-pulse-attention{
  0%,100%{box-shadow:0 0 0 0 var(--destructive);opacity:var(--pulse-attention-opacity)}
  50%    {box-shadow:0 0 0 6px var(--destructive);opacity:0}
}
@keyframes ck-pulse-activity{
  0%,100%{box-shadow:0 0 0 0 var(--info);opacity:var(--pulse-activity-opacity)}
  50%    {box-shadow:0 0 0 5px var(--info);opacity:0}
}
@keyframes ck-pulse-input{
  0%,100%{box-shadow:0 0 0 0 var(--primary);opacity:0}
  50%    {box-shadow:0 0 0 4px var(--primary);opacity:var(--pulse-input-opacity)}
}
@keyframes ck-pulse-guide{
  0%,100%{box-shadow:0 0 0 2px var(--background);opacity:var(--pulse-guide-opacity)}
  50%    {box-shadow:0 0 0 6px var(--background);opacity:calc(var(--pulse-guide-opacity) * .4)}
}
@media (prefers-reduced-motion:reduce){
  .ck-pulse::after{animation:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   NUMBER STEPPER — .ck-stepper
   ══════════════════════════════════════════════════════════════════════════
   A small integer control: a value with increment and decrement. Minted
   because .ck-input type=number gives the browser's own spinner, which cannot
   be styled, differs per engine, and disappears entirely on touch.

   SIZE. On the control scale (section 6): 36 default, 28 .size-sm, 44 .size-lg.

   TARGET. The two buttons stack inside the control's height, so at 36px each
   is 18px -- under WCAG 2.5.8. Each therefore carries a ::after that extends
   its target to 24x24 while the drawn button stays 18px. This is the pattern
   section 13 already records for a dismiss inside a pill: the hit area grows,
   the glyph does not. */
.ck-stepper{
  display:inline-flex;align-items:center;box-sizing:border-box;
  height:36px;gap:var(--space-sm);padding:0 var(--space-sm);
  border:1px solid var(--field-border);border-radius:var(--radius-md);
  background:var(--background);color:var(--foreground);
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);
  transition:border-color var(--duration-base),box-shadow var(--duration-base)}
.ck-stepper.size-sm{height:28px;padding:0 var(--space-xs)}
.ck-stepper.size-lg{height:44px;padding:0 var(--space-md)}
.ck-stepper:is(:hover,[data-force~="hover"]){border-color:var(--input-border-hover)}
.ck-stepper:focus-within{border-color:var(--ring);box-shadow:var(--focus-ring)}
.ck-stepper[aria-invalid="true"]{border-color:var(--destructive)}
.ck-stepper[aria-invalid="true"]:focus-within{box-shadow:var(--focus-ring-error)}
.ck-stepper:is(.is-disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:var(--muted);color:var(--muted-foreground);border-color:var(--field-border);
  cursor:not-allowed;pointer-events:none}
/* The value. Tabular figures so the control does not jitter as it counts. */
.ck-stepper-val{
  min-width:2ch;text-align:center;font-variant-numeric:tabular-nums;
  color:var(--foreground)}
/* An editable value — input.ck-stepper-val — for a number the reader types as
   well as steps to: page 200 of a 350-page document is not reached one click
   at a time. The input takes the value's own look and none of a field's
   chrome; the stepper's border and focus ring are the field. */
input.ck-stepper-val{
  inline-size:4ch;padding:0;border:0;background:transparent;
  font:inherit;outline:0;appearance:textfield}
/* 1px is not a spacing step and is not meant to be one: it is the hairline
   that stops the two buttons reading as a single split rectangle. The
   spacing scale starts at 2px and none of it applies to a seam. */
.ck-stepper-btns{display:flex;flex-direction:column;gap:1px/* scale-exempt: a seam, not spacing */}
.ck-stepper-btn{
  display:grid;place-items:center;position:relative;box-sizing:border-box;
  width:18px;height:17px;padding:0;border:0;
  background:transparent;color:var(--muted-foreground);cursor:pointer;
  border-radius:var(--radius-xs);
  transition:background var(--duration-base),color var(--duration-base)}
/* 24x24 target on a control that draws at 18x17 (WCAG 2.5.8). */
.ck-stepper-btn::after{
  content:'';position:absolute;left:50%;top:50%;
  width:24px;height:24px;transform:translate(-50%,-50%)}
.ck-stepper-btn svg{
  width:10px;height:10px;display:block;stroke-width:var(--icon-stroke)}
/* SIZE-SM: 28px tall leaves 26px inside the stroke, and two 17px buttons plus
   the 1px seam need 35 — they drew 3px above the box and 4px below it. At this
   size each button is 12px with an 8px glyph (25px, inside the 26); the 24x24
   target from ::after is unchanged. */
.ck-stepper.size-sm .ck-stepper-btn{width:16px;height:12px}
.ck-stepper.size-sm .ck-stepper-btn svg{width:8px;height:8px}
.ck-stepper-btn:is(:hover,[data-force~="hover"]){
  background:var(--hover-bg);color:var(--foreground)}
.ck-stepper-btn:is(:active,[data-force~="active"]){
  background:var(--selected-bg);color:var(--selected-fg)}
.ck-stepper-btn:is(:focus-visible,[data-force~="focus"]){
  outline:0;box-shadow:var(--focus-ring)}
.ck-stepper-btn:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  color:var(--input);cursor:not-allowed;pointer-events:none}

/* ══════════════════════════════════════════════════════════════════════════
   OVERFLOW IN AN ACTION BAR — .ck-action-bar.is-scroll-x / .is-scroll-y
   ══════════════════════════════════════════════════════════════════════════
   A bar whose controls do not fit scrolls. It does NOT wrap: re-stacking into
   a second row moves every control out from under the cursor and changes the
   bar's height, which shifts the content beside it. Horizontal bars scroll
   sideways, vertical bars scroll down. */
.ck-action-bar.is-scroll-x{
  flex-wrap:nowrap;overflow-x:auto;overflow-y:hidden;
  scrollbar-width:thin;overscroll-behavior-inline:contain}
.ck-action-bar.is-scroll-x > *{flex:none}
.ck-action-bar.is-vertical{
  flex-direction:column;align-items:stretch}
.ck-action-bar.is-vertical.is-scroll-y{
  flex-wrap:nowrap;overflow-y:auto;overflow-x:hidden;
  max-block-size:100%;scrollbar-width:thin;overscroll-behavior-block:contain}
.ck-action-bar.is-vertical.is-scroll-y > *{flex:none}
/* The scroll container must not clip a tooltip, so the bar stays a plain
   scroller and ckAutoTip renders its bubble on <body> (see section 8). */

/* ══════════════════════════════════════════════════════════════════════════
   MIDDLE TRUNCATION — .ck-trunc-mid
   ══════════════════════════════════════════════════════════════════════════
   CSS can only put the ellipsis at the end, and the end of a document name is
   usually the part that identifies it: "Invoice-2026-0…" could be any of a
   thousand files, "Invoice…-0412" is one. ckTruncateMid() measures the box and
   rebuilds the string with the middle removed; this rule is what it needs to
   measure against, and what keeps a non-JS page from wrapping. */
.ck-trunc-mid{
  display:inline-block;max-width:100%;
  white-space:nowrap;overflow:hidden;text-overflow:clip;
  vertical-align:bottom}

/* ── A MULTI-SELECT NEVER SHOWS ITS SELECTION INSIDE THE FIELD ─────────────
   The field keeps its placeholder, always, however many values are chosen.

   Chips packed into the trigger grow the control as the selection grows, so
   the field moves under the pointer while it is being used, the caret shifts,
   and at 37 values the "field" is a paragraph of chips with a caret somewhere
   in it. The control's job is to be a stable target that opens a list; it is
   not a display surface for the answer.

   So the answer lives BELOW it, in two parts:

     .ck-dd-summary   one row — the count on the leading edge, Clear All on the
                      trailing one. It answers "how many?" without the reader
                      counting chips, and it is where a bulk action belongs.
     .ck-dd-tags      the chips themselves, under that row.

   Both are hidden while nothing is chosen: a count of zero and a Clear All
   with nothing to clear are two controls that cannot be used. */
.ck-dd.is-multi .ck-dd-trigger > .ck-dd-tags{display:none}
.ck-dd.is-multi .ck-dd-value{
  flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap;
  color:var(--muted-foreground)}

.ck-dd-summary{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-sm);width:100%;min-width:0;
  margin-block-start:var(--space-sm)}
.ck-dd-count{
  min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap;
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--foreground);font-variant-numeric:tabular-nums}
/* Clear All is destructive and it is not the panel's main action, so it is a
   link button in the destructive tone — the same treatment .ck-clear-all
   already uses, not a filled red button competing with the field. */
.ck-dd-clear-all{
  display:inline-flex;align-items:center;gap:var(--space-xs);flex:none;
  border:0;background:0;padding:0;cursor:pointer;
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--destructive-soft-foreground)}
.ck-dd-clear-all > svg{
  width:var(--icon-size);height:var(--icon-size);flex:none;
  stroke-width:var(--icon-stroke)}
.ck-dd-clear-all:is(:hover,[data-force~="hover"]){color:var(--destructive)}
.ck-dd-clear-all:focus-visible{outline:0;box-shadow:var(--focus-ring-error);border-radius:var(--radius-xs)}

.ck-dd.is-multi > .ck-dd-tags{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-xs);
  flex:none;width:100%;margin-block-start:var(--space-xs);min-width:0}
/* Nothing chosen: no count, no Clear All, no chip rail. */
.ck-dd.is-multi:not(.has-value) > .ck-dd-summary,
.ck-dd.is-multi:not(.has-value) > .ck-dd-tags,
.ck-dd.is-multi > .ck-dd-tags:empty{display:none}
/* A CHIP'S DISMISS RESTS IN THE CHIP'S OWN FOREGROUND — every chip, every
   rail of chosen values, single or multi. A glyph inside a badge takes the
   badge's foreground token; the cross turns destructive only on approach —
   the --destructive-bg tint and the destructive glyph on hover, solid when
   pressed. These rails used to rest red (--icon-color-destructive) on the
   argument that dropping a chosen value is not the same act as closing; the
   design owner's rule is one cross, one behaviour. */
:is(.ck-dd.is-multi > .ck-dd-tags, .ck-dd-picker-chips) .ck-chip .ck-icon-btn{color:inherit}
:is(.ck-dd.is-multi > .ck-dd-tags, .ck-dd-picker-chips) .ck-chip .ck-icon-btn:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
:is(.ck-dd.is-multi > .ck-dd-tags, .ck-dd-picker-chips) .ck-chip .ck-icon-btn:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground)}

/* ══════════════════════════════════════════════════════════════════════════
   ONE-TIME CODE — .ck-otp-row / .ck-otp
   ══════════════════════════════════════════════════════════════════════════
   A row of single-character boxes for a verification code. Minted because
   .ck-input is the wrong component and not by a little: a field is a left-read
   line of text at --text-base, and a code box is ONE large centred character
   whose whole job is to be checked against a message on another device.
   Putting .ck-input on it produced a 36px field with 13px left-aligned digits,
   which is unreadable at a glance and loses the count-the-boxes affordance.

   SIZE. 44px square — .size-lg on the control scale (section 6). The previous
   44x52 was portrait and on no scale; square keeps the boxes reading as a set
   rather than as four tall fields.

   The character is --text-3xl semibold, the largest size the type scale gives
   a control, because it is read across a room from a phone.

   STATE. Six, and `filled` is the one a code entry actually needs: a box that
   already has a character takes the --primary stroke, so the user can see how
   far along they are without reading the digits back. */
.ck-otp-row{
  display:flex;align-items:center;gap:var(--space-sm);flex-wrap:wrap}
.ck-otp{
  box-sizing:border-box;width:44px;height:44px;padding:0;
  text-align:center;
  font:var(--font-weight-semibold) var(--text-3xl) var(--font-sans);
  font-variant-numeric:tabular-nums;
  color:var(--foreground);background:var(--background);
  border:1px solid var(--field-border);border-radius:var(--radius-md);
  transition:border-color var(--duration-base),box-shadow var(--duration-base)}
.ck-otp.size-sm{width:36px;height:36px;font-size:var(--text-xl)}
.ck-otp:is(:hover,[data-force~="hover"]){border-color:var(--input-border-hover)}
/* :focus, not :focus-visible — clicking a code box places a caret, and section
   13 records that showing focus is correct there. */
.ck-otp:is(:focus,[data-force~="focus"]){
  outline:0;border-color:var(--ring);box-shadow:var(--focus-ring)}
/* Filled — the count-your-progress state. */
.ck-otp.is-filled{border-color:var(--primary)}
.ck-otp[aria-invalid="true"]{border-color:var(--destructive)}
.ck-otp[aria-invalid="true"]:is(:focus,[data-force~="focus"]){
  box-shadow:var(--focus-ring-error)}
.ck-otp:is(:disabled,[aria-disabled="true"],[data-force~="disabled"]){
  background:var(--muted);color:var(--muted-foreground);border-color:var(--field-border);
  cursor:not-allowed}

/* ══════════════════════════════════════════════════════════════════════════
   AVATAR MODES — .ck-avatar.is-icon · .ck-avatar-stack
   ══════════════════════════════════════════════════════════════════════════
   The plate has three modes and they are mutually exclusive: a photo, two
   initials, or — when there is neither, which is every unclaimed seat and
   every system actor — a glyph. Without the third, call sites invent one, and
   the usual invention is a "?" that reads as an error rather than an absence.

   The glyph mode drops to --muted-foreground on the same plate: present, but
   plainly not a person yet. */
.ck-avatar.is-icon{color:var(--muted-foreground)}
.ck-avatar.is-icon > svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
.ck-avatar.size-sm.is-icon > svg{width:12px;height:12px}
.ck-avatar.size-lg.is-icon > svg{width:20px;height:20px}

/* A stack is how several people fit in one row's worth of width. They overlap
   by a third, and each carries a ring in the surface colour so the edge of one
   plate is still readable against the next. The ring is --card because a stack
   lives on a card or a table row, not on the page ground.

   The overflow count is the same plate, so +4 lines up with the faces rather
   than sitting a pixel high beside them. */
.ck-avatar-stack{display:inline-flex;align-items:center;flex-direction:row-reverse}
.ck-avatar-stack > .ck-avatar{
  margin-inline-start:calc(var(--icon-box) / -3);
  box-shadow:0 0 0 2px var(--card)}
.ck-avatar-stack > .ck-avatar:last-child{margin-inline-start:0}
.ck-avatar-stack > .ck-avatar.size-sm{margin-inline-start:calc(20px / -3)}
.ck-avatar-stack > .ck-avatar.size-lg{margin-inline-start:calc(32px / -3)}
.ck-avatar-stack > .ck-avatar.is-more{
  background:var(--muted);color:var(--muted-foreground);
  border-color:var(--panel-border)}

/* The rail and the folder "add" button used to be circles. Every other
   interactive glyph in the product hovers to a rounded square at
   --radius-md, and a circle among them reads as a different kind of control —
   section 8 calls that out. They take the icon button's radius now. The one
   deliberate circle left is .ck-avatar-edit-btn, which sits on a round photo
   and is shaped to it rather than to a hover surface. */

/* The column delete used to be a sibling of the header's text in a block-level
   th, so it fell onto a second line and pushed the row taller on hover. It is
   pinned to the cell's trailing edge now, on the label's line, and the cell
   reserves room for it so a long column name never runs underneath.

   pointer-events is off at rest: an invisible button still swallows the click
   that should have sorted the column. And the drawn glyph is 16px, which is
   under the 24px floor, so it carries a ::after that grows the target without
   moving the icon — the same pattern as a dismiss inside a pill. */
.ck-table th:has(> .ck-th-delete){
  padding-inline-end:calc(10px + var(--icon-box) + var(--space-sm))}

/* The column delete reads destructive at rest, like every other destructive
   glyph: --icon-color-destructive, then the --destructive-bg tint on hover and
   the solid --destructive when pressed — the same three steps
   .ck-icon-btn.tone-danger takes. It used to sit grey until hovered, which
   made the one irreversible control in the header the quietest thing in it.

   Drawn at 24px rather than 16, so the hover surface is a real rounded square
   and the target clears 2.5.8 without a ::after standing in for it. */


/* ─── RELATION TREE (Organism) — .ck-relation ──────────────────────────────
   A canvas of nodes joined by connectors: which folders feed which. Built from
   atoms and contributing layout only — the panel is .ck-panel, the zoom control
   is .ck-action-bar with .ck-icon-btn inside it, and a badge on a node is
   .ck-pill. Nothing here restyles an atom.

   Positions are authored as percentages, so the tree is resolution independent
   and reads the same at 320px and at 2000px. Scale lives in one custom
   property, --ck-rel-zoom, which the plot's transform AND its reserved size
   both read — that is what makes the canvas scroll rather than clip once a
   tree outgrows its panel. Transform alone would zoom the drawing and leave the
   scrollport unaware of it. */
.ck-relation{
  --ck-rel-zoom:1;
  --ck-rel-node:64px;          /* see the note under sizes: an illustration, not a control */
  --ck-rel-label:var(--text-md);
  --ck-rel-min:260px;
  position:relative;box-sizing:border-box;
  display:flex;flex-direction:column;min-height:0;
}
/* Deliberately NOT the 28/36/44 control scale. A node is an illustration the
   reader looks AT, like .ck-thumb or an avatar, not a control they operate —
   at 36px a folder is an icon and the tree stops reading as a picture of
   folders. Its own scale, still on the 4px grid. */
.ck-relation.size-sm{--ck-rel-node:48px;--ck-rel-label:var(--text-sm);--ck-rel-min:200px}
.ck-relation.size-lg{--ck-rel-node:80px;--ck-rel-label:var(--text-base);--ck-rel-min:340px}

.ck-relation-head{
  display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--space-base) var(--space-base) var(--space-sm);flex:none;
}
.ck-relation-title{
  flex:1;min-width:0;overflow:hidden;text-overflow:clip;white-space:nowrap;
  font:var(--font-weight-semibold) var(--text-xl) var(--font-sans);color:var(--foreground);
}

/* The scrollport. Its ground is --canvas: the plot is a surface the panel
   contains, not the panel itself. */
.ck-relation-canvas{
  position:relative;flex:1;min-height:var(--ck-rel-min);
  overflow:auto;scrollbar-width:thin;overscroll-behavior:contain;
  background:var(--canvas);color:var(--canvas-foreground);
  border:1px solid var(--panel-stroke);border-radius:var(--radius-2xl);
}
/* Plotting paper, so a node reads as placed rather than floating. The dots
   sit on the plot, so they scale with it and the spacing stays honest. */
.ck-relation-plot{
  background-image:radial-gradient(var(--panel-stroke) 1px,transparent 1px);
  background-size:20px 20px;background-position:0 0;
}
/* The plot's BOX stays the canvas's size, so a node's percentage resolves
   against something that does not move — then one transform scales the whole
   drawing. Scaling the box as well made percentages resolve against a box that
   had also grown, and nodes spread at zoom-squared while the folders grew at
   zoom. `zoom` instead of a transform changes how width:100% resolves and gets
   it wrong the other way.
   A transform is invisible to layout, so the scrollport would never learn the
   drawing grew; the margins reserve exactly the overflow the transform adds,
   which is what makes the canvas scroll rather than clip. */
.ck-relation-plot{
  position:relative;
  width:100%;height:100%;
  min-height:var(--ck-rel-min);
  transform:scale(var(--ck-rel-zoom));
  transform-origin:0 0;
  margin-inline-end:calc(100% * (var(--ck-rel-zoom) - 1));
  margin-block-end:calc(var(--ck-rel-min) * (var(--ck-rel-zoom) - 1));
}

.ck-relation-connectors{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}
/* non-scaling-stroke is what lets connectors be authored in a percentage
   viewBox: without it the viewBox transform multiplies stroke-width, so the
   same 2px line renders hairline on a tall panel and a slab on a wide one. */
/* NOT inside a <marker>. An arrowhead is a <path> and a descendant, so the
   blanket fill:none caught it too — and a marker can only carry its colour
   as a presentation attribute, which loses to every CSS rule. That made a
   working arrowhead unauthorable inside a kit connector svg: both DES-667
   relation diagrams drew headless arrows from the moment they adopted the
   class. */
.ck-relation-connectors :is(line,path,polyline):not(marker *){
  stroke:var(--connector);stroke-width:2;fill:none;
  stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
}
.ck-relation-connectors .is-active{stroke:var(--primary);stroke-width:2.5}

/* A node is centred on its own percentage, so a percentage names where the
   node IS rather than where its corner is. */
.ck-relation-node{
  position:absolute;transform:translate(-50%,-50%);
  display:flex;flex-direction:column;align-items:center;gap:var(--space-2xs);
  max-width:140px;text-align:center;cursor:pointer;
  background:0;border:0;padding:0;font:inherit;color:inherit;
}
/* A node is drawn as a folder rather than an icon on a plate: the tree is a
   picture of folders, and a plate around each one turns it back into a list. */
.ck-relation-node-glyph{
  width:var(--ck-rel-node);height:calc(var(--ck-rel-node) * .82);flex:none;
  display:block;
}
.ck-relation-node-glyph > svg{width:100%;height:100%;display:block;overflow:visible}
.ck-relation-folder-body{fill:var(--relation-node);transition:fill var(--duration-base)}
.ck-relation-folder-tab{fill:var(--relation-node-tab);transition:fill var(--duration-base)}
/* The node the tree is drawn FROM. One folder differs, which is what makes it
   findable at a glance. */
.ck-relation-node:is(.is-source,[data-source="true"]) .ck-relation-folder-body{fill:var(--relation-node-source)}
.ck-relation-node:is(.is-source,[data-source="true"]) .ck-relation-folder-tab{fill:var(--relation-node-source-tab)}
.ck-relation-node-label{
  max-width:100%;overflow:hidden;text-overflow:clip;white-space:nowrap;
  font:var(--font-weight-medium) var(--ck-rel-label) var(--font-sans);color:var(--foreground);
}
.ck-relation-node:is(:hover,[data-force~="hover"]) > .ck-relation-node-glyph{filter:brightness(1.06)}
.ck-relation-node:focus-visible{outline:0}
.ck-relation-node:focus-visible > .ck-relation-node-glyph{box-shadow:var(--focus-ring);border-radius:var(--radius-sm)}
.ck-relation-node:is([aria-selected="true"],.is-selected) > .ck-relation-node-glyph{
  outline:2px solid var(--primary);outline-offset:3px;border-radius:var(--radius-sm);
}
.ck-relation-node:is([aria-selected="true"],.is-selected) > .ck-relation-node-label{
  color:var(--primary);font-weight:var(--font-weight-semibold);
}
/* Dimming the rest is how a selected path is read, so it is a state of the
   others rather than a colour on the one. */
.ck-relation-node.is-muted{opacity:.45}
.ck-relation-node:is(:disabled,[aria-disabled="true"]){cursor:not-allowed;opacity:.5;pointer-events:none}

/* Pinned to the PANEL, not the scrollport and not the plot. Anchored to the
   scrollport it scrolls away with the content; anchored to the plot it scales
   with the zoom it controls, which is absurd. On the panel it holds its corner
   and its size through every view change. */
.ck-relation-zoom{
  position:absolute;inset-block-start:calc(var(--space-base) + var(--space-sm));
  inset-inline-end:var(--space-sm);
  z-index:var(--z-sticky);
  background:var(--card);color:var(--card-foreground);
}
/* A head that carries its own actions — a close button, say — has nowhere for
   an absolutely positioned bar to go without landing on top of them. Put the
   bar in the head and it joins that row instead, which is still the panel's
   top-right and still outside the scrollport, so it neither scrolls nor
   scales. */
.ck-relation-head > .ck-relation-zoom{position:static;margin-inline-start:auto}

/* The readout is reserved at its widest ("100%"), so the bar does not reflow
   as the number changes — a control that resizes while you use it is a moving
   target for the pointer already on it. */
.ck-relation-zoom [data-ck-zoom-label]{
  box-sizing:border-box;min-width:48px;   /* fits "100%" including the label's own padding */
  text-align:center;font-variant-numeric:tabular-nums;
}
/* The zoom slider is a fixed track, so the bar's width cannot follow the
   value. Without this the whole control resizes as you drag it. */
.ck-relation-zoom .ck-slider{width:104px;flex:none}
.ck-relation-zoom .ck-slider-row{gap:var(--space-sm)}

/* Section 7d — empty, loading and error, in the shape of the content. */
.ck-relation-canvas > :is(.ck-empty,.ck-error-state){margin:var(--space-xl) auto;max-width:320px}
.ck-relation-canvas > .ck-loading-overlay{border-radius:var(--radius-2xl)}

/* ── LABELS THE MARKUP NAMED BUT NOTHING STYLED ───────────────────────────
   Each of these was written into a component's markup and never given a rule,
   so the text fell back to whatever it inherited. Caught by the build's
   unstyled-class check rather than by eye. */

/* The count beside a "select all" box, in a picker and in manage-columns.
   It is a running total, not a label, so it reads muted against the row's
   own text — the same relationship .ck-field-hint has to its field. */
.ck-dd-all-label,.ck-col-master-label{
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}

/* The hint pinned under the sidebar flyout. */
.ck-sidebar-flyout-foot-text{
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground)}

/* The folder glyph in the relation tree. Its paths were styled; the svg that
   carries them was not, so it fell back to the replaced-element default of
   300x150 and the node sized itself around that. */
.ck-relation-folder{
  width:100%;height:100%;display:block;overflow:visible}

/* ══════════════════════════════════════════════════════════════════════════
   NAVIGATION LIST — the three rulings of 2026-09-17
   ══════════════════════════════════════════════════════════════════════════
   1. THE PANEL FILLS ITS COLUMN. A settings nav that stops where its items
      stop leaves the page ground showing below it and reads as a card that ran
      out, not as the side of the screen. It is --card, which is white in light
      and the elevated surface in dark, so it stays a surface rather than
      becoming a hole when the theme changes. */
.ck-navlist{min-block-size:100%;display:flex;flex-direction:column}

/*    2. THE CHEVRON IS ALWAYS THERE, and it turns clockwise in both
      directions. Closed it points DOWN, open it points RIGHT — so opening is
      three quarters of a turn and closing is the last quarter, and the pair
      completes one revolution. A two-state transform would unwind the long way
      on close, which reads as the control undoing itself rather than
      advancing. Same reasoning as the dropdown and the folder tree; only the
      resting angles differ. */
@keyframes ck-navlist-chev-close{from{transform:rotate(270deg)}to{transform:rotate(360deg)}}
.ck-navlist-chevron{
  margin-inline-start:auto;flex:none;
  width:var(--icon-size);height:var(--icon-size);
  display:grid;place-items:center;color:var(--icon-color);
  transform:rotate(0deg)}
.ck-navlist-chevron > svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
/* OPEN is a disclosure row expanded, OR a navigation row that is the current
   page. A settings list NAVIGATES — it discloses nothing — so the honest state
   for its selected row is aria-current="page", not aria-expanded, which would
   tell a screen reader the row controls a region. Both now turn the chevron to
   RIGHT, so a navigation list looks like the kit's demo without borrowing the
   wrong ARIA to get there. A row that stops being current carries .is-closing
   for the clockwise quarter back to DOWN. */
.ck-navlist-item:is([aria-expanded="true"],[aria-current="page"]) > .ck-navlist-chevron{
  transform:rotate(270deg);transition:transform var(--duration-chevron) var(--ease-default)}
.ck-navlist-item:is([aria-expanded="true"],[aria-expanded="false"],:not([aria-current="page"])).is-closing > .ck-navlist-chevron{
  animation:ck-navlist-chev-close var(--duration-chevron) var(--ease-default) forwards}

/*    3. SELECTED TURNS EVERYTHING WHITE. On the --primary lozenge the label
      already took --primary-foreground; the leading glyph and the chevron were
      left on --icon-color and sat at 2.35:1 on it. Every mark in the row reads
      from the same foreground now. */
.ck-navlist-item:is(.is-active,[aria-current="page"],[data-force~="selected"]) > .ck-ico,
.ck-navlist-item:is(.is-active,[aria-current="page"],[data-force~="selected"]) > .ck-navlist-chevron{
  color:var(--primary-foreground)}

/* Two variants and nothing else: with a leading glyph, or without. The row
   keeps its height either way, so a list of each lines up with the other. */
.ck-navlist.is-plain .ck-navlist-item > .ck-ico{display:none}

/* .is-soft — the chosen row in the accent blue (--selected-bg with
   --selected-fg, the pair a selected dropdown option uses) instead of the
   --primary lozenge. For a list that picks what the canvas shows, where a dark
   row would outweigh the content it selects. Marks keep their own tones on the
   light ground, so a delete stays red. */
.ck-navlist.is-soft .ck-navlist-item:is(.is-active,[aria-current="page"],[data-force~="selected"]),
.ck-navlist.is-soft .ck-navlist-item:is(.is-active,[aria-current="page"]):is(:hover,[data-force~="hover"],:active){
  background:var(--selected-bg);color:var(--selected-fg);font-weight:var(--font-weight-semibold)}
/* Semibold, as a selected .ck-dd-opt is: --hover-bg and --selected-bg are one
   value, so without it a hovered row and the chosen one looked the same. */
.ck-navlist.is-soft .ck-navlist-item:is(.is-active,[aria-current="page"],[data-force~="selected"]) > :is(.ck-ico,.ck-navlist-chevron){
  color:var(--selected-fg)}
.ck-navlist.is-soft .ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-icon-btn.tone-danger{
  color:var(--icon-color-destructive)}
.ck-navlist.is-soft .ck-navlist-item:is(.is-active,[aria-current="page"]) .ck-text.is-muted{
  color:var(--muted-foreground);opacity:1}

@media (prefers-reduced-motion:reduce){
  .ck-navlist-item > .ck-navlist-chevron{animation-duration:1ms;transition-duration:1ms}
}

/* ══════════════════════════════════════════════════════════════════════════
   CONFIRM AND CANCEL — .ck-icon-btn.tone-success · .tone-danger
   ══════════════════════════════════════════════════════════════════════════
   A tick and a cross side by side are opposite answers to the same question,
   and until now both were drawn in the neutral icon colour — so the pair read
   as two equal buttons and the reader had to decode the glyph to tell accept
   from discard. Semantic colour is what makes them a pair.

   tone-success mirrors tone-danger exactly: the soft foreground at rest, the
   tint on hover, the solid fill when pressed. --success-soft-foreground rather
   than --success at rest, because #2e9e52 on white is 3.03:1 and a 16px glyph
   is not large text. */
.ck-icon-btn.tone-success{
  color:var(--success-soft-foreground);
  --ck-icon-hover:var(--success-bg);
  --ck-icon-hover-fg:var(--success-soft-foreground)}
.ck-icon-btn.tone-success:is(:active,[data-force~="active"]){
  background:var(--success);color:var(--success-foreground)}
.ck-icon-btn.tone-success.is-selected{
  background:var(--success-bg);color:var(--success-soft-foreground)}
.ck-icon-btn.tone-success:is(:focus-visible,[data-force~="focus"]){
  box-shadow:var(--focus-ring)}

/* And the third one, for the same reason. The set had danger, success,
   primary, accent and neutral but no WARNING, so every screen that needed a
   cautionary icon control invented it — the recon folder listing painted four
   status glyphs with local `svg{stroke:…}` rules, which is a page overriding
   the colour the kit owns, and it is the tell § 14 check 45 describes: a
   variant missing from the set comes back as a local class.

   Same three steps, same arithmetic. --warning-soft-foreground #96650a at
   rest measures 5.04:1 on --background and 4.56:1 on the --warning-bg tint it
   takes on hover. --warning itself is 2.05:1 on white — under even the 3:1
   non-text floor, let alone the 4.5:1 a 16px glyph needs — which is exactly
   why the soft foreground is the resting colour here as it is on success. The
   pressed state goes solid, where --warning-foreground on --warning is
   7.38:1. */
.ck-icon-btn.tone-warning{
  color:var(--warning-soft-foreground);
  --ck-icon-hover:var(--warning-bg);
  --ck-icon-hover-fg:var(--warning-soft-foreground)}
.ck-icon-btn.tone-warning:is(:active,[data-force~="active"]){
  background:var(--warning);color:var(--warning-foreground)}
.ck-icon-btn.tone-warning.is-selected{
  background:var(--warning-bg);color:var(--warning-soft-foreground)}
.ck-icon-btn.tone-warning:is(:focus-visible,[data-force~="focus"]){
  box-shadow:var(--focus-ring)}
/* The pressed-state partner the other two tones already had. */
.ck-icon-btn.tone-warning:is(.is-on,[aria-pressed="true"]){color:var(--warning-soft-foreground)}

/* AND A PALETTE TONE — .ck-icon-btn.tone-amber
   Not a fourth semantic; a palette one. --warning is the colour of "something
   needs attention", and its mustard #96650a says fault. A status that means
   PARTIAL — half of something matched, the rest still running — is progress,
   not a fault, and the recon folder listing is where that showed: its Partial
   Reconciled glyph sat in the same gold as a genuine warning.

   The palette families ship -bg, -border and -soft-foreground but no solid
   -foreground, so this tone cannot invert on press the way the three semantic
   ones do. It deepens instead: the hover tint plus the family's own border as
   an inset ring, which is a state of its own and not a repeat of hover.
   --amber-soft-foreground measures 5.02:1 on --background and 4.51:1 on the
   --amber-bg tint (9.78 / 5.61 dark, 12.58 / 9.64 high contrast). */
.ck-icon-btn.tone-amber{
  color:var(--amber-soft-foreground);
  --ck-icon-hover:var(--amber-bg);
  --ck-icon-hover-fg:var(--amber-soft-foreground)}
.ck-icon-btn.tone-amber:is(:active,[data-force~="active"]),
.ck-icon-btn.tone-amber.is-selected{
  background:var(--amber-bg);color:var(--amber-soft-foreground);
  box-shadow:inset 0 0 0 1px var(--amber-border)}
.ck-icon-btn.tone-amber:is(.is-on,[aria-pressed="true"]){color:var(--amber-soft-foreground)}
.ck-icon-btn.tone-amber:is(:focus-visible,[data-force~="focus"]){box-shadow:var(--focus-ring)}



/* ══════════════════════════════════════════════════════════════════════════
   ERROR STATES — .ck-tab.is-error · .ck-notif-panel .ck-error-state
   ══════════════════════════════════════════════════════════════════════════
   Bible rule 6 asks for empty, loading AND error wherever content can be
   absent. Tabs and the notification panel each had two of the three: a tab
   whose panel failed to load looked exactly like one that had not been opened
   yet, and a panel that could not reach the server showed the same blank as a
   reader with nothing waiting. */

/* A tab reports its panel's failure on the tab itself, because the reader is
   looking at the strip, not at the panel they cannot see. The label goes
   destructive and a dot marks it, so the state survives the tab being
   unselected — which is the case that matters: you need to know the tab is
   broken before you click it. */
.ck-tab.is-error{color:var(--destructive-soft-foreground)}
.ck-tab.is-error:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-tab.is-error[aria-selected="true"],.ck-tab.is-error.active{
  color:var(--destructive-soft-foreground)}
.ck-tab.is-error[aria-selected="true"]::after,.ck-tab.is-error.active::after{
  background:var(--destructive)/* pairing-exempt: a 2px rail, carries no text */}
.ck-tab.is-error:focus-visible{box-shadow:var(--focus-ring-error)}
.ck-tab-error-dot{
  width:var(--space-sm);height:var(--space-sm);flex:none;
  border-radius:var(--radius-full);
  background:var(--destructive)/* pairing-exempt: a 8px dot, carries no text */}
.ck-tabs.is-segmented .ck-tab.is-error[aria-selected="true"],
.ck-tabs.is-segmented .ck-tab.is-error.active{
  background:var(--destructive);color:var(--destructive-foreground)}
.ck-tabs.is-segmented .ck-tab.is-error[aria-selected="true"] .ck-tab-error-dot,
.ck-tabs.is-segmented .ck-tab.is-error.active .ck-tab-error-dot{
  background:var(--destructive-foreground)}

/* The panel composes .ck-error-state rather than inventing its own, so a
   failure here looks like a failure anywhere else. It sits in the list so the
   header and the footer stay put and Retry lands where the reader is looking. */
.ck-notif-panel .ck-notif-list > :is(.ck-empty,.ck-error-state){
  margin:auto;max-width:280px;padding-block:var(--space-xl)}

/* A single notification can fail on its own — an action that did not go
   through. The row keeps its shape and takes the destructive tint. */
.ck-notif.is-error{background:var(--destructive-bg)}
.ck-notif.is-error:hover{background:var(--destructive-bg)}
.ck-notif.is-error .ck-notif-dot{
  background:var(--destructive)/* pairing-exempt: a status dot, carries no text */}
.ck-notif.is-error .ck-notif-text{color:var(--destructive-soft-foreground)}

/* ═══════════════════════════════════════════════════════════════════════════
   RECONCILIATION ADDITIONS — variants on existing components, and one new one.

   Everything here composes atoms and molecules the kit already ships. Where a
   thing was a variant of something that existed, it is a modifier class on
   that component rather than a new object (§ 14.3 check 45).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── MENU · the value list — .ck-menu.is-list ──────────────────────────────
   A chip that has truncated its values lists them in full. It is a menu
   because it is an anchored panel of rows placed by ckPlaceOverlay, and it is
   a VARIANT because nothing in it is chooseable: the rows are values being
   read, not actions being picked.

   So it drops everything that implies choice — no hover fill, no pointer, no
   focus ring on a row — and keeps the panel, the placement and the scroller.
   Reveal it only when the chip is genuinely cut (§ 14.3 check 33); a list that
   repeats what is already legible is noise. */
.ck-menu.is-list{padding:var(--space-xs) 0;cursor:default}
.ck-menu.is-list > .ck-menu-item{
  cursor:default;padding:6px var(--space-md);
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  color:var(--popover-foreground)}
.ck-menu.is-list > .ck-menu-item:is(:hover,:focus,[data-force~="hover"]){
  background:transparent;color:var(--popover-foreground)}
/* Long value sets scroll rather than running off the window (§ 14.3 check 55). */
.ck-menu.is-list{max-height:min(280px,calc(100vh - 2 * var(--overlay-margin)));overflow-y:auto}

/* ── BUTTON GROUP · the split button — .ck-btn-group ───────────────────────
   A split button is not a component. It is two buttons that share an edge:
   the action, and a chevron that opens the alternatives. Building it as a
   group means both halves are real .ck-btn, so every variant, size and state
   the button already has comes with it and nothing is re-declared.

   THERE IS NO SEAM. The design owner's rule: the two halves share one outer
   frame and no dividing line runs between them, so the pair reads as a single
   control. Each half still paints its own hover and pressed state. */
.ck-btn-group{display:inline-flex;align-items:stretch;min-width:0}
.ck-btn-group > .ck-btn{border-radius:0;position:relative}
.ck-btn-group > .ck-btn:first-child{
  border-start-start-radius:var(--radius-md);border-end-start-radius:var(--radius-md)}
.ck-btn-group > .ck-btn:last-child{
  border-start-end-radius:var(--radius-md);border-end-end-radius:var(--radius-md)}
.ck-btn-group > .ck-btn + .ck-btn{border-inline-start:0}
.ck-btn-group > .ck-btn:not(:last-child){border-inline-end:0}
/* The chevron half is square: it holds one glyph, so button padding would make
   it wider than it is tall. */
.ck-btn-group > .ck-btn.is-split-toggle{padding-inline:var(--space-sm)}
/* A focused half must not be overlapped by its neighbour's border. */
.ck-btn-group > .ck-btn:focus-visible{z-index:1}
/* Icon buttons in a group — the split icon control, an action and its menu
   chevron as one outlined object (a filter and its saved filters). Buttons are
   matched by type, not position, so a count badge or the menu the chevron opens
   can sit inside the group without breaking the corners. The halves pack close:
   the action keeps its glyph's width plus the kit steps, the chevron half is
   narrower, so the pair reads as one control rather than two squares. Found on
   the DES-667 saved-filters control, three copies of a hand-built frame. */
.ck-btn-group > .ck-icon-btn{border-radius:0;position:relative}
.ck-btn-group > .ck-icon-btn:first-of-type{
  border-start-start-radius:var(--radius-md);border-end-start-radius:var(--radius-md)}
.ck-btn-group > .ck-icon-btn:last-of-type{
  border-start-end-radius:var(--radius-md);border-end-end-radius:var(--radius-md)}
.ck-btn-group > .ck-icon-btn:not(:first-of-type){border-inline-start-width:0}
.ck-btn-group > .ck-icon-btn:not(:last-of-type){border-inline-end-width:0}
.ck-btn-group > .ck-icon-btn:focus-visible{z-index:1}
.ck-btn-group > .ck-icon-btn.is-split-main{width:auto;padding-inline:var(--space-sm) var(--space-xs)}
.ck-btn-group > .ck-icon-btn.is-split-toggle{width:auto;padding-inline:var(--space-xs) var(--space-sm)}
/* A COUNT ON AN ICON BUTTON is pinned to its top-end corner, out of flow. Placed
   between the two halves of a split it took a slot of its own, and the frame
   broke around it: a gap with no stroke above or below the badge. Inside the
   button it straddles the seam and the halves meet. It lifts above the next
   half, which is position:relative too and would paint over it. */
.ck-icon-btn:has(> .ck-counter){position:relative}
.ck-icon-btn > .ck-counter{
  position:absolute;z-index:2;pointer-events:none;
  inset-block-start:calc(var(--space-sm) * -1);inset-inline-end:calc(var(--space-sm) * -1)}
/* A MIXED GROUP — a labelled action with an icon half (Rescan and its
   model-change !) — is one frame too: the icon half drops its leading edge so no
   seam runs between them, and an outlined icon half takes the button's stroke,
   not its own lighter one, so the frame is one colour all the way round. */
.ck-btn-group > .ck-btn + .ck-icon-btn{border-inline-start-width:0}
.ck-btn-group > .ck-btn ~ .ck-icon-btn.is-outlined{border-color:var(--btn-border)}

/* ── DIALOG · the two-pane body — .ck-dialog-body.is-split ─────────────────
   .ck-dialog-body is a padded single column. A dialog that navigates before it
   edits — pick a document, then link it — needs a fixed rail beside a pane
   that scrolls, and the rail must NOT scroll with it.

   The rail is 260px because that is the folder navigator's own width; the two
   sit side by side in the same flow and a different number would read as a
   near-miss. The body loses its padding: each pane owns its own, or the
   divider cannot run the full height. */
.ck-dialog-body.is-split{
  flex-direction:row;gap:0;padding:0;min-height:0}
/* THE BODY DOES NOT CAP ITS OWN HEIGHT. It used to carry
   max-height:min(60vh,520px), which clipped the link picker from 655px to 520px
   — 135px of rows gone, with the rail measuring a perfect 260px beside it. A
   dialog's height is the DIALOG's to decide: .ck-dialog already holds the
   viewport cap, and a body that also caps itself competes with its own parent
   and wins by being the inner box. Same law as .ck-timeline carrying no
   padding: a component sizes its content, the container sizes the component.

   Where a cap is genuinely wanted, ask for it — it is opt-in, never inherent. */
.ck-dialog-body.is-split.is-capped{max-height:min(60vh,520px)}
/* The rail and the pane are styled on their OWN classes, not only as children
   of .is-split, so either can be lifted out and used on its own. Scoping a
   part to its parent is what makes a component impossible to adopt piecemeal. */
.ck-dialog-rail{
  flex:none;width:260px;min-width:0;overflow-y:auto;
  padding:var(--space-md);
  border-inline-end:1px solid var(--border);
  background:var(--muted);color:var(--muted-foreground)}
.ck-dialog-pane{
  flex:1;min-width:0;min-height:0;overflow-y:auto;
  padding:var(--container-inset);
  display:flex;flex-direction:column;gap:var(--space-md)}
@media (max-width:620px){
  .ck-dialog-body.is-split{flex-direction:column}
  .ck-dialog-body.is-split > .ck-dialog-rail{
    width:auto;border-inline-end:0;border-block-end:1px solid var(--border)}
}

/* ── CARD · the comments dock — .ck-card.is-dock ───────────────────────────
   A card pinned to the bottom-right of the viewport. It is a card and not a
   drawer, a dialog or a menu, and the distinction is behavioural: it does not
   dim the page, it does not trap focus, it does not close on an outside click,
   and the reader is expected to keep working with it open. That is a card that
   happens to float.

   Width is min(480px, 100vw - 32px) so it never touches the window edge and
   never needs a horizontal scrollbar (§ 14.3 check 55). --popover, not --card:
   it floats above the page, and § 4 binds the surface to the elevation. */
.ck-card.is-dock{
  position:fixed;inset-block-end:var(--space-base);inset-inline-end:var(--space-base);
  z-index:var(--z-dropdown);
  width:min(480px,calc(100vw - 2 * var(--space-base)));
  max-height:min(560px,calc(100vh - 2 * var(--space-base)));
  display:flex;flex-direction:column;padding:0;overflow:hidden;
  background:var(--popover);color:var(--popover-foreground);
  box-shadow:var(--shadow-overlay)}
.ck-dock-head{
  display:flex;align-items:center;gap:var(--space-sm);flex:none;
  padding:var(--space-sm) var(--space-md);
  border-block-end:1px solid var(--border)}
.ck-dock-title{
  flex:1;min-width:0;
  font:var(--font-weight-semibold) var(--text-base) var(--font-sans);
  color:var(--popover-foreground);
  overflow:hidden;text-overflow:clip;white-space:nowrap}
/* The thread. It scrolls; the head and the composer do not. */
.ck-dock-body{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  display:flex;flex-direction:column;gap:var(--space-md);
  padding:var(--container-inset-sm)}
.ck-dock-msg{display:flex;gap:var(--space-sm);min-width:0}
.ck-dock-msg > .ck-avatar{flex:none}
.ck-dock-msg-main{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-dock-msg-head{
  display:flex;align-items:baseline;gap:var(--space-sm);min-width:0;
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);
  color:var(--popover-foreground)}
.ck-dock-msg-time{
  font:var(--font-weight-normal) var(--text-xs) var(--font-sans);
  color:var(--muted-foreground);flex:none}
.ck-dock-msg-text{
  font:var(--font-weight-normal) var(--text-base) var(--font-sans);
  color:var(--popover-foreground);overflow-wrap:anywhere}
/* Own messages mirror: the bubble tints and the run right-aligns, which is how
   a reader tells their own line from someone else's without reading the name. */
.ck-dock-msg.is-own{flex-direction:row-reverse}
.ck-dock-msg.is-own .ck-dock-msg-main{align-items:flex-end}
.ck-dock-msg.is-own .ck-dock-msg-text{
  background:var(--hover-bg);color:var(--foreground);
  padding:var(--space-xs) var(--space-sm);border-radius:var(--radius-md)}
.ck-dock-foot{
  flex:none;display:flex;align-items:flex-end;gap:var(--space-sm);
  padding:var(--space-sm) var(--space-md);
  border-block-start:1px solid var(--border)}
.ck-dock-foot > .ck-textarea{flex:1;min-width:0;min-height:36px;max-height:120px;resize:none}
.ck-dock.is-collapsed .ck-dock-body,
.ck-card.is-dock.is-collapsed .ck-dock-body,
.ck-card.is-dock.is-collapsed .ck-dock-foot{display:none}

/* ── CARD · the guided tour — .ck-card.is-tour ─────────────────────────────
   The step card of a product tour: a spotlight cut out of a full-page mask,
   with this card anchored beside the thing being pointed at.

   The mask is the one place in the kit where an alpha genuinely cannot be
   pre-composited (§ 2): the cut-out is drawn with an enormous spread shadow,
   so what shows through is the live page, not a knowable surface. The colour
   is still a token and the alpha still rides its own --*-opacity companion. */
.ck-tour-mask{
  position:fixed;inset:0;z-index:var(--z-overlay);pointer-events:none;
  border-radius:var(--radius-md);
  box-shadow:0 0 0 9999px var(--scrim);opacity:var(--scrim-opacity)
  /* pairing-exempt: a cut-out mask, carries no text */}
.ck-card.is-tour{
  position:fixed;z-index:calc(var(--z-overlay) + 1);
  width:min(320px,calc(100vw - 2 * var(--overlay-margin)));
  display:flex;flex-direction:column;gap:var(--space-sm);
  background:var(--popover);color:var(--popover-foreground);
  box-shadow:var(--shadow-overlay)}
.ck-tour-step{
  font:var(--font-weight-semibold) var(--text-xs) var(--font-sans);
  color:var(--muted-foreground);text-transform:uppercase;letter-spacing:.04em}
.ck-tour-foot{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-sm);margin-block-start:var(--space-xs)}
.ck-tour-dots{display:inline-flex;align-items:center;gap:var(--space-xs)}
.ck-tour-dot{
  width:6px;height:6px;border-radius:var(--radius-full);
  background:var(--input)/* pairing-exempt: a position marker, carries no text */}
.ck-tour-dot.is-current{background:var(--primary)/* pairing-exempt: a position marker, carries no text */}

/* ── CARD · the preview card — .ck-card.is-media ───────────────────────────
   A document preview: a type-tinted header bleeding to the card's own edge, a
   fixed 320x480 body, and a scroller inside it.

   .ck-card is a padded container, and padding is exactly what a bleeding
   header cannot have — so the variant moves the padding OFF the card and onto
   the body. Taking the class and then overriding its padding per consumer is
   how the two drifted apart before; one variant settles it. */
.ck-card.is-media{
  padding:0;overflow:hidden;
  display:flex;flex-direction:column;
  width:320px;max-width:100%}
.ck-media-head{
  flex:none;display:flex;align-items:center;gap:var(--space-sm);
  padding:var(--space-sm) var(--space-md);
  background:var(--muted);color:var(--muted-foreground);
  border-block-end:1px solid var(--border);
  font:var(--font-weight-semibold) var(--text-md) var(--font-sans)}
/* The header takes the document type's own tone. One map, the semantic one
   (§ 14.3 check 46) — a type is not a status, so these are the palette tones. */
.ck-card.is-media.type-invoice  > .ck-media-head{background:var(--info-bg);color:var(--info-soft-foreground)}
.ck-card.is-media.type-po       > .ck-media-head{background:var(--violet-bg);color:var(--violet-soft-foreground)}
.ck-card.is-media.type-receipt  > .ck-media-head{background:var(--success-bg);color:var(--success-soft-foreground)}
.ck-media-body{
  flex:1;min-height:0;height:480px;max-height:480px;overflow-y:auto;
  padding:var(--container-inset-sm);background:var(--background);color:var(--foreground)}
.ck-media-foot{
  flex:none;display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-sm);padding:var(--space-sm) var(--space-md);
  border-block-start:1px solid var(--border)}

/* ── EVENT TIMELINE — .ck-timeline (new component) ─────────────────────────
   A vertical rail with one colour-coded dot per event, each carrying an actor,
   a change and a timestamp. The audit trail is built on it, and nothing in the
   kit expressed chronology before — a list of rows says what happened, not the
   order or the gaps between.

   It is a real <ol>, because the order IS the content: with stylesheets off,
   or to a screen reader, the sequence has to survive.

   The rail is drawn by the ITEM, not by the list: a pseudo-element on each row
   running its full height, so the line ends exactly at the last dot instead of
   overshooting past it the way a border on the container would.

   Spec: references/components/timeline.md */
.ck-timeline{
  display:flex;flex-direction:column;
  list-style:none;margin:0;padding:0;min-width:0}
/* NO PADDING ON THE LIST ITSELF — a component sizes its content, the container
   sizes the component. The gutter belongs to whatever holds the timeline, which
   is why adopting it needed 13 local rules deleted rather than added. What the
   list DOES own is the rhythm between its own events, and every one of those
   values is on the kit's scale. */
.ck-timeline-item{
  position:relative;display:flex;gap:var(--space-sm);
  padding-block-end:var(--space-md);padding-inline-start:var(--space-lg);
  min-width:0}
.ck-timeline-item:last-child{padding-block-end:0}
/* The rail. Stops at the last dot rather than running past it. */
.ck-timeline{--ck-tl-dot:10px;--ck-tl-ring:3px}
.ck-timeline.size-sm{--ck-tl-dot:8px}
/* The rail sits under the centre of the dot, derived rather than guessed. */
.ck-timeline-item::before{
  content:'';position:absolute;inset-block:0;
  inset-inline-start:calc(var(--ck-tl-dot) / 2);
  width:1px;background:var(--border)
  /* pairing-exempt: a rule, carries no text */}
.ck-timeline-item:last-child::before{block-size:0}
/* The dot sits ON the rail, so it needs the page ground behind it to break the
   line cleanly rather than sitting on top of a stripe. */
.ck-timeline-dot{
  position:absolute;inset-block-start:var(--space-xs);inset-inline-start:0;
  width:var(--ck-tl-dot);height:var(--ck-tl-dot);border-radius:var(--radius-full);
  box-shadow:0 0 0 var(--ck-tl-ring) var(--background);
  background:var(--muted-foreground)
  /* pairing-exempt: a status marker, carries no text — 4.68:1 clears the 3:1
     non-text floor on --background */}
.ck-timeline-item.is-success > .ck-timeline-dot{background:var(--success)/* pairing-exempt: a status dot, carries no text; the 3px --background ring separates it from the rail */}
.ck-timeline-item.is-error   > .ck-timeline-dot{background:var(--destructive)/* pairing-exempt: a status dot, carries no text; the 3px --background ring separates it from the rail */}
.ck-timeline-item.is-warning > .ck-timeline-dot{background:var(--warning)/* pairing-exempt: a status dot, carries no text; the 3px --background ring separates it from the rail */}
.ck-timeline-item.is-info    > .ck-timeline-dot{background:var(--info)/* pairing-exempt: a status dot, carries no text; the 3px --background ring separates it from the rail */}
.ck-timeline-main{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.ck-timeline-title{
  font:var(--font-weight-medium) var(--text-base) var(--font-sans);
  color:var(--foreground);min-width:0}
/* Actor and timestamp are one muted line: they answer "who and when", which is
   a single question, and two lines of small print would outweigh the event. */
.ck-timeline-meta{
  display:flex;align-items:baseline;gap:var(--space-xs);flex-wrap:wrap;
  font:var(--font-weight-normal) var(--text-xs) var(--font-sans);
  color:var(--muted-foreground);min-width:0}
.ck-timeline-time{font-variant-numeric:tabular-nums}
/* A from/to pair. The arrow is text, not a glyph: it sits on the baseline with
   the values and inherits their colour and size for free. */
.ck-timeline-change{
  display:inline-flex;align-items:baseline;gap:var(--space-xs);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);
  color:var(--muted-foreground);min-width:0}
.ck-timeline-change > .ck-timeline-to{color:var(--foreground)}
.ck-timeline.size-sm .ck-timeline-item{padding-block-end:var(--space-sm)}

/* ── TABLE · the child row — .ck-table tr.is-child ─────────────────────────
   A row that belongs to the row above it: a matched PO line under an invoice
   line. Reconciliation needs it and the table had no way to say "this row is
   subordinate to that one".

   The first version of this indented the cell and drew a vertical rail. That
   was wrong: at a table's row height a 20px indent is barely legible, and a
   hairline in the first column competes with the column borders around it.
   What the reconciliation build actually does — and what this now matches — is
   carry the whole group on its own tinted band, and mark each child with a
   turn-down glyph and the reference it came from.

     - the BAND is --canvas: the app's own ground showing through the table, so
       the group reads as one block whatever the column widths are
     - the GLYPH is Lucide corner-down-right, which says "this came from the row
       above" in a way an indent cannot
     - the REFERENCE pill names the source document, because a child with no
       reference is just an unexplained extra row

   Spec: references/components/table.md */
.ck-table tr.is-child > td{
  background:var(--canvas);color:var(--foreground);
  border-block-end:1px solid var(--border)}
/* The band is continuous: only the last child of a group closes it. */
.ck-table tr.is-child:not(.is-last) > td{border-block-end-color:var(--accordion-stroke)}
.ck-table tr.is-child.is-last > td{border-block-end:1px solid var(--border)}
/* The first cell carries the reference, the turn-down glyph and the label, on
   one line. The indent is the glyph's own box, not a padding guess. */
/* THE CELL STAYS A TABLE CELL. It was display:flex, which takes a <td> out of
   the table: its colspan stops spanning (a lead cell across five columns sat
   in one, and every cell after it slid left, the pinned actions included),
   and it stops sharing the row's height, so the row's band and a flag rail
   on its edge broke at that cell. The line is laid out inline instead —
   middle-aligned, one --space-sm between the parts, never wrapping. */
.ck-table tr.is-child > td:first-child{
  padding-inline-start:var(--space-md);white-space:nowrap}
.ck-table tr:is(.is-child,.has-children) > td:first-child > *{vertical-align:middle}
/* The group's chevron sits on that line too, ahead of its label: a grid box
   would drop the label onto a line of its own. */
.ck-table tr.has-children > td:first-child > .ck-row-chev{display:inline-grid}
/* A group row starts on its children's line, so its chevron and the next
   depth's first mark share an edge instead of sitting 2px apart. */
.ck-table tr.has-children > td:first-child{padding-inline-start:var(--space-md)}
.ck-table tr:is(.is-child,.has-children) > td:first-child > * + *{margin-inline-start:var(--space-sm)}
.ck-child-ref{
  flex:none;
  padding:2px var(--space-sm);border-radius:var(--radius-full);
  background:var(--accent);color:var(--accent-foreground);
  font:var(--font-weight-medium) var(--text-sm) var(--font-sans);
  font-variant-numeric:tabular-nums}
.ck-child-mark{
  flex:none;display:inline-grid;place-items:center;vertical-align:middle;
  width:var(--icon-size);height:var(--icon-size);
  color:var(--muted-foreground)}
.ck-child-mark svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke);display:block}
/* Deeper levels step by the glyph's own box, so the turn-downs line up in a
   column instead of drifting. */
.ck-table tr.is-child[data-depth="2"] > td:first-child{
  padding-inline-start:calc(var(--space-md) + var(--icon-box))}
.ck-table tr.is-child[data-depth="3"] > td:first-child{
  padding-inline-start:calc(var(--space-md) + var(--icon-box) * 2)}
.ck-table tr.is-child:is(:hover,[data-force~="hover"]) > td{background:var(--hover-bg)}
.ck-table tr.is-child:is(.is-selected,[aria-selected="true"]) > td{
  background:var(--selected-bg);color:var(--foreground)}
/* The parent of a group. Its chevron is .ck-row-chev, which already owns the
   clockwise rotation bound to aria-expanded — nothing is redeclared here. */
.ck-table tr.has-children > td:first-child{
  font-weight:var(--font-weight-medium);white-space:nowrap}
/* A child's pinned actions sit on the child band, not on the page: the pinned
   cell restates its surface (it has to be opaque), so it restates this one. */
.ck-table tbody tr.is-child > td.ck-cell-actions.is-pinned{background:var(--canvas);color:var(--foreground)}
.ck-table tbody tr.is-child:is(:hover,[data-force~="hover"]) > td.ck-cell-actions.is-pinned{background:var(--hover-bg)}
/* A collapsed group's children are hidden by the attribute, so the state lives
   in one place and a screen reader reads the same thing the eye sees. */
.ck-table tr.is-child[hidden]{display:none}

/* ── TABLE · the grouped list — .ck-table.is-grouped ────────────────────────
   Rows gathered under group headings — fields under "Extracted Data", columns
   under "Table · Items" — where the children ARE the content, not a
   subordinate match. Three surfaces, one per role, so none merges into the
   next: the header band stays --muted, each group heading takes the blue
   --accent (deepening to --accent-hover under the cursor, since --hover-bg is
   all but the same blue in light), and
   the children sit on the table's own --card like any ordinary row. The type
   glyph ahead of a name is --muted-foreground, so the name leads.
   The child band (--canvas on .is-child) is right for reconciliation, where a
   child hangs off a parent row; it is wrong here, where it tints the list. */
.ck-table.is-grouped tr.is-child > td{background:var(--card);color:var(--card-foreground)}
/* Check 80: the children of a group are one list, so no rule between them —
   the group heading's band is what separates one group from the next. A line
   under every child made a field list read as a stack of separate tables. */
.ck-table.is-grouped tr.is-child > td{border-top:0;border-block-end:0}
.ck-table.is-grouped tr.has-children > td{background:var(--accent);color:var(--accent-foreground)}
.ck-table.is-grouped tr.is-child:is(:hover,[data-force~="hover"]) > td{background:var(--hover-bg)}
.ck-table.is-grouped tr.has-children:is(:hover,[data-force~="hover"]) > td{background:var(--accent-hover);color:var(--accent-foreground)}
.ck-table.is-grouped tr.is-child:is(.is-selected,[aria-selected="true"]) > td{
  background:var(--selected-bg);color:var(--foreground)}
.ck-table.is-grouped tr.is-child .ck-ico{color:var(--muted-foreground)}
/* A grouped list is one level deep and its chevron trails the row, so every
   row's first cell keeps the table's own 14px start: the header's select-all,
   each group's box and each child's box fall on one vertical line. The
   --space-md start above is for a hierarchy whose chevron leads. */
.ck-table.is-grouped tr:is(.is-child,.has-children) > td:first-child{padding-inline-start:14px}

/* ── PILL · the icon-only badge — .ck-pill.is-icon ─────────────────────────
   A tinted, bordered box carrying one glyph and no text: the tick, the warning
   and the copy marks in a table's pinned action column.

   It is a pill and not a .ck-status, which is a dot BESIDE a label — remove the
   label from a status and nothing is left but the dot. This keeps the pill's
   frame, tone map and sizes, and drops only the text.

   Square, not a lozenge: --radius-md. At --radius-full a single glyph in a
   round box reads as an avatar or a button, and this is neither — it is a badge
   that happens to contain a mark instead of a word.

   A glyph alone is not an accessible name, so aria-label is required and the
   tooltip is the kit's own (§ 14.3 check 18). */
.ck-pill.is-icon,.ck-badge.is-icon{
  padding:0;gap:0;flex:none;
  inline-size:var(--ck-pill-h);min-inline-size:var(--ck-pill-h);
  max-inline-size:var(--ck-pill-h);
  border-radius:var(--radius-md)}
.ck-pill.is-icon > svg,.ck-badge.is-icon > svg{
  width:var(--icon-size);height:var(--icon-size);
  stroke-width:var(--icon-stroke)}
/* The 24px size step lands under the WCAG 2.5.8 floor, so an interactive one
   grows its target without moving the mark — the same ::after the dismiss
   inside a pill uses (§ 13). A decorative badge needs none of this. */
.ck-pill.is-icon.is-interactive{position:relative;cursor:pointer}
.ck-pill.is-icon.is-interactive::after{
  content:'';position:absolute;inset:50% auto auto 50%;
  width:24px;height:24px;transform:translate(-50%,-50%)}

/* ═══ [hidden] IS NOT A SUGGESTION ═══════════════════════════════════════
   `[hidden]{display:none}` lives in the USER-AGENT stylesheet, and every
   author rule beats the UA sheet no matter how specific it is. The kit sets a
   painting `display` on an icon in 23 places, so a glyph a page hides with the
   attribute — the two-state toggle, a swapped tone glyph, a loading swap — is
   un-hidden BY THE KIT and paints anyway. Nothing throws and nothing looks
   wrong in the markup; the element is simply there.

   Found on the Profile Settings header, where both halves of the sections
   toggle drew at once, stacked, one of them carrying `hidden` — `display:block`
   from `.ck-icon-btn svg` outranked the attribute.

   The floor has to clear the most specific of those rules —
   `.ck-search > .ck-search-clear svg`, (0,2,1) — and the kit uses no
   !important, so the attribute is repeated to raise its own specificity.
   `until-found` is excluded: that value means "hidden but findable", and the
   browser reveals it itself when a find-in-page lands inside.
   Last in the file so source order backs the specificity up. Check 76. */
[hidden][hidden][hidden]:not([hidden="until-found" i]){display:none}

/* ─── THE FIELD CLEAR — one behaviour for every clear cross in a field ─────
   .ck-input-clear, .ck-dd-clear, the search box's clear and the legacy
   .file-search-clear, .search-clear and .filter-input-clear. They rest in the default icon colour, --icon-color, and
   turn destructive only on approach — the --destructive-bg tint and the
   destructive glyph on hover, solid --destructive when pressed, the error
   focus ring — the same steps as the overlay close (.is-close). Clearing a
   value is a small undo, so it never stands red at rest; but it does remove
   something, so it says so the moment the pointer arrives. They used to hover
   to the grey --hover-bg and --foreground, the same as any neutral control.
   Stated last so it outranks each clear's own geometry rules. */
.ck-input-clear,.ck-dd-clear,.ck-search > .ck-search-clear,.file-search-clear,.search-clear,.filter-input-clear{color:var(--icon-color)}
.ck-input-clear:is(:hover,[data-force~="hover"]),
.ck-dd-clear:is(:hover,[data-force~="hover"]),
.ck-search > .ck-search-clear:is(:hover,[data-force~="hover"]),
.file-search-clear:is(:hover,[data-force~="hover"]),
.search-clear:is(:hover,[data-force~="hover"]),
.filter-input-clear:is(:hover,[data-force~="hover"]){
  background:var(--destructive-bg);color:var(--destructive-soft-foreground)}
.ck-input-clear:is(:active,[data-force~="active"]),
.ck-dd-clear:is(:active,[data-force~="active"]),
.ck-search > .ck-search-clear:is(:active,[data-force~="active"]),
.file-search-clear:is(:active,[data-force~="active"]),
.search-clear:is(:active,[data-force~="active"]),
.filter-input-clear:is(:active,[data-force~="active"]){
  background:var(--destructive);color:var(--destructive-foreground)}
.ck-input-clear:focus-visible,.ck-dd-clear:focus-visible,
.ck-search > .ck-search-clear:focus-visible,.file-search-clear:focus-visible,
.search-clear:focus-visible,.filter-input-clear:focus-visible{
  outline:0;box-shadow:var(--focus-ring-error)}
/* An error toast or alert already sits on --destructive-bg, so its dismiss's hover
   tint would vanish into it; there the hover lifts to --background instead
   and pressed still goes solid, so the three steps stay distinct. */
.ck-toast.is-error > .ck-icon-btn:is(:hover,[data-force~="hover"]),
.ck-alert.is-error > .ck-icon-btn:is(:hover,[data-force~="hover"]){background:var(--background)}

/* ─── RULE BUILDER — .ck-rule ─────────────────────────────────────────────
   A rule that SETS something, with the conditions it applies under nested
   beneath it as a tree: "Set Invoice Number to 123 … only if A and B". Set
   Values in Folder Settings and step 4 of Create Folder.

   One grid for every row — keyword, field, mode or operator, value, delete —
   so the value inputs and the delete buttons line up down the whole card
   whether a row is the rule or one of its conditions. The keyword column is
   --space-7xl wide; a condition's keyword is indented inside it, not by
   shifting the grid, which is what keeps the columns aligned.

   The tree rail is drawn by each row's keyword cell, not by the container, so
   it ends exactly at the last condition's elbow (the timeline's reasoning).
   It is --panel-stroke: --border is 1.09:1 on a card and draws nothing.

   Add Condition and Set a New Value are the last thing in their stack, on the
   trailing edge — .ck-filter-add-row's rule (drawer.md rule 8).

   Spec: references/components/rule-builder.md */
.ck-rule-list{display:flex;flex-direction:column;gap:var(--canvas-gap);min-width:0}
.ck-rule{display:flex;flex-direction:column;gap:var(--space-sm);min-width:0}
.ck-rule-row{display:grid;
  grid-template-columns:var(--space-7xl) minmax(0,4fr) minmax(0,3fr) minmax(0,4fr) auto;
  column-gap:var(--space-md);align-items:start;min-width:0}
/* The delete is the default 36px icon button, the controls' own height, so it
   sits on their line with no nudge; its column takes its width, which keeps
   the add buttons' trailing edge on the delete column's. */
.ck-rule-kw{position:relative;display:grid;grid-template-rows:36px;align-items:center;justify-items:start;
  align-self:stretch;min-width:0}
/* The cell stretches with its row (a row grows when a field shows its error
   line), so the rail it draws never breaks; the keyword stays centred on the
   first, 36px control line. */
.ck-rule-row.is-cond > .ck-rule-kw{padding-inline-start:var(--space-2xl)}
.ck-rule-row.is-sub{align-items:center}
.ck-rule-row.is-sub > .ck-rule-kw{grid-template-rows:auto}
.ck-rule-sub-main{grid-column:2 / -1;display:flex;align-items:center;justify-content:space-between;gap:var(--space-md);min-width:0}
.ck-rule-sub-title{display:inline-flex;align-items:center;gap:var(--space-xs);
  font:var(--font-weight-medium) var(--text-md) var(--font-sans);color:var(--foreground)}
.ck-rule-count{font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.ck-rule-tree{display:flex;flex-direction:column;gap:var(--space-sm);min-width:0}
/* the rail: under the Set keyword, down through every condition, an elbow
   into each; it stops at the last condition's elbow */
.ck-rule-tree > .ck-rule-row:is(.is-sub,.is-cond) > .ck-rule-kw::before{
  content:'';position:absolute;inset-inline-start:var(--space-base);
  top:calc(var(--space-sm) * -1);bottom:calc(var(--space-sm) * -1);
  border-inline-start:1px solid var(--panel-stroke)}
.ck-rule-tree > .ck-rule-row.is-cond:not(:has(~ .ck-rule-row.is-cond)) > .ck-rule-kw::before{
  bottom:auto;height:calc(var(--space-sm) + 18px)}
.ck-rule-tree > .ck-rule-row.is-cond > .ck-rule-kw::after{
  content:'';position:absolute;inset-inline-start:var(--space-base);top:18px;
  width:calc(var(--space-2xl) - var(--space-base) - var(--space-xs));
  border-top:1px solid var(--panel-stroke)}
/* .ck-rule-row.is-sub.is-break — the conditions heading ON the rail: it starts
   at the stack's leading edge and the rail breaks around it, the line running
   into the text from above and on from below. The count rides in the title,
   "Conditions (2/5)". */
.ck-rule-row.is-sub.is-break > .ck-rule-sub-main{grid-column:1 / -1;grid-row:1;justify-content:flex-start;position:relative;z-index:1}
.ck-rule-row.is-sub.is-break .ck-rule-sub-title{background:var(--card);padding-block:var(--space-xs)}
/* the add row: the stack's last thing, on its trailing edge */
.ck-rule-add{display:flex;justify-content:flex-end;min-width:0}
/* a value cell that holds an error or warning line under its control */
.ck-rule-row > .ck-field{gap:var(--space-xs)}

/* A section heading inside a dropdown list: "Document Fields", then one per
   line-item table. It is a label, not an option — no hover, no pointer. */
.ck-dd-group{padding:var(--space-sm) var(--space-sm) var(--space-xs);
  font:var(--font-weight-semibold) var(--text-md) var(--font-sans);color:var(--muted-foreground)}
.ck-dd-group:not(:first-child){margin-block-start:var(--space-xs);border-block-start:1px solid var(--border)}
/* A collapsible group heading: the whole row is the toggle; the chevron turns
   up while its section is open, as an accordion's does. */
.ck-dd-group.is-toggle{display:flex;align-items:center;justify-content:space-between;gap:var(--space-sm);width:100%;box-sizing:border-box;
  border-inline:0;border-block-end:0;background:transparent;border-radius:var(--radius-sm);cursor:pointer;text-align:start}
.ck-dd-group.is-toggle:first-child{border-block-start:0}
.ck-dd-group.is-toggle:hover{background:var(--hover-bg);color:var(--foreground)}
.ck-dd-group.is-toggle:focus-visible{outline:0;box-shadow:var(--focus-ring)}
.ck-dd-group.is-toggle > svg{width:var(--icon-size);height:var(--icon-size);flex:none;transition:transform var(--duration-chevron) var(--ease-default)}
.ck-dd-group.is-toggle[aria-expanded="true"] > svg{transform:rotate(180deg)}

/* ─── RULE BUILDER, RULES TAB PARTS ─────────────────────────────────────────
   .ck-rule-summary  the sticky count bar over a list of rule accordions; it
                     pins flush to the top of a size-sm panel body.
   .ck-accordion-desc a rule's generated sentence in its header: muted, two
                     lines at most.
   .ck-rule-row.is-stage  When / If / Then: the keyword column, then one wide
                     content column, on the same rail as a condition row.
   .ck-rule-compare  left value / operator / right value, one shared grid;
                     the columns stack before anything scrolls sideways.
   Spec: references/components/rule-builder.md § Rules */
.ck-rule-summary{position:sticky;top:calc(var(--container-inset-sm) * -1);z-index:var(--z-sticky);
  display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-md) var(--space-base);
  padding-block:var(--space-2xs);background:var(--card);color:var(--card-foreground);
  font:var(--font-weight-normal) var(--text-md) var(--font-sans)}
.ck-rule-summary > .ck-progress{inline-size:var(--space-7xl);flex:none}
.ck-rule-summary > .grow{flex:1}
.ck-rule-list > .ck-accordion{scroll-margin-block-start:var(--space-5xl)}
/* the list's gap is the whole gap: no stacked accordion margin on top of it */
.ck-rule-list > .ck-accordion + .ck-accordion{margin-top:0}
.ck-accordion-desc{flex:1;min-width:0;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
  font:var(--font-weight-normal) var(--text-md) var(--font-sans);color:var(--muted-foreground);white-space:normal}
.ck-accordion-trigger > .ck-accordion-label.is-fit{flex:none}
.ck-rule-row.is-stage{grid-template-columns:var(--space-6xl) minmax(0,1fr);column-gap:var(--space-sm)}
/* When / If / Then sit close to their rail: the rail at --space-sm, the
   keyword one --space-xl in, so the stage content starts right after it. */
.ck-rule-tree > .ck-rule-row.is-stage > .ck-rule-kw{padding-inline-start:var(--space-xl)}
.ck-rule-tree > .ck-rule-row.is-stage > .ck-rule-kw::before,
.ck-rule-tree > .ck-rule-row.is-stage > .ck-rule-kw::after{inset-inline-start:var(--space-sm)}
.ck-rule-tree > .ck-rule-row.is-stage > .ck-rule-kw::after{width:calc(var(--space-xl) - var(--space-sm) - var(--space-xs))}
/* a single choice that needs no full row: the When event, the label */
.ck-rule-stage > .is-short{inline-size:250px;max-inline-size:100%}
.ck-rule-stage{display:flex;flex-direction:column;gap:var(--space-sm);min-width:0}
.ck-rule-compare{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--space-md);align-items:start;min-width:0}
/* .is-inline — every dropdown of a comparison on one line: left operation,
   left field, operator, right operation, right field, under one header row;
   the tolerance sits on the next line under the left side. .span-2 takes two
   columns, .span-row the whole line. */
.ck-rule-compare.is-inline{grid-template-columns:repeat(5,minmax(0,1fr));row-gap:var(--space-xs)}
/* a column header inside a comparison takes the grid's columns, not the rule row's */
.ck-rule-compare > .ck-rule-sub-main{grid-column:auto}
.ck-rule-compare > .span-2{grid-column:span 2}
.ck-rule-compare > .span-row{grid-column:1 / -1}
.ck-rule-compare.is-inline > .is-tol{grid-column:1 / span 2;margin-block-start:var(--space-xs)}
/* Every column stacks together, never part-way: the rule's own width decides. */
.ck-rule-tree{container-type:inline-size}
@container (max-width:720px){
  .ck-rule-compare,.ck-rule-compare.is-inline{grid-template-columns:minmax(0,1fr)}
  .ck-rule-compare > :is(.span-2,.span-row,.is-tol){grid-column:auto}}
.ck-rule-join{display:flex;justify-content:flex-start}

/* A button or a badge written as <a> — it navigates — takes the browser's link
   underline and colour unless told otherwise. The link variants keep theirs. */
a:is(.ck-btn,.ck-pill,.ck-badge,.ck-chip):not(.link){text-decoration:none}
a.ck-pill{cursor:pointer}
a.ck-pill:focus-visible{outline:0;box-shadow:var(--focus-ring)}

/* A chevron at the END of an accordion head, after its actions: it turns with
   the trigger's state the same way the chevron inside the trigger does. */
.ck-accordion-head > .ck-accordion-chev{cursor:pointer;transform:rotate(0deg)}
.ck-accordion-head:has(> .ck-accordion-trigger[aria-expanded="true"]) > .ck-accordion-chev{
  transform:rotate(180deg);transition:transform var(--duration-chevron) var(--ease-default)}

/* A head that holds a field rather than a trigger (an always-editable
   section): .grow takes the slack so the row's actions sit on its end edge,
   and the field keeps a readable width. */
.ck-accordion-head > .grow{flex:1}
.ck-accordion-head > .ck-input-wrap{inline-size:360px;max-inline-size:100%;flex:0 1 auto}


/* ── A LINE OF FIELD CONTROLS — .ck-field-line ────────────────────────────
   Two or three controls that share one row of a field — a name input and its
   Reset Name button, a source dropdown and its warning, a search and its
   count. The inputs, dropdowns and searches share the free width; icon
   buttons, pills and icons keep their own. */
.ck-field-line{display:flex;align-items:center;gap:var(--space-sm);min-width:0}
.ck-field-line > :is(.ck-input,.ck-input-wrap,.ck-dd,.ck-search,.ck-toggle-group){flex:1 1 auto;min-width:0}
.ck-field-line > :is(.ck-icon-btn,.ck-pill,.ck-ico,.ck-tip-host){flex:none}

/* ── TEXT FOR SCREEN READERS ONLY — .ck-sr-only ───────────────────────────
   An announcement (a column moved, a filter count) that a sighted reader sees
   happen and a screen reader has to be told. Clipped, not display:none, so it
   stays in the accessibility tree. */
.ck-sr-only{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}
