/**
 * The one admin data table.
 *
 * House furniture for every table rendered through AIEO_Table. This file is
 * where the presentation tokens of the table contract live, and its purpose
 * is to end the inline styles: the product currently repeats
 * style="text-align:right" on every numeric cell, hardcodes six independent
 * colour-band schemes as inline hex, and picks table widths and font sizes
 * per screen.
 *
 * Tokens, so a future Metronic or Portal renderer restyles everything at
 * once: .aieo-t-a--* alignment, .aieo-t-tone--* value tone, .aieo-t-badge--*
 * status colour, .aieo-t--compact density.
 *
 * -- 11.1.7: THE COLOURS MOVED OUT --------------------------------------------
 *
 * Every literal below became a `var(--aieo-*)` out of `aieo-tokens.css`, which
 * is enqueued as this file's dependency. That file is shared with
 * `aieo-controls.css`, and sharing it is the point: a badge in a report and a
 * badge in a questionnaire now mean the same thing because they read the same
 * declaration, rather than because two people once picked the same hex.
 *
 * The BADGE variants were renamed at the same time and it had to happen in one
 * wave. `definition_version` is a content hash, so a token rename re-exports
 * every affected report definition -- doing the tables now and the form
 * controls later would have meant renaming twice and re-exporting twice.
 * `danger` became `destructive` and `neutral` became `secondary`;
 * AIEO_Variants normalises the old names on the way in, which is not
 * politeness: `tone_key` names a ROW FIELD, so some variants are read out of
 * the database at render time and cannot be found by searching the source.
 *
 * Every `var()` keeps a literal fallback. A custom property that is never
 * declared does not error and does not inherit -- the whole declaration is
 * dropped, so a badge would render with no background rather than the wrong
 * one. The fallback is what makes a missing stylesheet a cosmetic problem
 * instead of an invisible-status problem.
 *
 * @since 11.1.4
 */

/* ---------------------------------------------------------------------
 * WE OWN THE CHROME. `widefat` is gone.
 *
 * Every table this component renders used to carry `widefat striped` so it
 * inherited admin furniture for free. That was the right call while the
 * component was being built and the wrong one the moment the look became the
 * job, for three measured reasons.
 *
 * 1. 🚨 IT ALREADY FOUGHT US AND ALREADY WON ONCE. `.widefat th { text-align:
 *    left }` is (0,1,1) and beat our alignment token at (0,1,0) on every header
 *    in the product — a number under the NEXT column's heading, on 247 tables,
 *    for as long as the component existed. The fix restated three tokens at
 *    (0,2,1). `widefat` also owns the padding, the borders and the zebra, so
 *    keeping it meant restating ALL of those at (0,2,1) too, and every future
 *    token would arrive carrying the same tax.
 *
 * 2. 🚨 IT PRODUCED TWO DIFFERENT-LOOKING PRODUCTS. Measured 2026-08-31 on the
 *    live screens: a table inside `.aieo-dmm-section` got dmm.css's card
 *    treatment — 8px radius, #e4e4e7 border, a grey header bar, 12px/600
 *    headings, `vertical-align: middle`. The identical component OUTSIDE that
 *    wrapper got raw core — no border, no radius, no header background,
 *    12px/400 headings, `vertical-align: top`. Two looks from one renderer,
 *    decided by which div a screen happened to wrap itself in.
 *
 * 3. Nothing else needs it. `bin/harvest-tables.php` buckets a table by the
 *    `aieo-t` token FIRST, so the census still reads these as migrated;
 *    `aieo-table.js` keys on 19 selectors and every one is `aieo-*`; and we
 *    emit no `check-column`, which is the one piece of `widefat` we would have
 *    missed.
 *
 * ⏪ ONE FILTER PUTS IT BACK. `aieo_table_legacy_chrome` returning true re-adds
 * `widefat striped` to every table, so if this lands badly on a screen nobody
 * screenshotted the answer is a snippet in a mu-plugin rather than a release.
 *
 * ── The metrics, and where they come from ────────────────────────────────────
 *
 * Read from `@keenthemes/ktui/src/components/table/table.css`, which is the
 * component SOURCE rather than a purged build, and confirmed against the
 * vendor's own rendered demo:
 *
 *     thead th   h-10 (40px) · px-4 (16px) · font-normal · align-middle
 *                bg-muted/40 · text-secondary-foreground · sentence case
 *     tbody td   px-4 py-3 (16/12) · align-middle · text-sm (14px)
 *     tr         border-b border-border          ← a rule per row, no zebra
 *     tbody tr:last-child   border-0
 *     hover      bg-muted/50
 *     tfoot      border-t · bg-muted/50 · font-medium
 *
 * 🧠 THE HEADER IS THE WHOLE DIFFERENCE. Ours was UPPERCASE, 600/700, grey,
 * 12px — the wp-admin convention, which shouts. Theirs is sentence case, 400,
 * near-black, the same size as the body, separated by a rule and a hairline of
 * tint. That one change is most of what "looks like their tables" means; the
 * rest is padding.
 *
 * 🚫 AND NO ZEBRA. Striping and row rules are two answers to one question —
 * "which line am I on" — and running both is noise. The design language
 * answers it with a rule plus a hover wash, which is also the answer that
 * survives a 26-column table where a stripe is a wall of grey.
 * ------------------------------------------------------------------ */

/* The card the table sits in. It is the SCROLL BOX rather than the <table>,
 * which is how the vendor does it too, and it is why we can keep
 * `border-collapse: collapse`: a radius on a collapsed table leaks its corner
 * cells past the curve, and `separate` re-introduces the double borders this
 * file spent the rest of its life removing. The class is emitted by
 * AIEO_Table::render() rather than matched with `:has()`, so the frame is a
 * fact about the markup and not a browser feature. */
.aieo-t-frame {
	background: var(--aieo-card, #fff);
	border: 1px solid var(--aieo-border, #ececee);
	border-radius: var(--aieo-radius-lg, 8px);
}

/* ---------------------------------------------------------------------
 * Table shell
 * ------------------------------------------------------------------ */

.aieo-t {
	/* DENSITY IS A TOKEN BLOCK, exactly like dark mode is a token block.
	 *
	 * Every metric below reads one of these five names, so `--compact` is one
	 * block that moves the whole table rather than four `font-size` overrides
	 * chasing four rules — which is what it was, and which is why the compact
	 * tier used to change the type size and NOT the horizontal padding.
	 *
	 * 61 of the product's 134 render sites already ask for `compact`, so this
	 * is a real tier carrying real screens and not a theoretical one. */
	--aieo-t-pad-x: 16px;
	--aieo-t-pad-y: 12px;
	--aieo-t-head-h: 40px;
	--aieo-t-font: var(--aieo-text-base, 14px);
	--aieo-t-head-font: var(--aieo-text-base, 14px);

	width: 100%;
	border-collapse: collapse;
	background: var(--aieo-card, #fff);
	color: var(--aieo-foreground, #09090b);
	font-size: var(--aieo-t-font);
	text-align: left;
}

/* Compact. The tier for a table that is wide before it is anything else —
 * insights_t20 is 26 columns — where the roomy rhythm above buys air the
 * reader pays for in horizontal scrolling. */
.aieo-t--compact {
	--aieo-t-pad-x: 10px;
	--aieo-t-pad-y: 7px;
	--aieo-t-head-h: 32px;
	--aieo-t-font: var(--aieo-text-sm, 13px);
	--aieo-t-head-font: var(--aieo-text-sm, 13px);
}

.aieo-t th.aieo-t-h {
	height: var(--aieo-t-head-h);
	padding: 0 var(--aieo-t-pad-x);
	background: var(--aieo-thead-bg, #fbfbfc);
	border-bottom: 1px solid var(--aieo-border, #ececee);
	color: var(--aieo-secondary-fg, #18181b);
	font-size: var(--aieo-t-head-font);
	font-weight: 400;
	text-transform: none;
	letter-spacing: 0;
	line-height: 1.3;
	vertical-align: middle;
	white-space: nowrap;
}

.aieo-t td.aieo-t-c,
.aieo-t th.aieo-t-c {
	padding: var(--aieo-t-pad-y) var(--aieo-t-pad-x);
	vertical-align: middle;
	line-height: 1.45;
}

/* A rule per row, and none under the last one — the frame's own border is
 * already there and two lines a pixel apart read as a mistake. */
.aieo-t tbody tr {
	border-bottom: 1px solid var(--aieo-border, #ececee);
}
.aieo-t tbody tr:last-child {
	border-bottom: 0;
}

/* The row under the cursor.
 *
 * Painted on the CELLS, not the <tr>: a row-level background is drawn behind a
 * cell-level one, and every row tone further down this file sets a cell
 * background, so a <tr> rule would light up exactly the rows that already have
 * something to say and no others.
 *
 * (0,3,3), which is deliberate rather than accidental — it has to out-rank the
 * row tones at (0,2,3), so hovering a flagged row still tells the reader where
 * the cursor is. The tone's left rule is a box-shadow and survives, so the row
 * keeps saying what it is while it says where you are.
 *
 * `:not(.aieo-t--bare)` is the exclusion that matters: a bare table is a
 * SETTINGS FORM drawn on a grid, and a hover wash there tells the reader that
 * a row of labels is a row of data they can act on. */
.aieo-t:not(.aieo-t--bare) tbody tr:hover > td,
.aieo-t:not(.aieo-t--bare) tbody tr:hover > th {
	background: var(--aieo-row-hover, #fafafa);
}

/* ---------------------------------------------------------------------
 * 🎨 THE SKIN: the same paint, for a table this component did NOT render.
 *
 * The day `widefat` left the component, every hand-written table in the
 * product became the only thing in wp-admin still wearing the old chrome —
 * and on several screens one sits directly beside a repainted one.
 *
 * ── How many, and why the census undercounts them ────────────────────────────
 *
 * `bin/harvest-tables.php` reports 276 distinct data tables, 275 on the
 * contract and 1 hand-written. **That 1 is wrong and the census cannot see
 * why.** It de-duplicates by CARD ANCHOR, so where a card holds a component
 * table AND a hand-written one — Conversion goals is exactly this — the pair
 * counts once and is bucketed by whichever was found first. The browser
 * harness caught it: `click-t0-page.mjs conversion_goals` asserts
 * `table.widefat:not(.aieo-t):not(.form-table)` is empty and reported 1.
 *
 * A source sweep finds ~35 `<table class="…widefat…">` sites in PHP. Most do
 * not render on this install (feature off, no data, another tab) which is the
 * other half of why the census reads 1. They will render on somebody's.
 *
 * ── So the selector does the work, not a sweep of 35 edits ───────────────────
 *
 * `.aieo-dmm-modules-panel` wraps every DMM module screen (`page.php`), and
 * that is exactly the set of screens `aieo-table.css` is enqueued on. One
 * scoped selector therefore reaches the whole tail with NO markup change, and
 * that matters for two reasons beyond effort:
 *
 *   · `class="widefat"` is ALSO WordPress's full-width utility for inputs —
 *     `<input class="widefat">`, `<textarea class="widefat">`. A mechanical
 *     find-and-replace over 35 sites would have hit those. `table.widefat`
 *     cannot.
 *   · a table on a page WITHOUT this stylesheet must keep `widefat`, or it
 *     renders with no chrome at all rather than with the old chrome. Scoping
 *     to the panel makes that impossible by construction.
 *
 * `:not(.form-table)` because a `form-table` is WordPress's settings LAYOUT,
 * not data — the census excludes 221 of them by intent, and so does this.
 *
 * `.aieo-t-skin` stays as the explicit opt-in for tables OUTSIDE that panel:
 * two built in JavaScript from an AJAX payload (the `form_insights` drill-down
 * dialog and the attribution grid) and the two GSC lists, whose markup is
 * clearer for saying so.
 *
 * 🚨 NEITHER IS `aieo-t`, AND THAT IS THE POINT. The census buckets "on the
 * contract" by that exact token, so painting these with it would move the
 * count to 276/276 with nothing migrated — a number that lies in the
 * flattering direction. A LOOK IS NOT A MIGRATION. `harvest-tables.php` gained
 * a `skinned` bucket to keep the two apart.
 *
 * ⏸ One is deliberately NOT covered: the recommendations-cache status panel. A
 * probe on 2026-09-01 loaded that page and found NO aieo stylesheet on it at
 * all — it is a submenu of `ai-eshop-optimizer` that
 * `aieo_reorder_admin_submenu()` removes, and it is outside the panel. It
 * needs the stylesheet before it needs the skin, and the note saying so lives
 * beside its markup.
 *
 * The rules match BARE `th` and `td`. A hand-written table has no `aieo-t-h`
 * or `aieo-t-c` on its cells, which is exactly why the shell above cannot
 * simply be pointed at it.
 * ------------------------------------------------------------------ */

.aieo-t-skin,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) {
	--aieo-t-pad-x: 16px;
	--aieo-t-pad-y: 12px;
	--aieo-t-head-h: 40px;
	--aieo-t-font: var(--aieo-text-base, 14px);

	width: 100%;
	border-collapse: collapse;
	background: var(--aieo-card, #fff);
	color: var(--aieo-foreground, #09090b);
	font-size: var(--aieo-t-font);
	text-align: left;
	border: 1px solid var(--aieo-border, #ececee);
	border-radius: var(--aieo-radius-lg, 8px);
	overflow: hidden;
}

/* A skinned table inside the component's own scroll frame would draw a second
   border a pixel inside the first. The frame is the card there; the table is
   only its contents. */
.aieo-t-frame > .aieo-t-skin,
.aieo-t-frame > table.widefat:not(.aieo-t):not(.form-table) {
	border: 0;
	border-radius: 0;
}

.aieo-t-skin--compact {
	--aieo-t-pad-x: 10px;
	--aieo-t-pad-y: 7px;
	--aieo-t-head-h: 32px;
	--aieo-t-font: var(--aieo-text-sm, 13px);
}

.aieo-t-skin thead th,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) thead th {
	height: var(--aieo-t-head-h);
	padding: 0 var(--aieo-t-pad-x);
	background: var(--aieo-thead-bg, #fbfbfc);
	border-bottom: 1px solid var(--aieo-border, #ececee);
	color: var(--aieo-secondary-fg, #18181b);
	font-size: var(--aieo-t-font);
	font-weight: 400;
	text-align: left;
	text-transform: none;
	letter-spacing: 0;
	vertical-align: middle;
	white-space: nowrap;
}

.aieo-t-skin tbody td,
.aieo-t-skin tbody th,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody td,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody th {
	padding: var(--aieo-t-pad-y) var(--aieo-t-pad-x);
	vertical-align: middle;
	line-height: 1.45;
}

/* A `<th scope="row">` is a LABEL for its row, not a column heading, so it
 * takes the body's rhythm and only its weight says what it is. The
 * recommendations-cache panel and four of the Tools blocks are entirely this
 * shape. */
.aieo-t-skin tbody th[scope="row"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody th[scope="row"] {
	background: none;
	font-weight: 600;
	text-align: left;
}

.aieo-t-skin tbody tr,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody tr {
	border-bottom: 1px solid var(--aieo-border, #ececee);
}
.aieo-t-skin tbody tr:last-child,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody tr:last-child {
	border-bottom: 0;
}
.aieo-t-skin tbody tr:hover > td,
.aieo-t-skin tbody tr:hover > th,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody tr:hover > td,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) tbody tr:hover > th {
	background: var(--aieo-row-hover, #fafafa);
}

/* 🚫 AND NO ZEBRA. These tables carry `striped`, whose core rule is
   `.striped > tbody > :nth-child(odd)` — NOT scoped to `widefat`, so dropping
   `widefat` from the component was never going to reach it. Row rules and
   stripes are two answers to one question and running both is noise. */
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table).striped > tbody > tr:nth-child(odd),
.aieo-t-skin.striped > tbody > tr:nth-child(odd) {
	background-color: transparent;
}

/* These lists sort by RELOADING with a query argument, so the control is an
 * <a> in the heading rather than the whole <th>. It still has to read as the
 * same control. */
.aieo-t-skin thead th a,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) thead th a {
	color: inherit;
	text-decoration: none;
}
.aieo-t-skin thead th a:hover { color: var(--aieo-foreground, #09090b); }

/* ---------------------------------------------------------------------
 * Alignment and wrapping
 * ------------------------------------------------------------------ */

.aieo-t-a--left   { text-align: left; }
.aieo-t-a--right  { text-align: right; }
.aieo-t-a--center { text-align: center; }
.aieo-t-nowrap    { white-space: nowrap; }

/* 🚨 THE SAME TOKENS AGAIN, HEAVIER, BECAUSE WORDPRESS CORE OUTRANKS THEM.
 *
 * `wp-admin/css/common.css` carries
 *
 *     .widefat tbody td.check-column, .widefat tfoot td,
 *     .widefat th, .widefat thead td { text-align: left; }
 *
 * at specificity (0,1,1), and every table we render wears `widefat` on purpose
 * so it inherits admin furniture. The bare token above is (0,1,0), so core won
 * on EVERY header cell: the renderer emitted `aieo-t-a--right` on the <th> and
 * on the <td>, the cell obeyed and the header did not.
 *
 * The reader's experience of that, reported by the operator 2026-08-29 as "you
 * cannot make sense which figure belongs to which heading": in a wide table the
 * number sits at the right edge of its column while its heading sits at the
 * left edge of the same column, hundreds of pixels away and immediately beside
 * the NEXT heading. On competition_insights the value 16 for "Competitions
 * entered" rendered directly under the word "Wins". Every numeric column in the
 * product read one column to the right of the truth.
 *
 * (0,2,1) reclaims it. Scoped under `.aieo-t` rather than made `!important`, so
 * this is still a normal cascade a Portal or Metronic renderer can override.
 * `tfoot` matters as much as `thead` here: core's selector names it too, so a
 * totals row was mis-aligning against the column it totals.
 */
.aieo-t th.aieo-t-a--left,
.aieo-t td.aieo-t-a--left   { text-align: left; }
.aieo-t th.aieo-t-a--right,
.aieo-t td.aieo-t-a--right  { text-align: right; }
.aieo-t th.aieo-t-a--center,
.aieo-t td.aieo-t-a--center { text-align: center; }

/* Numbers line up on the decimal point only when they share a glyph width. */
.aieo-t-c--id,
.aieo-t-c--integer,
.aieo-t-c--decimal,
.aieo-t-c--money,
.aieo-t-c--percent {
	font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------------
 * Value tone. Replaces inline colours, and is what threshold bands resolve to.
 * ------------------------------------------------------------------ */

/* `danger` STAYS `danger` here, and it is not an oversight. This is the VALUE
 * axis: a figure that is bad is not an action that destroys something. The
 * variant axis retired the word; this one keeps it, and the two words are the
 * distinction. See AIEO_Variants. */
.aieo-t-tone--secondary { color: var(--aieo-tone-secondary, #646970); }
.aieo-t-tone--muted     { color: var(--aieo-tone-muted, #9ca3af); }
.aieo-t-tone--danger    { color: var(--aieo-tone-danger, #b91c1c); font-weight: 600; }
.aieo-t-tone--warning   { color: var(--aieo-tone-warning, #a16207); font-weight: 600; }
.aieo-t-tone--success   { color: var(--aieo-tone-success, #166534); font-weight: 600; }
.aieo-t-tone--info      { color: var(--aieo-tone-info, #6d28d9); font-weight: 600; }

/* ---------------------------------------------------------------------
 * Cell parts
 * ------------------------------------------------------------------ */

/* The subline: the second line of a composite cell. Census finding is that
 * this is the norm rather than the exception, so it is a first-class part. */
.aieo-t-sub {
	display: block;
	font-size: var(--aieo-text-2xs, 11px);
	color: var(--aieo-muted-fg, #646970);
	margin-top: 2px;
}

.aieo-t-suffix {
	color: var(--aieo-muted-fg, #646970);
	font-size: var(--aieo-text-xs, 12px);
}

.aieo-t-code {
	font-size: var(--aieo-text-xs, 12px);
	background: var(--aieo-muted, #f6f7f7);
	border: 1px solid var(--aieo-input, #e4e4e7);
	border-radius: 3px;
	padding: 1px 5px;
	word-break: break-all;
}

/* The empty marker reads as absent, never as data. */
.aieo-t-empty {
	color: var(--aieo-tone-muted, #9ca3af);
}

.aieo-t-yes { color: var(--aieo-tone-success, #166534); font-weight: 700; }
.aieo-t-no  { color: var(--aieo-tone-muted, #9ca3af); }

.aieo-t-trunc { cursor: help; }

/* Chips, for list columns. */
.aieo-t-chip {
	display: inline-block;
	font-size: var(--aieo-text-2xs, 11px);
	background: var(--aieo-muted, #f6f7f7);
	border: 1px solid var(--aieo-input, #e4e4e7);
	border-radius: var(--aieo-radius-pill, 10px);
	padding: 1px 8px;
	margin: 0 3px 3px 0;
}

/* A row thumbnail. object-fit so a portrait and a landscape product photo
   occupy the same box and the column stays a column. */
.aieo-t-thumb {
	display: block;
	object-fit: cover;
	border-radius: var(--aieo-radius-sm, 3px);
	background: var(--aieo-muted, #f0f0f1);
}

/* A chip carrying its own tooltip. Without the cue the tooltip is only
   discoverable by hovering something that gives no reason to hover it. */
.aieo-t-chip--tip {
	cursor: help;
	border-bottom-style: dotted;
	border-bottom-width: 2px;
}

/* ---------------------------------------------------------------------
 * Badges. One place, so the six inline band schemes collapse into tokens.
 * ------------------------------------------------------------------ */

/* ⚖️ THE SAME BADGE AS `aieo-c-badge--sm`, and the geometry below is copied
 * from it deliberately rather than approximated. The two files share the
 * colour tokens already; sharing only the colours left a report's status pill
 * a 10px-radius capsule and a questionnaire's the same colour in a 4px-radius
 * chip, which is two badges, not one design language.
 *
 * The CLASS NAME stays `aieo-t-badge`, and that is not laziness: the renderer
 * also emits `aieo-t-badge-v--{value}` beside it so a screen can style one
 * particular status, and `tone_key` means some of those values come out of the
 * DATABASE at render time. Renaming the family would break hooks that cannot
 * be found by grepping the source. Geometry converges; the name does not. */
.aieo-t-badge {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 5px;
	min-height: 20px;
	padding: 1px 6px;
	border-radius: var(--aieo-radius-sm, 4px);
	font-size: var(--aieo-text-2xs, 11px);
	font-weight: 500;
	line-height: 1.5;
	white-space: nowrap;
}

/* THE CANONICAL NAMES. `danger` and `neutral` are gone from this file and from
 * the markup: AIEO_Variants::badge() rewrites them before a class is ever
 * emitted, so a declaration that still says `danger` keeps working and still
 * cannot put the retired word on the page.
 *
 * `info` MOVED FROM BLUE TO VIOLET, and it is the one visible colour change in
 * this wave. It was blue-100/blue-800 -- the same hue as `primary` -- so an
 * informational badge and a primary one were telling the reader the same thing
 * in the same colour. Violet is what the design language uses for information
 * precisely so the two are distinguishable at a glance. */
.aieo-t-badge--primary     { background: var(--aieo-primary-soft, #dbeafe);     color: var(--aieo-primary-soft-fg, #1e40af); }
.aieo-t-badge--secondary   { background: var(--aieo-secondary-soft, #f0f0f1);   color: var(--aieo-secondary-soft-fg, #50575e); }
.aieo-t-badge--success     { background: var(--aieo-success-soft, #dcfce7);     color: var(--aieo-success-soft-fg, #166534); }
.aieo-t-badge--warning     { background: var(--aieo-warning-soft, #fef3c7);     color: var(--aieo-warning-soft-fg, #92400e); }
.aieo-t-badge--destructive { background: var(--aieo-destructive-soft, #fee2e2); color: var(--aieo-destructive-soft-fg, #991b1b); }
.aieo-t-badge--info        { background: var(--aieo-info-soft, #ede9fe);        color: var(--aieo-info-soft-fg, #5b21b6); }
.aieo-t-badge--mono        { background: var(--aieo-mono-soft, #f0f0f1);        color: var(--aieo-mono-soft-fg, #18181b); }

/* Ours, not the vendor's, and kept on purpose: a row that is STILL LISTED but
 * no longer live -- a captcha rule for a form whose plugin is gone, an unmapped
 * branch. `secondary` is the wrong word for it, because secondary is a live
 * thing of lesser importance and this is a dead thing still on the page. */
.aieo-t-badge--muted   { background: var(--aieo-muted, #f6f7f7); color: var(--aieo-tone-muted, #9ca3af); }

/* ---------------------------------------------------------------------
 * Bars
 * ------------------------------------------------------------------ */

.aieo-t-bar {
	display: inline-block;
	vertical-align: middle;
	width: 90px;
	height: 10px;
	background: var(--aieo-muted, #f3f4f6);
	border-radius: var(--aieo-radius-pill, 5px);
	overflow: hidden;
}

.aieo-t-bar-fill {
	display: block;
	height: 100%;
	background: var(--aieo-primary, #2271b1);
}

/* A bar takes its fill from the cell tone, so a distribution whose rows mean
   different things (happy / neutral / unhappy) reads at a glance. Declared
   via the column-level `tone_key`; the default blue stays for untoned bars. */
.aieo-t-tone--success .aieo-t-bar-fill { background: var(--aieo-tone-success-fill, #22c55e); }
.aieo-t-tone--warning .aieo-t-bar-fill { background: var(--aieo-tone-warning-fill, #f5b800); }
.aieo-t-tone--danger  .aieo-t-bar-fill { background: var(--aieo-tone-danger-fill, #ef4444); }
.aieo-t-tone--info    .aieo-t-bar-fill { background: var(--aieo-tone-info-fill, #4f46e5); }

.aieo-t-bar-val {
	margin-left: 8px;
	font-size: 12px;
	vertical-align: middle;
}

/* ---------------------------------------------------------------------
 * Diff
 * ------------------------------------------------------------------ */

.aieo-t-diff-from  { color: var(--aieo-tone-muted, #9ca3af); text-decoration: line-through; }
.aieo-t-diff-arrow { margin: 0 6px; color: var(--aieo-tone-muted, #9ca3af); }

/* ---------------------------------------------------------------------
 * Sorting
 * ------------------------------------------------------------------ */

.aieo-t-sortable {
	cursor: pointer;
	user-select: none;
	/* The geometry of the sort control, as three alpha combinations of ONE
	 * shape: a chevron pair, the active half opaque and the other half faded.
	 *
	 * Drawn here rather than borrowed. The design language's own arrows are
	 * vendor path data in a vendor stylesheet, and this plugin ships GPL — so
	 * the look is reproduced and the asset is not. Two triangles is not a lot
	 * to write down.
	 *
	 * 🎨 THEY ARE MASKS, NOT IMAGES, and that is the whole reason this is
	 * worth ten lines: a mask takes its colour from `background-color`, so the
	 * arrow is `currentColor` and follows the header's token — including into
	 * `.dark`, where a baked-in grey would have been a light arrow on a light
	 * background waiting to be discovered by somebody else. */
	--aieo-t-sort-none: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' viewBox='0 0 8 13'%3E%3Cpath d='M4 1 7.4 5 0.6 5Z' fill='%23000'/%3E%3Cpath d='M4 12 0.6 8 7.4 8Z' fill='%23000'/%3E%3C/svg%3E");
	--aieo-t-sort-asc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' viewBox='0 0 8 13'%3E%3Cpath d='M4 1 7.4 5 0.6 5Z' fill='%23000'/%3E%3Cpath d='M4 12 0.6 8 7.4 8Z' fill='%23000' fill-opacity='.25'/%3E%3C/svg%3E");
	--aieo-t-sort-desc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='13' viewBox='0 0 8 13'%3E%3Cpath d='M4 1 7.4 5 0.6 5Z' fill='%23000' fill-opacity='.25'/%3E%3Cpath d='M4 12 0.6 8 7.4 8Z' fill='%23000'/%3E%3C/svg%3E");
}

.aieo-t-sortable:hover {
	color: var(--aieo-foreground, #09090b);
}

/* ⚠️ THE GLYPHS ARE THE FALLBACK AND THEY COME FIRST, on purpose.
 *
 * A `mask-image` a browser does not understand is not ignored — the
 * declaration drops and what is left is an 8×13 block of solid `currentColor`
 * beside every sortable heading. So the masked version lives inside
 * `@supports` and this is what is painted when it does not apply. Same lesson
 * as the Panel's audit, one property over: check what the BASE rule does
 * before deciding a missing feature degrades quietly. */
.aieo-t-sortable::after {
	content: "\21C5";
	margin-left: 6px;
	opacity: .4;
	font-weight: 400;
}

.aieo-t-sortable.aieo-t-asc::after  { content: "\25B2"; opacity: .8; }
.aieo-t-sortable.aieo-t-desc::after { content: "\25BC"; opacity: .8; }

@supports ( mask-image: none ) or ( -webkit-mask-image: none ) {
	.aieo-t-sortable::after {
		content: "";
		display: inline-block;
		width: 8px;
		height: 13px;
		margin-left: 7px;
		vertical-align: -2px;
		opacity: .5;
		background-color: currentColor;
		-webkit-mask: var(--aieo-t-sort-none) center / 8px 13px no-repeat;
		mask: var(--aieo-t-sort-none) center / 8px 13px no-repeat;
	}

	/* `content` has to be reset on each of these too: they are (0,2,0) and the
	 * rule above is (0,1,0), so a sorted header would otherwise keep its
	 * glyph AND grow a mask box next to it. */
	.aieo-t-sortable.aieo-t-asc::after {
		content: "";
		opacity: .9;
		-webkit-mask-image: var(--aieo-t-sort-asc);
		mask-image: var(--aieo-t-sort-asc);
	}

	.aieo-t-sortable.aieo-t-desc::after {
		content: "";
		opacity: .9;
		-webkit-mask-image: var(--aieo-t-sort-desc);
		mask-image: var(--aieo-t-sort-desc);
	}
}

/* ---------------------------------------------------------------------
 * Empty state, caps, totals
 * ------------------------------------------------------------------ */

.aieo-t-empty-row td {
	text-align: center;
	color: var(--aieo-muted-fg, #71717a);
	padding: 28px 16px;
}

.aieo-t-cap {
	margin-top: 6px;
	font-style: italic;
}

.aieo-t-totals {
	display: flex;
	flex-wrap: wrap;
	gap: 18px;
	margin: 10px 0;
	padding: 12px 16px;
	background: var(--aieo-thead-bg, #fbfbfc);
	border: 1px solid var(--aieo-border, #ececee);
	border-radius: var(--aieo-radius-lg, 8px);
}

/* The one place UPPERCASE survives, and it is not an oversight. A totals strip
 * is a row of CAPTIONS over figures, not a table header — nothing sits under it
 * in a column, so the label has to be told apart from the number beside it
 * rather than from the column below it. */
.aieo-t-total-l {
	display: block;
	font-size: var(--aieo-text-2xs, 11px);
	text-transform: uppercase;
	letter-spacing: .03em;
	color: var(--aieo-muted-fg, #71717a);
}

.aieo-t-total-v {
	display: block;
	font-size: 16px;
	font-weight: 600;
}

/* ---------------------------------------------------------------------
 * Interactive cells
 * ------------------------------------------------------------------ */

/* An in-cell editor. Styled HERE rather than handed the control layer's
 * classes, because `aieo-c-input` is `display:block; width:100%` and a cell is
 * not a form field's column — and because `aieo-t-edit` is a BEHAVIOUR hook the
 * client saves through, so the fewer classes riding on it the better. Same
 * tokens, so it still agrees with everything around it. */
.aieo-t-edit {
	box-sizing: border-box;
	width: 100%;
	max-width: 180px;
	min-height: 28px;
	padding: 2px 8px;
	background: var(--aieo-background, #fff);
	border: 1px solid var(--aieo-input, #e4e4e7);
	border-radius: var(--aieo-radius-md, 6px);
	color: var(--aieo-foreground, #09090b);
	font-size: var(--aieo-text-xs, 12px);
}
.aieo-t-edit:focus {
	outline: none;
	border-color: var(--aieo-ring, #a1a1aa);
	box-shadow: 0 0 0 3px rgba(161, 161, 170, .3);
}
.aieo-t-edit.aieo-t-edit-bad {
	border-color: var(--aieo-destructive, #dc2626);
}

/* 🚨 (0,2,0) AND IT HAS TO BE. `aieo-t-danger` is added BESIDE a variant class
 * — the renderer sets it from the action's `destructive` boolean, which is a
 * separate fact from what the action is called — so at (0,1,0) it merely TIES
 * with `.aieo-c-btn`'s own colour and the winner is whichever stylesheet the
 * page happened to print last. It happens to be this one, because `aieo-table`
 * declares `aieo-controls` as a dependency. That is not a thing to rely on: an
 * override that works because of an enqueue order breaks the day somebody
 * reorders two lines for an unrelated reason, and a destructive action that
 * paints like an ordinary one is the single worst way for this file to fail.
 *
 * The `:not()` is the other half: a `variant => destructive` action is already
 * SOLID red with white text, and re-colouring its label red on red is how it
 * would have become unreadable. This tints the quiet ones — a ghost "Delete"
 * beside a plain "Edit" — and leaves the loud one alone. */
.aieo-c-btn.aieo-t-danger:not(.aieo-c-btn--destructive) {
	color: var(--aieo-destructive, #dc2626);
	border-color: var(--aieo-destructive, #dc2626);
}
.aieo-c-btn.aieo-t-danger:not(.aieo-c-btn--destructive):hover,
.aieo-c-btn.aieo-t-danger:not(.aieo-c-btn--destructive):focus {
	background: var(--aieo-destructive-soft, #fee2e2);
	color: var(--aieo-destructive-soft-fg, #991b1b);
}

/* Still reachable by a hand-written table passing its own `class`, and by the
 * bulk bar's destructive buttons, so the bare token stays declared. */
.aieo-t-danger {
	color: var(--aieo-destructive, #dc2626);
	border-color: var(--aieo-destructive, #dc2626);
}

/* 🚨 AN ACTION THE HOST BLOCKED MUST STILL BE HOVERABLE, and the control layer
 * takes that away. `.aieo-c-btn[disabled] { pointer-events: none }` is right
 * for an ordinary disabled button and wrong for this one: the whole point of
 * the blocked state is that it SAYS WHY, and the reason lives in `title`, which
 * a browser will not show for an element that receives no pointer events. At
 * (0,2,0) this only TIES with the control layer's rule, and a tooltip that does
 * not appear looks exactly like a tooltip that was never set — the quietest
 * possible failure for the one control whose entire job is to explain itself.
 * (0,3,0) settles it without reference to which file loaded first. */
.aieo-c-btn.aieo-t-blocked[disabled],
.aieo-c-btn.aieo-t-blocked[aria-disabled="true"] {
	cursor: help;
	pointer-events: auto;
}

/* ---------------------------------------------------------------------
 * 🚨 THE `control` CELL RENDERS THE SCREEN'S OWN MARKUP, AND 139 OF THEM DO.
 *
 * `type => control` exists so a table whose ROW IS A FORM RECORD can keep its
 * own input, its own `name` and its own handler while the component owns
 * everything around it. That is the right trade and it is why the long tail of
 * hand-written forms could be converted at all — but it means the component
 * does not choose what those cells contain, and what they contain is very
 * often a WordPress `.button`.
 *
 * Measured 2026-09-01: 139 `control` cells. Repainting the row actions and
 * leaving these would have put a core-blue (or, under the menu group's accent,
 * a core-AMBER) button in the middle of a repainted row on screens like
 * Conversion goals, where "Analyse" is a `control` cell. One button in the
 * wrong language is more noticeable than a whole table in it.
 *
 * So the paint is mapped here rather than at 139 call sites. Nothing about the
 * markup changes, the screen's handler still binds to `.button` or to its own
 * hook, and a caller that genuinely wants core's button outside a table still
 * gets it — this is scoped to a cell of ours.
 *
 * ⚠️ THIS IS THE ONE BLOCK IN THIS FILE THAT USES `!important`, and the reason
 * it is allowed here and nowhere else is worth stating. The rest of the file
 * deliberately wins on specificity so a Portal or Metronic renderer can still
 * override it — but `.button` is a WORDPRESS class that no other renderer will
 * ever emit, so this block is a shim for markup we do not control, competing
 * with two sibling stylesheets that already use `!important`. It cannot bind
 * anybody's hands because it cannot match anybody else's markup.
 *
 * What it is competing with, both measured rather than assumed:
 *
 *   `.aieo-dmm-section .button.button-small`  — min-width / height / padding /
 *      font-size / font-weight / line-height, all `!important`. That rule has
 *      its own history (three filter buttons rendering as 200px slabs beside a
 *      dropdown) and narrowing it would re-expose these to the sizing it exists
 *      to escape. Specificity cannot beat `!important`.
 *
 *   `.aieo-dmm-modules-panel[data-aieo-group] .button:not(…):not(…)` — (0,5,0),
 *      the MENU COLOUR CODING: "border takes the accent, letters take the ink".
 *      That is a deliberate wayfinding feature and it is right for the screen's
 *      own chrome. 🚨 Inside a data table it is not: the table has its own
 *      vocabulary, and once the row actions moved onto `aieo-c-btn` — which
 *      that rule cannot match — an amber-bordered `control` button sat in the
 *      same row as a token-coloured row action. One button in the wrong
 *      language is more noticeable than a whole table in it. The accent stops
 *      at the table's edge; everywhere else on the screen it is untouched.
 * ------------------------------------------------------------------ */

.aieo-t td .button,
.aieo-t td .button-primary,
.aieo-t td .button-secondary,
.aieo-t td .button-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 5px;
	box-sizing: border-box;
	min-width: 0 !important;
	height: 28px !important;
	padding: 0 10px !important;
	border: 1px solid transparent;
	border-radius: var(--aieo-radius-md, 6px);
	font-size: var(--aieo-text-xs, 12px) !important;
	font-weight: 500 !important;
	line-height: 1 !important;
	white-space: nowrap;
	text-decoration: none;
	text-shadow: none;
	cursor: pointer;
	box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
	background: var(--aieo-secondary, #f4f4f5) !important;
	border-color: transparent !important;
	color: var(--aieo-secondary-fg, #18181b) !important;
}

.aieo-t td .button:hover,
.aieo-t td .button:focus {
	background: var(--aieo-accent, #f4f4f5) !important;
	color: var(--aieo-foreground, #09090b) !important;
}

.aieo-t td .button-primary,
.aieo-t td .button-primary:hover,
.aieo-t td .button-primary:focus {
	background: var(--aieo-primary, #3b82f6) !important;
	border-color: transparent !important;
	color: var(--aieo-primary-fg, #fff) !important;
}
.aieo-t td .button-primary:hover,
.aieo-t td .button-primary:focus { filter: brightness(.94); }

/* Core's link-shaped button is its own word for "an action that should not
 * compete", which is what `ghost` means here. */
.aieo-t td .button-link {
	background: transparent !important;
	box-shadow: none;
	color: var(--aieo-accent-fg, #18181b) !important;
}
.aieo-t td .button-link:hover { background: var(--aieo-accent, #f4f4f5) !important; }

.aieo-t td .button-link-delete,
.aieo-t td .button.button-link-delete,
.aieo-t td .button-link-delete:hover,
.aieo-t td .button.button-link-delete:hover {
	color: var(--aieo-destructive, #dc2626) !important;
}

.aieo-t td .button:focus-visible,
.aieo-t td .button-link:focus-visible {
	outline: none;
	border-color: var(--aieo-ring, #a1a1aa);
	box-shadow: 0 0 0 3px rgba(161, 161, 170, .3);
}

.aieo-t td .button:disabled,
.aieo-t td .button[disabled] {
	opacity: .5;
	cursor: not-allowed;
}

/* ---------------------------------------------------------------------
 * The same argument as the buttons above, for the FIELDS beside them.
 *
 * A `control` cell's input is the screen's own — `<input type="number">` with
 * its own name and its own handler — so it arrives with no class of ours and
 * lands on core's `forms.css`: a #949494 border, a 2px radius and a 40px
 * minimum height, inside a row whose buttons and badges now use ours. On
 * Conversion goals that is a "worth of one" box per row, eight of them.
 *
 * Keyed on the TYPE rather than on a class, because there is no class to key
 * on and the type is what core keyed on to take them in the first place.
 *
 * 🚫 WHAT IS DELIBERATELY NOT LISTED: `checkbox`, `radio`, `file`, `submit`,
 * `button` and `color`. A tick box is a native control the whole admin draws
 * the same way and a reader recognises by its shape; giving it a text field's
 * border and 34px height would make it a small grey square that no longer
 * reads as a tick box. The selector column depends on that.
 *
 * ⚠️ AND `select` KEEPS ITS BACKGROUND AND ITS RIGHT PADDING — that is where
 * core draws the dropdown arrow. Same trap as the `.wp-core-ui` block in
 * `aieo-controls.css`, one file over.
 * ------------------------------------------------------------------ */

.aieo-t td input[type="text"],
.aieo-t td input[type="search"],
.aieo-t td input[type="url"],
.aieo-t td input[type="email"],
.aieo-t td input[type="number"],
.aieo-t td input[type="date"],
.aieo-t td textarea,
.aieo-t td select,
.aieo-t-skin td input[type="text"],
.aieo-t-skin td input[type="number"],
.aieo-t-skin td input[type="date"],
.aieo-t-skin td select,
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="text"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="number"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="date"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td select {
	box-sizing: border-box;
	min-height: 0;
	/* 28px, the same as `aieo-c-btn--sm` and as `aieo-t-edit`. A field and the
	   button that acts on it sit on one line in a row action cluster, and two
	   heights two pixels apart read as a misalignment rather than as a
	   choice. */
	height: 28px;
	border: 1px solid var(--aieo-input, #e4e4e7);
	border-radius: var(--aieo-radius-md, 6px);
	box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
	color: var(--aieo-foreground, #09090b);
	font-size: var(--aieo-text-xs, 12px);
	line-height: normal;
}

.aieo-t td input[type="text"],
.aieo-t td input[type="search"],
.aieo-t td input[type="url"],
.aieo-t td input[type="email"],
.aieo-t td input[type="number"],
.aieo-t td input[type="date"],
.aieo-t-skin td input[type="text"],
.aieo-t-skin td input[type="number"],
.aieo-t-skin td input[type="date"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="text"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="number"],
.aieo-dmm-modules-panel table.widefat:not(.aieo-t):not(.form-table) td input[type="date"] {
	padding: 0 8px;
	background: var(--aieo-background, #fff);
}

/* A textarea is not 28px tall and never was. */
.aieo-t td textarea {
	height: auto;
	min-height: 60px;
	padding: 6px 8px;
	line-height: 1.45;
	background: var(--aieo-background, #fff);
}

/* ⚠️ NOT a bare `input:focus`. That matches the selector column's tick box,
 * and a 3px halo around a 16px control is a blob rather than a focus ring —
 * core already gives a checkbox its own, correctly sized. */
.aieo-t td input:not([type="checkbox"]):not([type="radio"]):focus,
.aieo-t td textarea:focus,
.aieo-t td select:focus,
.aieo-t-skin td input:not([type="checkbox"]):not([type="radio"]):focus,
.aieo-t-skin td select:focus {
	outline: none;
	border-color: var(--aieo-ring, #a1a1aa);
	box-shadow: 0 0 0 3px rgba(161, 161, 170, .3);
}

/* The row-action cluster. See AIEO_Table::action_cell() for why it is wrapped
 * at all: five actions, each followed by an injected `?` help control, is ten
 * inline boxes in the narrowest column of the table, and they wrapped one per
 * line. `inline-flex` keeps the column's own text-align in charge of where the
 * cluster sits; the gap replaces the accidental whitespace between two inline
 * elements, which is the only thing that was separating them before. */
.aieo-t-actions {
	display: inline-flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 4px 5px;
	vertical-align: middle;
}

.aieo-t-c--actions .aieo-c-btn { vertical-align: middle; }

/* The second <tr> for one record. It reads as an ANNEXE of the row above, so
 * it takes the header's wash rather than the card's white and drops the rule
 * that would otherwise separate it from its own parent row. This cell carries
 * no `aieo-t-c`, so it needs its padding stated: it is the component's own
 * markup rather than a column's. */
.aieo-t-detail > td {
	padding: var(--aieo-t-pad-y) var(--aieo-t-pad-x);
	background: var(--aieo-thead-bg, #fbfbfc);
	border-top: 0;
}

/* ---------------------------------------------------------------------
 * Row tones — the vocabulary `row_class` returns.
 *
 * Every screen that wanted a highlighted row hand-wrote an inline
 * background, and they all disagreed: #fff7ed with an inset left rule on
 * signals, #f3fbfd on the ROI wizard, #fef3c7 on a totals row that should
 * have been a <tfoot>. A caller returning `aieo-t-row--warning` now gets
 * the same row on every screen, `!important`-free.
 *
 * ⬆️ These used to have to out-specify `.striped`. They no longer do — there
 * is no zebra any more — but they DO have to lose to the hover rule at the top
 * of this file, which is why that one is written at (0,3,3) and these are at
 * (0,2,3). The order matters: a row says what it IS all the time and says
 * where the CURSOR is only while the cursor is on it.
 *
 * The left rule is part of the tone deliberately: a pale background alone
 * does not survive being skimmed, which is what the signals screen found
 * when its dormant rows went unread.
 * ------------------------------------------------------------------ */

/* 🚨 THE RULE GOES ON THE FIRST CELL, NOT ON EVERY CELL.
 *
 * `box-shadow: inset 3px 0 0` on `> td` draws a 3px bar down the left edge of
 * EVERY cell in the row, which is a row of column dividers rather than one mark
 * at the row's edge. It shipped that way and was survivable while rows were
 * 30px of zebra; at the new rhythm the signals screen — whose cells run to five
 * and six lines — rendered five amber bars per row and read as a table with
 * vertical rules that no other table has.
 *
 * `:first-child` covers a panel's `<th scope="row">` as well, which is why the
 * selector names both: a label/value panel toned by `row_class` would otherwise
 * mark its second cell and leave the label unmarked. */
.aieo-t tbody tr.aieo-t-row--danger > td,
.aieo-t tbody tr.aieo-t-row--danger > th {
	background: var(--aieo-tone-danger-wash, #fef2f2);
}
.aieo-t tbody tr.aieo-t-row--danger > :first-child {
	box-shadow: inset 3px 0 0 var(--aieo-tone-danger, #b91c1c);
}

.aieo-t tbody tr.aieo-t-row--warning > td,
.aieo-t tbody tr.aieo-t-row--warning > th {
	background: var(--aieo-tone-warning-wash, #fefce8);
}
.aieo-t tbody tr.aieo-t-row--warning > :first-child {
	box-shadow: inset 3px 0 0 var(--aieo-tone-warning, #a16207);
}

.aieo-t tbody tr.aieo-t-row--info > td,
.aieo-t tbody tr.aieo-t-row--info > th {
	background: var(--aieo-tone-info-wash, #f5f3ff);
}
.aieo-t tbody tr.aieo-t-row--info > :first-child {
	box-shadow: inset 3px 0 0 var(--aieo-tone-info, #6d28d9);
}

.aieo-t tbody tr.aieo-t-row--success > td,
.aieo-t tbody tr.aieo-t-row--success > th {
	background: var(--aieo-tone-success-wash, #f0fdf4);
}
.aieo-t tbody tr.aieo-t-row--success > :first-child {
	box-shadow: inset 3px 0 0 var(--aieo-tone-success, #166534);
}

/* The fifth tone, and the only one that does not draw attention: a row that
 * is STILL LISTED but no longer live — a captcha rule for a form whose plugin
 * is gone, an orphan the operator should see and probably remove. The screens
 * that had it wrote `style="opacity:.75"` inline. It gets no left rule,
 * because the rule is what says "read this one first" and this one is the
 * opposite of that. */
.aieo-t tbody tr.aieo-t-row--muted > td {
	opacity: .75;
}

/* ---------------------------------------------------------------------
 * Greek header labels split mid-word without this, which is a real defect
 * one screen already works around with its own inline style. Two of the
 * three installs run Greek as the primary locale.
 * ------------------------------------------------------------------ */

.aieo-t th.aieo-t-h {
	word-break: normal;
	overflow-wrap: normal;
}

/* ---------------------------------------------------------------
 * A real <tfoot> totals row.
 *
 * Opt-in via 'totals_at' => 'foot'. It exists because five screens
 * already hand-rolled one and the fulfilment screen wrote down the
 * requirement a strip cannot meet: never sorted, always pinned at
 * the bottom. The client sorter walks tbody.rows only, so a row here
 * is structurally immune to it.
 * ------------------------------------------------------------- */
/* ✅ THE STEP THAT NOTE WAS WAITING FOR HAS HAPPENED. This used to carry a
 * hardcoded `2px solid #c3c4c7`, core's own heavy-rule grey, with a comment
 * saying it would become a token in the same step that decided whether we keep
 * `widefat`. That step is this one, `widefat` is gone, and there is no longer a
 * core chrome for our footer to agree with — so it agrees with the design
 * language instead: the vendor's tfoot is `border-t bg-muted/50 font-medium`,
 * which is one hairline rather than a slab, because the ROW is separated from
 * the body by being the last thing under a rule, not by being underlined
 * twice. */
.aieo-t tfoot .aieo-t-foot > td {
	border-top: 1px solid var(--aieo-border, #ececee);
	background: var(--aieo-row-hover, #fafafa);
	font-weight: 500;
}
.aieo-t-foot-label {
	font-weight: 600;
	white-space: nowrap;
}

/* ---------------------------------------------------------------
 * T1: the server-paginated tier.
 *
 * Reuses WordPress core's .tablenav / .displaying-num /
 * .pagination-links so the pager looks like every other admin list
 * table, rather than becoming the product's FIFTH pager. See the
 * defect register B33.
 * ------------------------------------------------------------- */
.aieo-t-wrap { position: relative; }

.aieo-t-toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	margin: 0 0 10px;
}
.aieo-t-tool label {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: var(--aieo-text-xs, 12px);
	color: var(--aieo-muted-fg, #71717a);
}

/* 🚨 THE CONTROL LAYER IS BLOCK-LEVEL AND THIS ROW IS NOT.
 *
 * `.aieo-c-input` and `.aieo-c-select` are `display:block; width:100%`, which
 * is right for a stacked form field and wrong for a search box beside a
 * "rows per page" dropdown — unchecked, each control takes the full width of
 * the toolbar and the row becomes a column. Restated here rather than made
 * conditional in the control layer, because it is the TOOLBAR that is unusual,
 * not the field. */
.aieo-t-toolbar .aieo-c-input,
.aieo-t-toolbar .aieo-c-select,
.aieo-t-bulkbar .aieo-c-input,
.aieo-t-bulkbar .aieo-c-select {
	display: inline-block;
	width: auto;
}

.aieo-t-search { min-width: 240px; max-width: 100%; }

.aieo-t-nav {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin: 10px 0 0;
}
.aieo-t-nav .displaying-num { font-size: var(--aieo-text-xs, 12px); color: var(--aieo-muted-fg, #71717a); }
.aieo-t-filtered-of { font-size: var(--aieo-text-xs, 12px); color: var(--aieo-tone-muted, #a1a1aa); }
.aieo-t-page-of {
	margin: 0 6px;
	font-size: var(--aieo-text-xs, 12px);
	color: var(--aieo-muted-fg, #71717a);
	font-variant-numeric: tabular-nums;
}
.aieo-t-nav .pagination-links { display: inline-flex; align-items: center; gap: 4px; }

/* The pager keeps core's `.tablenav` / `.displaying-num` / `.pagination-links`
 * STRUCTURE — that was B33's point, one pager and not a fifth — while its
 * buttons paint with ours. Structure and paint are different questions and
 * this is the line between them. Core gives `.tablenav` a 30px height and a
 * clearfix that leave the row half-collapsed once its children are flex items. */
.aieo-t-nav.tablenav {
	height: auto;
	margin: 10px 0 0;
	padding: 0;
}
.aieo-t-nav .aieo-c-btn { min-width: 30px; padding: 0 8px; }

/* The busy state dims but never HIDES: a table that disappears while it
 * reloads makes every page change feel like a page load, and on a fast
 * report it flashes. Pointer-events off is what stops a second click
 * queueing behind the first. */
.aieo-t-busy { opacity: .55; transition: opacity .12s ease; }
.aieo-t-busy .aieo-t-toolbar,
.aieo-t-busy .aieo-t-nav,
.aieo-t-busy table { pointer-events: none; }

/* ---------------------------------------------------------------
 * The bulk-action toolbar (item 5): buttons over the ticked
 * selection, or — with the all-matching toggle — over everything
 * the current filter matches, walked server-side in chunks.
 * ------------------------------------------------------------- */
.aieo-t-bulkbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin: 0 0 10px;
}
.aieo-t-bulk-all { font-size: 12px; color: var(--aieo-muted-fg, #646970); }
.aieo-t-bulk-count { font-size: 12px; color: var(--aieo-muted-fg, #646970); }
.aieo-t-bulk-status { font-size: 12px; color: var(--aieo-foreground, #1d2327); }

/* A parameterised action (G1): the value picker travels WITH its button as
 * one flex item, so wrapping never strands a picker above its verb. */
.aieo-t-bulk-with { display: inline-flex; align-items: center; gap: 6px; }
.aieo-t-bulk-param { max-width: 260px; }
select.aieo-t-bulk-param { min-width: 160px; }

.aieo-t-error { margin: 0 0 10px; padding: 6px 10px; }

/* A server-sorted header is still a button, but the client asks the
 * SERVER to re-sort rather than reordering the rows on screen. */
.aieo-t--server th.aieo-t-sortable { cursor: pointer; }

/* ---------------------------------------------------------------
 * The per-column filter panel.
 *
 * Six control kinds, which is a measured number rather than a guess:
 * a census of the 22 reports being replaced found exactly these six
 * across all 213 filterable columns. `checkbox` exists in the vendor
 * schema and is used on none of them, so it is not built.
 * ------------------------------------------------------------- */
.aieo-t-filters {
	display: flex;
	flex-wrap: wrap;
	align-items: flex-end;
	gap: 10px 14px;
	margin: 0 0 12px;
	padding: 12px 14px;
	background: var(--aieo-thead-bg, #fbfbfc);
	border: 1px solid var(--aieo-border, #ececee);
	border-radius: var(--aieo-radius-lg, 8px);
}
.aieo-t-filter { display: flex; flex-direction: column; gap: 4px; }
/* A filter's caption is a LABEL over a control, which is the shape the control
 * layer already has a rule for — so it takes that size and weight rather than
 * the small-caps the table header just gave up. */
.aieo-t-filter-l {
	font-size: var(--aieo-text-xs, 12px);
	font-weight: 500;
	text-transform: none;
	letter-spacing: 0;
	color: var(--aieo-muted-fg, #71717a);
}
.aieo-t-filter-c { min-width: 130px; max-width: 220px; }
.aieo-t-filter[data-kind="number_range"],
.aieo-t-filter[data-kind="date_range"] {
	flex-direction: row;
	align-items: center;
	flex-wrap: wrap;
	gap: 4px;
}
.aieo-t-filter[data-kind="number_range"] .aieo-t-filter-l,
.aieo-t-filter[data-kind="date_range"] .aieo-t-filter-l {
	flex: 0 0 100%;
}
.aieo-t-filter[data-kind="number_range"] .aieo-t-filter-c,
.aieo-t-filter[data-kind="date_range"] .aieo-t-filter-c { min-width: 0; width: 130px; }
.aieo-t-filter-sep { color: var(--aieo-tone-muted, #a1a1aa); }

/* ── The one filter control that is not ours ───────────────────────────
 *
 * A `select` filter is handed to Select2 (B77: a native multi-select needed
 * ctrl+click, ate vertical space, and rendered EMPTY until clicked because its
 * options load on focus). Select2 replaces the <select> with its own DOM, so
 * `aieo-c-select` lands on an element that is one pixel tall and hidden, and
 * the visible box is `.select2-selection` wearing dmm.css's chrome.
 *
 * ⚠️ SCOPED TO `.aieo-t-filters` ON PURPOSE. dmm.css carries a long, hard-won
 * Select2 block — a hard reset with `!important`, a no-stylesheet fallback,
 * chip layout for multis — shared with the brand-gift, brand-discount and
 * catalogue-importer forms. Retuning that globally to match one filter row is
 * a change to three screens nobody asked about. This aligns the box inside a
 * TABLE FILTER PANEL and leaves the rest alone. */
.aieo-t-filters .select2-container--default .select2-selection {
	min-height: 34px;
	border: 1px solid var(--aieo-input, #e4e4e7);
	border-radius: var(--aieo-radius-md, 6px);
	background: var(--aieo-background, #fff);
	box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.aieo-t-filters .select2-container--default .select2-selection--single .select2-selection__rendered {
	line-height: 32px;
	color: var(--aieo-foreground, #09090b);
	font-size: var(--aieo-text-sm, 13px);
}
.aieo-t-filters .select2-container--default .select2-selection--single .select2-selection__arrow {
	height: 32px;
}
.aieo-t-filters .select2-container--default.select2-container--focus .select2-selection {
	border-color: var(--aieo-ring, #a1a1aa);
	box-shadow: 0 0 0 3px rgba(161, 161, 170, .3);
}

/* Apply is deliberate and separate: every filter change is a fresh query
 * over the whole dataset, so a reader setting three filters pays once. */
.aieo-t-filter-actions {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-left: auto;
}

/* ── The card the table sits in ────────────────────────────────────────
   AIEO_Table::card() renders these, so the spacing a screen used to set
   inline (margin:14px 0 6px, font-size:13px on a sub-heading, and four
   different max-widths) has one home. */
.aieo-t-card-h { margin: 0 0 6px; }
h4.aieo-t-card-h { margin: 18px 0 6px; font-size: 13px; }
.aieo-t-card-d { margin: 0 0 10px; }
.aieo-t-card-f { margin: 10px 0 0; }

/* ── The label/value panel ─────────────────────────────────────────────
   `head => false`. Four shipped status panels are two columns of "name,
   then the value" with no header row at all, the value cell often holding
   a paragraph or a live counter. `row_header` makes the first cell a
   <th scope="row">, which is what those screens already wrote and the only
   thing left associating a value with its label once the header is gone. */
.aieo-t--panel tbody th {
	font-weight: 600;
	width: 260px;
}
/* 🚨 `.aieo-t-c` IS IN THIS SELECTOR FOR A REASON, and it is the reason the
   whole file exists. The shell now centres cells — `.aieo-t td.aieo-t-c`, which
   is (0,2,1) — and a panel's value cell routinely holds a paragraph or a live
   counter, where centring a block of text against a one-line label reads as
   drift. Written as `.aieo-t--panel tbody td` this rule is (0,1,2) and LOSES,
   silently, on exactly the four screens nobody opens twice a week. */
.aieo-t--panel tbody th.aieo-t-c,
.aieo-t--panel tbody td.aieo-t-c,
.aieo-t--panel tbody th,
.aieo-t--panel tbody td { vertical-align: top; }

/* ── Cells the owning screen renders ───────────────────────────────────
   `control`. The row IS a form record: the input keeps its own name, id and
   handler, and only the furniture around it belongs to the component. The
   spacing is here so sixteen converted forms stop each choosing their own. */
.aieo-t-c--control > input[type="text"],
.aieo-t-c--control > input[type="url"],
.aieo-t-c--control > input[type="number"],
.aieo-t-c--control > select { max-width: 100%; }
.aieo-t-c--control .description { margin: 4px 0 0; }

/* ── The layout grid ───────────────────────────────────────────────────
   `bare => true`. A table whose job is to line up a column of labels
   against a column of controls inside a settings form. It is ours and it
   is a table, but it must not read as DATA: striping a settings form is
   how a conversion turns into a visible regression. The screens that had
   this shape wrote `border:0;background:transparent` on every cell by
   hand, plus their own per-cell padding, which is why it lives here now. */
.aieo-t--bare,
.aieo-t--bare tbody tr,
.aieo-t--bare tbody tr:nth-child(odd),
.aieo-t--bare tbody th,
.aieo-t--bare tbody td {
	border: 0;
	background: transparent;
	box-shadow: none;
}
.aieo-t--bare { max-width: 560px; }
/* Same (0,2,x) trap as the panel above: the shell's cell padding is
   `.aieo-t td.aieo-t-c`, so a bare table's own padding has to name the cell
   class too or a settings form silently inherits a data table's 16px gutters
   and its label column stops lining up with the form's left edge. */
.aieo-t--bare tbody th.aieo-t-c,
.aieo-t--bare tbody td.aieo-t-c,
.aieo-t--bare tbody th,
.aieo-t--bare tbody td { padding: 6px 8px 6px 0; }
.aieo-t--bare tbody td.aieo-t-c:last-child,
.aieo-t--bare tbody td:last-child { padding-right: 0; }
.aieo-t--bare.aieo-t--panel tbody th { width: 140px; font-weight: 400; }

/* 🚨 BARE + NARROW IS NOT A SETTINGS FORM, IT IS A CARD'S CONTENTS, AND THE
   RULE ABOVE IS WRONG FOR IT. `bare` zeroes the outer padding on purpose: a
   label column in a settings form has to line up with the form's own left
   edge, so a first cell with padding would look indented by mistake.

   `micro_bar_table()` reuses the same variant for the distribution cards on
   Surveys, Badges and the customer profile — label / bar / count INSIDE a
   bordered card. There the identical rule reads as a defect: the label starts
   flush against the card border and the count ends flush against the other
   one, so the text appears to be falling out of its own box.

   `narrow` is what separates them. It is set by every card-embedded caller
   and by none of the settings-form ones, so it is the honest discriminator —
   better than a new modifier nobody would remember to pass. Specificity
   (0,2,2) and (0,3,2) beats both the bare rules above and the shell's
   `.aieo-t td.aieo-t-c` at (0,2,1), regardless of order. */
.aieo-t--bare.aieo-t--narrow tbody th,
.aieo-t--bare.aieo-t--narrow tbody td { padding: 6px 10px; }
.aieo-t--bare.aieo-t--narrow tbody td:last-child { padding-right: 10px; }

/* A bare table has no max-width reason inside a card: 560px truncates a
   half-width grid cell that is already narrower than that. */
.aieo-t--bare.aieo-t--narrow { max-width: none; }

/* `narrow => true`. A table living in a narrow column — half of a
   display:flex pair. The scroll wrapper's blanket `min-width: 900px`
   (dmm.css `.aieo-dmm-scroll-x > table`) is right for a full-width table
   and wrong here: two 900px minimums cannot share one viewport, so the
   second table overflows its own scroll box and its last column hides
   behind a scrollbar nobody notices. The 0,2,1 specificity beats the
   wrapper rule's 0,1,1 regardless of stylesheet order. */
.aieo-dmm-scroll-x > table.aieo-t--narrow { min-width: 0; }

/* ---------------------------------------------------------------------
 * Questionnaire results: the controls, and tinting a whole row by its change.
 *
 * The row tint is scoped to THIS screen on purpose. Colouring an entire line
 * from one cell is a strong claim — it says the row as a whole got better or
 * worse — and that is only true where the table has exactly one column
 * carrying a verdict. Every other report has several toned columns, and
 * tinting rows there would be four claims fighting over one background.
 *
 * Pale on purpose: at full strength the tint competes with the tone colour on
 * the number itself, and the number is the thing being read. The hues are the
 * existing success/danger tokens, so a palette change moves these with it.
 * ------------------------------------------------------------------ */

.aieo-qr-controls {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	/* 🚨 EVERY COLUMN MUST HAVE THE SAME STRUCTURE OR NO alignment VALUE CAN
	   SAVE IT. The first attempt put a hint under the date field only: that
	   column became taller, and a flex row can align tops or bottoms but not
	   both, so `flex-end` lifted its LABEL above the other three. The hint now
	   lives under the whole row, so each column is exactly label + control and
	   the two edges agree. */
	align-items: flex-end;
	margin: 0 0 8px;
}

/* The hint, on its own line under the row. */
.aieo-qr-note {
	flex: 1 0 100%;
	margin: 2px 0 0;
	font-size: 12px;
	color: var( --aieo-muted-fg, #71717a );
}

.aieo-qr-controls label {
	display: flex;
	flex-direction: column;
	gap: 4px;
	margin: 0;
}

.aieo-qr-lbl {
	font-weight: 600;
	line-height: 1.3;
}

.aieo-qr-controls select,
.aieo-qr-controls input[type="date"] {
	height: 32px;
	box-sizing: border-box;
}

.aieo-qr-controls .button {
	/* Its own line-height would otherwise sit it a pixel above the selects. */
	height: 32px;
	align-self: flex-end;
}

/* Improvement and degradation, on the line rather than on one figure. */
.aieo-qr-page tbody tr:has( .aieo-t-tone--success ) {
	background: var( --aieo-tone-success-pale, rgba( 34, 197, 94, 0.07 ) );
}

.aieo-qr-page tbody tr:has( .aieo-t-tone--danger ) {
	background: var( --aieo-tone-danger-pale, rgba( 239, 68, 68, 0.07 ) );
}

/* Hover must still win, or the row under the cursor stops being obvious. */
.aieo-qr-page tbody tr:has( .aieo-t-tone--success ):hover,
.aieo-qr-page tbody tr:has( .aieo-t-tone--danger ):hover {
	background: var( --aieo-row-hover, rgba( 0, 0, 0, 0.04 ) );
}

@media ( max-width: 782px ) {
	.aieo-qr-controls { gap: 12px; }
	.aieo-qr-controls label { flex: 1 1 46%; }
}

/* The customer's own words, shown in full rather than as a preview. Longest
   on the reference shop is 1,235 characters; a width cap keeps the row from
   stretching the table past the viewport, and the cell wraps instead. */
.aieo-qr-page td[data-key="comments"] {
	max-width: 620px;
	white-space: normal;
	line-height: 1.45;
}
