/* The two Systemkarta faces (DESIGN.md): Hanken Grotesk carries the UI, Fragment Mono carries
   everything that reads as data (hostnames, env vars, SEK, timestamps, section labels). Both
   OFL-licensed and self-hosted — no font CDN in the portal. Variable weight file for Hanken
   (headings run 800); Fragment Mono ships 400 only, so bold data text is a defect, not a font. */
@font-face {
    font-family: 'Hanken Grotesk';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/hanken-grotesk/hanken-grotesk-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Hanken Grotesk';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/hanken-grotesk/hanken-grotesk-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: 'Fragment Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/fragment-mono/fragment-mono-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Fragment Mono';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('/fonts/fragment-mono/fragment-mono-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Hostport design tokens — the Systemkarta slate family (DESIGN.md; owner-pinned
   #23303f / #2d3e50 / #6e92c0, 2026-08-06). The panels and text ramp were already this
   family; the re-anchor moved the grounds up to it and swapped the state trio. */

:root {
    /* The palette below is the dark theme, and dark is what a person gets until they or their OS
       ask for light (theme-choice.js). color-scheme is what the browser's own widgets read: form
       controls, scrollbars, the autofill wash, the date picker. Undeclared, they drew light on
       this dark ground. */
    color-scheme: dark;

    /* Brand */

--hp-primary: #6e92c0;
--hp-primary-dark: #5579a7;
--hp-primary-light: #88a8cf;
--hp-primary-accent: #a6bfe0;
--hp-primary-muted: rgba(110, 146, 192, 0.18);

/* Grounds, named by what they do so a light theme can re-bind each one on its own. DESIGN's
   --deep is two roles that share a value here: --hp-frame is the app's outer frame (header, nav
   rail, the page behind the shell, the sign-in screens) and --hp-well is what sits sunk into a
   surface (inputs, code and log wells, the detail panel, the map). --hp-ground is DESIGN's --gnd,
   the page ground the content area shows. --hp-ground-raised has no DESIGN counterpart — it is
   the interpolated step between ground and surface so the ladder stays monotonic. */

--hp-frame: #1b2634;
--hp-well: #1b2634;
--hp-ground: #23303f;
--hp-ground-raised: #243241;

--hp-surface: #263444;
--hp-surface-raised: #2d3e50;
--hp-surface-overlay: #34495d;
--hp-surface-highlight: #3e566d;
/* Translucent rather than flat: it backs table headers that sit on several different
   surface steps, so it must tint whatever is underneath instead of picking one. */
--hp-surface-muted: rgba(110, 146, 192, 0.06);
/* The hover wash on transparent controls (menu rows, disclosures). Same reason as above: these
   sit on the rail, on cards and inside alerts, so it lifts whatever is behind it. */
--hp-surface-hover: rgba(255, 255, 255, 0.06);
/* The inks the graded washes are mixed from, at the strength each call site needs:
   color-mix(in srgb, var(--hp-wash-ink) 3%, transparent) lifts a region off whatever lies
   under it, --hp-shade-ink sinks one and casts the drop shadows outside this palette. White and
   black over these dark grounds. */
--hp-wash-ink: #ffffff;
--hp-shade-ink: #000000;
/* The Systemkarta map's two soft pools of the brand behind the glyphs (.hp-map). A light theme
   drops them: over a light ground a tint this faint has only a few 8-bit steps to fade through,
   and they show as concentric rings. */
--hp-map-wash: color-mix(in srgb, var(--hp-primary) 7%, transparent);
--hp-map-wash-far: color-mix(in srgb, var(--hp-primary) 5%, transparent);
/* The support drawer's fields. chrille asked for them lighter, then rejected a fill that mixed
   --hp-text into the drawer: near-white lightens AND greys, so the field turned flat grey in a
   blue drawer. His call (2026-10-27): "Lifted, but barely — keep the blue". So the lift is the
   brand blue over the drawer, which keeps its hue and saturation, and 15% is as far as it goes:
   any lighter and the portal's muted placeholder drops under 4.5:1. The edge does the work of
   marking the field, in the same blue. Solid rather than a translucent wash, because the service
   list paints this fill over whatever lies under it. On :root because Radzen moves that list to
   <body>, where only :root tokens reach. SupportFieldContrastTests holds the floors. */
--hp-support-field: color-mix(in srgb, var(--hp-primary) 15%, var(--hp-surface-overlay));
--hp-support-field-edge: color-mix(in srgb, var(--hp-primary-light) 85%, var(--hp-surface-overlay));

/* Borders */

--hp-border: rgba(110, 146, 192, 0.28);
--hp-border-subtle: rgba(110, 146, 192, 0.14);

/* Text */

--hp-text: #edf2f8;
--hp-text-secondary: #d6e0ec;
--hp-text-muted: #b8c7d8;
/* Meant for disabled controls (WCAG exempts them): 4.19:1 on --hp-surface-raised, under the
   4.5:1 floor. Readable text paints it too — the rail's section titles, glyph meta lines, the
   map legend, the header search — mostly on the darker frame and well (5.85:1 on #1b2634). A
   light palette has to hold it at 4.5:1 for that reason. */
--hp-text-disabled: #8fa2b8;
/* One step above --hp-text: the onboarding header's lines, and the sign-in dev hint's link (the
   warning hue mixed toward it). White, where --hp-text is #edf2f8. */
--hp-text-strong: #ffffff;
/* Labels on a filled control (the account and sign-in submit buttons, the crash banner, the
   code editor's selected completion). Not --hp-text: a light theme darkens the text ramp, and
   a label on a filled button must stay light. */
--hp-text-on-fill: #ffffff;
/* The same role on Radzen's filled buttons, the stepper's current step, the date picker's selected
   day and the cookie banner's accept: every dark fill there was tuned against --hp-text's #edf2f8,
   so dark keeps that value rather than --hp-text-on-fill's white. Reading --hp-text directly put a
   dark label on the primary fill at 2.5:1 the moment a light theme darkened the text ramp. */
--hp-text-on-button: var(--hp-text);
/* The label on an amber warning fill, which is too light for --hp-text. It shares --hp-frame's
   value here and nothing else; a light palette's text-safe amber is too dark for a dark label. */
--hp-text-on-warning: #1b2634;

/* Semantic — the Systemkarta state trio. Computed pairs (WCAG):
   success 6.4:1 on --hp-ground, 5.2:1 on --hp-surface-raised — text-safe where state
   words render; warning 7.2:1 / 5.9:1 — text-safe everywhere it appears.
   danger #e5655a is pinned by DESIGN.md but measures 2.8–4.1:1 on the surfaces — a UI
   color (borders, fills, icons ≥3:1; stat values are large text), NEVER small text.
   Small danger text takes --hp-danger-light: same hue lightened until it clears 4.5:1
   on every plain surface, including --hp-surface-overlay (4.8:1), where the old value
   shipped at 3.56:1. As a badge's text it sits on the badge's tint instead, and on any
   tint a row or card lays under that; BadgeGroundTests measures it there. */

/* The neutral badge's tint and the sign-in select's chevron. Slate-400 from another palette,
   not a Systemkarta value; kept exact so LIGHT-1 moved no pixel. */
--hp-neutral: #94a3b8;

--hp-success: #45c98f;
--hp-warning: #f0b429;
--hp-danger: #e5655a;
--hp-danger-light: #f1a89f;
/* Badge text follows the danger-light precedent: the state hue lightened until 12 px text clears
   4.5:1 on its own tint over a plain surface, --hp-surface-overlay included (success 4.7:1, warning
   4.6:1); the pinned state colours measured 4.4:1 there on --hp-surface-raised and 3.8:1 on the overlay.
   A selected card or a hovered row lays a second tint under the badge's own, which these figures do
   not cover; BadgeGroundTests measures every badge on the ground it is rendered on. */
--hp-success-light: #7dd9b1;
--hp-warning-light: #f4ca69;
--hp-info: var(--hp-primary-light);

/* Env-var names on attachment chips (DESIGN.md): 9.1:1 on --hp-surface-raised, 12.6:1 on
   the chip's own dark well. */
--hp-attachment-label: #ffe9a8;

/* The logo's wordmark (HpBrand); the globe is --hp-primary. */
--hp-logo-wordmark: #acacad;

/* The card field Stripe draws in its own iframe, which no page style reaches:
   stripe-integration.js reads these three and hands Stripe the values. They are Stripe's
   sample values, drawn on --hp-surface. */
--hp-card-input-text: #32325d;
--hp-card-input-placeholder: #aab7c4;
--hp-card-input-invalid: #fa755a;

/* Filled-button fills. The pinned state trio are UI colours: #edf2f8 on them measures 3.2:1
   (primary), 2.1:1 (success) and 3.3:1 (danger), so every filled button takes a darker shade of
   the same hue — the precedent --hp-gradient-primary set for the primary button. Computed with
   --hp-text: primary 5.4:1, success 4.7:1, danger 5.0:1; the hover shades 6.3 / 6.2 / 6.3. */
--hp-primary-fill-hover: #3f5a7f;
--hp-success-fill: #1f7a54;
--hp-success-fill-hover: #196545;
--hp-danger-fill: #b83d33;
--hp-danger-fill-hover: #9e332b;
/* The secondary button (the app panel's Deploy, among others) is a tint of the brand over whatever
   surface it sits on, with no border: Radzen draws its filled buttons with border: none. A theme
   whose tint reads as grey, as a light one over white does, restates these three. */
--hp-secondary-fill: color-mix(in srgb, var(--hp-primary) 22%, transparent);
--hp-secondary-fill-hover: color-mix(in srgb, var(--hp-primary) 34%, transparent);
--hp-secondary-label: var(--hp-text);
/* The edge a Light button carries on the frame, the ground the anonymous pages put content on. Its
   fill stands 2:1 off the frame here, so it needs none; a theme whose fill does not binds one. */
--hp-frame-button-edge: transparent;
/* The sign-in page's tab track. Its selected tab is --hp-surface-raised, a step up from the track
   here; a theme that paints the two alike binds a deeper track. */
--hp-tab-track: var(--hp-surface);
/* A disabled button's edge, drawn as a ring because Radzen's filled buttons have no border (the
   disabled rule's border-color never paints). Its fill stands 1.18:1 or more off the frame, the grounds, the
   well and the surfaces here, so it needs none; a theme whose disabled fill does not binds one. */
--hp-disabled-button-edge: transparent;
/* #blazor-error-ui, the banner a dead circuit shows. Darker than --hp-danger-fill, and not
   from the state trio. */
--hp-crash-fill: #7f1d1d;

/* Terminal — the build and runtime log wells, on DESIGN's --deep. Dark in both themes, as an IDE
   keeps its terminal (LIGHT-4, card 55b096ed): the well is the process's own output, read line by
   line, and its error, warn and info colours are tuned against this ground. Only the loading
   spinner (--hp-primary) paints inside it from outside this family. The light block restates the
   three derived below, because it re-binds what they derive from. */

--hp-terminal-bg: #1b2634;
--hp-terminal-text: #d4d4d4;
--hp-terminal-muted: #8d94a0;
--hp-terminal-border: rgba(110, 146, 192, 0.35);
--hp-terminal-error: var(--hp-danger);
--hp-terminal-warn: var(--hp-warning);
--hp-terminal-info: var(--hp-primary-accent);

/* Surface fills. The -gradient- names are historical: every value went flat in the 2026-08-05
   minimal pass (owner brief: clean, no gradients), and keeping the names meant zero consumer
   churn across four stylesheets. */

--hp-gradient-brand: #23303f;

/* At or above 4.5:1 against --hp-text: the old #6e92c0 gradient stop put the PRIMARY
   BUTTON's label at 2.8:1. Brightness for the brand lives in accents and links, not
   under text. */
--hp-gradient-primary: #46648c;

--hp-gradient-content: #23303f;

--hp-gradient-shell: var(--hp-gradient-content);

/* Effects */

--hp-glow: rgba(110,146,192,.20);
--hp-glow-subtle: rgba(110,146,192,.12);

--hp-shadow: 0 1px 3px rgba(5,10,18,.50);

--hp-shadow-sm: 0 1px 2px rgba(5,10,18,.35);

--hp-shadow-md: 0 2px 6px rgba(5,10,18,.40);

/* The app header's hairline and drop. Its shadow ink, like --hp-scrim's, is a teal near-black
   (8, 18, 20), not the slate rgba(5, 10, 18) of the shadows above. */
--hp-shadow-header: 0 1px 0 color-mix(in srgb, var(--hp-primary) 10%, transparent), 0 4px 16px rgba(8, 18, 20, 0.22);

/* What dims the page behind a modal dialog. */
--hp-scrim: rgba(8, 18, 20, 0.72);

--hp-focus-ring:
    0 0 0 3px rgba(110,146,192,.32);

/* Radius
   These were referenced 59 times across Hostport and Lib/Ui while being defined only in
   Cupo — so 52 declarations computed to border-radius: 0, including .hp-card. Every card
   built on the shared primitive rendered square next to every raw RadzenCard at 4px.
   Never reintroduce a var(--hp-radius*, <literal>) fallback: the seven that existed
   disagreed with each other (8/10/12/16px), which is what kept the gap invisible. */

--hp-radius-xs: 3px;
--hp-radius-sm: 4px;
--hp-radius: 6px;
--hp-radius-lg: 8px;
--hp-radius-pill: 999px;
--hp-radius-round: 50%;

/* Stated, not inherited: Radzen's own default is 4px, and binding it here is what keeps
   RadzenCard and HpCard visually indistinguishable. */
--rz-border-radius: var(--hp-radius-xs);

/* Systemkarta glyph metrics (HpServiceGlyph in Lib/Ui). Defined here and not in hp-ui.css
   because Lib/Ui is frozen at zero literals; Cupo must define these before it renders
   glyphs. Values are the approved comps' px at scale 1; --hp-glyph-scale multiplies every
   glyph dimension and is overridden inline by the component. */

--hp-glyph-scale: 1;
--hp-glyph-tile-radius: 9px;
--hp-glyph-cyl-rx: 16px;
--hp-glyph-cyl-ry: 9px;
--hp-glyph-hero-rx: 48px;
--hp-glyph-hero-ry: 26px;
--hp-glyph-torso-radius-top: 7px;
--hp-glyph-torso-radius-bottom: 2px;
--hp-glyph-font-mark: 13px;
--hp-glyph-font-arrow: 14px;
/* Type floor (2026-09-01): the comps were drawn at 2000px and their 8.5–10px labels rendered
   unreadable on a phone. Nothing prints under 10px; the section grammar sits at 11px. */
--hp-glyph-font-name: 11.5px;
--hp-glyph-font-meta: 10px;
--hp-glyph-font-hero-name: 13px;
--hp-glyph-font-hero-meta: 10.5px;

/* Systemkarta map metrics (HpSystemMap) */

--hp-map-zone-radius: 14px;
--hp-map-font-chip: 10px;
--hp-map-font-zone: 11px;
--hp-map-font-zone-sub: 10px;
--hp-map-font-legend: 10px;

/* Systemkarta registry/table + SEK panel type (comp 18's "tables everywhere" grammar) */

--hp-sk-font-label: 11px;
--hp-sk-font-ty: 10px;
--hp-sk-font-row: 13.5px;
--hp-sk-font-data: 12px;
--hp-sk-font-state: 11px;
--hp-sk-font-big: 30px;
--hp-sk-font-state-line: 15px;
--hp-rail-font-label: 10px;

/* Type scale — fixed rem, never clamp()/vw.
   Radzen's own heading scale is fluid, so before this bridge existed the portal ran two
   scales at once: a RadzenText H6 (clamp(1rem, 1.667vw, 1.25rem)) rendered 25% LARGER on a
   wide screen than the HpSection <h2> above it (1rem), inverting the hierarchy as the
   browser widened and contradicting the DOM order. Restating Radzen's tokens on this scale
   is the only fix that does not touch 678 RadzenText call sites. */

--hp-font-3xs: 0.625rem;
--hp-font-2xs: 0.75rem;
--hp-font-xs: 0.8125rem;
--hp-font-sm: 0.875rem;
--hp-font-md: 1rem;
--hp-font-lg: 1.125rem;
--hp-font-xl: 1.25rem;
--hp-font-2xl: 1.5rem;
--hp-font-3xl: 1.75rem;
--hp-font-4xl: 2rem;
--hp-font-5xl: 2.25rem;
--hp-font-6xl: 2.5rem;

/* Radzen bridge. Body/subtitle/caption/overline are restated, not changed — Radzen's
   values are already identical to this scale, so those (the large majority of call sites)
   move by exactly 0px. Only the six heading steps actually change. H4 lands on the same
   1.75rem as HpPageHeader so the RadzenText page titles that still exist match the
   design-system header instead of out-sizing it. */
--rz-text-h1-font-size: var(--hp-font-6xl);
--rz-text-h2-font-size: var(--hp-font-5xl);
--rz-text-h3-font-size: var(--hp-font-4xl);
--rz-text-h4-font-size: var(--hp-font-3xl);
--rz-text-h5-font-size: var(--hp-font-xl);
--rz-text-h6-font-size: var(--hp-font-md);
--rz-text-body1-font-size: var(--hp-font-md);
--rz-text-body2-font-size: var(--hp-font-sm);
--rz-text-subtitle1-font-size: var(--hp-font-md);
--rz-text-subtitle2-font-size: var(--hp-font-sm);
--rz-text-caption-font-size: var(--hp-font-2xs);
--rz-text-overline-font-size: var(--hp-font-3xs);

/* Shared Hp* component tokens (Lib/Ui, _content/Ui/hp-ui.css) — Hostport values */

--hp-page-header-gap-bottom: 1.5rem;
--hp-page-header-title-size: var(--hp-font-3xl);
--hp-page-header-subtitle-size: 0.95rem;

--hp-card-bg: var(--hp-surface-raised);
--hp-card-header-padding: 1.25rem 1.5rem;
/* A flat overlay, not the header gradient this once was: its light stop rendered every accent
   card title at 2.2:1. The left-rule grammar carries the accent now. */
--hp-card-header-bg: var(--hp-surface-overlay);
--hp-card-header-font-size: var(--hp-font-md);
--hp-card-header-border: none;

--hp-stat-card-bg: var(--hp-surface-raised);
--hp-stat-value-size: var(--hp-font-2xl);
--hp-stat-label-weight: 400;
--hp-stat-label-transform: none;
--hp-stat-label-letter-spacing: normal;
--hp-stat-label-margin: 0 0 0.25rem;

--hp-empty-state-padding: 2.5rem 1.5rem;
--hp-empty-state-icon-size: 3.5rem;
--hp-empty-state-title-size: var(--hp-font-lg);
--hp-empty-state-description-size: 0.9375rem;
--hp-empty-state-actions-margin-top: 0.5rem;

--hp-section-margin-bottom: 1.5rem;
--hp-section-title-size: var(--hp-font-md);

    /* The one mono stack for everything that reads as data — previously four ad-hoc stacks plus
   literal 'monospace' (Courier on most platforms) in the build-log terminal of all places. */
--hp-font-mono: 'Fragment Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;

/* Spacing ladder, 4px base. New spacing uses a step; the 29 ad-hoc rem values already in the
   sheets migrate opportunistically as their components are touched. */
--hp-space-1: 0.25rem;
--hp-space-2: 0.5rem;
--hp-space-3: 0.75rem;
--hp-space-4: 1rem;
--hp-space-6: 1.5rem;
--hp-space-8: 2rem;
--hp-space-12: 3rem;

/* Code editor (HpCodeEditor) default height; the component overrides it per instance on its host. */
--hp-code-editor-height: 24rem;

/* Z ladder. The 20 magic values in circulation map here; the two files that disagreed about the
   skip link (10000 vs 10001) both mean --hp-z-skip-link now. */
--hp-z-raised: 30;
--hp-z-header: 1100;
--hp-z-drawer: 1300;
--hp-z-dialog: 1500;
--hp-z-palette: 2000;
--hp-z-toast: 4000;
--hp-z-skip-link: 10000;
/* Circuit-death banner and crash guard: nothing may cover the way out of a dead app. */
--hp-z-crash: 10010;
--hp-brand-font: 'Hanken Grotesk', ui-sans-serif, system-ui, sans-serif;
    /* Radzen's interactive <link> re-appends itself after this sheet at runtime, so its
       body { font-family: var(--rz-text-font-family) } can win the cascade — remapping the
       token is what holds regardless of stylesheet order. */
    --rz-text-font-family: var(--hp-brand-font);

    --rz-primary: var(--hp-primary);
    --rz-primary-light: var(--hp-primary-light);
    --rz-primary-lighter: var(--hp-primary-muted);
    --rz-primary-dark: var(--hp-primary-dark);
    --rz-primary-darker: #3a555b;

    /* Radzen disables by dropping the whole button to 20% opacity, which on this dark ground
       flattens a primary button to 1.56:1 label-on-fill — invisible, not disabled. The state is
       authored explicitly further down instead. */
    --rz-button-disabled-opacity: 1;
    --rz-body-background-color: var(--hp-ground);
    --rz-base-background-color: var(--hp-surface);

    --rz-base-50: #e8f0f2;
    --rz-base-100: #e8f0f2;
    --rz-base-200: #dce8eb;
    --rz-base-300: #d4e3e6;
    --rz-base-400: #c5d8dc;
    --rz-base-500: #a8bcc1;
    --rz-base-600: #9fb4b9;
    --rz-base-700: var(--hp-surface-highlight);
    --rz-base-800: var(--hp-surface-raised);
    --rz-base-900: var(--hp-surface);
    --rz-base-dark: var(--hp-ground-raised);
    --rz-base-darker: var(--hp-ground);

    --rz-text-color: var(--hp-text);
    --rz-text-title-color: var(--hp-text);
    --rz-text-secondary-color: var(--hp-text-secondary);
    --rz-text-tertiary-color: var(--hp-text-muted);
    --rz-text-disabled-color: var(--hp-text-disabled);
    --rz-text-contrast-color: var(--hp-text);

    --rz-input-value-color: var(--hp-text);
    --rz-input-placeholder-color: var(--hp-text-muted);
    --rz-input-disabled-color: var(--hp-text-disabled);
    --rz-form-field-label-color: var(--hp-text-secondary);
    --rz-form-field-label-focus-color: var(--hp-primary-accent);

    --rz-grid-header-color: var(--hp-text-secondary);
    --rz-grid-cell-color: var(--hp-text);
    --rz-grid-footer-color: var(--hp-text-muted);

    --rz-breadcrumb-item-color: var(--hp-text-muted);
    --rz-breadcrumb-item-active-color: var(--hp-text);
    --rz-breadcrumb-item-hover-color: var(--hp-text-secondary);

    --rz-panel-menu-item-color: var(--hp-text-secondary);
    --rz-panel-menu-item-background-color: transparent;
    --rz-panel-menu-item-hover-color: var(--hp-text);
    --rz-panel-menu-item-hover-background-color: var(--hp-primary-muted);
    --rz-panel-menu-item-active-color: var(--hp-primary-accent);
    --rz-panel-menu-item-active-background-color: var(--hp-primary-muted);
    --rz-panel-menu-item-2nd-level-color: var(--hp-text-muted);
    --rz-panel-menu-item-2nd-level-background-color: transparent;
    --rz-panel-menu-item-2nd-level-hover-color: var(--hp-text);
    --rz-panel-menu-item-2nd-level-hover-background-color: var(--hp-primary-muted);
    --rz-panel-menu-item-2nd-level-active-color: var(--hp-primary-accent);
    --rz-panel-menu-item-2nd-level-active-background-color: var(--hp-primary-muted);

    --rz-listbox-item-color: var(--hp-text-secondary);

    --rz-menu-item-color: var(--hp-text-secondary);
    --rz-menu-item-icon-color: var(--hp-text-muted);
    --rz-menu-top-item-color: var(--hp-text-secondary);

    --rz-pager-summary-color: var(--hp-text-muted);
    --rz-pager-numeric-button-color: var(--hp-text-secondary);
    --rz-steps-title-color: var(--hp-text-muted);
    --rz-steps-number-color: var(--hp-text-secondary);

    --hp-alert-surface: var(--hp-surface-raised);
    --hp-alert-info-bg: color-mix(in srgb, var(--hp-primary) 22%, transparent);
    --hp-alert-success-bg: color-mix(in srgb, var(--hp-success) 16%, transparent);
    --hp-alert-warning-bg: color-mix(in srgb, var(--hp-warning) 16%, transparent);
    --hp-alert-danger-bg: color-mix(in srgb, var(--hp-danger) 16%, transparent);

    --rz-success: var(--hp-success);
    --rz-success-lighter: var(--hp-alert-success-bg);
    --rz-warning: var(--hp-warning);
    --rz-warning-lighter: var(--hp-alert-warning-bg);
    --rz-danger: var(--hp-danger);
    --rz-danger-lighter: var(--hp-alert-danger-bg);
    --rz-info: var(--hp-info);
    --rz-info-lighter: var(--hp-alert-info-bg);

    --rz-on-success-lighter: var(--hp-text);
    --rz-on-warning-lighter: var(--hp-text);
    --rz-on-danger-lighter: var(--hp-text);
    --rz-on-info-lighter: var(--hp-text);
    --rz-on-primary-lighter: var(--hp-text);

    --rz-alert-background-color: var(--hp-alert-surface);
    --rz-alert-color: var(--hp-text);
    --rz-alert-title-color: var(--hp-text);
    --rz-alert-icon-color: var(--hp-text-secondary);

    --rz-dialog-background-color: var(--hp-surface-raised);
    --rz-dialog-title-background-color: var(--hp-surface);
    --rz-dialog-title-color: var(--hp-text);
    --rz-dialog-close-color: var(--hp-text-muted);
    --rz-dialog-close-hover-color: var(--hp-text);
    --rz-dialog-mask-background-color: var(--hp-scrim);
    --rz-dialog-shadow: var(--hp-shadow);
    --rz-dialog-border-radius: var(--hp-radius);

    --rz-notification-shadow: var(--hp-shadow-sm);
    --rz-notification-border-radius: var(--hp-radius-sm);
    --rz-notification-success-background-color: var(--hp-surface-raised);
    --rz-notification-success-color: var(--hp-text);
    --rz-notification-success-icon-color: var(--hp-success);
    --rz-notification-warning-background-color: var(--hp-surface-raised);
    --rz-notification-warning-color: var(--hp-text);
    --rz-notification-warning-icon-color: var(--hp-warning);
    --rz-notification-error-background-color: var(--hp-surface-raised);
    --rz-notification-error-color: var(--hp-text);
    --rz-notification-error-icon-color: var(--hp-danger);
    --rz-notification-info-background-color: var(--hp-surface-raised);
    --rz-notification-info-color: var(--hp-text);
    --rz-notification-info-icon-color: var(--hp-primary-accent);

    /* danger-light, not danger: validator messages are small text on panel surfaces, where the
       pinned #e5655a sits under 4.5:1 (see the state-trio comment). */
    --rz-form-error-color: var(--hp-danger-light);
    --rz-validator-background-color: var(--hp-surface-overlay);
    --rz-validator-color: var(--hp-text);

    --rz-tooltip-background-color: var(--hp-surface-overlay);
    --rz-tooltip-color: var(--hp-text);

    --rz-outline-color: var(--hp-primary-accent);
    --rz-link-color: var(--hp-primary-accent);
    --rz-focus-outline-color: var(--hp-primary);

    /* SK-9 state sweep. Every hover/selected state below rides Radzen's --rz-secondary teal (or
       its base ramp) until pinned, and --rz-secondary itself must stay stock — it doubles as the
       .rz-color-secondary TEXT colour (see the secondary-button rule). So the pinning happens at
       the component-variable level, one state at a time. */

    /* Inputs: an authored well. software-dark's fill is its own palette's, and the hairline
       family becomes the periwinkle --line ramp (DESIGN.md) everywhere a border-normal renders. */
    --rz-input-background-color: var(--hp-well);
    --rz-input-disabled-background-color: var(--hp-surface-overlay);
    --rz-border-normal: 1px solid var(--hp-border);
    --rz-border-hover: 1px solid var(--hp-primary);
    --rz-border-focus: 1px solid var(--hp-primary);
    --rz-border-disabled: 1px solid var(--hp-border-subtle);

    --rz-dropdown-open-background-color: var(--hp-surface-raised);
    --rz-dropdown-panel-shadow: var(--hp-shadow-md);
    --rz-dropdown-item-hover-background-color: var(--hp-surface-hover);
    --rz-dropdown-item-hover-color: var(--hp-text);
    --rz-dropdown-item-selected-background-color: var(--hp-primary-muted);
    --rz-dropdown-item-selected-color: var(--hp-primary-accent);
    --rz-dropdown-item-selected-hover-background-color: color-mix(in srgb, var(--hp-primary) 30%, transparent);
    --rz-dropdown-item-selected-hover-color: var(--hp-text);

    /* SplitButton popups and menus share the menu-item family. */
    --rz-menu-background-color: var(--hp-surface-raised);
    --rz-menu-border: 1px solid var(--hp-border);
    --rz-menu-item-hover-background-color: var(--hp-surface-hover);
    --rz-menu-item-hover-color: var(--hp-text);
    --rz-menu-item-icon-hover-color: var(--hp-text);
    --rz-menu-item-selected-color: var(--hp-primary-accent);
    --rz-menu-top-item-hover-color: var(--hp-text);
    --rz-menu-top-item-icon-hover-color: var(--hp-text);
    --rz-menu-top-item-selected-color: var(--hp-primary-accent);
    --rz-splitbutton-menu-shadow: var(--hp-shadow-md);

    /* Selected day fill is the primary BUTTON fill, not --hp-primary: the day number is small
       text and #6e92c0 puts it at 2.4:1. Same reasoning as --hp-gradient-primary's own comment. */
    --rz-datepicker-panel-background-color: var(--hp-surface-raised);
    --rz-datepicker-panel-border: 1px solid var(--hp-border);
    --rz-datepicker-panel-shadow: var(--hp-shadow-md);
    --rz-datepicker-header-background-color: var(--hp-surface-raised);
    --rz-datepicker-calendar-hover-background-color: var(--hp-surface-hover);
    --rz-datepicker-calendar-hover-color: var(--hp-text);
    --rz-datepicker-calendar-selected-background-color: var(--hp-gradient-primary);
    --rz-datepicker-calendar-selected-color: var(--hp-text-on-button);
    --rz-datepicker-calendar-selected-hover-background-color: var(--hp-primary-dark);
    --rz-datepicker-calendar-selected-hover-color: var(--hp-text-on-button);
    --rz-datepicker-calendar-today-color: var(--hp-primary-accent);

    --rz-accordion-hover-color: var(--hp-primary-accent);
    --rz-accordion-selected-color: var(--hp-primary-accent);

    /* Pager: quiet register furniture — transparent chrome, the active page carries the state. */
    --rz-pager-background-color: transparent;
    --rz-pager-back-button-background-color: transparent;
    --rz-pager-next-button-background-color: transparent;
    --rz-pager-numeric-button-background-color: transparent;
    --rz-pager-numeric-button-hover-background-color: var(--hp-surface-hover);
    --rz-pager-numeric-button-hover-color: var(--hp-text);
    --rz-pager-numeric-button-selected-background-color: var(--hp-primary-muted);
    --rz-pager-numeric-button-selected-color: var(--hp-primary-accent);
    --rz-pager-numeric-button-selected-border: 1px solid var(--hp-border);

    --rz-selectbar-background-color: transparent;
    --rz-selectbar-border: 1px solid var(--hp-border);
    --rz-selectbar-selected-background-color: var(--hp-primary-muted);
    --rz-selectbar-selected-color: var(--hp-primary-accent);
    --rz-selectbar-selected-border: 1px solid var(--hp-primary);

    /* Checked switches, checkboxes and radios, and the numeric field's step buttons, were filled
       with the teal until these were pinned (LIGHT-4, card 55b096ed). The rules that once painted
       them --hp-primary lost to Radzen's longer selector or named classes Radzen 11 no longer
       renders. On a light panel the teal was 2.6:1, under the 3:1 a control's state needs. White
       marks: --hp-text-on-button is #edf2f8 in dark, under 3:1 on --hp-primary. */
    --rz-switch-checked-background-color: var(--hp-primary);
    --rz-switch-checked-circle-background-color: var(--hp-text-on-fill);
    /* The off switch, pinned to what Radzen's --rz-base-700 and --rz-text-contrast-color resolve to
       here, so a theme re-binds it as a switch and not by way of the base ramp and body ink. */
    --rz-switch-background-color: var(--hp-surface-highlight);
    --rz-switch-circle-background-color: var(--hp-text);
    --rz-checkbox-checked-background-color: var(--hp-primary);
    --rz-checkbox-checked-hover-background-color: var(--hp-primary-light);
    --rz-checkbox-checked-disabled-background-color: var(--hp-primary);
    --rz-checkbox-checked-color: var(--hp-text-on-fill);
    --rz-radio-checked-background-color: var(--hp-primary);
    --rz-radio-active-background-color: var(--hp-primary);
    --rz-radio-checked-hover-background-color: var(--hp-primary-light);
    --rz-radio-checked-color: var(--hp-text-on-fill);
    --rz-numeric-button-background-color: var(--hp-primary);
    --rz-numeric-button-color: var(--hp-text-on-fill);
    /* Radzen gives a focused link its hover colour; unpinned, that was --rz-secondary-dark. */
    --rz-link-hover-color: var(--hp-primary-light);

    /* No zebra: Radzen alternates rows on its base ramp, which read as a lighter second palette
       on Members, Access control and Sessions. Rows are separated by their hairline. */
    --rz-grid-stripe-background-color: transparent;
    --rz-grid-stripe-odd-background-color: transparent;
    --rz-grid-sort-icon-color: var(--hp-text-muted);
    --rz-grid-header-sorted-background-color: var(--hp-surface-muted);
    --rz-grid-cell-focus-background-color: var(--hp-primary-muted);
    --rz-grid-cell-focus-color: var(--hp-primary-accent);
    --rz-grid-filter-background-color: var(--hp-surface-raised);
    --rz-grid-filter-buttons-background-color: var(--hp-surface-raised);
    --rz-grid-filter-focus-color: var(--hp-primary-accent);
}

/* The light theme (LIGHT-2, card 1979ba0c): <html data-theme="light">, stamped by the server from the
   person's choice or by theme-choice.js from their OS preference. It re-binds palette tokens and
   nothing else; a token it leaves alone keeps its dark value, and LightPaletteTests names the ones
   that may.

   The palette is "Paper" (LIGHT-3, card 74f3f286), chrille's choice over a variant that kept the
   header, nav rail and sign-in screens dark: the chrome goes light with the page. Every value is
   derived on the Systemkarta hues in OKLCH, and LightPaletteTests holds every text, state and button
   pair below at WCAG AA. */
:root[data-theme="light"] {
    color-scheme: light;

    /* Grounds on the Systemkarta slate hue at low chroma: the frame one step deeper than the page
       ground, panels white. --hp-surface-highlight is the Light button's fill, so it stays quieter
       than the secondary button's brand tint. */
    --hp-frame: #e7ecf2;
    --hp-well: #f8fafd;
    --hp-ground: #f3f6fa;
    --hp-ground-raised: #f6f8fb;
    --hp-surface: #ffffff;
    --hp-surface-raised: #ffffff;
    --hp-surface-overlay: #f0f4f9;
    --hp-surface-highlight: #e4eaf2;
    --hp-surface-muted: rgba(96, 131, 180, 0.06);
    --hp-surface-hover: rgba(25, 36, 49, 0.05);
    --hp-wash-ink: #192431;
    --hp-shade-ink: #0a131f;
    --hp-border: rgba(70, 87, 108, 0.3);
    --hp-border-subtle: rgba(70, 87, 108, 0.16);

    /* Text: the slate ink in four steps, each the lightest that clears its floor on the darkest ground
       it meets (the frame, the Light button's fill). --hp-text-disabled is a fourth text step at
       4.5:1, not a disabled-only grey, because readable text paints it (see the dark block). */
    --hp-text: #192431;
    --hp-text-strong: #0a131f;
    --hp-text-secondary: #3d495a;
    --hp-text-muted: #525e6e;
    --hp-text-disabled: #5e6977;
    --hp-text-on-button: #ffffff;
    --hp-text-on-warning: #ffffff;

    /* Brand: --hp-primary carries the identity as a UI colour (fills, checkmarks, focus edges, meters)
       at 3:1 on every ground; brand-hued text is --hp-primary-accent, 4.5:1 on every ground and on
       its own selected tint; the button fills carry a white label at 5.6:1 and up. */
    --hp-primary: #6083b4;
    --hp-primary-accent: #3c5e8f;
    --hp-primary-light: #355a8d;
    --hp-primary-dark: #2f5181;
    --hp-primary-muted: rgba(96, 131, 180, 0.14);
    --hp-gradient-primary: #43689c;
    --hp-primary-fill-hover: #355a8d;
    --rz-primary-darker: #2f5181;

    /* The secondary button (the app panel's Deploy): over white, the dark theme's 22% tint is the
       grey of the disabled fill. A stronger tint with a brand label reads as the action it is. */
    --hp-secondary-fill: color-mix(in srgb, var(--hp-primary) 24%, transparent);
    --hp-secondary-fill-hover: color-mix(in srgb, var(--hp-primary) 34%, transparent);
    --hp-secondary-label: var(--hp-primary-dark);

    /* States: the dark trio's hues taken down until each reads as text on every ground and on its own
       badge tint; the -light steps are the badge text, darker again. Warning turns toward orange
       (OKLCH hue 62; dark's is 83, Cupo's 59) because a text-safe amber at 83 is olive. A text-safe
       amber is too dark for a dark label, so the warning button's label is white here. */
    --hp-success: #007445;
    --hp-success-light: #00663b;
    --hp-success-fill: #007748;
    --hp-success-fill-hover: #006739;
    --hp-warning: #9a5200;
    --hp-warning-light: #894600;
    --hp-danger: #b6322d;
    --hp-danger-light: #a32724;
    --hp-danger-fill: #bb3731;
    --hp-danger-fill-hover: #a62b27;
    --hp-neutral: #707d8e;
    --hp-attachment-label: #875800;
    --hp-logo-wordmark: #494d54;

    /* Depth. A glow is a dark-theme device; on a light surface it is a shadow or nothing. */
    --hp-gradient-brand: #f3f6fa;
    --hp-gradient-content: #f3f6fa;
    --hp-glow: transparent;
    --hp-glow-subtle: transparent;
    --hp-shadow: 0 1px 3px rgba(15, 24, 34, 0.12), 0 1px 2px rgba(15, 24, 34, 0.06);
    --hp-shadow-sm: 0 1px 2px rgba(15, 24, 34, 0.08);
    --hp-shadow-md: 0 4px 12px rgba(15, 24, 34, 0.10), 0 1px 3px rgba(15, 24, 34, 0.06);
    --hp-shadow-header: 0 1px 0 rgba(15, 24, 34, 0.08), 0 4px 16px rgba(15, 24, 34, 0.04);
    --hp-scrim: rgba(15, 24, 34, 0.40);
    --hp-focus-ring: 0 0 0 3px rgba(96, 131, 180, 0.45);

    /* The terminal and log wells are dark in both themes (LIGHT-4, card 55b096ed), and the dark block
       derives these three from tokens this block re-binds, so they keep their dark values here. */
    --hp-terminal-error: #e5655a;
    --hp-terminal-warn: #f0b429;
    --hp-terminal-info: #a6bfe0;

    /* Radzen's own ramp runs light to dark in both its themes; the bridge points 700-900 at the
       grounds. 50-600 restate the dark block's teal-tinted literals on the slate hue, with 600 dark
       enough for chart axes and timeline rules on white. */
    --rz-base-50: #fcfeff;
    --rz-base-100: #f5f7f9;
    --rz-base-200: #e8edf5;
    --rz-base-300: #dce2e9;
    --rz-base-400: #c1ccd9;
    --rz-base-500: #959fac;
    --rz-base-600: #78818e;
}

/* Light bindings for surfaces the palette above does not decide (LIGHT-4, card 55b096ed). Their own
   block, so the palette's block holds the palette and nothing else. */
:root[data-theme="light"] {
    --hp-map-wash: transparent;
    --hp-map-wash-far: transparent;
    /* The Light button's fill is the frame's own tone here (1.02:1): on the cookie page a visitor saw
       only its label. */
    --hp-frame-button-edge: var(--hp-border);
    /* --hp-surface and --hp-surface-raised are both white here, so the selected sign-in tab sat on a
       track of its own colour and only its label said which one was open. */
    --hp-tab-track: var(--hp-frame);
    /* The disabled fill (--hp-surface-overlay) is within 1.1:1 of every ground here, and 1.02:1 on the
       page ground: there a disabled button showed only its label. */
    --hp-disabled-button-edge: var(--hp-border-subtle);
    /* An off switch reads as off: a slate track under a white knob, as a checked one is the brand under
       a white knob. Left to the dark bindings it was an ink knob on a track 1.2:1 off a white panel. */
    --rz-switch-background-color: var(--hp-neutral);
    --rz-switch-circle-background-color: var(--hp-text-on-fill);
}

html,
body {
    background: var(--hp-frame);
    color: var(--hp-text);
    font-family: var(--hp-brand-font);
}

.hostport-layout.rz-layout {
    background: var(--hp-gradient-shell);
    min-height: 100vh;
}

.hp-app-header.rz-header > .rz-stack,
.hp-app-header.rz-header > div {
    width: 100%;
}

.hostport-main.rz-body {
    background: transparent;
    /* The nav column is part of the frame (comp 18): it runs flush to the viewport edge, so the
       page gutter lives on .hp-shell-main, not on the body around both. */
    padding: 0;
    /* Radzen promotes the body with translateZ(0), which makes it the containing block for
       every position:fixed descendant — the mobile nav drawer and its backdrop rendered inset
       inside the body's box instead of flush to the viewport. */
    transform: none;
}

/* The surfaces the browser draws itself still carry the design. */

::selection {
    background: color-mix(in srgb, var(--hp-primary) 45%, transparent);
    color: var(--hp-text);
}

:root {
    accent-color: var(--hp-primary);
    scrollbar-color: var(--hp-surface-highlight) transparent;
}

input,
textarea {
    caret-color: var(--hp-primary-accent);
}

.hp-site-footer {
    padding: 1rem 1.5rem 1.25rem;
    text-align: center;
    border-top: 1px solid color-mix(in srgb, var(--hp-wash-ink) 6%, transparent);
}

.hp-site-footer-nav {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8125rem;
}

.hp-site-footer-nav .rz-link {
    color: var(--hp-text-muted);
}

.hp-site-footer-nav .rz-link:hover {
    color: var(--hp-text);
}

.hp-site-footer-sep {
    color: var(--hp-text-muted);
    user-select: none;
}

.main-content {
    position: relative;
    isolation: isolate;
}

.text-muted,
.hp-text-muted {
    color: var(--hp-text-muted) !important;
}

.hp-text-secondary {
    color: var(--hp-text-secondary);
}

/* Dialogs must cover the sticky app header below (z-index 1100) — Radzen's default wrapper
   z-index (1001) left the header painted over modal titles on desktop, and on mobile it sat on
   top of the dialog's close button entirely. Kept under the feedback drawer (1300). Do NOT give
   .rz-dialog-mask a z-index: DialogContainer renders the mask INSIDE the wrapper, after
   .rz-dialog, so its z-index competes with the dialog's own 1001 (--rz-dialog-zindex) in the
   wrapper's local stacking context — any wrapper-scale value dims the dialog itself, which is
   how two releases shipped a mask covering the very dialog it belonged to. The theme default
   (--rz-dialog-mask-zindex: 1000) already sits below the dialog. */
.rz-dialog-wrapper,
.rz-dialog-side-wrapper {
    z-index: 1250;
}

/* A dialog opened from inside a drawer (the publish confirm in the analytics settings drawer, a
   rollback confirm in the deployments drawer) must sit above that drawer's backdrop (1300) and
   panel (1310), or its buttons cannot be reached with the pointer. Scoped to "a drawer is open"
   so the default stacking above still holds when no drawer is up. */
body:has(.hp-drawer-portal .hp-workflow-drawer, .hp-drawer-portal .hp-feedback-drawer) .rz-dialog-wrapper,
body:has(.hp-drawer-portal .hp-workflow-drawer, .hp-drawer-portal .hp-feedback-drawer) .rz-dialog-side-wrapper {
    z-index: 1320;
}

/* App shell header */

.hp-app-header.rz-header {
    position: sticky;
    top: 0;
    z-index: 1100;
    overflow: visible;
    background: var(--hp-frame);
    border-bottom: 1px solid var(--hp-border);
    box-shadow: var(--hp-shadow-header);
    padding: 0;
    min-height: auto;
}

.hp-app-header.rz-header::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 1px;
    background: var(--hp-border-subtle);
    opacity: 0.45;
    pointer-events: none;
}

/* 1fr auto 1fr, not flex: the side clusters differ in width, and search must sit at the TRUE
   center of the header, not the center of whatever space the wider side left over. */
.hp-header-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 1rem;
    width: 100%;
    min-height: 3.5rem;
    padding: 0.5rem 1.25rem;
    box-sizing: border-box;
    overflow: visible;
}

.hp-header-start {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    justify-self: start;
}

.hp-brand-link {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    color: var(--hp-text);
}

.hp-brand-logo {
    display: block;
    /* Comp 18's header is a slim frame; the wordmark sits small inside 56px, not filling it. */
    height: 1.6rem;
    width: auto;
}

.hp-brand-logo-lg {
    height: 3.4rem;
}

/* The middle zone exists to hold exactly one thing: the search affordance, centered. */
.hp-header-center {
    display: flex;
    justify-content: center;
    min-width: 0;
}

.hp-header-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    justify-self: end;
}

@media (max-width: 1024px) {
    .hp-header-inner {
        padding: 0.5rem 1rem;
    }
}

@media (max-width: 640px) {
    .hp-header-action-label,
    .hp-header-action-chevron,
    .hp-live-chip {
        display: none;
    }

    .hp-header-inner {
        /* auto columns, not 1fr auto 1fr: with fixed thirds the action cluster overflowed the
           right edge on phones — grid items refuse to shrink below their content. */
        grid-template-columns: auto minmax(0, 1fr) auto;
        gap: 0.5rem;
        padding-inline: 0.75rem;
    }

    .hp-header-actions {
        gap: 0.25rem;
    }

    .hp-header-action-btn {
        min-height: 2.5rem;
        min-width: 2.5rem;
        justify-content: center;
    }
}

/* RadzenStack defaults to FlexWrap.NoWrap and writes it as an inline style, so the 160+
   horizontal stacks in this app squash or clip when a phone runs out of row. Wrapping is
   the correct overflow behaviour at this size, and !important is the only way past an
   inline style. Desktop keeps each call site's own wrap choice. */
@media (max-width: 768px) {
    .rz-stack.rz-flex-row {
        flex-wrap: wrap !important;
    }
}

/* Radzen + global brand enforcement */

.rz-button.rz-button-primary,
.rz-button.rz-primary {
    background: var(--hp-gradient-primary);
    border-color: var(--hp-primary);
    color: var(--hp-text-on-button);
}

.rz-button.rz-button-primary:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-primary:not(:disabled):not(.rz-state-disabled):hover {
    /* Hover darkens instead of lightening: any lighter stop pushes the label back under 4.5:1. */
    background: var(--hp-primary-fill-hover);
    border-color: var(--hp-primary-light);
}

.rz-button.rz-button-primary:not(:disabled):not(.rz-state-disabled):active,
.rz-button.rz-primary:not(:disabled):not(.rz-state-disabled):active {
    background: var(--hp-primary-dark);
    border-color: var(--hp-primary-dark);
}

/* The rules above never painted a filled button. Radzen paints every filled button through
   .rz-button.<severity>.rz-shade-default — three classes — and its interactive <link> re-appends
   after this sheet, so a two-class rule only ever reached the border: the fill and label underneath
   stayed Radzen's own, and every primary button in the portal rendered white on the raw
   --rz-primary #6e92c0 at 3.21:1 (axe, 39 nodes on 29 pages, 2026-09-01). These match Radzen's
   own specificity and carry the authored fills. check-radzen-state-theming.sh pins them. */
.rz-button.rz-primary.rz-shade-default,
.rz-button.rz-info.rz-shade-default {
    background: var(--hp-gradient-primary);
    border-color: var(--hp-primary);
    color: var(--hp-text-on-button);
}

.rz-button.rz-primary.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-info.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-primary-fill-hover);
    border-color: var(--hp-primary-light);
    color: var(--hp-text-on-button);
}

.rz-button.rz-success.rz-shade-default {
    background: var(--hp-success-fill);
    border-color: var(--hp-success-fill);
    color: var(--hp-text-on-button);
}

.rz-button.rz-success.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-success-fill-hover);
    border-color: var(--hp-success-fill-hover);
    color: var(--hp-text-on-button);
}

.rz-button.rz-danger.rz-shade-default {
    background: var(--hp-danger-fill);
    border-color: var(--hp-danger-fill);
    color: var(--hp-text-on-button);
}

.rz-button.rz-danger.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-danger-fill-hover);
    border-color: var(--hp-danger-fill-hover);
    color: var(--hp-text-on-button);
}

/* Warning is the one state whose pinned value carries dark text (8.2:1). */
.rz-button.rz-warning.rz-shade-default {
    background: var(--hp-warning);
    border-color: var(--hp-warning);
    color: var(--hp-text-on-warning);
}

/* Outlined and text variants keep the state as the label colour. Danger goes on the light ramp:
   the pinned #e5655a is 4.0:1 on the page ground and 2.3:1 on a hovered table row. The four-class
   selectors are Radzen's own for these variants. */
.rz-button.rz-variant-outlined.rz-danger.rz-shade-default,
.rz-button.rz-variant-text.rz-danger.rz-shade-default,
.rz-button.rz-variant-flat.rz-danger.rz-shade-default {
    background: transparent;
    border-color: var(--hp-danger);
    color: var(--hp-danger-light);
}

.rz-button.rz-variant-outlined.rz-danger.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-text.rz-danger.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-flat.rz-danger.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: color-mix(in srgb, var(--hp-danger) 14%, transparent);
    border-color: var(--hp-danger-light);
    color: var(--hp-danger-light);
}

.rz-button.rz-variant-outlined.rz-success.rz-shade-default,
.rz-button.rz-variant-text.rz-success.rz-shade-default,
.rz-button.rz-variant-flat.rz-success.rz-shade-default {
    background: transparent;
    border-color: var(--hp-success);
    color: var(--hp-success);
}

.rz-button.rz-variant-outlined.rz-success.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-text.rz-success.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-flat.rz-success.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: color-mix(in srgb, var(--hp-success) 14%, transparent);
    color: var(--hp-success);
}

.rz-button.rz-variant-outlined.rz-primary.rz-shade-default,
.rz-button.rz-variant-text.rz-primary.rz-shade-default,
.rz-button.rz-variant-flat.rz-primary.rz-shade-default {
    background: transparent;
    border-color: var(--hp-primary);
    color: var(--hp-primary-accent);
}

.rz-button.rz-variant-outlined.rz-primary.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-text.rz-primary.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-variant-flat.rz-primary.rz-shade-default:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-primary-muted);
    color: var(--hp-primary-accent);
}

/* Radzen's stock secondary is a pale teal from its own palette (2.01:1 for the label it ships
   with), and the old override set only border and text — so that fill went on rendering. It has
   to be fixed HERE and not by remapping --rz-secondary: that variable is also the text colour
   behind .rz-color-secondary, so tinting it fades body copy across the app (the onboarding
   banner's subtitle, first). The doubled class outranks Radzen's .rz-button.rz-secondary
   .rz-shade-default, which a two-class selector would lose to. */
.rz-button.rz-button-secondary,
.rz-button.rz-secondary.rz-secondary {
    background: var(--hp-secondary-fill);
    border-color: var(--hp-primary);
    color: var(--hp-secondary-label);
}

.rz-button.rz-button-secondary:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-secondary.rz-secondary:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-secondary-fill-hover);
    border-color: var(--hp-primary-light);
    color: var(--hp-secondary-label);
}

/* Disabled, authored. The repeated class is deliberate: the authored fills above sit at four
   classes (variant + severity + shade), so the disabled state needs five to outrank them whatever
   the order. Inert flatness carries the state, not illegibility. The edge is !important because
   Radzen's flat variants set box-shadow: none !important, which took it off every disabled flat
   button; among !important declarations the five classes still win. */
.rz-button.rz-state-disabled.rz-state-disabled.rz-state-disabled.rz-state-disabled,
.rz-button.rz-state-disabled.rz-state-disabled.rz-state-disabled.rz-state-disabled:hover,
.rz-button:disabled:disabled:disabled:disabled {
    background: var(--hp-surface-overlay);
    border-color: var(--hp-border-subtle);
    color: var(--hp-text-muted);
    cursor: not-allowed;
    box-shadow: inset 0 0 0 1px var(--hp-disabled-button-edge) !important;
}

/* Radzen's software theme hovers every button with a 3px white inset line along the bottom
   edge — another palette's affordance. Hover feedback here is the fill change, not a stripe. */
.rz-button:not(:disabled):not(.rz-state-disabled):hover {
    box-shadow: none;
}

.rz-button.rz-button-light:not(:disabled):not(.rz-state-disabled):hover,
.rz-button.rz-light.rz-light:not(:disabled):not(.rz-state-disabled):hover {
    background: var(--hp-surface-highlight);
}

/* An inset shadow, as Radzen draws its own edges, so the button keeps its size. The hover selector
   outranks the hover rule above, which would take the edge off. */
.login-layout .rz-button.rz-variant-filled.rz-light,
.login-layout .rz-button.rz-variant-filled.rz-light:not(:disabled):not(.rz-state-disabled):hover {
    box-shadow: inset 0 0 0 1px var(--hp-frame-button-edge);
}

/* RadzenBadge severities on the HpBadge grammar (tinted fill, same-hue light text, tinted
   border) — Radzen's own solid fills put small white labels on the raw state colours, where
   danger measures 3.1:1. Secondary/base ride the neutral pair for the same reason the
   secondary button does: --rz-secondary is another palette's teal. */
.rz-badge.rz-badge-success {
    color: var(--hp-success);
    background: color-mix(in srgb, var(--hp-success) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--hp-success) 30%, transparent);
}

.rz-badge.rz-badge-warning {
    color: var(--hp-warning);
    background: color-mix(in srgb, var(--hp-warning) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--hp-warning) 30%, transparent);
}

.rz-badge.rz-badge-danger,
.rz-badge.rz-badge-error {
    color: var(--hp-danger-light);
    background: color-mix(in srgb, var(--hp-danger) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--hp-danger) 30%, transparent);
}

.rz-badge.rz-badge-info,
.rz-badge.rz-badge-primary {
    color: var(--hp-primary-accent);
    background: var(--hp-primary-muted);
    border: 1px solid var(--hp-border);
}

.rz-badge.rz-badge-secondary,
.rz-badge.rz-badge-base,
.rz-badge.rz-badge-light {
    color: var(--hp-text-secondary);
    background: var(--hp-surface-overlay);
    border: 1px solid var(--hp-border-subtle);
}

.rz-link,
a.rz-link {
    color: var(--hp-primary-accent);
}

/* A bare <a> in customer copy (the support drawer's docs link, the access-control "Members"
   sentence) rendered the browser's own #0000ee on slate at 1.01:1. Unclassed anchors read as
   links of the product wherever they render — the drawers portal to <body>, outside the shell
   element, so this is not scoped to it. Radzen's own components carry their classes. */
a:not([class]) {
    color: var(--hp-primary-accent);
    text-decoration: underline;
    text-underline-offset: 0.15em;
}

a:not([class]):hover {
    color: var(--hp-primary-light);
}

.rz-link:hover,
a.rz-link:hover {
    color: var(--hp-primary-light);
}

.rz-steps-item.rz-state-highlight .rz-steps-number {
    background: var(--hp-gradient-primary);
    border-color: var(--hp-primary);
    color: var(--hp-text-on-button);
}

.rz-steps-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    padding: 0;
    box-sizing: border-box;
}

.rz-steps-item.rz-state-highlight .rz-steps-title {
    color: var(--hp-primary-accent);
}

.rz-steps-item.rz-state-complete .rz-steps-number {
    background: var(--hp-primary-dark);
    border-color: var(--hp-primary-dark);
}

.rz-progressbar-value {
    background: var(--hp-gradient-primary);
}

.rz-progressbar-circular-value {
    stroke: var(--hp-primary);
}

.rz-slider-handle,
.rz-slider-range {
    background: var(--hp-primary);
}

.rz-panel-menu-item-link.rz-state-active {
    background: var(--hp-primary-muted);
    color: var(--hp-primary-accent);
}

.rz-listbox .rz-listbox-item.rz-state-highlight {
    background: var(--hp-primary-muted);
    color: var(--hp-primary-accent);
}

.rz-datepicker-calendar td .rz-state-active,
.rz-datepicker-calendar td .rz-state-highlight {
    /* Button fill, not --hp-primary: the day number is small text and #6e92c0 puts it at 2.4:1. */
    background: var(--hp-gradient-primary);
    color: var(--hp-text-on-button);
}

.rz-chkbox-box.rz-state-active,
.rz-chkbox-box.rz-state-focused {
    border-color: var(--hp-primary);
}

/* RadzenSelectBarItem renders its icon inside a button Radzen classes as text-only, which has no
   icon/text spacing — "</>GitHub repo" ran together. */
.rz-selectbar .rz-button > .rz-button-icon-left {
    margin-inline-end: 0.4rem;
}

*:focus-visible {
    outline: 2px solid var(--hp-primary-accent);
    outline-offset: 2px;
}

.rz-card,
.rz-dialog,
.rz-panel {
    background-color: var(--hp-surface);
    border-color: var(--hp-border-subtle);
}

.rz-datatable,
.rz-grid-table {
    background-color: var(--hp-surface);
}


.hostport-layout .rz-data-grid-data tr:not(.rz-expanded-row-content):hover > td,
.hostport-layout .rz-datatable-data tr:not(.rz-expanded-row-content):hover > td {
    background-color: var(--hp-primary-muted) !important;
}

.hostport-layout .rz-data-grid-data tr.rz-state-highlight:not(.rz-expanded-row-content) > td,
.hostport-layout .rz-datatable-data tr.rz-state-highlight:not(.rz-expanded-row-content) > td {
    background-color: color-mix(in srgb, var(--hp-primary) 32%, transparent) !important;
    color: var(--hp-primary-accent);
}

.hostport-layout .rz-text-body1,
.hostport-layout .rz-text-body2,
.hostport-layout .rz-text-caption,
.hostport-layout .rz-text-overline,
.hostport-layout .rz-text-subtitle1,
.hostport-layout .rz-text-subtitle2 {
    color: var(--hp-text);
}

.hostport-layout .rz-form-field-label,
.hostport-layout .rz-label,
.hostport-layout .rz-chkbox-label,
.hostport-layout .rz-radiobutton-label {
    color: var(--hp-text-secondary);
}

.rz-form-field-content > .rz-dropdown > .rz-dropdown-panel,
.rz-form-field-content > .rz-multiselect > .rz-multiselect-panel {
    display: none !important;
}

/* A full-width field's help ran the whole field: about 180 characters a line under the web app's
   Content-Security-Policy box at 1960px, too long to read back to the next line's start. */
.hostport-layout .rz-form-field-helper {
    max-width: 80ch;
}

.hostport-layout .rz-inputtext,
.hostport-layout .rz-textarea,
.hostport-layout .rz-numeric,
.hostport-layout .rz-colorpicker,
.hostport-layout .rz-autocomplete-input {
    color: var(--hp-text);
}

.hostport-layout .rz-inputtext::placeholder,
.hostport-layout .rz-textarea::placeholder {
    color: var(--hp-text-muted);
    opacity: 1;
}

.hostport-layout .rz-data-grid-data td,
.hostport-layout .rz-data-grid-data .rz-cell-data,
.hostport-layout .rz-datatable-data td,
.hostport-layout .rz-datatable-data .rz-cell-data {
    color: var(--hp-text);
}

.hostport-layout .rz-grid-table thead th,
.hostport-layout .rz-data-grid-header .rz-column-title,
.hostport-layout .rz-datatable-thead th {
    color: var(--hp-text-secondary);
}

.hostport-layout .rz-breadcrumb-item,
.hostport-layout .rz-breadcrumb-item a {
    color: var(--hp-text-muted);
}

.hostport-layout .rz-breadcrumb-item.rz-state-active,
.hostport-layout .rz-breadcrumb-item.rz-state-active a {
    color: var(--hp-text);
}

.hostport-layout .rz-listbox .rz-listbox-item {
    color: var(--hp-text-secondary);
}

.hostport-layout .rz-listbox .rz-listbox-item:hover {
    color: var(--hp-text);
}

.hostport-layout .rz-panel-menu .rz-navigation-item-text,
.hostport-layout .rz-panel-menu .rz-navigation-item-link {
    color: var(--hp-text-secondary);
}

.hostport-layout .rz-panel-menu .rz-navigation-item-link:hover {
    color: var(--hp-text);
}

.hostport-layout .rz-panel-menu .rz-navigation-item-link.rz-state-active {
    color: var(--hp-primary-accent);
}

.hostport-layout .rz-pager-summary,
.hostport-layout .rz-pager-pages .rz-pager-page {
    color: var(--hp-text-muted);
}

.hostport-layout .rz-steps-title {
    color: var(--hp-text-muted);
}

.hostport-layout .rz-steps-item.rz-state-highlight .rz-steps-title {
    color: var(--hp-text);
}

.hostport-layout .rz-fieldset-legend-text {
    color: var(--hp-text-secondary);
}

.hostport-layout .rz-dialog-title,
.hostport-layout .rz-sidebar-title {
    color: var(--hp-text);
}

.hostport-layout .rz-notification-content,
.hostport-layout .rz-alert-content {
    color: var(--hp-text);
}

/* Alerts, dialogs, notifications — global (Radzen portals to body) */

.rz-alert {
    border: 1px solid var(--hp-border-subtle);
}

.rz-alert .rz-alert-title {
    color: var(--hp-text);
    font-weight: 600;
}

/* A readable measure: alert copy used to run the full content width (~190 characters on a wide
   screen), which read as a wall rather than a message. */
.rz-alert .rz-alert-message,
.rz-alert .rz-alert-content {
    max-width: 82ch;
    color: var(--hp-text-secondary);
}

.rz-alert .rz-alert-icon {
    color: var(--hp-text-secondary);
}

/* The close button inside an alert is a text button in the alert's severity at Radzen's
   "darker" shade — a stock teal on every info alert. It is furniture; it takes the text ramp. */
.rz-alert .rz-button.rz-variant-text.rz-shade-darker {
    color: var(--hp-text-secondary);
}

.rz-alert .rz-button.rz-variant-text.rz-shade-darker:not(:disabled):hover {
    color: var(--hp-text);
    background: var(--hp-surface-hover);
}

.rz-alert.rz-info,
.rz-alert.rz-info.rz-shade-lighter,
.rz-alert.rz-info.rz-shade-light {
    background: var(--hp-alert-info-bg);
    border-color: color-mix(in srgb, var(--hp-primary) 42%, transparent);
    border-inline-start: 2px solid var(--hp-primary-accent);
    color: var(--hp-text);
}

/* The icon keeps the accent (a UI colour); the title is small text and the accent measures
   4.39:1 on the info tint. */
.rz-alert.rz-info .rz-alert-icon {
    color: var(--hp-primary-accent);
}

.rz-alert.rz-info .rz-alert-title {
    color: var(--hp-text);
}

.rz-alert.rz-info .rz-alert-message,
.rz-alert.rz-info .rz-alert-content {
    color: var(--hp-text);
}

.rz-alert.rz-success,
.rz-alert.rz-success.rz-shade-lighter,
.rz-alert.rz-success.rz-shade-light {
    background: var(--hp-alert-success-bg);
    border-color: color-mix(in srgb, var(--hp-success) 35%, transparent);
    border-inline-start: 2px solid var(--hp-success);
    color: var(--hp-text);
}

.rz-alert.rz-success .rz-alert-title,
.rz-alert.rz-success .rz-alert-icon {
    color: var(--hp-success);
}

.rz-alert.rz-success .rz-alert-message,
.rz-alert.rz-success .rz-alert-content {
    color: var(--hp-text);
}

.rz-alert.rz-warning,
.rz-alert.rz-warning.rz-shade-lighter,
.rz-alert.rz-warning.rz-shade-light {
    background: var(--hp-alert-warning-bg);
    border-color: color-mix(in srgb, var(--hp-warning) 35%, transparent);
    border-inline-start: 2px solid var(--hp-warning);
    color: var(--hp-text);
}

.rz-alert.rz-warning .rz-alert-title,
.rz-alert.rz-warning .rz-alert-icon {
    color: var(--hp-warning);
}

.rz-alert.rz-warning .rz-alert-message,
.rz-alert.rz-warning .rz-alert-content {
    color: var(--hp-text);
}

.rz-alert.rz-danger,
.rz-alert.rz-error,
.rz-alert.rz-danger.rz-shade-lighter,
.rz-alert.rz-error.rz-shade-lighter,
.rz-alert.rz-danger.rz-shade-light,
.rz-alert.rz-error.rz-shade-light {
    background: var(--hp-alert-danger-bg);
    border-color: color-mix(in srgb, var(--hp-danger) 35%, transparent);
    border-inline-start: 2px solid var(--hp-danger);
    color: var(--hp-text);
}

/* danger-light: the pinned danger measures 2.8:1 on the alert's own tinted background. */
.rz-alert.rz-danger .rz-alert-title,
.rz-alert.rz-error .rz-alert-title,
.rz-alert.rz-danger .rz-alert-icon,
.rz-alert.rz-error .rz-alert-icon {
    color: var(--hp-danger-light);
}

.rz-alert.rz-danger .rz-alert-message,
.rz-alert.rz-error .rz-alert-message,
.rz-alert.rz-danger .rz-alert-content,
.rz-alert.rz-error .rz-alert-content {
    color: var(--hp-text);
}

.rz-alert.rz-light,
.rz-alert.rz-base {
    background: var(--hp-surface);
    border-color: var(--hp-border);
    color: var(--hp-text);
}

.rz-alert.rz-light .rz-alert-title,
.rz-alert.rz-light .rz-alert-message,
.rz-alert.rz-light .rz-alert-content,
.rz-alert.rz-light .rz-alert-icon {
    color: var(--hp-text);
}

.rz-notification {
    inset-block-start: auto;
    inset-block-end: 1rem;
}

.rz-notification-item {
    border: 1px solid var(--hp-border-subtle);
    border-radius: var(--hp-radius-sm);
    box-shadow: var(--hp-shadow-sm);
}

.rz-notification-success .rz-notification-item {
    border-inline-start: 2px solid var(--hp-success);
}

.rz-notification-warn .rz-notification-item {
    border-inline-start: 2px solid var(--hp-warning);
}

.rz-notification-error .rz-notification-item {
    border-inline-start: 2px solid var(--hp-danger);
}

.rz-notification-info .rz-notification-item {
    border-inline-start: 2px solid var(--hp-primary-accent);
}

.rz-notification-content {
    color: var(--hp-text);
}

.rz-notification-close {
    color: var(--hp-text-muted);
}

.rz-notification-close:hover {
    color: var(--hp-text);
}

.rz-dialog,
.rz-dialog-side {
    background: var(--hp-surface-raised);
    border: 1px solid var(--hp-border);
    color: var(--hp-text);
}

.rz-dialog-titlebar,
.rz-dialog-side-titlebar {
    background: var(--hp-surface);
    border-block-end: 1px solid var(--hp-border-subtle);
}

.rz-dialog-title,
.rz-dialog-side-title {
    color: var(--hp-text);
}

.rz-dialog-content,
.rz-dialog-side-content {
    color: var(--hp-text);
    background: var(--hp-surface-raised);
}

.rz-dialog-content .rz-text-body1,
.rz-dialog-content .rz-text-body2,
.rz-dialog-content .rz-text-caption,
.rz-dialog-content .rz-label,
.rz-dialog-content .rz-form-field-label {
    color: var(--hp-text-secondary);
}

.rz-dialog-content .rz-text-subtitle1,
.rz-dialog-content .rz-text-subtitle2,
.rz-dialog-content .rz-text-h6 {
    color: var(--hp-text);
}

.rz-dialog-mask {
    background-color: var(--hp-scrim);
    opacity: 1;
}

.rz-dialog-confirm-message,
.rz-dialog-alert-message {
    color: var(--hp-text-secondary);
}

.rz-dialog-confirm .rz-button.rz-button-light,
.rz-dialog-alert .rz-button.rz-button-light {
    color: var(--hp-text-secondary);
    border-color: var(--hp-border);
    background: color-mix(in srgb, var(--hp-wash-ink) 4%, transparent);
}

.rz-message-popup,
.rz-form-field-error,
.rz-validator {
    background: var(--hp-surface-overlay);
    color: var(--hp-text);
    border: 1px solid var(--hp-border);
}

/* danger-light, not danger: these sit on --hp-surface-overlay, where #e5655a is 2.8:1. */
.rz-form-field-error-message,
.rz-validator-text {
    color: var(--hp-danger-light);
}

.rz-tooltip {
    background: var(--hp-surface-overlay);
    color: var(--hp-text);
    border: 1px solid var(--hp-border);
}

/* Skip link + reduced-motion live in hostport-a11y.css alone — this file used to carry a second
   copy that disagreed about the border and z-index, and always lost (a11y loads last). */

/* ==========================================================================
   Web-app service page — Release rail
   The one-line manifest under the page header (Services/Components/ReleaseRail.razor)
   and the page's single animated element.
   ========================================================================== */

.hp-release-rail {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem 0.6rem;
    padding: 0.625rem 1rem;
    background: var(--hp-surface-raised);
    border: 1px solid var(--hp-border-subtle);
    border-radius: var(--hp-radius);
    font-size: var(--hp-font-sm);
    color: var(--hp-text);
}

.hp-release-rail-seg {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    min-width: 0;
}

.hp-release-rail-seg code {
    font-size: var(--hp-font-xs);
    color: var(--hp-text);
    background: none;
    padding: 0;
}

.hp-release-rail-link {
    border: none;
    background: none;
    padding: 0.1rem 0.15rem;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    border-radius: var(--hp-radius-xs);
}

.hp-release-rail-link:hover {
    color: var(--hp-primary-accent);
}

.hp-release-rail-link:hover code {
    color: var(--hp-primary-accent);
}

.hp-release-rail-status {
    font-weight: 600;
}

.hp-release-rail-muted {
    color: var(--hp-text-muted);
}

.hp-release-rail-sep {
    color: var(--hp-text-disabled);
    user-select: none;
}

.hp-release-rail-problem,
.hp-release-rail-problem:hover {
    color: var(--hp-danger-light);
}

/* The hostname takes the room the rail has and ellipsizes only when the rail runs out: a fixed
   20rem cap cut it at 1960px with half the rail empty beside it. */
.hp-release-rail-url {
    gap: 0.3rem;
    max-width: 100%;
}

.hp-release-rail-url code {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hp-release-dot {
    position: relative;
    width: 0.625rem;
    height: 0.625rem;
    border-radius: var(--hp-radius-round);
    flex-shrink: 0;
    background: var(--hp-text-disabled);
}

.hp-release-dot-success { background: var(--hp-success); }
.hp-release-dot-warning { background: var(--hp-warning); }
.hp-release-dot-danger  { background: var(--hp-danger); }
.hp-release-dot-neutral { background: var(--hp-text-disabled); }

/* background: inherit lets one ping ring pick up whichever state color the dot has. */
.hp-release-dot-pulse::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--hp-radius-round);
    background: inherit;
    animation: hp-release-pulse 2.6s ease-out infinite;
}

@keyframes hp-release-pulse {
    0%   { transform: scale(1);    opacity: 0.55; }
    70%  { transform: scale(2.4);  opacity: 0; }
    100% { transform: scale(2.4);  opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .hp-release-dot-pulse::after {
        display: none;
    }
}

/* A phone stacks the segments one to a line, where a separator only ever dangled at a line end. */
@media (max-width: 640px) {
    .hp-release-rail {
        flex-direction: column;
        align-items: flex-start;
    }

    .hp-release-rail-sep {
        display: none;
    }
}

/* Screen-reader-only text (e.g. the deployment table's unlabeled actions column). Anchored at the
   inline start: left at its static place, a label inside a table's own scroll box escaped that
   box (no positioned ancestor between them) and widened the whole page on a phone. */
.hp-visually-hidden {
    position: absolute;
    inset-inline-start: 0;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
