/*
 * CKN theme — Inno Biz design system, Role A (Operator console).
 *
 * ─────────────────────────────────────────────────────────────────────────────────────────
 * CANONICAL SOURCE: shared/themes/ckn/ — do not edit a copy under a project's themes/ckn/.
 * Every ckn.systems.* project carries a byte-identical copy, placed there by
 * tools/sync-theme.sh. Edit here, run `tools/sync-theme.sh`, and every service moves together.
 * `tools/sync-theme.sh --check` fails if any copy has drifted.
 * ─────────────────────────────────────────────────────────────────────────────────────────
 *
 * This file is framework-free: it carries the tokens, layout and components of
 * `context/design/[DevSpec] Design system.md` and nothing else, and the webfonts it needs are
 * self-hosted beside it, so it reaches no CDN. Bootstrap 5 is vendored alongside it (loaded by
 * ckn.js, toned by ckn-bootstrap-bridge.css) only because the standard `@ckn-technology/ui`
 * components emit Bootstrap class names and `ckn-modal`/`ckn-confirm` need the Bootstrap JS
 * global — none of that leaks in here.
 *
 * Implements the DevSpec for a locked operator surface: true-black nav rail, #f4f4f5 canvas,
 * white panels, ink text, hairlines instead of shadows, tabular figures in every data context,
 * and the functional palette used unchanged. Section references below point at that document.
 */

@import url("./lib/fonts.css");

/* ============================================================ tokens (§2) */

:root {
    /* neutrals — the chassis */
    --ink:   #141414;   /* text, structure, every action button */
    --g4:    #5a5a5e;   /* secondary text */
    --g3:    #a6a6ab;   /* tertiary text, placeholders */
    --paper: #ffffff;   /* card / panel surface */
    --bg:    #f4f4f5;   /* canvas behind cards (operator) */
    --bd:    rgba(0, 0, 0, .08);   /* default hairline */
    --bd2:   rgba(0, 0, 0, .13);   /* emphasised hairline */
    --rail:  #000000;              /* operator nav rail — always true black */

    /* functional — hard-coded, never themed, never replaced by an accent */
    --ok:   #157F47;
    --warn: #DB6704;
    --err:  #DC2B2B;

    /*
     * The same two functional colours, lifted for the true-black rail.
     *
     * §4 describes rail content as white at reduced opacity and does not contemplate a
     * status colour there, so this is the one place the design system leaves undefined.
     * `--err` on #000 measures 4.42:1, below the 4.5:1 that 11px text needs, so a status
     * word on the rail has to be lifted to stay readable.
     *
     * It is lifted *from the token* rather than written as a new hex, which is the whole
     * point: these cannot drift into a different hue, and changing the palette changes
     * them. Measured: #eea379 at 10.15:1 and #f2887d at 8.59:1 on black, both within 6° of
     * their source hue.
     *
     * The unlifted values below are the real fallback. A custom property accepts any token
     * sequence at parse time, so a second declaration always wins whether or not the
     * browser understands it — the plain value has to be guarded by @supports instead, or
     * a browser without color-mix() would get an invalid colour and inherit white.
     */
    --warn-on-rail: var(--warn);
    --err-on-rail:  var(--err);

    /*
     * Unit accent (DevSpec §1-2) — SIGNAL ONLY: the active nav item, data-viz, one highlight
     * per view, roughly 5-10% of a screen. Never a button fill, never a status badge, never a
     * large surface — those stay --ink / functional regardless of unit (§3 "Never").
     *
     * All three live here always; which one applies to a given page is a runtime choice, not
     * a build-time one — see --accent below.
     */
    --idb: #1D6FFF;  /* Inno Digital Biz — Build */
    --isb: #F5B800;  /* Inno Supply Chain Biz — Run */
    --imb: #8D24AA;  /* Inno Marketing Biz — Grow (deep/gradient variant: #6F1687) */

    /*
     * The one accent a component actually reads. Never `var(--idb)` etc. directly — that
     * would hard-code a unit into a shared component. A page picks its unit by setting
     * `data-unit="idb" | "isb" | "imb"` on <body> (`page.dataAttributes.unit`, the same
     * mechanism the nav rail's data-ckn-nav-* filtering already uses — see theme.ckn.ejs).
     * No data-unit at all falls back to IDB, since an operator console with no configured
     * unit is closer to platform tooling than to any one business line.
     */
    --accent: var(--idb);

    /* type */
    --sans:  'Manrope', 'IBM Plex Sans Thai', -apple-system, BlinkMacSystemFont, sans-serif;
    --serif: 'Cormorant Garamond', Georgia, serif;
    --serif-scale: 1.2;

    /* radius — operator surfaces use the tighter end; there is no --r-xl here (§4) */
    --r-xs: 6px;
    --r-sm: 8px;
    --r-md: 10px;
    --r-lg: 13px;
    --r-full: 999px;

    /* Height of the sticky top bar. The rail's brand block and the content column's bar
       both read this, so the two can never drift apart. */
    --ckn-header-h: 3.25rem;

    /* Rail width. A token because the responsive rules below narrow it, and the drawer
       needs one value to slide by; two hard-coded widths would drift apart. */
    --ckn-rail-w: 240px;
}

@supports (color: color-mix(in oklab, red 50%, white)) {
    :root {
        --warn-on-rail: color-mix(in oklab, var(--warn) 62%, #ffffff);
        --err-on-rail:  color-mix(in oklab, var(--err) 62%, #ffffff);
    }
}

/*
 * Unit selection. `[data-unit="idb"]` is written out even though it matches the --root
 * default, so the mapping from attribute value to token is explicit here rather than
 * relying on a fallback nobody can see by reading this file alone.
 */
[data-unit="idb"] { --accent: var(--idb); }
[data-unit="isb"] { --accent: var(--isb); }
[data-unit="imb"] { --accent: var(--imb); }

/* ============================================================ base / reset */

*, *::before, *::after { box-sizing: border-box; }

/*
 * The shell is a frame, not part of the scroll.
 *
 * The document itself never scrolls: it is exactly the height of the viewport, and the
 * only thing that scrolls is the content region. Pinning the rail and the bar with
 * `position: sticky` over a scrolling document looks the same until a trackpad is used —
 * a momentum fling that runs past the end rubber-bands the whole document, and sticky
 * elements ride along, so the frame visibly moves. A frame that cannot scroll cannot be
 * overscrolled.
 *
 * `overscroll-behavior: none` stops the bounce being handed up here from a nested
 * scroller as well.
 */
html {
    -webkit-text-size-adjust: 100%;
    height: 100%;
    overscroll-behavior: none;
}

body.ckn-body {
    margin: 0;
    height: 100%;
    overflow: hidden;
    background: var(--bg);
    color: var(--ink);
    font-family: var(--sans);
    font-size: .875rem;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }

button { font: inherit; color: inherit; margin: 0; }

code { font-family: inherit; font-size: .95em; color: var(--g4); }

svg { display: block; }

[hidden] { display: none !important; }

/*
 * Class-toggle visibility for a subtree that must stay mounted (never used for
 * ordinarily-hidden things — see .ckn-group-header etc.). `:if` unmounts and remounts its
 * subtree; a `:for` loop nested inside one that toggles after the initial mount hit a real
 * bug in @ckn-technology/ui 5.5.3 (confirmed live in Overview's tab panels — a keyed list
 * duplicated its rows on first reveal). The HTML `hidden` attribute cannot substitute for
 * `:if` here either: the renderer's attribute binding has no boolean special-case for it
 * (unlike checked/disabled/readonly/required), so a bound `hidden="{{expr}}"` would literally
 * write the string "false" — which still hides the element, since a boolean HTML attribute
 * hides on *presence*, any value. A plain class binding has neither problem.
 */
.ckn-hidden { display: none; }

/*
 * Tabular figures in every data context (§2). The design system specifies no separate
 * monospace face — Manrope's tabular figures do that job — so .ckn-mono only fixes the
 * digit width and keeps the sans stack.
 */
.ckn-mono,
.ckn-num,
.ckn-table td,
.ckn-stat-value {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.ckn-mono { letter-spacing: -.005em; }

/* Highlight face: italic Cormorant, scaled because its x-height is much smaller than
   Manrope's. At most one per surface. */
.hl {
    font-family: var(--serif);
    font-style: italic;
    font-size: calc(1em * var(--serif-scale));
}

/* ============================================================ layout (§4)
 *
 * Two columns: the operator nav rail on the left, the selected content on the right.
 * The rail is fixed; the content column scrolls beneath a sticky bar.
 */

.ckn-layout {
    display: flex;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
}

/*
 * The rail is always true black, on every unit and every deployment. The design system is
 * explicit that this never takes a unit or client colour: a reader must never mistake a
 * colour here for something that carries meaning.
 */
.ckn-sidebar {
    flex: 0 0 var(--ckn-rail-w);
    width: var(--ckn-rail-w);
    min-width: 0;           /* lets the collapsed state (below) actually reach 0 — a flex
                                item's default min-width is its content's, same reason
                                .ckn-content sets it */
    position: relative;     /* anchors .ckn-nav-resize-handle to this box's own edge */
    background: var(--rail);
    color: #ffffff;
    display: flex;
    flex-direction: column;
    height: 100%;
    /*
     * A safety net, not the normal path: `.ckn-nav` alone absorbs a long menu in the
     * ordinary case (its own overflow-y below), so this scrollbar never appears there. It
     * exists for the fixed chrome around it — brand, identity block, Tenant switch, all
     * `flex: 0 0 auto` and therefore unable to shrink — on a viewport short enough (a
     * heavy zoom level, a small embedded frame) that even that alone overflows. Without it
     * the excess is clipped by `.ckn-layout`'s `overflow: hidden` with nothing anywhere in
     * the rail able to reach it — sign-out included.
     */
    overflow-y: auto;
    visibility: visible;
    transition: flex-basis .16s ease, width .16s ease, visibility 0s;
}

/*
 * Collapsing the rail — desktop only (below the drawer breakpoint the rail already has its
 * own hide/show mechanism, and the two must not fight over the same visual space). Shrinks
 * to 0 rather than being removed from the layout, so `.ckn-content` widens to fill the gap
 * through the same flex arithmetic that sizes it every other time, not a special case.
 *
 * `visibility` is delayed on the way in (this rule) and immediate on the way out (the
 * `[data-ckn-rail-collapsed="false"]`-equivalent, which is simply `.ckn-sidebar`'s own base
 * rule above) — identical trick to the drawer's transform/visibility pair below, and for
 * the same reason: content stays visible while it shrinks away, and reappears the instant
 * it starts growing back rather than after.
 */
@media (min-width: 768px) {
    [data-ckn-rail-collapsed="true"] .ckn-sidebar {
        flex-basis: 0;
        width: 0;
        overflow: hidden;
        visibility: hidden;
        transition: flex-basis .16s ease, width .16s ease, visibility 0s linear .16s;
    }

    /* The drag is felt at 1:1 with the pointer, not chasing a width transition. */
    .ckn-nav-resizing .ckn-sidebar { transition: none; }
}

body.ckn-nav-resizing { cursor: col-resize; user-select: none; }

/*
 * The drag handle. Absolutely positioned on the sidebar's own right edge (the `position:
 * relative` above is for exactly this) rather than a layout sibling, so it takes no space
 * of its own and cannot shift the content column by a pixel while idle.
 *
 * Built by the theme script, same as `.ckn-nav-toggle` and `.ckn-nav-search` — a handle
 * that cannot resize anything is worse than no handle, so it only exists where the script
 * that drives it ran. Hidden below the drawer breakpoint and while collapsed: there is
 * nothing to drag in either state.
 */
.ckn-nav-resize-handle {
    position: absolute;
    top: 0;
    right: -3px;
    width: 6px;
    height: 100%;
    z-index: 5;
    cursor: col-resize;
    touch-action: none;
    border-radius: var(--r-full);
}

.ckn-nav-resize-handle:hover,
.ckn-nav-resize-handle:focus-visible {
    background: rgba(255, 255, 255, .18);
    outline: none;
}

@media (max-width: 767.98px) {
    .ckn-nav-resize-handle { display: none; }
}

[data-ckn-rail-collapsed="true"] .ckn-nav-resize-handle { display: none; }

/*
 * The brand block is the rail's share of the top bar, so it takes the same height as the
 * content column's bar. Both read --ckn-header-h and both carry a 1px bottom border,
 * which makes the two borders meet as one line across the page instead of stepping where
 * the columns join.
 */
.ckn-brand {
    flex: 0 0 auto;
    height: var(--ckn-header-h);
    padding: 0 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
    display: flex;
    align-items: center;
    gap: .6rem;
}

/*
 * The supplied mark is a coloured gradient on transparent, so it sits directly on the
 * true-black rail with no tile behind it — the colour itself reads against black with no
 * extra contrast treatment needed.
 */
.ckn-brand-logo {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    object-fit: contain;
}

.ckn-brand-text { min-width: 0; }

.ckn-brand-name {
    font-size: .875rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: #ffffff;
    line-height: 1.2;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ckn-brand-sub {
    font-size: .6875rem;
    line-height: 1.2;
    color: rgba(255, 255, 255, .6);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ckn-brand code { color: rgba(255, 255, 255, .75); }

/*
 * Only the menu scrolls. `min-height: 0` overrides the flex default of `auto`, which would
 * otherwise refuse to shrink below the menu's full content height and defeat overflow — the
 * same reason `.ckn-content-body` needs it below. That confines scrolling to this element
 * instead of the whole rail, which is what keeps the identity block below (`.ckn-user`) and
 * the Tenant switch pinned at the bottom of the rail rather than scrolling away with a long
 * menu — the same "outside the scrolling region" approach `.ckn-header` uses, not
 * `position: sticky`.
 */
.ckn-nav {
    padding: .55rem .5rem;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.ckn-nav-section {
    font-size: .625rem;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .4);
    padding: .6rem .55rem .35rem;
}

/*
 * Rail items: white at reduced opacity when inactive, full white when active (§4).
 *
 * These are real links now that each page has its own URL, so the anchor defaults have to
 * be cleared — an underlined, blue-ish rail item would be the browser's styling showing
 * through, not the design system's.
 */
.ckn-nav-item {
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    text-align: left;
    border: 0;
    background: transparent;
    color: rgba(255, 255, 255, .6);
    border-radius: var(--r-sm);
    padding: .45rem .55rem;
    font-size: .8125rem;
    line-height: 1.3;
    cursor: pointer;
}

.ckn-nav-item:hover { background: rgba(255, 255, 255, .07); color: #ffffff; }

/*
 * The unit accent's one sanctioned spot on the rail (DevSpec §1/§4): a signal, not a fill —
 * the background stays the same white-tint every unit uses, and only a slim edge picks up
 * --accent. A tinted background here would read as "the accent recoloured the rail", which
 * §3 rules out.
 */
.ckn-nav-item.active {
    background: rgba(255, 255, 255, .12);
    color: #ffffff;
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--accent);
}

/* A count on a rail item, so a problem is visible without opening the view. */
.ckn-nav-count {
    margin-left: auto;
    font-size: .6875rem;
    font-variant-numeric: tabular-nums;
    color: rgba(255, 255, 255, .45);
}
.ckn-nav-item.active .ckn-nav-count { color: rgba(255, 255, 255, .7); }
.ckn-nav-count.warn { color: var(--warn-on-rail); font-weight: 600; }
.ckn-nav-count.bad  { color: var(--err-on-rail);  font-weight: 600; }

.ckn-content {
    flex: 1 1 auto;
    min-width: 0;          /* lets wide tables scroll instead of stretching the layout */
    min-height: 0;         /* without this the body region refuses to shrink and scroll */
    display: flex;
    flex-direction: column;
}

/*
 * The content column's scrolling region (the rail's own menu is the other one, `.ckn-nav`
 * above). `overscroll-behavior: contain` keeps a fling that reaches the end from being
 * handed to the document, which is what makes the frame stay still under a trackpad rather
 * than bouncing with the content.
 */
.ckn-content-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 1.25rem 1.5rem 3rem;
}

/* ============================================================ responsive
 *
 * The design system defines no breakpoints, so these are the theme's own and are declared
 * in one place rather than scattered through the file. Two widths matter for an operator
 * console: the point where a 240px rail stops being affordable beside a data table, and
 * the point where it cannot stay in the layout at all.
 *
 *   ≤ 1023.98px   rail narrows, padding tightens
 *   ≤  767.98px   rail leaves the flow and becomes a drawer
 *   ≤  519.98px   stat tiles tighten so two still fit across a small phone
 *
 * These are max-width only, so no two queries can both apply at a fractional width and
 * there is no min/max pairing to keep in step.
 */

@media (max-width: 1023.98px) {
    :root { --ckn-rail-w: 200px; }
    .ckn-content-body { padding: 1rem 1rem 2.5rem; }
    .ckn-header { gap: .75rem; }
}

@media (max-width: 767.98px) {
    .ckn-content-body { padding: 1rem 1rem 2.5rem; }
    .ckn-header { gap: .6rem; padding: 0 .75rem; }

    /*
     * Without the theme script the rail stacks above the content, and the frame has to
     * give way: a fixed-height frame would clip a rail that no longer fits inside it and
     * put the menu out of reach. So in the unenhanced case the document scrolls normally,
     * which is the one place the shell is allowed to move.
     *
     * Navigation is never removed by CSS alone — a console whose menu is gone is not a
     * responsive console, it is a broken one, and that must not be the no-JavaScript
     * outcome.
     */
    html, body.ckn-body { height: auto; overflow: visible; }

    .ckn-layout {
        display: block;
        height: auto;
        overflow: visible;
    }

    .ckn-content-body { overflow: visible; }

    .ckn-sidebar {
        position: static;
        flex: none;
        width: 100%;
        height: auto;
        overflow-y: visible;
        border-bottom: 1px solid rgba(255, 255, 255, .12);
    }

    /*
     * Enhanced: the rail is an overlay again, so it occupies no space in the layout and
     * the frame comes back — which is what keeps the bar still while the list is flung.
     */
    html[data-ckn-nav="drawer"],
    html[data-ckn-nav="drawer"] body.ckn-body { height: 100%; overflow: hidden; }

    [data-ckn-nav="drawer"] .ckn-layout {
        height: 100dvh;
        overflow: hidden;
    }

    [data-ckn-nav="drawer"] .ckn-content { height: 100%; }

    [data-ckn-nav="drawer"] .ckn-content-body {
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    /*
     * Enhanced. The script sets data-ckn-nav="drawer" once it has added a control that can
     * open the rail again, and only then may the rail leave the flow.
     *
     * `visibility` is toggled alongside the transform so a closed drawer is not merely
     * off-screen: an off-screen element still takes focus, and tabbing into invisible
     * navigation is worse than having none.
     */
    [data-ckn-nav="drawer"] .ckn-sidebar {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 60;
        width: min(17rem, 82vw);
        height: 100dvh;
        border-bottom: 0;
        transform: translateX(-100%);
        visibility: hidden;
        transition: transform .2s ease, visibility 0s linear .2s;
        /* A drawer is a floating element, which is the one case §3 allows a shadow. */
        box-shadow: 0 0 2rem rgba(0, 0, 0, .45);
    }

    [data-ckn-nav="drawer"][data-ckn-nav-open="true"] .ckn-sidebar {
        transform: none;
        visibility: visible;
        transition: transform .2s ease, visibility 0s;
    }

    [data-ckn-nav="drawer"] .ckn-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 50;
        background: rgba(0, 0, 0, .45);
        opacity: 0;
        pointer-events: none;
        transition: opacity .2s ease;
    }

    [data-ckn-nav="drawer"][data-ckn-nav-open="true"] .ckn-scrim {
        opacity: 1;
        pointer-events: auto;
    }

    /*
     * The scroll lock lives here rather than as an inline style set by the script, so it
     * cannot outlive the breakpoint. Widening the window while the drawer is open would
     * otherwise leave the page unscrollable with nothing overlaying it — and an inline
     * style is exactly what a media query cannot take back. Above this width the rule does
     * not exist, so that state is not representable.
     *
     * It targets the content region rather than the body because that is what scrolls now.
     */
    [data-ckn-nav="drawer"][data-ckn-nav-open="true"] .ckn-content-body { overflow: hidden; }
}

/* Two tiles still fit across a small phone; one very wide tile wastes the fold. */
@media (max-width: 519.98px) {
    .ckn-grid { grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr)); gap: .5rem; }
    .ckn-stat { padding: .7rem .8rem; }
    .ckn-stat-value { font-size: 1.35rem; }
}

/*
 * Touch. The design system asks operator surfaces to be dense, which is right for a mouse
 * and wrong for a finger, so the density relaxes only where the pointer is actually
 * coarse rather than at a width guessed to mean "phone".
 */
@media (pointer: coarse) {
    .ckn-nav-item { padding-block: .7rem; }
    .ckn-icon-btn,
    .ckn-nav-toggle { width: 2.5rem; height: 2.5rem; }
    .ckn-table th,
    .ckn-table td { padding-block: .7rem; }
}

@media (prefers-reduced-motion: reduce) {
    .ckn-sidebar,
    .ckn-scrim { transition: none; }
}

/*
 * The top bar is part of the frame rather than something pinned over the content. It is a
 * non-shrinking row of the content column, so it holds its place because it is outside
 * the scrolling region — not because `position: sticky` keeps putting it back.
 *
 * The table header is deliberately NOT sticky: a panel needs `overflow: hidden` to clip
 * its rounded corners and a wide table needs `overflow-x: auto`, and either one becomes
 * the scrollport for `position: sticky` — which pushes a sticky `thead` down by its own
 * offset inside a box that never scrolls vertically, putting the first row above the
 * header.
 */
.ckn-header {
    flex: 0 0 auto;
    z-index: 20;
    height: var(--ckn-header-h);
    padding: 0 1rem;
    display: flex;
    align-items: center;
    gap: 1rem;
    background: var(--paper);
    border-bottom: 1px solid var(--bd);
}

.ckn-header-title {
    font-size: .9375rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ink);
    min-width: 0;              /* a long view name truncates instead of pushing the bar */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*
 * The rail's one visibility control, at every width — below the drawer breakpoint it opens
 * and closes the overlay (`ckn.js`'s `setOpen`); at or above it, it collapses and expands
 * the in-flow rail instead (`setCollapsed`). Same element, same position in the header,
 * different mechanism underneath, because "make the rail go away" is one operator intent
 * regardless of window width — two separate controls that pop in and out at the breakpoint
 * would read as two different features.
 *
 * It is created by the theme script, not by the page, so every surface using this theme
 * gets it without changing its markup — and a surface with no rail never gets one. It sits
 * on the white bar rather than the black rail, so it takes ink rather than white.
 */
.ckn-nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    margin-left: -.25rem;
    padding: 0;
    border: 0;
    background: transparent;
    border-radius: var(--r-sm);
    color: var(--ink);
    cursor: pointer;
}

.ckn-nav-toggle:hover { background: rgba(0, 0, 0, .06); }

/* Hidden until the rail is a drawer; the responsive section makes it a real overlay. */
.ckn-scrim { display: none; }

/* ============================================================ icons (§3)
 *
 * Icons inherit colour from their container, so one symbol works on the black rail and on
 * a white panel with no variant.
 */

.ckn-icon { width: 1.125rem; height: 1.125rem; flex: 0 0 auto; color: inherit; }
.ckn-icon-lg { width: 1.375rem; height: 1.375rem; }

/* An icon-only control on the rail: quiet until pointed at, never a filled button. */
.ckn-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 0;
    background: transparent;
    border-radius: var(--r-sm);
    color: rgba(255, 255, 255, .6);
    cursor: pointer;
}

.ckn-icon-btn:hover:not(:disabled) { background: rgba(255, 255, 255, .1); color: #ffffff; }

/*
 * A control that exists but cannot act yet. It is shown rather than hidden so the
 * surface is complete and the gap is visible, and it is disabled rather than inert-
 * looking so it cannot be mistaken for something that worked.
 */
.ckn-icon-btn:disabled {
    color: rgba(255, 255, 255, .25);
    cursor: not-allowed;
}

/* ==================================================== rail identity block */

.ckn-user {
    flex: 0 0 auto;         /* part of the rail's fixed footer, below the scrolling menu */
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .6rem .6rem .6rem .75rem;
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.ckn-avatar {
    flex: 0 0 auto;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: var(--r-full);
    background: rgba(255, 255, 255, .12);
    color: rgba(255, 255, 255, .85);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.ckn-user-text { min-width: 0; flex: 1 1 auto; }

.ckn-user-name,
.ckn-user-role {
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.2;
}

.ckn-user-name { font-size: .78rem; font-weight: 600; color: #ffffff; }
.ckn-user-role { font-size: .6875rem; color: rgba(255, 255, 255, .55); }

/* An unauthenticated session is a standing condition, not a neutral default. */
.ckn-user-role.unauth { color: var(--warn-on-rail); }

/* ============================================================ stat tiles
 *
 * Hairlines rather than shadows (§3), quiet by default so a changed number draws the eye.
 * The grid fits as many tiles per row as the column allows, with no breakpoint list.
 */

.ckn-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    gap: .75rem;
}

.ckn-stat {
    background: var(--paper);
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    padding: .85rem 1rem;
}

/*
 * The tile the reader is standing in — the current bucket of an ageing band, the open period.
 * Marked by promoting its hairline to ink rather than by tinting the tile, so a row of tiles
 * keeps one shape and the emphasis reads as position, not as a status. §4: the neutral
 * emphasis on an operator surface is black.
 */
.ckn-stat-current {
    border-color: var(--ink);
    box-shadow: inset 0 0 0 1px var(--ink);
}

.ckn-stat-label {
    font-size: .625rem;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--g4);
    margin-bottom: .35rem;
}

.ckn-stat-value {
    font-size: 1.6rem;
    line-height: 1.15;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--ink);
}

.ckn-stat-value small { font-size: .85rem; font-weight: 500; color: var(--g4); }

/* Where a tile carries a word rather than a number. */
.ckn-stat-text { font-size: .9375rem; font-weight: 500; letter-spacing: 0; }

/* A stat tile that is also a button (Overview's per-group cards, linking into that
   group's tab) — same shape as .ckn-stat, but focusable/clickable and left-aligned since
   <button> centers text by default. */
.ckn-group-card {
    text-align: left;
    cursor: pointer;
    border-radius: var(--r-lg);
    padding: .85rem 1rem;
}

.ckn-group-card:hover { border-color: var(--bd2); background: rgba(0, 0, 0, .015); }

/* ============================================================ tabs
 *
 * Underline style, per §3: hairlines rather than shadows or filled pills. No unit accent
 * is defined for this console (documents/ui-ci's accent system belongs to the wider Inno
 * Biz product family), so the active tab takes --ink, the same rule as a primary button.
 */
/*
 * Wraps rather than scrolls, matching `.ckn-account-tabs` below.
 *
 * `overflow-x: auto` reads as the safe choice but hides content on exactly the screens that
 * can least afford it: the setup and reference pages carry nine to fifteen tabs, and on a
 * 1280px viewport the last of them sits past the edge, cut mid-word. macOS draws no resting
 * scrollbar, so nothing on screen says the row continues — a reader who never drags sideways
 * simply does not learn those tabs exist. Wrapping costs one extra line on those pages and
 * nothing at all on the four- and five-tab pages, which is most of them: `flex-wrap` only
 * takes effect once the row would have overflowed, so the common case is untouched.
 *
 * The row gap stays small because `.ckn-tab` already carries .65rem of vertical padding; a
 * full 1.5rem between wrapped rows would read as two separate controls rather than one bar.
 */
.ckn-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .1rem 1.5rem;
    border-bottom: 1px solid var(--bd);
    margin-bottom: 1.25rem;
}

.ckn-tab {
    flex: 0 0 auto;
    background: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    padding: .65rem .05rem;
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    color: var(--g4);
    white-space: nowrap;
    cursor: pointer;
}

.ckn-tab:hover { color: var(--ink); }
.ckn-tab:disabled { color: var(--g3); cursor: default; }

.ckn-tab.active { color: var(--ink); border-bottom-color: var(--ink); }

/* ============================================================ panel + table */

.ckn-panel {
    background: var(--paper);
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    overflow: hidden;
}

/* A panel's own internal header — grouping a table under a label (Overview's per-project
   sections), distinct from .ckn-header, which is the page's top bar. */
.ckn-group-header {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--bd);
}

.ckn-group-empty { padding: 1rem; }

/*
 * A wide table scrolls inside its panel rather than stretching the layout.
 *
 * `overscroll-behavior-x: contain` matters more than it looks: a two-finger horizontal
 * swipe that reaches the end of the table would otherwise chain outwards, and on macOS
 * that gesture is the browser's back navigation — so scrolling a table sideways could
 * leave the page entirely.
 */
.ckn-table-scroll {
    overflow-x: auto;
    overscroll-behavior-x: contain;
}

.ckn-table {
    width: 100%;
    border-collapse: collapse;
    background: var(--paper);
    color: var(--ink);
    font-size: .8125rem;
}

.ckn-table th,
.ckn-table td {
    padding: .55rem .85rem;
    text-align: left;
    vertical-align: middle;
    border-bottom: 1px solid var(--bd);
}

/* Header row on white, not grey — the design system rules out grey-on-grey tables (§3). */
.ckn-table thead th {
    font-size: .625rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--g4);
    background: var(--paper);
    border-bottom: 1px solid var(--bd2);
    white-space: nowrap;
}

.ckn-table tbody tr:last-child td { border-bottom: 0; }
.ckn-table tbody tr:hover td { background: rgba(0, 0, 0, .02); }

.ckn-table th.ckn-right,
.ckn-table td.ckn-right { text-align: right; }

.ckn-table th.ckn-center,
.ckn-table td.ckn-center { text-align: center; }

/*
 * Inside a padded card the table hangs its own cell padding off the card's, so the first
 * column's text landed ~13px right of the tab row and the notice above it — three different
 * left edges down one card. Dropping the outer cells' side padding lets the column text sit on
 * the card's own padding line, which is the edge the eye is already following. The row tint
 * and hover still run the full width because they are painted by the cell background, not by
 * its padding. A table in an unpadded `.ckn-panel` keeps its padding: there it IS the inset.
 */
.ckn-panel-pad .ckn-table th:first-child,
.ckn-panel-pad .ckn-table td:first-child,
.ckn-panel-body .ckn-table th:first-child,
.ckn-panel-body .ckn-table td:first-child { padding-left: 0; }

.ckn-panel-pad .ckn-table th:last-child,
.ckn-panel-pad .ckn-table td:last-child,
.ckn-panel-body .ckn-table th:last-child,
.ckn-panel-body .ckn-table td:last-child { padding-right: 0; }

/*
 * Row-level emphasis. The tint is the status colour at low alpha over paper — the same rule
 * .ckn-notice follows — so the row still reads as part of the table rather than as a block of
 * colour. Kept faint on purpose: a table where six rows shout has told the reader nothing.
 *
 * These carry the same specificity as the `tbody tr:hover td` rule above and come later, so
 * they would otherwise swallow the hover feedback entirely and leave a tinted row feeling
 * dead. The explicit `:hover` pairs below restore it at a deeper step of the same tint.
 */
.ckn-table tbody tr.ckn-row-warn td,
.ckn-table tbody tr.ckn-table-row-warn td { background: rgba(214, 158, 46, .10); }
.ckn-table tbody tr.ckn-row-err td        { background: rgba(197, 48, 48, .09); }

.ckn-table tbody tr.ckn-row-warn:hover td,
.ckn-table tbody tr.ckn-table-row-warn:hover td { background: rgba(214, 158, 46, .16); }
.ckn-table tbody tr.ckn-row-err:hover td        { background: rgba(197, 48, 48, .15); }

/*
 * A totals row closes a table: rule above it, bold figures, no bottom hairline to fight the
 * panel edge. It wins over `tbody tr:last-child td { border-bottom: 0 }` by being more
 * specific, so it works whether or not it is the final row.
 */
.ckn-table tbody tr.ckn-total-row td {
    border-top: 1px solid var(--bd2);
    border-bottom: 0;
    font-weight: 700;
    background: var(--paper);
}

/*
 * A group header inside the body — one spanning cell that labels the rows beneath it. Set in
 * the same uppercase micro-type as thead so the reader recognises it as structure, not data.
 */
.ckn-table tbody tr.ckn-group-row td {
    font-size: .625rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--g4);
    background: var(--bg);
    padding-top: .45rem;
    padding-bottom: .45rem;
}

/* A narrow, centred column for a manual per-row selection checkbox — replaces what used to be
   a per-instance `style="width:40px;"` on the header cell. */
.ckn-table th.ckn-table-checkbox-col,
.ckn-table td.ckn-table-checkbox-col { width: 40px; text-align: center; }

/* ============================================================ table toolkit
 *
 * Sort, per-column Excel-style filter, universal search and pagination for a `.ckn-table`.
 * No inline `style="..."` anywhere in the markup that pairs with these classes — several
 * services run a `style-src 'self'` CSP with no inline-style exception, so positioning
 * (e.g. the filter menu) has to be CSS-only, never a per-instance style attribute. */

.ckn-table th.ckn-th-sort { cursor: pointer; user-select: none; position: relative; }
.ckn-th-sort-icon { margin-left: .3rem; color: var(--g4); font-size: .75em; }
.ckn-table th.ckn-th-sorted .ckn-th-sort-icon { color: var(--ink); }

/* The filter menu's own anchor, not th.ckn-th-sort's — a column can be filterable without being
 * sortable, and that th never gets position:relative, so .ckn-col-filter-menu (position:absolute)
 * would anchor to whatever positioned ancestor is next up the tree instead of this header cell. */
.ckn-col-filter { position: relative; }

.ckn-col-filter-btn {
    margin-left: .25rem;
    color: var(--g4);
    border: 0;
    background: none;
    cursor: pointer;
    font: inherit;
    padding: 0;
}
.ckn-col-filter-btn.ckn-active { color: var(--ink); }

.ckn-col-filter-menu {
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 20;
    min-width: 220px;
    max-height: 320px;
    overflow-y: auto;
    background: var(--paper);
    border: 1px solid var(--bd2);
    border-radius: var(--r-md);
    box-shadow: 0 4px 16px rgba(0, 0, 0, .12);
    padding: .4rem;
    /* Header cells are uppercase/tracked (§ above); the menu holds ordinary row values. */
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
}
.ckn-col-filter-search { width: 100%; margin-bottom: .3rem; }
.ckn-col-filter-item {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .3rem;
    border-radius: var(--r-xs);
    font-size: .8125rem;
    color: var(--ink);
}
.ckn-col-filter-item:hover { background: rgba(0, 0, 0, .03); }

.ckn-table-toolbar {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-bottom: .6rem;
    flex-wrap: wrap;
}
.ckn-table-search { flex: 1 1 260px; }
/* Overrides .ckn-input's own width: 100% — a rows-per-page select has no reason to stretch
   to fill its flex container; it should size to its own text ("25 / page"), not the row.
   Doubled class selector so specificity beats .ckn-input regardless of source order below. */
.ckn-input.ckn-table-pagesize { width: auto; flex: 0 0 auto; }

/*
 * The table's footer bar: rows-per-page on the left, the pager on the right — sits INSIDE
 * the same panel as the table (a border-top under the last row, not a separate block below
 * the card), so pagination reads as part of the table rather than page chrome floating near
 * it. `.ckn-pager` (below) still works standalone elsewhere (e.g. a plain CursorList
 * Previous/Next with no rows-per-page control) — nesting it in here just resets its own
 * margin so the footer's own gap/justify-content is what spaces the two halves.
 */
.ckn-table-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    flex-wrap: nowrap;
    margin-top: .75rem;
    padding-top: .6rem;
    border-top: 1px solid var(--bd);
}
.ckn-table-footer .ckn-pager { margin-top: 0; }

.ckn-pager {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .6rem;
    justify-content: flex-end;
    font-size: .8125rem;
    color: var(--g4);
}

/* ============================================================ status pill
 *
 * Only the functional palette, at pill radius (§3). A state that is neither good nor bad
 * yet is neutral rather than informational-blue, because blue is not in that palette.
 */

.ckn-badge {
    display: inline-block;
    padding: .1rem .5rem;
    border-radius: var(--r-full);
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .01em;
    line-height: 1.45;
    white-space: nowrap;
    /*
     * The base carries a COMPLETE appearance — background as well as colour — so a badge
     * whose tone class is missing or unmapped still renders as a readable neutral pill.
     * It used to set `color:#ffffff` alone and leave the background to the modifier, which
     * meant one unmapped tone (`ckn-badge-<something we never defined>`) painted white text
     * on white paper: the value vanished with no error and no visual clue it was ever there.
     * A tone is an emphasis, never the thing that makes the content visible at all.
     */
    background: var(--g3);
    color: var(--ink);
}

.ckn-badge-ok      { background: var(--ok);   color: #ffffff; }
.ckn-badge-warn    { background: var(--warn); color: #ffffff; }
.ckn-badge-err     { background: var(--err);  color: #ffffff; }
.ckn-badge-neutral { background: var(--g3);   color: var(--ink); }

/*
 * Lifecycle track — a document's stages rendered as a row of pills (Draft → Confirmed →
 * Delivered), or a numbered stepper. These are progress positions, not health, so they stay
 * off the functional palette: §3 reserves ok/warn/err for status and this is not status.
 * Emphasis instead comes from the neutral end of the scale, which §4 makes black.
 */
.ckn-badge-past { background: var(--bd2); color: var(--g4); }   /* stage already passed */
.ckn-badge-cur  { background: var(--ink); color: #ffffff; }     /* the stage it is in now */
.ckn-badge-pend {                                               /* not reached yet */
    background: transparent;
    color: var(--g3);
    box-shadow: inset 0 0 0 1px var(--bd2);
}

/* The brand dot, as a small state marker where a full badge would shout. */
.ckn-dot {
    display: inline-block;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    margin-right: .4rem;
    vertical-align: .05rem;
}
.ckn-dot-ok   { background: var(--ok); }
.ckn-dot-warn { background: var(--warn); }
.ckn-dot-bad  { background: var(--err); }

/* ============================================================ notice
 *
 * A tinted panel for a standing condition. Functional palette only; the tint is the
 * status colour at low alpha over paper, never a new hue.
 */

.ckn-notice {
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    padding: .7rem .9rem;
    background: var(--paper);
}

.ckn-notice-title { font-weight: 600; }
.ckn-notice-body  { font-size: .8125rem; color: var(--g4); }
.ckn-notice ul    { margin: .35rem 0 0; padding-left: 1.1rem; list-style: disc; }
.ckn-notice li    { font-size: .8125rem; color: var(--g4); margin: .1rem 0; }

.ckn-notice-ok   { border-color: rgba(21, 127, 71, .35); background: rgba(21, 127, 71, .06); }
.ckn-notice-warn { border-color: rgba(219, 103, 4, .35); background: rgba(219, 103, 4, .06); }
.ckn-notice-err  { border-color: rgba(220, 43, 43, .35); background: rgba(220, 43, 43, .06); }

.ckn-notice-ok   .ckn-notice-title { color: var(--ok); }
.ckn-notice-warn .ckn-notice-title { color: var(--warn); }
.ckn-notice-err  .ckn-notice-title { color: var(--err); }

/* ============================================================ auth shell (no-menu pages)
 *
 * For a page with no operator identity yet — sign-in, password reset, an invitation link —
 * where there is nothing to put in a nav rail. Same tokens as the two-column operator shell
 * (--bg canvas, --paper card, --ink text), one centered card instead of two columns. The
 * document is allowed to scroll here: unlike the operator frame there is no fixed-height
 * layout to protect, and a tall card on a short viewport must still be reachable.
 */

.ckn-auth-shell {
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    background: var(--bg);
    padding: 2rem 1.25rem;
}

.ckn-auth-brand {
    display: flex;
    align-items: center;
    gap: .6rem;
}

/*
 * The brand mark is a coloured gradient (drawn for the true-black rail), so on a light
 * canvas it needs the same true-black field under it rather than a second, differently
 * coloured tile — the surrounding chrome stays neutral black, the mark carries the colour.
 */
.ckn-auth-brand-mark {
    flex: 0 0 auto;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--r-md);
    background: var(--rail);
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.ckn-auth-brand-mark img { width: 1.25rem; height: 1.25rem; }

.ckn-auth-brand-name {
    font-size: .9375rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ink);
}

.ckn-auth-card {
    width: 100%;
    max-width: 23rem;
    background: var(--paper);
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    padding: 1.75rem 1.75rem 1.5rem;
}

.ckn-auth-title {
    font-size: 1.0625rem;
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--ink);
    margin-bottom: .3rem;
}

.ckn-auth-subtitle {
    font-size: .8125rem;
    color: var(--g4);
    margin-bottom: 1.4rem;
}

.ckn-auth-footer {
    font-size: .75rem;
    color: var(--g4);
    text-align: center;
}

.ckn-auth-footer a { color: var(--ink); }

/* ============================================================ form controls
 *
 * The operator shell had no forms (every existing page is read-only), so this is the
 * theme's first field/button pair. Hairlines rather than a shadow or a heavy border,
 * consistent with §3; the filled button is always --ink, never an accent (§2 reserves the
 * functional palette for status, not action).
 */

.ckn-field { display: block; margin-bottom: 1rem; }
.ckn-field:last-of-type { margin-bottom: 1.4rem; }

.ckn-label {
    display: block;
    font-size: .75rem;
    font-weight: 600;
    color: var(--g4);
    margin-bottom: .35rem;
}

.ckn-input {
    display: block;
    width: 100%;
    background: var(--paper);
    color: var(--ink);
    border: 1px solid var(--bd2);
    border-radius: var(--r-sm);
    padding: .55rem .7rem;
    font: inherit;
    font-size: .8125rem;
    line-height: 1.4;
}

.ckn-input::placeholder { color: var(--g3); }
.ckn-input:focus { outline: 2px solid var(--ink); outline-offset: 1px; }

.ckn-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    border: 0;
    border-radius: var(--r-sm);
    padding: .6rem 1rem;
    font: inherit;
    font-size: .8125rem;
    font-weight: 600;
    cursor: pointer;
    background: var(--ink);
    color: #ffffff;
}

.ckn-btn:hover { background: #000000; }

/* Inline variant for a page-body action ("+ New user") rather than a form's sole submit —
   auto width, no block stacking, and usable on an <a> as well as a <button>. */
.ckn-btn-sm {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    border: 0;
    border-radius: var(--r-sm);
    padding: .4rem .8rem;
    font: inherit;
    font-size: .75rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    background: var(--ink);
    color: #ffffff;
}

.ckn-btn-sm:hover { background: #000000; }

/*
 * Self-sufficient on its own — 433 of the ~512 places this class is used carry no
 * `.ckn-btn`/`.ckn-btn-sm` alongside it, so it needs its own complete sizing rather than
 * relying on a base class that often isn't there (that gap used to make unpaired ghost
 * buttons fall back to unstyled browser-button chrome — inconsistent height, no set
 * padding). Sized to match `.ckn-btn-sm`, the more common pairing when one is present.
 * `.ckn-btn.ckn-btn-ghost` below restores the bigger `.ckn-btn` sizing for the fewer
 * full-width ghost buttons that explicitly pair the two.
 */
.ckn-btn-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: auto;
    border-radius: var(--r-sm);
    padding: .4rem .8rem;
    font: inherit;
    font-size: .75rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    background: transparent;
    color: var(--ink);
    border: 1px solid var(--bd2);
}

.ckn-btn-ghost:hover { background: rgba(0, 0, 0, .04); }

/* The explicit full-width, larger-padding ghost variant — `.ckn-btn`'s own sizing, kept
   transparent/bordered instead of filled. Higher specificity than either base rule alone,
   so it wins regardless of which one is declared later in this file. */
.ckn-btn.ckn-btn-ghost {
    width: 100%;
    padding: .6rem 1rem;
    font-size: .8125rem;
}

.ckn-checkbox-field {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
}

.ckn-checkbox-field input { width: 1rem; height: 1rem; }

.ckn-field-error {
    font-size: .75rem;
    color: var(--err);
    margin-top: .3rem;
}

/* ============================================================ row actions
 *
 * Icon-only controls for a table row (e.g. Users) — the light-surface counterpart to
 * .ckn-icon-btn, which is quiet-white and tuned for the true-black rail. A row of these
 * stays on one line by construction: an icon has a fixed width where a text label like
 * "Reset password" does not, so three of them next to a right-aligned column never wrap.
 */
.ckn-row-actions {
    display: inline-flex;
    align-items: center;
    gap: .15rem;
    white-space: nowrap;
}

.ckn-icon-btn-row {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 0;
    background: transparent;
    border-radius: var(--r-sm);
    color: var(--g4);
    cursor: pointer;
}

.ckn-icon-btn-row:hover { background: rgba(0, 0, 0, .06); color: var(--ink); }

/* Deactivate is the one destructive control here — --err on hover only, per §3: a
   destructive action is coloured, never coloured *by default* (that would make every row
   look like a warning before anyone has touched anything). */
.ckn-icon-btn-row.ckn-danger:hover { background: rgba(220, 43, 43, .08); color: var(--err); }

/* ============================================================ utilities
 *
 * Deliberately small: only what these surfaces use. This is not a utility framework and
 * should not grow into one — a repeated pattern belongs in a named component above.
 */

/* Wraps rather than overflows — same reasoning as .ckn-toolbar above: on a narrow viewport a
   row of selects, buttons, badges or a long ckn-mono value has nowhere else to go, and this
   class is used for all of those. */
.ckn-row    { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; }
.ckn-push   { margin-left: auto; }
.ckn-muted  { color: var(--g4); }
.ckn-small  { font-size: .78rem; }
.ckn-strong { font-weight: 600; }
.ckn-right  { text-align: right; }
.ckn-center { text-align: center; }

.ckn-text-ok   { color: var(--ok); }
.ckn-text-warn { color: var(--warn); }
.ckn-text-err  { color: var(--err); }

.ckn-mt    { margin-top: .75rem; }
.ckn-mb    { margin-bottom: .75rem; }
.ckn-mb-lg { margin-bottom: 1.25rem; }
.ckn-mt-lg { margin-top: 1.25rem; }

/* ============================================================ detail drawer
 *
 * A side panel rather than a centred modal — reads a row's full detail without leaving the
 * table behind it. Floating over the page is the one case §3 allows a shadow. Kept
 * permanently mounted (class-toggled via .open, not `:if`) per this file's own header note
 * in overview.ts on `@ckn-technology/ui`'s list-duplication bug — irrelevant here since the
 * drawer holds no `:for`, but consistency costs nothing.
 */
.ckn-clickable-row { cursor: pointer; }

.ckn-drawer-scrim {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 70;
    background: rgba(0, 0, 0, .45);
    opacity: 0;
    pointer-events: none;
    transition: opacity .2s ease;
}

.ckn-drawer-scrim.open { display: block; opacity: 1; pointer-events: auto; }

.ckn-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 71;
    width: min(24rem, 88vw);
    background: var(--paper);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    visibility: hidden;
    transition: transform .2s ease, visibility 0s linear .2s;
    box-shadow: 0 0 2rem rgba(0, 0, 0, .35);
}

.ckn-drawer.open {
    transform: translateX(0);
    visibility: visible;
    transition: transform .2s ease, visibility 0s;
}

.ckn-drawer-header {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    height: var(--ckn-header-h);
    padding: 0 .6rem 0 1.25rem;
    border-bottom: 1px solid var(--bd);
}

.ckn-drawer-body {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 1.25rem;
}

.ckn-drawer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .65rem 0;
    border-bottom: 1px solid var(--bd);
    font-size: .8125rem;
}

.ckn-drawer-row:last-child { border-bottom: 0; }

.ckn-drawer-action { margin-top: 1.25rem; }

/* Action buttons wrap rather than scroll: the set changes with the unit's state (a live
   unit offers Stop, a down one offers Start and Clear backoff), so the row's width is not
   fixed and a horizontal scrollbar would hide whichever action ended up last. */
.ckn-action-row {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
}

.ckn-drawer-section {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--bd);
}

/* One evidence line: a fixed-width timestamp column so the times align down the list,
   and the summary/detail stacked beside it. */
.ckn-history-entry {
    display: flex;
    gap: .65rem;
    padding: .4rem 0;
    font-size: .78rem;
}

/*
 * Tone on a history entry. An activity feed is scanned for the one line that went wrong, so
 * the tone has to be visible while the eye is moving — but a full-width tint would turn the
 * panel into stripes. A 2px rule on the leading edge marks the entry and leaves the text
 * alone. Functional palette for the three states; `accent` is the one place a unit colour is
 * allowed here, as "notable, not a health state" (§3 permits accent for one highlight).
 * `neutral` is deliberately transparent-but-present so every entry keeps the same indent.
 */
/*
 * Matched on the attribute rather than by listing the five names, so an entry carrying a tone
 * nobody mapped still gets the indent and lines up with its neighbours — it simply shows no
 * colour. Listing the names would have left an unmapped tone unindented and visibly out of
 * step with the rows above it. An untoned `.ckn-history-entry` has no trailing hyphen and so
 * does not match, which keeps the plain feed in the other services exactly as it was.
 */
[class*="ckn-history-entry-"] {
    border-left: 2px solid transparent;
    padding-left: .6rem;
}

.ckn-history-entry-ok     { border-left-color: var(--ok); }
.ckn-history-entry-warn   { border-left-color: var(--warn); }
.ckn-history-entry-err    { border-left-color: var(--err); }
.ckn-history-entry-accent { border-left-color: var(--accent); }
.ckn-history-entry-neutral{ border-left-color: var(--bd2); }

.ckn-history-at {
    flex: 0 0 auto;
    width: 5.5rem;
    color: var(--g3);
}

.ckn-history-body {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
}

/* A reason string can be a full sentence; it wraps rather than truncating, because the
   reason is the whole point of showing the line. */
.ckn-history-body span { overflow-wrap: anywhere; }

/* ============================================================ resource chart */

/*
 * The Overview chart panel.
 *
 * §2 reserves colour for status and leaves this console without a unit accent, so the
 * bars are drawn in --ink and magnitude is carried by length alone — the chart reads
 * those tokens at paint time (src/apps/main/overview.ts) rather than hard-coding hex,
 * which is why changing the palette here changes the chart too.
 */
.ckn-chart-panel { padding: 1rem 1.15rem 1.15rem; }

.ckn-chart-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem;
    margin-bottom: .9rem;
}

.ckn-chart-title {
    font-size: .8125rem;
    font-weight: 600;
    color: var(--ink);
    margin-bottom: .15rem;
}

.ckn-chart-controls { display: flex; flex-wrap: wrap; gap: .4rem; }

/* Segmented control — the same hairline-and-ink vocabulary as .ckn-tab, boxed because
   these switch what a single chart shows rather than which screen you are on. */
.ckn-seg {
    display: inline-flex;
    border: 1px solid var(--bd);
    border-radius: var(--r-sm);
    overflow: hidden;
}

.ckn-seg-btn {
    background: transparent;
    border: 0;
    border-right: 1px solid var(--bd);
    padding: .35rem .6rem;
    font: inherit;
    font-size: .75rem;
    font-weight: 600;
    color: var(--g4);
    white-space: nowrap;
    cursor: pointer;
}

.ckn-seg-btn:last-child { border-right: 0; }
.ckn-seg-btn:hover      { color: var(--ink); }
.ckn-seg-btn.active     { background: var(--ink); color: var(--paper); }

/* Chart.js sizes the canvas to this box, so the height set inline by the component
   (one row per unit) is what decides how tall the plot gets. */
.ckn-chart-wrap { position: relative; width: 100%; }
.ckn-chart-wrap canvas { display: block; }

/* ============================================================ log viewer */

/*
 * The live output pane.
 *
 * True black, like the operator rail (§4) — the one surface this design system already
 * paints black. It is also what makes the pane read as a terminal rather than as another
 * card, which is the whole point: this is the stream you would have watched had Engine
 * not taken the process detached.
 */
.ckn-term-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
}

.ckn-term-bar .ckn-btn { width: auto; flex: 0 0 auto; }

/* The pane fills its column and keeps its own scroll; the rail beside it is
   independently scrollable, so neither drags the other. */
.ckn-split .ckn-term { height: calc(100vh - 22rem); min-height: 20rem; }

/* Paused reads as the recessive state — following is the default and stays filled. */
.ckn-btn-quiet {
    background: transparent;
    color: var(--g4);
    border: 1px solid var(--bd);
}

.ckn-term {
    margin: 0;
    background: var(--rail);
    color: rgba(255, 255, 255, .88);
    border-radius: var(--r-lg);
    padding: .9rem 1.1rem;
    height: 26rem;
    overflow: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .75rem;
    line-height: 1.55;
    /* Log lines are long and must not be truncated — a wrapped line is readable, a cut
       one silently drops the part that mattered. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    tab-size: 4;
}

/* Grant checkboxes on the operator form — a row of small labelled boxes, not a stack. */
.ckn-check {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin: .15rem .9rem .15rem 0;
    font-size: .8125rem;
    cursor: pointer;
}

.ckn-check input { accent-color: var(--ink); }

/* ============================================================ filter rail */

/*
 * A left rail of choices, beside the thing it filters.
 *
 * Both pages that needed this had the same failure: the choices existed but were not
 * visible as choices. Activity filtered only when a table row was clicked — nothing on
 * screen said that was possible, or what else could be picked. Logs put 21 units in a
 * horizontally-scrolling button row, where the list is only knowable by scrolling it and
 * the selection can sit off-screen. A rail shows the whole set and the current selection
 * at once, which is the entire job.
 *
 * `minmax(0, 1fr)` on the content column, not `1fr`: a grid track defaults to `min-content`
 * width, so a wide table inside would push the layout out instead of scrolling within it.
 */
.ckn-split {
    display: grid;
    grid-template-columns: 232px minmax(0, 1fr);
    gap: 1.15rem;
    align-items: start;
}

/* Below this the rail would leave the content too narrow to read, so it stacks. */
@media (max-width: 900px) {
    .ckn-split { grid-template-columns: 1fr; }
    .ckn-filter-rail { position: static; max-height: 14rem; }
}

.ckn-filter-rail {
    background: var(--paper);
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    padding: .5rem;
    /* Follows the reader down a long table — the filter is useless at the top of a page
       whose content is 100 rows tall. */
    position: sticky;
    top: calc(var(--ckn-header-h) + .75rem);
    max-height: calc(100vh - var(--ckn-header-h) - 3rem);
    overflow-y: auto;
}

.ckn-filter-head {
    padding: .35rem .55rem .5rem;
    font-size: .6875rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--g3);
}

.ckn-filter-btn {
    display: flex;
    align-items: center;
    gap: .45rem;
    width: 100%;
    background: transparent;
    border: 0;
    border-radius: var(--r-sm);
    padding: .4rem .55rem;
    font: inherit;
    font-size: .78125rem;
    color: var(--g4);
    text-align: left;
    cursor: pointer;
}

.ckn-filter-btn:hover  { background: var(--bg); color: var(--ink); }
.ckn-filter-btn.active { background: var(--ink); color: var(--paper); }

/* The service name is the long part; it truncates rather than wrapping to two lines,
   because a rail whose rows are different heights is harder to scan. */
.ckn-filter-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ckn-filter-note {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: .6875rem;
    font-variant-numeric: tabular-nums;
    opacity: .75;
}

.ckn-filter-sep {
    height: 1px;
    background: var(--bd);
    margin: .4rem .35rem;
}

/* ============================================================ panel primitives
 *
 * The form/detail blocks the screens build out of a panel. `.ckn-panel` itself stays flush —
 * a panel wrapping a table must not add padding around cells that bring their own — so the
 * padded variant is a modifier rather than a redefinition.
 *
 * Came from ckn.systems.integration's console.css, which redefined `.ckn-panel` with padding
 * and so silently changed it for that service only. Folded in here as `.ckn-panel-pad` so
 * every service sees the same two shapes.
 */

/*
 * Side padding matches `.ckn-panel-body`'s 1.5rem rather than the 1rem it used to carry. The
 * two are the same thing — the inset between a card's edge and its content — and a product
 * that uses both ended up with content sitting 16px from the edge on one screen and 24px on
 * the next. 16px also simply reads as tight at this card width: the tab row and the first
 * table column looked pinned to the border rather than placed inside it.
 */
.ckn-panel-pad {
    padding: .9rem 1.5rem;
    margin: .9rem 0;
}

.ckn-panel-title {
    margin-bottom: .6rem;
    font-weight: 600;
}

.ckn-panel-actions {
    display: flex;
    gap: .5rem;
    margin-top: .9rem;
}

/* Free-form content inside a panel — prose, a form, a welcome block. A table brings its own
   cell padding and uses the flush panel instead. */
.ckn-panel-body {
    padding: 1.25rem 1.5rem;
}

/**
 * A filter bar. Wraps rather than scrolls, because a filter an operator cannot see is one they
 * cannot tell is applied — and L.7 requires a list to show what its filter excluded.
 */
.ckn-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .6rem;
    margin-bottom: .9rem;
}
.ckn-toolbar > * { min-width: 10rem; }
.ckn-toolbar > button { min-width: 0; }

/**
 * Previous / Next for a cursor-paginated list.
 *
 * There is no page count because a keyset cursor cannot produce one — the page *number* shown
 * beside the buttons is how many pages this session has walked, not how many exist.
 */
.ckn-pager {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .5rem;
    margin-top: .6rem;
}

/** A read-only label/value grid for the detail screens. */
.ckn-facts {
    display: grid;
    grid-template-columns: minmax(9rem, max-content) 1fr;
    gap: .35rem .9rem;
    margin: .6rem 0;
}
.ckn-facts dt { font-size: .8125rem; color: var(--g4); }
.ckn-facts dd { margin: 0; font-variant-numeric: tabular-nums; }

/* ============================================================ account tabs
 *
 * The five My-account screens (L.3) share one `ckn-auth` card rather than the sidebar shell
 * — see ckn.systems.identity's plan/README.md. This is their only navigation.
 */

.ckn-account-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--bd);
    padding-bottom: .6rem;
}

.ckn-account-tab {
    font-size: .8125rem;
    font-weight: 600;
    color: var(--g4);
    text-decoration: none;
    padding: .3rem .55rem;
    border-radius: var(--r-sm, 6px);
}

.ckn-account-tab:hover { color: var(--ink); background: rgba(0, 0, 0, .04); }
.ckn-account-tab.active { color: var(--ink); background: rgba(0, 0, 0, .06); }

.ckn-account-item {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem;
    padding: .85rem 0;
    border-bottom: 1px solid var(--bd);
    /* Every use of this class is on an <a>; browsers default a link to underlined and blue.
       Was an inline `style="text-decoration: none; color: inherit;"` on every caller — dropped
       by CSP's `style-src 'self'` the same way `.ckn-auth-footer`'s margin was. */
    text-decoration: none;
    color: inherit;
}

.ckn-account-item:last-child { border-bottom: 0; }

.ckn-account-item-title {
    font-size: .875rem;
    font-weight: 600;
    color: var(--ink);
}

.ckn-account-item-meta {
    font-size: .78rem;
    color: var(--g4);
    margin-top: .15rem;
}

.ckn-account-item-current {
    display: inline-block;
    font-size: .7rem;
    font-weight: 700;
    color: var(--ok);
    margin-left: .4rem;
}

/* ============================================================ detail list
 *
 * A labelled key/value block, for the screens that must *state facts* before offering a
 * control rather than list rows a person acts on: device approval (which by UI-06 may not
 * show an approve button until client, realm, audience and scope are on screen) and
 * invitation acceptance (inviter, Tenant, roles, expiry). `ckn-account-item` is the wrong
 * shape for those — it is a row with an action on the right, and these have no per-row action.
 *
 * Not to be confused with `.ckn-drawer-row` in the detail-drawer section above: that one is a
 * label/value pair inside a slide-over, centred and space-between. These two carried the same
 * name in two services until the theme was unified; the drawer's was the one renamed.
 */

.ckn-detail {
    margin: 1rem 0;
    border-top: 1px solid var(--bd);
}

.ckn-detail-row {
    display: flex;
    align-items: baseline;
    gap: .75rem;
    padding: .7rem 0;
    border-bottom: 1px solid var(--bd);
}

.ckn-detail-key {
    flex: 0 0 8.5rem;
    font-size: .78rem;
    font-weight: 600;
    color: var(--g4);
}

.ckn-detail-value {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    font-size: .875rem;
    color: var(--ink);
    /* A long audience or issuer URI must wrap inside the card rather than widen it. */
    overflow-wrap: anywhere;
}

/* ============================================================ rail: note and Tenant switch
 *
 * The rail could not be filtered (specification L.2).
 *
 * Deliberately quiet — it explains why the list is complete, and is not a warning about
 * anything the operator did. Warning treatment here would cry wolf on every page load of a
 * deployment whose Identity client tier simply does not release permission codes.
 */
.ckn-nav-note {
    flex: 0 0 auto;        /* part of the rail's fixed footer, below the scrolling menu */
    font-size: .6875rem;
    line-height: 1.45;
    color: rgba(255, 255, 255, .5);
    padding: .5rem .8rem 1rem;
    border-top: 1px solid rgba(255, 255, 255, .08);
    margin: .5rem .5rem 0;
}

/* The sign-out form is a submit target, not a layout box — it must not add one. */
.ckn-signout-form { margin: 0; display: flex; }

/*
 * The Tenant switch is a redirect through Identity, not a local control (L.2) — collapsed by
 * default behind <details> so the common case (staying in the current Tenant) costs nothing
 * on the rail, and expanded it is still a small named-entry form, not a styled dropdown,
 * since there is no API to populate one from (see theme.ckn.ejs's own comment).
 */
.ckn-tenant-switch {
    flex: 0 0 auto;         /* part of the rail's fixed footer, below the scrolling menu */
    border-top: 1px solid rgba(255, 255, 255, .12);
    padding: .5rem .75rem .75rem;
}

.ckn-tenant-switch summary {
    font-size: .6875rem;
    color: rgba(255, 255, 255, .6);
    cursor: pointer;
    list-style: none;
}

.ckn-tenant-switch summary::-webkit-details-marker { display: none; }
.ckn-tenant-switch summary:hover { color: rgba(255, 255, 255, .85); }

.ckn-tenant-switch form {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    margin-top: .5rem;
}

.ckn-tenant-switch-label {
    font-size: .625rem;
    color: rgba(255, 255, 255, .5);
}

.ckn-tenant-switch-input {
    font-size: .75rem;
    padding: .35rem .5rem;
    border-radius: var(--r-sm, 4px);
    border: 1px solid rgba(255, 255, 255, .2);
    background: rgba(255, 255, 255, .08);
    color: #ffffff;
}

.ckn-tenant-switch-input::placeholder { color: rgba(255, 255, 255, .4); }

.ckn-tenant-switch-submit {
    font-size: .6875rem;
    padding: .35rem .5rem;
    border-radius: var(--r-sm, 4px);
    border: 1px solid rgba(255, 255, 255, .2);
    background: transparent;
    color: rgba(255, 255, 255, .85);
    cursor: pointer;
}

.ckn-tenant-switch-submit:hover { background: rgba(255, 255, 255, .1); color: #ffffff; }

/* ============================================================ spacing utilities
 *
 * Every value here used to be a `style="margin-..."` attribute on a view template, and CSP's
 * `style-src 'self'` (no `unsafe-inline`, no nonce, no matching hash) drops an inline style
 * with no visible error on the page — only a console line nobody watching the rendered output
 * would see. Named to match the spacing scale the views actually use, not a general system.
 *
 * `.ckn-mt-lg` is defined once, in the utilities section above.
 */
.ckn-mt-sm  { margin-top: .5rem; }
.ckn-mt-md  { margin-top: 1rem; }
.ckn-mt-xl  { margin-top: 1.5rem; }
.ckn-mt-xxl { margin-top: 1.75rem; }
.ckn-mb-sm  { margin-bottom: .5rem; }
.ckn-mb-md  { margin-bottom: 1rem; }
.ckn-m-0    { margin: 0; }
.ckn-my-md  { margin: 1rem 0; }
.ckn-opacity-dim { opacity: .6; }

/* `.ckn-btn-left` is the one further variant a ghost button needs beyond the base sizing
   `.ckn-btn-ghost` now carries on its own: left-aligned content instead of centered. */
.ckn-btn-left { justify-content: flex-start; text-align: left; }

/* ============================================================ rail: modules and filter
 *
 * The rail is three levels — Group (a heading, never a link), Module (a <details>
 * disclosure), Page (the only thing with a URL). Depth is carried by indent and by weight,
 * not by rules or boxes: §3 rules out drawing a border for every level of a hierarchy, and
 * on the true-black rail a hairline per level would read as clutter long before it read as
 * structure.
 */

/* Real air between groups — was .1rem, which read as one continuous list with occasional
   labels rather than as separate sections. */
.ckn-nav-group + .ckn-nav-group { margin-top: .6rem; }

.ckn-nav-module { margin: 0; }

/*
 * The module row is the rail's odd one out: it never navigates, so it must never be
 * mistaken for a Page link. It used to be *smaller and dimmer* than the Page links nested
 * under it (11px at 48% white vs. a Page's 13px at 60%) — a parent reading as quieter than
 * its own children, which is backwards and was the reason all three levels blurred
 * together. It is now the brightest, heaviest text in the rail short of an active Page: a
 * Group (10px, uppercase, 40% white) reads as a label; a Module (13px, weight 700, 74%
 * white, a caret) reads as a control; a Page (13px/12.5px, weight 400, 58% white, no caret)
 * reads as content. `list-style: none` plus the -webkit pseudo-element removes the default
 * disclosure triangle in both engines; the caret below is drawn instead, so it can sit on
 * the right and take the rail's own reduced-opacity white.
 */
.ckn-nav-module-label {
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .55rem;
    font-size: .8125rem;
    font-weight: 700;
    color: rgba(255, 255, 255, .74);
    border-radius: var(--r-sm);
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.ckn-nav-module-label::-webkit-details-marker { display: none; }
.ckn-nav-module-label:hover { color: #ffffff; background: rgba(255, 255, 255, .05); }

/* The caret. A rotated square drawn from two borders rather than a glyph, so it inherits
   colour and needs no font. */
.ckn-nav-module-label::after {
    content: "";
    margin-left: auto;
    flex: 0 0 auto;
    width: .4rem;
    height: .4rem;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: rotate(-45deg);
    transition: transform .12s ease;
}

.ckn-nav-module[open] > .ckn-nav-module-label::after { transform: rotate(45deg); }
/*
 * The unit accent while open — this is where the operator's attention currently is, whether or
 * not a child happens to be the active one, and "the active nav item" is exactly the signal use
 * --accent is reserved for (see the token's own note). It reads as the same colour as the
 * active-Page bar (`box-shadow: inset 3px 0 0 var(--accent)`), so the rail's two "you are here"
 * marks agree: IDB blue in the ckn.systems.* consoles, ISB yellow in LogisticsOS. Was #ffffff,
 * which made every unit's rail identical and left the accent doing half a job.
 */
.ckn-nav-module[open] > .ckn-nav-module-label { color: var(--accent); }

/*
 * The rail is true black, so the browser's own focus ring (system blue on macOS) both clashes
 * with a non-IDB accent and reads as chrome from outside the design system. <summary> is
 * focusable by default, so this is a real keyboard affordance, not decoration — it is restyled
 * rather than removed.
 */
.ckn-nav-module-label:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

@media (prefers-reduced-motion: reduce) {
    .ckn-nav-module-label::after { transition: none; }
}

/*
 * An open module gets a faint contained card — background only, no border, so it still
 * respects §3's rule against a hairline per level — so its Page items read as *inside this
 * module* rather than as the next item in one long undifferentiated list. Collapsed
 * siblings stay bare, which is what keeps this from turning into a box around every row.
 */
.ckn-nav-module[open] {
    background: rgba(255, 255, 255, .035);
    border-radius: var(--r-sm);
    padding-bottom: .3rem;
}

/*
 * Level 3 sits under its module rather than beside it. The rule is a single hairline at the
 * indent, which reads as "these belong to the row above" without boxing anything — the one
 * place a line earns its keep, because a collapsed module's items would otherwise be
 * indistinguishable from the next module's. Brighter than a hairline usually runs (§3 keeps
 * these faint) because the open-state card above is *also* faint, and the two need to read
 * as one system rather than compete.
 */
.ckn-nav-module-items {
    margin-left: .6rem;
    padding-left: .55rem;
    border-left: 1px solid rgba(255, 255, 255, .16);
}

.ckn-nav-module-items .ckn-nav-item { font-size: .78rem; color: rgba(255, 255, 255, .55); }
.ckn-nav-module-items .ckn-nav-item:hover { color: #ffffff; }
.ckn-nav-module-items .ckn-nav-item.active { color: #ffffff; }

/* ---------------------------------------------------------------- the filter field
 *
 * Built by public/ckn.js, never by the template: a field that cannot filter is worse than
 * no field, so it exists only where the script that drives it ran.
 */
.ckn-nav-search {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: 0 .05rem .35rem;
    padding: .3rem .5rem;
    border-radius: var(--r-sm);
    background: rgba(255, 255, 255, .07);
    border: 1px solid transparent;
}

.ckn-nav-search:focus-within {
    border-color: rgba(255, 255, 255, .28);
    background: rgba(255, 255, 255, .1);
}

.ckn-nav-search-icon {
    width: .85rem;
    height: .85rem;
    flex: 0 0 auto;
    color: rgba(255, 255, 255, .45);
}

.ckn-nav-search-input {
    flex: 1 1 auto;
    min-width: 0;
    border: 0;
    background: transparent;
    color: #ffffff;
    font: inherit;
    font-size: .78rem;
    padding: 0;
}

.ckn-nav-search-input:focus { outline: none; }   /* the wrapper carries the focus treatment */
.ckn-nav-search-input::placeholder { color: rgba(255, 255, 255, .4); }

/* Safari draws its own clear button inside a type="search"; Escape is the theme's way out
   and the extra glyph only competes with the icon on the left. */
.ckn-nav-search-input::-webkit-search-decoration,
.ckn-nav-search-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.ckn-nav-empty {
    margin: .5rem .55rem;
    font-size: .6875rem;
    line-height: 1.45;
    color: rgba(255, 255, 255, .45);
}

/*
 * What the filter hides.
 *
 * A class, not the `hidden` attribute: `hidden` applies `display: none` from the user-agent
 * stylesheet, which loses to `.ckn-nav-item { display: flex }` in this file — the row would
 * stay on screen. Declared here so the rule and the thing it beats live in the same sheet.
 */
.ckn-is-filtered-out { display: none !important; }

/* ============================================================ CSP-safe replacements (2026-08)
 *
 * ckn-loading / ckn-toast / ckn-splitter previously positioned themselves with an inline
 * `style="..."` attribute — a per-instance z-index/background/dimension that only script could
 * know at render time. This platform's CSP is `style-src 'self'` with no exception for inline
 * style (no 'unsafe-inline', no hash, no nonce), and CSP governs a script-driven style mutation
 * exactly as it governs the `style=` attribute — so every one of those was silently refused by
 * the browser, and the component simply never looked or behaved as authored. These classes are
 * the class-based equivalent: the same visual result, carrying no inline style for the CSP to
 * have an opinion about.
 */

.ckn-loading-overlay {
    position: absolute;
    inset: 0;
    z-index: 1050;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    background: rgba(255, 255, 255, .75);
}
.ckn-loading-overlay.ckn-loading-fullscreen { position: fixed; }

.ckn-toast-container {
    position: fixed;
    z-index: 1090;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding: 1rem;
}
.ckn-toast-top-right    { top: 0; right: 0; }
.ckn-toast-top-left     { top: 0; left: 0; }
.ckn-toast-top-center   { top: 0; left: 50%; transform: translateX(-50%); }
.ckn-toast-bottom-right { bottom: 0; right: 0; }
.ckn-toast-bottom-left  { bottom: 0; left: 0; }

.ckn-toast-item {
    display: flex;
    gap: .75rem;
    align-items: flex-start;
    min-width: 16rem;
    max-width: 22rem;
    padding: .7rem .9rem;
    border-radius: var(--r-sm);
    background: var(--ink);
    color: #ffffff;
    font-size: .8125rem;
    box-shadow: 0 4px 16px rgba(0, 0, 0, .18);
}
.ckn-toast-ok    { background: var(--ok); }
.ckn-toast-warn  { background: var(--warn); }
.ckn-toast-err   { background: var(--err); }
.ckn-toast-close { margin-left: auto; background: transparent; border: 0; color: inherit; opacity: .8; cursor: pointer; }
.ckn-toast-close:hover { opacity: 1; }

/* ckn-checkbox-group's per-instance column count used to be `style="columns:{{n}}"`; a data
   attribute carries the same per-instance number without being inline style at all — CSS
   attribute selectors are ordinary style-sheet rules, not something style-src has ever governed. */
.ckn-checkbox-columns[data-columns="2"] { columns: 2; }
.ckn-checkbox-columns[data-columns="3"] { columns: 3; }
.ckn-checkbox-columns[data-columns="4"] { columns: 4; }

/* A 3- or 4-column checkbox list is unreadable below ~520px — one label's worth of width
   per column is what a narrow phone has to give. */
@media (max-width: 519.98px) {
    .ckn-checkbox-columns[data-columns="3"],
    .ckn-checkbox-columns[data-columns="4"] { columns: 2; }
}

/* ckn-splitter's divider dragged an arbitrary, continuously-variable flex-basis via
   `element.style` — no finite set of classes can stand in for a value the person is actively
   dragging to. The native `resize` property is the CSP-safe equivalent: the browser itself
   draws the resize handle and owns the dimension, so nothing ever needs to touch `style=`. The
   trade is a corner handle instead of a full-length divider bar. */
.ckn-split-start {
    resize: horizontal;
    overflow: auto;
    min-width: 10rem;
    max-width: 80%;
    border-right: 1px solid var(--bd2);
}
.ckn-split-start.ckn-split-vertical {
    resize: vertical;
    max-width: none;
    max-height: 80%;
    border-right: 0;
    border-bottom: 1px solid var(--bd2);
}
.ckn-split-end { flex: 1 1 auto; overflow: auto; min-width: 0; min-height: 0; }
.ckn-split-row { display: flex; height: 100%; min-height: 0; }
.ckn-split-column { display: flex; flex-direction: column; height: 100%; min-height: 0; }

/* Below this a resizable side-by-side pane has nowhere left to shrink into — same
   breakpoint the drawer uses to leave the layout, for the same reason. Stacks top/bottom
   instead, matching what .ckn-split-vertical already looks like at any width. */
@media (max-width: 767.98px) {
    .ckn-split-row { flex-direction: column; }
    .ckn-split-row > .ckn-split-start {
        resize: vertical;
        min-width: 0;
        max-width: none;
        max-height: 50%;
        border-right: 0;
        border-bottom: 1px solid var(--bd2);
    }
}

/* ------------------------------------------------------------------------------------------
   Process Designer canvas — a palette of step types on the left, the flow as an SVG in the
   middle, and the selected step's properties in a `.ckn-drawer` sliding in from the right.
   Node colours are baked into SVG presentation attributes by the page (CSP blocks `style=`),
   so only layout, cursors and the palette live here. */
.ckn-designer {
    display: grid;
    grid-template-columns: 12rem minmax(0, 1fr);
    gap: .75rem;
    align-items: stretch;
}

.ckn-designer-palette {
    border: 1px solid var(--bd);
    border-radius: .5rem;
    background: var(--paper);
    padding: .5rem;
    overflow-y: auto;
    max-height: 34rem;
}

.ckn-designer-palette-title {
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--g4);
    padding: .25rem .35rem .5rem;
}

.ckn-designer-palette-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .4rem .5rem;
    margin-bottom: .25rem;
    border: 1px solid var(--bd);
    border-radius: .375rem;
    font-size: .78rem;
    cursor: grab;
    user-select: none;
    background: var(--paper);
}

.ckn-designer-palette-item:hover { border-color: var(--g3); background: var(--bg); }
.ckn-designer-palette-item:active { cursor: grabbing; }

.ckn-designer-swatch {
    width: .7rem;
    height: .7rem;
    border-radius: .15rem;
    border: 1.5px solid currentColor;
    flex: 0 0 auto;
}

.ckn-designer-canvas-wrap {
    border: 1px solid var(--bd);
    border-radius: .5rem;
    background:
        linear-gradient(var(--bd) 1px, transparent 1px) 0 0 / 24px 24px,
        linear-gradient(90deg, var(--bd) 1px, transparent 1px) 0 0 / 24px 24px,
        var(--paper);
    overflow: auto;
    max-height: 34rem;
    min-height: 18rem;
    position: relative;
}

.ckn-designer-canvas { display: block; touch-action: none; }
.ckn-designer-canvas.ckn-dragging { cursor: grabbing; }
.ckn-designer-canvas.ckn-linking { cursor: crosshair; }

.ckn-designer-node { cursor: grab; }
.ckn-designer-node text { pointer-events: none; user-select: none; }
.ckn-designer-port { cursor: crosshair; }
.ckn-designer-edge-hit { cursor: pointer; }

.ckn-designer-hint {
    position: absolute;
    left: .6rem;
    bottom: .4rem;
    font-size: .7rem;
    color: var(--g4);
    pointer-events: none;
}

/* The property drawer is wider than a detail drawer: it carries a whole step form. */
.ckn-drawer.ckn-drawer-wide { width: min(30rem, 92vw); }

@media (max-width: 900px) {
    .ckn-designer { grid-template-columns: minmax(0, 1fr); }
    .ckn-designer-palette { max-height: 10rem; }
}

.ckn-designer-empty {
    position: absolute;
    left: 1rem;
    top: 1rem;
    font-size: .8rem;
    color: var(--g4);
    pointer-events: none;
}

/* A table whose cells are inputs (the Designer's transitions / variables / triggers): the
   controls sit flush in the cell so the columns read as one grid, and the selected row is the
   one the canvas has picked out. */
.ckn-table-form td { padding: .35rem .5rem; vertical-align: top; }
.ckn-table-form td .ckn-input { margin: 0; min-width: 7rem; }
.ckn-table-form tr.ckn-row-selected td {
    background: rgba(29, 111, 255, .06);
    background: color-mix(in oklab, var(--accent) 6%, transparent);
}

/* ------------------------------------------------------------------------------------------
   Transformation Studio — a revision rail on the left and the mapper on the right: source
   fields, a column of SVG lines, target fields. Rows are a fixed 30px so the page can place a
   line's ends by row index alone (see STUDIO_ROW in transformationeditorpage.ts). */
.ckn-studio {
    display: grid;
    grid-template-columns: 15rem minmax(0, 1fr);
    gap: .75rem;
    align-items: start;
}

.ckn-studio-rail {
    border: 1px solid var(--bd);
    border-radius: var(--r-lg);
    background: var(--paper);
    padding: .6rem;
    max-height: calc(100vh - 12rem);
    overflow-y: auto;
}

.ckn-studio-rail-title {
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--g4);
    padding: .1rem .2rem .5rem;
}

.ckn-studio-rail-item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .1rem;
    width: 100%;
    text-align: left;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    padding: .4rem .5rem;
    font: inherit;
    cursor: pointer;
    color: var(--ink);
}

.ckn-studio-rail-item:hover { background: var(--bg); }
.ckn-studio-rail-item.ckn-active { border-color: var(--ink); background: var(--bg); }
.ckn-studio-rail-code { font-size: .8125rem; font-weight: 600; }

.ckn-studio-action { width: 14rem; margin: 0; }
.ckn-studio-new { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .75rem; }

.ckn-studio-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 120px minmax(0, 1fr);
    align-items: end;
    gap: 0;
    padding: .75rem 1rem .5rem;
    border-bottom: 1px solid var(--bd);
}

.ckn-studio-col-head { display: flex; align-items: flex-end; justify-content: space-between; gap: .75rem; }
.ckn-studio-col-mid { text-align: center; padding-bottom: .35rem; }
.ckn-studio-search { width: 11rem; margin: 0; }

.ckn-studio-body {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 120px minmax(0, 1fr);
    align-items: start;
    padding: .5rem 1rem;
    max-height: 34rem;
    overflow: auto;
}

.ckn-studio-col { min-width: 0; }
.ckn-studio-lines { display: block; overflow: visible; }
.ckn-studio-lines path { cursor: pointer; }
.ckn-studio-lines path:hover { stroke-width: 3; }

.ckn-studio-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: .5rem;
    height: 30px;
    padding: 0 .5rem;
    border-radius: var(--r-sm);
    font-size: .8125rem;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
}

.ckn-studio-row:hover { background: var(--bg); }
.ckn-studio-row-mapped .ckn-studio-field { font-weight: 600; }
.ckn-studio-row-selected {
    background: rgba(29, 111, 255, .08);
    background: color-mix(in oklab, var(--accent) 8%, transparent);
    outline: 1px solid var(--accent);
}
.ckn-studio-row-problem .ckn-studio-field { color: var(--err); }

.ckn-studio-indent-1 { padding-left: 1.25rem; }
.ckn-studio-indent-2 { padding-left: 2rem; }
.ckn-studio-indent-3 { padding-left: 2.75rem; }
.ckn-studio-indent-4 { padding-left: 3.5rem; }

.ckn-studio-field { overflow: hidden; text-overflow: ellipsis; }
.ckn-studio-type { font-size: .7rem; color: var(--g3); font-family: var(--mono, ui-monospace, monospace); }
.ckn-studio-req { color: var(--err); margin-left: .15rem; }
.ckn-studio-uses { font-size: .7rem; color: var(--g4); }
.ckn-studio-rule {
    font-size: .7rem;
    color: var(--g4);
    border: 1px solid var(--bd2);
    border-radius: 999px;
    padding: 0 .45rem;
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ckn-studio-problem {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background: var(--err);
    color: #fff;
    font-size: .7rem;
    font-weight: 700;
}

.ckn-studio-port {
    position: absolute;
    top: 50%;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    transform: translateY(-50%);
    background: var(--paper);
    border: 1.5px solid var(--g4);
}
.ckn-studio-port-out { right: -5px; }
.ckn-studio-port-in { left: -5px; }
.ckn-studio-row-mapped .ckn-studio-port { background: var(--g4); }
.ckn-studio-row:hover .ckn-studio-port-out {
    background: var(--accent);
    border-color: var(--accent);
    cursor: crosshair;
}

.ckn-studio-empty { padding: .5rem; }
.ckn-studio-hint { padding: .5rem 1rem .75rem; font-size: .7rem; color: var(--g4); border-top: 1px solid var(--bd); }
.ckn-studio-rule-form { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .75rem; }

@media (max-width: 1000px) {
    .ckn-studio { grid-template-columns: minmax(0, 1fr); }
    .ckn-studio-rail { max-height: 12rem; }
    .ckn-studio-new, .ckn-studio-rule-form { grid-template-columns: minmax(0, 1fr); }
}
