@grafloria/element 0.4.34 → 0.4.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,5 +21,7 @@
21
21
  * (pages own their card innerHTML).
22
22
  */
23
23
  export declare const DASHBOARD_KIT_STYLE_ID = "grafloria-dashboard-kit-styles";
24
+ /** The kit stylesheet as text — for a host that bundles its own CSS, and for the styling spec. */
25
+ export declare const DASHBOARD_KIT_CSS = "\n/* ===== reflow glide (armed on the html layer for the whole gesture) ===== */\n.grafloria-html-layer.axdb-glide > .grafloria-node-host {\n transition: left .28s cubic-bezier(.2, 0, .2, 1), top .28s cubic-bezier(.2, 0, .2, 1),\n width .28s cubic-bezier(.2, 0, .2, 1), height .28s cubic-bezier(.2, 0, .2, 1);\n}\n\n/* Motion is a preference (WCAG 2.3.3): no glide for those who asked for none. */\n@media (prefers-reduced-motion: reduce) {\n .grafloria-html-layer.axdb-glide > .grafloria-node-host { transition: none; }\n}\n\n/* ===== keyboard focus: the roving tab stop shows where it is (WCAG 2.4.7) ===== */\n.grafloria-html-layer > .grafloria-node-host:focus-visible {\n outline: 2px solid var(--axdb-accent, #3b52d9);\n outline-offset: 2px;\n border-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== the held tile: transition-exempt ghost, above everything ===== */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost,\n.grafloria-html-layer.axdb-glide > .grafloria-node-host.axdb-ghost {\n transition: none;\n z-index: 30;\n opacity: .85;\n cursor: grabbing;\n filter: drop-shadow(0 10px 16px rgba(16, 24, 40, .3));\n}\n/* Outside the board: release will REMOVE \u2014 dim the ghost to say so. */\n.grafloria-html-layer > .grafloria-node-host.axdb-ghost.axdb-out { opacity: .35; filter: grayscale(.6); }\n\n/* ===== the placeholder: dashed slab, truthful, never animated ===== */\n.grafloria-html-layer > .axdb-ph {\n position: absolute;\n border-radius: var(--axdb-rs-radius, 8px);\n background: rgba(30, 34, 45, .14);\n border: 2px dashed rgba(30, 34, 45, .28);\n box-sizing: border-box;\n pointer-events: none;\n z-index: 0;\n transition: none;\n}\n/* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */\n.grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }\n\n@media (prefers-color-scheme: dark) {\n .grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }\n}\n\n/* ===== corner resize handle (hover-revealed, radius-matched) ===== */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs {\n position: absolute;\n right: 0;\n bottom: 0;\n width: 24px; /* WCAG 2.5.8: a 24-px minimum target (was 18) */\n height: 24px;\n cursor: nwse-resize;\n border-right: 3px solid rgba(120, 130, 148, .55);\n border-bottom: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: var(--axdb-rs-radius, 8px);\n opacity: 0;\n transition: opacity .12s;\n z-index: 5;\n}\n.grafloria-node-host:hover > .axdb-rs { opacity: 1; }\n/* A finger never hovers: on touch devices the corner handle is always visible\n (review D8 \u2014 hover-only meant no resize at all on a tablet). */\n@media (hover: none) { .grafloria-node-host > .axdb-rs { opacity: .8; } }\n.grafloria-node-host > .axdb-rs:hover { border-color: #3b52d9; }\n/* RTL boards grow leftwards, so the grab corner mirrors with them. */\n:is(.grafloria-node-host, .axdb-slab) > .axdb-rs.axdb-rs--rtl {\n right: auto;\n left: 0;\n cursor: nesw-resize;\n border-right: none;\n border-left: 3px solid rgba(120, 130, 148, .55);\n border-bottom-right-radius: 0;\n border-bottom-left-radius: var(--axdb-rs-radius, 8px);\n}\n\n/* ===== palette drag-in chip (screen-space clone following the cursor) ===== */\n.axdb-drag-chip {\n position: fixed;\n z-index: 9999;\n pointer-events: none;\n opacity: .9;\n filter: drop-shadow(0 8px 14px rgba(16, 24, 40, .3));\n transition: opacity .12s, filter .12s;\n}\n/* A bounded board with no room refuses the entry: the chip dims to say so\n (the same signal a tile ghost gives outside the board). Without this rule\n the class was set and nothing showed \u2014 a refusal a user could not see. */\n.axdb-drag-chip.axdb-out { opacity: .35; filter: grayscale(.7) drop-shadow(0 8px 14px rgba(16, 24, 40, .2)); }\n/* A tab dragged off its strip: VS Code carries the TAB, not the editor, so\n what follows the pointer is a chip wearing the tab's own label. */\n.axdb-tab-chip {\n padding: 5px 12px;\n max-width: 220px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n color: var(--axdb-tabs-on-fg, #1f2430);\n background: var(--axdb-tabs-on-bg, #fff);\n border-radius: var(--axdb-tab-radius, 6px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);\n}\n@media (prefers-color-scheme: dark) {\n .axdb-tab-chip {\n color: var(--axdb-tabs-on-fg, #eceef4);\n background: var(--axdb-tabs-on-bg, #232836);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);\n }\n}\n/* A tab held over ANOTHER group: it will join that group, not the board. The\n group's frame lights up, and its strip marks the slot the tab will take \u2014\n a bar before the tab it lands in front of, or after the last one. */\n.grafloria-html-layer > .axdb-join {\n position: absolute;\n z-index: 3;\n pointer-events: none;\n box-sizing: border-box;\n border: 2px dashed var(--axdb-accent, #3b52d9);\n background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));\n border-radius: var(--axdb-rs-radius, 8px);\n}\n.axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }\n.axdb-tab.axdb-tab--drop-before { position: relative; }\n.axdb-tab.axdb-tab--drop-before::before {\n content: \"\"; position: absolute; left: -3px; top: 4px; bottom: 4px; width: 3px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9);\n}\n.axdb-tabs.axdb-tabs--drop-end::after {\n content: \"\"; display: inline-block; width: 3px; height: 18px; margin: 0 4px;\n border-radius: 2px; background: var(--axdb-accent, #3b52d9); vertical-align: middle; align-self: center;\n}\n\n/* ===========================================================================\n BUILT-IN WIDGET CARDS \u2014 what widgets.ts paints when a page writes no\n renderWidget. Class-prefixed (never element selectors), so this can only\n ever style DOM the kit itself produced. Colours are CSS variables on the\n card, so a page re-skins every built-in widget by setting them once; the\n corner radius follows --axdb-rs-radius, which is also the placeholder's and\n the resize handle's, so the three can never drift apart.\n =========================================================================== */\n.axdb-widget {\n --axdb-ink: #1f2430;\n /* 5.95:1 on the card \u2014 WCAG 1.4.3 for the 9\u201311px captions this paints\n (the #7a8496 it replaced sat at 3.77:1, axe-core's first finding). */\n --axdb-muted: #5a6478;\n --axdb-grid: rgba(120, 130, 148, .22);\n --axdb-card: #fff;\n --axdb-line: #e5e8ef;\n --axdb-soft: rgba(120, 130, 148, .14);\n --axdb-up: #0f7a3d; /* 5.42:1 */\n --axdb-down: #be123c; /* 6.29:1 */\n /* The categorical palette, as tokens so the dark card can carry its own\n steps: every entry clears 3:1 against its card (WCAG 1.4.11). */\n --axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;\n --axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;\n /* The header band: a panel wears its title in a band across its top, the\n way DevExpress, Grafana and Metabase panels do \u2014 tinted ground, hairline\n below, sentence case (0.4.36; the tracked uppercase micro-label before). */\n --axdb-head-bg: #f7f8fb;\n --axdb-head-fg: #2a3140;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 0;\n background: var(--axdb-card);\n border: 1px solid var(--axdb-line);\n border-radius: var(--axdb-rs-radius, 8px);\n box-shadow: 0 1px 2px rgba(16, 24, 40, .04);\n font-family: system-ui, -apple-system, \"Segoe UI\", sans-serif;\n color: var(--axdb-ink);\n}\n/* The host is the card's size container (named, so a widget's inner parts can\n ask the TILE's size as well as their own body's), so the card can shed its\n padding as the tile gets short instead of clipping its own content. */\n.grafloria-html-layer > .grafloria-node-host { container: axdb-tile / size; }\n/* Two class hops on the header: these blocks precede the header's own rule\n below and would lose the cascade at equal specificity \u2014 at 54 px the margin\n stayed 8 px, the body shrank to 14 px and the KPI figure hid. */\n@container axdb-tile (max-height: 90px) {\n .axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }\n .axdb-widget > .axdb-widget-b { padding: 6px 12px; }\n}\n/* The row floor: the band gives way to an inline label beside the figure. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }\n .axdb-widget > .axdb-widget-b { padding: 3px 12px; }\n}\n@container axdb-tile (max-height: 26px) {\n .axdb-widget > .axdb-widget-h { display: none; }\n .axdb-widget > .axdb-widget-b { padding: 1px 12px; }\n}\n.axdb-widget-h {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: none;\n min-width: 0;\n padding: 9px 14px;\n background: var(--axdb-head-bg);\n border-bottom: 1px solid var(--axdb-line);\n font: 600 12.5px/1.3 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-head-fg);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n/* Drag-handle mode (DevExpress drags an item by its caption): the header is\n the grip and says so with a dot pattern and a grab cursor; the body keeps\n its own cursor because a press there starts nothing. */\n.axdb-drag-handle .axdb-widget > .axdb-widget-h { cursor: grab; }\n.axdb-drag-handle .axdb-widget > .axdb-widget-h::before {\n content: ''; flex: none; width: 8px; height: 12px; opacity: .55;\n background: radial-gradient(circle, currentColor 1.1px, transparent 1.5px) 0 0 / 4px 4px;\n}\n/* A PAINTED GRIP (dragHandle: { grip: true }): the only drag zone, a small\n dotted tab along the card's top edge. Inside sits in the header band and\n the header makes room for it; outside is a tab above the card, the\n DevExpress item bar. The host must not clip it. */\n.grafloria-node-host > .axdb-grip {\n position: absolute; z-index: 4; box-sizing: border-box; width: 24px; height: 12px;\n border: 1px solid var(--axdb-line, #e7eaf1); border-radius: 3px; background: var(--axdb-card, #fff);\n color: var(--axdb-muted, #5a6478); cursor: grab;\n /* Shown on the SELECTED widget only (the DevExpress designer shows an item's\n bar on the selected item): a press or keyboard focus selects, a void\n click clears. Hidden, it takes no pointer either. */\n opacity: 0; pointer-events: none; transition: opacity .12s ease;\n}\n.grafloria-node-host.axdb-selected > .axdb-grip, .grafloria-node-host:focus-within > .axdb-grip { opacity: 1; pointer-events: auto; }\n.grafloria-node-host > .axdb-grip::before {\n content: ''; position: absolute; left: 5px; top: 2px; width: 12px; height: 6px;\n background: radial-gradient(circle, currentColor 1px, transparent 1.4px) 0 0 / 4px 3px;\n}\n.grafloria-node-host > .axdb-grip:hover { border-color: var(--axdb-accent, #3b52d9); color: var(--axdb-accent, #3b52d9); }\n/* The selected card says so, quietly. --axdb-accent themes the grip's hover, the\n ring and the focus outline together; --axdb-accent-ring the ring alone. */\n.grafloria-node-host.axdb-selected > .axdb-widget { box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)), 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .05); }\n/* While a resize edge is near, the host and everything in it (a chart canvas\n with its own cursor included) show the edge's cursor. */\n.grafloria-node-host[data-axdb-edge=\"ns-resize\"], .grafloria-node-host[data-axdb-edge=\"ns-resize\"] * { cursor: ns-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"ew-resize\"], .grafloria-node-host[data-axdb-edge=\"ew-resize\"] * { cursor: ew-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nwse-resize\"], .grafloria-node-host[data-axdb-edge=\"nwse-resize\"] * { cursor: nwse-resize !important; }\n.grafloria-node-host[data-axdb-edge=\"nesw-resize\"], .grafloria-node-host[data-axdb-edge=\"nesw-resize\"] * { cursor: nesw-resize !important; }\n/* INSIDE: centred on the header's text line and flush with the card padding,\n per size tier (padding 13/15, then 8/14, 4/12, 2/12). */\n.grafloria-node-host > .axdb-grip--inside { top: 12px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }\n.grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }\n@container axdb-tile (max-height: 90px) {\n .grafloria-node-host > .axdb-grip--inside { top: 9px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }\n}\n@container axdb-tile (max-height: 46px) {\n .grafloria-node-host > .axdb-grip--inside { top: 5px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }\n .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }\n .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }\n}\n@container axdb-tile (max-height: 26px) {\n .grafloria-node-host > .axdb-grip--inside { top: 2px; }\n}\n/* OUTSIDE: a tab on the card's top edge, its corners in line with the card's. */\n/* The OUTSIDE tab lives in the gap between tiles: never taller than the gap\n less a pixel (an 11-px tab in a 10-px gap sat on the tile above), never\n shorter than 6 px so the dots still read. */\n.grafloria-node-host > .axdb-grip--outside { top: calc(-1 * clamp(6px, var(--axdb-gap, 11px) - 1px, 11px)); height: clamp(6px, calc(var(--axdb-gap, 11px) - 1px), 11px); border-bottom-left-radius: 0; border-bottom-right-radius: 0; border-bottom: 0; box-shadow: 0 -1px 2px rgba(16, 24, 40, .08); }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }\n.grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }\n.grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }\n/* The header makes room for an inside grip on its side \u2014 inset + the 24-px\n grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the\n first letter of the title) \u2014 and a centred one sits above the text. */\n.grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }\n.grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }\n.axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }\n/* A drag across a STATIC board (nothing prevents the press's default there)\n used to select every label on it; kit cards are not prose. Tables stay\n copyable \u2014 a figure in a grid is the one thing a viewer selects. */\n.axdb-widget { user-select: none; -webkit-user-select: none; }\n.axdb-widget .axdb-table { user-select: text; -webkit-user-select: text; }\n.axdb-widget-b > svg { display: block; width: 100%; height: 100%; }\n.axdb-widget-b.axdb-scroll { overflow: auto; }\n/* A chart WITH a legend under it: the plot yields height, the legend keeps its\n own (without this the 100%-tall svg pushes the legend out of the card). */\n.axdb-widget-b.axdb-has-lg { display: flex; flex-direction: column; }\n.axdb-widget-b.axdb-has-lg > svg { flex: 1; height: auto; min-height: 0; }\n/* The data behind a chart, for readers that cannot see the chart: present in\n the accessibility tree, absent from the picture (WCAG 1.1.1). */\n.axdb-sr {\n position: absolute; top: 0; left: 0; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;\n clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;\n}\n.axdb-widget-empty {\n display: flex; align-items: center; justify-content: center; height: 100%;\n font: 500 11.5px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n\n/* kpi: value + delta stack, the spark yields its height before they do */\n/* A SIZE CONTAINER, so the card's type scales with the tile instead of\n clipping. Fit-mode shrinks tiles when rows are added (dropping a chart above\n the KPI row took these from 122px to 85px) and fixed 30px/12px lines were\n sliced mid-glyph by the card's overflow \u2014 the audited \"trimmed\" widgets.\n cqh = 1% of the body's own height, clamped so full-size tiles look exactly\n as before and short tiles compress instead of cutting. */\n.axdb-widget-b.axdb-kpi { display: flex; flex-direction: column; container: axdb-kpi / size; }\n/* STEP DOWN, NEVER CLIP. As the body gets shorter the sparkline goes first, then the\n delta, then the value \u2014 the header alone at the row floor. Before this a 99-px row\n drew the sparkline as a 13-px sliver and a 28-px row cut the value mid-glyph (the\n fluid board, after a drag pushed the KPI row down). Thresholds are body heights. */\n/* Two class hops, so these outrank \".axdb-widget-b > svg { display: block }\" \u2014\n a bare .axdb-kpi-s lost that cascade and the sparkline stayed. */\n@container (max-height: 78px) { .axdb-kpi > .axdb-kpi-s { display: none; } }\n@container (max-height: 40px) { .axdb-kpi > .axdb-kpi-d { display: none; } }\n@container (max-height: 16px) { .axdb-kpi > .axdb-kpi-v { display: none; } }\n.axdb-kpi-v { font: 700 clamp(15px, 44cqh, 30px)/1.05 system-ui, sans-serif; letter-spacing: -.02em; color: var(--axdb-ink); white-space: nowrap; }\n.axdb-kpi-d { margin-top: clamp(1px, 5cqh, 6px); font: 600 clamp(9px, 19cqh, 12px)/1.2 system-ui, sans-serif; white-space: nowrap; }\n.axdb-kpi-d span { color: var(--axdb-muted); font-weight: 500; }\n.axdb-kpi-d.up { color: var(--axdb-up); }\n.axdb-kpi-d.down { color: var(--axdb-down); }\n/* Grows into a tall tile (a 3-row KPI is not a number over a void) and never\n takes more than two fifths of the body. */\n.axdb-kpi-s { margin-top: auto; height: auto; min-height: 0; flex: 1 1 34px; max-height: 40%; }\n/* SHORT AND WIDE \u2014 a one-row KPI on a fluid board. Stacked, a short tile dropped\n its sparkline and left a small figure in the corner of an empty card. Here the\n figure, its delta and the sparkline sit in a row and the spark fills the width.\n The outer query asks the TILE (host), the inner one the body, so a strip too\n thin for a readable spark (under 24 px) keeps the figure alone. 340 px is what\n a figure, its delta, the gaps and a spark worth reading need \u2014 the builder's\n 236-px tiles got a 40-px wedge of spark beside the delta. */\n@container axdb-tile (max-height: 125px) and (min-width: 340px) {\n .axdb-widget--kpi > .axdb-kpi { flex-direction: row; align-items: center; gap: 14px; }\n .axdb-widget--kpi .axdb-kpi-d { margin-top: 0; }\n @container axdb-kpi (min-height: 24px) {\n .axdb-kpi > .axdb-kpi-s { display: block; flex: 1 1 40%; min-width: 80px; height: 100%; max-height: 100%; margin-top: 0; align-self: stretch; }\n }\n}\n/* THE STRIP (row floor): header and figure on one line, the spark beside them\n when the tile is wide enough. */\n@container axdb-tile (max-height: 46px) {\n .axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }\n .axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }\n /* The body is a SIZE container: in a centred row it would collapse to zero\n and every height query would fire. It takes the card's full inner height. */\n .axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }\n}\n\n/* SPLIT LAYOUT chrome: a divider sits in the gap between two siblings (a\n 24-px hit zone around a 10-px gap) and shows its line on hover or while\n dragged; the insertion line marks the edge a dragged widget will land on. */\n.grafloria-html-layer > .axdb-div { position: absolute; z-index: 2; pointer-events: auto; background: transparent; }\n.grafloria-html-layer > .axdb-div--row { cursor: col-resize; }\n.grafloria-html-layer > .axdb-div--column { cursor: row-resize; }\n.grafloria-html-layer > .axdb-div::after {\n content: \"\"; position: absolute; border-radius: 2px; background: #3b52d9; opacity: 0; transition: opacity .12s ease;\n}\n.grafloria-html-layer > .axdb-div--row::after { left: 50%; top: 8px; bottom: 8px; width: 4px; margin-left: -2px; }\n.grafloria-html-layer > .axdb-div--column::after { top: 50%; left: 8px; right: 8px; height: 4px; margin-top: -2px; }\n.grafloria-html-layer > .axdb-div:hover::after, .grafloria-html-layer > .axdb-div.axdb-active::after { opacity: .9; }\n.grafloria-html-layer > .axdb-ins {\n position: absolute; z-index: 3; pointer-events: none; border-radius: 2px;\n background: #3b52d9; box-shadow: 0 0 0 3px rgba(59, 82, 217, .22);\n}\n@media (prefers-reduced-motion: reduce) { .grafloria-html-layer > .axdb-div::after { transition: none; } }\n\n/* donut: ring beside its legend. The ring takes the body's height (a tall\n tile gets a bigger ring, not dead card), square, capped so its centre figure\n stays a figure and not a headline. */\n.axdb-widget-b.axdb-donut { display: flex; align-items: center; gap: 10px; }\n/* A legend taller than a SHORT body (a 2-row donut on a squeezed board) is\n clipped at its own foot, not centred over the title above it. */\n.axdb-widget-b.axdb-donut > .axdb-lg--col { max-height: 100%; min-height: 0; overflow: hidden; }\n.axdb-widget-b.axdb-donut > svg {\n flex: 0 0 auto; width: auto; height: 100%; max-height: 260px; max-width: 60%; aspect-ratio: 1 / 1;\n}\n\n/* READABILITY TIERS (widgets.ts chartTier): the text is always in the DOM;\n what a short body cannot afford is hidden, not dropped. */\n.axdb-tier-1 .axdb-yt--q, .axdb-tier-1 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--q, .axdb-tier-2 .axdb-yl--q,\n.axdb-tier-2 .axdb-yt--h, .axdb-tier-2 .axdb-yl--h,\n.axdb-tier-2 .axdb-xt, .axdb-tier-2 .axdb-vt,\n.axdb-lg--off { display: none; }\n\n/* SECTION CHROME: a pointer-transparent overlay on every member group. It\n wears the selection ring and, while selected, the corner handle. */\n.grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }\n/* Selected, the overlay rises above the tiles so ITS corner handle wins a\n corner it shares with a child's; unselected, its handle takes no presses. */\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }\n.grafloria-html-layer > .axdb-slab > .axdb-rs { pointer-events: none; opacity: 0; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-rs { pointer-events: auto; opacity: 1; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-rs { display: none; }\n\n/* SECTION CAPTION (0.4.22): the band on the slab. Geometry is inline (the\n reserve and the pixels come from one function); everything visual is a\n variable the options write and a theme may override. The band takes the\n pointer \u2014 the slab does not \u2014 so a press on it is the section's. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;\n display: flex; align-items: center; gap: 6px; min-width: 0;\n padding: var(--axdb-caption-pad, 0 12px);\n background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));\n /* A hairline by default: two bands meeting (a captioned section inside a\n captioned section) read as two headers, not one grey block. */\n border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));\n color: var(--axdb-caption-fg, #1f2430);\n font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, \"Segoe UI\", sans-serif);\n text-transform: var(--axdb-caption-transform, none);\n letter-spacing: -.005em;\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;\n transition: opacity .12s;\n}\n/* The modifiers carry the band's own selector so they outrank its defaults. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--center { justify-content: center; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--end { justify-content: flex-end; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vtop { align-items: flex-start; }\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--vbottom { align-items: flex-end; }\n/* 'tab': the same band sized to its text, a chip at the leading corner. It\n reserves its height like 'inside' \u2014 see captionReserve. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {\n max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);\n border-bottom: var(--axdb-caption-border-tab, none);\n box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);\n}\n/* 'hover': an overlay. It reserves nothing, so while hidden it must not take\n the pointer (an invisible band swallowed clicks on the content beneath),\n and while shown it paints OPAQUE \u2014 a 5% tint over a chart is a smear. */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover {\n opacity: 0; pointer-events: none;\n background: var(--axdb-caption-bg, #fff);\n box-shadow: 0 1px 4px rgba(31, 36, 48, .16);\n}\n/* .axdb-slab--hot is set by the binder from the live pointer (the overlay\n takes no pointer of its own, so CSS :hover can never fire on it). */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h.axdb-slab-h--hover,\n.grafloria-html-layer > .axdb-slab.axdb-slab--selected > .axdb-slab-h.axdb-slab-h--hover { pointer-events: auto; opacity: 1; }\n/* the actions of a hovered or selected section */\n.grafloria-html-layer > .axdb-slab.axdb-slab--hot > .axdb-slab-h > .axdb-slab-h-actions { opacity: 1; }\n/* the tight tier: a section under 90 px */\n.grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tight { font-size: min(var(--axdb-caption-font-size, 12px), 12px); gap: 4px; }\n.axdb-slab-h--tight .axdb-slab-h-sub, .axdb-slab-h--tight .axdb-slab-h-actions { display: none; }\n.axdb-slab-h-icon { flex: none; }\n.axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }\n.axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }\n.axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }\n.axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }\n.axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }\n.axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }\n.axdb-slab-h-action {\n all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;\n border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;\n}\n.axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }\n.axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-slab-h-action[disabled] { opacity: .4; cursor: default; }\n.axdb-slab-h-action[disabled]:hover { background: none; }\n.grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }\n@media (prefers-color-scheme: dark) {\n /* The band sits on the PAGE, whose ground the kit does not own (a host that\n stays light while the kit goes dark is common): a solid ink, not a tint,\n so the light caption always reads. */\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h {\n background: var(--axdb-caption-bg, #1b2029);\n color: var(--axdb-caption-fg, #eceef4);\n border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));\n }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab { box-shadow: inset 0 0 0 1px rgba(236, 238, 244, .12); }\n .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--hover { background: var(--axdb-caption-bg, #1a1d25); box-shadow: 0 1px 4px rgba(0, 0, 0, .5); }\n .axdb-slab-h-action:hover { background: rgba(236, 238, 244, .1); }\n}\n\n/* TAB CONTAINER (0.4.27): a strip of pages across the container's top. The\n strip takes the pointer (the tabs are real buttons); the pages below it are\n ordinary boards. */\n.grafloria-html-layer > .axdb-tabs {\n position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;\n display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;\n background: var(--axdb-tabs-bg, #f1f3f8);\n border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);\n border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;\n scrollbar-width: thin;\n}\n.axdb-tabs--center { justify-content: center; }\n.axdb-tabs--end { justify-content: flex-end; }\n.axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }\n/* A tab is a PILL on the track (a segmented control): quiet until hovered,\n the active one lifted in the card's own ground. */\n.axdb-tab {\n all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;\n padding: 0 12px; height: 100%; display: inline-flex; align-items: center;\n font: 500 12.5px/1 system-ui, -apple-system, \"Segoe UI\", sans-serif;\n letter-spacing: -.005em;\n color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;\n border-radius: var(--axdb-tab-radius, 6px);\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n transition: background-color .12s, color .12s, box-shadow .12s;\n}\n.axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }\n.axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }\n.axdb-tab.axdb-tab--on {\n background: var(--axdb-tabs-on-bg, #fff);\n color: var(--axdb-tabs-on-fg, #1f2430);\n font-weight: 600;\n box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);\n}\n@media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }\n@media (prefers-color-scheme: dark) {\n /* Solid for the same reason as the caption band: the track sits on the page. */\n .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }\n .axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }\n .axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }\n .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }\n}\n\n/* legend chips, shared by line and donut */\n.axdb-lg { display: flex; flex-wrap: wrap; gap: 4px 12px; margin-top: 9px; }\n.axdb-lg--col { flex-direction: column; flex-wrap: nowrap; gap: 6px; margin-top: 0; }\n.axdb-lg i {\n display: inline-flex; align-items: center; gap: 5px; font-style: normal;\n font: 500 11px/1.3 system-ui, sans-serif; color: var(--axdb-muted);\n}\n.axdb-lg i b { width: 9px; height: 9px; border-radius: 3px; flex: none; }\n\n/* table */\n.axdb-table { width: 100%; border-collapse: collapse; font-size: 12px; color: var(--axdb-ink); }\n.axdb-table th {\n text-align: left; padding: 4px 8px 7px; border-bottom: 1px solid var(--axdb-line);\n font: 600 10px/1.3 system-ui, sans-serif; letter-spacing: .4px; text-transform: uppercase;\n color: var(--axdb-muted);\n}\n.axdb-table td { padding: 6px 8px; border-bottom: 1px solid var(--axdb-line); white-space: nowrap; }\n.axdb-table td.num { text-align: right; font-variant-numeric: tabular-nums; }\n.axdb-table tr:last-child td { border-bottom: none; }\n\n@media (prefers-color-scheme: dark) {\n .axdb-widget {\n --axdb-ink: #eceef4;\n --axdb-muted: #98a1b4;\n --axdb-grid: rgba(150, 160, 182, .2);\n --axdb-card: #161a22;\n --axdb-line: #2a3040;\n --axdb-head-bg: rgba(255, 255, 255, .035);\n --axdb-head-fg: #e6e9f0;\n --axdb-soft: rgba(150, 160, 182, .16);\n --axdb-up: #4ade80;\n --axdb-down: #fb7185;\n --axdb-c1: #8b9cff; --axdb-c2: #38bdf8; --axdb-c3: #2dd4bf;\n --axdb-c4: #fbbf24; --axdb-c5: #a78bfa; --axdb-c6: #94a3b8;\n }\n}\n";
24
26
  /** Idempotently inject the kit stylesheet (safe to call per binder). */
25
27
  export declare function ensureDashboardKitStyles(doc?: Document): void;
@@ -37,7 +37,7 @@ const CSS = `
37
37
  .grafloria-html-layer > .grafloria-node-host:focus-visible {
38
38
  outline: 2px solid var(--axdb-accent, #3b52d9);
39
39
  outline-offset: 2px;
40
- border-radius: var(--axdb-rs-radius, 3px);
40
+ border-radius: var(--axdb-rs-radius, 8px);
41
41
  }
42
42
 
43
43
  /* ===== the held tile: transition-exempt ghost, above everything ===== */
@@ -55,7 +55,7 @@ const CSS = `
55
55
  /* ===== the placeholder: dashed slab, truthful, never animated ===== */
56
56
  .grafloria-html-layer > .axdb-ph {
57
57
  position: absolute;
58
- border-radius: var(--axdb-rs-radius, 3px);
58
+ border-radius: var(--axdb-rs-radius, 8px);
59
59
  background: rgba(30, 34, 45, .14);
60
60
  border: 2px dashed rgba(30, 34, 45, .28);
61
61
  box-sizing: border-box;
@@ -63,6 +63,9 @@ const CSS = `
63
63
  z-index: 0;
64
64
  transition: none;
65
65
  }
66
+ /* A slab move asked for a cell it cannot have (a locked section in the way): the wanted cell in the danger tone. */
67
+ .grafloria-html-layer > .axdb-ph.axdb-ph--no { border-color: var(--axdb-danger, #b3123c); background: var(--axdb-danger-soft, rgba(179, 18, 60, .07)); }
68
+
66
69
  @media (prefers-color-scheme: dark) {
67
70
  .grafloria-html-layer > .axdb-ph { background: rgba(220, 225, 240, .12); border-color: rgba(220, 225, 240, .3); }
68
71
  }
@@ -77,7 +80,7 @@ const CSS = `
77
80
  cursor: nwse-resize;
78
81
  border-right: 3px solid rgba(120, 130, 148, .55);
79
82
  border-bottom: 3px solid rgba(120, 130, 148, .55);
80
- border-bottom-right-radius: var(--axdb-rs-radius, 3px);
83
+ border-bottom-right-radius: var(--axdb-rs-radius, 8px);
81
84
  opacity: 0;
82
85
  transition: opacity .12s;
83
86
  z-index: 5;
@@ -95,7 +98,7 @@ const CSS = `
95
98
  border-right: none;
96
99
  border-left: 3px solid rgba(120, 130, 148, .55);
97
100
  border-bottom-right-radius: 0;
98
- border-bottom-left-radius: var(--axdb-rs-radius, 3px);
101
+ border-bottom-left-radius: var(--axdb-rs-radius, 8px);
99
102
  }
100
103
 
101
104
  /* ===== palette drag-in chip (screen-space clone following the cursor) ===== */
@@ -122,14 +125,14 @@ const CSS = `
122
125
  font: 500 12px/1.4 var(--axdb-font, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
123
126
  color: var(--axdb-tabs-on-fg, #1f2430);
124
127
  background: var(--axdb-tabs-on-bg, #fff);
125
- border-radius: var(--axdb-rs-radius, 3px);
126
- box-shadow: 0 0 0 1px rgba(31, 36, 48, .12);
128
+ border-radius: var(--axdb-tab-radius, 6px);
129
+ box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .08);
127
130
  }
128
131
  @media (prefers-color-scheme: dark) {
129
132
  .axdb-tab-chip {
130
133
  color: var(--axdb-tabs-on-fg, #eceef4);
131
- background: var(--axdb-tabs-on-bg, #1a1d25);
132
- box-shadow: 0 0 0 1px rgba(236, 238, 244, .16);
134
+ background: var(--axdb-tabs-on-bg, #232836);
135
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .12);
133
136
  }
134
137
  }
135
138
  /* A tab held over ANOTHER group: it will join that group, not the board. The
@@ -142,7 +145,7 @@ const CSS = `
142
145
  box-sizing: border-box;
143
146
  border: 2px dashed var(--axdb-accent, #3b52d9);
144
147
  background: var(--axdb-accent-soft, rgba(59, 82, 217, .08));
145
- border-radius: var(--axdb-rs-radius, 3px);
148
+ border-radius: var(--axdb-rs-radius, 8px);
146
149
  }
147
150
  .axdb-tabs.axdb-tabs--drop { box-shadow: inset 0 -2px 0 var(--axdb-accent, #3b52d9); }
148
151
  .axdb-tab.axdb-tab--drop-before { position: relative; }
@@ -170,7 +173,7 @@ const CSS = `
170
173
  --axdb-muted: #5a6478;
171
174
  --axdb-grid: rgba(120, 130, 148, .22);
172
175
  --axdb-card: #fff;
173
- --axdb-line: #e7eaf1;
176
+ --axdb-line: #e5e8ef;
174
177
  --axdb-soft: rgba(120, 130, 148, .14);
175
178
  --axdb-up: #0f7a3d; /* 5.42:1 */
176
179
  --axdb-down: #be123c; /* 6.29:1 */
@@ -178,17 +181,22 @@ const CSS = `
178
181
  steps: every entry clears 3:1 against its card (WCAG 1.4.11). */
179
182
  --axdb-c1: #3b52d9; --axdb-c2: #0369a1; --axdb-c3: #0f766e;
180
183
  --axdb-c4: #b45309; --axdb-c5: #6d28d9; --axdb-c6: #475569;
184
+ /* The header band: a panel wears its title in a band across its top, the
185
+ way DevExpress, Grafana and Metabase panels do — tinted ground, hairline
186
+ below, sentence case (0.4.36; the tracked uppercase micro-label before). */
187
+ --axdb-head-bg: #f7f8fb;
188
+ --axdb-head-fg: #2a3140;
181
189
  box-sizing: border-box;
182
190
  width: 100%;
183
191
  height: 100%;
184
192
  display: flex;
185
193
  flex-direction: column;
186
194
  overflow: hidden;
187
- padding: 13px 15px 12px;
195
+ padding: 0;
188
196
  background: var(--axdb-card);
189
197
  border: 1px solid var(--axdb-line);
190
- border-radius: var(--axdb-rs-radius, 3px);
191
- box-shadow: 0 1px 2px rgba(16, 24, 40, .05), 0 1px 3px rgba(16, 24, 40, .05);
198
+ border-radius: var(--axdb-rs-radius, 8px);
199
+ box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
192
200
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
193
201
  color: var(--axdb-ink);
194
202
  }
@@ -200,25 +208,33 @@ const CSS = `
200
208
  below and would lose the cascade at equal specificity — at 54 px the margin
201
209
  stayed 8 px, the body shrank to 14 px and the KPI figure hid. */
202
210
  @container axdb-tile (max-height: 90px) {
203
- .axdb-widget { padding: 8px 14px 7px; }
204
- .axdb-widget > .axdb-widget-h { margin-bottom: 4px; }
211
+ .axdb-widget > .axdb-widget-h { padding: 5px 12px; font-size: 11.5px; }
212
+ .axdb-widget > .axdb-widget-b { padding: 6px 12px; }
205
213
  }
214
+ /* The row floor: the band gives way to an inline label beside the figure. */
206
215
  @container axdb-tile (max-height: 46px) {
207
- .axdb-widget { padding: 4px 12px 3px; }
208
- .axdb-widget > .axdb-widget-h { margin-bottom: 0; }
216
+ .axdb-widget > .axdb-widget-h { padding: 0 0 0 12px; background: none; border-bottom: 0; }
217
+ .axdb-widget > .axdb-widget-b { padding: 3px 12px; }
209
218
  }
210
219
  @container axdb-tile (max-height: 26px) {
211
- .axdb-widget { padding: 2px 12px 1px; }
220
+ .axdb-widget > .axdb-widget-h { display: none; }
221
+ .axdb-widget > .axdb-widget-b { padding: 1px 12px; }
212
222
  }
213
223
  .axdb-widget-h {
214
224
  display: flex;
215
225
  align-items: center;
216
- gap: 6px;
217
- margin-bottom: 8px;
218
- font: 600 11px/1.3 system-ui, sans-serif;
219
- letter-spacing: .4px;
220
- text-transform: uppercase;
221
- color: var(--axdb-muted);
226
+ gap: 8px;
227
+ flex: none;
228
+ min-width: 0;
229
+ padding: 9px 14px;
230
+ background: var(--axdb-head-bg);
231
+ border-bottom: 1px solid var(--axdb-line);
232
+ font: 600 12.5px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
233
+ letter-spacing: -.005em;
234
+ color: var(--axdb-head-fg);
235
+ white-space: nowrap;
236
+ overflow: hidden;
237
+ text-overflow: ellipsis;
222
238
  }
223
239
  /* Drag-handle mode (DevExpress drags an item by its caption): the header is
224
240
  the grip and says so with a dot pattern and a grab cursor; the body keeps
@@ -258,7 +274,7 @@ const CSS = `
258
274
  .grafloria-node-host[data-axdb-edge="nesw-resize"], .grafloria-node-host[data-axdb-edge="nesw-resize"] * { cursor: nesw-resize !important; }
259
275
  /* INSIDE: centred on the header's text line and flush with the card padding,
260
276
  per size tier (padding 13/15, then 8/14, 4/12, 2/12). */
261
- .grafloria-node-host > .axdb-grip--inside { top: 14px; }
277
+ .grafloria-node-host > .axdb-grip--inside { top: 12px; }
262
278
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 15px; }
263
279
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 15px; }
264
280
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 3px; }
@@ -267,12 +283,18 @@ const CSS = `
267
283
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 14px; }
268
284
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 14px; }
269
285
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 2px; }
286
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 46px; }
287
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 46px; }
288
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 15px; }
270
289
  }
271
290
  @container axdb-tile (max-height: 46px) {
272
291
  .grafloria-node-host > .axdb-grip--inside { top: 5px; }
273
292
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--left { left: 12px; }
274
293
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--right { right: 12px; }
275
294
  .grafloria-node-host > .axdb-grip--inside.axdb-grip--center { top: 1px; }
295
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 44px; }
296
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 44px; }
297
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 13px; }
276
298
  }
277
299
  @container axdb-tile (max-height: 26px) {
278
300
  .grafloria-node-host > .axdb-grip--inside { top: 2px; }
@@ -285,11 +307,13 @@ const CSS = `
285
307
  .grafloria-node-host > .axdb-grip--outside.axdb-grip--left { left: 10px; }
286
308
  .grafloria-node-host > .axdb-grip--outside.axdb-grip--right { right: 10px; }
287
309
  .grafloria-node-host > .axdb-grip--center { left: 50%; transform: translateX(-50%); }
288
- /* The header makes room for an inside grip on its side; a centred one sits above it. */
289
- .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 32px; }
290
- .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 32px; }
291
- .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 12px; }
292
- .axdb-widget-b { flex: 1; min-height: 0; position: relative; }
310
+ /* The header makes room for an inside grip on its side inset + the 24-px
311
+ grip + an 8-px gap, per tier (32 px under the band's 14-px inset hid the
312
+ first letter of the title) and a centred one sits above the text. */
313
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-left .axdb-widget > .axdb-widget-h { padding-left: 47px; }
314
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-right .axdb-widget > .axdb-widget-h { padding-right: 47px; }
315
+ .grafloria-node-host.axdb-gp-inside.axdb-gp-center .axdb-widget > .axdb-widget-h { padding-top: 17px; }
316
+ .axdb-widget-b { flex: 1; min-height: 0; position: relative; padding: 12px 14px; }
293
317
  /* A drag across a STATIC board (nothing prevents the press's default there)
294
318
  used to select every label on it; kit cards are not prose. Tables stay
295
319
  copyable — a figure in a grid is the one thing a viewer selects. */
@@ -355,7 +379,7 @@ const CSS = `
355
379
  when the tile is wide enough. */
356
380
  @container axdb-tile (max-height: 46px) {
357
381
  .axdb-widget--kpi { flex-direction: row; align-items: center; gap: 12px; }
358
- .axdb-widget--kpi > .axdb-widget-h { flex: none; }
382
+ .axdb-widget--kpi > .axdb-widget-h { flex: none; padding: 0 0 0 12px; background: none; border-bottom: 0; }
359
383
  /* The body is a SIZE container: in a centred row it would collapse to zero
360
384
  and every height query would fire. It takes the card's full inner height. */
361
385
  .axdb-widget--kpi > .axdb-kpi { flex: 1; align-self: stretch; }
@@ -400,7 +424,7 @@ const CSS = `
400
424
 
401
425
  /* SECTION CHROME: a pointer-transparent overlay on every member group. It
402
426
  wears the selection ring and, while selected, the corner handle. */
403
- .grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 3px); z-index: 4; }
427
+ .grafloria-html-layer > .axdb-slab { position: absolute; pointer-events: none; border-radius: var(--axdb-rs-radius, 8px); z-index: 4; }
404
428
  /* Selected, the overlay rises above the tiles so ITS corner handle wins a
405
429
  corner it shares with a child's; unselected, its handle takes no presses. */
406
430
  .grafloria-html-layer > .axdb-slab.axdb-slab--selected { z-index: 6; box-shadow: 0 0 0 1.5px var(--axdb-accent-ring, rgba(59, 82, 217, .55)); }
@@ -415,16 +439,16 @@ const CSS = `
415
439
  .grafloria-html-layer > .axdb-slab > .axdb-slab-h {
416
440
  position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 1;
417
441
  display: flex; align-items: center; gap: 6px; min-width: 0;
418
- padding: var(--axdb-caption-pad, 0 10px);
419
- background: var(--axdb-caption-bg, rgba(31, 36, 48, .055));
442
+ padding: var(--axdb-caption-pad, 0 12px);
443
+ background: var(--axdb-caption-bg, rgba(31, 36, 48, .045));
420
444
  /* A hairline by default: two bands meeting (a captioned section inside a
421
445
  captioned section) read as two headers, not one grey block. */
422
446
  border-bottom: var(--axdb-caption-border, 1px solid rgba(31, 36, 48, .1));
423
447
  color: var(--axdb-caption-fg, #1f2430);
424
448
  font: var(--axdb-caption-font-weight, 600) var(--axdb-caption-font-size, 13px)/1.2 var(--axdb-caption-font-family, system-ui, -apple-system, "Segoe UI", sans-serif);
425
449
  text-transform: var(--axdb-caption-transform, none);
426
- letter-spacing: .01em;
427
- border-radius: var(--axdb-rs-radius, 3px) var(--axdb-rs-radius, 3px) 0 0;
450
+ letter-spacing: -.005em;
451
+ border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;
428
452
  cursor: default; user-select: none; -webkit-user-select: none; overflow: hidden;
429
453
  transition: opacity .12s;
430
454
  }
@@ -436,7 +460,7 @@ const CSS = `
436
460
  /* 'tab': the same band sized to its text, a chip at the leading corner. It
437
461
  reserves its height like 'inside' — see captionReserve. */
438
462
  .grafloria-html-layer > .axdb-slab > .axdb-slab-h.axdb-slab-h--tab {
439
- max-width: 100%; border-radius: var(--axdb-rs-radius, 3px);
463
+ max-width: 100%; border-radius: var(--axdb-rs-radius, 8px);
440
464
  border-bottom: var(--axdb-caption-border-tab, none);
441
465
  box-shadow: inset 0 0 0 1px rgba(31, 36, 48, .08);
442
466
  }
@@ -460,14 +484,14 @@ const CSS = `
460
484
  .axdb-slab-h-icon { flex: none; }
461
485
  .axdb-slab-h-body { display: flex; flex-direction: column; justify-content: center; min-width: 0; flex: 0 1 auto; }
462
486
  .axdb-slab-h-text, .axdb-slab-h-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
463
- .axdb-slab-h-sub { font-size: .85em; font-weight: 500; opacity: .72; }
487
+ .axdb-slab-h-sub { font-size: .88em; font-weight: 500; opacity: .7; }
464
488
  .axdb-slab-h-info { flex: none; opacity: .6; font-size: .9em; cursor: help; }
465
489
  .axdb-slab-h-actions { flex: none; display: flex; gap: 2px; margin-inline-start: auto; opacity: 0; transition: opacity .12s; }
466
490
  .axdb-slab-h--center .axdb-slab-h-actions, .axdb-slab-h--end .axdb-slab-h-actions { margin-inline-start: 0; }
467
491
  .axdb-slab-h:hover > .axdb-slab-h-actions, .axdb-slab--selected > .axdb-slab-h > .axdb-slab-h-actions, .axdb-slab-h:focus-within > .axdb-slab-h-actions { opacity: 1; }
468
492
  .axdb-slab-h-action {
469
- all: unset; box-sizing: border-box; width: 24px; height: 24px; display: inline-flex; align-items: center; justify-content: center;
470
- border-radius: 4px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit;
493
+ all: unset; box-sizing: border-box; width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
494
+ border-radius: 6px; cursor: pointer; font-size: 14px; line-height: 1; color: inherit; transition: background-color .12s;
471
495
  }
472
496
  .axdb-slab-h-action:hover { background: rgba(31, 36, 48, .08); }
473
497
  .axdb-slab-h-action:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }
@@ -475,8 +499,11 @@ const CSS = `
475
499
  .axdb-slab-h-action[disabled]:hover { background: none; }
476
500
  .grafloria-html-layer > .axdb-slab.axdb-slab--static > .axdb-slab-h { cursor: default; }
477
501
  @media (prefers-color-scheme: dark) {
502
+ /* The band sits on the PAGE, whose ground the kit does not own (a host that
503
+ stays light while the kit goes dark is common): a solid ink, not a tint,
504
+ so the light caption always reads. */
478
505
  .grafloria-html-layer > .axdb-slab > .axdb-slab-h {
479
- background: var(--axdb-caption-bg, rgba(236, 238, 244, .07));
506
+ background: var(--axdb-caption-bg, #1b2029);
480
507
  color: var(--axdb-caption-fg, #eceef4);
481
508
  border-bottom: var(--axdb-caption-border, 1px solid rgba(236, 238, 244, .12));
482
509
  }
@@ -490,36 +517,42 @@ const CSS = `
490
517
  ordinary boards. */
491
518
  .grafloria-html-layer > .axdb-tabs {
492
519
  position: absolute; box-sizing: border-box; pointer-events: auto; z-index: 5;
493
- display: flex; align-items: flex-end; gap: 2px; padding: 0 6px; overflow-x: auto; overflow-y: hidden;
494
- background: var(--axdb-tabs-bg, rgba(31, 36, 48, .05));
495
- border-bottom: 1px solid var(--axdb-tabs-line, rgba(31, 36, 48, .12));
496
- border-radius: var(--axdb-rs-radius, 3px) var(--axdb-rs-radius, 3px) 0 0;
520
+ display: flex; align-items: center; gap: 2px; padding: 3px 6px; overflow-x: auto; overflow-y: hidden;
521
+ background: var(--axdb-tabs-bg, #f1f3f8);
522
+ border-bottom: 1px solid var(--axdb-tabs-line, #e5e8ef);
523
+ border-radius: var(--axdb-rs-radius, 8px) var(--axdb-rs-radius, 8px) 0 0;
497
524
  scrollbar-width: thin;
498
525
  }
499
526
  .axdb-tabs--center { justify-content: center; }
500
527
  .axdb-tabs--end { justify-content: flex-end; }
501
528
  .axdb-tabs--stretch > .axdb-tab { flex: 1 1 0; }
529
+ /* A tab is a PILL on the track (a segmented control): quiet until hovered,
530
+ the active one lifted in the card's own ground. */
502
531
  .axdb-tab {
503
532
  all: unset; box-sizing: border-box; flex: 0 0 auto; max-width: 200px;
504
- padding: 0 12px; height: calc(100% - 4px); display: inline-flex; align-items: center;
505
- font: 600 12px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
533
+ padding: 0 12px; height: 100%; display: inline-flex; align-items: center;
534
+ font: 500 12.5px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
535
+ letter-spacing: -.005em;
506
536
  color: var(--axdb-tabs-fg, #5a6478); cursor: pointer;
507
- border-radius: var(--axdb-rs-radius, 3px) var(--axdb-rs-radius, 3px) 0 0;
537
+ border-radius: var(--axdb-tab-radius, 6px);
508
538
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
539
+ transition: background-color .12s, color .12s, box-shadow .12s;
509
540
  }
510
- .axdb-tab:hover { background: rgba(31, 36, 48, .06); }
541
+ .axdb-tab:hover { background: rgba(31, 36, 48, .06); color: var(--axdb-tabs-on-fg, #1f2430); }
511
542
  .axdb-tab:focus-visible { outline: 2px solid var(--axdb-accent-ring, rgba(59, 82, 217, .55)); outline-offset: -2px; }
512
- /* The active tab reads as the front page: the card's own ground, lifted. */
513
543
  .axdb-tab.axdb-tab--on {
514
544
  background: var(--axdb-tabs-on-bg, #fff);
515
545
  color: var(--axdb-tabs-on-fg, #1f2430);
516
- box-shadow: 0 -1px 0 var(--axdb-accent, #3b52d9) inset, 0 0 0 1px rgba(31, 36, 48, .1);
546
+ font-weight: 600;
547
+ box-shadow: 0 1px 2px rgba(16, 24, 40, .1), 0 0 0 1px rgba(31, 36, 48, .06);
517
548
  }
549
+ @media (prefers-reduced-motion: reduce) { .axdb-tab { transition: none; } }
518
550
  @media (prefers-color-scheme: dark) {
519
- .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, rgba(236, 238, 244, .06)); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .14)); }
551
+ /* Solid for the same reason as the caption band: the track sits on the page. */
552
+ .grafloria-html-layer > .axdb-tabs { background: var(--axdb-tabs-bg, #1b2029); border-bottom-color: var(--axdb-tabs-line, rgba(236, 238, 244, .12)); }
520
553
  .axdb-tab { color: var(--axdb-tabs-fg, #98a1b4); }
521
- .axdb-tab:hover { background: rgba(236, 238, 244, .08); }
522
- .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #1a1d25); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 -1px 0 var(--axdb-accent, #7d8ff0) inset, 0 0 0 1px rgba(236, 238, 244, .14); }
554
+ .axdb-tab:hover { background: rgba(236, 238, 244, .08); color: var(--axdb-tabs-on-fg, #eceef4); }
555
+ .axdb-tab.axdb-tab--on { background: var(--axdb-tabs-on-bg, #232836); color: var(--axdb-tabs-on-fg, #eceef4); box-shadow: 0 1px 2px rgba(0, 0, 0, .45), 0 0 0 1px rgba(236, 238, 244, .1); }
523
556
  }
524
557
 
525
558
  /* legend chips, shared by line and donut */
@@ -547,8 +580,10 @@ const CSS = `
547
580
  --axdb-ink: #eceef4;
548
581
  --axdb-muted: #98a1b4;
549
582
  --axdb-grid: rgba(150, 160, 182, .2);
550
- --axdb-card: #1a1d25;
551
- --axdb-line: #2b3040;
583
+ --axdb-card: #161a22;
584
+ --axdb-line: #2a3040;
585
+ --axdb-head-bg: rgba(255, 255, 255, .035);
586
+ --axdb-head-fg: #e6e9f0;
552
587
  --axdb-soft: rgba(150, 160, 182, .16);
553
588
  --axdb-up: #4ade80;
554
589
  --axdb-down: #fb7185;
@@ -557,6 +592,8 @@ const CSS = `
557
592
  }
558
593
  }
559
594
  `;
595
+ /** The kit stylesheet as text — for a host that bundles its own CSS, and for the styling spec. */
596
+ export const DASHBOARD_KIT_CSS = CSS;
560
597
  /** Idempotently inject the kit stylesheet (safe to call per binder). */
561
598
  export function ensureDashboardKitStyles(doc) {
562
599
  // SERVER-SAFE: `dashboard()` is a pure spec builder and runs where there is
package/src/lib/load.js CHANGED
@@ -238,6 +238,7 @@ export function fromDocument(document, options = {}) {
238
238
  : {},
239
239
  active: (_x = activeGroup === null || activeGroup === void 0 ? void 0 : activeGroup.id) !== null && _x !== void 0 ? _x : 'main',
240
240
  apiRef: null,
241
+ container: null,
241
242
  };
242
243
  const handle = createDashboardHandle(ctx);
243
244
  const renderCustomNode = (node, host) => {
@@ -255,7 +256,7 @@ export function fromDocument(document, options = {}) {
255
256
  (_b = getNodeType(node.type)) === null || _b === void 0 ? void 0 : _b(node, host);
256
257
  };
257
258
  const finalize = (api) => {
258
- var _a, _b, _c, _d, _e, _f;
259
+ var _a, _b, _c, _d, _e, _f, _g;
259
260
  const a = api;
260
261
  if (!a)
261
262
  return;
@@ -296,6 +297,7 @@ export function fromDocument(document, options = {}) {
296
297
  // work immediately — no camera move, no showView, so the paint is byte-for-
297
298
  // byte what a boards-only load produced.
298
299
  ctx.apiRef = a;
300
+ ctx.container = (_e = a.container) !== null && _e !== void 0 ? _e : null;
299
301
  for (const group of groups) {
300
302
  const board = group.getMetadata('dashboardBoard');
301
303
  if (!board)
@@ -308,7 +310,7 @@ export function fromDocument(document, options = {}) {
308
310
  }
309
311
  // Tab containers, with the same runtime dashboard() uses — a reloaded
310
312
  // document must switch pages exactly like a board built from a literal.
311
- attachTabsRuntime(ctx, model, (_e = a.container) !== null && _e !== void 0 ? _e : null, handle);
313
+ attachTabsRuntime(ctx, model, (_f = a.container) !== null && _f !== void 0 ? _f : null, handle);
312
314
  // LIVE LAYOUT SWITCH on a loaded document — the same contract dashboard()
313
315
  // finalize offers: cells persisted where the grid reads them, any tree and
314
316
  // column cache cleared, the board's `layout` flag flipped, a fresh binder.
@@ -355,7 +357,7 @@ export function fromDocument(document, options = {}) {
355
357
  }
356
358
  // A loaded board follows the history exactly as an authored one does: an
357
359
  // undo re-syncs every binder without the consumer calling refresh().
358
- (_f = ctx.attachHistory) === null || _f === void 0 ? void 0 : _f.call(ctx);
360
+ (_g = ctx.attachHistory) === null || _g === void 0 ? void 0 : _g.call(ctx);
359
361
  };
360
362
  return Object.assign({ nodes, edges: model.getLinks(), renderCustomNode,
361
363
  finalize,