/*
 * GM8 OS — "Aurora" theme for the /ops panel.
 *
 * Plain CSS on purpose. The production deploy runs `composer install` but never `npm run build`,
 * so this ships through Filament's asset pipeline (see AppServiceProvider) rather than Vite —
 * nothing here may depend on Tailwind being compiled.
 *
 * Colour discipline: purple is chrome and brand. Green/amber/red are state. A status colour is
 * never decoration, and purple never indicates status. The whole scheme rests on that separation.
 */

/* ---------------------------------------------------------------------------
 * 1. Tokens
 *
 * --brand* is the only place purple is defined; everything else derives from it via color-mix.
 * Reproduce that indirection in new components — if a component doesn't re-theme when the accent
 * switches, it's bypassing the tokens.
 *
 * Nothing below this section may write a colour literal. Every translucent surface, line, shadow
 * and status tint in this file resolves to one of the tokens here, so a second ground (a light
 * theme) is a matter of redefining --lift and the grounds rather than editing 300 declarations.
 * An earlier iteration eyeballed each component instead: twenty-five distinct white alphas for
 * what are really eight surface roles, and status tints built from Tailwind's palette while the
 * matching text came from the oklch tokens — a chip whose fill and label were two different
 * greens, which only a black ground could hide. Reach for a role below; if none fits, add one.
 * ------------------------------------------------------------------------ */

:root {
    --brand: #660066;
    --brandMid: #a0149e;
    --brandLo: #5c0159;
    /* There is no --brandHi. It was the scale's lightest stop and could not carry white
       text (4.22:1, 2.60:1 under the brighter accent), so once the avatar ramp and the
       light primary hover stopped reaching for it, nothing legitimately could. */
    --brandTint: #d6a0d6;
    --brandDeep: #3c043c;
    --brandDeepMid: #5c007e;
    --canvas: #0a0710;

    --txt: #faf7fc;
    --dim: oklch(0.76 0.02 315);
    --dimmer: oklch(0.62 0.025 315);

    /* The ink that translucency is made of. On this ground it is white — light lifts a surface
       off a near-black canvas. Everything in the two blocks below is this at some alpha, so the
       ground can be inverted here rather than per-component. Space-separated on purpose: it is
       substituted into rgb()'s slash-alpha form. */
    --lift: 255 255 255;

    /* Fills, recessed through prominent. --fill-track is the empty half of a meter, which wants
       to read as a channel rather than as a raised surface — bars, segments, day marks and the
       lifecycle stepper all previously picked their own value for this.
       --fill-hover is the exception and is deliberately not a rung on that ladder: it is the
       hover state of an already-raised control, so it lifts on the dark ground and settles
       toward the canvas on the light one. Both read as "pressed" against their own rest state,
       which is what a hover has to do; neither is a depth you can order against the others. */
    --fill-recessed: rgb(var(--lift) / 0.022);
    --fill-wash: rgb(var(--lift) / 0.035);
    --fill-surface: rgb(var(--lift) / 0.05);
    --fill-raised: rgb(var(--lift) / 0.07);
    --fill-track: rgb(var(--lift) / 0.08);
    --fill-strong: rgb(var(--lift) / 0.09);
    --fill-hover: rgb(var(--lift) / 0.13);
    --fill-active: rgb(var(--lift) / 0.16);
    --fill-mute: rgb(var(--lift) / 0.2);

    /* --fill-raised is a control standing on the canvas; --fill-inset is a chip or badge
       sitting inside a panel. Identical here — over a dark ground both are "a little lighter
       than my parent" — and opposite once the ground is light, where the control goes white
       and the chip has to go grey or disappear into the panel it sits on. */
    --fill-inset: rgb(var(--lift) / 0.07);

    /* The brand chrome is purple in both themes, so its surfaces ride --lift and are the one
       group the light theme leaves alone. */
    --fill-chrome: rgb(var(--lift) / 0.07);
    --line-chrome: rgb(var(--lift) / 0.12);

    /* Lines. --edge/--edge2 stay brand-derived: they are the rim of a purple-tinted panel, not
       neutral structure, and they already re-themed correctly with the accent. */
    --line-hairline: rgb(var(--lift) / 0.055);
    --line-soft: rgb(var(--lift) / 0.08);
    --line: rgb(var(--lift) / 0.12);
    --line-strong: rgb(var(--lift) / 0.16);
    --edge: color-mix(in srgb, var(--brandWash) 15%, transparent);
    --edge2: color-mix(in srgb, var(--brandWash) 28%, transparent);

    /* The ink brand washes are mixed from. Pale purple reads as the accent over a dark
       ground; over a light one the same 16% disappears, so the light theme swaps this for
       the deep brand and every wash below follows — the --lift trick, for the accent. */
    --brandWash: var(--brandTint);
    --brand-fill: color-mix(in srgb, var(--brandWash) 16%, transparent);
    --brand-rule: color-mix(in srgb, var(--brandWash) 42%, transparent);
    --brand-ramp: color-mix(in srgb, var(--brandWash) 50%, transparent);

    /* The specular sweep, appended when data lands. Its own role because it lightens a dark
       surface and has to darken a light one — it cannot ride a fill. */
    --sweep: rgb(var(--lift) / 0.13);

    /* Ink over the brand chrome, where the ground is purple rather than the canvas — so these
       track --lift with the sidebar, not --txt with the data area. */
    --ink-on-brand: #fff;
    --ink-chrome: rgb(var(--lift) / 0.8);
    --ink-chrome-dim: rgb(var(--lift) / 0.5);
    /* 0.36 measured 2.53:1 on the light sidebar and 3.19:1 on the dark one - a 10.5px
       uppercase label needs 4.5:1. At 0.6 the worst ground is 4.91:1 and it still sits
       clearly below --ink-chrome, so the hierarchy survives the fix. */
    --ink-chrome-faint: rgb(var(--lift) / 0.6);

    /* Depth. Whole shadows, not colours: a light ground wants a different spread and softness,
       not the same geometry at a different alpha. --specular is the rim highlight on a hover. */
    --shadow-sm: 0 4px 24px rgb(0 0 0 / 0.28);
    --shadow-md: 0 4px 28px rgb(0 0 0 / 0.3);
    --shadow-lg: 0 6px 30px rgb(0 0 0 / 0.34);
    --shadow-xl: -30px 0 70px rgb(0 0 0 / 0.55);

    /* The rim a card or pill catches when it lifts under the cursor. Data area, not chrome:
       both consumers are hover states on .gm8-card and .gm8-pill, so this is overridden for
       the light ground rather than shared. White-on-white would simply be nothing. */
    --specular: inset 0 1px 0 rgb(var(--lift) / 0.15);

    /* Grounds that are not the canvas. */
    --scrim: rgb(8 4 10 / 0.62);
    --ground-dot: #14091a;
    --ground-menu: #211b28;
    --ground-over-near: color-mix(in srgb, var(--brandDeep) 96%, transparent);
    --ground-over: rgb(14 8 18 / 0.98);

    /* In-gamut on purpose. The values these replace were outside sRGB — oklch(0.84 0.16 76)
       and three others — so what actually shipped was whatever the browser clamped them to,
       and a tint mixed from one drifted from the clamped label beside it. These are the
       clamped renders round-tripped back to oklch: identical pixels, no clipping, and safe
       to mix from. Keep new status colours inside the gamut. */
    --live: oklch(0.8502 0.1696 160.1);
    --warn: oklch(0.8327 0.157 78.4);
    --bad: oklch(0.7104 0.1829 19.5);
    --violet: oklch(0.8002 0.1582 318.2);
    --violet-hi: oklch(0.8971 0.0907 324.5);

    /* The same status, as a shape rather than as a word. WCAG asks 4.5:1 of a label and
       3:1 of a graphic, and over this ground one value clears both — so these are aliases
       here and dark mode never sees the distinction. They exist because over white it is
       not one value: the amber that clears 4.5:1 is a brown, and a brown dot does not read
       as caution. Dots, bars, tracks, sparklines and rules use these; labels never do. */
    --live-mark: var(--live);
    --warn-mark: var(--warn);
    --bad-mark: var(--bad);
    --violet-mark: var(--violet);

    /* The critical wash. Denser and darker than --bad, which is ink: this is the red a sev 1
       floods the hero and the canvas with, and collapsing the two would lighten both. */
    --crit: #be2828;

    /* Status tints, derived — the discipline --brand* already had and status did not. A tint and
       its label now come from one source, so a status cannot drift hue between fill and text. */
    --live-fill-soft: color-mix(in srgb, var(--live) 10%, transparent);
    --live-fill: color-mix(in srgb, var(--live) 14%, transparent);
    --live-fill2: color-mix(in srgb, var(--live) 20%, transparent);
    --live-edge: color-mix(in srgb, var(--live) 23%, transparent);
    --live-edge2: color-mix(in srgb, var(--live) 30%, transparent);
    --live-glow: color-mix(in srgb, var(--live) 60%, transparent);

    --warn-fill: color-mix(in srgb, var(--warn) 16%, transparent);
    --warn-edge: color-mix(in srgb, var(--warn) 21%, transparent);
    --warn-edge2: color-mix(in srgb, var(--warn) 28%, transparent);
    --warn-glow: color-mix(in srgb, var(--warn) 70%, transparent);

    --bad-fill: color-mix(in srgb, var(--bad) 15%, transparent);
    --bad-edge: color-mix(in srgb, var(--bad) 22%, transparent);
    --bad-edge2: color-mix(in srgb, var(--bad) 31%, transparent);

    /* The load ramp, brand into alarm. --heat-1 is --brandMid; the far end is deliberately not
       --warn, because the ramp reads as heat rather than as a warning state. */
    --heat-1: var(--brandMid);
    --heat-2: #f0a020;
    --heat-3: #facc15;

    --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}

[data-gm8-accent='deeper'] {
    --brand: #42003f;
    --brandMid: #6d0d6b;
    --brandLo: #380135;
    --brandTint: #b183b1;
    --brandDeep: #26031f;
    --brandDeepMid: #3f0059;
    --violet: oklch(0.7 0.13 312);
}

/* Nothing in the app sets data-gm8-accent - these blocks are a design tool, not a live
   feature. If one is ever wired up, note that 'brighter' does not currently meet contrast:
   its --brandMid (#cf27cb) carries white text at 4.37:1 and its --brand (#9b019b) puts the
   sidebar group label at 3.28:1, because both stops are too light for the white ink the brand
   chrome uses. 'deeper' and the default clear 4.5:1 everywhere. Darken this one before
   switching it on. */
[data-gm8-accent='brighter'] {
    --brand: #9b019b;
    --brandMid: #cf27cb;
    --brandLo: #84027f;
    --brandTint: #f0c8f0;
    --brandDeep: #5c045c;
    --brandDeepMid: #7d00a8;
    --violet: oklch(0.87 0.18 320);
}

/* ---------------------------------------------------------------------------
 * 1b. The light ground
 *
 * Filament owns the switch: its theme script adds `.dark` to <html> for dark and for
 * "system" on a dark OS, and adds nothing otherwise. So `:not(.dark)` is light, and dark
 * stays what a broken or absent script falls back to — the product's own ground.
 *
 * This is a re-grounding, not an inversion. Two things do not move:
 *
 *   - The brand chrome. The sidebar stays purple in both themes, because it is the
 *     identity, so everything painted on it (--fill-chrome, --fill-active, --ink-*)
 *     rides --lift and is deliberately absent below.
 *   - Where lift means lift. --specular is still a white rim; --lift stays white.
 *
 * What does move is the meaning of a translucent surface. Over near-black, white at 5%
 * reads as raised. Over near-white it reads as nothing, so a raised surface here is
 * *opaque white over a tinted canvas*, and the dark ink (--sink) is spent only on the
 * things that are genuinely cut into the page: washes, channels, empty meters, rules.
 * That is why --fill-inset exists and why --fill-surface is not simply --lift flipped.
 * ------------------------------------------------------------------------ */

:root:not(.dark) {
    --canvas: #f2eef6;
    --txt: #17121c;
    --dim: oklch(0.42 0.02 315);
    --dimmer: oklch(0.5 0.025 315);

    /* The ink recesses are made of, opposite --lift. */
    --sink: 26 18 34;

    /* Raised is white and opaque; recessed is --sink. See the note above. */
    --fill-recessed: rgb(var(--sink) / 0.045);
    --fill-wash: rgb(var(--sink) / 0.03);
    --fill-surface: rgb(255 255 255 / 0.75);
    --fill-raised: rgb(255 255 255 / 0.92);
    --fill-inset: rgb(var(--sink) / 0.06);
    --fill-track: rgb(var(--sink) / 0.1);
    --fill-strong: rgb(255 255 255 / 1);
    --fill-hover: rgb(var(--sink) / 0.04);
    --fill-mute: rgb(var(--sink) / 0.25);

    --line-hairline: rgb(var(--sink) / 0.08);
    --line-soft: rgb(var(--sink) / 0.12);
    --line: rgb(var(--sink) / 0.16);
    --line-strong: rgb(var(--sink) / 0.22);

    /* The deep brand, not the pale tint: a wash has to darken this ground to register. */
    --brandWash: var(--brand);

    --sweep: rgb(var(--sink) / 0.07);

    /* A white rim over a white surface is invisible; on this ground the lip reads as a thin
       shade along the top edge instead. --line-chrome is absent on purpose: it belongs to the
       sidebar, which is brand purple in both themes. */
    --specular: inset 0 1px 0 rgb(var(--sink) / 0.06);

    /* Tighter and softer. A dark theme's 28px spread at 30% black reads as smoke here. */
    --shadow-sm: 0 1px 2px rgb(var(--sink) / 0.05), 0 4px 12px rgb(var(--sink) / 0.06);
    --shadow-md: 0 1px 3px rgb(var(--sink) / 0.06), 0 6px 18px rgb(var(--sink) / 0.07);
    --shadow-lg: 0 2px 6px rgb(var(--sink) / 0.08), 0 10px 26px rgb(var(--sink) / 0.09);
    --shadow-xl: -18px 0 50px rgb(var(--sink) / 0.18);

    --scrim: rgb(26 18 34 / 0.42);
    --ground-dot: #fff;
    --ground-menu: #fff;
    --ground-over-near: #fff;
    --ground-over: rgb(255 255 255 / 0.99);

    /* Solved rather than eyeballed: the most chroma each hue keeps inside sRGB at the
       lightness that clears 4.7:1 against all three grounds a status colour lands on —
       the panel, the canvas, and its own tint. The dark values would sit near 1.6:1 here. */
    --live: oklch(0.49 0.11 160);
    --warn: oklch(0.51 0.11 70);
    --bad: oklch(0.49 0.19 20);
    --violet: oklch(0.5 0.23 318);
    --violet-hi: oklch(0.42 0.2 318);
    --crit: #a3122b;

    /* The graphics tier, solved to 3:1 instead of 4.5:1 — which is what lets a dot stay
       amber (#bc7806) where the label has to go brown (#8d5901) to stay readable. */
    --live-mark: oklch(0.61 0.135 160);
    --warn-mark: oklch(0.63 0.135 70);
    --bad-mark: oklch(0.58 0.2 20);
    --violet-mark: oklch(0.58 0.235 318);

    /* A dark ink carries further over white than a light one does over black, so the fills
       come down and the glows come down hard. */
    --live-fill-soft: color-mix(in srgb, var(--live) 7%, transparent);
    --live-fill: color-mix(in srgb, var(--live) 11%, transparent);
    --live-fill2: color-mix(in srgb, var(--live) 16%, transparent);
    --live-edge: color-mix(in srgb, var(--live) 28%, transparent);
    --live-edge2: color-mix(in srgb, var(--live) 40%, transparent);
    --live-glow: color-mix(in srgb, var(--live) 30%, transparent);

    --warn-fill: color-mix(in srgb, var(--warn) 12%, transparent);
    --warn-edge: color-mix(in srgb, var(--warn) 26%, transparent);
    --warn-edge2: color-mix(in srgb, var(--warn) 36%, transparent);
    --warn-glow: color-mix(in srgb, var(--warn) 34%, transparent);

    --bad-fill: color-mix(in srgb, var(--bad) 11%, transparent);
    --bad-edge: color-mix(in srgb, var(--bad) 27%, transparent);
    --bad-edge2: color-mix(in srgb, var(--bad) 38%, transparent);

    /* The brand wash carries much further over white than the pale tint did over black,
       so the accent fill comes down — it was both too heavy and too close to its own label. */
    --brand-fill: color-mix(in srgb, var(--brandWash) 11%, transparent);

    --heat-2: #c26a00;
    --heat-3: #9a7b0a;
}

/* The accent blocks above set --violet for the dark ground, and :root:not(.dark) outranks
   them, so each accent needs its light counterpart or switching the accent would stop
   retinting the violet in this theme. Same solve, per hue. */
:root:not(.dark)[data-gm8-accent='deeper'] {
    --violet: oklch(0.5 0.24 312);
    --violet-hi: oklch(0.42 0.21 312);
    --violet-mark: oklch(0.58 0.245 312);
}

:root:not(.dark)[data-gm8-accent='brighter'] {
    --violet: oklch(0.5 0.23 320);
    --violet-hi: oklch(0.42 0.2 320);
    --violet-mark: oklch(0.58 0.235 320);
}

/* ---------------------------------------------------------------------------
 * 2. Motion
 *
 * Entrances animate transform only — no opacity, no filter. A faded entrance leaves panels
 * invisible when first paint is slow, and breaks DOM-based screenshotting.
 * ------------------------------------------------------------------------ */

@keyframes gm8-rise {
    from { transform: translateY(16px) scale(0.99); }
    to { transform: none; }
}

@keyframes gm8-pulse {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--live-mark) 55%, transparent), 0 0 12px color-mix(in srgb, var(--live-mark) 70%, transparent); }
    50% { box-shadow: 0 0 0 8px color-mix(in srgb, var(--live-mark) 0%, transparent), 0 0 4px color-mix(in srgb, var(--live-mark) 35%, transparent); }
}

@keyframes gm8-grow {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

@keyframes gm8-grow-y {
    from { transform: scaleY(0); }
    to { transform: scaleY(1); }
}

@keyframes gm8-shimmer {
    0% { background-position: 0% 50%; }
    100% { background-position: 200% 50%; }
}

/* Cross-document view transitions: a row becomes the record it opens.
 *
 * The panel does not run ->spa(), so opening a record is a real document navigation, and
 * `navigation: auto` is the whole switch. Every name is applied from JS (see gm8-motion.js) —
 * nothing here names anything, because naming has to be *directional*. A static name would also
 * apply on the way out, lifting those elements out of the departing snapshot to animate against
 * a destination that has no counterpart for them.
 *
 * The opt-in is document-wide; it cannot be scoped to a selector. So the root snapshots are held
 * still below, and a navigation with no pairing looks exactly as it did before this existed: an
 * instant swap. Only a real pairing moves — the same law as everywhere else in this file.
 */
@media (prefers-reduced-motion: no-preference) {
    @view-transition {
        navigation: auto;
    }
}

::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
    animation-duration: 460ms; /* gm8-rise's duration — the entrance this replaces */
    animation-timing-function: var(--ease);
}

/* The page itself does not cross-fade.
 *
 * Every navigation in /ops runs through the opt-in above, and almost none of them pair anything
 * — a fade on all of them would be exactly the decoration this layer exists to avoid. Hiding the
 * outgoing root and holding the incoming one at full opacity leaves the swap instant, with the
 * paired elements animating over the top. mix-blend-mode is reset because the default
 * plus-lighter is only correct for two snapshots that are cross-fading; with both held opaque it
 * washes the page out.
 *
 * Must stay after the universal rules above, which would otherwise re-animate these. */
::view-transition-old(root),
::view-transition-new(root) {
    animation: none;
    mix-blend-mode: normal;
}

::view-transition-old(root) { display: none; }

/* ---------------------------------------------------------------------------
 * 3. Page shell
 * ------------------------------------------------------------------------ */

.gm8 {
    --gm8-max: 1460px;

    color: var(--txt);
    font-variant-numeric: tabular-nums; /* every number here sits in a column or ticks live */
    max-width: var(--gm8-max);
}

.gm8 :is(h1, h2, h3) {
    color: var(--txt);
}

.gm8-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}

.gm8-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Intrinsic, never breakpoint-driven — fixed repeat() columns break below ~1200px. */
.gm8-grid {
    display: grid;
    gap: 16px;
}

.gm8-grid--cards { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.gm8-grid--pills { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.gm8-grid--split { grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); }
.gm8-grid--pair { grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); }
.gm8-grid--trio { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.gm8-grid--stats { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; }
.gm8-grid--stats-wide { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; }
.gm8-grid--shipped { grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); }
.gm8-grid--halves { grid-template-columns: 1fr 1fr; }

/* Bento: one wide cell beside a narrow sidebar column. minmax(0, Nfr) — not auto-fit — because
   the whole point is a deliberate size *imbalance*; fr shares always sum to 100% of the row, so
   this shrinks gracefully instead of overflowing the way a fixed-px column would. */
.gm8-grid--bento { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }

/* ---------------------------------------------------------------------------
 * 4. Page header
 *
 * Wrap plus a floor on the prose column. Without both, all shrinkage lands on the text: at 924px
 * the Reporting subtitle rendered as eight lines in a 168px column.
 * ------------------------------------------------------------------------ */

.gm8-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 18px 24px;
    margin-bottom: 26px;
    animation: gm8-rise 460ms var(--ease) both;
}

.gm8-page-header__text {
    flex: 1 1 340px;
    min-width: 0;
}

.gm8-page-header__eyebrow {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--violet);
}

.gm8-page-header__title {
    margin: 6px 0 0;
    font-size: 38px;
    font-weight: 600;
    letter-spacing: -0.035em;
    line-height: 1;
}

.gm8-page-header__subtitle {
    margin: 9px 0 0;
    font-size: 14.5px;
    color: var(--dim);
    max-width: 56ch;
    text-wrap: pretty;
}

.gm8-page-header__aside {
    display: flex;
    align-items: center;
    gap: 9px;
    flex-shrink: 0;
    flex-wrap: wrap;
}

/* ---------------------------------------------------------------------------
 * 5. Panel
 * ------------------------------------------------------------------------ */

.gm8-panel {
    border-radius: 24px;
    background: var(--fill-surface);
    border: 1px solid var(--edge);
    overflow: hidden;
    box-shadow: var(--shadow-md);
    animation: gm8-rise 460ms var(--ease) both;
}

.gm8-panel--bad { border-color: var(--bad-edge); }
.gm8-panel--warn { border-color: var(--warn-edge); }
.gm8-panel--live { border-color: var(--live-edge); }

.gm8-panel--brand {
    background: linear-gradient(155deg, color-mix(in srgb, var(--brandMid) 40%, transparent), color-mix(in srgb, var(--brand) 10%, transparent));
    border-color: var(--edge2);
    box-shadow: 0 8px 34px color-mix(in srgb, var(--brand) 32%, transparent);
}

.gm8-panel--heat {
    background: linear-gradient(150deg, color-mix(in srgb, var(--brandMid) 22%, transparent), color-mix(in srgb, var(--warn) 6%, transparent));
    border-color: var(--warn-edge);
}

.gm8-panel__header {
    padding: 19px 22px;
    display: flex;
    align-items: center;
    gap: 11px;
    flex-wrap: wrap;
    background: linear-gradient(100deg, color-mix(in srgb, var(--brandMid) 16%, transparent), transparent);
}

.gm8-panel--bad > .gm8-panel__header {
    background: linear-gradient(100deg, color-mix(in srgb, var(--crit) 22%, transparent), transparent);
}

.gm8-panel__title {
    margin: 0;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.gm8-panel__note {
    font-size: 12.5px;
    color: var(--dimmer);
}

.gm8-panel__actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 7px;
}

.gm8-panel__body { padding: 22px; }
.gm8-panel__body--flush { padding: 0 10px 10px; }
.gm8-panel__body:empty { display: none; }

/* ---------------------------------------------------------------------------
 * 6. Rows
 * ------------------------------------------------------------------------ */

.gm8-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    padding: 14px 12px;
    border-radius: 16px;
    transition: background 180ms;
}

.gm8-row:hover { background: var(--fill-surface); }
.gm8-row--divided + .gm8-row--divided { border-top: 1px solid var(--line-hairline); border-radius: 0; }

.gm8-row__main {
    min-width: 210px;
    flex: 1;
}

.gm8-row__title {
    font-size: 14px;
    font-weight: 650;
    text-wrap: pretty;
}

.gm8-row__meta {
    font-size: 12.5px;
    color: var(--dimmer);
    margin-top: 2px;
    text-wrap: pretty;
}

/* A row's trailing figure. Rows had --main/--title/--meta/--actions but no figure, so the
   first screen that needed one borrowed .gm8-pill__figure from a different component. */
.gm8-row__figure {
    font-size: 15px;
    font-weight: 600;
    white-space: nowrap;
    flex-shrink: 0;
}

.gm8-row__actions {
    display: flex;
    gap: 7px;
    flex-shrink: 0;
}

/* Tinted icon tile that leads a "Needs you" row. */
.gm8-tile {
    width: 34px;
    height: 34px;
    border-radius: 11px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.gm8-tile svg { width: 17px; height: 17px; }
.gm8-tile--warn { background: var(--warn-fill); color: var(--warn); }
.gm8-tile--bad { background: var(--bad-fill); color: var(--bad); }
.gm8-tile--live { background: var(--live-fill); color: var(--live); }
.gm8-tile--violet { background: var(--brand-fill); color: var(--violet); }

/* ---------------------------------------------------------------------------
 * 7. Chips and badges
 * ------------------------------------------------------------------------ */

.gm8-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    padding: 3px 8px;
    border-radius: 7px;
    background: var(--fill-inset);
    color: var(--dim);
    white-space: nowrap;
    flex-shrink: 0;
}

.gm8-chip--live { background: var(--live-fill); color: var(--live); font-weight: 650; }
.gm8-chip--warn { background: var(--warn-fill); color: var(--warn); font-weight: 650; }
.gm8-chip--bad { background: var(--bad-fill); color: var(--bad); font-weight: 650; }
.gm8-chip--violet { background: var(--brand-fill); color: var(--violet); font-weight: 600; }
.gm8-chip--ghost { background: transparent; color: var(--dimmer); }

.gm8-chip--sev {
    font-weight: 700;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    padding: 4px 9px;
    border-radius: 8px;
    border: 1px solid transparent;
}

.gm8-chip--sev.gm8-chip--bad { background: var(--bad-fill); border-color: color-mix(in srgb, var(--bad) 40%, transparent); }
.gm8-chip--sev.gm8-chip--warn { background: var(--warn-fill); border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
.gm8-chip--sev.gm8-chip--neutral { background: var(--fill-inset); border-color: var(--line); }

.gm8-badge {
    font-size: 11px;
    font-weight: 700;
    padding: 2px 8px;
    border-radius: 8px;
    background: var(--fill-inset);
    color: var(--dim);
}

.gm8-badge--live { background: var(--live-fill); color: var(--live); }
.gm8-badge--warn { background: var(--warn-fill); color: var(--warn); }
.gm8-badge--bad { background: var(--bad-fill); color: var(--bad); }
.gm8-badge--violet { background: var(--brand-fill); color: var(--violet); }

/* Filter chips. Active is purple-tinted with an emphasised border; inactive stays neutral. */
.gm8-filter {
    padding: 8px 14px;
    border-radius: 12px;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    background: var(--fill-wash);
    color: var(--dim);
    border: 1px solid var(--line-soft);
    font-family: inherit;
    transition: all 160ms;
}

.gm8-filter:hover { background: var(--fill-strong); color: var(--txt); }

.gm8-filter--on {
    font-weight: 650;
    background: var(--brand-fill);
    color: var(--violet);
    border-color: var(--edge2);
}

/* ---------------------------------------------------------------------------
 * 8. Avatars
 * ------------------------------------------------------------------------ */

.gm8-avatar-wrap {
    position: relative;
    flex-shrink: 0;
    display: inline-flex;
    width: var(--gm8-avatar-size, 32px);
    height: var(--gm8-avatar-size, 32px);
}

.gm8-avatar {
    width: 100%;
    height: 100%;
    border-radius: 9999px;
    /* --brandHi cannot carry white text (4.22:1). Initials name a person, so the ramp
       starts one stop deeper; --brandMid gives 6.80:1. */
    background: linear-gradient(140deg, var(--brandMid), var(--brandLo));
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: calc(var(--gm8-avatar-size, 32px) * 0.34);
    font-weight: 700;
    color: var(--ink-on-brand);
    letter-spacing: 0;
}

.gm8-avatar--muted {
    background: var(--fill-inset);
    /* Initials are a name, not a decoration: --dimmer measured 3.82:1 on this fill. */
    color: var(--dim);
}

.gm8-avatar__dot {
    position: absolute;
    right: -2px;
    bottom: -2px;
    width: calc(var(--gm8-avatar-size, 32px) * 0.29);
    height: calc(var(--gm8-avatar-size, 32px) * 0.29);
    border-radius: 9999px;
    border: 2px solid var(--ground-dot);
}

.gm8-avatar__dot--live { background: var(--live-mark); animation: gm8-pulse 2.6s ease-in-out infinite; }
.gm8-avatar__dot--warn { background: var(--warn-mark); box-shadow: 0 0 12px var(--warn-glow); }
.gm8-avatar__dot--violet { background: var(--violet-mark); }

/* ---------------------------------------------------------------------------
 * 9. Buttons
 * ------------------------------------------------------------------------ */

.gm8-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    border-radius: 11px;
    border: 1px solid var(--line);
    background: var(--fill-raised);
    color: var(--txt);
    padding: 7px 13px;
    font-size: 12.5px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: all 160ms;
    white-space: nowrap;
}

.gm8-btn:hover { background: var(--fill-hover); }
.gm8-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.gm8-btn svg { width: 15px; height: 15px; }
.gm8-btn--sm { padding: 5px 11px; font-size: 11.5px; }

.gm8-btn--primary {
    border-radius: 14px;
    border-color: var(--line-strong);
    background: linear-gradient(135deg, color-mix(in srgb, var(--brandMid) 85%, transparent), color-mix(in srgb, var(--brand) 85%, transparent));
    color: var(--ink-on-brand);
    padding: 13px 18px;
    font-size: 13.5px;
    font-weight: 650;
    box-shadow: 0 8px 26px color-mix(in srgb, var(--brand) 50%, transparent);
    transition: all 180ms;
}

.gm8-btn--primary:hover {
    background: linear-gradient(135deg, color-mix(in srgb, var(--brandMid) 100%, transparent), color-mix(in srgb, var(--brand) 100%, transparent));
    transform: translateY(-2px);
    box-shadow: 0 14px 34px color-mix(in srgb, var(--brand) 65%, transparent);
}

.gm8-btn--approve {
    border-color: var(--live-edge2);
    background: var(--live-fill-soft);
    color: var(--live);
    font-weight: 650;
}

.gm8-btn--approve:hover { background: var(--live-fill2); }

.gm8-btn--quiet {
    border-color: transparent;
    background: transparent;
    color: var(--dim);
}

.gm8-btn--quiet:hover { background: var(--fill-raised); color: var(--txt); }

.gm8-btn--deny {
    border-color: transparent;
    background: transparent;
    color: var(--dim);
}

.gm8-btn--deny:hover { background: var(--bad-fill); color: var(--bad); }

.gm8-btn--resolve {
    border-color: transparent;
    background: transparent;
    color: var(--dim);
}

.gm8-btn--resolve:hover { background: var(--live-fill); color: var(--live); }

/* ---------------------------------------------------------------------------
 * 10. Stats
 * ------------------------------------------------------------------------ */

.gm8-stat {
    border-radius: 24px;
    background: var(--fill-surface);
    border: 1px solid var(--edge);
    padding: 18px 20px;
    animation: gm8-rise 460ms var(--ease) both;
}

.gm8-stat--flagged { border-color: var(--warn-edge); }

.gm8-stat__eyebrow {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dimmer);
    margin-bottom: 8px;
}

.gm8-stat__value {
    font-size: 30px;
    font-weight: 500;
    letter-spacing: -0.035em;
    line-height: 1;
}

.gm8-stat__value--warn { color: var(--warn); }
.gm8-stat__value--bad { color: var(--bad); }
.gm8-stat__value--live { color: var(--live); }
.gm8-stat__value--violet { color: var(--violet); }
.gm8-stat__suffix { font-size: 0.63em; }

.gm8-stat__label {
    font-size: 12px;
    color: var(--dimmer);
    margin-top: 5px;
}

.gm8-stat__note { font-size: 12px; margin-top: 6px; }
.gm8-stat__note--warn { color: var(--warn); }
.gm8-stat__note--dim { color: var(--dimmer); }

/* Numerals, shared across cards. */
.gm8-num-hero { font-size: 60px; font-weight: 500; letter-spacing: -0.05em; line-height: 0.86; }
.gm8-num-lg { font-size: 38px; font-weight: 500; letter-spacing: -0.04em; line-height: 0.92; }
.gm8-num-md { font-size: 30px; font-weight: 500; letter-spacing: -0.035em; line-height: 1; }

/* ---------------------------------------------------------------------------
 * 11. Bars
 * ------------------------------------------------------------------------ */

.gm8-bar {
    height: var(--gm8-bar-height, 6px);
    border-radius: 4px;
    background: var(--fill-track);
    overflow: hidden;
}

.gm8-bar__fill {
    height: 100%;
    border-radius: 4px;
    transform-origin: left;
    animation: gm8-grow 620ms var(--ease) both;
}

.gm8-bar__fill--violet { background: linear-gradient(90deg, var(--brand-ramp), var(--violet-mark)); }
.gm8-bar__fill--live { background: linear-gradient(90deg, color-mix(in srgb, var(--live-mark) 50%, transparent), var(--live-mark)); }
.gm8-bar__fill--warn { background: linear-gradient(90deg, color-mix(in srgb, var(--warn-mark) 45%, transparent), var(--warn-mark)); }
.gm8-bar__fill--mixed { background: linear-gradient(90deg, var(--violet-mark), var(--warn-mark)); }
.gm8-bar__fill--muted { background: var(--fill-mute); animation: none; }

.gm8-bar__fill--heat {
    background: linear-gradient(90deg, var(--heat-1), var(--heat-2), var(--heat-3));
    background-size: 200% 100%;
    animation: gm8-shimmer 3.5s linear infinite, gm8-grow 1s var(--ease) both;
}

.gm8-scale {
    margin-top: 7px;
    display: flex;
    justify-content: space-between;
    font-size: 10.5px;
    color: var(--dimmer);
}

/* Week-strip segments (leave days remaining). */
.gm8-segments { display: flex; gap: 4px; }
.gm8-segments > span { flex: 1; height: 4px; border-radius: 3px; background: var(--fill-track); }
.gm8-segments > span.is-on { background: var(--violet-mark); }

/* Column chart. */
.gm8-columns { display: flex; height: 170px; align-items: flex-end; gap: 12px; }

.gm8-columns__item {
    display: flex;
    flex: 1;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: 7px;
    height: 100%;
}

.gm8-columns__bar {
    width: 100%;
    border-radius: 7px 7px 3px 3px;
    background: linear-gradient(180deg, var(--violet-mark), color-mix(in srgb, var(--brandMid) 35%, transparent));
    transform-origin: bottom;
    animation: gm8-grow-y 620ms var(--ease) both;
}

.gm8-columns__value { font-size: 11.5px; color: var(--dim); font-weight: 600; }
.gm8-columns__label { font-size: 10.5px; color: var(--dimmer); }

/* ---------------------------------------------------------------------------
 * 12. Cards (roster)
 * ------------------------------------------------------------------------ */

.gm8-card {
    border-radius: 22px;
    background: var(--fill-surface);
    border: 1px solid var(--edge);
    padding: 20px;
    box-shadow: var(--shadow-sm);
    animation: gm8-rise 460ms var(--ease) both;
    /* Material response, not a lift: the fill deepens and the rim catches light. */
    transition: background 260ms ease, border-color 260ms ease, box-shadow 260ms ease;
    text-align: left;
    width: 100%;
    font-family: inherit;
    color: inherit;
    display: block;
}

.gm8-card--interactive { cursor: pointer; }

.gm8-card--interactive:hover {
    background: var(--fill-raised);
    box-shadow: var(--specular), var(--shadow-lg);
}

.gm8-card--live { border-color: var(--live-edge); }
.gm8-card--live.gm8-card--interactive:hover { border-color: color-mix(in srgb, var(--live) 34%, transparent); }
.gm8-card--warn { border-color: var(--warn-edge); }
.gm8-card--warn.gm8-card--interactive:hover { border-color: color-mix(in srgb, var(--warn) 38%, transparent); }

.gm8-card--done {
    background: var(--fill-recessed);
    border-color: color-mix(in srgb, var(--brandWash) 9%, transparent);
}

.gm8-card--leave {
    background: linear-gradient(150deg, color-mix(in srgb, var(--brandMid) 28%, transparent), color-mix(in srgb, var(--brand) 8%, transparent));
    border-color: var(--edge2);
}

.gm8-card__person { display: flex; align-items: center; gap: 11px; }
.gm8-card__name { font-size: 14.5px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm8-card__where { font-size: 11.5px; color: var(--dimmer); }
.gm8-card__figure { margin-top: 18px; }
.gm8-card__foot { margin-top: 9px; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.gm8-card__foot small { font-size: 11.5px; color: var(--dimmer); }

/* Compact roster pill (the Now screen). */
.gm8-pill {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 12px 14px;
    border-radius: 16px;
    background: var(--fill-wash);
    border: 1px solid var(--line-soft);
    min-width: 0;
    transition: background 260ms ease, border-color 260ms ease, box-shadow 260ms ease;
}

.gm8-pill:hover {
    background: var(--fill-raised);
    border-color: var(--edge2);
    box-shadow: var(--specular);
}

.gm8-pill--live { border-color: var(--live-edge); }
.gm8-pill--warn { border-color: var(--warn-edge); }
.gm8-pill__body { min-width: 0; flex: 1; }
.gm8-pill__name { font-size: 13.5px; font-weight: 650; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm8-pill__meta { font-size: 11px; color: var(--dimmer); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gm8-pill__figure { font-size: 15px; font-weight: 600; white-space: nowrap; }

/* ---------------------------------------------------------------------------
 * 13. Tables
 * ------------------------------------------------------------------------ */

.gm8-table-wrap { overflow-x: auto; }

.gm8-table {
    width: 100%;
    border-collapse: collapse;
}

.gm8-table th {
    padding: 11px 14px;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--dimmer);
    white-space: nowrap;
}

.gm8-table td {
    padding: 13px 14px;
    font-size: 13.5px;
    color: var(--dim);
}

.gm8-table tbody tr { border-top: 1px solid var(--line-hairline); transition: background 160ms; }
.gm8-table tbody tr:hover { background: var(--fill-wash); }
.gm8-table .is-num { text-align: right; font-weight: 600; color: var(--txt); }
.gm8-table .is-name { color: var(--txt); }
.gm8-table__amend { font-size: 11px; color: var(--warn); margin-top: 3px; }
.gm8-table__person { display: flex; align-items: center; gap: 10px; }
.gm8-table__actions { display: flex; gap: 7px; justify-content: flex-end; }

/* The raw punch log beneath a derived shift. */
.gm8-punches-row > td {
    padding: 0 14px 16px;
    background: color-mix(in srgb, var(--brandWash) 5%, transparent);
}

.gm8-punches {
    border-left: 2px solid var(--brand-rule);
    padding-left: 16px;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.gm8-punches__eyebrow {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--dimmer);
}

.gm8-punches__item { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; font-size: 12.5px; }
.gm8-punches__note { color: var(--dimmer); font-style: italic; }

/* ---------------------------------------------------------------------------
 * 14. Activity feed
 * ------------------------------------------------------------------------ */

.gm8-entry {
    display: flex;
    gap: 14px;
    padding: 16px 22px;
    border-top: 1px solid var(--line-hairline);
    transition: background 160ms;
}

.gm8-entry:hover { background: var(--fill-wash); }
.gm8-entry__body { min-width: 0; flex: 1; }

.gm8-entry__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 4px 14px;
}

.gm8-entry__who { font-size: 13.5px; font-weight: 650; color: var(--txt); }
.gm8-entry__when { font-size: 11.5px; color: var(--dimmer); }
.gm8-entry__tz { color: var(--dimmer); }

/* Never truncated — that was the point of the screen. */
.gm8-entry__note {
    margin: 5px 0 0;
    font-size: 13.5px;
    color: var(--dim);
    text-wrap: pretty;
    white-space: pre-line;
}

.gm8-entry__note--thin { color: var(--dimmer); font-style: italic; }

.gm8-entry__chips {
    margin-top: 9px;
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    align-items: center;
}

.gm8-link { font-size: 11px; color: var(--violet); }
.gm8-link:hover { color: var(--violet-hi); text-decoration: underline; }

/* ---------------------------------------------------------------------------
 * 15. Calendar
 * ------------------------------------------------------------------------ */

.gm8-cal__head,
.gm8-cal__week {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
}

.gm8-cal__head { border-bottom: 1px solid var(--edge); }

.gm8-cal__head > div {
    padding: 11px 10px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--dimmer);
    border-right: 1px solid var(--line-hairline);
}

.gm8-cal__row + .gm8-cal__row { border-top: 1px solid var(--line-hairline); }

.gm8-cal__day {
    min-height: 74px;
    padding: 9px 10px;
    border-right: 1px solid var(--line-hairline);
}

.gm8-cal__day--recessed { background: var(--fill-recessed); }
.gm8-cal__num { font-size: 12.5px; font-weight: 600; color: var(--dim); }
.gm8-cal__day--recessed .gm8-cal__num { color: var(--dimmer); }

.gm8-cal__closure {
    margin-top: 5px;
    font-size: 10.5px;
    font-weight: 650;
    padding: 2px 6px;
    border-radius: 6px;
    background: var(--warn-fill);
    color: var(--warn);
    display: inline-block;
}

.gm8-cal__bars {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
    padding: 0 8px 11px;
}

.gm8-cal__bar {
    font-size: 11px;
    font-weight: 650;
    padding: 4px 9px;
    border-radius: 8px;
    background: var(--brand-fill);
    color: var(--violet);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border: 1px solid transparent;
}

.gm8-cal__bar--pending { border-style: dashed; border-color: var(--brand-rule); }

/* ---------------------------------------------------------------------------
 * 16. Slide-over
 * ------------------------------------------------------------------------ */

.gm8-scrim {
    position: fixed;
    inset: 0;
    background: var(--scrim);
    z-index: 40;
    border: 0;
    padding: 0;
    cursor: pointer;
}

.gm8-slideover {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: min(470px, 100vw);
    z-index: 50;
    background: linear-gradient(200deg, var(--ground-over-near), var(--ground-over));
    border-left: 1px solid var(--edge2);
    box-shadow: var(--shadow-xl);
    display: flex;
    flex-direction: column;
    animation: gm8-rise 420ms var(--ease) both;
    color: var(--txt);
}

.gm8-slideover__head { padding: 24px 26px; border-bottom: 1px solid var(--line-soft); }
.gm8-slideover__name { font-size: 20px; font-weight: 650; letter-spacing: -0.02em; }
.gm8-slideover__meta { font-size: 12.5px; color: var(--dim); }

.gm8-slideover__body {
    flex: 1;
    overflow-y: auto;
    padding: 24px 26px;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

.gm8-slideover__foot {
    padding: 18px 26px;
    border-top: 1px solid var(--line-soft);
    display: flex;
    gap: 10px;
}

.gm8-slideover__foot .gm8-btn { flex: 1; justify-content: center; border-radius: 13px; padding: 11px; font-size: 13.5px; }

.gm8-eyebrow {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--dimmer);
    margin-bottom: 13px;
}

.gm8-live-strip {
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 15px 17px;
    border-radius: 16px;
    background: var(--live-fill-soft);
    border: 1px solid var(--live-edge);
}

.gm8-dot {
    width: 9px;
    height: 9px;
    border-radius: 9999px;
    background: var(--live-mark);
    animation: gm8-pulse 2.6s ease-in-out infinite;
    flex-shrink: 0;
}

.gm8-dot--bad { background: var(--bad-mark); animation-duration: 1.8s; }

.gm8-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 11px; }

.gm8-tiles > div {
    border-radius: 15px;
    background: var(--fill-inset);
    border: 1px solid var(--line-soft);
    padding: 15px;
}

.gm8-tiles__value { font-size: 25px; font-weight: 500; letter-spacing: -0.03em; }
/* --dim, not --dimmer: these tiles are the one gm8 component that also renders inside a
   Filament modal, whose panel is a lighter ground than our own — --dimmer measured 3.99:1
   there. Anything else placed in a modal needs the same check; the muted tokens are
   solved against .gm8-panel, not against Filament's. */
.gm8-tiles__label { font-size: 11.5px; color: var(--dim); margin-top: 3px; }

.gm8-timeline { display: flex; flex-direction: column; gap: 15px; }

.gm8-timeline__item {
    padding-left: 14px;
    border-left: 2px solid var(--brand-rule);
}

.gm8-timeline__item--thin { border-left-color: var(--line); }
.gm8-timeline__head { display: flex; justify-content: space-between; font-size: 12px; color: var(--dimmer); }

/* Two-tone allowance bar: taken solid, pending translucent. */
.gm8-split-bar { height: 7px; border-radius: 4px; background: var(--fill-track); overflow: hidden; display: flex; }
.gm8-split-bar__taken { height: 100%; background: var(--violet-mark); }
.gm8-split-bar__pending { height: 100%; background: color-mix(in srgb, var(--brandWash) 35%, transparent); }

/* A floating menu. The mention autocomplete is the only one today, and it had been given
   --canvas — the page ground — which is *darker* than the panel it floats over in both
   themes, so it read as a hole rather than as something raised. It also had no hover state
   at all, which is most of why it felt broken. */
.gm8-menu {
    position: absolute;
    /* Defaults so a caller that sets no offsets still gets a menu under its field rather
       than one pinned to the corner of the nearest positioned ancestor. z-index 20 matches
       Filament's own .fi-dropdown-panel; its topbar is 30 and its modals are 40/50, so this
       sits under the chrome and inside a modal's stacking context, which is right both ways. */
    inset-inline-start: 0;
    top: 100%;
    z-index: 20;
    border-radius: 12px;
    background: var(--ground-menu);
    border: 1px solid var(--line);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.gm8-menu__item {
    display: block;
    width: 100%;
    text-align: left;
    padding: 8px 12px;
    background: transparent;
    border: 0;
    cursor: pointer;
    color: var(--txt);
    font-family: inherit;
    font-size: 13px;
    transition: background 140ms;
}

.gm8-menu__item:hover,
.gm8-menu__item:focus-visible {
    background: var(--fill-hover);
}

.gm8-menu__handle { font-weight: 600; }
.gm8-menu__name { color: var(--dimmer); margin-left: 6px; font-size: 12px; }

/* ---------------------------------------------------------------------------
 * 17. Odds and ends
 * ------------------------------------------------------------------------ */

/* Dual wall clock in the page header. */
.gm8-clock {
    display: flex;
    gap: 1px;
    border-radius: 14px;
    overflow: hidden;
    border: 1px solid var(--edge);
    background: var(--fill-surface);
}

.gm8-clock__cell { padding: 11px 17px; text-align: center; }
.gm8-clock__divider { width: 1px; background: var(--edge); }

.gm8-clock__label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dimmer);
}

.gm8-clock__time { font-size: 17px; font-weight: 600; line-height: 1.15; margin-top: 2px; }

/* The severity word leads the incident meta line, colour-matched to the pill. */
.gm8-sev-word { font-weight: 650; }
.gm8-sev-word--bad { color: var(--bad); }
.gm8-sev-word--warn { color: var(--warn); }
.gm8-sev-word--neutral { color: var(--dim); }

.gm8-gasmark {
    font-size: 62px;
    font-weight: 500;
    letter-spacing: -0.05em;
    line-height: 0.85;
    background: linear-gradient(120deg, var(--violet), var(--warn));
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.gm8-daymark {
    width: 8px;
    height: 18px;
    border-radius: 3px;
    background: var(--fill-track);
}

.gm8-daymark.is-on {
    background: var(--violet-mark);
    box-shadow: 0 0 8px var(--brand-ramp);
}

.gm8-empty {
    padding: 22px 12px;
    font-size: 13px;
    color: var(--dimmer);
    text-wrap: pretty;
}

/*
 * A full-width notice band. Wrap plus a floor on the message, same rule as the page header —
 * pairing prose against a fixed cluster without both collapses the prose into a ribbon.
 */
.gm8-band {
    padding: 18px 22px;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
}

.gm8-band__message { flex: 1 1 260px; min-width: 260px; }
.gm8-band__chips { display: flex; gap: 7px; flex-wrap: wrap; flex-shrink: 0; }

.gm8-shipped {
    display: flex;
    gap: 12px;
    padding: 12px;
    border-radius: 14px;
    transition: background 180ms;
}

.gm8-shipped:hover { background: var(--fill-wash); }

.gm8-select {
    padding: 8px 12px;
    border-radius: 12px;
    font-size: 12.5px;
    font-weight: 600;
    font-family: inherit;
    background: var(--fill-wash);
    color: var(--dim);
    border: 1px solid var(--line-soft);
}

.gm8-select option { background: var(--canvas); color: var(--txt); }

/* A card that pushes its figure to the bottom needs a full-height column to push against. */
.gm8-grid > .gm8-panel { height: 100%; }
.gm8-panel--brand > .gm8-panel__body { display: flex; flex-direction: column; height: 100%; }

/* ---------------------------------------------------------------------------
 * 18. The panel shell
 *
 * Filament's own chrome, re-skinned. Deliberately layered on top of its compiled stylesheet
 * rather than replacing it: the CRUD resources keep working, and only the surfaces the design
 * actually speaks about change.
 *
 * An earlier iteration built the canvas glows as absolutely-positioned blurred divs with
 * backdrop-filter panels above them. That combination broke compositing and blanked the content.
 * These are background gradients on the shell root, and the translucent-panel look comes from
 * --fill-* roles over them — identical result, none of the compositing cost.
 * ------------------------------------------------------------------------ */

html,
body.fi-body {
    background-color: var(--canvas);
}

body.fi-body {
    background-image:
        radial-gradient(58% 52% at 10% -10%,
            color-mix(in srgb, var(--brandMid) 42%, transparent),
            color-mix(in srgb, var(--brandMid) 0%, transparent) 68%),
        radial-gradient(46% 44% at 94% -8%,
            color-mix(in srgb, var(--brandDeepMid) 38%, transparent),
            color-mix(in srgb, var(--brandDeepMid) 0%, transparent) 70%),
        radial-gradient(44% 40% at 42% 112%,
            color-mix(in srgb, var(--brand) 24%, transparent),
            color-mix(in srgb, var(--brand) 0%, transparent) 68%);
    background-attachment: fixed;
    background-repeat: no-repeat;
    color: var(--txt);
    font-variant-numeric: tabular-nums;
}

/* Sidebar */
.fi-main-sidebar {
    --sidebar-width: 236px;
    width: var(--sidebar-width);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--brand) 34%, transparent),
        color-mix(in srgb, var(--brandDeep) 50%, transparent));
    border-right: 1px solid var(--edge);
}

.fi-main-sidebar .fi-sidebar-header {
    background: transparent;
    border-bottom: 0;
    box-shadow: none;
}

.fi-main-sidebar .fi-logo {
    color: var(--ink-on-brand);
    font-weight: 700;
    letter-spacing: -0.02em;
}

.fi-main-sidebar .fi-sidebar-group-label {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.11em;
    text-transform: uppercase;
    color: var(--ink-chrome-faint);
}

.fi-main-sidebar .fi-sidebar-item-btn {
    border-radius: 13px;
    border: 1px solid transparent;
    padding: 10px 12px;
    transition: all 160ms;
}

.fi-main-sidebar .fi-sidebar-item-btn:hover {
    background: var(--fill-chrome);
    filter: brightness(1.12);
}

.fi-main-sidebar .fi-sidebar-item-label {
    font-size: 14px;
    font-weight: 500;
    color: var(--ink-chrome);
}

.fi-main-sidebar .fi-sidebar-item-btn .fi-icon {
    color: var(--ink-chrome-dim);
}

.fi-main-sidebar .fi-sidebar-item.fi-active > .fi-sidebar-item-btn,
.fi-main-sidebar .fi-sidebar-item-btn.fi-active {
    background: linear-gradient(100deg, var(--fill-active), var(--fill-chrome));
    border-color: var(--line-chrome);
    box-shadow: 0 6px 20px color-mix(in srgb, var(--brand) 40%, transparent);
}

.fi-main-sidebar .fi-sidebar-item.fi-active .fi-sidebar-item-label,
.fi-main-sidebar .fi-sidebar-item-btn.fi-active .fi-sidebar-item-label {
    font-weight: 650;
    color: var(--ink-on-brand);
}

.fi-main-sidebar .fi-sidebar-item.fi-active .fi-icon,
.fi-main-sidebar .fi-sidebar-item-btn.fi-active .fi-icon {
    color: var(--ink-on-brand);
}

/* Filament paints a focused sidebar item with --gray-100 and guards only its dark variant.
   Our sidebar is brand purple in *both* themes with white labels, so in light that put a
   near-white pill under white text — measured 1.08:1. It is a focus state, so no text-contrast
   sweep over rendered nodes can see it; it has to be reasoned about. */
.fi-main-sidebar .fi-sidebar-item-btn:focus-visible {
    background: var(--fill-chrome);
    outline: 2px solid var(--ink-on-brand);
    outline-offset: -2px;
}

/* Topbar: the sidebar and canvas carry the brand, so this recedes. */
.fi-topbar > nav,
.fi-topbar {
    background: transparent;
    border-bottom: 1px solid var(--edge);
    box-shadow: none;
}

/* Filament picked its breadcrumb grey against its own page background, and ours is a
   different colour in both themes — measured, it lands at 4.21:1 on the light canvas, just
   under the 4.5:1 it needs. It is the one piece of stock chrome our canvas made worse, so
   it reads from our own muted role instead. */
.fi-breadcrumbs-item,
.fi-breadcrumbs-item-label {
    color: var(--dim);
}

/* Our screens are laid out against a wider column than Filament's default. Scoped with :has so
   the stock resource pages keep the width their tables were built for. */
.fi-main-ctn:has(.gm8-page) .fi-main {
    max-width: 1460px;
}

.fi-page.gm8-page {
    color: var(--txt);
}

/*
 * Filament action buttons rendered inside our screens.
 *
 * The actions themselves are real Filament Actions — they bring the modal, the schema validation
 * and the notification, and reimplementing that to get a border-radius would be a bad trade. Only
 * their skin is overridden, and only inside .gm8, so every other page keeps stock buttons.
 */
.gm8 .fi-btn {
    border-radius: 11px;
    padding: 7px 13px;
    font-size: 12.5px;
    font-weight: 600;
    box-shadow: none;
    border: 1px solid var(--line);
    background: var(--fill-raised);
    color: var(--txt);
    transition: all 160ms;
}

.gm8 .fi-btn:hover { background: var(--fill-hover); }

.gm8 .fi-btn.fi-color-success {
    background: var(--live-fill-soft);
    border-color: var(--live-edge2);
    color: var(--live);
    font-weight: 650;
}

.gm8 .fi-btn.fi-color-success:hover { background: var(--live-fill2); }

.gm8 .fi-btn.fi-color-danger {
    background: transparent;
    border-color: transparent;
    color: var(--dim);
}

.gm8 .fi-btn.fi-color-danger:hover { background: var(--bad-fill); color: var(--bad); }

.gm8 .fi-btn.fi-color-gray {
    background: transparent;
    border-color: transparent;
    color: var(--dim);
}

.gm8 .fi-btn.fi-color-gray:hover { background: var(--fill-raised); color: var(--txt); }

/* ---------------------------------------------------------------------------
 * 19. Reduced motion
 * ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    .gm8 *,
    .gm8-slideover *,
    .gm8-scrim {
        animation: none !important;
        transition: none !important;
    }

    /* No view-transition rules needed here: the opt-in in section 2 is itself wrapped in
     * `prefers-reduced-motion: no-preference`, so under reduced motion the transition never
     * starts rather than starting and having its animations stripped. */
}

/* ---------------------------------------------------------------------------
 * 20. Ops screens
 *
 * Motion means live: chrome never moves, state does. Anything that animates here is driven by a
 * push source (Kuma, Wordfence) or by data actually arriving — never by a timer.
 * ------------------------------------------------------------------------ */

/* Stat strip. Leads every screen carrying numbers, above verdicts, tables and narrative. */
.gm8-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
}

.gm8-strip__value {
    font-size: 32px;
    font-weight: 500;
    line-height: 36px;
    letter-spacing: -0.035em;
    /* The odometer slides a digit column; without this it would paint outside the card. */
    overflow: hidden;
    display: block;
}

.gm8-strip__value--bad { color: var(--bad); }
.gm8-strip__value--warn { color: var(--warn); }
.gm8-strip__value--live { color: var(--live); }
.gm8-strip__value--violet { color: var(--violet); }

/* Severity pill — number only, fixed column, so titles align across rows.
   The severity *word* belongs in the meta line. Do not put it back in here. */
.gm8-sev {
    width: 66px;
    flex-shrink: 0;
}

.gm8-sev__pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border-radius: 11px;
    padding: 4px 9px;
    font-size: 9.5px;
    font-weight: 700;
    line-height: 1.4;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    border: 1px solid transparent;
}

.gm8-sev__pill--1 { background: var(--bad-fill); color: var(--bad); border-color: color-mix(in srgb, var(--bad) 40%, transparent); }
.gm8-sev__pill--2 { background: var(--warn-fill); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 34%, transparent); }
.gm8-sev__pill--3 { background: var(--fill-inset); color: var(--dim); border-color: var(--line); }

/* The signal stack: what folded into this incident. The count is what increments on dedup. */
.gm8-signals {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    flex-shrink: 0;
}

.gm8-signals__count {
    font-size: 11px;
    font-weight: 650;
    color: var(--dimmer);
    white-space: nowrap;
}

/* Specular sweep, appended by gm8.sweep() and removed when it finishes. */
.gm8-sweep {
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background: linear-gradient(100deg, transparent 20%, var(--sweep) 50%, transparent 80%);
}

/* Canvas drift — purple to red while a sev 1 is unresolved. A fixed layer over the shell
   gradient, faded in over 8s; cleared the moment nothing is critical. */
.gm8-drift {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    opacity: 0;
    background: radial-gradient(58% 52% at 12% -10%, color-mix(in srgb, var(--crit) 28%, transparent), transparent 68%);
    transition: opacity 8000ms ease-in-out;
}

.gm8-drift.is-on { opacity: 1; }

/* The metrics stamp. Not decoration — it is how an operator knows the age of the number, and it
   is what lets this screen go without a poll at all. */
.gm8-stamp {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    padding: 12px 18px;
    border-radius: 16px;
    background: var(--fill-surface);
    border: 1px solid var(--edge);
    font-size: 12.5px;
    color: var(--dimmer);
}

.gm8-stamp__note { flex: 1 1 260px; min-width: 260px; }
.gm8-stamp--stale { border-color: var(--warn-edge); }

/* Sparkline. Hand-rolled polyline, no charting library. */
.gm8-spark {
    width: 100%;
    height: 34px;
    display: block;
    overflow: visible;
}

.gm8-spark polyline {
    fill: none;
    /* Default stroke matters: memory and disk have no threshold, so they carry no status tone.
       Without this they would be drawn with no colour at all and simply not appear. */
    stroke: var(--violet-mark);
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
}

.gm8-spark--live polyline { stroke: var(--live-mark); }
.gm8-spark--warn polyline { stroke: var(--warn-mark); }
.gm8-spark--bad polyline { stroke: var(--bad-mark); }

/* Progress / load track, shared by run queues, load bars, confidence and clean runs. */
.gm8-track {
    height: 7px;
    border-radius: 5px;
    background: var(--fill-track);
    overflow: hidden;
}

.gm8-track__fill {
    height: 100%;
    border-radius: 5px;
    transition: width 520ms cubic-bezier(0.3, 0.7, 0.3, 1), background-color 520ms ease;
}

.gm8-track__fill--live { background: var(--live-mark); }
.gm8-track__fill--warn { background: var(--warn-mark); }
.gm8-track__fill--bad { background: var(--bad-mark); }
.gm8-track__fill--brand { background: linear-gradient(90deg, var(--brand-ramp), var(--violet-mark)); }

/* Any grid column holding text needs minmax(0, …). A bare 1fr resolves as
   max(min-content, share), so a row-per-grid table drifts column to column and overflows its
   panel. This was a real defect caught in review — keep the floors. */
.gm8-rowgrid {
    display: grid;
    align-items: center;
    gap: 10px 14px;
}

.gm8-rowgrid > * { min-width: 0; }

.gm8-ellipsis {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Resolved-today strip. The ratio is the project's own scoreboard, so it gets a permanent home. */
.gm8-scoreboard {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 14px;
    padding: 14px 18px;
    border-radius: 16px;
    background: var(--fill-recessed);
    border: 1px solid var(--edge);
    font-size: 12.5px;
    color: var(--dimmer);
}

@media (prefers-reduced-motion: reduce) {
    .gm8-drift,
    .gm8-track__fill {
        transition: none !important;
    }
}

/* ---------------------------------------------------------------------------
 * 21. Incident view — hero, lifecycle stepper, recommendation cards, signal feed
 *
 * Same law as everywhere else: entrances run once, the sev-1 throb and the live dot loop are the
 * only things allowed to repeat, and both say something true (this is still open, this is still
 * critical) rather than just moving for its own sake.
 * ------------------------------------------------------------------------ */

.gm8-hero {
    position: relative;
    padding: 26px 28px 24px;
    overflow: hidden;
}

/* A hero that arrived by morphing out of a row must not also run the gm8-rise it inherits from
 * .gm8-panel — the two compound into a lift on top of a slide.
 *
 * The class is added by gm8-motion.js on pagereveal, and only when elements were actually paired.
 * Arriving any other way — a Slack deep link, a bookmark, a reload, a list that has no markers —
 * never gets the class and keeps the entrance. An earlier attempt gated this on @supports, which
 * asks whether the *browser* could morph rather than whether it *did*, and so killed the
 * entrance on every path into this page.
 *
 * Two classes on purpose: .gm8-panel sets the animation further up the file, and a single-class
 * override would lose the cascade at equal specificity. */
.gm8-hero.gm8-vt-arrived {
    animation: none;
}

.gm8-hero--bad {
    background: linear-gradient(155deg, color-mix(in srgb, var(--crit) 24%, transparent), color-mix(in srgb, var(--brand) 14%, transparent));
    border-color: var(--bad-edge2);
    box-shadow: 0 10px 40px color-mix(in srgb, var(--crit) 22%, transparent);
}

.gm8-hero--warn {
    background: linear-gradient(155deg, var(--warn-fill), color-mix(in srgb, var(--brand) 12%, transparent));
    border-color: var(--warn-edge2);
}

.gm8-hero--neutral {
    background: linear-gradient(155deg, color-mix(in srgb, var(--brandMid) 22%, transparent), color-mix(in srgb, var(--brand) 8%, transparent));
}

.gm8-hero--live {
    background: linear-gradient(155deg, var(--live-fill), color-mix(in srgb, var(--brand) 10%, transparent));
    border-color: var(--live-edge);
}

/* The throb says one thing: a sev-1 is still open. It stops the instant that stops being true. */
.gm8-hero--critical::before {
    content: '';
    position: absolute;
    inset: -50% -10% auto -10%;
    height: 240%;
    background: radial-gradient(closest-side, color-mix(in srgb, var(--bad) 30%, transparent), transparent 70%);
    animation: gm8-hero-throb 3.2s ease-in-out infinite;
    pointer-events: none;
}

@keyframes gm8-hero-throb {
    0%, 100% { opacity: 0.5; transform: scale(1); }
    50% { opacity: 0.85; transform: scale(1.05); }
}

.gm8-hero__top {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px 12px;
}

.gm8-hero__title {
    position: relative;
    margin: 16px 0 0;
    font-size: 29px;
    font-weight: 650;
    letter-spacing: -0.02em;
    line-height: 1.18;
    text-wrap: pretty;
    max-width: 68ch;
}

.gm8-hero__summary {
    position: relative;
    margin: 8px 0 0;
    font-size: 14.5px;
    color: var(--dim);
    max-width: 78ch;
    text-wrap: pretty;
}

.gm8-hero__age {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    margin-left: auto;
}

.gm8-hero__age-value {
    font-size: 25px;
    font-weight: 500;
    letter-spacing: -0.03em;
    font-variant-numeric: tabular-nums;
}

.gm8-hero__age-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dimmer);
}

/* Lifecycle stepper: opened → acknowledged → resolved, filled by real timestamps only. */
.gm8-stepper {
    position: relative;
    display: flex;
    margin-top: 24px;
}

.gm8-stepper__item {
    position: relative;
    flex: 1;
    padding-top: 20px;
    min-width: 0;
}

.gm8-stepper__line {
    position: absolute;
    top: 5px;
    left: -50%;
    width: 100%;
    height: 2px;
    background: var(--fill-track);
    overflow: hidden;
}

.gm8-stepper__item:first-child .gm8-stepper__line { display: none; }

.gm8-stepper__line-fill {
    height: 100%;
    width: 100%;
    background: var(--live-mark);
    transform-origin: left;
    animation: gm8-grow 620ms var(--ease) both;
}

.gm8-stepper__dot {
    position: absolute;
    top: 0;
    left: 0;
    width: 12px;
    height: 12px;
    border-radius: 9999px;
    background: var(--fill-track);
    border: 2px solid var(--line-strong);
}

.gm8-stepper__dot--done {
    background: var(--live-mark);
    border-color: var(--live-mark);
    box-shadow: 0 0 10px var(--live-glow);
}

.gm8-stepper__dot--now {
    background: var(--warn-mark);
    border-color: var(--warn-mark);
    animation: gm8-pulse 2.6s ease-in-out infinite;
}

.gm8-stepper__label {
    font-size: 11px;
    font-weight: 650;
    color: var(--dimmer);
}

.gm8-stepper__item--done .gm8-stepper__label,
.gm8-stepper__item--now .gm8-stepper__label {
    color: var(--txt);
}

.gm8-stepper__time {
    font-size: 10.5px;
    color: var(--dimmer);
    margin-top: 1px;
}

/* Recommendation cards — the autonomy ladder's "recommend" rung made visible instead of buried
   in a Section nobody scrolled to. */
.gm8-rec {
    border-radius: 18px;
    background: var(--fill-surface);
    border: 1px solid var(--edge);
    border-left: 3px solid var(--violet-mark);
    padding: 16px 18px;
    animation: gm8-rise 460ms var(--ease) both;
}

.gm8-rec__title {
    font-weight: 650;
    font-size: 14px;
}

.gm8-rec__detail {
    font-size: 13px;
    color: var(--dim);
    margin-top: 4px;
    text-wrap: pretty;
}

/* Signal feed: the connecting line is the "these folded into one incident" claim, drawn once. */
.gm8-feed {
    position: relative;
    display: flex;
    flex-direction: column;
    padding-left: 22px;
}

.gm8-feed::before {
    content: '';
    position: absolute;
    top: 6px;
    bottom: 6px;
    left: 5px;
    width: 2px;
    background: linear-gradient(var(--edge2), transparent);
}

.gm8-feed__item {
    position: relative;
    padding: 12px 0 12px 14px;
    animation: gm8-rise 420ms var(--ease) both;
}

.gm8-feed__item + .gm8-feed__item { border-top: 1px solid var(--line-hairline); }

.gm8-feed__dot {
    position: absolute;
    left: -22px;
    top: 20px;
    width: 10px;
    height: 10px;
    border-radius: 9999px;
    background: var(--violet-mark);
    box-shadow: 0 0 0 3px var(--canvas);
}

.gm8-feed__head {
    display: flex;
    align-items: baseline;
    gap: 8px;
    flex-wrap: wrap;
}

.gm8-feed__time {
    font-size: 11px;
    color: var(--dimmer);
    white-space: nowrap;
}

.gm8-feed__message {
    margin-top: 3px;
    font-size: 13px;
    color: var(--dim);
    text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
    .gm8-hero--critical::before {
        animation: none !important;
        opacity: 0.6;
    }
}

/* ---------------------------------------------------------------------------
 * 22. The light ground — component corrections
 *
 * Everything that re-themes on a token has already re-themed. What is left is the set of
 * places where a *percentage* was chosen for a dark ground and is not merely different
 * over a light one but wrong: brand mixed toward transparent over near-black lands as a
 * tint, and the same mix over near-white lands as a bruise. Each rule below is the same
 * design intent restated at the strength this ground needs.
 *
 * Keep this section small. A new component belongs in the token layer, not here — if you
 * find yourself adding a rule, check first whether the role you want is missing from
 * section 1 instead.
 * ------------------------------------------------------------------------ */

/* The canvas glow. 42% brand over near-black is atmosphere; over near-white it is a stain. */
:root:not(.dark) body.fi-body {
    background-image:
        radial-gradient(58% 52% at 10% -10%,
            color-mix(in srgb, var(--brandMid) 11%, transparent),
            color-mix(in srgb, var(--brandMid) 0%, transparent) 68%),
        radial-gradient(46% 44% at 94% -8%,
            color-mix(in srgb, var(--brandDeepMid) 9%, transparent),
            color-mix(in srgb, var(--brandDeepMid) 0%, transparent) 70%),
        radial-gradient(44% 40% at 42% 112%,
            color-mix(in srgb, var(--brand) 7%, transparent),
            color-mix(in srgb, var(--brand) 0%, transparent) 68%);
}

/* The sidebar is the identity and stays purple. Translucent brand over a light canvas
   would wash it to lavender and take the white labels on it down to unreadable, so here
   it is opaque and the ink over it never changes. */
:root:not(.dark) .fi-main-sidebar {
    background: linear-gradient(180deg, var(--brand), var(--brandDeep));
}


:root:not(.dark) .gm8-panel__header {
    background: linear-gradient(100deg, color-mix(in srgb, var(--brandMid) 8%, transparent), transparent);
}

:root:not(.dark) .gm8-panel--brand {
    background: linear-gradient(155deg, color-mix(in srgb, var(--brandMid) 14%, transparent), color-mix(in srgb, var(--brand) 5%, transparent));
    box-shadow: 0 6px 22px color-mix(in srgb, var(--brand) 14%, transparent);
}

:root:not(.dark) .gm8-panel--heat {
    background: linear-gradient(150deg, color-mix(in srgb, var(--brandMid) 9%, transparent), color-mix(in srgb, var(--warn) 5%, transparent));
}

:root:not(.dark) .gm8-panel--bad > .gm8-panel__header {
    background: linear-gradient(100deg, color-mix(in srgb, var(--crit) 11%, transparent), transparent);
}

:root:not(.dark) .gm8-card--leave {
    background: linear-gradient(150deg, color-mix(in srgb, var(--brandMid) 11%, transparent), color-mix(in srgb, var(--brand) 4%, transparent));
}

/* The primary action goes opaque: a translucent purple over white loses the contrast its
   white label depends on. */
:root:not(.dark) .gm8-btn--primary {
    background: linear-gradient(135deg, var(--brandMid), var(--brand));
    box-shadow: 0 6px 18px color-mix(in srgb, var(--brand) 26%, transparent);
}

:root:not(.dark) .gm8-btn--primary:hover {
    /* Deepens rather than brightens. The first version went to --brandHi, which cannot carry
       white text (4.22:1, and 2.60:1 under the brighter accent) - and on a light ground a
       control that darkens under the cursor is the right direction anyway. */
    background: linear-gradient(135deg, var(--brand), var(--brandLo));
    box-shadow: 0 10px 26px color-mix(in srgb, var(--brand) 34%, transparent);
}

/* A column has to hold its own against a white panel all the way to its foot. */
:root:not(.dark) .gm8-columns__bar {
    background: linear-gradient(180deg, var(--violet), color-mix(in srgb, var(--brandMid) 55%, transparent));
}

/* The incident hero washes, and the two things that say "still critical". */
:root:not(.dark) .gm8-hero--bad {
    background: linear-gradient(155deg, color-mix(in srgb, var(--crit) 12%, transparent), color-mix(in srgb, var(--brand) 5%, transparent));
    box-shadow: 0 8px 26px color-mix(in srgb, var(--crit) 12%, transparent);
}

:root:not(.dark) .gm8-hero--warn {
    background: linear-gradient(155deg, var(--warn-fill), color-mix(in srgb, var(--brand) 4%, transparent));
}

:root:not(.dark) .gm8-hero--neutral {
    background: linear-gradient(155deg, color-mix(in srgb, var(--brandMid) 10%, transparent), color-mix(in srgb, var(--brand) 4%, transparent));
}

:root:not(.dark) .gm8-hero--live {
    background: linear-gradient(155deg, var(--live-fill), color-mix(in srgb, var(--brand) 4%, transparent));
}

:root:not(.dark) .gm8-hero--critical::before {
    background: radial-gradient(closest-side, color-mix(in srgb, var(--bad) 14%, transparent), transparent 70%);
}

:root:not(.dark) .gm8-drift {
    background: radial-gradient(58% 52% at 12% -10%, color-mix(in srgb, var(--crit) 11%, transparent), transparent 68%);
}
