/*
 * Aura theme — Industry Master Phase 359
 *
 * Canonical token surface for the application's frontend theme.
 * Sources of truth:
 *   - @vaadin/aura 25.1.2 — Aura token definitions (bundled by Vaadin
 *     platform 25.1.4; see UI-SPEC §"Stack Pre-Flight Requirement")
 *   - UI-SPEC §"Design System" + §"Stack Pre-Flight Requirement" (rev 2)
 *
 * Forbidden: --lumo-* tokens. Existing pre-359 references in OTHER files
 * (NotificationPanel, MapView, etc.) are migrated by Phase 359.2 charter
 * (authored by Plan 13). This file is canonicalized for Phase 359 and
 * gated by saas-app/scripts/lumo-grep-gate.sh against the theme dir.
 *
 * Architecture — Civic light is the ONLY skin (Phase 437.1 D-03/D-05):
 *
 * 1. html {} — REAL Aura 25.1.2 API tokens.
 *    These drive Vaadin component rendering via Aura's internal light-dark()
 *    system. color-scheme is pinned light at :root, so Aura's light branch is
 *    always taken; no OS-following init module remains.
 *
 * 2. :root {} — App-level aliases + Civic BASE surface tokens.
 *    Maps --im-* design tokens to --aura-* names used in our custom view CSS,
 *    and declares the Civic light surface family (--bg/--surface/--accent-1/
 *    --led-*) on :root (folded from the retired civic-atlas.css override).
 *    The dark :root[data-color-scheme] alias block and the V19 dark palette
 *    presets were DELETED at the atomic flip — there is no dark branch.
 */

/* TODO(phase-359-2): legacy --lumo-* references in OTHER files (e.g.
 * NotificationPanel, MapView) are migrated by Phase 359.2 charter.
 * Do not add new --lumo-* tokens to this file or any file under
 * saas-app/src/main/frontend/themes/portal/ — the lumo-grep-gate.sh
 * verify-phase script enforces this scope. */

/* ─────────────────────────────────────────────────────────────────────────
   SECTION 1 — Real Aura 25.1.1 API
   ───────────────────────────────────────────────────────────────────────── */
html {
  /* Font */
  /* The HOUSE font, not the OS font. This said `system-ui` until 2026-08-24, which meant the
     product rendered in San Francisco on a Mac, Segoe on Windows and Roboto on Android -- three
     different-looking applications, none of them ours, and the single largest reason the UI read
     as generic. IBM Plex Sans was already downloaded and @font-face'd in fonts.css; it was simply
     unreachable unless a user went to Preferences and picked it by hand. FontPreference.SYSTEM
     now means "the house default" rather than "whatever the OS ships". */
  --aura-font-family: 'IBM Plex Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  /* Phase 359 (UI-SPEC §"Typography" + §"Stack Pre-Flight Requirement" rev 2):
     Mono accents for status codes, IDs, and numeric stats per industrial-utilitarian
     direction (D-24). JetBrains Mono at weight 400 — one weight, no italic. Aura 25.1.2
     ships no mono-family token, so we declare it here as a project-local Aura alias. */
  --aura-font-family-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --aura-base-font-size: 14;   /* unitless px — Aura computes xs–xl scale from this */
  --aura-base-size:       16;  /* unitless — controls gap / padding computed vars */
  --aura-base-radius:      6;  /* unitless, range 0–10 */

  /* Accent: primary action color — drives buttons, focus rings, links */
  --aura-accent-color-light: #2563EB;
  --aura-accent-color-dark:  #5B8CFF;   /* sketch 084 accent */

  /* Background: page canvas */
  --aura-background-color-light: #F8FAFC;
  /* Sketch 084's ground, not a generic dark. Every other dark value below is chosen AGAINST
     this one, so changing it re-tunes the whole dark branch — that is the point of the pair. */
  --aura-background-color-dark:  #0A0E14;

  /* 7-color Aura palette — used by status variants and badge utilities */
  --aura-blue:    #2563EB;
  --aura-green:   #16A34A;
  --aura-red:     #DC2626;
  --aura-orange:  #D97706;
  --aura-yellow:  #CA8A04;
  --aura-purple:  #7C3AED;

/* ─── Scheme-adaptive blockers (2026-08-24) ───────────────────────────────
   These four are Aura API that Aura COMPUTES PER COLOUR SCHEME. Pinning them to
   a single light value is what made `color-scheme: dark` reachable and empty:
   every surface stayed white however the scheme was set.

   Each is now wrapped in the native `light-dark()` function. That keeps the LIGHT
   value byte-identical to what shipped — Civic light cannot regress, by
   construction — and adds the dark branch that was missing. `light-dark()`
   resolves at the point of USE against the element's computed `color-scheme`, so
   the App Layout drawer (global scheme) and the content area
   (`--aura-content-color-scheme: light`) resolve differently from the same
   declaration. That is the split Aura documents for dark navigation over light
   content, and it is why this needs four values rather than a theme fork.

   Aura's docs permit overriding these read-only properties on the condition that
   BOTH schemes meet contrast requirements — the dark values below are chosen
   against `--aura-background-color-dark` (#0D1117), not guessed from the light ones.
   ────────────────────────────────────────────────────────────────────────── */
  /* Neutral drives Aura's computed TEXT and BORDER colours. #94A3B8 on #0D1117 is ~7.4:1. */
  /* #8B96A9 on #10151F is ~5.9:1 — the reference's own #6B7689 is ~3.8:1 and fails AA, which is
     the single deliberate deviation from sketch 084's palette. */
  --aura-neutral: light-dark(#64748B, #8B96A9);
}

/* ─────────────────────────────────────────────────────────────────────────
   SECTION 2 — App-level aliases (light defaults)
   ───────────────────────────────────────────────────────────────────────── */

/* === Phase 359 token aliases ===
   Project-local --im-* aliases mapping to Aura tokens. Recipe styles in
   Tier A (Plans 359-05 / 359-06) consume these names; future plans extend
   this surface. Two rev-2 amendments forced by 25.1.2 token reality:
     1. --aura-font-family-mono (declared in html {} above) — Aura 25.1.2
        ships no mono-family token; project-local alias.
     2. --im-error-color: var(--aura-red) — Aura 25.1.2 ships no
        error-color semantic token; declared in :root {} below. */
:root {
  /* ─── Primary ──────────────────────────────────────────────────────── */
  --aura-primary-color:            var(--im-color-primary, #2563EB);
  --aura-primary-color-rgb:        37, 99, 235;
  --aura-primary-color-10pct:      color-mix(in srgb, var(--aura-primary-color) 10%, transparent);
  --aura-primary-color-50pct:      color-mix(in srgb, var(--aura-primary-color) 50%, transparent);
  --aura-primary-5pct:             color-mix(in srgb, var(--aura-primary-color) 5%, transparent);
  --aura-primary-10pct:            color-mix(in srgb, var(--aura-primary-color) 10%, transparent);
  --aura-primary-text-color:       var(--im-color-primary-dark, #1D4ED8);
  --aura-primary-contrast-color:   var(--im-color-on-primary, #FFFFFF);
  --aura-color-primary:            var(--aura-primary-color);

  /* ─── Accent (alias for primary in Aura) ───────────────────────────── */
  --aura-accent-color:             var(--aura-primary-color);
  --aura-accent-color-10pct:       var(--aura-primary-color-10pct);
  /* Accent text must invert: a #1D4ED8 label is unreadable on #0D1117. #93B4FF is ~7.1:1. */
  --aura-accent-text-color:        light-dark(var(--aura-primary-text-color), #9DBBFF);
  --aura-accent-contrast-color:    var(--aura-primary-contrast-color);
  --aura-accent-surface:           light-dark(var(--im-color-primary-surface, #EFF6FF),
                                              color-mix(in srgb, #5B8CFF 18%, #0A0E14));

  /* ─── Success ──────────────────────────────────────────────────────── */
  --aura-success:                  var(--im-color-success, #16A34A);
  --aura-success-color:            var(--aura-success);
  --aura-success-10:               color-mix(in srgb, var(--aura-success) 10%, transparent);
  --aura-success-10pct:            var(--aura-success-10);
  --aura-success-50pct:            color-mix(in srgb, var(--aura-success) 50%, transparent);
  --aura-success-color-10pct:      var(--aura-success-10);
  --aura-success-text-color:       var(--im-color-success-text, #15803D);
  --aura-color-success:            var(--aura-success);
  --aura-green-text:               var(--aura-success-text-color);

  /* ─── Warning ──────────────────────────────────────────────────────── */
  --aura-warning:                  var(--im-color-warning, #D97706);
  --aura-warning-color:            var(--aura-warning);
  --aura-warning-10:               color-mix(in srgb, var(--aura-warning) 10%, transparent);
  --aura-warning-10pct:            var(--aura-warning-10);
  --aura-warning-color-10pct:      var(--aura-warning-10);
  --aura-warning-color-50pct:      color-mix(in srgb, var(--aura-warning) 50%, transparent);
  --aura-warning-text-color:       var(--im-color-warning-text, #B45309);
  --aura-color-warning:            var(--aura-warning);
  --aura-orange-text:              var(--aura-warning-text-color);

  /* ─── Error / Danger ───────────────────────────────────────────────── */
  --aura-error:                    var(--im-color-error, #DC2626);
  --aura-error-color:              var(--aura-error);
  --aura-danger-color:             var(--aura-error);
  --aura-error-10:                 color-mix(in srgb, var(--aura-error) 10%, transparent);
  --aura-error-10pct:              var(--aura-error-10);
  --aura-error-color-10pct:        var(--aura-error-10);
  --aura-error-text-color:         var(--im-color-error-text, #B91C1C);
  --aura-red-text:                 var(--aura-error-text-color);
  /* Phase 359 rev-2 amendment (UI-SPEC §"Stack Pre-Flight Requirement" item 2):
     Aura 25.1.2 ships no error-color semantic token. Project-local alias for
     destructive-confirmation surfaces; foreground destructive copy still uses
     var(--aura-red-text) for contrast-adjusted readability via light-dark(). */
  --im-error-color:                var(--aura-red);

  /* ─── Info (blue alias) ────────────────────────────────────────────── */
  --aura-blue-text:                var(--im-color-info-text, #1D4ED8);

  /* ─── Surfaces & Backgrounds ───────────────────────────────────────── */
  --aura-background:               var(--im-color-background, #F8FAFC);
  --aura-base:                     var(--im-color-background, #F8FAFC);
  --aura-base-color:               var(--im-color-background, #F8FAFC);
  --aura-surface:                  var(--im-color-surface, #FFFFFF);
  /* Elevated surface. #161D2B is one step above the dark page ground (#0D1117). */
  /* SUPERSEDED: two later :root declarations of this same key (var(--surface)) override
     this line -- same specificity, last wins. Kept only so the name is greppable here;
     the scheme now comes from --surface itself, which carries both branches. */
  --aura-surface-color:            light-dark(var(--im-color-surface, #FFFFFF), #10151F);
  --aura-surface-container:        var(--im-color-neutral-50,  #F8FAFC);
  --aura-surface-variant:          var(--im-color-neutral-100, #F1F5F9);
  --aura-color-surface:            var(--aura-surface);
  --aura-color-background-secondary: var(--im-color-neutral-100, #F1F5F9);

  /* ─── Contrast Scale ───────────────────────────────────────────────── */
  --aura-contrast:                 var(--im-color-neutral-900, #0F172A);
  --aura-contrast-5:               color-mix(in srgb, var(--aura-contrast) 5%, transparent);
  --aura-contrast-5pct:            var(--aura-contrast-5);
  --aura-contrast-10:              color-mix(in srgb, var(--aura-contrast) 10%, transparent);
  --aura-contrast-10pct:           var(--aura-contrast-10);
  --aura-contrast-20:              color-mix(in srgb, var(--aura-contrast) 20%, transparent);
  --aura-contrast-20pct:           var(--aura-contrast-20);
  --aura-contrast-30pct:           color-mix(in srgb, var(--aura-contrast) 30%, transparent);
  --aura-contrast-40:              color-mix(in srgb, var(--aura-contrast) 40%, transparent);
  --aura-contrast-40pct:           var(--aura-contrast-40);
  --aura-contrast-50:              color-mix(in srgb, var(--aura-contrast) 50%, transparent);
  --aura-contrast-50pct:           var(--aura-contrast-50);
  --aura-contrast-60:              color-mix(in srgb, var(--aura-contrast) 60%, transparent);
  --aura-contrast-60pct:           var(--aura-contrast-60);
  --aura-contrast-70:              color-mix(in srgb, var(--aura-contrast) 70%, transparent);
  --aura-contrast-70pct:           var(--aura-contrast-70);
  --aura-color-contrast-70pct:     var(--aura-contrast-70pct);
  --aura-color-outline:            var(--im-color-border, #E2E8F0);
  --aura-color-on-surface-variant: var(--im-color-text-secondary, #475569);
  --aura-color-text-secondary:     var(--im-color-text-secondary, #475569);

  /* ─── Text Colors ──────────────────────────────────────────────────── */
  --aura-body-text-color:          var(--im-color-text-primary, #0F172A);
  --aura-heading-text-color:       var(--im-color-text-primary, #0F172A);
  --aura-text-primary:             var(--im-color-text-primary, #0F172A);
  --aura-text-secondary:           var(--im-color-text-secondary, #475569);
  --aura-secondary-text-color:     var(--im-color-text-secondary, #475569);
  --aura-tertiary-text-color:      var(--im-color-text-tertiary, #94A3B8);
  --aura-disabled-text-color:      var(--im-color-text-disabled, #94A3B8);

  /* ─── Input placeholder (SEED-026 #3 — Phase 392 a11y) ─────────────────
   * Dedicated placeholder color, SEPARATE from --text-mute (which is reused
   * for disabled text + subtle non-input copy and must NOT be brightened
   * globally). global-overhaul.css points
   * --vaadin-input-field-placeholder-color at this token instead of
   * --text-mute. Per-palette AA-passing overrides live in the palette blocks
   * below. Default (light/white-field) = #64748B (≈ 4.76:1 on white). */
  --im-placeholder-color: #64748B;

  /* ─── Typography ───────────────────────────────────────────────────── */
  --aura-font-size-xs:             var(--im-typography-font-size-xs, 11px);
  --aura-font-size-s:              var(--im-typography-font-size-s,  13px);
  --aura-font-size-m:              var(--im-typography-font-size-m,  14px);
  --aura-font-size-l:              var(--im-typography-font-size-l,  16px);
  --aura-font-size-xl:             var(--im-typography-font-size-xl, 20px);
  --aura-font-size-xxl:            var(--im-typography-font-size-2xl, 24px);
  --aura-font-weight-regular:      var(--im-typography-font-weight-normal,   400);
  --aura-font-weight-medium:       var(--im-typography-font-weight-medium,   500);
  --aura-font-weight-semibold:     var(--im-typography-font-weight-semibold, 600);

  /* ─── Spacing ──────────────────────────────────────────────────────── */
  --aura-space-xs:                 var(--im-spacing-xs, 4px);
  --aura-space-s:                  var(--im-spacing-s,  8px);
  --aura-space-m:                  var(--im-spacing-m,  16px);
  --aura-space-l:                  var(--im-spacing-l,  24px);
  --aura-space-xl:                 var(--im-spacing-xl, 32px);

  /* ─── Border Radius ────────────────────────────────────────────────── */
  --aura-border-radius-s:          var(--im-border-radius-s, 4px);
  --aura-border-radius-m:          var(--im-border-radius-m, 8px);
  --aura-border-radius-l:          var(--im-border-radius-l, 12px);

  /* ─── Shadows ──────────────────────────────────────────────────────── */
  --aura-box-shadow-xs:            var(--im-elevation-xs, 0 1px 2px rgba(15,23,42,0.05));
  --aura-box-shadow-s:             var(--im-elevation-s,  0 2px 4px rgba(15,23,42,0.08));
  --aura-shadow-s:                 var(--aura-box-shadow-s);
  --aura-shadow-m:                 var(--im-elevation-m, 0 4px 12px rgba(15,23,42,0.10));
}

/* ─────────────────────────────────────────────────────────────────────────
   SECTION 2b — Dark mode app-alias overrides RETIRED (Phase 437.1 D-03/D-05)
   The dark app-alias override block was DELETED at the atomic Civic flip. With
   @ColorScheme(LIGHT) and the OS-following init module gone, the dark color
   scheme attribute is never written, so this selector could only ever be
   dead-but-dangerous tech debt. All --aura-* aliases resolve to the
   Civic light --im-color-* values declared above; severity stays via the
   --led-* civic constants below.
   ───────────────────────────────────────────────────────────────────────── */

/* ─────────────────────────────────────────────────────────────────────────
   SECTION 2c — Civic Atlas / Core-Line BASE surface tokens (Phase 437.1 D-05)

   The V19 industrial-cyber dark palette presets (6 user-switchable dark
   blocks + the dark default, all keyed on a palette attribute) were DELETED
   at the atomic Civic flip. With Civic light as the ONLY skin there is
   nothing to toggle against, so the palette tier is retired entirely.

   The Civic light surface family lifted verbatim from civic-atlas.css:96-101
   (the proven foundation, direction locked 2026-06-08) now lives at :root, on
   the same cascade tier as the Aura theme. Component CSS that references
   var(--bg) / var(--surface) / var(--accent-1) / var(--led-*) / etc.
   re-resolves to Civic daylight for free — the whole point of the fold
   (the @layer / !important fragility of the old override tier disappears).

   Token semantics (unchanged names, Civic values):
     --accent-1          primary action / CTA / focus / active (Civic teal)
     --accent-2          secondary CTA hover, info accents
     --accent-1-glow     low-alpha derivative for box-shadows / focus ring
     --bg                page canvas
     --surface           work-container panel (white)
     --surface-2         secondary panel surface
     --bezel             on-accent ink / panel chrome
     --text / --text-2 / --text-mute   3-step ink contrast scale (on white)
     --border / --border-soft          rule lines + hairline dividers
     --led-red / --led-amber / --led-green   semantic status (AA on white)
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* Status LED colors — Civic light severity, tuned for contrast on white
     (civic-atlas.css:101). Semantic status signaling is palette-independent
     by design; with one skin these are simply the base constants. */
  --led-red:   #C73535;
  --led-amber: #B26A00;
  --led-green: #16845B;
}

/* Media letterbox — the fill behind a <video> frame whose aspect ratio does not
   match its box (SafeVideo.of). Like the LED family above this is deliberately
   palette-INDEPENDENT: it must stay black under every skin, so it is NOT an
   alias of --surface / --aura-surface-color, both of which resolve to a light
   value under the Civic daylight base and would letterbox the clip white.

   Lives here rather than beside the rest of the --fm-* family in
   coreline-kit.css because that file is under Phase 572's stylelint
   remediation; aura-theme.css is the file GlobalCssInitListener links on every
   page, so a :root declaration here reaches the same documents. Consumers
   always spell it var(--fm-video-letterbox, #000) — the fallback makes the
   rendered colour identical if this declaration is ever moved or dropped. */
:root {
  --fm-video-letterbox: #000;
}

/* ─── Civic Atlas BASE surfaces (folded from civic-atlas.css:96-101 + 280-291)
   The 6 dark V19 palette presets + the dark default were deleted here. The surface
   family lives directly on :root and the --aura-* alias hybrid carries it into
   component CSS that reads var(--aura-*).

   Civic light is no longer the ONLY skin: every token below carries both branches
   via light-dark(), light branch byte-identical to what shipped. That is what lets
   the ~20 downstream --aura-* aliases (--aura-surface-color: var(--surface), and
   friends) follow the scheme without being touched -- and it is why those aliases
   re-declaring the same key three times in this file stopped mattering. Wrapping an
   alias is futile when a later declaration of the same name overrides it; wrapping
   the SOURCE it reads is not.

   The color-scheme declaration below is a DEFAULT, not a pin. Page.setColorScheme
   writes documentElement.style.colorScheme -- an inline style -- which outranks this
   rule, so this only governs a document Flow has not reached yet. */
:root {
  color-scheme: light;

  /* Civic daylight surfaces (civic-atlas.css:96-101) */
  --bg:             light-dark(#f4f6f8, #0A0E14);
  --surface:        light-dark(#ffffff, #10151F);
  --surface-2:      light-dark(#eef2f5, #182231);
  --bezel:          light-dark(#ffffff, #10151F);
  --text:           light-dark(#17212b, #E6EDF5);
  --text-2:         light-dark(#2c3a47, #C2CDDA);
  --text-mute:      light-dark(#5c6b78, #8B96A9); /* AA 4.7:1 on white, 6.4:1 on #10151F */
  --text-mute-raised: light-dark(#5c6b78, #8B96A9);
  --im-placeholder-color: light-dark(#5c6b78, #8B96A9); /* SEED-026 #3: AA-passing placeholder on either field surface */
  --security-row-cell-color: light-dark(#17212b, #E6EDF5); /* A11Y-01: civic ink on the security band */
  --border:         light-dark(#c9d3dc, #26303F);
  --border-soft:    light-dark(#e4e9ee, #1B2431);

  /* Civic accent — Río de la Plata teal */
  --accent-1:       light-dark(#007f8c, #2AA9B5);
  --accent-1-glow:  light-dark(rgba(0, 127, 140, 0.18), rgba(42, 169, 181, 0.22));
  --accent-2:       light-dark(#005e68, #1E8B96);
  --accent-2-glow:  light-dark(rgba(0, 94, 104, 0.15), rgba(30, 139, 150, 0.20));

  /* --aura-* alias hybrid — carries Civic surfaces into existing component CSS.
     These re-declare the Aura surface family against the Civic light core tokens
     so the routed view + grid + dialogs paint light (Pitfall-2 fix from the fold:
     --aura-surface* froze to the V19 dark --surface when declared once on :root;
     re-declaring here against the light values re-resolves them). */
  --aura-background:        var(--bg);
  --aura-base:              var(--bg);
  --aura-base-color:        var(--bg);
  --aura-surface:           var(--surface);
  --aura-surface-color:     var(--surface);
  --aura-surface-container: var(--surface-2);
  --aura-surface-variant:   var(--surface-2);
  --aura-background-color-dark: var(--bg);
  --aura-accent-color:      var(--accent-1);
  --aura-accent-color-10pct: var(--accent-1-glow);
  --aura-text-primary:      var(--text);
  --aura-text-secondary:    var(--text-2);
  --aura-body-text-color:   var(--text);
  --aura-heading-text-color: var(--text);
  --aura-disabled-text-color: var(--text-mute);
  --aura-color-outline:     var(--border);
}

/* ─────────────────────────────────────────────────────────────────────────
   D-09 Aura normalization layer (Phase 437.1)

   Aura's component shadow-DOM is gated on `color-scheme` + its own internal
   surface vars, NOT on our core tokens. With Civic light as the base we pin
   color-scheme:light at :root (done above) and map Aura's internal surface
   family to the Civic tokens at the highest scope, so unlayered Aura core
   styles (scrollbars, grid host, field/dialog chrome, header borders) read
   light against the light grid — the "Frankenstein-component" fix.

   Var names confirmed against the in-repo Aura alias surface (this file already
   declares --aura-base-color / --aura-surface-color / --aura-surface-container /
   --aura-surface-variant) and the proven civic-atlas.css:280-291 fold block that
   was visually verified live (Vaadin 25.1 Aura styling docs, MCP — see
   civic-atlas.css:266). --lumo-* is forbidden in this theme dir (lumo-grep-gate),
   so the normalization targets the Aura families only; Aura's native light
   default supplies the rest. */
html,
:root {
  color-scheme: light;
  /* Aura internal surface family → Civic light (re-resolves shadow-DOM light) */
  --aura-base-color:            var(--bg);
  --aura-surface:               var(--surface);
  --aura-surface-color:         var(--surface);
  --aura-surface-container:     var(--surface-2);
  --aura-surface-variant:       var(--surface-2);
  --aura-background-color-light: var(--surface);
  --aura-background-color-dark:  var(--bg);
}

/* ─────────────────────────────────────────────────────────────────────────
   Civic viewport-fill + grid theming (folded from civic-atlas.css:292-334)

   The routed view is app-layout's only non-slot child; Aura otherwise paints it
   with --aura-surface-color and wraps it as a floating rounded card sized to
   content. Pin it to the Civic gray canvas, re-assert the 100vh fill so kit rows
   give grids room to stretch, and flatten the card to an edge-to-edge canvas.
   With Civic as the base (no per-view skin override layer) the old !important
   on these rules is no longer needed to beat an override tier — but Aura's
   content-child sizing rules are UNLAYERED core styles, and unlayered beats
   layered regardless of specificity, so !important is retained here to sort
   above the cascade @layer the theme @CssImport is wrapped in (RESEARCH A2:
   keep only if a live test shows Aura unlayered rules still win — confirmed by
   the routed-view paint test, so retained). */
vaadin-app-layout > :not([slot]) {
  background: var(--bg);
  min-block-size: calc(100vh - var(--vaadin-app-layout-navbar-offset-top, 76px) - var(--vaadin-app-layout-navbar-offset-bottom, 26px) - 8px) !important;
  border: 0 !important;
  border-radius: 0 !important;
}

/* THE RULE ABOVE ASSUMES ONE UNSLOTTED CHILD, AND THE TENANT PORTAL HAS TWO.
   `vaadin-app-layout > :not([slot])` means "every unslotted child", and it gives EACH of them a
   full viewport of minimum height with `!important`. That is right for the routed view — it is
   what makes a short page still paint the background to the bottom edge — and wrong for anything
   else that lands in the content slot.
   TenantPortalLayout ships a bare `<div></div>` there: no class, no attributes, no children. With
   min-block-size: 100vh it claims the entire first screen, and the real view starts at 763px on a
   655px viewport — so EVERY /portal/* route (33 of them) opened on a 1280x800 laptop shows the
   tenant nothing but navigation until they scroll. Measured 2026-09-07 on /portal/announcements,
   /portal/bookings and /portal/amenity-booking: all three at exactly 763, against 81 for the
   operator shell. It survives a hard reload with a cache-busting parameter, so it is structural,
   not a leftover from SPA navigation.
   Excluding `:empty` is the narrowest correction: it changes nothing for the view (still 655px of
   minimum height, verified) and stops an empty node from claiming a screen. The empty div itself
   is the real defect and its origin is NOT identified — TenantPortalLayout adds no Div to the
   content slot and overrides neither setContent nor showRouterLayoutContent — so this rule is a
   guard, not a cure, and it is written to keep working if that div is ever found and removed. */
vaadin-app-layout > :not([slot]):empty { min-block-size: 0 !important; }

vaadin-grid {
  --vaadin-grid-background: var(--surface);
  --vaadin-grid-cell-background-color: var(--surface);
  --vaadin-grid-row-background-color: var(--surface);
  --vaadin-grid-row-odd-background-color: var(--surface-2);
  --vaadin-grid-row-hover-background-color: color-mix(in srgb, var(--accent-1) 8%, transparent);
  --vaadin-grid-row-selected-background-color: color-mix(in srgb, var(--accent-1) 14%, transparent);
  --vaadin-grid-border-color: var(--border);
  --vaadin-grid-header-text-color: var(--text-2);
  --vaadin-grid-header-font-weight: 600;
  color: var(--text);
}
/* header band: lift it off the body rows with the muted civic surface + a hairline */
vaadin-grid::part(header-cell) {
  background: var(--surface-2);
  color: var(--text-2);
  border-color: var(--border);
}
/* slotted cell content lives in the light DOM — pin ink so values read on white */
vaadin-grid-cell-content { color: var(--text); }

/* page header title/breadcrumb already token-driven; ensure the H2/H3 read as ink */
.im-page-header__title,
.im-page h2,
.im-page h3 { color: var(--text); }

/* =====================================================================
 * === Phase 359 Tier A recipe styles ===
 *
 * Reusable utility classes that Tier A recipes (Plans 359-05 / 359-06)
 * and earlier surfaces (Phase 358 Demo Console) consume.
 *
 * Conventions:
 *   - Aura tokens only (--aura-*, --vaadin-*, project-local --im-*).
 *     The Phase 359-07 lumo-grep-gate.sh script (verify phase) fails
 *     the build on any --lumo-* added under this theme directory.
 *   - Logical CSS properties only (D-32). The Phase 359-02 stylelint
 *     diff gate (verify phase) fails on margin-left / padding-right /
 *     inset-left / inset-right / border-left etc. on touched files.
 *
 * Section history:
 *   - Phase 358 (commit 79ef7c4cc): im-badge + im-pill utilities for
 *     Demo Console status pills (UI-SPEC §"Open Items For The Planner"
 *     item 2). Variants: neutral / success / warning / error.
 *   - Phase 359 Plans 05-06: Tier A recipes extend this section with
 *     zebra-striping grid styles, im-card-interactive, im-skip-link.
 * ===================================================================== */
.im-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--im-spacing-xs, 4px);
  padding-block: var(--im-spacing-xs, 4px);
  padding-inline: var(--im-spacing-s, 8px);
  border-radius: var(--im-border-radius-full, 9999px);
  font-size: var(--im-typography-font-size-s, 13px);
  font-weight: var(--im-typography-font-weight-medium, 500);
  line-height: var(--im-typography-line-height-tight, 1.25);
}

.im-badge--neutral {
  background: var(--im-color-neutral-100, var(--aura-background-color-light, #F8FAFC));
  color: var(--im-color-text-secondary, var(--aura-secondary-text-color, #334155));
}
.im-badge--success {
  background: var(--im-color-success-surface,
                color-mix(in srgb, var(--im-color-success, #16A34A) 12%, transparent));
  color: var(--im-color-success-text, var(--im-color-success, #16A34A));
}
.im-badge--warning {
  background: var(--im-color-warning-surface,
                color-mix(in srgb, var(--im-color-warning, #D97706) 12%, transparent));
  color: var(--im-color-warning-text, var(--im-color-warning, #D97706));
}
.im-badge--error {
  background: var(--im-color-error-surface,
                color-mix(in srgb, var(--im-color-error, #DC2626) 12%, transparent));
  color: var(--im-color-error-text, var(--im-color-error, #DC2626));
}

.im-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--im-spacing-xs, 4px);
  padding-block: var(--im-spacing-xs, 4px);
  padding-inline: var(--im-spacing-s, 8px);
  border-radius: var(--im-border-radius-full, 9999px);
  font-size: var(--im-typography-font-size-s, 13px);
  font-weight: var(--im-typography-font-weight-medium, 500);
  line-height: 1;
  min-block-size: 32px;
}

.im-pill--neutral {
  background: var(--im-color-neutral-100);
  color: var(--im-color-text-secondary);
}
.im-pill--success {
  background: var(--im-color-success-surface,
                color-mix(in srgb, var(--im-color-success, #16A34A) 12%, transparent));
  color: var(--im-color-success-text, var(--im-color-success, #16A34A));
}
.im-pill--warning {
  background: var(--im-color-warning-surface,
                color-mix(in srgb, var(--im-color-warning, #D97706) 12%, transparent));
  color: var(--im-color-warning-text, var(--im-color-warning, #D97706));
}
.im-pill--error {
  background: var(--im-color-error-surface,
                color-mix(in srgb, var(--im-color-error, #DC2626) 12%, transparent));
  color: var(--im-color-error-text, var(--im-color-error, #DC2626));
}

/* =====================================================================
 * Phase 359 Tier A recipe styles (Plan 06)
 * Per UI-SPEC §"Tier A Component Recipe Specifications" recipe 6 (Grid).
 * Logical properties only (D-32). Aura tokens only — no --lumo-* refs
 * (lumo-grep-gate.sh enforces this scope).
 * ===================================================================== */

/* GridRecipe: zebra striping + hover tint.
   ---------------------------------------------------------------------------
   SEED-217 / cross-AI R3 (2026-07-26, 3/3). This block previously read:

       .im-grid-zebra::part(row):nth-child(even) { ... }
       .im-grid-zebra [part="row"]:nth-child(even) { ... }

   with a comment saying both were kept "so styling applies regardless of which
   surface receives the part attribute". Neither ever applied:

   * `::part(row):nth-child(even)` is INVALID CSS. Selectors L4 allows only an
     explicit set of pseudo-classes after a pseudo-element (logical-combination
     and user-action, e.g. :hover / :focus). `:nth-child()` is tree-structural,
     so the whole rule was discarded by every browser that ever parsed it. It
     surfaced now only because Vaadin's pre-compiled bundle meant the theme was
     never minified locally, and lightningcss — unlike a browser — reports
     invalid selectors instead of silently dropping them.
   * `[part="row"]` without `::part()` is a plain attribute selector and cannot
     cross a shadow boundary at all, so it never matched either.
   * Even if structural selectors were legal here, the Grid VIRTUALISES rows:
     "nth child in the DOM" is not "nth row of data", so the stripes would have
     shifted as you scrolled.

   The supported mechanism is a style property on the host, verified against the
   Vaadin 25.2 Grid styling docs. `::part(even-row)` / `::part(odd-row)` also
   exist, but the docs are explicit that ROW-level background must go through the
   cell background property — the cells paint over the row surface. */
.im-grid-zebra {
  --vaadin-grid-row-odd-background-color: var(--aura-surface-container);
  --vaadin-grid-row-hover-background-color:
      color-mix(in srgb, var(--aura-accent-color) 5%, transparent);
}
/* The two `.im-grid-zebra [part="row"]` rules that stood here are gone with the
   ::part() ones above: a bare attribute selector cannot cross a shadow boundary,
   so they matched nothing either. They were VALID CSS, which is why no tool ever
   complained — the more durable half of this defect. */

/* CardRecipe: hover-shadow upgrade for the interactive variant. Resting
   shadow is applied inline via getStyle() in CardRecipe.build(); this rule
   adds the hover/focus state without forcing JS event handlers. */
.im-card-interactive {
  cursor: pointer;
  transition: box-shadow 120ms ease-in-out;
}
.im-card-interactive:hover,
.im-card-interactive:focus-visible {
  box-shadow: var(--aura-shadow-s);
}

/* AppLayoutRecipe: skip-to-content link is visually hidden until focused.
   Rendered before the drawer in tab order so keyboard users can bypass nav. */
.im-skip-link {
  position: absolute;
  inset-block-start: -9999px;
  inset-inline-start: 0;
  padding-block: var(--vaadin-padding-s, 8px);
  padding-inline: var(--vaadin-padding-m, 12px);
  background: var(--aura-surface);
  color: var(--aura-accent-text-color);
  z-index: 100;
}
.im-skip-link:focus,
.im-skip-link:focus-visible {
  inset-block-start: 0;
}

/* AppLayoutRecipe: active nav item indicator. Inline-start border + accent
   weight bump signal current route without relying on color alone. */
.im-applayout-nav-item--active {
  border-inline-start: 3px solid var(--aura-accent-color);
  font-weight: 600;
}

/* ─────────────────────────────────────────────────────────────────────────
   SECTION 3 — Phase 359 Plan 11: Welcome view (V19 industrial-cyber)

   Layout classes consumed by:
     industry-management/.../ui/welcome/{WelcomeView, UtilityBandComponent,
     StatusBandComponent, HeroPanelComponent, LiveGaugesComponent,
     CommitmentsPanel, CommitmentCard, LocalePickerComponent,
     PaletteSelectorComponent}.java

   Tokens: all references go through var(--bg / --surface / --surface-2 /
   --bezel / --text / --text-2 / --text-mute / --accent-1 / --accent-1-glow /
   --accent-2 / --accent-2-glow / --border / --border-soft / --led-{red,
   amber, green}). NO --lumo-*. Logical CSS only (D-32).

   Source: ports the V19 preview HTML structure
   (sketches/preview/v19-industrial-cyber.html lines 113-228) onto the
   palette-aware Aura tokens declared in Section 2c above.
   ───────────────────────────────────────────────────────────────────────── */

/* Page-level container */
.im-welcome-page {
  background: var(--bg);
  color: var(--text);
  font-family: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 13px;
  line-height: 1.55;
  min-block-size: 100vh;
  transition: background 0.3s, color 0.3s;
}
@media (prefers-reduced-motion: reduce) {
  .im-welcome-page { transition: none; }
}

/* Mounting screw glyph (8x8 dot at panel corners) */
.im-welcome-screw {
  position: absolute;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  background: var(--bezel);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.5),
              inset 1px 1px 1px rgba(255, 255, 255, 0.05);
}
.im-welcome-screw--tl { inset-block-start: 8px; inset-inline-start: 8px; }
.im-welcome-screw--tr { inset-block-start: 8px; inset-inline-end: 8px; }
.im-welcome-screw--bl { inset-block-end: 8px; inset-inline-start: 8px; }
.im-welcome-screw--br { inset-block-end: 8px; inset-inline-end: 8px; }

/* LED dot (status indicator) */
.im-welcome-led {
  display: inline-block;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
}
.im-welcome-led--live  { background: var(--led-green); box-shadow: 0 0 4px var(--led-green); }
.im-welcome-led--warn  { background: var(--led-amber); box-shadow: 0 0 4px var(--led-amber); }
.im-welcome-led--idle  { background: var(--text-mute); }
.im-welcome-led--brand { background: var(--led-green); box-shadow: 0 0 4px var(--led-green); }

/* Panel-label tab (▸ HERO · 01 etc.) — floats above panel top edge */
.im-welcome-panel-label {
  position: absolute;
  inset-block-start: -12px;
  inset-inline-start: 32px;
  padding-inline: 12px;
  padding-block: 4px;
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border-radius: 2px;
}
.im-welcome-panel-label--accent-1 { background: var(--accent-1); color: var(--bg); }
.im-welcome-panel-label--accent-2 { background: var(--accent-2); color: var(--bg); }

/* === Palette selector bar === */
.im-welcome-palette-bar {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  padding-block: 8px;
  padding-inline: 16px;
  background: rgba(0, 0, 0, 0.7);
  border-block-end: 1px solid var(--border-soft);
  backdrop-filter: blur(8px);
}
.im-welcome-palette-bar__label {
  font-size: 10px;
  letter-spacing: 0.2em;
  color: var(--text-mute);
  text-transform: uppercase;
}
.im-welcome-palette-divider {
  color: var(--border);
  font-size: 14px;
  padding-inline: 4px;
  user-select: none;
}
.im-welcome-palette-swatch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding-inline: 10px;
  padding-block: 4px;
  cursor: pointer;
  border: 1px solid transparent;
  background: transparent;
  color: var(--text-2);
  font-size: 11px;
  letter-spacing: 0.05em;
  transition: all 0.15s;
}
.im-welcome-palette-swatch:hover  { border-color: var(--border); color: var(--text); }
.im-welcome-palette-swatch:focus-visible {
  outline: 2px solid var(--accent-1);
  outline-offset: 2px;
}
.im-welcome-palette-swatch--active { border-color: var(--accent-1); color: var(--text); }
.im-welcome-palette-swatch--tier-1 { color: var(--text); }
/* Phase 363 Plan 03: WCAG 2.2 AA contrast (was --text-mute @ opacity 0.7 -> --text-2 @ opacity 1.0) */
.im-welcome-palette-swatch--tier-2 { color: var(--text-2); }
.im-welcome-palette-swatch--tier-2:hover { color: var(--text); }
.im-welcome-palette-swatch__dots {
  display: inline-flex;
  gap: 2px;
}
.im-welcome-palette-dot {
  inline-size: 10px;
  block-size: 10px;
  border-radius: 50%;
}
.im-welcome-palette-swatch__name {
  text-transform: uppercase;
}

/* === Utility band (header) === */
.im-welcome-utility {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-block: 12px;
  padding-inline: 32px;
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
  border-block-end: 1px solid var(--bezel);
  position: relative;
  /* Vaadin VerticalLayout child sizing gotcha: declare inline-size:100%
   * explicitly so the band stretches edge-to-edge instead of shrinking
   * to content width. Without this, the bottom border + nav items only
   * fill ~half the viewport on wide screens. */
  inline-size: 100%;
  box-sizing: border-box;
}
.im-welcome-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.15em;
  color: var(--text);
}
.im-welcome-utility__nav {
  display: flex;
  align-items: center;
  gap: 0;
}
.im-welcome-nav-link {
  padding-inline: 14px;
  color: var(--text-2);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}
.im-welcome-nav-link:hover { color: var(--accent-1); }
.im-welcome-signin {
  background: var(--accent-1);
  color: var(--bg);
  padding-inline: 18px;
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  box-shadow: 0 0 8px var(--accent-1-glow);
  margin-inline-start: 8px;
  transition: background 0.15s;
}
.im-welcome-signin:hover {
  background: var(--accent-2);
  box-shadow: 0 0 8px var(--accent-2-glow);
}

/* === Status band === */
.im-welcome-statusband {
  background: var(--bg);
  border-block-end: 1px solid var(--border);
  padding-block: 10px;
  padding-inline: 32px;
  display: flex;
  gap: 32px;
  font-size: 11px;
  /* Phase 363 Plan 02: WCAG 2.2 AA contrast (was --text-mute -> --text-2) */
  color: var(--text-2);
  align-items: center;
  flex-wrap: wrap;
}
.im-welcome-statusband__row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.im-welcome-statusband__label-strong {
  color: var(--text);
  font-weight: 700;
}

/* === Hero panel === */
.im-welcome-hero {
  margin: 32px;
  background: var(--surface);
  border: 1px solid var(--bezel);
  border-radius: 4px;
  position: relative;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.3);
}
.im-welcome-hero__content {
  display: grid;
  grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  border-block-start: 1px solid var(--border-soft);
}
.im-welcome-hero__text {
  padding-block: 56px 40px;
  padding-inline: 40px;
  border-inline-end: 1px solid var(--border);
}
.im-welcome-hero__specs {
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-block-end: 24px;
  display: flex;
  gap: 24px;
  padding-block-end: 12px;
  border-block-end: 1px dashed var(--border-soft);
  flex-wrap: wrap;
}
.im-welcome-hero__spec-value {
  color: var(--accent-2);
  font-weight: 700;
}
.im-welcome-hero__title {
  font-size: 64px;
  font-weight: 800;
  line-height: 0.95;
  letter-spacing: -0.04em;
  margin: 0 0 24px 0;
  text-transform: uppercase;
}
.im-welcome-hero__bracket { color: var(--accent-1); font-weight: 400; }
.im-welcome-hero__pipe    { color: var(--accent-2); }
.im-welcome-hero__lede {
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-2);
  margin-block-end: 32px;
  max-inline-size: 50ch;
}
.im-welcome-hero__cta-row {
  display: flex;
  gap: 0;
  flex-wrap: wrap;
}
.im-welcome-cta-primary {
  background: var(--accent-1);
  color: var(--bg);
  border: 1px solid var(--accent-1);
  border-radius: 0;
  padding-inline: 28px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  box-shadow: 0 0 8px var(--accent-1-glow);
  transition: all 0.15s;
}
.im-welcome-cta-primary:hover {
  background: var(--accent-2);
  border-color: var(--accent-2);
  box-shadow: 0 0 10px var(--accent-2-glow);
}
.im-welcome-cta-secondary {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--text-mute);
  border-radius: 0;
  padding-inline: 24px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-inline-start: -1px;
}
.im-welcome-cta-secondary:hover {
  border-color: var(--accent-2);
  color: var(--accent-2);
}
.im-welcome-hero__side {
  padding-block: 56px 32px;
  padding-inline: 32px;
  background: var(--bg);
}

/* === Live gauges === */
.im-welcome-gauges__heading {
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin: 0 0 16px 0;
  font-weight: 700;
}
.im-welcome-gauge-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding-block: 8px;
  border-block-end: 1px dashed var(--border-soft);
}
.im-welcome-gauge-row:last-child { border-block-end: none; }
.im-welcome-gauge-row__label {
  font-size: 11px;
  color: var(--text-2);
  flex: 1;
}
.im-welcome-gauge-row__value {
  font-size: 18px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: -0.02em;
}
.im-welcome-gauge-led {
  display: inline-block;
  inline-size: 6px;
  block-size: 6px;
  border-radius: 50%;
}
.im-welcome-sla-progress { margin-block-start: 24px; }
.im-welcome-sla-progress__bar {
  block-size: 8px;
  background: var(--bezel);
  border-radius: 0;
  overflow: hidden;
}
.im-welcome-sla-progress__fill {
  block-size: 100%;
  background: linear-gradient(90deg, var(--accent-1), var(--accent-2));
}
.im-welcome-sla-progress__text {
  font-size: 10px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-block-start: 6px;
  display: flex;
  justify-content: space-between;
}

/* === Commitments panel === */
.im-welcome-commitments-panel {
  margin: 32px;
  background: var(--surface);
  border: 1px solid var(--bezel);
  border-radius: 4px;
  position: relative;
  padding: 32px;
}
.im-welcome-commitments-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 32px;
  padding-block-start: 16px;
}
.im-welcome-commitment {
  /* Border, padding, color set inline by CommitmentCard.java to keep
     palette tokens flowing through cascade. */
}

/* === Footer === */
.im-welcome-footer {
  padding-block: 16px;
  padding-inline: 32px;
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--text-mute);
  border-block-start: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  /* Same Vaadin VerticalLayout gotcha as .im-welcome-utility: explicit
   * inline-size:100% so the footer stretches edge-to-edge. */
  inline-size: 100%;
  box-sizing: border-box;
}
.im-welcome-footer__legal a { color: var(--accent-1); text-decoration: underline; }
.im-welcome-footer__right {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* === Locale picker === */
.im-welcome-locale-picker__trigger {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding-inline: 14px;
}
.im-welcome-locale-picker__trigger:hover {
  border-color: var(--accent-1);
  color: var(--accent-1);
}

/* V19 styling for the locale picker dropdown overlay.
 * Note: vaadin-context-menu-overlay is a global element rendered in
 * document.body, so this rule affects ALL ContextMenu dropdowns app-wide.
 * Acceptable because V19 industrial-cyber is the project design system
 * — consistent dropdown look across the app. The Aura theme parts
 * (`::part(overlay)`, `::part(content)`) penetrate the overlay shadow
 * DOM in Vaadin 25.1; vaadin-context-menu-item is a light-DOM child
 * directly stylable. */
vaadin-context-menu-overlay::part(overlay) {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 2px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
}
vaadin-context-menu-overlay::part(content) {
  padding-block: 4px;
  padding-inline: 0;
}
vaadin-context-menu-item {
  background: transparent;
  color: var(--text);
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  font-size: 12px;
  letter-spacing: 0.04em;
  padding-block: 8px;
  padding-inline: 16px;
  min-block-size: 36px;
  cursor: pointer;
}
vaadin-context-menu-item:hover,
vaadin-context-menu-item[focused] {
  background: var(--bezel);
  color: var(--accent-1);
}
/* Active locale row: highlighted in --accent-1 amber so the user sees
 * which language is currently active as soon as the dropdown opens. */
vaadin-context-menu-item[aria-current="true"],
vaadin-context-menu-item[menu-item-checked] {
  color: var(--accent-1);
  background: var(--surface-2);
  font-weight: 700;
}
vaadin-context-menu-item[aria-current="true"]::before {
  content: "▸ ";
  color: var(--accent-1);
}

/* === V19 IN-APP SHELL CHROME (Phase 359 propagation, M006-91vubn) === */
/* Parallel .im-shell-* selector family — propagates V19 industrial-cyber chrome
   into MainLayout + TenantPortalLayout shells. Compacted vs the welcome surface
   (smaller padding, smaller LEDs, smaller panel-label font) so it fits inside
   AppLayout navbar height without crowding the operational content. Reuses the
   Civic surface tokens declared on :root (the palette tier was retired in
   Phase 437.1 — Civic light is the only skin). */

/* Opt-in container — only views that explicitly add this class get the V19
   monospace shell treatment. Non-demo views remain unaffected. */
.im-shell-page {
  font-family: var(--aura-font-family-mono);
  color: var(--text);
  background: var(--bg);
}

/* Compact status band — 6px padding-block vs welcome's 10px; gap 24px vs 32px;
   intended for the top of MainLayout / TenantPortalLayout where AppLayout's
   default navbar lives. */
.im-shell-statusband {
  padding-block: 6px;
  padding-inline: 24px;
  display: flex;
  gap: 24px;
  font-size: 11px;
  /* Phase 363 Plan 02: WCAG 2.2 AA contrast (was --text-mute -> --text-2) */
  color: var(--text-2);
  background: var(--bg);
  border-block-end: 1px solid var(--border);
  align-items: center;
  flex-wrap: wrap;
}
.im-shell-statusband__row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.im-shell-statusband__label-strong {
  color: var(--text);
  font-weight: 700;
}

/* LED dot — 6x6 compact (welcome is 8x8). Status semantics palette-independent
   per Plan 09 D-30: red/amber/green meaning is constant across palettes. */
.im-shell-led {
  display: inline-block;
  inline-size: 6px;
  block-size: 6px;
  border-radius: 50%;
}
.im-shell-led--live { background: var(--led-green); box-shadow: 0 0 4px var(--led-green); }
.im-shell-led--warn { background: var(--led-amber); box-shadow: 0 0 4px var(--led-amber); }
.im-shell-led--idle { background: var(--text-mute); }

/* SYS LIVE badge — offline state, driven by browser-side SYS_LIVE_MONITOR_JS
   (see ShellStatusBandComponent). When the periodic /themes/portal/... HEAD
   ping fails or the browser fires `offline`, [data-row="sys-live"] gets
   data-sys-state="down" and the LED + label flip to the alert palette. */
[data-row="sys-live"][data-sys-state="down"] .im-shell-led--live {
  background: var(--led-red);
  box-shadow: 0 0 4px var(--led-red);
}
[data-row="sys-live"][data-sys-state="down"] [data-sys-live-label] {
  color: var(--led-red);
}

/* 390.3-03 status-band fold — Row 1 operational-state cluster.
   The standalone .im-shell-statusband strip is retired from MainLayout (it still
   styles the tenant/driver/customer portal shells, so the base block above stays).
   In MainLayout the operational cells (LIVE · region clock · version · alerts) now
   sit inline in the Row 1 right cluster, just before the user menu. Hairline
   separators + compact gaps keep them coherent with V19 navbar geometry; the
   region clock cell is visually distinct (strong, always-visible) and never
   rotated out of view. Aura tokens only — no --lumo-*. */
.im-shell-ops {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  font-size: 11px;
  color: var(--text-2);
  white-space: nowrap;
}
.im-shell-ops__cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Hairline separators between cells (not before the first). */
.im-shell-ops__cell + .im-shell-ops__cell {
  padding-inline-start: 14px;
  border-inline-start: 1px solid var(--border);
}
/* Phase 544: the ops cluster and the Action Center are SIBLINGS in one .coreline-shell-row (the
   390.3-03 status-band fold) — the "second row" older screenshots showed was the header WRAPPING
   under three overflowing chips, not a designed row. Once the aggregated pill made the row fit, the
   pill ended up butting straight against "SISTEMA EN VIVO" as the only element in that row without a
   boundary. Same hairline idiom as the cells above, so the separation reads as intentional. */
.im-shell-action-center + .im-shell-ops {
  padding-inline-start: 14px;
  border-inline-start: 1px solid var(--border);
  margin-inline-start: 4px;
}
/* Persistent region clock — distinct + always-visible. */
.im-shell-ops__clock {
  color: var(--text);
}
.im-shell-ops__clock [data-clock] {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5px;
}
.im-shell-ops__clock [data-clock-zone] {
  font-size: 9px;
  color: var(--text-2);
  text-transform: uppercase;
}

/* ── Action Center pill (UI-01 / D-02) — masthead recommendation slot ──────
   The honest-empty resting state (no recommendations) shows a muted text
   LABEL ("ACCIONES"/"ACTIONS") plus a leading neutral ○ glyph — never a fake
   green "0 issues" success badge (D-04 honest-empty rule). Adopts the
   SEED-037 muted resting-affordance contract + sketch 037-D statusband style:
   12px / 600 / uppercase, muted-neutral ink, small positive letter-spacing.
   Geometry lives here (no inline geometry on the Java host). The pill keeps its
   role="region" + aria-label + aria-live attributes (set in MainLayout). */
.im-shell-action-center {
  display: inline-flex;
  align-items: center;
  /* Phase 544: the container carried no shrink/wrap control, so with MAX_VISIBLE=3 real cards it
     grew past the masthead and wrapped onto a full-width second row. nowrap keeps the pill on the
     masthead line; min-inline-size:0 lets it shrink inside the header flex row (min-width:auto would
     refuse); overflow:hidden is the final bound so a long locale can never push the row open. The
     per-item ellipsis (.im-shell-action-center__item) does the visible truncating. */
  flex-wrap: nowrap;
  min-inline-size: 0;
  overflow: hidden;
  gap: 8px;
}
/* D-1 / SC1 / SEED-169 — UNSCOPED hard truncation net on the collapsed pill text so NO i18n string
   in any locale can widen the masthead flex row, independent of whether .coreline-shell-row wraps the
   pill. This is the belt to the kit's suspenders (coreline-kit.css scopes the same cap under
   .coreline-shell-row); the collapsed label is already short after the composeCollapsedLabel seam, but
   the CSS must GUARANTEE the bound. min-inline-size:0 is load-bearing: a flex item defaults to
   min-width:auto and would otherwise refuse to ellipsis. CSS-only — no inline geometry / set-padding. */
.im-shell-action-center__text {
  max-inline-size: 22ch;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Resting (honest-empty) row: leading glyph + label, 4px (xs) inline gap. */
.im-shell-action-center__resting {
  display: none !important;
}
.im-shell-action-center:empty {
  display: none !important;
}
/* ── Phase 544 (SC2-SC4 / SEED-169) — the expanded action card ──────────────────────────────────
   The ACTIONABLE half of the Action Center. The collapsed pill above stays bounded; clicking it
   opens a Popover carrying the systemic card contract
   { severity LED · short label · cause · effect · remediation · CTA }.
   All geometry lives here — the Java host sets BEM class names only (no setPadding/setSpacing,
   no inline getStyle geometry; closed-kit discipline). Tokens only, no hex literals. */

/* The slot wraps [pill item + popover] so the popover has a host without joining the flex row.
   display:contents is load-bearing: the wrapper must not become a flex item itself, or the
   masthead row would gain a phantom cell per alert. */
.im-shell-action-center__slot {
  display: contents;
}
/* Phase 544 (2026-07-31) — the pill ITEM had NO rule at all, and that is what deformed the masthead.
   Because __slot is display:contents, the Vaadin Button IS the flex item in the masthead row; the
   __text Span below it is only a grandchild. The 22ch truncation net was therefore applied one level
   too deep: a flex item defaults to min-width:auto, so the Button refused to shrink, the inline-flex
   container overflowed, and the row wrapped to a full-width second line carrying untruncated
   sentences.
   Why this was never caught: 537-01 shipped SC1 ("the collapsed pill cannot deform the masthead") and
   it was verified — but it could not have been verified against real cards, because the tenant lens
   was unreachable for every non-SUPER_ADMIN (see MainLayout.updateSurfaces). The contract was
   validated against a surface that was structurally always empty. The first render of real cards
   broke it.
   min-inline-size:0 is the load-bearing declaration — the same reasoning the __text rule already
   documents, applied to the element that is actually the flex item. */
.im-shell-action-center__item {
  min-inline-size: 0;
  max-inline-size: 26ch;
  overflow: hidden;
  flex: 0 1 auto;
}
/* Sketch 077 / cross-AI 3/3 — the AGGREGATED pill replaces the per-alert chips. One control, a short
   count label, never a sentence. Fixed bound so no locale can widen the masthead: the label is
   "{n} críticas", not the alert text. Severity here is the WORST active tier only; per-item tiers
   live on the rows inside the popover. */
.im-shell-action-center__pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-inline-size: 0;
  max-inline-size: 22ch;
  border-radius: 999px;
  border: 1px solid transparent;
  padding-inline: 10px;
  padding-block: 4px;
  flex: 0 0 auto;
}
/* Masthead utility cluster chip styling: sleek frosted dark treatment */
.coreline-shell-row .im-shell-action-center__pill--alert {
  background: rgba(239, 68, 68, 0.16);
  color: #fca5a5;
  border: 1px solid rgba(239, 68, 68, 0.4);
}
.coreline-shell-row .im-shell-action-center__pill--warn {
  background: rgba(245, 158, 11, 0.16);
  color: #fcd34d;
  border: 1px solid rgba(245, 158, 11, 0.4);
}
.coreline-shell-row .im-shell-action-center__pill--info {
  background: rgba(59, 130, 246, 0.16);
  color: #93c5fd;
  border: 1px solid rgba(59, 130, 246, 0.4);
}
/* Inside the chip the glyph is NOT a second chip — it keeps the two-axis a11y role (colour AND
   glyph) but drops the LED's own background/border/pill geometry, which is what produced the
   "only the ! is in a chip" look. Higher specificity (0,3,0) than the kit's LED tier rules. */
.coreline-shell-row .im-shell-action-center__pill .im-shell-action-center__led {
  background: transparent;
  border-color: transparent;
  color: inherit;
  padding-inline: 0;
  min-inline-size: auto;
  font-weight: 900;
  /* Separator is a MARGIN, not the host's `gap`. The pill is a <vaadin-button>, so the glyph and the
     count are slotted into the button's shadow [part="label"]; that wrapper is their real flex
     container, and `gap` declared on the host never reaches them. Without this the chip renders
     "!3 críticas" with the glyph fused to the digit. Margin crosses the slot boundary, gap does not. */
  margin-inline-end: 6px;
}
.coreline-shell-row .im-shell-action-center__pill .im-shell-action-center__pill-text {
  color: inherit;
}
.im-shell-action-center__pill-text {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
}
/* The opened list surface. Height-capped + scrolled so a long action list can never blow the
   layout (the reason the panel is a Popover and not an inline expansion). */
.im-shell-action-center__panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  inline-size: 380px;
  max-inline-size: 90vw;
}
.im-shell-action-center__panel-title {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--aura-text-color-secondary, #475569);
  padding: 2px 2px 0;
}
.im-shell-action-center__list {
  display: flex;
  flex-direction: column;
  max-block-size: 60vh;
  overflow-block: auto;
}
/* Rows separate visually; the card primitive itself keeps its existing geometry. */
.im-shell-action-center__list > .im-shell-action-center__card + .im-shell-action-center__card {
  border-block-start: 1px solid var(--aura-border-color-secondary, #E2E8F0);
  padding-block-start: 10px;
  margin-block-start: 2px;
}
.im-shell-action-center__card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-inline-size: 320px;
  padding: 12px 14px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text, var(--fm-text, inherit));
}
/* Head: LED + the SAME short label the collapsed pill carries (one identity, two surfaces). */
.im-shell-action-center__card-head {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.im-shell-action-center__card-title {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
/* Body lines wrap freely — the expanded surface is where the full taxonomy is allowed to breathe
   (the 22ch truncation net applies to the COLLAPSED pill only, never here). */
.im-shell-action-center__card-line {
  min-inline-size: 0;
  overflow-wrap: anywhere;
}
.im-shell-action-center__card-cause,
.im-shell-action-center__card-effect {
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}
/* remediation is the FIX — the field SEED-169 flagged as modeled-but-never-rendered. It carries
   full-strength ink and a leading accent spine so the solution reads louder than the problem. */
.im-shell-action-center__card-remediation {
  padding-inline-start: 8px;
  border-inline-start: 2px solid var(--accent-1, var(--fm-accent, currentcolor));
  color: var(--text, var(--fm-text, inherit));
  font-weight: 500;
}
/* Inline reason shown when no resolution route is allowlisted for the alert — paired with a
   DISABLED cta (never render an action that will not do what it says). */
.im-shell-action-center__card-hint {
  font-size: 12px;
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}
.im-shell-action-center__cta {
  align-self: flex-start;
}

/* ── Phase 545 (V45-CF-02) — dispute-defense export on the adjudication desk ────────────────────
   The `.coreline-adj__*` family otherwise lives in coreline-kit.css; these members are declared
   here deliberately because that file is under active stylelint remediation in a parallel lane and
   must not be touched. Same closed-kit contract either way: geometry lives in CSS, and the Java
   host sets class names only. Fold these into coreline-kit.css once that lane lands. */
.coreline-adj__export {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* Paired with a DISABLED cta when the tenant is not on the advanced tier — the operator is told
   why up front rather than being denied on click. */
.coreline-adj__export-hint {
  font-size: 12px;
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}
.coreline-adj__export-readout {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.coreline-adj__export-line {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* Muted text label — operational-band statusband style (sketch 037-D). */
.im-shell-action-center__label {
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}

/* SEED-022 (Phase 390.4): clickable multi-zone clock + popover.
   The clock cell becomes an affordance when >1 distinct zone is in play. No
   animation anywhere here, so prefers-reduced-motion needs no special-casing
   (the explicit guard below keeps any future transition opt-out honest). */
.im-shell-ops__clock--interactive,
.im-shell-ops__sys-live--interactive {
  cursor: pointer;
  border-radius: 4px;
  padding-inline: 4px;
  transition: background 120ms ease;
}
.im-shell-ops__clock--interactive:hover,
.im-shell-ops__clock--interactive:focus-visible,
.im-shell-ops__sys-live--interactive:hover,
.im-shell-ops__sys-live--interactive:focus-visible {
  background: var(--surface-2);
  outline: none;
}
.im-shell-ops__clock--interactive:focus-visible,
.im-shell-ops__sys-live--interactive:focus-visible {
  box-shadow: 0 0 0 2px var(--accent-1-glow);
}

.im-mz-clock {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-inline-size: 200px;
  padding: 4px 2px;
}
.im-mz-clock__title {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-mute);
  padding-block-end: 2px;
}
.im-mz-clock__row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 5px 8px;
  border-radius: 4px;
  color: var(--text-2);
}
.im-mz-clock__row--active {
  background: var(--surface-2);
  color: var(--text);
  box-shadow: inset 2px 0 0 var(--accent-1);
}
.im-mz-clock__city {
  flex: 1 1 auto;
  font-size: 12px;
}
.im-mz-clock__time {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.5px;
  font-size: 13px;
  color: var(--text);
}
.im-mz-clock__badge {
  font-size: 8px;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-1);
}
@media (prefers-reduced-motion: reduce) {
  .im-shell-ops__clock--interactive {
    transition: none;
  }
}

/* ── World Clock Dialog: Modal with Backdrop Blur ─────────────────── */
vaadin-dialog-overlay[theme~="im-world-clock-dialog"]::part(backdrop) {
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  background-color: rgba(15, 23, 42, 0.52);
}

vaadin-dialog-overlay[theme~="im-world-clock-dialog"]::part(overlay) {
  border-radius: var(--aura-border-radius-l, 12px);
  border: 1px solid var(--aura-color-outline, rgba(255, 255, 255, 0.18));
  box-shadow: 0 20px 45px -10px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(0, 0, 0, 0.08);
  background: var(--im-surface-card, #ffffff);
  padding: 0;
  max-width: 740px;
  width: 94vw;
  overflow: hidden;
}

vaadin-dialog-overlay[theme~="im-world-clock-dialog"]::part(content) {
  padding: 0;
}

.im-world-clock-dialog {
  display: flex;
  flex-direction: column;
  padding: 16px 20px;
  gap: 12px;
  color: var(--aura-body-text-color, #0f172a);
}

.im-world-clock-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--im-color-border, #e2e8f0);
  padding-bottom: 10px;
}

.im-world-clock-dialog__title-box {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.im-world-clock-dialog__title {
  font-size: 16px;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--aura-heading-text-color, #0f172a);
  letter-spacing: -0.01em;
}

.im-world-clock-dialog__subtitle {
  font-size: 11.5px;
  color: var(--aura-color-text-secondary, #64748b);
}

/* Reference Strip: Operating base on left, UTC on right */
.im-world-clock-reference {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  padding: 7px 12px;
  border-radius: var(--im-border-radius-m, 6px);
  background: var(--im-surface-subtle, rgba(241, 245, 249, 0.85));
  border: 1px solid var(--im-color-border, #e2e8f0);
  font-size: 12px;
}

.im-world-clock-reference__left {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.im-world-clock-reference__icon {
  font-size: 13px;
}

.im-world-clock-reference__title {
  font-weight: 600;
  color: var(--aura-heading-text-color, #0f172a);
}

.im-world-clock-reference__country {
  font-weight: 700;
  color: var(--aura-heading-text-color, #0f172a);
}

.im-world-clock-reference__city {
  font-weight: 500;
  color: var(--aura-color-text-secondary, #64748b);
}

.im-world-clock-reference__right {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2.5px 8px;
  border-radius: 999px;
  background: rgba(0, 127, 140, 0.08);
  border: 1px solid rgba(0, 127, 140, 0.22);
  font-variant-numeric: tabular-nums;
  font-family: var(--fm-font-mono, monospace);
  font-weight: 600;
  font-size: 11.5px;
  color: var(--aura-color-brand, #007f8c);
  white-space: nowrap;
}

/* Clocks Groups: Consolidated time slot cards */
.im-world-clock-groups {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-height: 58vh;
  overflow-y: auto;
  padding: 2px;
}

/* Unified Group Card (~70px height) */
.im-world-clock-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--im-border-radius-m, 8px);
  background: var(--im-surface-card, #ffffff);
  border: 1px solid var(--im-color-border, #e2e8f0);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
  transition: border-color 140ms ease, box-shadow 140ms ease;
  box-sizing: border-box;
}

.im-world-clock-group:hover {
  border-color: var(--aura-color-brand, #007f8c);
  box-shadow: 0 2px 6px rgba(15, 23, 42, 0.07);
}

.im-world-clock-group--base {
  background: var(--surface-2, rgba(0, 127, 140, 0.035));
  border-color: var(--aura-color-brand, #007f8c);
  box-shadow: 0 0 0 1px var(--aura-color-brand, #007f8c);
}

.im-world-clock-group--utc {
  background: var(--im-surface-subtle, rgba(241, 245, 249, 0.65));
  border-style: dashed;
}

/* Header Row: Meta badges on left, Large digital clock on right */
.im-world-clock-group__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.im-world-clock-group__meta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.im-world-clock-time {
  font-variant-numeric: tabular-nums;
  font-family: var(--fm-font-mono, "SF Mono", "IBM Plex Mono", monospace);
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--aura-heading-text-color, #0f172a);
  line-height: 1;
  flex-shrink: 0;
}

.im-world-clock-zone {
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  font-weight: 600;
  color: var(--aura-heading-text-color, #334155);
  flex-shrink: 0;
}

.im-world-clock-date {
  font-size: 10.5px;
  font-weight: 500;
  color: var(--aura-color-text-secondary, #64748b);
  flex-shrink: 0;
}

/* Country Chips Row */
.im-world-clock-group__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 6px;
  border-top: 1px solid var(--im-color-border-subtle, rgba(226, 232, 240, 0.6));
}

.im-world-clock-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3.5px 8px;
  border-radius: 6px;
  background: var(--im-surface-subtle, rgba(241, 245, 249, 0.85));
  border: 1px solid var(--im-color-border, #e2e8f0);
  font-size: 11.5px;
  color: var(--aura-heading-text-color, #0f172a);
  transition: background-color 120ms ease, border-color 120ms ease;
  line-height: 1.2;
}

.im-world-clock-chip:hover {
  background: rgba(0, 127, 140, 0.08);
  border-color: var(--aura-color-brand, #007f8c);
}

.im-world-clock-chip--base {
  background: rgba(0, 127, 140, 0.10);
  border-color: var(--aura-color-brand, #007f8c);
  font-weight: 600;
}

.im-world-clock-chip__flag {
  font-size: 14px;
  line-height: 1;
  flex-shrink: 0;
}

.im-world-clock-chip__name {
  font-weight: 600;
  color: var(--aura-heading-text-color, #0f172a);
}

.im-world-clock-chip__city {
  font-weight: 400;
  color: var(--aura-color-text-secondary, #64748b);
  font-size: 11px;
}

.im-world-clock-chip__badge {
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--aura-color-brand, #007f8c);
  color: #ffffff;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-left: 2px;
}

/* Badges / Pills */
.im-world-clock-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 1.5px 6px;
  border-radius: 999px;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.3;
  white-space: nowrap;
}

.im-world-clock-pill--base {
  background: var(--aura-color-brand, #007f8c);
  color: #ffffff;
  font-weight: 700;
}

.im-world-clock-pill--same {
  background: rgba(100, 116, 139, 0.12);
  color: var(--aura-color-text-secondary, #475569);
}

.im-world-clock-pill--ahead {
  background: rgba(16, 185, 129, 0.12);
  color: #047857;
}

.im-world-clock-pill--behind {
  background: rgba(245, 158, 11, 0.14);
  color: #b45309;
}

.im-world-clock-pill--biz {
  background: rgba(16, 185, 129, 0.10);
  color: #059669;
}

.im-world-clock-pill--night {
  background: rgba(100, 116, 139, 0.12);
  color: #64748b;
}

.im-world-clock-pill--utc {
  background: rgba(0, 127, 140, 0.12);
  color: var(--aura-color-brand, #007f8c);
  font-weight: 600;
}

.im-world-clock-dialog__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid var(--im-color-border, #e2e8f0);
  padding-top: 10px;
}

/* ── Network Diagnostics & Speed Test Dialog: Modal with Backdrop Blur ─ */
vaadin-dialog-overlay[theme~="im-network-diagnostics-dialog"]::part(backdrop) {
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  background-color: rgba(15, 23, 42, 0.52);
}

vaadin-dialog-overlay[theme~="im-network-diagnostics-dialog"]::part(overlay) {
  border-radius: var(--aura-border-radius-l, 12px);
  border: 1px solid var(--aura-color-outline, rgba(255, 255, 255, 0.18));
  box-shadow: 0 20px 45px -10px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(0, 0, 0, 0.08);
  background: var(--im-surface-card, #ffffff);
  padding: 0;
  max-width: 720px;
  width: 94vw;
  overflow: hidden;
}

vaadin-dialog-overlay[theme~="im-network-diagnostics-dialog"]::part(content) {
  padding: 0;
}

/* ── macOS 27 Spotlight & Copilot Dialog: Modal with Backdrop Blur ─ */
vaadin-dialog[theme~="im-spotlight-dialog"],
vaadin-dialog-overlay[theme~="im-spotlight-dialog"],
vaadin-dialog[theme~="im-spotlight-overlay"],
vaadin-dialog-overlay[theme~="im-spotlight-overlay"] {
  --vaadin-dialog-padding: 0;
  --vaadin-dialog-border-radius: 18px;
  --vaadin-dialog-background: transparent;
  --vaadin-dialog-shadow: none;
}

vaadin-dialog-overlay[theme~="im-spotlight-dialog"]::part(backdrop),
vaadin-dialog-overlay[theme~="im-spotlight-overlay"]::part(backdrop) {
  background: rgba(6, 10, 18, 0.65) !important;
  backdrop-filter: blur(16px) !important;
  -webkit-backdrop-filter: blur(16px) !important;
}

vaadin-dialog-overlay[theme~="im-spotlight-dialog"]::part(overlay),
vaadin-dialog-overlay[theme~="im-spotlight-overlay"]::part(overlay) {
  background: transparent !important;
  box-shadow: none !important;
  padding: 0 !important;
  border: none !important;
  border-radius: 18px !important;
  overflow: hidden !important;
  max-width: min(860px, calc(100vw - 32px)) !important;
  width: min(860px, calc(100vw - 32px)) !important;
  max-height: min(580px, calc(100vh - 120px)) !important;
  height: min(580px, calc(100vh - 120px)) !important;
}

vaadin-dialog-overlay[theme~="im-spotlight-dialog"]::part(content),
vaadin-dialog-overlay[theme~="im-spotlight-overlay"]::part(content) {
  padding: 0 !important;
  height: 100% !important;
  max-height: 100% !important;
  display: flex !important;
  flex-direction: column !important;
  overflow: hidden !important;
  box-sizing: border-box !important;
  background: transparent !important;
}

.im-net-dialog {
  display: flex;
  flex-direction: column;
  padding: 18px 22px;
  gap: 14px;
  color: var(--aura-body-text-color, #0f172a);
}

.im-net-dialog__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--im-color-border, #e2e8f0);
  padding-bottom: 10px;
}

.im-net-dialog__title-box {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.im-net-dialog__title {
  font-size: 16px;
  font-weight: 700;
  display: flex;
  align-items: center;
  letter-spacing: -0.01em;
}

.im-net-dialog__subtitle {
  font-size: 12px;
  color: var(--im-color-text-secondary, #64748b);
}

.im-net-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--im-surface-subtle, #f8fafc);
  border: 1px solid var(--im-color-border, #e2e8f0);
  border-radius: 8px;
  padding: 8px 14px;
  font-size: 12px;
}

.im-net-strip__left {
  display: flex;
  align-items: center;
  gap: 6px;
}

.im-net-strip__label {
  color: var(--im-color-text-secondary, #64748b);
  font-weight: 500;
}

.im-net-strip__target {
  font-weight: 700;
  font-family: var(--aura-font-family-mono, monospace);
  color: var(--aura-body-text-color, #0f172a);
}

.im-net-strip__right {
  display: flex;
  align-items: center;
  gap: 6px;
}

.im-net-live-dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #10b981;
  box-shadow: 0 0 6px rgba(16, 185, 129, 0.7);
  display: inline-block;
}

.im-net-live-dot::after {
  content: '';
  position: absolute;
  top: -3px;
  left: -3px;
  right: -3px;
  bottom: -3px;
  border-radius: 50%;
  border: 1.5px solid rgba(16, 185, 129, 0.6);
  animation: imNetRadar 1.8s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}

@keyframes imNetRadar {
  0% { transform: scale(0.8); opacity: 1; }
  100% { transform: scale(2.4); opacity: 0; }
}

.im-net-strip__status {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #059669;
}

.im-net-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

@media (max-width: 600px) {
  .im-net-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

.im-net-card {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--im-surface-subtle, #f8fafc);
  border: 1px solid var(--im-color-border, #e2e8f0);
  border-radius: 8px;
  padding: 10px 14px;
  transition: border-color 200ms ease, box-shadow 200ms ease, background-color 200ms ease;
}

.im-net-card--active {
  border-color: #0284c7 !important;
  background: #f0f9ff !important;
  box-shadow: 0 0 0 1px #0284c7, 0 3px 12px rgba(2, 132, 199, 0.2) !important;
  animation: imNetCardPulse 1.6s ease-in-out infinite;
}

@keyframes imNetCardPulse {
  0%, 100% {
    box-shadow: 0 0 0 1px #0284c7, 0 2px 8px rgba(2, 132, 199, 0.15);
  }
  50% {
    box-shadow: 0 0 0 1.5px #0284c7, 0 4px 18px rgba(2, 132, 199, 0.35);
  }
}

.im-net-pulse {
  animation: imNetPulse 1.2s ease-in-out infinite;
}

@keyframes imNetPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

.im-net-card__header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 2px;
}

.im-net-card__icon {
  font-size: 13px;
}

.im-net-card__title {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--im-color-text-secondary, #64748b);
}

.im-net-card__val {
  font-size: 20px;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--aura-body-text-color, #0f172a);
}

.im-net-card__val--accent {
  color: var(--accent-1, #0284c7);
}

.im-net-card__sub {
  font-size: 11px;
  color: var(--im-color-text-secondary, #64748b);
}

.im-net-badge {
  display: inline-flex;
  align-items: center;
  font-size: 13px;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: 6px;
  width: fit-content;
}

.im-net-badge--testing {
  background: rgba(2, 132, 199, 0.12);
  color: #0284c7;
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.im-net-badge--testing::before {
  content: '';
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #0284c7;
  box-shadow: 0 0 0 0 rgba(2, 132, 199, 0.7);
  animation: imNetBeaconDot 1s ease-in-out infinite;
}

@keyframes imNetBeaconDot {
  0% { transform: scale(0.85); box-shadow: 0 0 0 0 rgba(2, 132, 199, 0.7); }
  70% { transform: scale(1.15); box-shadow: 0 0 0 5px rgba(2, 132, 199, 0); }
  100% { transform: scale(0.85); box-shadow: 0 0 0 0 rgba(2, 132, 199, 0); }
}

.im-net-badge--success {
  background: rgba(16, 185, 129, 0.14);
  color: #059669;
}

.im-net-badge--warning {
  background: rgba(245, 158, 11, 0.14);
  color: #d97706;
}

.im-net-badge--danger {
  background: rgba(239, 68, 68, 0.14);
  color: #dc2626;
}

.im-net-progress-box {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-block: 4px;
}

.im-net-progress-label-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.im-net-activity-indicator {
  display: inline-flex;
  align-items: flex-end;
  gap: 2px;
  height: 12px;
  width: 14px;
  vertical-align: middle;
}

.im-net-eq-bar {
  width: 2.5px;
  background: #0284c7;
  border-radius: 1px;
  display: inline-block;
  height: 3px;
  transition: height 150ms ease;
}

.im-net-activity-indicator--active .im-net-eq-bar--1 {
  animation: imNetEq1 0.7s ease-in-out infinite alternate;
}
.im-net-activity-indicator--active .im-net-eq-bar--2 {
  animation: imNetEq2 0.5s ease-in-out infinite alternate 0.15s;
}
.im-net-activity-indicator--active .im-net-eq-bar--3 {
  animation: imNetEq3 0.8s ease-in-out infinite alternate 0.3s;
}
.im-net-activity-indicator--active .im-net-eq-bar--4 {
  animation: imNetEq4 0.6s ease-in-out infinite alternate 0.1s;
}

@keyframes imNetEq1 { 0% { height: 2px; } 100% { height: 12px; } }
@keyframes imNetEq2 { 0% { height: 11px; } 100% { height: 3px; } }
@keyframes imNetEq3 { 0% { height: 4px; } 100% { height: 12px; } }
@keyframes imNetEq4 { 0% { height: 12px; } 100% { height: 5px; } }

.im-net-status-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--im-color-text-secondary, #64748b);
}

.im-net-progress-pct {
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #0284c7;
  background: rgba(2, 132, 199, 0.08);
  padding: 1.5px 6px;
  border-radius: 4px;
  transition: all 200ms ease;
}

.im-net-progress-pct--done {
  color: #059669;
  background: rgba(16, 185, 129, 0.12);
}

.im-net-progress-track {
  width: 100%;
  height: 6px;
  background: var(--surface-2, #e2e8f0);
  border-radius: 3px;
  overflow: hidden;
}

.im-net-progress-bar {
  width: 0%;
  height: 100%;
  background: linear-gradient(90deg, #0284c7, #10b981);
  border-radius: 3px;
  transition: width 250ms ease;
}

.im-net-progress-bar--active {
  background: linear-gradient(90deg, #0284c7 0%, #38bdf8 30%, #10b981 60%, #38bdf8 85%, #0284c7 100%);
  background-size: 200% 100%;
  animation: imNetShimmer 1.5s infinite linear;
  position: relative;
}

.im-net-progress-bar--active::after {
  content: '';
  position: absolute;
  right: 0;
  top: -1px;
  bottom: -1px;
  width: 4px;
  background: #ffffff;
  border-radius: 2px;
  box-shadow: 0 0 6px #38bdf8, 0 0 10px #0284c7;
  animation: imNetLaser 0.8s ease-in-out infinite alternate;
}

@keyframes imNetLaser {
  0% { opacity: 0.6; }
  100% { opacity: 1; }
}

@keyframes imNetShimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.im-net-btn-spin vaadin-icon {
  animation: imNetBtnSpin 0.8s linear infinite;
}

@keyframes imNetBtnSpin {
  to { transform: rotate(360deg); }
}

.im-net-dialog__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid var(--im-color-border, #e2e8f0);
  padding-top: 12px;
}


/* Panel-label tab — floats above panel top edge. Smaller (9px font, 10px+3px
   padding) than welcome's (10px font, 12px+4px padding) so it doesn't overpower
   in-app panels which are denser than welcome's hero/commitments cards. */
.im-shell-panel-label {
  position: absolute;
  inset-block-start: -10px;
  inset-inline-start: 24px;
  padding-inline: 10px;
  padding-block: 3px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border-radius: 2px;
  background: var(--accent-1);
  color: var(--bg);
}

/* Corner-ribbon variant — anchors at the top-left corner of a relatively-
   positioned host (typically a map-frame Div or similar live-data panel).
   Per S03 T01 / Sketch 003-B: gives V19 signaling on a panel WITHOUT
   floating chrome above the panel edge. The bottom-right corner gets the
   only rounded notch; the inner edges (bottom + right) get a 1px accent
   line so the ribbon reads as a tucked-in tab against the live content
   below it. z-index 5 keeps the ribbon above default Leaflet/MapLibre
   layers; the ribbon's host can override z-index inline if a map's
   stacking context fights for a higher z-index. */
.im-shell-panel-label--corner {
  inset-block-start: 0;
  inset-inline-start: 0;
  border-radius: 0;
  border-end-end-radius: 4px;
  border-block-end: 1px solid var(--accent-1);
  border-inline-end: 1px solid var(--accent-1);
  z-index: 5;
}

/* Phase 386 AP-01 enforcement: the mounting-screw decorative-disc family of
   selectors is permanently DELETED (corner glyphs retired in V19 chrome v2).
   The user-feedback record `feedback_no_mounting_screws` (2026-05-12,
   sketch 055) made this a hard ban. Do NOT re-introduce the retired family
   of selectors in any new sketch or view — the corresponding Java helper on
   V19ShellChrome is gone as of Phase 386 D-01. */

/* Left-bordered card — accent-1 spine + standard space-m inset + surface
   fallback. Mirrors the V19 commitment-card grammar but generic enough for
   any in-app card surface. */
.im-shell-card--left-border {
  border-inline-start: 3px solid var(--accent-1);
  padding-inline-start: var(--aura-space-m);
  background: var(--surface, var(--bg));
}

/* Gauge bar — compact horizontal progress strip for KPI rows, SLA bars,
   utilization meters. 80x6 default; warn/alert variants swap to the
   palette-independent LED colors. */
.im-shell-gauge-bar {
  display: inline-block;
  inline-size: 80px;
  block-size: 6px;
  background: var(--border-soft);
  position: relative;
  overflow: hidden;
}
.im-shell-gauge-bar__fill {
  block-size: 100%;
  background: var(--accent-1);
}
.im-shell-gauge-bar__fill--warn  { background: var(--led-amber); }
.im-shell-gauge-bar__fill--alert { background: var(--led-red);   }

/* Monospace numeric utility — opts a span/td into JetBrains Mono with tabular
   figures for aligned cost/metric grids. Consumed by the S04 cost-analysis
   re-skin per the M006 ROADMAP. */
.im-shell-mono {
  font-family: var(--aura-font-family-mono);
  font-feature-settings: "tnum" 1;
}

/* Compact-only responsive collapse for the in-app shell status band.
   The welcome surface has its own 1024/767 breakpoints below; this one
   targets handheld widths where the AppLayout chrome must shrink further. */
@media (max-width: 720px) {
  .im-shell-statusband { padding-inline: 12px; }
  .im-shell-statusband__row[data-row="version"] { display: none; }
}

/* 390.3-03 follow-up — responsive Row 1 masthead media queries live AFTER the
   chrome-v2 component base rules (role / tenant-pill, defined ~line 1600+) so
   their overrides actually win on source order. See the consolidated block at
   the END of the chrome-v2 section. */

/* 390.3-03 follow-up — Row 1 identity header inline padding aligned with the
   Row 2 work-row (24px). Without this the right-most user menu sat flush against
   (and visually overran) the row's right edge while Row 2 content kept a 24px
   inset — the two rows' right boundaries didn't line up (operator feedback). */
.im-shell-identity-row {
  padding-inline: 24px;
}
/* The user menu is the right anchor — avatar-only (name removed in Java), so it
   has a fixed compact width and never overruns the row edge. */
.im-shell-user-trigger {
  flex-shrink: 0;
}

/* === Phase 386 chrome v2 contract ===
   Anchored by Phase 386 D-01 HARD-REPLACE. Adds CSS for the 5 v2 attach
   methods on V19ShellChrome (sketches 037-D / 045-A / 046-C-inverted /
   047-D / 055-D). All transitions are bounded by CONTEXT.md D-07 visual
   cohesion — limited to color/background-color/border-color/box-shadow
   (signal feedback) for ≤200ms, NEVER transform/translate/opacity/width/
   height/max-height. The single AP-21 EXEMPT clause is the bell
   `--increment` transient (sketch 047-D §5 — 400ms debounce + 700ms
   auto-remove via BellIncrementDebouncer) plus the bell `--alert` pulse
   feedback; the @keyframes `bell-shake-d`, `count-flash-d`, and
   `bell-pulse` are the only animations permitted in this block.

   390.3-05 synthesis-Q1 EXEMPTION (NARROWLY SCOPED): the impersonation
   `.im-shell-security-row` is the ONE selector permitted a max-height/opacity
   collapse-in-place transition (so the security alarm slides rather than jumps).
   The D-07 ban is NOT broadened — no other selector gains a max-height/opacity
   transition, and a prefers-reduced-motion fallback snaps it instantly. */

/* Aura alias declarations — UI-SPEC references --aura-accent-1/2/
   --aura-surface-2/--aura-border which do NOT exist as declared custom
   properties in this file. The project's native palette tokens are
   --accent-1/2 / --surface / --border (redefined per palette block at
   L330..L495). Aliasing here keeps palette responsiveness — switching
   palettes still rewires chrome v2 elements correctly. Mirrors the
   pre-existing `--aura-base-color: var(--bg)` alias pattern. */
:root {
  --aura-accent-1:  var(--accent-1);
  --aura-accent-2:  var(--accent-2);
  --aura-surface-2: var(--surface);
  --aura-border:    var(--border);
}

/* Brand-tab portal label. Single source of truth for the per-portal
   suffix (OPERATOR / PORTAL / DRIVER / CUSTOMER) emitted by
   V19ShellChrome.attachPortalLabel — replaces the 4 portal-private
   buildPortalLabel helpers retired in WR-01.

   Realigned (390.2) to the operator-approved ChatGPT masthead: a STACKED
   brand lockup — chevron, then the small muted product name ("INDUSTRY
   MASTER") over the large amber edition word ("OPERATOR"). NOT a bordered
   chip (the prior drifted 2px-square gray-bordered tag is gone): the lockup
   is transparent and the edition word carries the brand accent, so the
   brand reads as intentional identity yet stays SUBTLE relative to the
   dominant tenant pill (masthead hierarchy: tenant context is the safety
   device; the brand is one compact identifier). */
.im-shell-portal-label {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 2px 2px;
  background: transparent;
  border: none;
  white-space: nowrap;
  cursor: pointer;
}

/* INDUSTRY MASTER / OPERATOR header bookend — brand identity, pinned to a
   fixed system-bold stack regardless of the user's font picker. Family is
   hardcoded (not var(--aura-font-family)) so future picker swaps don't drag
   the brand along; per-user "Appearance → Font" only affects body/sidebar. */
.im-shell-portal-label__chevron {
  font: 700 22px/1 system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: color-mix(in srgb, var(--aura-accent-1) 70%, transparent);
}

.im-shell-portal-label__stack {
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
}

.im-shell-portal-label__name {
  font: 700 10px/1 system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--aura-text-secondary);
}

.im-shell-portal-label__edition {
  font: 700 18px/1 system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* Phase 564 WS1 (SEED-033): 18px bold is below the WCAG large-text threshold
   * (14pt bold ≈ 18.66px), so this label needs 4.5:1. The brand teal
   * --aura-accent-1 (#007F8C) on the light portal navbar (#F4F6F8) measured
   * 4.39:1 (axe color-contrast, serious, on /tenant-portal/* surfaces). Use the
   * darker brand shade (#005E68 ≈ 6.5:1) — same Río de la Plata teal, AA-safe. */
  color: var(--im-color-primary-dark, #005E68);
}

/* sketch 037-D — Adaptive Identity. The right-side identity cluster lives
   in the portal's primary navbar HorizontalLayout (NOT the secondary
   ShellStatusBandComponent — per RESEARCH OQ #2). The `--with-identity`
   modifier engages the flex cluster geometry. */
.im-shell-statusband--with-identity {
  display: flex;
  gap: var(--vaadin-space-m, 12px);
  align-items: center;
}

/* Impersonation hijack banner — STUB in Phase 386 (Plan 02 portals pass
   impersonationActive=false unconditionally until follow-up phase wires
   TenantContext.getImpersonationOriginUserId + audit-log emitter). */
/* 390.3-06 visual lock (cross-AI 3/3): FRAME, don't FILL. When impersonating, Row 1 stays dark
   and legible — the "abnormal mode" cue is an amber top frame + inset edge, NOT hazard stripes
   behind the controls (stripes are isolated to the security band below). Industrial-HMI principle:
   alarm the perimeter + affected chips, keep system controls usable so the operator can navigate out. */
.im-shell-statusband--impersonating {
  border-block-start: 3px solid var(--led-amber);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--led-amber) 45%, transparent),
              inset 4px 0 0 0 var(--led-amber);
}

/* 390.3-05 impersonation security row — additive Row 2 alarm. ALWAYS mounted;
   collapsed by default, expanded (loud amber-hazard) when a role projection is
   active. Collapse-in-place via the narrowly-scoped D-07 synthesis-Q1 exemption
   (max-height/opacity transition on THIS selector only). Aura tokens only. */
/* 390.3-05 impersonation security row — modernized high-visibility Aura amber status banner */
.im-shell-security-row {
  display: flex;
  align-items: center;
  gap: var(--aura-space-s, 12px);
  padding-inline: 20px;
  font-family: var(--aura-font-family-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);
  font-size: 12px;
  color: var(--im-color-text-primary, #0f172a);
  background: linear-gradient(90deg, rgba(245, 158, 11, 0.18) 0%, rgba(217, 119, 6, 0.12) 100%);
  border-block-start: 1px solid rgba(245, 158, 11, 0.45);
  border-block-end: 1px solid rgba(245, 158, 11, 0.25);
  font-weight: 500;
  /* Collapsed-in-place defaults (mounted but hidden). */
  max-block-size: 0;
  padding-block: 0;
  opacity: 0;
  overflow: clip;
  pointer-events: none;
  flex-shrink: 0;
  inline-size: 100%;
  box-sizing: border-box;
  transition: max-block-size 200ms ease, opacity 200ms ease, padding-block 200ms ease;
}
.im-shell-security-row--expanded {
  max-block-size: 44px;
  padding-block: 6px;
  opacity: 1;
  pointer-events: auto;
}
/* Left decorative marker - subtle elegant amber pip instead of raw hazard stripes */
.im-shell-security-row::before {
  content: "";
  inline-size: 4px;
  align-self: stretch;
  background: #f59e0b;
  border-radius: 2px;
  flex-shrink: 0;
}
.im-shell-security-row__badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: #fef3c7;
  color: #b45309;
  border: 1px solid #fde68a;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  flex-shrink: 0;
}
.im-shell-security-row__cell {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-weight: 500;
  color: var(--im-color-text-primary, #1e293b);
}
.im-shell-security-row__chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  background: rgba(255, 255, 255, 0.65);
  border: 1px solid rgba(0, 0, 0, 0.08);
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 11px;
  font-weight: 600;
  color: #334155;
}
.im-shell-security-row__role {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.im-shell-security-row__role-pill {
  background: #ea580c;
  color: #ffffff;
  padding: 2px 10px;
  border-radius: 9999px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 1px 2px rgba(234, 88, 12, 0.25);
}
.im-shell-security-row__audit {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  color: #15803d;
  background: #dcfce7;
  border: 1px solid #bbf7d0;
  padding: 2px 8px;
  border-radius: 12px;
}
.im-shell-security-row__audit-led {
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: #16a34a;
  box-shadow: 0 0 6px #22c55e;
  animation: im-audit-pulse 2s infinite ease-in-out;
}
@keyframes im-audit-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.85); }
}
.im-shell-security-row__exit {
  margin-inline-start: auto;
  font-family: var(--aura-font-family-sans, sans-serif);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.03em;
  color: #ffffff !important;
  background: #dc2626 !important;
  border: none !important;
  border-radius: 6px !important;
  padding: 4px 12px !important;
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(220, 38, 38, 0.3) !important;
  transition: background 150ms ease, transform 100ms ease;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.im-shell-security-row__exit:hover {
  background: #b91c1c !important;
  transform: translateY(-1px);
}
/* prefers-reduced-motion: snap (no slide) */
@media (prefers-reduced-motion: reduce) {
  .im-shell-security-row {
    transition: none;
  }
  .im-shell-security-row__audit-led {
    animation: none;
  }
}

/* === Role Capability Tree in Context Switcher Dialog === */
.im-role-tree-container {
  display: flex;
  flex-direction: column;
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--aura-contrast-15pct, rgba(0, 0, 0, 0.12));
  border-radius: var(--aura-border-radius-m, 8px);
  background: var(--aura-surface-color, #ffffff);
  padding: 6px;
  box-sizing: border-box;
}
.im-role-tree-root {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--aura-primary-color, #2563eb);
  background: var(--aura-primary-color-5pct, rgba(37, 99, 235, 0.06));
  border: 1px dashed var(--aura-primary-color-30pct, rgba(37, 99, 235, 0.3));
  margin-bottom: 6px;
  transition: all 120ms ease;
}
.im-role-tree-root:hover {
  background: var(--aura-primary-color-10pct, rgba(37, 99, 235, 0.12));
}
.im-role-tree-root--selected {
  background: var(--aura-primary-color-20pct, rgba(37, 99, 235, 0.2)) !important;
  border-style: solid;
}
.im-role-tree-tier {
  flex-shrink: 0;
  min-height: min-content;
  margin-bottom: 6px;
  border: 1px solid var(--aura-contrast-10pct, rgba(0, 0, 0, 0.06));
  border-radius: 6px;
  overflow: hidden;
}
.im-role-tree-tier__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  background: var(--aura-contrast-5pct, rgba(0, 0, 0, 0.03));
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--im-color-text-secondary, #64748b);
  cursor: pointer;
  user-select: none;
  transition: background 120ms ease;
}
.im-role-tree-tier__header:hover {
  background: var(--aura-contrast-10pct, rgba(0, 0, 0, 0.06));
}
.im-role-tree-tier__count {
  font-size: 0.68rem;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 10px;
  background: var(--aura-contrast-10pct, rgba(0, 0, 0, 0.08));
  color: var(--im-color-text-secondary, #64748b);
}
.im-role-tree-tier__items {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px;
}
.im-role-tree-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 0.82rem;
  color: var(--im-color-text-primary, #1e293b);
  transition: all 120ms ease;
}
.im-role-tree-item:hover {
  background: var(--aura-contrast-5pct, rgba(0, 0, 0, 0.05));
}
.im-role-tree-item--selected {
  background: var(--aura-primary-color-15pct, rgba(37, 99, 235, 0.15)) !important;
  color: var(--aura-primary-text-color, #1d4ed8) !important;
  font-weight: 600;
}
.im-role-tree-item__left {
  display: flex;
  align-items: center;
  gap: 8px;
}
.im-role-tree-item__right {
  display: flex;
  align-items: center;
  gap: 6px;
}
.im-role-tree-item__weight {
  font-size: 0.68rem;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--aura-contrast-10pct, rgba(0, 0, 0, 0.06));
  color: var(--im-color-text-secondary, #64748b);
}
.im-role-tree-item__check {
  font-size: 0.75rem;
  color: var(--aura-primary-color, #2563eb);
  font-weight: 700;
}

/* Tenant pill — direct-click affordance (D-07: NO hover dropdown). */
/* Tenant context pill — the masthead's DOMINANT safety element (390.2-05,
 * sketch 037-D + operator-approved mockup). Three states:
 *   scoped (default) = dominant two-line pill (caption + name), accent border + glow
 *   --platform       = subdued single-line (no tenant bound, SUPER_ADMIN lens)
 *   --impersonating  = loud amber hijack */
.im-shell-tenant-pill {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 1px;
  /* 390.3-06 visual lock (3/3): slimmed from an oversized hero capsule to a compact, always-glanceable
     environment badge — reclaims Row 1 space now that switch/impersonate lives behind the Row 2 control. */
  padding: 4px 12px;
  min-inline-size: 0;
  /* 390.3-03 follow-up: the tenant pill is the safety-critical context label —
     never let sibling flex items squeeze it to an unreadable sliver at narrow
     width. It keeps its content width; long tenant names ellipsize via __name. */
  flex-shrink: 0;
  background: linear-gradient(180deg,
      color-mix(in srgb, var(--aura-accent-2) 14%, transparent),
      color-mix(in srgb, var(--aura-accent-2) 4%, transparent));
  color: var(--text);
  border: 1px solid color-mix(in srgb, var(--aura-accent-2) 45%, transparent);
  border-radius: 10px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--aura-accent-2) 10%, transparent),
              0 0 18px color-mix(in srgb, var(--aura-accent-2) 8%, transparent);
  cursor: pointer;
  transition: box-shadow 150ms;
}
.im-shell-tenant-pill__caption {
  font: 600 10px/1 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--aura-accent-2);
  opacity: 0.85;
}
.im-shell-tenant-pill__name {
  font-size: 14px;
  font-weight: 700;
  line-height: 1.15;
  /* 390.3-03 follow-up: long tenant names must NOT wrap the pill to 2-3 lines
     (operator feedback) — keep one line and ellipsize. min-inline-size:0 lets
     the flex child shrink below its content width so the ellipsis engages. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-inline-size: 0;
  max-inline-size: 220px;
}

/* 390.3-03 follow-up — single-tenant identity badge (buildTenantContextBadge).
   For a non-multi-tenant, non-SUPER_ADMIN user the dominant .im-shell-tenant-pill
   is NOT rendered (tenantPillVisible=false); the tenant identity shows here as two
   Vaadin-badge spans (tenant name + localized role). These were unclassed plain
   spans that wrapped long names/roles to 2-3 lines (operator feedback). Keep each
   on a single line and ellipsize; the badge row never wraps. */
.im-shell-tenant-badge {
  flex-wrap: nowrap;
  min-inline-size: 0;
}
.im-shell-tenant-badge__name,
.im-shell-tenant-badge__role {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-inline-size: 200px;
}
.im-shell-tenant-pill:hover {
  box-shadow: 0 0 0 1px var(--aura-accent-2),
              0 0 22px color-mix(in srgb, var(--aura-accent-2) 14%, transparent);
}
.im-shell-tenant-pill:focus-visible {
  outline: 2px solid var(--aura-accent-2);
  outline-offset: 2px;
}
/* Platform lens — no tenant bound: subdued, single-line; NOT the dominant glowing pill. */
.im-shell-tenant-pill--platform {
  flex-direction: row;
  align-items: center;
  min-inline-size: 0;
  padding: 6px 12px;
  background: var(--aura-surface-2);
  border: 1px solid var(--aura-border);
  box-shadow: none;
}
.im-shell-tenant-pill--platform .im-shell-tenant-pill__caption { display: none; }
.im-shell-tenant-pill--platform .im-shell-tenant-pill__name {
  font-size: 14px;
  font-weight: 600;
  color: var(--aura-text-secondary, #8f8f8f);
}
.im-shell-tenant-pill--impersonating {
  background: var(--led-amber);
  color: var(--aura-base-color);
  border-color: var(--led-amber);
  box-shadow: 0 0 18px color-mix(in srgb, var(--led-amber) 30%, transparent);
}
.im-shell-tenant-pill--impersonating .im-shell-tenant-pill__caption {
  color: var(--aura-base-color);
  opacity: 0.9;
}

/* Impersonation EXIT CTA — 10px UPPERCASE 600 weight 0.12em tracking. */
.im-shell-impersonation-exit {
  font: 600 10px/1 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 0 12px;
  color: var(--aura-base-color);
  background: var(--led-red);
  border-radius: 2px;
  cursor: pointer;
}
.im-shell-impersonation-exit:focus-visible {
  outline: 2px solid var(--aura-base-color);
  outline-offset: 2px;
}

/* sketch 045-A — Role badge tier × color. 7px JetBrains Mono UPPERCASE
   0.12em letter-spacing. STATIC — no hover detail card per D-07. */
.im-shell-statusband__role {
  display: inline-flex;
  align-items: center;
  border: 1px solid;
  padding: 4px 10px;
  border-radius: 999px;  /* 390.2-05: proper pill form (operator feedback: "role didn't adopt pill form") */
  font: 600 10px/1 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: transparent;
  /* 390.3-03 follow-up: long localized role labels (e.g. "Administrador de
     Instalaciones") must stay on a single pill line, not wrap to 2-3 lines. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-inline-size: 220px;
  /* Don't let the pill-shaped role chip compress to a single-letter circle when
     space is tight — it's hidden wholesale at narrow width instead (see media). */
  flex-shrink: 0;
}
.im-shell-statusband__role--platform {
  color: var(--aura-accent-1);
  border-color: var(--aura-accent-1);
  box-shadow: 0 0 4px var(--aura-accent-1);
}
.im-shell-statusband__role--tenant-admin {
  color: var(--aura-accent-2);
  border-color: var(--aura-accent-2);
}
.im-shell-statusband__role--operator {
  color: var(--led-green);
  border-color: var(--led-green);
}
.im-shell-statusband__role--enduser {
  color: var(--text-mute);
  border-color: var(--text-mute);
}

/* sketch 046-C inverted — softness=authority. 4-shape vocabulary
   exhaustive per AP-03 (no 5th sigil shape). Shape encoded in CSS via
   border-radius / clip-path; Java never branches on shape. */
.im-shell-statusband__sigil {
  inline-size: 18px;
  block-size: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
}
.im-shell-statusband__sigil__monogram {
  color: inherit;
}
.im-shell-statusband__sigil--platform {
  background: var(--aura-accent-1);
  border-radius: 50%;
  color: var(--aura-base-color);
}
.im-shell-statusband__sigil--tenant-admin {
  background: var(--aura-accent-2);
  border-radius: 5px;
  color: var(--aura-base-color);
}
.im-shell-statusband__sigil--operator {
  background: var(--led-green);
  clip-path: polygon(25% 5%, 75% 5%, 100% 50%, 75% 95%, 25% 95%, 0% 50%);
  color: var(--aura-base-color);
}
.im-shell-statusband__sigil--enduser {
  background: var(--text-mute);
  border-radius: 1px;
  color: var(--aura-base-color);
}

/* sketch 047-D — bell B+C composite, bell-tier color matrix. Click-only
   per D-07; NO hover panel. */
.im-shell-statusband__bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 16px;
  block-size: 16px;
  color: var(--text-mute);
  cursor: pointer;
}
.im-shell-statusband__bell:focus-visible {
  outline: 2px solid var(--aura-accent-2);
  outline-offset: 2px;
}
.im-shell-statusband__bell__glyph {
  font-size: 14px;
  line-height: 1;
}
.im-shell-statusband__bell--info  { color: var(--accent-2); }
.im-shell-statusband__bell--warn  { color: var(--led-amber); }
.im-shell-statusband__bell--alert {
  color: var(--led-red);
  animation: bell-pulse 1.6s infinite;
}
/* AP-21 EXEMPT — server-event feedback per CONTEXT.md D-07 item 9 +
   sketch 047-D §"Canonical decisions locked" #5 (400ms debounce). */
.im-shell-statusband__bell--increment {
  animation: bell-shake-d 500ms, count-flash-d 600ms;
}
.im-shell-statusband__bell__count {
  position: absolute;
  inset-block-start: -4px;
  inset-inline-end: -4px;
  min-inline-size: 12px;
  block-size: 12px;
  border-radius: 50%;
  font: 600 10px/1 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  background: currentColor;
  color: var(--aura-base-color);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 2px;
}

/* 390.3-02 Row 1 / Row 2 split — work-context navbar row geometry.
   The breadcrumb partition (· depth chip · search · ⌘K · ACTIONS) lives here,
   stacked beneath the Row 1 identity header. Reads as a distinct, DENSER sub-row:
   a hairline top border separates it from Row 1, reduced block padding, slightly
   muted surface. Primitive owns geometry (height/padding/border) — the Java host
   sets NO inline padding/background. D-07 ban stays in force: NO max-height /
   opacity / transform transition on this row (the narrowly-scoped collapse
   exemption is reserved for the Plan 05 security row). */
.im-shell-work-row {
  min-block-size: 34px;
  padding-block: var(--aura-space-xs, 4px);
  padding-inline: 24px;
  gap: var(--aura-space-m, 16px);
  border-block-start: 1px solid var(--aura-border, var(--border));
  background: var(--aura-surface-2, var(--surface, var(--bg)));
  font-family: var(--aura-font-family-mono, 'JetBrains Mono', ui-monospace, monospace);
  /* H-07 fix: left-aligned content (module › section › entity), Row-2 grammar. */
  justify-content: flex-start;
}
/* 390.3-06: compact context-switch icon pushed to the far right of Row 2 (switch tenant /
   impersonate), so the heavy tenant selector doesn't have to crowd Row 1.
   Label is a two-line stack: tenant name (main) + current/projected role (subline).
   Doubles the control as a live status read-out so the operator never has to look
   at Row 1 to verify "where am I + as whom". */
.im-shell-work-row__context-switch {
  margin-inline-start: auto;
  block-size: auto;
  padding-block: 2px;
  padding-inline: 10px;
  text-transform: none;
  line-height: 1.1;
}
.im-shell-work-row__context-switch__stack {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
}
.im-shell-work-row__context-switch__tenant {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--aura-text-primary, #e8edf5);
}
.im-shell-work-row__context-switch__role {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--aura-text-secondary, #8b95a8);
}

/* 390.3-02 H-07 fix — TRUE stacked rows.
   Vaadin AppLayout's [part="navbar"] is a flex-row container; by default each
   addToNavbar(false, …) slot becomes a flex ITEM on the SAME line as Row 1,
   so the work-row + status-band cluster inline-right instead of stacking.
   Force the navbar to wrap and make Row 1 + every secondary row occupy a full
   line (flex-basis:100%). This is a STATIC layout rule — no transition (D-07).
   Aura/Vaadin tokens only; zero --lumo-*. */
vaadin-app-layout::part(navbar) {
  flex-wrap: wrap;

  /* THE SHELL OUTRANKS PAGE CONTENT. WINDOWS #1549.
     Vaadin ships this part as `position: fixed; z-index: 1`, which makes it a STACKING CONTEXT.
     Everything addToNavbar() puts here is slotted INSIDE it — the account menu included — so the
     menu's own `z-index: 1000` is scoped to a context whose number, seen from outside, is 1. The
     dispatch console's status strip sits at 40 in page content, and 1 loses to 40: the operator
     opened the account menu and the heartbeat chip painted straight through it.

     Raising the panel's 1000 fixes nothing. That was the first thing tried and it is why this
     comment exists: a child can never escape a parent that already lost.

     WHY 150, measured live on this app rather than guessed:

         navbar (this rule)                  1  ->  150
         console rail / ribbon / mapctrl     30 / 20 / 10
         console status strip                40      <- the collision that was reported
         VAADIN OVERLAY LAYER               200      <- the ceiling; must NOT be crossed
         map legend + map status chip       400
         MapLibre cooperative-gesture      99999      (transient, and it should win)

     150 clears every console layer and stays BELOW 200, so a dialog, combo-box or context menu
     still covers the shell — which is the behaviour anyone expects from a modal. The obvious
     "just make it 500" would have put the whole navbar OVER the overlay layer and left every
     Vaadin dialog rendering under the app bar: a worse bug than the one being fixed, and invisible
     until someone opened a dialog.

     KNOWN AND NOT FIXED HERE: the map legend and map status chip sit at 400, i.e. already ABOVE
     the 200 overlay layer, so a Vaadin dialog renders UNDER them today. That is a pre-existing
     defect of the map chrome, not of this part, and it is filed separately — this rule
     deliberately does not paper over it by climbing past 400.

     The durable fix for the menu specifically is to make it a real overlay so it leaves this
     stacking context altogether; that is a follow-up on WINDOWS #1549 because it touches a
     component hosted by six layouts. This rule is the correct layering regardless of that: app
     shell chrome above page content is not a workaround. */
  z-index: 150;
}
/* Every top navbar slot (Row 1 identity + each secondary row) takes a full
   line, so they stack vertically instead of clustering on Row 1's right. The
   :not([slot="touch-navbar"]) guard leaves any future touch-bottom-bar slot
   (touchOptimized=true) out of the wrap rule. */
vaadin-app-layout [slot="navbar"]:not([slot~="touch-navbar"]) {
  flex: 1 1 100%;
  inline-size: 100%;
  max-inline-size: 100%;
  /* Belt-and-suspenders against horizontal overflow on the stacked rows. */
  box-sizing: border-box;
}

/* sketch 055-D — hierarchical breadcrumb + depth chip. STATIC chip per
   D-07 (NO hover-expand popover). Depth ≥4 renders static red. */
.im-shell-breadcrumb {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.im-shell-breadcrumb__top-section {
  font: 11px/1.4 'JetBrains Mono', ui-monospace, monospace;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--vaadin-secondary-text-color, var(--text-2));
  cursor: pointer;
}
/* Phase 600 plan 01 — a crumb whose cumulative path cannot be opened keeps its label and
   stops pretending to be a link. ONE modifier serves BOTH causes ("no route matches" and
   "your role set is narrower"); a second one would let a user distinguish them by reading
   the chrome, which is the route-enumeration oracle T-600-01 forbids.
   The colour MUST be --im-color-text-disabled (#5C6B78, a measured 4.7:1 on white). Not an
   --aura-contrast-Npct token and not a bare hex — both shipped 29 serious WCAG 2.2 AA
   color-contrast failures on this codebase in August 2026.
   CAUTION: design-tokens/generated/design-tokens.css declares a DIFFERENT value for this
   same token name. That file is mobile-only and generated; do not edit it, and do not read
   its value as the one that applies here.
   No :focus-visible rule, deliberately: the element carries no tabindex, so it can never be
   focused, and a focus ring that can never appear reads as a promise the component does not
   keep. */
.im-shell-breadcrumb__top-section--static {
  color: var(--im-color-text-disabled, #5C6B78);
  cursor: default;
}
/* 390.3-03 follow-up: separator between expanded breadcrumb-trail segments
   (operator wants the full path, not a "+N" collapse chip). Muted, non-interactive. */
.im-shell-breadcrumb__sep {
  font: 11px/1.4 'JetBrains Mono', ui-monospace, monospace;
  color: var(--vaadin-tertiary-text-color, var(--text-mute));
  user-select: none;
}
.im-shell-breadcrumb__top-section:focus-visible {
  outline: 2px solid var(--aura-accent-2);
  outline-offset: 2px;
}
.im-shell-depth-chip {
  padding: 2px 6px;
  font: 600 9px/1 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  color: var(--aura-accent-2);
  border: 1px solid var(--aura-accent-2);
  border-radius: 2px;
  background: transparent;
  cursor: pointer;
}
.im-shell-depth-chip--warn {
  color: var(--led-amber);
  border-color: var(--led-amber);
}
.im-shell-depth-chip--alert {
  /* STATIC red — NO pulse animation per D-07. */
  color: var(--led-red);
  border-color: var(--led-red);
}

/* 390.3-07 — Notification-bell SEVERITY TIERS. PLACED AFTER the chrome-v2
   component base rules so the severity modifiers win on source order (the
   masthead @media block below references the same .im-shell-bell host). The
   .im-shell-bell host carries exactly one tier modifier (muted/amber/red) set
   server-side from the unread count; the live push plumbing is untouched —
   only the badge presentation changes. Aura tokens only; no --lumo-*.
   Industrial-HMI restraint: muted is the resting state so the alarm channel
   (amber → red) stays meaningful for the moment it actually fires. */
.im-shell-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
}
.im-shell-bell__button {
  position: relative;
  cursor: pointer;
}
.im-shell-bell__badge {
  position: absolute;
  inset-block-start: -4px;
  inset-inline-end: -4px;
  min-inline-size: 18px;
  block-size: 18px;
  line-height: 18px;
  padding: 0 4px;
  border-radius: 50%;
  font: 600 10px/18px 'JetBrains Mono', ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--aura-base-color, #0b0b0d);
  /* Amber is the default visible-badge tier (unread present). */
  background: var(--led-amber);
}
/* Muted: 0 unread — the badge is hidden server-side (setVisible(false)); the
   resting bell glyph stays its normal secondary color (no badge dot). */
.im-shell-bell--muted .im-shell-bell__badge {
  background: var(--vaadin-tertiary-text-color, var(--text-mute));
}
/* Amber: unread alerts present — attention without alarm. */
.im-shell-bell--amber .im-shell-bell__badge {
  background: var(--led-amber);
  color: var(--aura-base-color, #0b0b0d);
}
/* Red (critical): an UNACKNOWLEDGED panic alert -- loud, with a subtle pulse. White text on
   red for max legibility.
   This tier used to mean "high unread volume" (ten or more unread notifications). That was a
   volume signal wearing a severity costume: ten routine notifications painted the masthead red
   and one panic alert painted it amber. applySeverityTier() now reaches this class only from
   CriticalAlertState.hasOutstanding(), so red means severity and nothing else. The count moved to
   the badge TEXT, which is what a count is for. */
.im-shell-bell--critical .im-shell-bell__badge {
  background: var(--led-red);
  color: #fff;
  animation: im-shell-bell-pulse 1.6s ease-in-out infinite;
}
@keyframes im-shell-bell-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--led-red) 55%, transparent); }
  50%      { box-shadow: 0 0 0 4px color-mix(in srgb, var(--led-red) 0%, transparent); }
}
/* The critical pulse honors prefers-reduced-motion: stay red, drop the animation. */
@media (prefers-reduced-motion: reduce) {
  .im-shell-bell--critical .im-shell-bell__badge {
    animation: none;
  }
}

/* ── The GLYPH carries the alarm, not only the badge ────────────────────────────────────────────
   Everything above styles the 18px badge and leaves the bell itself the same secondary grey it is
   at rest. That asks the operator to read a two-digit disc to learn a yes/no fact. The icon is the
   larger, more peripheral shape -- it is what a glance actually lands on -- so the state is put
   there and the badge goes back to answering "how many".

   COLOUR COMES FROM --fm-warn, NOT --led-amber. The badge above uses --led-amber, which is
   `#B26A00` flat -- NOT wrapped in light-dark(), because it is tuned to pass contrast on white.
   That is right for a filled disc (dark amber under near-black text) and wrong for a glyph on the
   dark masthead, where #B26A00 reads as muddy brown rather than gold. --fm-warn is
   light-dark(#b26a00, #e0a030): the same amber in daylight, real gold in the dark scheme, chosen
   per scheme instead of compromising across both.

   THE GLOW IS drop-shadow, NOT box-shadow. box-shadow follows the element's BOX, so it would draw
   a soft rectangle around a bell-shaped glyph -- the giveaway being a halo with corners. filter:
   drop-shadow() follows the rendered alpha, so on an SVG icon it traces the bell's own outline.
   Two stacked drop-shadows give the falloff a single blur cannot: a tight bright core plus a wide
   faint bloom. */
.im-shell-bell--amber .im-shell-bell__button vaadin-icon,
.im-shell-bell--critical .im-shell-bell__button vaadin-icon {
  color: var(--fm-warn, #e0a030);
  filter:
    drop-shadow(0 0 3px color-mix(in srgb, var(--fm-warn, #e0a030) 70%, transparent))
    drop-shadow(0 0 9px color-mix(in srgb, var(--fm-warn, #e0a030) 40%, transparent));
  transition: color 180ms ease, filter 180ms ease;
}

/* Critical keeps the RED badge while the glyph stays gold: two channels saying different things --
   "there is something" and "how many emergencies nobody has taken" -- rather than one colour
   repeated at two sizes. The glow breathes in step with the badge's existing pulse so the
   pair reads as one instrument; same keyframe duration and easing, deliberately. */
.im-shell-bell--critical .im-shell-bell__button vaadin-icon {
  animation: im-shell-bell-glyph-glow 1.6s ease-in-out infinite;
}
@keyframes im-shell-bell-glyph-glow {
  0%, 100% { filter: drop-shadow(0 0 3px color-mix(in srgb, var(--fm-warn, #e0a030) 70%, transparent))
                     drop-shadow(0 0 9px color-mix(in srgb, var(--fm-warn, #e0a030) 40%, transparent)); }
  50%      { filter: drop-shadow(0 0 5px color-mix(in srgb, var(--fm-warn, #e0a030) 95%, transparent))
                     drop-shadow(0 0 16px color-mix(in srgb, var(--fm-warn, #e0a030) 65%, transparent)); }
}

/* Reduced motion: keep the gold and the glow -- they are STATE, and removing them would remove the
   information. Only the breathing stops. This mirrors the badge rule above rather than inventing a
   second policy for the same preference. */
@media (prefers-reduced-motion: reduce) {
  .im-shell-bell--critical .im-shell-bell__button vaadin-icon {
    animation: none;
  }
}

/* The resting state is stated EXPLICITLY instead of being left to inheritance. The tier modifiers
   are swapped live by applySeverityTier() as pushes arrive, so a bell that has been amber and goes
   back to zero must actively return to grey; relying on "no rule matches" leaves whatever the
   previous class set still applied in browsers that keep the computed filter. */
.im-shell-bell--muted .im-shell-bell__button vaadin-icon {
  color: var(--im-color-text-disabled, #5C6B78);
  filter: none;
  animation: none;
}

/* Notification-panel ARRIVAL highlight. The open panel refreshes itself over Vaadin Push, so a row
   can appear under the operator's eyes with nothing marking WHICH row is new — the list simply got
   longer. This is the one-shot grammar the bell already uses for --increment (sketch 047-D), not the
   toast's: the panel is an INLINE surface sharing attention with its surroundings, and the 022-C
   asymmetry rule keeps inline rhetoric subtle (sketch 054-C was rejected for shaking one).

   Applied as an inset box-shadow rather than background-color on purpose: unread rows already carry
   a background tint, and animating that property would fight the resting state instead of layering
   over it. Colour comes from the 035-C tier palette (--accent-2, the info tier) — a panel-specific
   severity token would fragment the vocabulary. */
@keyframes im-notif-arrive {
  from {
    box-shadow: inset 0 0 0 9999px color-mix(in srgb, var(--accent-2, #4db8c8) 24%, transparent);
  }
  to {
    box-shadow: inset 0 0 0 9999px transparent;
  }
}
.im-notif-card--new {
  animation: im-notif-arrive 1400ms ease-out 1;
}
/* Reduced motion still needs to answer "which one is new?" — so the tint STAYS instead of fading.
   Dropping to no signal at all would make the feature exist only for people who can take animation. */
@media (prefers-reduced-motion: reduce) {
  .im-notif-card--new {
    animation: none;
    box-shadow: inset 0 0 0 9999px color-mix(in srgb, var(--accent-2, #4db8c8) 16%, transparent);
  }
}

/* Notification-panel ACTIVE glow. This dialog is MODELESS: the application behind it stays live and
   clickable, so nothing tells the operator which surface their keyboard is actually pointed at — a
   modal answers that with its backdrop, this one had no answer at all.

   Same vocabulary as the global field focus glow (sketch 081-A): a tone, an on/off switch that
   resolves the halo to `0 0 0 0px` at rest, and :focus-within as the ONLY activation selector. Reusing
   the field formula rather than inventing a panel-specific one keeps "this thing has focus" looking
   the same everywhere in the product.

   Scoped by .im-notif-panel rather than the [modeless] state selector: modelessness is a behaviour
   many surfaces could adopt later, and this rule is about THIS panel. */
vaadin-dialog.im-notif-panel {
  --im-panel-tone: var(--im-color-primary, #007f8c);
  --im-panel-on: 0;
  --im-panel-glow: color-mix(in oklab, var(--im-panel-tone) 22%, transparent);
}
/* ACTIVATION — measured, not assumed. `:focus-within` alone DOES NOT WORK on vaadin-dialog: with a
   button inside the panel holding focus, the chain reads true all the way up through
   vaadin-dialog-content and then false on the host. Vaadin projects the content into a shadow
   overlay, and focus-within does not survive that boundary, so a glow keyed on it would light up for
   the instant after open and never again — worse than no glow, because it would claim the panel is
   inactive while the operator types in it. `:has()` reaches across, and is verified to match on the
   same DOM where `:focus-within` returns false. Both are kept: if a Vaadin upgrade ever propagates
   focus-within properly, the first selector takes over and the second becomes redundant, not wrong. */
vaadin-dialog.im-notif-panel:focus-within,
vaadin-dialog.im-notif-panel:has(> vaadin-dialog-content:focus-within) {
  --im-panel-on: 1;
}

/* ── Notification Drawer: Modal Slide-out with Backdrop Blur ─────────────────── */
vaadin-dialog.im-notif-panel,
vaadin-dialog[theme~="im-notif-drawer"] {
  --vaadin-dialog-padding: 0;
  --vaadin-dialog-border-radius: 0;
  --vaadin-overlay-backdrop-background: rgba(15, 23, 42, 0.28);
}

vaadin-dialog-overlay[theme~="im-notif-drawer"] {
  justify-content: flex-end;
  align-items: stretch;
}

vaadin-dialog.im-notif-panel::part(backdrop),
vaadin-dialog[theme~="im-notif-drawer"]::part(backdrop),
vaadin-dialog-overlay[theme~="im-notif-drawer"]::part(backdrop) {
  backdrop-filter: blur(2.5px);
  -webkit-backdrop-filter: blur(2.5px);
  background-color: rgba(15, 23, 42, 0.28);
}

vaadin-dialog.im-notif-panel::part(overlay),
vaadin-dialog[theme~="im-notif-drawer"]::part(overlay),
vaadin-dialog-overlay[theme~="im-notif-drawer"]::part(overlay) {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  height: 100vh;
  max-height: 100vh;
  width: 440px;
  max-width: 100vw;
  margin: 0 0 0 auto;
  border-radius: 0;
  border-inline-start: 1px solid var(--border, #c9d3dc);
  box-shadow:
    0 0 0 calc(var(--im-panel-on) * 3px) var(--im-panel-glow),
    -8px 0 28px rgba(0, 0, 0, 0.16);
  background: var(--surface, #ffffff);
  animation: im-notif-drawer-slide-in 200ms cubic-bezier(0.16, 1, 0.3, 1);
  transition: box-shadow 150ms ease;
}

@keyframes im-notif-drawer-slide-in {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  vaadin-dialog.im-notif-panel::part(overlay),
  vaadin-dialog[theme~="im-notif-drawer"]::part(overlay),
  vaadin-dialog-overlay[theme~="im-notif-drawer"]::part(overlay) {
    animation: none;
    transition: none;
  }
}

vaadin-dialog.im-notif-panel::part(content),
vaadin-dialog[theme~="im-notif-drawer"]::part(content),
vaadin-dialog-overlay[theme~="im-notif-drawer"]::part(content) {
  padding: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.im-notif-drawer__header {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border, #c9d3dc);
  background: var(--surface, #ffffff);
  flex-shrink: 0;
}
.im-notif-drawer__settings-btn {
  color: var(--im-color-text-secondary, #475569);
}
.im-notif-categories {
  padding: 8px 16px;
  background: var(--surface, #ffffff);
  border-bottom: 1px solid var(--border, #c9d3dc);
  display: flex;
  gap: 6px;
  overflow-x: auto;
  flex-shrink: 0;
  scrollbar-width: thin;
}
.im-notif-category-pill {
  border-radius: 999px;
  font-size: var(--aura-font-size-xs, 0.75rem);
  font-weight: 500;
  padding: 4px 10px;
  background: var(--im-color-neutral-subtle, #f1f5f9);
  color: var(--im-color-text-primary, #1e293b);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: all 120ms ease;
  white-space: nowrap;
}
.im-notif-category-pill:hover {
  background: var(--im-color-neutral-muted, #e2e8f0);
}
.im-notif-category-pill--active {
  background: var(--accent-1, #007f8c) !important;
  color: #ffffff !important;
  font-weight: 600;
}
.im-notif-category-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  font-size: 0.6875rem;
  font-weight: 700;
  background: var(--im-color-badge-red, #dc2626);
  color: #ffffff;
  line-height: 1;
}
.im-notif-category-pill--active .im-notif-category-badge {
  background: #ffffff;
  color: var(--accent-1, #007f8c);
}
.im-notif-drawer__content {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface, #ffffff);
  overflow: hidden;
}
.im-notif-feed {
  overflow-y: auto;
  flex-grow: 1;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* "N nuevas ↑" pill — the reach-up affordance for arrivals that land above a scrolled viewport,
   where the arrival highlight fades unseen. Info tier (--accent-2) from the 035-C palette: an
   arrival is not a severity event, and borrowing amber/red here would inflate routine traffic into
   something that looks like an alert. Text colour comes from the locked --im-color-* family, whose
   contrast ratios are measured — an --aura-contrast-*pct value is an appearance, not a guarantee. */
/* The rotating border needs --im-pill-angle REGISTERED. An unregistered custom property is just a
   string to the engine, so it cannot be interpolated and the gradient sits frozen at 0deg — the
   animation runs and nothing moves. Registering it with syntax:"<angle>" is what makes it animatable.
   First use of @property in this codebase.

   The sweep stays inside the BRAND family (accent-2 → accent-1 → a white-lifted highlight) and
   deliberately leaves out --led-red / --led-amber / --led-green. Those are the severity palette: a
   rotating red-and-amber ring turns a routine arrival into something that reads like an alarm, and
   the operator learns to distrust the colours that are supposed to mean something. What sweeps here
   is a highlight along a brand-toned ring — motion carries the "look at me", not hue. */
@property --im-pill-angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

.im-notif-newpill {
  align-self: center;
  padding: 2px 12px;
  /* Transparent border + two backgrounds, NOT border-image. border-image ignores border-radius, so
     the obvious spelling would square off the pill's corners while painting the gradient. Clipping
     one layer to padding-box and the gradient to border-box keeps the 999px radius intact. */
  border: 2px solid transparent;
  border-radius: 999px;
  background:
    linear-gradient(
      color-mix(in srgb, var(--accent-2, #005e68) 10%, var(--surface, #fff)),
      color-mix(in srgb, var(--accent-2, #005e68) 10%, var(--surface, #fff))
    )
    padding-box,
    conic-gradient(
      from var(--im-pill-angle),
      var(--accent-2, #005e68) 0deg,
      var(--accent-1, #007f8c) 80deg,
      color-mix(in srgb, var(--accent-1, #007f8c) 30%, #fff) 140deg,
      var(--accent-1, #007f8c) 200deg,
      var(--accent-2, #005e68) 360deg
    )
    border-box;
  color: var(--im-color-text-secondary, #2c3a47);
  font-size: var(--vaadin-font-size-xs, 0.75rem);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}

/* Only spin while the pill is actually showing. An animation on an invisible element burns frames
   for nobody, and this one is on screen exactly as long as there is something unseen — the moment
   the operator scrolls up to the arrival, the pill goes and the motion goes with it. Self-limiting
   by construction, which is why an infinite loop is acceptable here and would not be on a permanent
   surface. */
.im-notif-newpill:not(.im-notif-newpill--off) {
  animation: im-pill-rotate 3s linear infinite;
}
@keyframes im-pill-rotate {
  to {
    --im-pill-angle: 360deg;
  }
}

/* Reduced motion keeps the multicolour ring, drops the rotation — the border still reads as "this
   just arrived" without anything moving. */
@media (prefers-reduced-motion: reduce) {
  .im-notif-newpill:not(.im-notif-newpill--off) {
    animation: none;
  }
}

.im-notif-newpill:hover {
  filter: brightness(1.06);
}

/* ── PINNED critical-alert row (Phase 608, CriticalAlertPinnedRow) ─────────────────────────────
   The oldest OUTSTANDING panic alert, pinned directly under the drawer header and above every feed
   affordance. It replaces the click hijack that used to send the operator straight at the alert
   INSTEAD of opening the drawer at all: the severity claim survives, the drawer comes back.

   Severity vocabulary is the one already locked for this state, not a new one. --led-red is the
   same token .im-shell-bell--critical paints the badge with, so the row and the bell that opened it
   say the same thing in the same colour. It is used as a LEFT RULE and a low-percentage tint rather
   than a fill: this is an inline surface sharing attention with the feed below it, and the 022-C
   asymmetry rule keeps inline rhetoric quieter than a modal's. No pulse here either — the bell is
   already pulsing a few pixels away, and two instruments breathing out of phase reads as noise.

   TEXT COLOUR COMES FROM --im-color-*, WITHOUT EXCEPTION. Not --aura-contrast-Npct, which names an
   appearance rather than a measured ratio, and not a bare hex, which measures nothing. Both are
   light-dark() pairs, so the row is legible in either scheme without a second rule. */
.im-notif-pinned {
  border-inline-start: 3px solid var(--led-red, #C73535);
  border-block-end: 1px solid var(--border, #c9d3dc);
  background: color-mix(in srgb, var(--led-red, #C73535) 8%, transparent);
  padding: var(--vaadin-space-m, 0.75rem);
  display: flex;
  flex-direction: column;
  gap: var(--vaadin-space-xs, 0.25rem);
}
.im-notif-pinned__headline {
  color: var(--im-color-text-secondary, #2C3A47);
  font-weight: 600;
  /* The bell's own label wraps in the masthead tooltip; here it has a whole row, and the age and
     the count must both stay readable rather than being clipped to fit one line. */
  line-height: 1.35;
}
.im-notif-pinned__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vaadin-space-xs, 0.25rem);
}
/* The "no deep link" explanation. Muted on purpose: its subject is the ABSENCE of a link, not the
   alert — raising it to alert tier would tell the operator that something new is wrong when the
   alert itself is unchanged and still stated in full above. --im-color-text-disabled is the muted
   member of the measured family (4.7:1 on white), not a grey picked by eye. */
.im-notif-pinned__note {
  color: var(--im-color-text-disabled, #5C6B78);
  font-size: var(--vaadin-font-size-xs, 0.75rem);
}

/* ── Notification Cards ─────────────────────────────────────────────────────── */
.im-notif-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--border, #e2e8f0);
  background: var(--surface, #ffffff);
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}
.im-notif-card:hover {
  background-color: var(--aura-contrast-5pct, var(--im-color-neutral-50, #f8fafc));
  border-color: var(--im-color-primary, #007f8c);
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.08);
}
.im-notif-card--unread {
  background-color: color-mix(in srgb, var(--im-color-primary, #007f8c) 4%, var(--surface, #ffffff));
  border-inline-start: 4px solid var(--im-color-primary, #007f8c);
}
.im-notif-card--unread:hover {
  background-color: color-mix(in srgb, var(--im-color-primary, #007f8c) 8%, var(--surface, #ffffff));
}
.im-notif-card--read {
  background-color: var(--surface, #ffffff);
  border-inline-start: 4px solid transparent;
  opacity: 0.9;
}
.im-notif-card--read:hover {
  opacity: 1;
}

.im-notif-card__header {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
.im-notif-icon-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  min-width: 28px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--im-color-primary, #007f8c) 10%, transparent);
  color: var(--im-color-primary, #007f8c);
}
.im-notif-card--read .im-notif-icon-wrap {
  background: var(--aura-contrast-10pct, var(--im-color-neutral-100, #f1f5f9));
  color: var(--im-color-text-secondary, #475569);
}
.im-notif-card__title {
  font-size: var(--vaadin-font-size-s, 0.875rem);
  color: var(--im-color-text-primary, #1e293b);
  line-height: 1.35;
  letter-spacing: -0.01em;
}
.im-notif-card__message {
  font-size: var(--vaadin-font-size-xs, 0.78rem);
  color: var(--im-color-text-secondary, #475569);
  line-height: 1.45;
  padding-inline-start: 36px;
}
.im-notif-card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-inline-start: 36px;
  margin-top: 2px;
}
.im-notif-card__time {
  font-size: 11px;
  color: var(--im-color-text-disabled, #64748b);
  font-variant-numeric: tabular-nums;
}
.im-notif-card__channels {
  display: flex;
  align-items: center;
  gap: 6px;
}
.im-notif-archive-btn {
  opacity: 0.45;
  transition: opacity 120ms ease;
}
.im-notif-card:hover .im-notif-archive-btn {
  opacity: 1;
}
.im-notif-more-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-inline-start: 36px;
  margin-top: 4px;
  font-size: 11px;
  font-weight: 500;
  color: var(--im-color-primary, #007f8c);
  padding: 3px 8px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--im-color-primary, #007f8c) 8%, transparent);
  cursor: pointer;
  transition: background-color 120ms ease;
  width: fit-content;
}
.im-notif-more-btn:hover {
  background: color-mix(in srgb, var(--im-color-primary, #007f8c) 16%, transparent);
}
.im-notif-group-expanded {
  margin-inline-start: 16px;
  border-inline-start: 2px dashed var(--border, #cbd5e1);
  padding-inline-start: 8px;
  margin-top: 4px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* NUEVA badge on an arrival row. Green (--led-green, the success tier) rather than red: red is the
   ALERT tier, and hanging it on routine arrivals is how a severity palette stops meaning anything —
   the operator learns that red is just decoration and then misses the one that mattered. Green is
   the smaller collision of the two: it reads "fresh" rather than "something is wrong".

   White text on --led-green (#16845B) measures 4.9:1, past the 4.5:1 AA threshold for this size. */
.im-notif-badge-new {
  flex: 0 0 auto;
  padding: 1px 7px;
  border-radius: 999px;
  background: var(--led-green, #16845b);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 15px;
  text-transform: uppercase;
}

/* The pill RESERVES its 31px whether or not it is showing. Hiding it with the `hidden` attribute
   removed those pixels from the flex column and the list grew to fill them — so a reader sitting at
   the bottom lost part of the last row the moment a notification arrived. Scroll anchoring cannot
   fix that: the offset was right, the WINDOW got shorter. `visibility` keeps the box, so the list's
   height never moves, and it also drops the button out of the tab order while invisible — which the
   `hidden` attribute did for free and `opacity: 0` would have silently broken. */
.im-notif-newpill--off {
  visibility: hidden;
}

/* 390.3-03 follow-up + 390.3-07 — responsive Row 1 masthead collapse. PLACED
   AFTER all chrome-v2 component base rules (role ~1600, tenant-pill ~1443,
   bell severity above) so these overrides win on source order (an earlier copy
   was silently defeated by the later base rules).

   390.3-07 LOCKED collapse order (UI-SPEC §Responsive collapse) — as width
   shrinks the masthead sheds its lowest-priority elements FIRST, in this order:
       env chip → ticker → breadcrumb depth → search → role badge → actions(⋮)
   HARD INVARIANT (H-07 gate): NEVER hidden at any breakpoint — the tenant pill,
   the impersonation security row (+ its EXIT), the notification bell, the
   navigation title (breadcrumb top-section), and the region clock
   ([data-row="utc"]). The ticker (390.3-04) is RETIRED — no .im-shell-ticker
   element exists, so it is intentionally absent from the cascade (not
   resurrected). Aura tokens only; no --lumo-*. */
@media (max-width: 1280px) {
  .im-shell-ops { gap: 10px; }
  /* 1. env chip drops first (lowest priority). version cell is the ambient env read-out. */
  .im-shell-ops [data-row="version"] { display: none; }
}
@media (max-width: 1180px) {
  /* 2. breadcrumb depth chip — the trail top-section (navigation title) stays. */
  .im-shell-depth-chip { display: none; }
}
@media (max-width: 1080px) {
  /* 3. context search (if/when present) drops before the role badge. */
  .im-shell-search,
  .im-shell-work-row__search { display: none; }
  .im-shell-ops__cell + .im-shell-ops__cell { padding-inline-start: 10px; }
  .im-shell-tenant-pill__name { max-inline-size: 160px; }
  .im-shell-tenant-badge__name { max-inline-size: 150px; }
}
@media (max-width: 940px) {
  /* 4. role badge — the lowest-priority identity token. The tenant pill carries
     the safety-critical context and STAYS. */
  .im-shell-statusband__role { display: none; }
  .im-shell-tenant-badge__role { display: none; }
  /* Keep the LIVE LED dot, drop its "SISTEMA EN VIVO" label text. */
  .im-shell-ops [data-row="sys-live"] span:not(.im-shell-led) { display: none; }
  .im-shell-tenant-pill__name { max-inline-size: 120px; }
  .im-shell-tenant-badge__name { max-inline-size: 120px; }
}
@media (max-width: 820px) {
  /* 5. actions(⋮) menu collapses last of the droppable elements. The bell,
     tenant pill, security row, EXIT, and navigation title never drop. */
  .im-shell-actions,
  .im-shell-work-row__actions { display: none; }
}

/* Drawer slides in sync with the content area (operator UAT 2026-05-22 —
   drawer + main content must move together). Matches AppLayout's content
   padding transition (0.2s ease), superseding the D-07 item-3 instant-toggle. */
vaadin-app-layout::part(drawer),
vaadin-app-layout [slot="drawer"] {
  transition: transform 0.2s ease !important;
}

/* @keyframes for bell --increment (AP-21 EXEMPT per CONTEXT.md D-07
   item 9 + sketch 047-D §5). No other chrome v2 element animates. */
@keyframes bell-pulse {
  0%, 100% { filter: brightness(0.85); }
  50%      { filter: brightness(1.15); }
}
@keyframes bell-shake-d {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-2px); }
  75%      { transform: translateX(2px); }
}
@keyframes count-flash-d {
  0%, 100% { transform: scale(1);    filter: brightness(1); }
  50%      { transform: scale(1.15); filter: brightness(1.3); }
}
/* === end Phase 386 chrome v2 contract === */

/* ============================================================
   V19 view-level grammar — S02 T01
   ============================================================
   View-level recipes for the demo-console + operator-dashboard
   re-skin (and downstream S03–S05 consumers). These layer on top
   of the shell-level .im-shell-* family above; consumers pick by
   intent — shell chrome decoration vs. view content surface.

   Extensibility per D032: each recipe references only the palette-
   scoped CSS variables (--bg, --text, --accent-1, --accent-2,
   --led-*, --border, --surface) declared on :root. Zero --lumo-* references.
   (Civic light is the only skin since Phase 437.1; no palette tier.) */

/* @deprecated v33.0 (Phase 361)
   Replaced by .im-dash-* recipes in dash-kit.css.
   Kept resolvable until Phases 362-366 migrate Demo Console / Dispatch /
   Cost+Driver / Parent Portal off these classes.
   New views MUST NOT add this class — see sketch-findings-facilitymanagement skill. */
/* Left-bordered card — the structural unit for S02–S05 view content.
   4 px accent spine + standard padding + surface fallback. Five semantic
   variants swap the spine color for status / accent intent without
   changing geometry. Sketch 001-D / 002-D won on this primitive. */
.im-card-bordered {
  position: relative;
  box-sizing: border-box;
  inline-size: 100%;
  padding-block: var(--aura-space-m, 14px);
  padding-inline: 16px;
  background: var(--surface, var(--bg));
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--accent-1);
  border-radius: 4px;
}
.im-card-bordered--accent-2 { border-inline-start-color: var(--accent-2); }
.im-card-bordered--success  { border-inline-start-color: var(--led-green); }
.im-card-bordered--warn     { border-inline-start-color: var(--led-amber); }
.im-card-bordered--alert    { border-inline-start-color: var(--led-red); }

/* @deprecated v33.0 (Phase 361)
   Replaced by .im-dash-* recipes in dash-kit.css. Per-card inline gauges
   land in the V19-DASHBOARD-001 follow-up against the new kit primitives.
   Kept resolvable until Phases 362-366 migrate consumers off this class.
   New views MUST NOT add this class — see sketch-findings-facilitymanagement skill. */
/* Gauge bar — CSS-variable-driven horizontal fill. Default block-size
   6 px (per-card inline gauges); callers can opt into a taller hero
   variant by overriding block-size inline (e.g. sketch 002-D's
   sessions hero gauge runs at 10 px). Single element + ::before pseudo
   keeps the Java integration clean: setStyle("--fill-pct", "42%")
   rather than constructing a child Div with computed inline width. */
.im-gauge-bar {
  position: relative;
  display: block;
  block-size: 6px;
  background: var(--border-soft, var(--border));
  border-radius: 3px;
  overflow: hidden;
}
.im-gauge-bar::before {
  content: "";
  display: block;
  block-size: 100%;
  inline-size: var(--fill-pct, 0%);
  background: linear-gradient(90deg, var(--accent-1), var(--accent-1-glow, var(--accent-1)));
  transition: inline-size 200ms ease;
}

/* @deprecated v33.0 (Phase 361)
   Replaced by DashboardMasthead.setBookend() in dash-kit.css.
   The masthead bookend slot now carries the panel-label rhetoric inline.
   Kept resolvable until Phases 362-366 migrate consumers off this class.
   New views MUST NOT add this class — see sketch-findings-facilitymanagement skill. */
/* Panel-label tab — view-level variant of .im-shell-panel-label.
   Inline (not absolute-positioned), monospace, slightly larger
   (11 px vs the shell-level 9 px) so it reads as a section header
   inside view content rather than as floating chrome on a panel
   border. Mute color so it doesn't compete with the actual heading
   below. Per Sketch 001-D and 002-D: text format "▸ VIEW NAME · NN". */
.im-panel-label {
  font-family: var(--aura-font-family-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-mute);
  padding-block: var(--aura-space-xs, 4px);
  padding-inline: var(--aura-space-s, 8px);
  border-block-end: 1px solid var(--border-soft, var(--border));
  margin-block-end: var(--aura-space-m, 14px);
}

/* @deprecated v33.0 (Phase 361)
   Replaced by .im-dash-led in dash-kit.css.
   The kit's .im-dash-led primitive (sketches 006-013) is the canonical
   LED dot surface for dashboard chrome. Sketch 035-C codified .im-status-led
   variants as spine-level severity primitives in severity-and-status.md.
   Kept resolvable until Phases 362-366 migrate Cost Analysis + Driver
   Compliance Grid<> consumers off this class.
   New views MUST NOT add this class — see sketch-findings-facilitymanagement skill. */
/* ─── S04 view-level grammar (Sketch 004-A) ─────────────────────────────
 * Inline status-LED helper + informational banner + legacy
 * recomputing-badge alias. Per Sketch 004 Variant A: status surfaces as
 * 8 px LED dots inside Vaadin Grid<> cell renderers (semantic accent
 * inline rather than per-row spine, since Grid::part(row) recipe is
 * deferred to V19-RECIPE-001). Palette tokens only. */

.im-status-led {
  display: inline-block;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  margin-inline-end: var(--aura-space-s, 8px);
  vertical-align: middle;
}
.im-status-led--green { background: var(--led-green); box-shadow: 0 0 4px var(--led-green); }
.im-status-led--amber { background: var(--led-amber); box-shadow: 0 0 4px var(--led-amber); }
.im-status-led--red {
  background: var(--led-red);
  box-shadow: 0 0 4px var(--led-red);
  animation: im-status-led-pulse 1.6s ease-in-out infinite;
}

@keyframes im-status-led-pulse {
  0%, 100% { opacity: 1;    box-shadow: 0 0 4px var(--led-red); }
  50%      { opacity: 0.55; box-shadow: 0 0 10px var(--led-red); }
}

/* THE SAME PULSE FOR A GLYPH LED, and it needs different keyframes rather than a tweak.
 *
 * `box-shadow` follows the element's BOX. That is correct for the LEDs above, which are real
 * elements with `border-radius: 50%` and a background — a round box gets a round glow. It is wrong
 * for a LED drawn as TEXT (`content: "● "` on an inline ::before), whose box is a rectangular text
 * box roughly 7x14px: the glow comes out as a soft SQUARE around a round dot, which reads as a
 * broken icon rather than as a pulse. Reported on /schoolbus/dispatch.
 *
 * `text-shadow` follows the GLYPH OUTLINE, so the same declaration on the same character produces
 * a round halo. Nothing else changes — same colours, same radii, same timing.
 *
 * Note why only ONE surface showed it: `.im-banner-warn::before` draws the identical `●` and looks
 * fine, because it carries no animation. The defect needed the glyph AND the pulse together, so
 * four of the five consumers of the box version are correct and stay on it. */
@keyframes im-status-led-pulse-glyph {
  0%, 100% { opacity: 1;    text-shadow: 0 0 4px var(--led-red); }
  50%      { opacity: 0.55; text-shadow: 0 0 10px var(--led-red); }
}

/* Banner two-line shape: a scannable headline and the consequence under it.
 *
 * NO COLOUR IS SET ON EITHER, deliberately. The banner rules already establish `color: var(--text-2)`
 * against three different backgrounds, and those pairings are the ones that were checked. Muting the
 * detail line with a new token — or with `opacity`, which is the same thing wearing a disguise —
 * would introduce a contrast ratio nobody has measured, on the line that explains an ALERT. The
 * hierarchy comes from weight and size only, which cost no contrast at all. */
.im-banner__label {
  display: block;
  font-weight: 600;
}
.im-banner__detail {
  display: block;
  margin-block-start: 3px;
  font-size: 11px;
  line-height: 1.45;
}
/* An empty detail Span must not draw a 3px gap under a one-line banner. */
.im-banner__detail:empty {
  display: none;
}

/* ============================================================================
   REDUCED MOTION — the pulse stops, the URGENCY does not
   ============================================================================
   Cross-AI round 2026-08-26, 3/3 UNANIMOUS (Codex + Gemini + Grok), option B of five put to them:
   stop every looping pulse under the preference AND replace the lost channel with a static one,
   rather than either ignoring the preference on "important" screens or freezing the LEDs flat.

   WHY NOT SIMPLY `animation: none` -- and this is the part that is easy to get backwards. The
   keyframes' 0%/100% frame is the REST state (`0 0 4px`); the 50% frame is the bright one. So
   stopping the animation parks every LED on its DIMMEST keyframe, and a naive reduced-motion guard
   makes alerts LESS visible than they were while animating. Each rule below therefore sets the
   static glow explicitly.

   WHY A SIZE LADDER. On the three map markers the pulse RATE is semantic -- the source comment
   says so: "Calmer = on time. More urgent = delayed. Most urgent = alert." Colour already carries
   the same three tiers, so nothing is lost outright, but on a Leaflet map a moving dot is what
   pulls the eye out of a field of static ones. Halo RADIUS replaces period as the pre-attentive
   channel: 4px / 7px / 11px reads as a hierarchy standing still.

   ON THE PRINCIPLE, since this is safety-relevant software and the temptation is real: all three
   landed on the same side, and it is the right one. A reduce-motion preference MAY remove the
   motion from an alert; it may NOT remove the alert. WCAG 2.2.2's exception is for ESSENTIAL
   information, not for important screens, and the information here survives in colour, in halo
   size, and -- on the banner -- in the sentence itself. The operator who set this flag set it
   because motion is the hazard; forcing 0.6s oscillation through an eight-hour shift can drive
   them off the console, which is the worse safety outcome. */
@media (prefers-reduced-motion: reduce) {
  /* Always-red rest LED: hold the BRIGHT frame, not the rest frame. */
  .im-status-led--red {
    animation: none;
    box-shadow: 0 0 11px var(--led-red);
  }
  /* The rate ladder becomes a radius ladder. Same three tiers, no motion. */
  .im-status-led--green-pulse {
    animation: none;
    box-shadow: 0 0 4px var(--led-green);
  }
  .im-status-led--amber-pulse {
    animation: none;
    box-shadow: 0 0 7px var(--led-amber);
  }
  .im-status-led--red-pulse {
    animation: none;
    box-shadow: 0 0 11px var(--led-red);
  }
  /* The banner glyph. text-shadow, not box-shadow, for the reason given at the glyph keyframes:
     this LED is a character, and a box glow around it comes out square. */
  .im-banner-alert::before {
    animation: none;
    opacity: 1;
    text-shadow: 0 0 10px var(--led-red);
  }
}

/* Informational banner — used for "live data pending" / soft-info notices.
 * Per Sketch 004-A: 3 px accent-2 spine on the inline-start edge + leading
 * ⓘ glyph (also accent-2). Not promoted to a full .im-card-bordered card
 * because that over-amplifies a passive notice (Variant B treatment was
 * rejected). */
.im-banner-info {
  padding-block: 10px;
  padding-inline: 14px;
  font-size: 11.5px;
  color: var(--text-2);
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border-soft, var(--border));
  border-inline-start: 3px solid var(--accent-2);
  border-radius: 3px;
}
.im-banner-info::before {
  content: "ⓘ ";
  color: var(--accent-2);
  margin-inline-end: var(--aura-space-xs, 4px);
}

/* Phase 364 SBVD-01 — emergency banner status modifiers. Used by
 * SchoolBusDispatchDashboardView.createEmergencyBanner(). Siblings of
 * .im-banner-info above; same structural shape (padding, spine, monospace
 * mini, ::before LED prefix) — only color tokens change.
 *   --warn  → amber LED + spine (soft emergency, weather delay)
 *   --alert → red LED + spine, pulsing ::before glyph (active lockdown) */
.im-banner-warn {
  padding-block: 10px;
  padding-inline: 14px;
  font-size: 11.5px;
  color: var(--text-2);
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border-soft, var(--border));
  border-inline-start: 3px solid var(--led-amber);
  border-radius: 3px;
}
.im-banner-warn::before {
  content: "● ";
  color: var(--led-amber);
  margin-inline-end: var(--aura-space-xs, 4px);
}

.im-banner-alert {
  padding-block: 10px;
  padding-inline: 14px;
  font-size: 11.5px;
  color: var(--text-2);
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border-soft, var(--border));
  border-inline-start: 3px solid var(--led-red);
  border-radius: 3px;
}
.im-banner-alert::before {
  content: "● ";
  color: var(--led-red);
  margin-inline-end: var(--aura-space-xs, 4px);
  /* GLYPH pulse, not the box one — see the keyframes. This ::before is text, so the box version
     painted a square halo around a round dot. */
  animation: im-status-led-pulse-glyph 0.6s ease-in-out infinite;
}

/* ─── UI-01 / D-01 — three deferred dashboard widget classes ───────────────
 * The Phase 402 styling micro-PR deferred these three legacy .im-dash-*
 * widget classes without a kit rule. They are styled in place here (legacy
 * .im-dash-* family, NOT new Core-Line view chrome — CoreLineKitTouchedFileGate
 * does not apply to a theme edit). The section primitive owns geometry/padding/
 * gap; these widgets compose INSIDE a section host and never re-declare it.
 * Palette tokens only — zero --lumo-*. */

/* .im-dash-why — FM dashboard "Why?" progressive-disclosure inline block
 * (sketch-findings .im-dash-why-nested): an indented hairline body that reveals
 * the reasoning behind a KPI/severity row WITHOUT a page jump. Indented from
 * the inline-start edge by a hairline rule; muted body ink; tight leading. */
.im-dash-why {
  display: block;
  margin-block-start: var(--aura-space-xs, 4px);
  padding-inline-start: var(--aura-space-m, 14px);
  border-inline-start: 1px solid var(--border-soft, var(--border));
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}
.im-dash-why__label {
  font-weight: 600;
  color: var(--text-2);
  margin-inline-end: var(--aura-space-xs, 4px);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 11px;
}

/* .im-dash-activation-badge* — activation / "🆕 vN" badge family. A compact
 * pill marking a freshly-activated capability or a new seed-pack version. The
 * base badge is neutral; the --new modifier carries the info accent (NEVER a
 * success/green tint — activation is informational, not a pass state). */
.im-dash-activation-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--aura-space-xs, 4px);
  padding-block: 2px;
  padding-inline: var(--aura-space-s, 8px);
  border-radius: 999px;
  font-family: var(--aura-font-family-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  background: var(--surface-2, var(--surface));
  color: var(--text-2);
  border: 1px solid var(--border-soft, var(--border));
}
.im-dash-activation-badge--new {
  background: var(--fm-info-bg, color-mix(in srgb, var(--accent-1) 12%, transparent));
  color: var(--accent-1);
  border-color: var(--accent-1);
}

/* .im-dash-tech-hero — technician hero load-gauge block (sketch 002
 * hero-gauge-below-trio): the headline workload surface on the technician
 * dashboard. A surface card whose hero gauge sits below a compact value trio.
 * Geometry stays inside the section host; this owns only the hero's own
 * vertical rhythm + the taller (10px) hero gauge variant. */
.im-dash-tech-hero {
  display: flex;
  flex-direction: column;
  gap: var(--aura-space-s, 8px);
  background: var(--surface, var(--bg));
  border: 1px solid var(--border);
  border-inline-start: 4px solid var(--accent-1);
  border-radius: 4px;
  padding-block: var(--aura-space-m, 14px);
  padding-inline: 16px;
}
.im-dash-tech-hero__value {
  font-family: var(--aura-font-family-mono);
  font-size: 28px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.im-dash-tech-hero__label {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-mute, var(--fm-text-muted, #5c6b78));
}
/* Hero load gauge — taller (10px) variant of .im-gauge-bar per sketch 002-D. */
.im-dash-tech-hero .im-gauge-bar {
  block-size: 10px;
}

/* ─── Phase 364 SBVD-02 (Plan 02) ──────────────────────────────────────────
 * V19-RECIPE-001 — per-row status spine (green=ok / amber=warn / red=alert).
 * Phase 364 SBVD-02 + sketch 004 winner A. Unblocked via Vaadin 25.1
 * Grid.setPartNameGenerator() + ::part() selector contract verified through
 * mcp__vaadin__get_component_styling("grid", "25.1"). Consumers:
 *   - CostAnalysisView.costGrid       → margin classification
 *   - CostAnalysisView.rideFleetGrid  → margin classification
 *   - DriverComplianceView.complianceGrid → license expiry classification */
vaadin-grid::part(im-row-status-ok)    { box-shadow: inset 4px 0 0 var(--led-green); }
vaadin-grid::part(im-row-status-warn)  { box-shadow: inset 4px 0 0 var(--led-amber); }
vaadin-grid::part(im-row-status-alert) { box-shadow: inset 4px 0 0 var(--led-red); }

/* Legacy alias — CostAnalysisView ships a Span with class
 * "cost-analysis-recomputing-badge" (set in CostAnalysisView.java line 164).
 * Rather than rename the production class (lumo-grep cares about token
 * usage, not class names) this alias dresses the existing class in V19
 * styling. The view's existing setVisible(false) toggle keeps working. */
.cost-analysis-recomputing-badge {
  display: inline-block;
  font-size: 10px;
  letter-spacing: 0.08em;
  padding-block: 2px;
  padding-inline: var(--aura-space-s, 8px);
  background: var(--bezel);
  border: 1px solid var(--accent-2);
  color: var(--accent-2);
  border-radius: 2px;
}

/* ─── S05 view-level grammar (Sketch 005-E) ─────────────────────────────
 * Per-card mini-map tracking strip + sized map host + status-aware bus
 * pulse rates. Designed for ParentFamilyDashboardView's per-child
 * cards but reusable wherever a Vaadin view embeds a <leaflet-map>
 * web component inline (future fleet/vehicle live-tracking views). */

/* Tracking strip — header above the mini-map. Carries operational
 * content (last-update timestamp, status warning) so it earns visual
 * weight rather than being decorative chrome. Status modifiers swap
 * text color; --warn / --alert carry through to the .__live LED dot. */
.im-leaflet-tracking-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--aura-space-xs, 4px);
  padding-inline: 10px;
  background: var(--bezel);
  border: 1px solid var(--border-soft, var(--border));
  border-block-end: none;
  border-start-start-radius: 3px;
  border-start-end-radius: 3px;
  font-family: var(--aura-font-family-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-1);
}
.im-leaflet-tracking-strip--warn  { color: var(--led-amber); }
.im-leaflet-tracking-strip--alert { color: var(--led-red); }

.im-leaflet-tracking-strip__live {
  color: var(--text-mute);
  font-size: 8px;
  letter-spacing: 0.08em;
}
.im-leaflet-tracking-strip__live::before {
  content: "● ";
  color: var(--led-green);
}
.im-leaflet-tracking-strip--warn .im-leaflet-tracking-strip__live { color: var(--led-amber); }
.im-leaflet-tracking-strip--warn .im-leaflet-tracking-strip__live::before { color: var(--led-amber); }
.im-leaflet-tracking-strip--alert .im-leaflet-tracking-strip__live { color: var(--led-red); }
.im-leaflet-tracking-strip--alert .im-leaflet-tracking-strip__live::before { color: var(--led-red); }

/* Mini-map host — 200 px tall, seamless border continuity into the
 * tracking strip above (no top border, only bottom corners rounded).
 * Applied to the <leaflet-map> web component's host element. */
.im-leaflet-mini {
  display: block;
  block-size: 200px;
  border: 1px solid var(--border-soft, var(--border));
  border-block-start: none;
  border-end-start-radius: 3px;
  border-end-end-radius: 3px;
  overflow: hidden;
}

/* ─── Phase 364 SBVD-03 (Plan 03) ──────────────────────────────────────────
 * ETA badge recipe — replaces inline getStyle() block previously rendered
 * at ParentFamilyDashboardView.java:279 (Sketch 005-E winner). Used inside
 * the per-child active-trip card to surface the live ETA in minutes.
 *
 * Composition:
 *   base       → neutral pill (bezel surface + soft border, mono text)
 *   --warn     → amber color + amber border (TripStatus.DELAYED)
 *   --alert    → red color + red border (TripStatus.ALERT)
 *
 * Palette tokens only (--aura-*, --vaadin-*, --led-*, --border-soft, --bezel).
 * No --lumo-*. Visual rhyme with .im-leaflet-tracking-strip__live LED. */
.im-eta-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--aura-space-xs, 4px);
  padding-block: 2px;
  padding-inline: var(--aura-space-s, 8px);
  border-radius: 999px;
  background: var(--bezel, var(--vaadin-background-color));
  border: 1px solid var(--border-soft, var(--vaadin-border-color));
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  font-size: 12px;
  color: var(--text, var(--vaadin-text-color));
  margin-block-end: var(--aura-space-m, 16px);
}
.im-eta-badge--warn  { color: var(--led-amber); border-color: var(--led-amber); }
.im-eta-badge--alert { color: var(--led-red);   border-color: var(--led-red);   }

/* Status-scaled pulse rates extending the S04 im-status-led-pulse
 * keyframes. Calmer = on time. More urgent = delayed. Most urgent
 * = alert. Applied to the bus-position marker via the leaflet
 * map's marker className. */
.im-status-led--green-pulse {
  background: var(--led-green);
  box-shadow: 0 0 6px var(--led-green);
  animation: im-status-led-pulse 1.6s ease-in-out infinite;
}
.im-status-led--amber-pulse {
  background: var(--led-amber);
  box-shadow: 0 0 6px var(--led-amber);
  animation: im-status-led-pulse 1.0s ease-in-out infinite;
}
.im-status-led--red-pulse {
  background: var(--led-red);
  box-shadow: 0 0 6px var(--led-red);
  animation: im-status-led-pulse 0.6s ease-in-out infinite;
}

/* Optional mono-amber palette reinforcement: when the warn (amber)
 * The mono-amber-palette-scoped card-spine reinforcement was DELETED in
 * Phase 437.1 — it only ever activated under the retired amber palette, and
 * Civic light is now the only skin (no palette tier to scope against). */

/* === Responsive collapse — under 1024 px === */
@media (max-width: 1023px) {
  .im-welcome-hero__content { grid-template-columns: 1fr; }
  .im-welcome-hero__text {
    border-inline-end: none;
    border-block-end: 1px solid var(--border);
    padding-block: 32px;
    padding-inline: 16px;
  }
  .im-welcome-hero__side {
    padding-block: 24px;
    padding-inline: 16px;
  }
  .im-welcome-hero__title { font-size: 36px; }
  .im-welcome-hero,
  .im-welcome-commitments-panel { margin: 16px; }
  .im-welcome-commitments-grid {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .im-welcome-utility,
  .im-welcome-statusband {
    padding-inline: 16px;
    flex-wrap: wrap;
    gap: 12px;
  }
  .im-welcome-cta-primary,
  .im-welcome-cta-secondary { inline-size: 100%; }
  .im-welcome-footer {
    padding-inline: 16px;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
  }
  .im-welcome-palette-bar { padding-inline: 8px; }
}

/* ─── Phase 360.3 — FulfillmentBoardView (CSS Grid rebuild) ───
 * Per 360.3-UI-SPEC.md §Top-level layout grid + §Card visual encoding +
 * §Mobile / responsive contract. Aura tokens only (no --lumo-*); logical
 * properties only (Phase 359 I18N-02 + I18N-03 gates). Status LEDs use the
 * palette-independent --led-* constants.
 */
.fulfillment-board {
  display: grid;
  grid-template-rows: auto auto auto auto 1fr;
  gap: var(--vaadin-gap-l);
  padding-block: var(--vaadin-padding-xl);
  padding-inline: var(--vaadin-padding-xl);
  block-size: 100dvh;
  background: var(--aura-background);
}

.fulfillment-board__statusband {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vaadin-gap-m);
  padding-block: var(--vaadin-padding-xs);
  padding-inline: var(--vaadin-padding-m);
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xs);
  color: var(--aura-text-color-secondary);
  background: var(--aura-surface-variant);
  border-radius: var(--vaadin-radius-s);
}

.fulfillment-board__lens-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--vaadin-gap-m);
  padding-block: var(--vaadin-padding-s);
  padding-inline: var(--vaadin-padding-m);
  background: var(--aura-surface);
  border-radius: var(--vaadin-radius-m);
}

.fulfillment-board__kpi-strip {
  display: grid;
  grid-template-columns: repeat(3, minmax(180px, 1fr));
  gap: var(--vaadin-gap-xl);
  padding-block: var(--vaadin-padding-l);
  padding-inline: var(--vaadin-padding-l);
}

.fulfillment-board__kpi-tile {
  display: grid;
  grid-template-rows: auto auto;
  gap: var(--vaadin-gap-xs);
  padding-block: var(--vaadin-padding-l);
  padding-inline: var(--vaadin-padding-l);
  background: var(--aura-surface);
  border-radius: var(--vaadin-radius-m);
}

.fulfillment-board__kpi-tile-label {
  font-size: var(--aura-font-size-xs);
  color: var(--aura-text-color-secondary);
}

.fulfillment-board__kpi-tile-value {
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xl);
  font-weight: 600;
  line-height: var(--aura-line-height-xl);
}

.fulfillment-board__kpi-tile[data-threshold="green"] .fulfillment-board__kpi-tile-value { color: var(--led-green); }
.fulfillment-board__kpi-tile[data-threshold="amber"] .fulfillment-board__kpi-tile-value { color: var(--led-amber); }
.fulfillment-board__kpi-tile[data-threshold="red"]   .fulfillment-board__kpi-tile-value { color: var(--led-red); }

.fulfillment-board__filter-row {
  display: grid;
  grid-template-columns: 1fr repeat(5, auto);
  gap: var(--vaadin-gap-m);
  align-items: center;
}

.fulfillment-board__lanes {
  display: grid;
  grid-template-columns: repeat(var(--lane-count, 4), minmax(280px, 1fr));
  gap: var(--vaadin-gap-xl);
  overflow-block: auto;
  overflow-inline: auto;
}

.fulfillment-board__lane {
  display: grid;
  grid-template-rows: auto 1fr;
  background: var(--aura-surface-variant);
  border-radius: var(--vaadin-radius-l);
  padding-block: var(--vaadin-padding-l);
  padding-inline: var(--vaadin-padding-l);
}

.fulfillment-board__lane-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-block-end: var(--vaadin-gap-m);
}

.fulfillment-board__lane-title {
  font-size: var(--aura-font-size-xl);
  font-weight: 600;
  line-height: var(--aura-line-height-xl);
}

.fulfillment-board__lane-led {
  display: inline-block;
  inline-size: 8px;
  block-size: 8px;
  border-radius: 50%;
  margin-inline-end: var(--vaadin-gap-xs);
}

.fulfillment-board__lane[data-lane="open"]       .fulfillment-board__lane-led { background: var(--led-red); }
.fulfillment-board__lane[data-lane="assigned"]   .fulfillment-board__lane-led,
.fulfillment-board__lane[data-lane="inprogress"] .fulfillment-board__lane-led { background: var(--led-amber); }
.fulfillment-board__lane[data-lane="done"]       .fulfillment-board__lane-led { background: var(--led-green); }

.fulfillment-board__lane-count {
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xs);
  color: var(--aura-text-color-secondary);
}

.fulfillment-board__lane-cards {
  display: grid;
  gap: var(--vaadin-gap-m);
  align-content: start;
  overflow-block: auto;
}

.fulfillment-board__card {
  display: grid;
  grid-template-rows: auto auto auto auto;
  gap: var(--vaadin-gap-xs);
  min-block-size: 96px;
  padding-block: var(--vaadin-padding-m);
  padding-inline: var(--vaadin-padding-m);
  background: var(--aura-surface);
  border-radius: var(--vaadin-radius-m);
  cursor: grab;
  transition: transform 200ms ease, box-shadow 200ms ease;
}

.fulfillment-board__card:hover  { box-shadow: 0 0 0 1px var(--accent-2); }
.fulfillment-board__card:focus-visible {
  outline: 2px solid var(--aura-focus-ring-color);
  outline-offset: 2px;
}

.fulfillment-board__card--dense {
  min-block-size: 56px;
  padding-block: var(--vaadin-padding-s);
  padding-inline: var(--vaadin-padding-s);
  gap: var(--vaadin-gap-xs);
}

.fulfillment-board__card--demo {
  border-inline-start: 2px solid var(--led-amber);
}

.fulfillment-board__card--selected {
  border-inline-start: 2px solid var(--accent-1);
  background: color-mix(in srgb, var(--accent-1) 6%, var(--aura-surface));
}

.fulfillment-board__card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--vaadin-gap-xs);
}

.fulfillment-board__card-id {
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xs);
  color: var(--aura-text-color-secondary);
}

.fulfillment-board__card-demo-badge {
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xs);
  padding-block: var(--vaadin-padding-xs);
  padding-inline: var(--vaadin-padding-s);
  background: color-mix(in srgb, var(--led-amber) 12%, transparent);
  color: var(--led-amber);
  border-radius: var(--vaadin-radius-s);
}

.fulfillment-board__card-title {
  font-size: var(--aura-font-size-m);
  line-height: var(--aura-line-height-m);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: block-axis;
  overflow: hidden;
}

.fulfillment-board__card--dense .fulfillment-board__card-title {
  -webkit-line-clamp: 1;
}

.fulfillment-board__card-address {
  font-size: var(--aura-font-size-xs);
  color: var(--aura-text-color-secondary);
}

.fulfillment-board__card--dense .fulfillment-board__card-address { display: none; }

.fulfillment-board__card-status-strip {
  display: flex;
  align-items: center;
  gap: var(--vaadin-gap-m);
  font-family: var(--aura-font-family-mono);
  font-size: var(--aura-font-size-xs);
  padding-block-start: var(--vaadin-padding-xs);
  border-block-start: 1px solid var(--aura-divider-color);
}

.fulfillment-board__card-led {
  inline-size: 6px;
  block-size: 6px;
  border-radius: 50%;
  display: inline-block;
}

.fulfillment-board__card[data-sla="breach"]   .fulfillment-board__card-led { background: var(--led-red); }
.fulfillment-board__card[data-sla="due-soon"] .fulfillment-board__card-led { background: var(--led-amber); }
.fulfillment-board__card[data-sla="ok"]       .fulfillment-board__card-led { background: var(--led-green); }

.fulfillment-board__bulk-toolbar {
  display: flex;
  align-items: center;
  gap: var(--vaadin-gap-m);
  padding-block: var(--vaadin-padding-s);
  padding-inline: var(--vaadin-padding-m);
  background: var(--aura-surface);
  border-radius: var(--vaadin-radius-m);
}

/* Drag-drop affordances per UI-SPEC §Drag-drop semantics */
.fulfillment-board__lane[data-drop-active] {
  border-inline-start: 2px dashed var(--accent-2);
}

.fulfillment-board__lane[data-drop-invalid] {
  border-inline-start: 2px solid var(--led-red);
}

/* Push-status indicator pulse on completion (per UI-SPEC §@Push contract). */
@keyframes fulfillment-board-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--led-green) 60%, transparent); }
  100% { box-shadow: 0 0 0 12px transparent; }
}

.fulfillment-board__card[data-just-completed] {
  animation: fulfillment-board-pulse 600ms ease-out;
}

/* Tablet — single-pane horizontal scroll-snap */
@media (max-width: 1023px) {
  .fulfillment-board__lanes {
    grid-template-columns: repeat(var(--lane-count, 4), minmax(min(85vw, 320px), 1fr));
    scroll-snap-type: inline mandatory;
  }
  .fulfillment-board__lane { scroll-snap-align: start; }
  .fulfillment-board__kpi-strip {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Mobile — dense override + stacked KPI tiles */
@media (max-width: 767px) {
  .fulfillment-board {
    padding-block: var(--vaadin-padding-l);
    padding-inline: var(--vaadin-padding-m);
  }
  .fulfillment-board__lens-toolbar {
    flex-direction: column;
    align-items: stretch;
  }
  .fulfillment-board__kpi-strip {
    grid-template-columns: 1fr;
  }
  .fulfillment-board__filter-row {
    grid-template-columns: 1fr;
  }
  .fulfillment-board__card {
    min-block-size: 56px;
    padding-block: var(--vaadin-padding-s);
    padding-inline: var(--vaadin-padding-s);
    gap: var(--vaadin-gap-xs);
  }
  .fulfillment-board__card-title { -webkit-line-clamp: 1; }
  .fulfillment-board__card-address { display: none; }
}

/* Accessibility — honor prefers-reduced-motion (UI-SPEC §Animation discipline). */
@media (prefers-reduced-motion: reduce) {
  .fulfillment-board__card {
    transition: none;
  }
  .fulfillment-board__card[data-just-completed] {
    animation: none;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   S09 — Suggested Price Panel badges (demand-tier chips)
   Uses Aura semantic color variables only — no --lumo-* tokens.
   ───────────────────────────────────────────────────────────────────────── */
.sp-badge-peak,
.sp-badge-standard,
.sp-badge-off-peak,
.sp-badge-surge {
  display: inline-block;
  padding-block: var(--vaadin-padding-xs, 2px);
  padding-inline: var(--vaadin-padding-s, 6px);
  border-radius: var(--vaadin-radius-s, 4px);
  font-size: var(--vaadin-font-size-s, 0.75rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.sp-badge-peak {
  background-color: var(--aura-warning-10, color-mix(in srgb, var(--aura-orange) 10%, transparent));
  color: var(--aura-orange-text, #B45309);
  border: 1px solid var(--aura-orange, #D97706);
}

.sp-badge-standard {
  background-color: var(--aura-contrast-5, color-mix(in srgb, var(--aura-contrast) 5%, transparent));
  color: var(--aura-body-text-color, #0F172A);
  border: 1px solid var(--aura-color-outline, #E2E8F0);
}

.sp-badge-off-peak {
  background-color: var(--aura-success-10, color-mix(in srgb, var(--aura-green) 10%, transparent));
  color: var(--aura-green-text, #15803D);
  border: 1px solid var(--aura-green, #16A34A);
}

.sp-badge-surge {
  background-color: var(--aura-error-10, color-mix(in srgb, var(--aura-red) 10%, transparent));
  color: var(--aura-red-text, #B91C1C);
  border: 1px solid var(--aura-error, #DC2626);
}

/* Redacted price state — shown when D006 REDACTED mode zeroes all price fields */
.sp-price-redacted {
  color: var(--aura-color-text-secondary, #475569);
  font-style: italic;
}

/* Layout helpers for the panel itself */
.suggested-price-panel {
  gap: var(--vaadin-gap-s, 6px);
}

.sp-label {
  font-size: var(--vaadin-font-size-s, 0.75rem);
  color: var(--aura-color-text-secondary, #475569);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.sp-price {
  font-size: var(--vaadin-font-size-xl, 1.25rem);
  font-weight: 700;
  color: var(--aura-body-text-color, #0F172A);
}

.sp-audit-link {
  font-size: var(--vaadin-font-size-s, 0.75rem);
  color: var(--aura-blue-text, #1D4ED8);
}

.sp-no-quote {
  color: var(--aura-color-text-secondary, #475569);
  font-style: italic;
}

/* ============================================================
 * Auth surface button overrides — V19 amber for LoginForm submit
 * + outline cyan for passkey button. These rules live here
 * (not in component-recipes/) because aura-theme.css is reliably
 * injected into vaadin-button host styling, while recipe files
 * are bundled but don't always reach all component shadow DOMs.
 * Auth-flow #9 follow-up. */
.im-auth-page {
  --aura-primary-color: var(--accent-1);
  --aura-primary-text-color: var(--bg);
  --lumo-primary-color: var(--accent-1); /* lumo-gate-allow: Lumo->Aura bridge for vaadin-login-form host internals (auth surface); mirrors --aura-primary-color above. Phase 359.2. */
  --lumo-primary-text-color: var(--bg); /* lumo-gate-allow: Lumo->Aura bridge; mirrors --aura-primary-text-color above. Phase 359.2. */
}
.im-auth-page vaadin-login-form vaadin-button,
.im-auth-page vaadin-login-form vaadin-button[theme~="primary"] {
  background: var(--accent-1) !important;
  color: var(--bg) !important;
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border-radius: 2px;
  border: none;
}
.im-auth-page .im-auth-passkey vaadin-button,
.im-auth-page .im-auth-passkey vaadin-button[theme~="primary"] {
  background: transparent !important;
  color: var(--accent-2) !important;
  border: 1px solid var(--accent-2) !important;
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  border-radius: 2px;
}

/* ============================================================
 * Auth surfaces — Login + Signup (Phase 359 Plan 12 chrome
 * brought up to spec; classes were referenced by Java but the
 * matching CSS was never shipped — see auth-flow-v19-fixup
 * sketch set 2026-05-09).
 *
 * Renders the equipment-style "centered card with mounting
 * screws + panel-label tab" intent of LoginView/SignupView.
 * Reuses the .im-welcome-screw, .im-welcome-led, and palette
 * tokens already defined above.
 * ============================================================ */
.im-auth-page {
  background: var(--bg);
  color: var(--text);
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  min-block-size: 100vh;
}

/* Stage that holds the centered panel — radial-glow ambience.
 * align-items: center vertically centers the panel in whatever space the
 * VerticalLayout gives this wrap (which is `flex: 1` so it takes all the
 * vertical space between the status band and the footer). Without this,
 * the panel anchors to the top of the wrap and leaves a large empty
 * valley between the panel and the footer at any viewport ≥ ~900px. */
.im-auth-panel-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  /* Explicit inline-size so flex centering works regardless of parent's
   * cross-axis stretching behavior. Without this, Vaadin VerticalLayout's
   * default child sizing leaves the panel-wrap shrunk to content width
   * and the panel inside ends up left-aligned within a narrow stage. */
  inline-size: 100%;
  box-sizing: border-box;
  /* Compacted block padding so the form fits on shorter laptop screens
   * (≤900px viewport). Was 56px top+bottom; clamp scales between 16px on
   * tight screens and 40px on tall viewports without forcing scroll. */
  padding-block: clamp(16px, 4vh, 40px);
  padding-inline: 24px;
  background:
    radial-gradient(circle at 20% 30%, var(--accent-1-glow) 0%, transparent 38%),
    radial-gradient(circle at 80% 70%, var(--accent-2-glow) 0%, transparent 38%);
}

/* The equipment-style card itself */
.im-auth-panel {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  /* Tightened inner padding so the form fits on shorter screens without
   * losing the equipment-panel breathing room on tall ones. Was 56px top
   * (to leave room for the floating label tab) / 28px bottom. */
  padding-block: 32px 22px;
  padding-inline: 36px;
  inline-size: 100%;
  /* Widened from 480 → 760 to accommodate the 2-column split (email + passkey)
   * introduced by the login layout fix. Single-column collapse on ≤640px
   * viewports brings the effective width back down to fit the form. */
  max-inline-size: 760px;
  box-shadow:
    0 0 0 1px var(--border-soft),
    0 20px 60px rgba(0, 0, 0, 0.4);
}

/* Panel-label tab that floats above the top edge */
.im-auth-panel__label {
  position: absolute;
  inset-block-start: -12px;
  inset-inline-start: 32px;
  background: var(--bg);
  color: var(--accent-1);
  padding-inline: 12px;
  padding-block: 2px;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--border);
  border-radius: 2px;
}

/* Inner content card */
.im-auth-card {
  display: flex;
  flex-direction: column;
  /* Tightened row gap to make the auth stack fit on shorter screens. */
  gap: 10px;
}

/* Two-column split inside the auth card: email/password on the left,
   passkey + signup on the right, separated by a vertical "o" divider.
   Cuts the card's vertical footprint roughly in half so the login fits
   on shorter laptop screens without scrolling.
   Collapses to a stacked single-column on narrow viewports. */
.im-auth-card__split {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 12px;
  align-items: stretch;
}
.im-auth-card__split-col {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-inline-size: 0;
}
.im-auth-card__split-col--passkey {
  justify-content: center;
  /* Mirror the inline padding that vaadin-login-form-wrapper's shadow host
     puts on the left column. The wrapper has 24px inline padding *and* its
     fixed 360px inline-size leaves ~4px slack at the host's right edge —
     together that's ~28px between the form's visible right edge and the
     divider. Without this inset the passkey column hugged the divider and
     the OR chip felt closer to passkey than to the form even though the
     grid tracks are mathematically symmetric. */
  padding-inline-start: 28px;
}
.im-auth-card__split-divider {
  position: relative;
  inline-size: 1px;
  background: var(--border);
  margin-inline: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.im-auth-card__split-or {
  position: absolute;
  background: var(--surface);
  color: var(--text-muted, var(--vaadin-secondary-text-color));
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding-block: 4px;
  padding-inline: 8px;
}
/* Tablet / phone: collapse to a stacked column with a horizontal rule */
@media (max-inline-size: 640px) {
  .im-auth-card__split {
    grid-template-columns: 1fr;
  }
  .im-auth-card__split-divider {
    inline-size: 100%;
    block-size: 1px;
    background: var(--border);
    margin-block: 8px;
  }
  .im-auth-card__split-or {
    background: var(--surface);
  }
}

/* Phase 390.6 — SSO sign-in buttons (Google, Apple). Styled as outlined
   secondary buttons (vs the primary passkey button) so the single primary
   action (passkey) keeps visual dominance while still offering SSO. */
.im-auth-sso-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  inline-size: 100%;
  padding-block: 10px;
  padding-inline: 14px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: transparent;
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  transition: background-color 120ms, border-color 120ms;
}
.im-auth-sso-button:hover {
  background: var(--surface-hover, color-mix(in srgb, var(--text) 6%, transparent));
  border-color: var(--accent-1);
}
.im-auth-sso-button--apple {
  background: #000;
  color: #fff;
  border-color: #000;
}
.im-auth-sso-button--apple:hover {
  background: #1d1d1f;
}
/* Phase 390.6.3 follow-up: inline-SVG provider logos. The svg is wrapped
   in __icon so we can size + align it independently of the label. */
.im-auth-sso-button__icon {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  line-height: 0;
}
.im-auth-sso-button__icon svg {
  display: block;
}
.im-auth-sso-button__label {
  flex: 1;
  text-align: center;
}
/* Apple logo is monochrome — render it white on the black Apple button. */
.im-auth-sso-button--apple .im-auth-sso-button__icon svg {
  fill: #fff;
}

/* Heading: single render, no -webkit-text-fill-color trick.
 * Accent line below the heading (1px x 36px x --accent-1) substitutes
 * for the gradient effect that previously caused the "ghost text"
 * double-render when combined with the Vaadin LoginForm internal H2. */
.im-auth-card__heading {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.005em;
  margin: 0;
  color: var(--text);
  position: relative;
  padding-block-end: 14px;
}
.im-auth-card__heading::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  inline-size: 36px;
  block-size: 2px;
  background: var(--accent-1);
  box-shadow: 0 0 8px var(--accent-1-glow);
}

/* Field styling — applied to text/password/email inputs and the
 * Vaadin LoginForm's inner inputs via the .im-auth-loginform host */
.im-auth-field {
  inline-size: 100%;
}
.im-auth-field::part(label),
.im-auth-loginform::part(label) {
  font-size: 10px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-2);
  font-weight: 500;
}
.im-auth-field::part(input-field),
.im-auth-loginform::part(input-field) {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 2px;
  color: var(--text);
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  min-block-size: 38px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.im-auth-field::part(input-field):focus-within,
.im-auth-loginform::part(input-field):focus-within {
  border-color: var(--accent-2);
  box-shadow: 0 0 0 2px var(--accent-2-glow);
}

/* Submit button — Vaadin LoginForm theming via host part. */
.im-auth-submit,
.im-auth-loginform::part(vaadin-button) {
  background: var(--accent-1);
  color: var(--bg);
  border: none;
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 12px;
  border-radius: 2px;
  min-block-size: 44px;
  padding-inline: 16px;
  cursor: pointer;
  transition: box-shadow 0.15s ease;
}
.im-auth-submit:hover,
.im-auth-loginform::part(vaadin-button):hover {
  box-shadow: 0 0 16px var(--accent-1-glow);
}

/* Forgot password link — secondary, accent-2 underline */
.im-auth-loginform::part(forgot-password-button) {
  color: var(--accent-2);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 11px;
  text-decoration: none;
  margin-block-start: 8px;
}
.im-auth-loginform::part(forgot-password-button):hover {
  text-decoration: underline;
}

/* Passkey block divider + button */
.im-auth-passkey {
  margin-block-start: 18px;
  padding-block-start: 14px;
  border-block-start: 1px solid var(--border-soft);
}
.im-auth-passkey vaadin-button[theme~="primary"]::part(button) {
  background: transparent;
  color: var(--accent-2);
  border: 1px solid var(--accent-2);
  border-radius: 2px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 11px;
  font-family: var(--aura-font-family-mono, ui-monospace, monospace);
}

/* Alt link: "Need an account? Sign up" / "Already have an account?" */
.im-auth-alt-link {
  display: block;
  text-align: center;
  margin-block-start: 14px;
  color: var(--accent-2);
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
}
.im-auth-alt-link:hover {
  text-decoration: underline;
}

/* Login: Vaadin LoginForm host — strip its default chrome so the
 * panel-wrap styling above takes over visually. */
.im-auth-loginform {
  background: transparent !important;
  box-shadow: none !important;
  padding: 0 !important;
}
.im-auth-loginform::part(vaadin-login-form-wrapper) {
  background: transparent;
  padding: 0;
  box-shadow: none;
}
/* Vaadin LoginForm renders an internal <h2> from i18n.form.title.
 * LoginView.java now sets title to empty string to avoid double-rendering
 * with the outer H1; this rule additionally hides any residual H2 to
 * defend against future Vaadin version changes that may render an empty
 * heading element. */
.im-auth-loginform::part(form-title) {
  display: none;
}

/* Reduced-motion: drop the radial-glow background animation hint
 * on .im-auth-panel-wrap (currently static, but reserve for future). */
@media (prefers-reduced-motion: reduce) {
  .im-auth-card__heading::after { transition: none; }
}

/* ─────────────────────────────────────────────────────────────────────────
 * Phase 393 — Vehicle color tokens (UI-SPEC §Color Q2)
 * ─────────────────────────────────────────────────────────────────────────
 * Six dedicated tokens for ExcursionVehicleAssignment.visual_color enum.
 * DISTINCT from severity tokens (--aura-severity-critical) to avoid the
 * "red bus ≡ alert bus" semantic collision. Per UI-SPEC Double-signal rule
 * (WCAG 2.2 AA), every consumer of .im-vehicle-chip MUST also render the
 * handle text + emoji adjacent to the chip; the chip is decorative-only.
 * The chip itself carries role="presentation" via the Vaadin component.
 */
/* Phase 437.1 (D-05): the per-palette vehicle-chip color overrides were
 * DELETED — they only differentiated across the retired V19 dark palettes.
 * Civic light is the only skin, so the single :root baseline below applies
 * everywhere. (Vehicle chips are decorative fills paired with adjacent text
 * labels per the UI-SPEC double-signal rule.) */
:root {
  --vehicle-color-blue:   #4a9eff;
  --vehicle-color-red:    #c73e3e; /* distinct hue from --aura-severity-critical #d23f3f */
  --vehicle-color-green:  #3fa86b;
  --vehicle-color-yellow: #e8c547;
  --vehicle-color-orange: #e88c3a;
  --vehicle-color-purple: #9b6dd7;
}

/* Phase 393 — .im-vehicle-chip structural primitive.
 * 16x16px solid fill + 2px surface-toned border. Always paired with
 * adjacent handle text + color name (UI-SPEC Double-signal rule).
 * Chip is decorative; semantics live on the surrounding text. */
.im-vehicle-chip {
  inline-size: 16px;
  block-size: 16px;
  border: 2px solid var(--aura-surface-variant, #2D2924);
  border-radius: 50%;
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;
}

.im-vehicle-chip--blue   { background: var(--vehicle-color-blue); }
.im-vehicle-chip--red    { background: var(--vehicle-color-red); }
.im-vehicle-chip--green  { background: var(--vehicle-color-green); }
.im-vehicle-chip--yellow { background: var(--vehicle-color-yellow); }
.im-vehicle-chip--orange { background: var(--vehicle-color-orange); }
.im-vehicle-chip--purple { background: var(--vehicle-color-purple); }

/* Phase 393 — .im-page-aside--sticky structural primitive (UI-SPEC Surface 1).
 * 360px sticky aside on the right of FormPageTemplate.setAside split. */
.im-page-aside--sticky {
  position: sticky;
  inset-block-start: var(--aura-space-l, 24px);
  inline-size: 360px;
  flex-shrink: 0;
  align-self: flex-start;
}

/* Phase 393 — print stylesheet for ExcursionManifestView (UI-SPEC §Print).
 * Hides chrome + scales QR to 256x256 + full-bleed manifest table. The view
 * sets data-manifest-print="true" on its host so the print media query is
 * scoped to manifest routes only (other pages remain non-printable). */
@media print {
  [data-manifest-print="true"] .im-page-header,
  [data-manifest-print="true"] .im-page-breadcrumb,
  [data-manifest-print="true"] .im-page-actions {
    display: none !important;
  }
  [data-manifest-print="true"] .im-manifest-qr {
    inline-size: 256px !important;
    block-size: 256px !important;
  }
  [data-manifest-print="true"] .im-manifest-table {
    inline-size: 100% !important;
    border-collapse: collapse;
  }
  [data-manifest-print="true"] .im-manifest-table tr {
    page-break-inside: avoid;
  }
  /* B&W printer fallback — chips render as patterned discs so color names
   * remain identifiable on monochrome prints. The handle text + color name
   * remain visible adjacent to the chip per Double-signal rule. */
  [data-manifest-print="true"] .im-vehicle-chip {
    border: 1px solid black;
    background-image: repeating-linear-gradient(45deg, transparent 0 2px, currentColor 2px 4px);
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Phase 390.5 — Font Picker Coverage
   ─────────────────────────────────────────────────────────────────────────
   fonts.css activates `--aura-font-family` per `:root[data-font="<slug>"]`,
   but Vaadin components (vaadin-app-layout, vaadin-side-nav, vaadin-button,
   etc.) read `--lumo-font-family` from their internal Lumo defaults — they
   do not see `--aura-font-family`. Mirror the active value into the Lumo
   token so the font swap propagates through every Vaadin surface
   (sidebar + main content + dialogs + chrome) instead of stopping at
   plain HTML elements.

   Also explicitly inherit on body so non-Vaadin DOM (legacy v19 chrome,
   custom Divs/Spans) picks up the swap without re-declaring the family.
   ───────────────────────────────────────────────────────────────────── */
:root[data-font] {
  --lumo-font-family: var(--aura-font-family); /* lumo-gate-allow: load-bearing — Vaadin built-in components read this token internally for the Phase 390.5 font picker; no --aura-* equivalent exists. Phase 359.2. */
}
:root[data-font] body,
:root[data-font] vaadin-app-layout,
:root[data-font] vaadin-side-nav,
:root[data-font] vaadin-menu-bar,
:root[data-font] vaadin-vertical-layout,
:root[data-font] vaadin-horizontal-layout {
  font-family: var(--aura-font-family);
}

/* ─────────────────────────────────────────────────────────────────────────
   Grid / data monospace coverage — the GRID font picker (data-grid-font).
   ─────────────────────────────────────────────────────────────────────────
   page-kit.css now flows the GENERAL body font through .im-page, so every grid
   must be explicitly re-pinned to the monospace grid-font token (--dash-mono),
   which :root[data-grid-font="<slug>"] in fonts.css swaps (default = JetBrains
   Mono). Fixed-width glyphs keep numeric columns aligned — the whole point of a
   data grid. Covers the host, the shadow-DOM header/body cell parts, AND the
   slotted light-DOM cell content (where the actual text renders).
   ───────────────────────────────────────────────────────────────────────── */
vaadin-grid,
vaadin-grid::part(header-cell),
vaadin-grid::part(body-cell),
vaadin-grid vaadin-grid-cell-content {
  font-family: var(--dash-mono, 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace);
}


/* ─────────────────────────────────────────────────────────────────────────
   Signature-dialog facts grid + the denied row action (Phase 595 plan 03).
   ─────────────────────────────────────────────────────────────────────────
   Sketch 078-D draws the acknowledge modal's facts as a GRID — small uppercase
   label ABOVE its value, hairline-separated — and the row's denied action as a
   button with its reason beside it. The markup existed with class names and no
   rules, so the label and value rendered as one run of text
   ("Excluido del despachoACTIVO · 5609…7d", observed live 2026-08-01). A class
   with no rule is worse than no class: it reads as structured in the Java and
   is invisible on screen.

   These live here rather than in coreline-kit.css because that file is phase
   572's; they use the kit's own --fm-* tokens so a later migration into the kit
   is a cut-and-paste. Logical properties only (stylelint plugin/use-logical-
   properties-and-values is an error-severity gate).
   ───────────────────────────────────────────────────────────────────────── */
.coreline-facts {
  display: flex;
  flex-direction: column;
  border-block-start: 1px solid var(--fm-gridline);
}

.coreline-fact {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-block: 8px;
  border-block-end: 1px solid var(--fm-gridline);
}

.coreline-fact__label {
  font-family: var(--fm-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fm-text-muted);
}

.coreline-fact__value {
  font-size: var(--fm-fs);
  color: var(--fm-text);
  overflow-wrap: anywhere;
}

/* The consequence is a paragraph, not a container: it carries its own breathing
   room instead of borrowing a flow container's gap. */
.coreline-consequence {
  display: block;
  font-size: var(--fm-fs);
  line-height: 1.5;
  color: var(--fm-text);
}

/* The denied row action: the button, then the reason, stacked so the sentence
   is legible in a grid cell rather than clipped onto one line. */
.coreline-denial {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
}

.coreline-denial__reason {
  display: inline-flex;
  align-items: flex-start;
  gap: 4px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--fm-text-muted);
  overflow-wrap: anywhere;
}

.coreline-denial__reason svg {
  inline-size: 12px;
  block-size: 12px;
  flex: none;
  margin-block-start: 1px;
  fill: currentcolor;
}

/* ─────────────────────────────────────────────────────────────────────────
   The disabled header affordance's inline reason (Phase 595 plan 05).
   ─────────────────────────────────────────────────────────────────────────
   The incident list's "Report hazard" button is disabled: it used to open a
   confirm whose callback persisted nothing. A disabled control has to say WHY,
   and it has to say it without a hover — a tooltip alone is unreachable on a
   touch device.

   Block-level with its own inline-start-agnostic spacing because
   `.coreline-page__heading` has NO rule anywhere in this theme (14 view sites
   emit it; tracked as deferred debt, not fixed here). This note therefore
   cannot borrow a container gap and has to carry its own, or it renders on the
   same line as the button it explains.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-page__heading-note {
  display: block;
  margin-block-start: 4px;
  max-inline-size: 78ch;
  font-size: 12px;
  line-height: 1.45;
  color: var(--fm-text-muted);
}

/* ─────────────────────────────────────────────────────────────────────────
   The incident list's Estado cell (Phase 595 follow-on, WINDOWS id 8).
   ─────────────────────────────────────────────────────────────────────────
   The cell clipped mid-word in BOTH states ("Enviado · pendient|",
   "Acusado por Juan Pé|", observed live 2026-08-01) because the column took a
   leftover flex share while carrying a sentence. The Java side now measures the
   column (setAutoWidth + setFlexGrow(0)); this side owns the two things CSS has
   to guarantee for that measurement to be safe:

     1. a MIN width, so the measured width can never fall under a readable
        floor for the short label, and
     2. a WRAP, so a custodian name longer than any row auto-width happened to
        measure degrades to a second visual line instead of being cut. That is
        load-bearing: `vaadin-grid::part(cell)` sets min-block-size, not a fixed
        height, so a row grows to fit.

   The custodian's name is IN the cell and not in a tooltip. That is a cross-AI
   3/3 refusal, not a preference (595-CROSSAI-ESTADO-COLUMN.md): a tooltip is
   unreachable on touch, awkward by keyboard and absent from an export, and this
   cell's payload is a named, irreversible act.

   Lives here rather than in coreline-kit.css because that file is phase 572's;
   built on the kit's own --fm-* tokens so a later migration into the kit is a
   cut-and-paste. Logical properties only (stylelint
   plugin/use-logical-properties-and-values is error-severity).
   ───────────────────────────────────────────────────────────────────────── */
.coreline-estado {
  display: flex;
  flex-direction: column;
  gap: 3px;
  align-items: flex-start;
  min-inline-size: 11ch;
}

/* The chip inherits .coreline-chip from the kit (fixed 22px block-size,
   nowrap). Only the ONE-WORD label goes in it, so nowrap is correct here — the
   part that can be long is the custodian line below, which wraps. */
.coreline-estado .coreline-chip {
  max-inline-size: 100%;
}

/* The glyph is decorative (aria-hidden in Java); the word beside it carries the
   meaning. Sized through the icon's own documented custom property because the
   <svg> lives in the <vaadin-icon> shadow root — a descendant `svg` selector
   never reaches it. `fill` already resolves to currentColor from the icon's own
   base styles, so the chip's tier colour tints it with no extra rule. */
.coreline-estado__glyph {
  --vaadin-icon-size: 12px;
}

/* The acknowledging custodian — quieter than the chip, never louder, and always
   present rather than hover-only.

   `white-space: normal` is declared EXPLICITLY (Phase 596 plan 03). This rule
   shipped in 595 carrying `overflow-wrap: anywhere` alone, which does nothing
   while `white-space` is `nowrap` — and `vaadin-grid-cell-content` sets exactly
   that as its base styling. So the wrap this rule believed it had was inert, and
   a custodian name longer than any auto-width-measured row would have been CUT.
   That name is a forensic field on the row, and SC-9 says no forensic field on
   the row is lost to column geometry. Same latent defect, same one-line fix, as
   the identity facts line below. */
.coreline-estado__custodian {
  display: block;
  max-inline-size: 24ch;
  font-size: 11px;
  line-height: 1.3;
  color: var(--fm-text-muted);
  white-space: normal;
  overflow-wrap: anywhere;
}

/* ─────────────────────────────────────────────────────────────────────────
   Phase 596 plan 03 — the incident's operator-facing identity (SC-7 / SC-9).

   The `coreline-identity` class (selector below) is the frozen first column's
   whole content: the minted
   INC-YYYYMM-NNNNN, or the visibly distinct legacy form for a row that predates
   numbering. Monospaced with tabular figures so a column of numbers reads as a
   column — the digits line up and a mistyped one is visible.

   The `coreline-identity__facts` class is what is LEFT on the finding sub-line
   once the identity moved out of it: the place, and how long the incident has
   been blocking. That line is why this block exists. It carried
   `.coreline-mono`, which sets font, numeric variant and letter-spacing and no
   `white-space` at all — so it inherited `vaadin-grid-cell-content`'s
   nowrap-with-ellipsis and the relative age was cut at the column edge
   (observed live 2026-08-02). Both rules therefore declare `white-space: normal`
   EXPLICITLY and pair it with `overflow-wrap`; the pairing is the whole fix,
   because `overflow-wrap` is inert while `white-space` is `nowrap`.

   Lives here rather than in coreline-kit.css because that file is phase 572's;
   built on the kit's own --fm-* tokens so a later migration into the kit is a
   cut-and-paste. Logical properties only (stylelint
   plugin/use-logical-properties-and-values is error-severity).
   ───────────────────────────────────────────────────────────────────────── */
/* The case number, now the FIRST line of the identity cell rather than a column
   of its own (2026-08-04): the two columns cost ~11rem of width between them and
   pushed the action column off the viewport.

   Lighter and smaller than the finding beneath it, and that ordering is the
   point. As a standalone column it was the cell, so weight 600 at full text
   colour was right. Stacked above the headline at the SAME weight, two lines
   would compete and the operator's eye would have nowhere to land first — the
   defect the dossier's one-sentence lead exists to avoid, reappearing in a grid
   cell. The number is the key you cite; the finding is what you read. */
.coreline-identity {
  display: block;
  min-inline-size: 12ch;
  font-family: var(--fm-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--fm-text-muted);
  margin-block-end: 2px;
  white-space: normal;
  /* anywhere, not nowrap: a REF-xxxxxxxx fallback must never be ellipsised. A
     truncated identifier is worse than a wrapped one — it looks like a whole
     value and cites something that does not exist. */
  overflow-wrap: anywhere;
}

/* One line, ellipsised. The place name is context, not the row's subject: it
   earned four wrapped lines of "AUDIT-/DEMO-/AMENITY-/SPACE" under the free-wrap
   fix, which pushed the finding it was meant to support off the screen. Full
   value in the read panel and in the signature dialog. */
.coreline-identity__facts {
  display: block;
  font-family: var(--fm-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  color: var(--fm-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ─────────────────────────────────────────────────────────────────────────
   Phase 596 plan 04 correction — the finding headline, the THIRD class on
   this row with the same inert-wrap defect.

   `coreline-strong` (phase 595) had no rule anywhere in the theme, so the
   finding headline inherited `vaadin-grid-cell-content`'s nowrap-with-ellipsis
   and the tenant-authored finding text was cut mid-word — observed live
   2026-08-02 as "Emergency egress pa". The block above fixed the identity and
   its sub-line and stopped one class short of the one carrying the text this
   whole phase exists to put on the record.

   Same pairing, same reason: `overflow-wrap` is inert while `white-space` is
   `nowrap`, so both must be declared. The finding is tenant-authored prose of
   unbounded length, so it wraps rather than truncating — a truncated hazard
   description on a signature surface is the defect, not the layout cost.
   ───────────────────────────────────────────────────────────────────────── */
/* ─────────────────────────────────────────────────────────────────────────
   The finding headline — clamped to two lines, and that is a DIFFERENT
   decision from the truncation this class used to suffer.

   It first shipped with no rule at all, inherited vaadin-grid-cell-content's
   nowrap-with-ellipsis, and cut tenant-authored text mid-word ("Emergency
   egress pa"). The first fix let it wrap freely, which produced a 450px row
   breaking words at arbitrary points ("unobstruc/ted"). Both are wrong: one
   hides the hazard, the other makes the list unreadable.

   Two lines, broken at word boundaries, is the settlement — and it is only
   defensible because Phase 596 plan 04 added the disclosure control. When the
   ONLY way to see more text was the irreversible signature dialog, clamping
   was a trap; with a non-destructive panel one click away (and the full text
   also in the dialog itself), the row can be a summary. `break-word` rather
   than `anywhere`: a word is split only when it genuinely cannot fit, so
   ordinary prose keeps its shape.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-strong {
  display: -webkit-box;
  /* `block-axis` not `vertical`: both are valid in the legacy box spec WebKit implemented,
     and the project's stylelint requires logical values (error severity). */
  -webkit-box-orient: block-axis;
  -webkit-line-clamp: 2;
  overflow: hidden;
  font-weight: 600;
  color: var(--fm-text);
  white-space: normal;
  overflow-wrap: break-word;
}

/* ─────────────────────────────────────────────────────────────────────────
   The blocking-duration column — how long the asset has been out of service.

   The elapsed quantity leads and carries the weight; the absolute timestamp
   sits under it, muted, because a relative duration alone is not a forensic
   fact ("5 h" means nothing in a record read next week). Tabular numerals so
   durations line up down the column and a longer one is visibly longer.

   No severity tone, deliberately. Reddening past a threshold would invent an
   SLA nobody defined, and the row spine encodes status and severity only —
   age never drives it. Elapsed-time thresholds, if wanted, are tenant policy.

   Both declare `white-space: nowrap`: unlike the finding text these are short
   bounded quantities, and wrapping "2026-08-02 15:47" mid-timestamp would be
   worse than the column being one word wider.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-elapsed {
  display: block;
  font-family: var(--fm-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: 1.0625em;
  color: var(--fm-text);
  white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────────────
   Sketch 079-D — the record panel reads as a record, the dialog reads as an
   authorization.

   Both surfaces shipped with the same flat `coreline-facts` grid: identical
   typography, identical rhythm, equal weight on every fact. One is a read and
   the other cannot be undone, and an operator could not tell them apart at a
   glance. The panel now leads with the finding and groups the rest; the dialog
   leads with the state change, names what is being overridden, and previews
   the ledger entry about to be written.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-record__headline {
  display: block;
  font-size: 1.115em;
  font-weight: 600;
  line-height: 1.35;
  color: var(--fm-text);
  white-space: normal;
  overflow-wrap: break-word;
}

/* No inspector ever wrote this text — it is the generic label standing in for a
   finding that was never recorded. It must not carry the weight of prose that
   someone actually typed. */
.coreline-record__headline--unrecorded {
  font-weight: 500;
  color: var(--fm-text-muted);
}

.coreline-record__subline {
  display: block;
  margin-block-start: 2px;
  font-family: var(--fm-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.9em;
  color: var(--fm-text-muted);
}

.coreline-record__section {
  margin-block-start: 14px;
}

.coreline-record__section-title {
  display: block;
  font-size: 0.72em;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fm-accent-strong, var(--fm-accent));
  padding-block-end: 5px;
  margin-block-end: 6px;
  border-block-end: 1px solid var(--fm-border);
}

/* A named gap. Never a dash, never invented prose — the absence is the fact. */
.coreline-gap {
  display: block;
  background: var(--fm-warn-bg, #fbf1dd);
  border: 1px solid var(--fm-warn-border, #e7d3a6);
  border-radius: 4px;
  padding: 8px 11px;
  margin-block: 8px;
  font-size: 0.92em;
  line-height: 1.45;
  color: var(--fm-warn-text, #6d4d10);
}

/* The before → after pair. Grid rather than flex so the two states keep equal
   width regardless of label length: an "after" cell wider than the "now" cell
   would read as the recommended choice. */
.coreline-transition {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-block-end: 12px;
}

.coreline-transition__state {
  border: 1px solid var(--fm-border);
  border-radius: 4px;
  padding: 9px 12px;
}

.coreline-transition__now {
  background: var(--fm-alert-bg, #fbe7e1);
  border-color: var(--fm-alert-border, #f0c6ba);
}

.coreline-transition__after {
  background: var(--fm-success-bg, #e2f2e9);
  border-color: var(--fm-success-border, #bcdcc9);
}

.coreline-transition__label {
  display: block;
  font-size: 0.7em;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fm-text-muted);
  margin-block-end: 4px;
}

.coreline-transition__value {
  display: block;
  font-weight: 600;
  line-height: 1.3;
  overflow-wrap: break-word;
}

.coreline-transition__detail {
  display: block;
  margin-block-start: 3px;
  font-size: 0.82em;
  font-variant-numeric: tabular-nums;
  color: var(--fm-text-muted);
}

/* What is being overridden. Set apart from the facts because it is the reason
   the signature is a decision rather than a formality. */
.coreline-despite {
  display: block;
  margin-block-end: 12px;
  font-size: 1.02em;
  line-height: 1.5;
  overflow-wrap: break-word;
}

/* The ledger entry about to be written. Dashed border so it reads as a preview
   of a record rather than as another panel of current state. */
.coreline-receipt {
  border: 1px dashed var(--fm-border);
  border-radius: 4px;
  background: var(--fm-surface-2);
  padding: 10px 12px;
  margin-block-end: 11px;
}

.coreline-receipt__title {
  display: block;
  font-size: 0.7em;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--fm-text-muted);
  margin-block-end: 6px;
}

/* Inside the receipt a label and its value are a PAIR, not a hierarchy, so they
   sit on one line. Stacked they doubled the block's height for no added
   information and pushed the provenance line and the consequence sentence out
   of the viewport — on a signature surface that means the operator confirms
   without having seen the footer (observed live 2026-08-03). */
.coreline-receipt .coreline-fact {
  display: grid;
  grid-template-columns: minmax(0, 11ch) 1fr;
  gap: 0 14px;
  align-items: baseline;
  padding-block: 4px;
}

.coreline-receipt .coreline-fact__label {
  margin-block-end: 0;
}

/* A value the SERVER fills in. Toned so it is visibly not yet a fact. */
.coreline-fact__value--pending {
  color: var(--fm-accent);
  font-style: italic;
}

/* Provenance, demoted to a footnote: still present, but it does not change the
   decision the way the blocks above it do. */
.coreline-provenance {
  display: block;
  font-size: 0.85em;
  line-height: 1.45;
  color: var(--fm-text-muted);
  border-block-start: 1px solid var(--fm-gridline, var(--fm-border));
  padding-block-start: 8px;
  margin-block-end: 10px;
  overflow-wrap: break-word;
}

.coreline-elapsed__since {
  display: block;
  font-family: var(--fm-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
  font-size: 0.875em;
  color: var(--fm-text-muted);
  white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────────────
   Phase 596 plan 04 — the in-row read panel (SC-3 / D-3).

   The `coreline-detail` class (selector below) is the wrapper the grid's item
   details renderer emits: a padded band under the row carrying the record's
   facts grid. It exists so the panel reads as a SEPARATE surface from the row
   above it — an expanded panel that shares the row's background looks like a
   taller row, which is the visual equivalent of the defect this plan closes
   (expanding must never be mistakable for acting).

   The facts inside it are the SAME `coreline-facts` primitive the signature
   dialog uses; this block deliberately adds no parallel fact rules, because a
   second family of fact styling is how the panel and the modal start disagreeing
   about how the same fact reads.

   Lives here rather than in coreline-kit.css because that file is phase 572's;
   built on the kit's own --fm-* tokens so a later migration into the kit is a
   cut-and-paste. Logical properties only (stylelint
   plugin/use-logical-properties-and-values is error-severity).
   ───────────────────────────────────────────────────────────────────────── */
.coreline-detail {
  display: block;
  padding-block: 12px;
  padding-inline: 16px;
  background: var(--fm-surface-2);
  border-inline-start: 3px solid var(--fm-gridline);
}

/* ─────────────────────────────────────────────────────────────────────────
   Incident dossier — sketch 080, operator pick C+B (2026-08-03).

   C: the record as one composed sentence. It replaced a flat label/value grid
   that was honest but flat — the incident number, the finding and the signature
   time all weighed the same, so the eye had nothing to land on first.

   B: the chain as a timeline whose links are VISIBLE. The product's claim is
   that facts are chained; a table of hashes states that, a line that names what
   each entry hangs from shows it. It reads hardest in the negative case, where
   an unsigned incident is a chain that simply stops.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-dossier__section {
  margin-block-end: 1.5rem;
}
.coreline-dossier__section:last-child {
  margin-block-end: 0;
}

.coreline-dossier__lead {
  font-size: 1.05rem;
  line-height: 1.65;
  margin: 0 0 0.9rem;
  max-inline-size: 78ch;
  /* Prose of unbounded length from four bundles: it wraps, it never truncates.
     A dossier that clips its own summary is the defect this surface exists to
     avoid one layer up. */
  overflow-wrap: break-word;
}

.coreline-dossier__heading {
  display: block;
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--fm-accent-strong, var(--fm-text-muted));
  padding-block-end: 0.35rem;
  margin-block-end: 0.6rem;
  border-block-end: 1px solid var(--fm-border);
}

.coreline-dossier__notice {
  border-radius: 6px;
  padding: 0.6rem 0.8rem;
  font-size: 0.9rem;
  margin-block-start: 0.6rem;
  border: 1px solid var(--fm-border);
  background: var(--fm-surface-2);
  color: var(--fm-text);
}
.coreline-dossier__notice[data-tone="alert"] {
  border-color: color-mix(in srgb, var(--fm-alert) 40%, transparent);
  background: color-mix(in srgb, var(--fm-alert) 10%, transparent);
}
.coreline-dossier__notice[data-tone="warn"] {
  border-color: color-mix(in srgb, var(--fm-warn) 40%, transparent);
  background: color-mix(in srgb, var(--fm-warn) 10%, transparent);
}
.coreline-dossier__notice[data-tone="ok"] {
  border-color: color-mix(in srgb, var(--fm-success) 40%, transparent);
  background: color-mix(in srgb, var(--fm-success) 10%, transparent);
}

/* ─────────────────────────────────────────────────────────────────────────
   The dossier's ACTIONS and its case number (reported 2026-08-04).
   ─────────────────────────────────────────────────────────────────────────
   Both shipped with class names that had no rule anywhere in this theme, so
   the two links rendered as bare inline anchors with no separator and read as
   one word: "Firmar por este incidenteVerlo en la bandeja". Two controls
   touching with no gap is not a cosmetic defect on a page whose entire job is
   to be unambiguous — the operator cannot tell where one action ends.

   A missing CSS rule is invisible in Java review and invisible in a test that
   asserts the DOM: the class WAS emitted, exactly as written. Only the screen
   knows. Same class of defect as `.coreline-page__heading`, noted above as
   deferred debt and still unfixed — which is why the eyebrow below carries its
   own spacing instead of borrowing a container gap that does not exist.
   ───────────────────────────────────────────────────────────────────────── */
.coreline-dossier__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-block-start: 0.8rem;
  /* align-items rather than a fixed height: the two CTAs carry sentences from
     four bundles and Italian is the long one. They line up by their edges, and
     a longer label grows the row instead of clipping. */
  align-items: stretch;
}

.coreline-dossier__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.95rem;
  border: 1px solid var(--fm-border);
  border-radius: 6px;
  background: var(--fm-surface-2);
  color: var(--fm-text);
  font-size: 0.9rem;
  font-weight: 500;
  line-height: 1.35;
  text-decoration: none;
  /* A link that acts like a control has to LOOK like one on a page where the
     other blue text is prose. Underline removed here and restored on hover, so
     nothing about it reads as an ordinary reference. */
  transition: background 120ms ease, border-color 120ms ease;
}
.coreline-dossier__cta:hover,
.coreline-dossier__cta:focus-visible {
  border-color: var(--fm-accent-strong, var(--fm-border));
  background: color-mix(in srgb, var(--fm-accent-strong, var(--fm-text)) 8%, var(--fm-surface-2));
}
.coreline-dossier__cta:focus-visible {
  /* The signature is irreversible; a keyboard operator must never be unsure
     which control has focus. */
  outline: 2px solid var(--fm-accent-strong, var(--fm-text));
  outline-offset: 2px;
}

/* The consequential one, and only one of the two is. A dossier that gave equal
   weight to "sign this" and "see it in the list" would be asking the operator
   to work out which act is irreversible from the label alone. */
.coreline-dossier__cta--strong {
  border-color: var(--fm-accent-strong, var(--fm-text));
  background: color-mix(in srgb, var(--fm-accent-strong, var(--fm-text)) 12%, var(--fm-surface-2));
  font-weight: 600;
}

.coreline-dossier__note {
  display: block;
  max-inline-size: 78ch;
  margin: 0 0 0.2rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--fm-text-muted);
  overflow-wrap: break-word;
}

/* A statement of fact, not a warning: "this incident is ACKNOWLEDGED, there is
   nothing to sign". The three existing tones all carry alarm, and borrowing one
   would tell the operator something is wrong when nothing is. */
.coreline-dossier__notice[data-tone="muted"] {
  border-color: var(--fm-border);
  background: var(--fm-surface-2);
  color: var(--fm-text-muted);
}

/* The case number, under the title. Its own block spacing because
   `.coreline-page__heading` still has no rule to inherit from. */
.coreline-page__eyebrow {
  display: block;
  margin-block-start: 2px;
  font-family: var(--fm-font-mono, ui-monospace, monospace);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--fm-text-muted);
}

/* The timeline. The rail is a pseudo-element so no extra DOM carries meaning a
   screen reader would then have to skip. */
.coreline-tl {
  position: relative;
  padding-inline-start: 1.4rem;
}
.coreline-tl::before {
  content: "";
  position: absolute;
  inset-block: 0.5rem auto;
  inset-inline-start: 0.32rem;
  block-size: calc(100% - 1rem);
  inline-size: 2px;
  background: color-mix(in srgb, var(--fm-accent) 30%, transparent);
}
.coreline-tl__i {
  position: relative;
  padding-block: 0.55rem;
}
.coreline-tl__i::before {
  content: "";
  position: absolute;
  inset-inline-start: -1.24rem;
  inset-block-start: 0.85rem;
  inline-size: 0.62rem;
  block-size: 0.62rem;
  border-radius: 50%;
  background: var(--fm-accent);
  border: 2px solid var(--fm-surface-1);
}
.coreline-tl__ev {
  display: block;
  font-weight: 600;
}
.coreline-tl__when,
.coreline-tl__hash,
.coreline-tl__link {
  display: block;
  font-size: 0.8rem;
  color: var(--fm-text-muted);
  font-variant-numeric: tabular-nums;
}
/* The link line is the point of variant B, so it is the one that is not muted
   into the background. */
.coreline-tl__link {
  color: var(--fm-success);
}

/* ATTENDED: a panic somebody TOOK and nobody has closed. Ranks below critical and above an
   ordinary unread count -- an emergency being worked on outranks nine unread messages, and is
   outranked by an emergency nobody has taken.

   No pulse, on purpose. The pulse is what says "act now"; this one is already being acted on, and
   a second pulsing tier would compete with the only one that means drop everything. Steady amber
   with a badge is "still open", which is what it is. */
.im-shell-bell--attended .im-shell-bell__badge{
  background: var(--led-amber);
  color: #1A1200;
  animation: none;
}
.im-shell-bell--attended .im-shell-bell__button{
  color: var(--led-amber);
}
