@import url('https://fonts.googleapis.com/css2?family=Figtree:wght@300;400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Inter:wght@400;500;600;700&family=Open+Sans:wght@400;500;600;700&family=Poppins:wght@400;500;600;700&family=Montserrat:wght@400;500;600;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700&family=Rubik:wght@400;500;600;700&family=Ubuntu:wght@400;500;700&family=Outfit:wght@400;500;600;700&family=Merriweather:wght@400;700&family=Lora:wght@400;500;600;700&display=swap');

/* Global: never show underline on links - overrides Tabler's a:hover underline */
a, a:hover, a:focus, a:active {
    text-decoration: none !important;
}

/* Prevent ApexCharts SVGs from overflowing card boundaries on wide viewports */
.card:has([data-controller="apexchart"]) {
    overflow: hidden;
}


.steps-no-border {
    margin-left: 0px;
    padding-left: 0px;
    border-left: 0px;
}

/* Remove border-left from counter-based horizontal steps
   (Tabler has a second .steps definition for doc-style vertical steps
   that adds border-left, margin-left, and padding-left - override those) */
.steps.steps-counter {
    border-left: none;
    margin-left: 0;
    padding-left: 0;
    margin-bottom: 0;
}

/* Vodafone font family - WOFF2 optimized for web rendering */
/* Light weight (300) */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone-Light.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone-Light.ttf') format('truetype');
    font-weight: 300;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* Normal/Regular weight (400) */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* Medium weight (500) - uses regular as no dedicated medium exists */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* SemiBold weight (600) - uses bold */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone-Bold.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone-Bold.ttf') format('truetype');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* Bold weight (700) */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone-Bold.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone-Bold.ttf') format('truetype');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* ExtraBold weight (800) */
@font-face {
    font-family: 'Vodafone';
    src: url('/tabler/custom/fonts/vodafone/Vodafone-ExtraBold.woff2') format('woff2'),
         url('/tabler/custom/fonts/vodafone/Vodafone-ExtraBold.ttf') format('truetype');
    font-weight: 800;
    font-style: normal;
    font-display: swap;
    font-synthesis: none;
}

/* Averta font family (Speakup brand font, sourced from speakup.nl) - only regular/bold weights exist */
@font-face {
    font-family: 'Averta';
    src: url('/tabler/custom/fonts/averta/Averta-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Averta';
    src: url('/tabler/custom/fonts/averta/Averta-Bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* Spline Sans + Spline Sans Mono - CallerConnect's own brand kit fonts (SIL OFL,
   self-hosted per the brand kit's own guidance, see ~/docs/brand/typography/typography.md).
   Rubik (the kit's third/display font) is already covered by the Google Fonts @import above. */
@font-face {
    font-family: 'Spline Sans';
    src: url('/tabler/custom/fonts/spline-sans/spline-sans-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Spline Sans';
    src: url('/tabler/custom/fonts/spline-sans/spline-sans-600.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Spline Sans';
    src: url('/tabler/custom/fonts/spline-sans/spline-sans-700.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Spline Sans Mono';
    src: url('/tabler/custom/fonts/spline-sans-mono/spline-sans-mono-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Spline Sans Mono';
    src: url('/tabler/custom/fonts/spline-sans-mono/spline-sans-mono-500.woff2') format('woff2');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

/* Titillium Web - x2com customer brand font (sourced from x2com.nl), only regular/bold exist */
@font-face {
    font-family: 'Titillium Web';
    src: url('/tabler/custom/fonts/titillium-web/TitilliumWeb-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Titillium Web';
    src: url('/tabler/custom/fonts/titillium-web/TitilliumWeb-Bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* Open Sans - self-hosted weights from x2com.nl, layered on top of the Google Fonts
   @import above (which covers 400/500/600/700) so light (300) is also available and
   this specific customer's own asset is used for the weights they provided. */
@font-face {
    font-family: 'Open Sans';
    src: url('/tabler/custom/fonts/open-sans-local/OpenSans-Light.woff2') format('woff2');
    font-weight: 300;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Open Sans';
    src: url('/tabler/custom/fonts/open-sans-local/OpenSans-Regular.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Open Sans';
    src: url('/tabler/custom/fonts/open-sans-local/OpenSans-Bold.woff2') format('woff2');
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}

/* Extra theme-primary colors beyond Tabler's built-in 12 hues + inverted
   (public/tabler/dist/css/tabler-themes.css) - same [data-bs-theme-primary]
   mechanism, just --tblr-primary/--tblr-primary-rgb for a color Tabler doesn't ship. */
[data-bs-theme-primary="amber"] {
    --tblr-primary: #f59e0b;
    --tblr-primary-rgb: 245, 158, 11;
}

[data-bs-theme-primary="emerald"] {
    --tblr-primary: #10b981;
    --tblr-primary-rgb: 16, 185, 129;
}

[data-bs-theme-primary="rose"] {
    --tblr-primary: #f43f5e;
    --tblr-primary-rgb: 244, 63, 94;
}

[data-bs-theme-primary="slate"] {
    --tblr-primary: #475569;
    --tblr-primary-rgb: 71, 85, 105;
}

[data-bs-theme-primary="violet"] {
    --tblr-primary: #8b5cf6;
    --tblr-primary-rgb: 139, 92, 246;
}

[data-bs-theme-primary="coral"] {
    --tblr-primary: #eb4a61;
    --tblr-primary-rgb: 235, 74, 97;
}

/* CallerConnect brand kit exact colors (~/docs/brand/colors/palette.md) - Cyan Signal
   and Signal Blue are the brand's primary/hover pair, Live Orange is the accent/CTA
   color. Distinct from Tabler's own "cyan"/"azure"/"orange", which are close but not
   pixel-accurate to the brand. */
[data-bs-theme-primary="cyan-signal"] {
    --tblr-primary: #11b5e4;
    --tblr-primary-rgb: 17, 181, 228;
}

[data-bs-theme-primary="signal-blue"] {
    --tblr-primary: #1481ba;
    --tblr-primary-rgb: 20, 129, 186;
}

[data-bs-theme-primary="live-orange"] {
    --tblr-primary: #ff5f00;
    --tblr-primary-rgb: 255, 95, 0;
}

:root {
    --bs-body-bg: var(--tblr-bg-surface);
}

/* Figtree font family support for Tabler theme system */
html[data-bs-theme-font="figtree"] {
    --tblr-font-sans-serif: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="figtree"] body {
    font-family: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Vodafone font family support for Tabler theme system */
html[data-bs-theme-font="vodafone"] {
    --tblr-font-sans-serif: Vodafone, Arial, sans-serif;
}

html[data-bs-theme-font="vodafone"] body {
    font-family: Vodafone, Arial, sans-serif;
    font-size: 16px;
    -webkit-font-smoothing: subpixel-antialiased;
    -moz-osx-font-smoothing: auto;
    text-rendering: geometricPrecision;
    font-smooth: always;
    transform: translateZ(0);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
}

/* Averta font family support for Tabler theme system */
html[data-bs-theme-font="averta"] {
    --tblr-font-sans-serif: 'Averta', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="averta"] body {
    font-family: 'Averta', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* DM Sans font family support for Tabler theme system */
html[data-bs-theme-font="dm-sans"] {
    --tblr-font-sans-serif: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="dm-sans"] body {
    font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Roboto font family support for Tabler theme system */
html[data-bs-theme-font="roboto"] {
    --tblr-font-sans-serif: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="roboto"] body {
    font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Inter font family support for Tabler theme system */
html[data-bs-theme-font="inter"] {
    --tblr-font-sans-serif: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="inter"] body {
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Open Sans font family support for Tabler theme system */
html[data-bs-theme-font="open-sans"] {
    --tblr-font-sans-serif: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="open-sans"] body {
    font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Poppins font family support for Tabler theme system */
html[data-bs-theme-font="poppins"] {
    --tblr-font-sans-serif: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="poppins"] body {
    font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Montserrat font family support for Tabler theme system */
html[data-bs-theme-font="montserrat"] {
    --tblr-font-sans-serif: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="montserrat"] body {
    font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Nunito font family support for Tabler theme system */
html[data-bs-theme-font="nunito"] {
    --tblr-font-sans-serif: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="nunito"] body {
    font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Rubik font family support for Tabler theme system */
html[data-bs-theme-font="rubik"] {
    --tblr-font-sans-serif: 'Rubik', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="rubik"] body {
    font-family: 'Rubik', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Ubuntu font family support for Tabler theme system */
html[data-bs-theme-font="ubuntu"] {
    --tblr-font-sans-serif: 'Ubuntu', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="ubuntu"] body {
    font-family: 'Ubuntu', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Spline Sans font family support for Tabler theme system (CallerConnect brand: body) */
html[data-bs-theme-font="spline-sans"] {
    --tblr-font-sans-serif: 'Spline Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

html[data-bs-theme-font="spline-sans"] body {
    font-family: 'Spline Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}

/* Spline Sans Mono font family support for Tabler theme system (CallerConnect brand: meta/code) */
html[data-bs-theme-font="spline-sans-mono"] {
    --tblr-font-sans-serif: 'Spline Sans Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

html[data-bs-theme-font="spline-sans-mono"] body {
    font-family: 'Spline Sans Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* Titillium Web font family support for Tabler theme system */
html[data-bs-theme-font="titillium-web"] {
    --tblr-font-sans-serif: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="titillium-web"] body {
    font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Outfit font family support for Tabler theme system */
html[data-bs-theme-font="outfit"] {
    --tblr-font-sans-serif: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

html[data-bs-theme-font="outfit"] body {
    font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* Merriweather font family support for Tabler theme system (serif) */
html[data-bs-theme-font="merriweather"] {
    --tblr-font-sans-serif: 'Merriweather', Georgia, Cambria, 'Times New Roman', Times, serif;
}

html[data-bs-theme-font="merriweather"] body {
    font-family: 'Merriweather', Georgia, Cambria, 'Times New Roman', Times, serif;
}

/* Lora font family support for Tabler theme system (serif) */
html[data-bs-theme-font="lora"] {
    --tblr-font-sans-serif: 'Lora', Georgia, Cambria, 'Times New Roman', Times, serif;
}

html[data-bs-theme-font="lora"] body {
    font-family: 'Lora', Georgia, Cambria, 'Times New Roman', Times, serif;
}

/* Optional separate heading font (theme_font_heading), independent of the body font
   (data-bs-theme-font above). Each font sets --theme-heading-font-family (reusing the
   exact same stack as its body-font block); h1-h6 fall back to `inherit` (the body
   font) when no heading override is set - see WhitelabelConfiguration#theme_data_attributes. */
html[data-bs-theme-font-heading="sans-serif"] { --theme-heading-font-family: var(--tblr-font-sans-serif); }
html[data-bs-theme-font-heading="serif"] { --theme-heading-font-family: var(--tblr-font-serif); }
html[data-bs-theme-font-heading="monospace"] { --theme-heading-font-family: var(--tblr-font-monospace); }
html[data-bs-theme-font-heading="comic"] { --theme-heading-font-family: var(--tblr-font-comic); }
html[data-bs-theme-font-heading="figtree"] { --theme-heading-font-family: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="vodafone"] { --theme-heading-font-family: Vodafone, Arial, sans-serif; }
html[data-bs-theme-font-heading="averta"] { --theme-heading-font-family: 'Averta', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="dm-sans"] { --theme-heading-font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="roboto"] { --theme-heading-font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="inter"] { --theme-heading-font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="open-sans"] { --theme-heading-font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="poppins"] { --theme-heading-font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="montserrat"] { --theme-heading-font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="nunito"] { --theme-heading-font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="rubik"] { --theme-heading-font-family: 'Rubik', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="ubuntu"] { --theme-heading-font-family: 'Ubuntu', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="spline-sans"] { --theme-heading-font-family: 'Spline Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
html[data-bs-theme-font-heading="spline-sans-mono"] { --theme-heading-font-family: 'Spline Sans Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace; }
html[data-bs-theme-font-heading="titillium-web"] { --theme-heading-font-family: 'Titillium Web', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="outfit"] { --theme-heading-font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; }
html[data-bs-theme-font-heading="merriweather"] { --theme-heading-font-family: 'Merriweather', Georgia, Cambria, 'Times New Roman', Times, serif; }
html[data-bs-theme-font-heading="lora"] { --theme-heading-font-family: 'Lora', Georgia, Cambria, 'Times New Roman', Times, serif; }

h1, h2, h3, h4, h5, h6 {
    font-family: var(--theme-heading-font-family, inherit);
}

/* Set dropdown location */
.ts-dropdown {
    z-index: 10000;
}

/* Color swatch shown before the label in color-picker dropdowns
   (data-controller="color-select" - Primary Color, Sidebar Color). */
.color-select-swatch {
    display: inline-block;
    width: 1.1rem;
    height: 1.1rem;
    border-radius: 0.25rem;
    margin-right: 0.5rem;
    vertical-align: middle;
    /* Outset, not inset - an inset shadow eats into the small square from the
       edges, which visibly darkens already-dark colors more than light ones. */
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
}

.ts-dropdown .active {
    color: var(--tblr-dropdown-link-active-color);
    background-color: var(--tblr-dropdown-link-active-bg);
}

.ts-control, .ts-control input, .ts-dropdown {
    color: var(--bs-body-color);
}

.plugin-dropdown_input .dropdown-input {
    border: 0;
}

.plugin-dropdown_input .dropdown-input:focus  {
    outline-width: 0;
    outline: none;
    border: 0;
}

/* Page specific */
/* Services */
.card-header-avatar {
    margin-left: -5px;
    padding-right: 10px;
}

.card-img-start-service {
    /*border-top-left-radius: calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));*/
    border-bottom-left-radius: calc(var(--tblr-border-radius-lg) - (var(--tblr-border-width)));
    height: 180px !important;
    width: 160px !important;
}

/* Datatables */
#dt-search-0 {
    padding: .4375rem .75rem;
    font-family: var(--tblr-font-sans-serif), serif;
    font-size: .875rem;
    font-weight: 400;
    line-height: 1.4285714286;
}

.card-no-padding {
    padding-left: 0px !important;
    padding-right: 0px !important;
    padding-bottom: 0px !important;
    padding-top: 0px !important;
}

.dataTable thead {
    background-color: var(--tblr-card-cap-bg);
}

.dataTable thead tr th.dt-left {
    padding: var(--tblr-card-cap-padding-y) var(--tblr-card-cap-padding-x) !important;
}

#dt-length-0 {
    padding: .4375rem .75rem;
    min-width: 80px;
    font-family: var(--tblr-font-sans-serif), serif;
    font-size: .875rem;
    font-weight: 400;
    line-height: 1.4285714286;
}

.note-popover, .note-popover:visited {
    color: var(--tblr-secondary-rgb);
    text-decoration: none;
}

.note-popover:hover, .note-popover:active {
    color: var(--tblr-body-color);
    text-decoration: none;
}

table.dataTable {
    clear: both;
    margin-top: 0px !important;
    margin-bottom: 0px !important;
    max-width: none !important;
    border-collapse: separate !important;
    border-spacing: 0;
}

#call-detail-records-datatable_length label select , #call-detail-records-datatable_filter label input {
    padding: .4375rem 2.25rem .4375rem .75rem !important;
    font-size: .875rem !important;
    border: var(--tblr-border-width) solid var(--tblr-border-color) !important;
    border-radius: var(--tblr-border-radius) !important;
}

/* DataTable controls area background matching header style */
.py-3:has(table.dataTable),
.py-3:has(.dataTables_wrapper),
.dataTables_wrapper .py-3,
.card:has(table.dataTable) .py-3 {
    background-color: var(--tblr-bg-surface-tertiary);
}

/* Account context indicator in top navbar — absolutely centered on page */
.navbar-account-context {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    pointer-events: none;
}

/* Layout */
.navbar-selector {
    width: 300px;
}

.ts-dropdown-content {
    max-height: 300px;
    overflow-y: auto;
}

/* Remove underline from list-group navigation items on hover */
.list-group-item-action:hover {
    text-decoration: none;
}

/* Cursor pointer utility class */
.cursor-pointer {
    cursor: pointer;
}

/* Cursor move utility class for drag handles */
.cursor-move {
    cursor: move;
}

/* BetterStack Status Item Link */
.status-item-link {
    cursor: pointer;
    transition: background-color 0.15s ease-in-out;
}

.status-item-link:hover {
    background-color: var(--tblr-bg-surface-secondary) !important;
}

/* BetterStack Uptime Bar Component */
.uptime-bar-container {
    margin-top: 0.35rem;
    margin-bottom: 0;
}

/* Grid layout for daily bars */
.uptime-bars-grid {
    display: flex;
    gap: 2px;
    width: 100%;
}

.uptime-bar-item {
    flex: 1;
    min-width: 0;
}

.uptime-bar-single {
    display: block;
    width: 100%;
    height: 16px;
    border-radius: 2px;
    overflow: hidden;
    background-color: #e9ecef;
    position: relative;
}

.uptime-segment {
    display: block;
    height: 100%;
    width: 100%;
    transition: all 0.2s ease;
    cursor: pointer;
    position: relative;
}

.uptime-segment:hover {
    opacity: 0.9;
    transform: scaleY(1.2);
}

/* Uptime status colors */
.uptime-excellent {
    background-color: #2fb344; /* Green - 99.9%+ */
}

.uptime-good {
    background-color: #f59f00; /* Yellow - 99.0-99.9% */
}

.uptime-degraded {
    background-color: #fd7e14; /* Orange - <99% but not down */
}

.uptime-down {
    background-color: #d63939; /* Red - Down */
}

.uptime-unknown {
    background-color: #868e96; /* Gray - No data */
}

/* Availability percentage display */
.availability-percentage {
    font-size: 0.875rem;
    font-weight: 600;
    margin-left: 0.5rem;
}

.availability-label {
    font-size: 0.75rem;
    color: #6c757d;
    margin-bottom: 0.25rem;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

/* Responsive adjustments */
@media (max-width: 768px) {
    .uptime-bar-single {
        height: 12px;
    }

    .uptime-bars-grid {
        gap: 1px;
    }
}

/* Drag-and-drop styles for action reordering */

/* Drag handle column - left side of card */
.drag-handle-column {
    width: 40px !important;
    min-width: 40px !important;
    max-width: 40px !important;
    background-color: #f8f9fa !important;
    border-right: 1px solid #e6e7e9 !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: center !important;
    justify-content: center !important;
    border-radius: 4px 0 0 4px !important;
    flex-shrink: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
}

/* Drag handle - grip icon */
.drag-handle {
    cursor: grab;
    opacity: 0.4;
    transition: opacity 0.2s ease;
    color: var(--tblr-secondary);
}

.drag-handle:hover {
    opacity: 0.8;
}

.drag-handle:active {
    cursor: grabbing;
}

/* Ghost element - shows where item will be dropped */
.sortable-ghost {
    opacity: 0.4;
    background: var(--tblr-bg-surface-secondary);
    border: 2px dashed var(--tblr-primary);
}

/* Chosen element - item being dragged */
.sortable-chosen {
    cursor: grabbing;
}

/* Drag element - the element being dragged */
.sortable-drag {
    opacity: 0.8;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
    transform: rotate(3deg);
}

/* Fallback element for non-HTML5 browsers */
.sortable-fallback {
    opacity: 0.8;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

/* Action card during drag */
.action-card.sortable-chosen .drag-handle {
    opacity: 1;
}

/* Disable pointer events on delete/edit buttons during drag */
.sortable-chosen .btn-action,
.sortable-chosen .btn {
    pointer-events: none;
}
/* ========================================
   Turbo Frames + Pagy Tables Styling
   (for DataTables migration)
   ======================================== */

/* Ensure table cells are vertically centered (matches DataTables) */
.table-vcenter tbody td {
    vertical-align: middle !important;
}

/* Top controls area background (generic, works without DataTables) */
.card .card-body.py-3:has(+ .card-body.card-no-padding) {
    background-color: var(--tblr-bg-surface-tertiary);
}

/* ========================================
   Accounts Table Enhancements
   ======================================== */

/* Avatar badge for user counts - REMOVED */
/*
.avatar-status-badge {
    position: absolute;
    bottom: -2px;
    right: -2px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    font-size: 10px;
    font-weight: 600;
    line-height: 18px;
    color: white;
    background-color: var(--tblr-primary);
    border-radius: 10px;
    border: 2px solid white;
    text-align: center;
    z-index: 2;
}
*/

/* Service health indicator dots - REMOVED (replaced with Tabler badges) */
/*
.avatar-status-indicator {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    border: 2px solid white;
    z-index: 2;
}

.avatar-status-indicator.status-success {
    background-color: var(--tblr-success);
}

.avatar-status-indicator.status-warning {
    background-color: var(--tblr-warning);
}

.avatar-status-indicator.status-danger {
    background-color: var(--tblr-danger);
}

.avatar-status-indicator.status-secondary {
    background-color: var(--tblr-secondary);
}
*/

/* Clickable service avatars */
.avatar-list a {
    text-decoration: none;
}

.avatar-list .avatar:hover {
    opacity: 0.8;
    transition: opacity 0.2s ease;
}

/* Status-based row borders */
tr.row-status-active {
    border-left: 3px solid var(--tblr-success);
}

tr.row-status-setup {
    border-left: 3px solid var(--tblr-warning);
}

tr.row-status-suspended {
    border-left: 3px solid var(--tblr-orange);
}

tr.row-status-cancelled {
    border-left: 3px solid var(--tblr-danger);
}

/* Row hover effect */
.table-hover tbody tr:hover {
    background-color: var(--tblr-bg-surface-secondary);
    cursor: pointer;
    transition: background-color 0.15s ease;
}

/* Sortable column headers */
.sortable-header {
    cursor: pointer;
    user-select: none;
    position: relative;
    padding-right: 20px !important;
}

.sortable-header:hover {
    background-color: var(--tblr-bg-surface-secondary);
}

.sortable-header .sort-icon {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 12px;
    opacity: 0.5;
}

.sortable-header.sorted .sort-icon {
    opacity: 1;
}

/* Search result highlighting */
.search-highlight {
    background-color: var(--tblr-yellow);
    padding: 2px 0;
    font-weight: 600;
}

/* Quick actions (shown on row hover) - REMOVED */
/*
.quick-actions {
    opacity: 0;
    transition: opacity 0.2s ease;
    display: inline-flex;
    gap: 4px;
    margin-right: 8px;
}

tr:hover .quick-actions {
    opacity: 1;
}

.quick-actions .btn {
    padding: 2px 6px;
    font-size: 12px;
}
*/

/* Status filter buttons */
.status-filters {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.status-filter-btn {
    font-size: 0.875rem;
    padding: 0.375rem 0.75rem;
}

.status-filter-btn.active {
    background-color: var(--tblr-primary);
    color: white;
    border-color: var(--tblr-primary);
}

/* Expandable row indicator */
.expand-row-icon {
    cursor: pointer;
    transition: transform 0.2s ease;
}

.expand-row-icon.expanded {
    transform: rotate(90deg);
}

/* Expandable row content */
.expandable-row-content {
    background-color: var(--tblr-bg-surface-secondary);
    border-top: 1px solid var(--tblr-border-color);
    padding: 1rem;
}

/* Hierarchical account indicators */
.hierarchical-indent {
    margin-left: 1.5rem;
    position: relative;
}

.hierarchical-indent::before {
    content: '';
    position: absolute;
    left: -12px;
    top: 50%;
    width: 8px;
    height: 1px;
    background-color: var(--tblr-border-color);
}

/* ========================================
   Account Switcher Button Styling
   ======================================== */

/* Unstyled button for service avatar links with account switching */
.btn-link-unstyled {
    background: none !important;
    border: none !important;
    padding: 0 !important;
    margin: 0 !important;
    font: inherit !important;
    color: inherit !important;
    cursor: pointer !important;
    text-decoration: none !important;
    display: inline-block !important;
    line-height: inherit !important;
}

.btn-link-unstyled:hover,
.btn-link-unstyled:focus,
.btn-link-unstyled:active {
    background: none !important;
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
    color: inherit !important;
}

/* Ensure button_to form is also unstyled */
.btn-link-unstyled form {
    display: inline-block !important;
    margin: 0 !important;
    padding: 0 !important;
}

/* ========================================
   Dashboard Component Styling
   ======================================== */

/* Health Indicator Cards */
.health-indicator {
    border-radius: var(--tblr-border-radius);
    transition: all 0.2s ease;
    background-color: var(--tblr-bg-surface);
}

.health-indicator:hover {
    box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}

/* Timeline Component Styling */
.list-timeline {
    position: relative;
    padding-left: 2rem;
}

.list-timeline-item {
    position: relative;
    padding-bottom: 1.5rem;
}

.list-timeline-item:not(:last-child)::before {
    content: '';
    position: absolute;
    left: -1.5rem;
    top: 2rem;
    bottom: -0.5rem;
    width: 2px;
    background-color: rgba(var(--tblr-border-color-rgb), 1);
}

.list-timeline-icon {
    position: absolute;
    left: -2rem;
    top: 0;
    width: 2rem;
    height: 2rem;
    border-radius: 50%;
    background-color: var(--tblr-bg-surface);
    border: 2px solid rgba(var(--tblr-border-color-rgb), 1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
    z-index: 1;
}

.list-timeline-time {
    font-size: 0.75rem;
    margin-bottom: 0.25rem;
}

.list-timeline-title {
    font-weight: 600;
    margin-bottom: 0.25rem;
}

.list-timeline-content {
    padding-left: 0.5rem;
}

/* Action Items & Investigation Items Styling */
.list-group-item .avatar {
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.25rem;
}

/* Quick Stats Card Progress Bars */
.card-progress {
    height: 0.25rem;
}

/* Partner Dashboard Styling */
.partner-dashboard .h1 {
    font-size: 2.5rem;
    font-weight: 600;
}

.partner-dashboard .card-table thead {
    background-color: var(--tblr-bg-surface-secondary);
}

/* Dashboard Tab Navigation */
.nav-tabs-lg .nav-link {
    padding: 0.75rem 1.5rem;
    font-weight: 500;
}

/* Empty State Styling */
.empty {
    padding: 3rem 1rem;
}

.empty-icon {
    margin-bottom: 1rem;
}

/* Badge Enhancements for Dashboard */
.badge {
    font-weight: 500;
}

/* Responsive Dashboard Adjustments */
@media (max-width: 767.98px) {
    .health-indicator {
        margin-bottom: 1rem;
    }

    .list-timeline {
        padding-left: 1.5rem;
    }

    .list-timeline-icon {
        left: -1.75rem;
        width: 1.5rem;
        height: 1.5rem;
        font-size: 0.875rem;
    }

    .partner-dashboard .h1 {
        font-size: 2rem;
    }
}

/* Dashboard Chart Container */
.dashboard-chart {
    min-height: 300px;
    height: 100%;
}

/* ECharts Canvas Visibility Fix */
[data-controller="dashboard-echarts"] {
    position: relative;
    width: 100% !important;
}

[data-controller="dashboard-echarts"] canvas {
    display: block !important;
    position: relative !important;
    z-index: 1 !important;
    opacity: 1 !important;
    visibility: visible !important;
}

/* Status Indicator Colors */
.status-indicator {
    display: inline-block;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    margin-right: 0.5rem;
}

.status-indicator.operational {
    background-color: var(--tblr-green);
}

.status-indicator.degraded {
    background-color: var(--tblr-yellow);
}

.status-indicator.critical {
    background-color: var(--tblr-red);
}

/* ========================================
   Print Optimization for Support Cases
   ======================================== */

@media print {
    /* Hide navigation and non-essential elements */
    .navbar,
    .page-header,
    .d-print-none,
    .card-footer,
    .btn,
    button,
    .dropzone,
    .form-control,
    textarea,
    .dropdown {
        display: none !important;
    }

    /* Full width layout */
    .container-xl,
    .container {
        max-width: 100% !important;
        padding: 0 !important;
    }

    /* Remove margins and spacing */
    body {
        margin: 0;
        padding: 20px;
    }

    .page-body {
        padding: 0 !important;
    }

    /* Card styling for print */
    .card {
        border: 1px solid #ddd !important;
        page-break-inside: avoid;
        margin-bottom: 20px;
        box-shadow: none !important;
    }

    .card-header {
        background-color: #f8f9fa !important;
        border-bottom: 2px solid #ddd !important;
        padding: 10px 15px !important;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    .card-body {
        padding: 15px !important;
    }

    /* Case header card */
    .card-header h3 {
        font-size: 18px !important;
        margin: 0 !important;
    }

    /* Metadata grid */
    .row {
        display: flex !important;
        flex-wrap: wrap !important;
    }

    .col,
    .col-md-2,
    .col-md-3 {
        flex: 0 0 33.333% !important;
        max-width: 33.333% !important;
        padding: 10px !important;
    }

    /* Badge styling */
    .badge {
        border: 1px solid currentColor !important;
        padding: 2px 6px !important;
        font-size: 11px !important;
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* Timeline styling */
    .list-group-item {
        border: 1px solid #ddd !important;
        padding: 8px !important;
        page-break-inside: avoid;
    }

    /* Avatar icons */
    .avatar {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* File list */
    .list-group {
        border: 1px solid #ddd !important;
    }

    /* Chat bubbles */
    .chat-bubble {
        border: 1px solid #ddd !important;
        padding: 10px !important;
        margin-bottom: 10px !important;
        page-break-inside: avoid;
    }

    /* Links - show URL */
    a[href]:after {
        content: none !important; /* Don't show URLs after links for cleaner print */
    }

    /* Table styling */
    table {
        page-break-inside: auto;
    }

    tr {
        page-break-inside: avoid;
        page-break-after: auto;
    }

    /* Print header with case number */
    .card:first-child::before {
        content: "Support Case Report";
        display: block;
        font-size: 24px;
        font-weight: bold;
        margin-bottom: 10px;
        padding-bottom: 10px;
        border-bottom: 2px solid #000;
    }

    /* Preserve colors for status indicators */
    .bg-blue,
    .bg-blue-lt,
    .text-blue,
    .bg-yellow,
    .bg-yellow-lt,
    .text-yellow,
    .bg-green,
    .bg-green-lt,
    .text-green,
    .bg-red,
    .bg-red-lt,
    .text-red {
        -webkit-print-color-adjust: exact !important;
        print-color-adjust: exact !important;
    }

    /* SVG icons */
    svg {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* Ensure single column layout for main content */
    .col-8,
    .col-4 {
        flex: 0 0 100% !important;
        max-width: 100% !important;
    }

    /* Activity timeline compact for print */
    .list-timeline-item {
        padding-bottom: 0.5rem !important;
    }

    /* Hide upload areas */
    .dz-message,
    .dropzone {
        display: none !important;
    }
}

/* ========================================
   Support Case File List Hover Effects
   ======================================== */

/* File list item default state */
.file-list-item .file-download-btn {
    display: none;
}

.file-list-item .file-size {
    display: block;
}

/* File list item hover state */
.file-list-item:hover .file-download-btn {
    display: block;
}

.file-list-item:hover .file-size {
    display: none;
}

/* Smooth transition for hover effect */
.file-list-item {
    transition: background-color 0.2s ease;
}

/* Condensed file list rows */
.file-list-item {
    padding: 0.5rem 1rem !important;
}

.file-list-item .d-flex {
    min-height: 32px;
}

/* Smaller icon size for condensed layout */
.file-list-item i.ti {
    font-size: 1.5rem;
}

/* ========================================
   Table Dropdown Overflow Fix
   ======================================== */

/* Fix dropdown menus being clipped by table-responsive overflow */
/* When table has few entries, dropdowns should appear above the table */
.card .table-responsive {
    overflow: visible;
}


/* Ensure dropdown menus always appear on top */
.table .dropdown-menu {
    z-index: 1050;
    position: absolute;
}

/* For tables within cards, ensure card doesn't clip dropdown */
.card-body.card-no-padding {
    overflow: visible;
}

/* Ensure btn-list dropdowns in tables display correctly */
.table .btn-list .dropdown {
    position: static;
}

.table .btn-list .dropdown-menu {
    position: absolute;
    z-index: 1050;
}

/* ========================================
   Service Avatar Image Mode
   ======================================== */

/* When using mode: :image, the avatar contains an img tag instead of background-image */
.avatar .avatar-img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    border-radius: inherit;
}

/* ========================================
   TomSelect Dark Mode
   ======================================== */

/* ========================================
   Dark Mode: Form Controls
   ======================================== */

[data-bs-theme="dark"] .form-control:disabled,
[data-bs-theme="dark"] .form-control[readonly] {
    background-color: var(--tblr-bg-surface-secondary) !important;
    color: var(--tblr-body-color) !important;
    border-color: var(--tblr-border-color) !important;
    opacity: 0.7;
}

[data-bs-theme="dark"] .form-select:disabled {
    background-color: var(--tblr-bg-surface-secondary) !important;
    color: var(--tblr-body-color) !important;
    border-color: var(--tblr-border-color) !important;
    opacity: 0.7;
}

[data-bs-theme="dark"] .ts-control {
    background-color: var(--tblr-bg-forms) !important;
    border-color: var(--tblr-border-color) !important;
    color: var(--tblr-body-color) !important;
}

[data-bs-theme="dark"] .ts-control input {
    color: var(--tblr-body-color) !important;
}

[data-bs-theme="dark"] .ts-dropdown,
[data-bs-theme="dark"] .ts-dropdown.single {
    background-color: var(--tblr-bg-surface) !important;
    border-color: var(--tblr-border-color) !important;
    color: var(--tblr-body-color) !important;
}

[data-bs-theme="dark"] .ts-dropdown .ts-dropdown-content .option,
[data-bs-theme="dark"] .ts-dropdown .ts-dropdown-content .optgroup-header,
[data-bs-theme="dark"] .ts-dropdown .ts-dropdown-content .no-results {
    color: var(--tblr-body-color) !important;
}

[data-bs-theme="dark"] .ts-dropdown .dropdown-input,
[data-bs-theme="dark"] .ts-dropdown .dropdown-input input {
    background-color: var(--tblr-bg-surface-secondary) !important;
    color: var(--tblr-body-color) !important;
    border-color: var(--tblr-border-color) !important;
}

[data-bs-theme="dark"] .ts-dropdown .active {
    background-color: var(--tblr-dropdown-link-active-bg) !important;
    color: var(--tblr-dropdown-link-active-color) !important;
}

/* ========================================
   TomSelect Height Normalization
   ======================================== */

/* Match TomSelect control height to standard form-select (40px) */
.ts-wrapper.single .ts-control {
    padding: .625rem .75rem !important;
    min-height: 0 !important;
    height: 40px;
    line-height: 1.4285714286;
    display: flex;
    align-items: center;
}

/* Ensure input inside TomSelect doesn't add extra height */
.ts-wrapper.single .ts-control input {
    min-height: 0 !important;
    height: auto !important;
    padding: 0 !important;
    line-height: inherit;
}

/* Remove extra padding from selected item */
.ts-wrapper.single .ts-control > .item {
    padding: 0;
    margin: 0;
    line-height: inherit;
}


/* ========================================
   Edge-to-edge Table Container
   ======================================== */

/* Extends table to card edges - use on .table-responsive inside card-body */
.table-edge {
    margin: 0 -20px -17px -20px;
    border-top: 1px solid var(--tblr-border-color);
}

/* Compensate first/last cell padding to align with card-body content */
.table-edge .table th:first-child,
.table-edge .table td:first-child {
    padding-left: 20px;
}

.table-edge .table th:last-child,
.table-edge .table td:last-child {
    padding-right: 20px;
}

/* Remove border from last row */
.table-edge .table tbody tr:last-child td {
    border-bottom: none;
}

/* Modal variant — extends table to the modal-body edges.
   Tabler's .modal-body default padding is 1.5rem (24px); the negative
   margins cancel that so the table flush-mounts to the modal walls. The
   bottom margin also cancels the modal-body's bottom padding so the last
   row sits tight against the modal-footer divider. */
.table-edge-modal {
    margin: 0 -1.5rem -1.5rem -1.5rem;
    border-top: 1px solid var(--tblr-border-color);
}

/* First column holds the service-brand avatar — sit it closer to the
   modal's left wall (1rem vs 1.5rem) so the icon reads as leading art
   rather than being tucked behind the action name column. The
   neighbouring (second) column gets explicit left padding so the action
   name doesn't butt up against the avatar. */
.table-edge-modal .table th:first-child,
.table-edge-modal .table td:first-child {
    padding-left: 1rem;
    padding-right: 0;
}

.table-edge-modal .table th:nth-child(2),
.table-edge-modal .table td:nth-child(2) {
    padding-left: 0.75rem;
}

.table-edge-modal .table th:last-child,
.table-edge-modal .table td:last-child {
    padding-right: 1.5rem;
}

.table-edge-modal .table tbody tr:last-child td {
    border-bottom: none;
}

/* ========================================
   Invoice-Style Order Table
   Aligns cell content with card-lg body padding
   while table borders stretch edge-to-edge
   ======================================== */

table.table-invoice th:first-child,
table.table-invoice td:first-child {
    padding-left: 3.5rem !important;
}

table.table-invoice th:last-child,
table.table-invoice td:last-child {
    padding-right: 3.5rem !important;
}

/* ========================================
   Reapop-Style Notifications
   ======================================== */

/* Container - fixed bottom-right */
.reapop-container {
    position: fixed;
    z-index: 9999;
    bottom: 20px;
    right: 20px;
    max-width: 400px;
    display: flex;
    flex-direction: column-reverse;
    gap: 12px;
    pointer-events: none;
}

/* Notification card */
.reapop-notification {
    display: flex;
    width: 350px;
    position: relative;
    border-radius: 5px;
    border: 1px solid rgba(0, 0, 0, 0.1);
    box-shadow: rgba(0, 0, 0, 0.3) 0px 3px 8px 2px;
    background-color: rgb(47, 47, 54);
    color: rgb(255, 255, 255);
    animation: reapop-slide-in 0.3s ease-out;
    pointer-events: all;
}

/* Status icons */
.reapop-notification-icon {
    width: 20px;
    height: 20px;
    margin: 12px 0 12px 15px;
    flex-shrink: 0;
    align-self: flex-start;
}

/* Icon colors by variant */
.reapop-notification--success .reapop-notification-icon {
    color: rgb(50, 211, 139);
}

.reapop-notification--info .reapop-notification-icon {
    color: rgb(65, 146, 255);
}

.reapop-notification--warning .reapop-notification-icon {
    color: rgb(255, 193, 7);
}

.reapop-notification--danger .reapop-notification-icon {
    color: rgb(239, 68, 68);
}

/* Image avatar (when present, replaces icon) */
.reapop-notification-image-container {
    padding: 10px 0 10px 15px;
    flex-shrink: 0;
}

.reapop-notification-image {
    display: inline-flex;
    border-radius: 50%;
    width: 40px;
    height: 40px;
    background-size: cover;
    background-position: center;
}

/* Content area */
.reapop-notification-meta {
    flex: 1;
    padding: 10px 15px;
    min-width: 0;
}

.reapop-notification-title {
    margin: 0 0 4px 0;
    font-size: 14px;
    font-weight: 700;
    line-height: 1.42857;
    color: rgb(255, 255, 255);
}

.reapop-notification-message {
    margin: 0;
    font-size: 14px;
    line-height: 1.42857;
    color: rgb(185, 194, 203);
}

/* Dismiss button */
.reapop-notification-dismiss {
    width: 12px;
    height: 12px;
    margin: 14px 10px;
    cursor: pointer;
    color: rgb(185, 194, 203);
    flex-shrink: 0;
    transition: color 0.2s ease;
}

.reapop-notification-dismiss:hover {
    color: rgb(255, 255, 255);
}

/* Action buttons container */
.reapop-notification-buttons {
    display: flex;
    flex-direction: column;
    border-left: 1px solid rgb(70, 76, 95);
    flex-shrink: 0;
}

.reapop-notification-button {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    min-height: 40px;
    min-width: 90px;
    max-width: 150px;
    padding: 0 15px;
    background: rgb(47, 47, 54);
    border: none;
    color: rgb(255, 255, 255);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s ease;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.reapop-notification-buttons .reapop-notification-button:not(:last-child) {
    border-bottom: 1px solid rgb(70, 76, 95);
}

.reapop-notification-button:hover {
    background-color: rgb(60, 60, 70);
    color: rgb(255, 255, 255);
    text-decoration: none;
}

/* Notification without buttons - ensure dismiss is visible */
.reapop-notification:not(:has(.reapop-notification-buttons)) .reapop-notification-dismiss {
    margin-right: 15px;
}

/* Animations */
@keyframes reapop-slide-in {
    from {
        opacity: 0;
        transform: translateX(100%);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes reapop-slide-out {
    from {
        opacity: 1;
        transform: translateX(0);
    }
    to {
        opacity: 0;
        transform: translateX(100%);
    }
}

.reapop-notification.dismissing {
    animation: reapop-slide-out 0.3s ease-in forwards;
}

/* Respect reduced motion preference - instant transitions instead of animations */
@media (prefers-reduced-motion: reduce) {
    .reapop-notification {
        animation: none;
    }

    .reapop-notification.dismissing {
        animation: none;
        opacity: 0;
        transition: opacity 0.1s ease-out;
    }
}


/* Table Export Dropdown - hide Bootstrap dropdown arrow for icon-only button */
.card-actions .btn-action.dropdown-toggle::after {
    display: none;
}

/* Card with sidebar layout - uniform minimum height with footer at bottom */
.card-sidebar-layout {
    min-height: 800px;
    display: flex;
    flex-direction: column;
}

/* Form wrapper grows to fill space */
.card-sidebar-layout > form {
    flex: 1;
    display: flex;
    flex-direction: column;
}

/* Row inside form grows */
.card-sidebar-layout > form > .row {
    flex: 1;
}

/* Direct row child (no form wrapper - e.g., security page) grows */
.card-sidebar-layout > .row {
    flex: 1;
}

/* Content column fills height */
.card-sidebar-layout .row > .col-12.col-md-9 {
    min-height: 100%;
}

/* Footer stays at bottom */
.card-sidebar-layout > .card-footer,
.card-sidebar-layout > form > .card-footer {
    margin-top: auto;
}

/* ========================================
   Setup Wizard - Multi-step Onboarding
   ======================================== */

/* Wizard Progress Bar Container */
.wizard-progress {
    background-color: var(--tblr-bg-surface);
    border-bottom: 1px solid var(--tblr-border-color);
    position: sticky;
    top: 0;
    z-index: 1000;
}

.wizard-progress .steps {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    align-items: flex-start;
}

/* Individual Step */
.wizard-progress .step {
    flex: 1;
    text-align: center;
    position: relative;
    opacity: 0.5;
    transition: opacity 0.2s ease;
}

.wizard-progress .step.active {
    opacity: 1;
    font-weight: 600;
}

.wizard-progress .step.completed {
    opacity: 0.8;
}

.wizard-progress .step.skipped {
    opacity: 0.4;
    text-decoration: line-through;
}

/* Step Number Circle */
.wizard-progress .step-number {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    background-color: var(--tblr-primary);
    color: white;
    margin-bottom: 0.5rem;
    font-weight: 600;
    transition: background-color 0.2s ease;
}

.wizard-progress .step.completed .step-number {
    background-color: var(--tblr-success);
}

.wizard-progress .step.skipped .step-number {
    background-color: var(--tblr-secondary);
}

/* Step Title */
.wizard-progress .step-title {
    display: block;
    font-size: 0.875rem;
    margin-top: 0.5rem;
}

/* Progress Percentage Bar */
.wizard-progress .progress {
    height: 4px;
    margin-bottom: 0;
    background-color: var(--tblr-bg-surface-tertiary);
}

.wizard-progress .progress-bar {
    background-color: var(--tblr-primary);
    transition: width 0.3s ease;
}

/* Wizard Step Content Area */
.wizard-step-content {
    min-height: 500px;
    padding: 2rem 0;
}

/* Wizard Navigation Footer */
.wizard-footer {
    background-color: var(--tblr-bg-surface);
    border-top: 1px solid var(--tblr-border-color);
    position: sticky;
    bottom: 0;
    z-index: 1000;
    padding: 1rem 0;
}

/* Skip Button Styling */
.wizard-footer .btn-link {
    text-decoration: none;
    padding: 0.5rem 1rem;
    color: var(--tblr-secondary);
    transition: color 0.2s ease;
}

.wizard-footer .btn-link:hover {
    text-decoration: underline;
    color: var(--tblr-body-color);
}

/* Wizard Feature Cards */
.wizard-feature-card {
    height: 100%;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.wizard-feature-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

/* Wizard Carrier Option Cards */
.wizard-carrier-option {
    cursor: pointer;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.wizard-carrier-option:hover {
    border-color: var(--tblr-primary);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.wizard-carrier-option.selected {
    border-color: var(--tblr-primary);
    border-width: 2px;
    background-color: var(--tblr-primary-lt);
}

/* Progressive Disclosure - Service Connection Cards */
.wizard-services-connection .card.border-dashed {
    border-style: dashed;
    border-width: 2px;
    background-color: var(--tblr-bg-surface-secondary);
}

/* Responsive Wizard Adjustments */
@media (max-width: 767.98px) {
    .wizard-progress .steps {
        gap: 0.5rem;
    }

    .wizard-progress .step-number {
        width: 2rem;
        height: 2rem;
        font-size: 0.875rem;
    }

    .wizard-progress .step-title {
        font-size: 0.75rem;
    }

    .wizard-step-content {
        padding: 1rem 0;
    }

    .wizard-footer {
        padding: 0.75rem 0;
    }
}

/* ========================================
   Step Indicator - Animated Progress Line
   ======================================== */

/* Container for animated step indicator - use inner wrapper for the line */
.steps-animated {
    position: relative;
}

/* Progress line wrapper - positioned behind step circles */
.steps-animated .step-progress-line {
    position: absolute;
    top: calc(var(--tblr-steps-dot-size, 1.5rem) / 2);
    left: var(--line-start, 8.33%);
    right: var(--line-start, 8.33%);
    height: 2px;
    margin-top: -1px;
    background-color: var(--tblr-border-color);
    z-index: 0;
    pointer-events: none;
}

/* Green progress overlay inside the line wrapper */
.steps-animated .step-progress-line::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: var(--progress-width, 0%);
    background-color: var(--tblr-green);
    transition: width 0.8s ease-out;
}

/* Step items need to be positioned above the line */
.steps-animated .step-item {
    position: relative;
    z-index: 1;
}

/* Override Tabler's step-item::after connector lines (hide them, we draw our own) */
.steps-animated.steps-counter .step-item::after {
    display: none;
}

/* Step counter circle styling */
.steps-animated.steps-counter .step-item::before {
    transition: transform 0.3s ease-out, box-shadow 0.3s ease-out;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
}

/* Increase gap between circle and label (default is 1.5rem = dot size) */
.steps-animated.steps-counter .step-item {
    padding-top: 1.75rem;
}


/* Future/inactive step circles: solid grey background */
.steps-animated.steps-green .step-item:not(.step-completed):not(.step-current):not(.step-skipped):not(.active)::before {
    background-color: var(--tblr-border-color);
    color: #fff;
}

/* Make step links cover the entire step item (circle + label) */
.steps-animated .step-item .step-link {
    display: block;
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}
.steps-animated .step-item .step-link:hover {
    text-decoration: none;
    color: inherit;
}

/* Pop animation for step counter */
@keyframes step-pop {
    0% {
        transform: scale(1);
    }
    50% {
        transform: scale(1.3);
    }
    100% {
        transform: scale(1);
    }
}

/* Apply pop animation when step becomes active */
.steps-animated .step-item.step-popping::before {
    animation: step-pop 0.4s ease-out;
}

/* Green glow effect on active step circle */
.steps-animated.steps-green .step-item.active::before {
    box-shadow: 0 0 0 4px rgba(47, 179, 68, 0.25);
}

/* Completed step: green fill with checkmark */
.steps-animated.steps-green .step-item.step-completed::before {
    background-color: var(--tblr-green);
    border-color: var(--tblr-green);
    color: #fff;
    content: '\ea5e'; /* ti-check */
    font-family: 'tabler-icons';
    font-size: 0.75rem;
}
.steps-animated.steps-green .step-item.step-completed {
    color: var(--tblr-green);
    font-weight: 500;
}
.steps-animated.steps-green .step-item.step-completed:hover::before {
    box-shadow: 0 0 0 3px rgba(47, 179, 68, 0.2);
}

/* Skipped step: reachable but not completed — white bg, green border, dash icon */
.steps-animated.steps-green .step-item.step-skipped::before {
    background-color: var(--tblr-bg-surface);
    border: 2px solid var(--tblr-green);
    box-sizing: border-box;
    color: var(--tblr-green);
    content: '\eb55'; /* ti-minus */
    font-family: 'tabler-icons';
    font-size: 0.75rem;
    z-index: 2;
}
.steps-animated.steps-green .step-item.step-skipped {
    color: var(--tblr-secondary);
}
.steps-animated.steps-green .step-item.step-skipped:hover::before {
    box-shadow: 0 0 0 3px rgba(47, 179, 68, 0.2);
}

/* Current step (wizard frontier) — white bg, green border */
.steps-animated.steps-green .step-item.step-current::before {
    background-color: var(--tblr-bg-surface);
    border: 2px solid var(--tblr-green);
    box-sizing: border-box;
    color: var(--tblr-green);
    z-index: 2;
}
.steps-animated.steps-green .step-item.step-current {
    color: var(--tblr-green);
    font-weight: 600;
}

/* ========================================
   Assignment Rule Cards (Global Styling)
   Used in Directory Service rules management
   and Setup Wizard suggested rules
   ======================================== */

.assignment-rule-card {
    transition: all 0.2s ease;
    background-color: var(--tblr-bg-surface-tertiary);
    border: 2px solid var(--tblr-border-color) !important;
}

.assignment-rule-card:hover {
    border-color: var(--tblr-primary) !important;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

/* Dark-mode: lean on a slightly lighter surface over the page bg so the card
   still reads as a distinct panel, and use an explicitly visible border. */
[data-bs-theme="dark"] .assignment-rule-card {
    background-color: #1F2937;
    border-color: rgba(255, 255, 255, 0.12) !important;
}
[data-bs-theme="dark"] .assignment-rule-card:hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}

/* ========================================
   Segment Tree Sortable (Drag & Drop)
   ======================================== */

/* Sortable containers for segment and product items */
.segment-sortable,
.product-sortable {
    min-height: 10px;
}

/* When items are being dragged, highlight potential drop zones */
.segment-sortable.sortable-hover,
.product-sortable.sortable-hover {
    background-color: var(--tblr-primary-lt);
    border-radius: var(--tblr-border-radius);
}

/* Segment item specific drag handle */
.segment-item .drag-handle {
    padding: 0.25rem;
    font-size: 1rem;
}

.segment-item .drag-handle:hover,
.product-item .drag-handle:hover {
    opacity: 1;
    color: var(--tblr-primary);
}

/* Product item specific drag handle */
.product-item .drag-handle {
    padding: 0.25rem;
    font-size: 0.875rem;
}

/* EasyMDE Markdown Editor - Tabler integration */
.EasyMDEContainer {
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
}

.EasyMDEContainer .editor-toolbar {
    border-top: none;
    border-left: none;
    border-right: none;
    border-bottom: 1px solid var(--tblr-border-color);
    background-color: var(--tblr-bg-surface-tertiary);
    border-radius: var(--tblr-border-radius) var(--tblr-border-radius) 0 0;
}

.EasyMDEContainer .editor-toolbar button {
    color: var(--tblr-body-color);
}

.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar button.active {
    background-color: var(--tblr-bg-surface-secondary);
    border-color: var(--tblr-border-color);
}

.EasyMDEContainer .CodeMirror {
    border: none;
    border-radius: 0 0 var(--tblr-border-radius) var(--tblr-border-radius);
    background-color: var(--tblr-bg-surface);
    color: var(--tblr-body-color);
    font-size: 0.875rem;
    padding: 0.5rem;
}

.EasyMDEContainer .CodeMirror-cursor {
    border-left-color: var(--tblr-body-color);
}

.EasyMDEContainer .editor-preview,
.EasyMDEContainer .editor-preview-side {
    background-color: var(--tblr-bg-surface);
    color: var(--tblr-body-color);
    font-size: 0.875rem;
    padding: 1rem;
}

.EasyMDEContainer .editor-preview-side {
    border-left: 1px solid var(--tblr-border-color);
}

.EasyMDEContainer .editor-toolbar.disabled-for-preview button:not(.no-disable) {
    opacity: 0.4;
}

.EasyMDEContainer .CodeMirror .CodeMirror-selected {
    background: var(--tblr-primary-lt);
}

.EasyMDEContainer .editor-toolbar i.separator {
    border-left-color: var(--tblr-border-color);
}

/* Markdown rendered content */
.markdown-content h1,
.markdown-content h2,
.markdown-content h3,
.markdown-content h4 {
    margin-top: 1rem;
    margin-bottom: 0.5rem;
}

.markdown-content h1:first-child,
.markdown-content h2:first-child,
.markdown-content h3:first-child,
.markdown-content h4:first-child {
    margin-top: 0;
}

.markdown-content p {
    margin-bottom: 0.5rem;
}

.markdown-content ul,
.markdown-content ol {
    padding-left: 1.5rem;
    margin-bottom: 0.5rem;
}

.markdown-content li {
    margin-bottom: 0.25rem;
}

.markdown-content hr {
    margin: 1rem 0;
}

.markdown-content code {
    padding: 0.125rem 0.25rem;
    background-color: var(--tblr-bg-surface-tertiary);
    border-radius: 3px;
    font-size: 0.875em;
}

.markdown-content pre {
    padding: 0.75rem;
    background-color: var(--tblr-bg-surface-tertiary);
    border-radius: var(--tblr-border-radius);
    overflow-x: auto;
}

.markdown-content pre code {
    padding: 0;
    background: none;
}

.markdown-content blockquote {
    border-left: 3px solid var(--tblr-primary);
    padding-left: 1rem;
    margin-left: 0;
    color: var(--tblr-secondary);
}

.markdown-content table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 0.5rem;
}

.markdown-content table th,
.markdown-content table td {
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--tblr-border-color);
}

.markdown-content table th {
    background-color: var(--tblr-bg-surface-tertiary);
    font-weight: 600;
}

.markdown-content a {
    color: var(--tblr-primary);
}

/* ========================================
   QR Code Container — constrain SVG to parent
   ======================================== */

.qr-code-container svg {
    width: 100%;
    height: 100%;
}

/* ========================================
   OTP Input with Visual Digit Slots
   ======================================== */

.otp-container {
    position: relative;
    display: inline-flex;
    width: 282px;  /* 6 slots x 42px + 5 gaps x 6px */
    height: 56px;
    overflow: clip;
}

.otp-field {
    position: absolute;
    top: 0;
    left: 0;
    width: calc(100% + 48px);  /* extra slot-step to absorb trailing letter-spacing */
    height: 100%;
    background: transparent;
    border: none;
    outline: none;
    font-family: var(--tblr-font-monospace);
    font-size: 1.5rem;
    font-weight: 600;
    /* slot step (42 + 6 = 48px) minus one character width centers each digit */
    letter-spacing: calc(48px - 1ch);
    padding-left: calc((42px - 1ch) / 2);
    color: var(--tblr-body-color);
    z-index: 1;
    caret-color: var(--tblr-primary);
}

.otp-field.caret-hidden {
    caret-color: transparent;
}

.otp-field::selection {
    background: transparent;
}

.otp-slots {
    display: flex;
    gap: 6px;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.otp-slot {
    flex: 0 0 42px;
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    background-color: var(--tblr-bg-surface);
    transition: border-color 0.15s;
}

/* Highlight slots when input is focused */
.otp-field:focus ~ .otp-slots .otp-slot {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 1px rgba(var(--tblr-primary-rgb), 0.15);
}

/* ============================================================
   PARTNER SIDEBAR — Left-side, dark theme, persistent + collapsed
   ============================================================ */

/* Dark sidebar theme (matches Tabler navbar-dark pattern) */
/* Sidebar dimensions — defined at :root so siblings (.page-wrapper) can access them */
:root {
    --sidebar-width: 420px;
    --sidebar-collapsed-width: 5rem; /* avatar-md (2.5rem) + left/right padding (1.25rem each) */
}

.partner-sidebar,
.sidebar-flap--mobile {
    --sidebar-bg: var(--tblr-bg-surface-dark);
    --sidebar-color: rgba(255, 255, 255, 0.7);
    --sidebar-color-hover: rgba(255, 255, 255, 0.85);
    --sidebar-border-color: rgba(255, 255, 255, 0.1);
    color: var(--sidebar-color);
}

/* Dark mode: slightly lighter surface for sidebar */
[data-bs-theme="dark"] .partner-sidebar,
[data-bs-theme="dark"] .sidebar-flap--mobile {
    --sidebar-bg: var(--tblr-bg-surface);
    --sidebar-border-color: var(--tblr-border-color);
}

/* Optional sidebar background color (theme_sidebar_color) - dedicated muted/
   desaturated palette (WhitelabelConfiguration::THEME_SIDEBAR_COLORS), NOT
   THEME_BASES or THEME_PRIMARIES. THEME_BASES's darkest shades all converge to
   near-identical near-black regardless of hue (slate/gray were indistinguishable);
   THEME_PRIMARIES is too saturated/bold for a sidebar surface. Spans near-black
   through medium-light so the existing white sidebar text (--sidebar-color) stays
   legible against every option. Unset falls through to the default --sidebar-bg above. */
[data-bs-theme-sidebar-color="ash"] .partner-sidebar, [data-bs-theme-sidebar-color="ash"] .sidebar-flap--mobile { --sidebar-bg: #52525b; }
[data-bs-theme-sidebar-color="charcoal"] .partner-sidebar, [data-bs-theme-sidebar-color="charcoal"] .sidebar-flap--mobile { --sidebar-bg: #27272a; }
[data-bs-theme-sidebar-color="denim"] .partner-sidebar, [data-bs-theme-sidebar-color="denim"] .sidebar-flap--mobile { --sidebar-bg: #2c4a6e; }
[data-bs-theme-sidebar-color="forest"] .partner-sidebar, [data-bs-theme-sidebar-color="forest"] .sidebar-flap--mobile { --sidebar-bg: #2d4a3a; }
[data-bs-theme-sidebar-color="graphite"] .partner-sidebar, [data-bs-theme-sidebar-color="graphite"] .sidebar-flap--mobile { --sidebar-bg: #343438; }
[data-bs-theme-sidebar-color="ink"] .partner-sidebar, [data-bs-theme-sidebar-color="ink"] .sidebar-flap--mobile { --sidebar-bg: #0a1628; }
[data-bs-theme-sidebar-color="mist"] .partner-sidebar, [data-bs-theme-sidebar-color="mist"] .sidebar-flap--mobile { --sidebar-bg: #64748b; }
[data-bs-theme-sidebar-color="navy"] .partner-sidebar, [data-bs-theme-sidebar-color="navy"] .sidebar-flap--mobile { --sidebar-bg: #1e3a5f; }
[data-bs-theme-sidebar-color="onyx"] .partner-sidebar, [data-bs-theme-sidebar-color="onyx"] .sidebar-flap--mobile { --sidebar-bg: #0a0a0b; }
[data-bs-theme-sidebar-color="petrol"] .partner-sidebar, [data-bs-theme-sidebar-color="petrol"] .sidebar-flap--mobile { --sidebar-bg: #0c6481; }
[data-bs-theme-sidebar-color="plum"] .partner-sidebar, [data-bs-theme-sidebar-color="plum"] .sidebar-flap--mobile { --sidebar-bg: #3d2c4a; }
[data-bs-theme-sidebar-color="pulse-blue"] .partner-sidebar, [data-bs-theme-sidebar-color="pulse-blue"] .sidebar-flap--mobile { --sidebar-bg: #001021; }
[data-bs-theme-sidebar-color="pulse-surface"] .partner-sidebar, [data-bs-theme-sidebar-color="pulse-surface"] .sidebar-flap--mobile { --sidebar-bg: #0f2c42; }
[data-bs-theme-sidebar-color="slate"] .partner-sidebar, [data-bs-theme-sidebar-color="slate"] .sidebar-flap--mobile { --sidebar-bg: #334155; }
[data-bs-theme-sidebar-color="steel"] .partner-sidebar, [data-bs-theme-sidebar-color="steel"] .sidebar-flap--mobile { --sidebar-bg: #46586b; }
[data-bs-theme-sidebar-color="stone"] .partner-sidebar, [data-bs-theme-sidebar-color="stone"] .sidebar-flap--mobile { --sidebar-bg: #44403c; }
[data-bs-theme-sidebar-color="taupe"] .partner-sidebar, [data-bs-theme-sidebar-color="taupe"] .sidebar-flap--mobile { --sidebar-bg: #57534e; }
[data-bs-theme-sidebar-color="teal"] .partner-sidebar, [data-bs-theme-sidebar-color="teal"] .sidebar-flap--mobile { --sidebar-bg: #1f4d4d; }
[data-bs-theme-sidebar-color="wine"] .partner-sidebar, [data-bs-theme-sidebar-color="wine"] .sidebar-flap--mobile { --sidebar-bg: #4a2a30; }

.partner-sidebar .subheader {
    color: rgba(255, 255, 255, 0.5);
}

[data-bs-theme="dark"] .partner-sidebar .subheader {
    color: var(--tblr-secondary);
}

/* Desktop (lg+): Persistent fixed sidebar on LEFT */
@media (min-width: 992px) {
    /* Tabler ships `:host,:root { margin-left: calc(100vw - 100%) }` at this exact
       breakpoint — it shifts the whole document right by the live scrollbar width
       to keep centred pages from jumping when a scrollbar appears. That shift moves
       everything in flow (#site-header, .page-wrapper) but NOT the sidebar, which is
       position:fixed and so is laid out against the initial containing block. The
       difference showed up as a scrollbar-wide gap between the sidebar and the
       content. Cancel the shift here and let the sidebar offsets below be plain
       constants; scrollbar-gutter keeps Tabler's no-jump intent without displacing
       the document. */
    :root {
        margin-left: 0;
        scrollbar-gutter: stable;
    }

    .partner-sidebar {
        position: fixed !important;
        top: 0;
        left: 0;
        bottom: 0;
        width: var(--sidebar-width);
        border-right: 1px solid var(--sidebar-border-color);
        background-color: var(--sidebar-bg) !important;
        overflow: visible;
        z-index: 1020;
        visibility: visible !important;
        transform: none !important;
    }

    body.sidebar-transitioning .partner-sidebar {
        transition: width 0.3s ease;
    }

    .partner-sidebar .offcanvas-body {
        padding: 1.25rem !important;
        display: flex !important;
        flex-direction: column !important;
        overflow-y: auto !important;
        height: 100%;
    }

    /* Push main content and header to the right (only when sidebar is present). */
    #site-header,
    .partner-sidebar ~ .page-wrapper {
        margin-left: var(--sidebar-width);
    }

    body.sidebar-transitioning #site-header,
    body.sidebar-transitioning .partner-sidebar ~ .page-wrapper {
        transition: margin-left 0.3s ease;
    }

    /* ---- COLLAPSED STATE ---- */
    .partner-sidebar.collapsed {
        width: var(--sidebar-collapsed-width);
    }

    .partner-sidebar.collapsed .offcanvas-body {
        overflow: hidden !important;
    }

    body.sidebar-collapsed #site-header,
    body.sidebar-collapsed .partner-sidebar ~ .page-wrapper {
        margin-left: var(--sidebar-collapsed-width);
    }

    /* Toggle visibility classes: hide text/full elements, show icon replacements */
    .sidebar-collapse-show {
        display: none !important;
    }

    .partner-sidebar.collapsed .sidebar-collapse-hide {
        display: none !important;
    }

    .partner-sidebar.collapsed .sidebar-collapse-show {
        display: block !important;
    }

    /* Collapsed layout: center items in narrow strip */
    .partner-sidebar.collapsed .sidebar-content {
        align-items: center;
    }

    .partner-sidebar.collapsed .sidebar-header-section {
        margin-bottom: 0.5rem !important;
    }

    .partner-sidebar.collapsed .sidebar-header-section .d-flex {
        justify-content: center;
    }

    /* User section: center avatar when collapsed */
    .partner-sidebar.collapsed .sidebar-user-section .d-flex {
        justify-content: center;
    }

    /* Collapsed icon buttons — avatar-md sized (2.5rem) for uniform look */
    .sidebar-collapsed-icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 2.5rem;
        height: 2.5rem;
        border: none;
        background: rgba(255, 255, 255, 0.08);
        color: var(--sidebar-color);
        font-size: 1.15rem;
        line-height: 1;
        border-radius: var(--tblr-avatar-border-radius, 0.375rem);
        cursor: pointer;
        text-decoration: none;
        transition: background-color 0.15s, color 0.15s;
    }

    .sidebar-collapsed-icon:hover:not(.disabled) {
        background: rgba(255, 255, 255, 0.14);
        color: var(--sidebar-color-hover);
    }

    .sidebar-collapsed-icon.disabled {
        opacity: 0.35;
        cursor: default;
    }

    /* Collapsed show items — centered column with uniform spacing */
    .partner-sidebar.collapsed .sidebar-collapse-show {
        display: flex !important;
        justify-content: center;
        margin-bottom: 0.35rem !important;
    }

    /* Nav items when collapsed — shrink to icon box only */
    .partner-sidebar.collapsed .sidebar-nav {
        align-items: center;
    }

    .partner-sidebar.collapsed .sidebar-nav .nav-link {
        width: 2.5rem;
        justify-content: center;
        background: rgba(255, 255, 255, 0.08);
    }

    .sidebar-collapsed-badge {
        position: absolute;
        top: 2px;
        right: 2px;
        font-size: 0.6rem;
        min-width: 16px;
        height: 16px;
        line-height: 16px;
        padding: 0 4px;
        border-radius: 8px;
    }

    /* ---- Flap handle on the sidebar edge ---- */
    .sidebar-flap {
        position: absolute;
        top: 50%;
        right: -14px;
        transform: translateY(-50%);
        width: 14px;
        height: 48px;
        background: var(--sidebar-bg);
        border: 1px solid var(--sidebar-border-color);
        border-left: none;
        border-radius: 0 6px 6px 0;
        cursor: pointer;
        align-items: center;
        justify-content: center;
        z-index: 1021;
        transition: background 0.15s;
    }

    .sidebar-flap:hover {
        background: color-mix(in srgb, var(--sidebar-bg), white 10%);
    }

    .sidebar-flap-icon {
        font-size: 0.7rem;
        color: var(--sidebar-color);
        transition: transform 0.3s ease;
    }

    /* Flip chevron when collapsed */
    .partner-sidebar.collapsed .sidebar-flap-icon {
        transform: rotate(180deg);
    }
}

/* Mobile (<lg): Standard offcanvas from left */
@media (max-width: 991.98px) {
    .partner-sidebar {
        --tblr-offcanvas-width: 360px;
        background-color: var(--sidebar-bg) !important;
    }

    .partner-sidebar .offcanvas-header {
        color: var(--sidebar-color-hover);
        border-bottom-color: var(--sidebar-border-color) !important;
    }

    .partner-sidebar .btn-close {
        filter: invert(1);
    }

    [data-bs-theme="dark"] .partner-sidebar .btn-close {
        filter: none;
    }

    /* Hide the desktop flap and collapse-show elements on mobile; the mobile
       flap below is the one exception - it is how the offcanvas opens. */
    .sidebar-flap:not(.sidebar-flap--mobile),
    .sidebar-collapse-show {
        display: none !important;
    }

    /* The desktop flap, pinned to the screen edge: tapping it opens the
       offcanvas. The open offcanvas (z-index 1045) covers it.
       appearance: none matters - iOS Safari otherwise replaces all of this
       with its own button chrome. */
    .sidebar-flap--mobile {
        appearance: none;
        -webkit-appearance: none;
        -webkit-tap-highlight-color: transparent;
        position: fixed;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        width: 20px;
        height: 56px;
        padding: 0;
        display: flex;
        align-items: center;
        justify-content: center;
        background: var(--sidebar-bg);
        border: 1px solid var(--sidebar-border-color);
        border-left: none;
        border-radius: 0 6px 6px 0;
        color: var(--sidebar-color);
        z-index: 1021;
    }

    .sidebar-flap--mobile:hover,
    .sidebar-flap--mobile:active {
        background: color-mix(in srgb, var(--sidebar-bg), white 10%);
    }

    /* Same quiet chevron as the desktop flap (whose 0.7rem rule lives in the
       desktop media block and does not reach down here). */
    .sidebar-flap--mobile .sidebar-flap-icon {
        font-size: 0.7rem;
        color: var(--sidebar-color);
    }
}

/* ---- Sidebar Header ---- */
.sidebar-header-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    color: var(--sidebar-color);
    opacity: 0.6;
    font-size: 1rem;
    text-decoration: none !important;
    background: rgba(255, 255, 255, 0.08);
    border-radius: var(--tblr-avatar-border-radius, 0.375rem);
    transition: opacity 0.15s, background-color 0.15s;
}

.sidebar-header-btn:hover {
    opacity: 1;
    color: var(--sidebar-color-hover);
    background: rgba(255, 255, 255, 0.14);
}

/* Inline icon in sidebar user bar — no background box */
.sidebar-inline-icon {
    color: var(--sidebar-color);
    opacity: 0.5;
    font-size: 1.4rem;
    text-decoration: none !important;
    transition: opacity 0.15s;
    flex-shrink: 0;
    padding: 0 0.15rem;
    cursor: pointer;
}

.sidebar-inline-icon:hover {
    opacity: 1;
    color: var(--sidebar-color-hover);
}

.sidebar-brand-avatar {
    background: rgba(255, 255, 255, 0.85);
    color: #182433;
    font-weight: 600;
    border-radius: var(--tblr-avatar-border-radius, 0.375rem);
}

.sidebar-brand-avatar img {
    object-fit: contain;
    padding: 3px;
}

/* ---- Sidebar Navbar Nav — 2.5rem row height to match avatar-md ---- */
.partner-sidebar .sidebar-nav {
    gap: 0.35rem;
}

.partner-sidebar .sidebar-nav .nav-link {
    display: flex;
    align-items: center;
    height: 2.5rem;
    padding: 0;
    color: var(--sidebar-color);
    border-radius: var(--tblr-avatar-border-radius, 0.375rem);
    font-size: 0.85rem;
    background: rgba(255, 255, 255, 0.04);
    text-decoration: none;
    transition: background-color 0.15s;
}

.partner-sidebar .sidebar-nav .nav-link:hover:not(.disabled) {
    color: var(--sidebar-color-hover);
    background: rgba(255, 255, 255, 0.08);
}

.partner-sidebar .sidebar-nav .nav-link.disabled {
    color: var(--sidebar-color);
    opacity: 0.4;
}

/* Icon box inside nav link — 2.5rem square, centered icon */
.partner-sidebar .sidebar-nav .nav-link-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    flex-shrink: 0;
    color: var(--sidebar-color);
    opacity: 0.7;
    margin: 0;
}

.partner-sidebar .sidebar-nav .nav-link-title {
    padding-left: 0.5rem;
    padding-right: 0.75rem;
}

/* ---- Sidebar Search — 2.5rem height to match avatar-md ---- */
.sidebar-search-input {
    height: 2.5rem !important;
    background-color: rgba(255, 255, 255, 0.08) !important;
    border-color: rgba(255, 255, 255, 0.1) !important;
    color: var(--sidebar-color) !important;
    border-radius: var(--tblr-avatar-border-radius, 0.375rem) !important;
    font-size: 0.85rem !important;
}

.sidebar-search-input::placeholder {
    color: rgba(255, 255, 255, 0.4) !important;
}

.sidebar-search-input:focus {
    background-color: rgba(255, 255, 255, 0.12) !important;
    border-color: rgba(255, 255, 255, 0.2) !important;
}

.sidebar-search-results {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1050;
    background: var(--tblr-bg-surface);
    border: 1px solid var(--tblr-border-color);
    border-radius: 0.375rem;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    max-height: 300px;
    overflow-y: auto;
    margin-top: 4px;
}

/* button_to wraps in a form — reset it */
.sidebar-search-results form {
    margin: 0;
    padding: 0;
}

.sidebar-search-result-item {
    color: var(--tblr-body-color);
    text-decoration: none;
    transition: background-color 0.15s;
    cursor: pointer;
}

/* Reset button styling for button_to results */
button.sidebar-search-result-item {
    background: none;
    border: none;
    text-align: left;
    font: inherit;
}

.sidebar-search-result-item:hover {
    background-color: var(--tblr-bg-surface-secondary);
    color: var(--tblr-body-color);
    text-decoration: none;
}

/* Avatar icons inside search results */
.sidebar-search-result-item .avatar {
    flex-shrink: 0;
}

.sidebar-search-result-item .avatar i {
    font-size: 1rem;
}

/* ---- Version Card ---- */
.sidebar-version-card {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.1);
    color: var(--sidebar-color);
}

.sidebar-version-card .text-secondary {
    color: rgba(255, 255, 255, 0.5) !important;
}

/* ---- User Section ---- */
.sidebar-user-section {
    position: relative;
    padding-top: 0.5rem;
}

/* ---- User Avatar VIP Badge ---- */
/* Admin crown badge on avatar */
.sidebar-avatar-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    color: #f5c842;
    line-height: 1;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}

/* ---- User Flyout Menu (right of sidebar) ---- */
.sidebar-user-flyout {
    position: fixed;
    z-index: 1060;
    background: var(--tblr-bg-surface);
    border: 1px solid var(--tblr-border-color);
    min-width: 260px;
    max-width: 300px;
}

.sidebar-flyout-item {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.75rem;
    color: var(--tblr-body-color);
    text-decoration: none !important;
    font-size: 0.85rem;
    border-radius: 0.375rem;
    transition: background-color 0.15s;
}

.sidebar-flyout-item:hover {
    background-color: var(--tblr-bg-surface-secondary);
    color: var(--tblr-body-color);
}

.sidebar-flyout-item.text-danger:hover {
    background-color: rgba(var(--tblr-danger-rgb), 0.08);
}

/* ---- Dark mode overrides ---- */
[data-bs-theme="dark"] .sidebar-search-input {
    background-color: rgba(255, 255, 255, 0.05) !important;
    border-color: rgba(255, 255, 255, 0.08) !important;
}

[data-bs-theme="dark"] .sidebar-version-card {
    background: rgba(255, 255, 255, 0.04);
    border-color: rgba(255, 255, 255, 0.08);
}

/* ---- Notification card styling ---- */
.partner-sidebar .notification-card {
    border-radius: 0.5rem;
    background: rgba(255, 255, 255, 0.06);
    color: var(--sidebar-color-hover);
}

[data-bs-theme="dark"] .partner-sidebar .notification-card {
    background: rgba(255, 255, 255, 0.04);
}

.partner-sidebar .notification-card .text-secondary {
    color: rgba(255, 255, 255, 0.5) !important;
}

.partner-sidebar .notification-card .text-muted {
    color: rgba(255, 255, 255, 0.35) !important;
}

.partner-sidebar .notification-dismiss {
    position: absolute;
    top: 8px;
    right: 8px;
    color: rgba(255, 255, 255, 0.5);
    text-decoration: none;
    font-size: 0.75rem;
    line-height: 1;
    z-index: 1;
    transition: color 0.15s;
}

.partner-sidebar .notification-dismiss:hover {
    color: rgba(255, 255, 255, 0.85);
}

/* ---- Tenant switch loading overlay ---- */
.tenant-switch-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(var(--tblr-body-bg-rgb), 0.92);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 9999;
    backdrop-filter: blur(4px);
    animation: fadeIn 0.2s ease;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* ============================================================
   Partner Layout — Dual Layout Architecture
   ============================================================ */

/* Active nav item in sidebar */
.partner-sidebar .sidebar-nav .nav-link.active {
    color: var(--sidebar-color-hover);
    background: rgba(255, 255, 255, 0.12);
    border-radius: 0.375rem;
}

/* Sidebar enter-account button — subtle border highlight */
.sidebar-enter-btn {
    border: 1px solid rgba(255, 255, 255, 0.25) !important;
    border-radius: 0.25rem;
    opacity: 0.8;
    transition: opacity 0.15s, border-color 0.15s;
}
.sidebar-enter-btn:hover {
    opacity: 1;
    border-color: rgba(255, 255, 255, 0.5) !important;
}

/* Sidebar header: inline button_to form wrapper */
.sidebar-header-section .button_to {
    display: inline;
}

/* Changelog iframe — fills card body */
.changelog-iframe {
    width: 100%;
    min-height: 80vh;
    border: none;
    border-radius: 0 0 var(--tblr-card-border-radius) var(--tblr-card-border-radius);
}

/* Setup wizard — clickable carrier/profile cards */
.config-card {
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.config-card:hover {
    border-color: var(--tblr-primary);
}

/* Fix form-switch alignment — ensure toggle and label sit on the same line */
.form-check.form-switch {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 1.5rem;
    padding-left: 0;
}
.form-check.form-switch .form-check-input {
    float: none;
    margin-left: 0;
    margin-top: 0;
}
.form-check.form-switch .form-check-label {
    margin-top: 0;
}

/* Setup wizard sub-step slide animation (trunk config panels) */
@keyframes slide-in-from-right {
    from { opacity: 0; transform: translateX(30px); }
    to { opacity: 1; transform: translateX(0); }
}
@keyframes slide-in-from-left {
    from { opacity: 0; transform: translateX(-30px); }
    to { opacity: 1; transform: translateX(0); }
}
.panel-slide-forward {
    animation: slide-in-from-right 0.25s ease-out;
}
.panel-slide-back {
    animation: slide-in-from-left 0.25s ease-out;
}

/* Setup Wizard Filter Funnel */
.filter-step-number {
    min-width: 2rem;
    text-align: center;
}
.filter-step-number .badge {
    width: 2rem;
    height: 2rem;
    line-height: 2rem;
    font-size: 0.85rem;
}
.filter-type-card {
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
}
.filter-type-card:hover {
    border-color: var(--tblr-primary);
}
.filter-type-card.active {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 1px var(--tblr-primary);
    background-color: rgba(var(--tblr-primary-rgb), 0.04);
}
.filter-panel {
    display: none;
    animation: fadeIn 0.2s ease-in-out;
}
.filter-panel.show {
    display: block;
}
.filter-results-body {
    overflow-y: auto;
    scrollbar-width: none;
}
.filter-results-body::-webkit-scrollbar {
    display: none;
}
.table-compact th,
.table-compact td {
    padding: 0.35rem 0.5rem;
    font-size: 0.8125rem;
}

/* Form section pattern for multi-section forms within cards */
.form-section {
    margin-bottom: 1.5rem;
}

.form-section:last-child {
    margin-bottom: 0;
}

.form-section-title {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: 0.75rem;
}

/* Number range add/remove buttons — match form-control-sm height (30px) */
.number-range-action-btn {
    width: 32px;
    height: 32px;
    min-width: 0;
    min-height: 0;
    padding: 0;
}

/* Trix rich text editor — Tabler theme integration */
trix-editor {
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    min-height: 200px;
    padding: 0.75rem;
    font-family: inherit;
}
trix-editor:focus {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 0.25rem rgba(var(--tblr-primary-rgb), 0.25);
    outline: none;
}
trix-toolbar .trix-button-group--file-tools {
    display: none;
}

/* ========================================
   Navbar Service Dropdown Overflow
   ======================================== */

/* Prevent long service names from expanding the dropdown beyond its container */
.navbar .dropend > .dropdown-menu {
    min-width: auto;
    width: max-content;
}

.navbar .dropend .dropdown-item {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Service name in the dropend trigger */
.navbar .dropend > .dropdown-item.dropdown-toggle {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 240px;
}

/* ========================================
   Top menu highlight (brand colour, hover-driven)
   - Highlight FONT colour follows the brand primary (blue/teal via whitelabel);
     background stays the Tabler default. The hover-menu Stimulus controller keeps
     exactly one chain open, so .show always equals the hovered path.
   ======================================== */

/* Top-level items: brand colour on hover and while their dropdown is open.
   !important beats the dark-mode `.navbar .nav-link { color: ... !important }`. */
.navbar-nav > .nav-item > .nav-link:hover,
.navbar-nav > .nav-item.show > .nav-link {
    color: var(--tblr-primary) !important;
}

/* The label lives in a `.nav-link-title` span that carries its own
   `color: ... !important` (dark-mode brighten), so it ignores the brand colour
   the parent link takes on hover/open/active - only the icon and caret (which
   inherit) recolour. Re-assert the brand colour on the title in those states. */
.navbar-nav > .nav-item > .nav-link:hover .nav-link-title,
.navbar-nav > .nav-item.show > .nav-link .nav-link-title,
.navbar-nav > .nav-item > .nav-link.active .nav-link-title {
    color: var(--tblr-primary) !important;
}

/* Dropdown items + nested submenu toggles: brand-colour the font on hover and
   when their own submenu is open. Background unchanged. */
.navbar .dropdown-item:hover,
.navbar .dropend.show > .dropdown-item.dropdown-toggle {
    color: var(--tblr-primary);
}

/* A clicked item must not keep its highlight after the pointer leaves: neutralise
   the lingering mouse :focus background (keyboard :focus-visible still highlights). */
.navbar .dropdown-item:focus:not(:hover):not(:focus-visible) {
    background-color: transparent;
}

/* Active section ("you are here"): persistent brand text + a flat border-bottom
   underline. A transparent 2px border is reserved on every top nav-link so the
   active one just colours it (no layout shift); border-radius:0 keeps the line
   flat (no eyebrow curve). Distinct from the transient hover/open highlight
   (which is text-colour only). */
.navbar-nav > .nav-item > .nav-link {
    border-bottom: 2px solid transparent;
    border-radius: 0;
}

.navbar-nav > .nav-item > .nav-link.active {
    color: var(--tblr-primary) !important;
    border-bottom-color: var(--tblr-primary);
}

/* No focus glow ring on the top nav toggles - clicking to open showed a stray
   box-shadow border. State is already conveyed by the brand colour + underline. */
.navbar-nav > .nav-item > .nav-link:focus,
.navbar-nav > .nav-item > .nav-link:focus-visible {
    box-shadow: none;
    outline: none;
}

/* The partner sidebar list also carries .navbar-nav, so the horizontal-nav
   border-bottom "eyebrow" underline leaks onto its vertical buttons. Drop the
   underline and express the active state as branding-coloured text + icon only. */
.partner-sidebar .sidebar-nav .nav-link,
.partner-sidebar .sidebar-nav .nav-link.active {
    border-bottom: 0;
}

.partner-sidebar .sidebar-nav .nav-link.active {
    color: var(--tblr-primary) !important;
}

.partner-sidebar .sidebar-nav .nav-link.active .nav-link-icon {
    color: var(--tblr-primary);
    opacity: 1;
}

/* Section nav items read as full-height tabs (only the menu bar, #site-navigation):
   the grey active/open fill runs all the way to the bar's TOP edge and the brand
   underline rests ON the bar's bottom border - instead of a rounded pill floating
   centred in the bar with a gap above and below.

   Tabler floors every nav-link at min-height:2.5rem (40px) and centres it in the
   3.5rem bar, leaving a gap top and bottom. We clamp the link up to the bar's full
   height (min-height:3.5rem) so its grey fill spans the bar and its 2px underline
   lands on the bar's bottom border; padding-y:0 removes the surrounding gap and
   border-radius:0 squares the fill to the edges. (align-items:stretch alone can't
   do this - Tabler's min-height keeps the link at 40px - so the explicit clamp is
   required.) */
#site-navigation .navbar {
    --tblr-navbar-padding-y: 0;
}
#site-navigation .navbar-nav > .nav-item > .nav-link {
    min-height: 3.5rem;     /* clamp Tabler's 2.5rem nav-link up to the full bar height */
    display: flex;
    align-items: center;    /* keep the label vertically centred in the tall tab */
    border-radius: 0;       /* square the grey fill flush to the edges */
}

/* Same for items inside the dropdowns (incl. the .dropend submenu toggles like
   "Numbers"): opening one left a blue focus box around it. Highlight is already
   the brand colour, so drop the ring. */
.navbar .dropdown-menu .dropdown-item:focus,
.navbar .dropdown-menu .dropdown-item:focus-visible {
    box-shadow: none;
    outline: none;
}

/* ========================================
   DateRangePicker - Tabler Integration
   ======================================== */

/* Match Tabler form-control styling */
.daterangepicker {
    font-family: var(--tblr-font-sans-serif);
    font-size: 0.875rem;
    border-color: var(--tblr-border-color);
    box-shadow: var(--tblr-box-shadow-dropdown);
    z-index: 10000;
}

/* Preset range buttons */
.daterangepicker .ranges li {
    font-size: 0.8125rem;
    padding: 0.375rem 0.75rem;
    border-radius: var(--tblr-border-radius);
    color: var(--tblr-body-color);
}

.daterangepicker .ranges li:hover {
    background-color: var(--tblr-bg-surface-secondary);
}

.daterangepicker .ranges li.active {
    background-color: var(--tblr-primary);
    color: #fff;
}

/* Calendar styling */
.daterangepicker td.active,
.daterangepicker td.active:hover {
    background-color: var(--tblr-primary);
}

.daterangepicker td.in-range {
    background-color: rgba(var(--tblr-primary-rgb), 0.1);
    color: var(--tblr-body-color);
}

/* Apply/Cancel buttons */
.daterangepicker .drp-buttons .btn {
    font-size: 0.8125rem;
    padding: 0.25rem 0.75rem;
}

.daterangepicker .drp-buttons .applyBtn {
    background-color: var(--tblr-primary);
    border-color: var(--tblr-primary);
}

/* Dark mode */
[data-bs-theme="dark"] .daterangepicker {
    background-color: var(--tblr-bg-surface);
    border-color: var(--tblr-border-color);
}

[data-bs-theme="dark"] .daterangepicker .calendar-table {
    background-color: var(--tblr-bg-surface);
    border-color: var(--tblr-border-color);
}

[data-bs-theme="dark"] .daterangepicker td.off,
[data-bs-theme="dark"] .daterangepicker td.off.in-range {
    background-color: var(--tblr-bg-surface);
    color: var(--tblr-secondary);
}

[data-bs-theme="dark"] .daterangepicker th,
[data-bs-theme="dark"] .daterangepicker td {
    color: var(--tblr-body-color);
}

[data-bs-theme="dark"] .daterangepicker .ranges li {
    color: var(--tblr-body-color);
}

[data-bs-theme="dark"] .daterangepicker .ranges li:hover {
    background-color: var(--tblr-bg-surface-secondary);
}

[data-bs-theme="dark"] .daterangepicker .drp-buttons {
    border-top-color: var(--tblr-border-color);
}

/* ------------------------------------------------------------------
   Sidebar-aware modal centering
   ------------------------------------------------------------------
   Bootstrap's `.modal` and `.modal-backdrop` are `position: fixed` and
   span the full viewport (`left: 0; right: 0`), which means modals get
   centered over the whole page including the persistent sidebar.
   Shift their left edge to match the page-wrapper's margin so they
   visually centre within the content area. Only applied on >= lg where
   the sidebar is persistent; on mobile the sidebar is offcanvas and
   modals should continue to use the full viewport.
   ------------------------------------------------------------------ */
@media (min-width: 992px) {
    body:has(.partner-sidebar) .modal,
    body:has(.partner-sidebar) .modal-backdrop {
        left: var(--sidebar-width);
        width: calc(100% - var(--sidebar-width));
    }

    body.sidebar-collapsed:has(.partner-sidebar) .modal,
    body.sidebar-collapsed:has(.partner-sidebar) .modal-backdrop {
        left: var(--sidebar-collapsed-width);
        width: calc(100% - var(--sidebar-collapsed-width));
    }

    body.sidebar-transitioning:has(.partner-sidebar) .modal,
    body.sidebar-transitioning:has(.partner-sidebar) .modal-backdrop {
        transition: left 0.3s ease, width 0.3s ease;
    }
}

/* Reporting page live indicator — matches the countdown ring footprint
   (20x20 SVG) and pulses to signal ActionCable-driven updates. */
@keyframes sdrLivePulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.5; transform: scale(0.75); }
}
.sdr-live-pulse {
    transform-box: fill-box;
    transform-origin: center;
    animation: sdrLivePulse 1.4s ease-in-out infinite;
}

/* Match plain `.form-select` to TomSelect's visual style so forms that mix
   native selects and TomSelect-enhanced inputs line up pixel-for-pixel.
   The persona "Identity configuration" sections show/hide via display:none —
   TomSelect doesn't survive that, so those selects stay native. */
.form-select {
    padding: .625rem 2.25rem .625rem .75rem;
    height: 40px;
    line-height: 1.4285714286;
    font-size: .875rem;
    border-color: var(--tblr-border-color);
    background-color: var(--tblr-bg-forms);
    box-shadow: none;
    transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
.form-select:focus {
    border-color: var(--tblr-primary);
    box-shadow: 0 0 0 .25rem rgba(var(--tblr-primary-rgb), .1);
}
.form-select:disabled {
    background-color: var(--tblr-bg-surface-secondary);
    color: var(--tblr-secondary);
}

/* Dashed placeholder card — used for "New persona" / "New blueprint" tiles.
   Muted at rest (so the real cards draw the eye); switches to primary
   border + foreground on hover to signal interactivity. */
.card-placeholder {
    border-style: dashed;
    border-color: var(--tblr-border-color);
    background-color: transparent;
    color: var(--tblr-secondary);
    transition: border-color 0.15s ease, color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.card-placeholder .card-title,
.card-placeholder p,
.card-placeholder i {
    color: var(--tblr-secondary);
    transition: color 0.15s ease;
}
.card-placeholder:hover {
    border-color: var(--tblr-primary);
    color: var(--tblr-primary);
    transform: translateY(-4px);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.card-placeholder:hover .card-title,
.card-placeholder:hover p,
.card-placeholder:hover i {
    color: var(--tblr-primary);
}

/* ============================================================
   Dark-mode contrast lifts
   ------------------------------------------------------------
   Target the elements where Tabler's default dark palette sits
   below WCAG AA (or close enough to feel washed out). All rules
   are scoped to [data-bs-theme="dark"] so light mode is untouched.

   Audited via in-page Playwright script (WCAG luminance ratios);
   each rule here brings the flagged element to >= 4.5:1 (AA) or
   higher. See ticket / screenshot "identities-contrast-*.png".
   ============================================================ */
[data-bs-theme="dark"] {
    /* Sync button: white on mid-green (#2FB344) was 2.63:1.
       Darker green brings white to ~7.4:1 (AAA). */
    .btn-green,
    .btn.btn-green {
        background-color: #136329;
        border-color: #136329;
        color: #FFFFFF;
    }
    .btn-green:hover,
    .btn.btn-green:hover {
        background-color: #0F5321;
        border-color: #0F5321;
        color: #FFFFFF;
    }

    /* Outline-primary buttons ("View" etc.): blue-400 → blue-300
       (6.4:1). Darker dark-mode bg on hover for reversed legibility. */
    .btn-outline-primary {
        color: #63B3ED !important;
        border-color: #63B3ED !important;
    }
    .btn-outline-primary:hover,
    .btn-outline-primary:focus,
    .btn-outline-primary.active {
        background-color: #63B3ED !important;
        border-color: #63B3ED !important;
        color: #0F172A !important;
    }

    /* Top navbar + nav-link titles: lift gray-400 → gray-300 (≈10:1). */
    .navbar .nav-link,
    .navbar .nav-link-title,
    .nav-link-title {
        color: #D1D5DB !important;
    }

    /* Table column headers: same brighten for the text-muted slab. */
    .table > thead th,
    .table > thead tr > th,
    th {
        color: #D1D5DB !important;
    }

    /* Table-body muted text (emails, phone numbers, sub-labels):
       Tabler dark default ~#6C7A96 → #A3ADBC (6.5:1, AA+). Also
       nudge font-weight on the 11-12 px variants so small sizes read. */
    .table .text-muted,
    .table-vcenter .text-muted,
    td .text-muted,
    .card-body .text-muted.small,
    .text-muted.small {
        color: #A3ADBC !important;
    }
    .text-muted.small {
        font-weight: 500 !important;
    }

    /* Footer nav muted links: #6B7280 → #B8C0CC (~9.7:1). */
    footer .list-inline-item,
    footer .list-inline-item a,
    .footer .list-inline-item,
    .footer .list-inline-item a {
        color: #B8C0CC !important;
    }

    /* Subtle boost to card borders so cards read as distinct surfaces
       against the body (rgba(255,255,255,.04) → .12). */
    .card {
        border-color: rgba(255, 255, 255, 0.12) !important;
    }

    /* ------------------------------------------------------------
       Tint ("-lt") pills, avatars and chips: the worst offender.
       Tabler paints both background and foreground with the same
       base hue, producing "blue on blue" mush (~3.5:1) in dark.
       We keep the tint vibe but brighten the foreground to a light
       pastel token (Tailwind 300-ish) and lift the background tint
       to 22 % so the pill boundary is still legible. The selectors
       cover any element using a `.bg-<hue>-lt` utility (badges,
       .avatar chips, etc.) plus any icon/svg inside them.
       ------------------------------------------------------------ */
    .bg-blue-lt,   .bg-blue-lt i,   .bg-blue-lt svg   { color: #93C5FD !important; }
    .bg-azure-lt,  .bg-azure-lt i,  .bg-azure-lt svg  { color: #7DD3FC !important; }
    .bg-indigo-lt, .bg-indigo-lt i, .bg-indigo-lt svg { color: #A5B4FC !important; }
    .bg-purple-lt, .bg-purple-lt i, .bg-purple-lt svg { color: #D8B4FE !important; }
    .bg-pink-lt,   .bg-pink-lt i,   .bg-pink-lt svg   { color: #F9A8D4 !important; }
    .bg-red-lt,    .bg-red-lt i,    .bg-red-lt svg    { color: #FCA5A5 !important; }
    .bg-orange-lt, .bg-orange-lt i, .bg-orange-lt svg { color: #FDBA74 !important; }
    .bg-yellow-lt, .bg-yellow-lt i, .bg-yellow-lt svg { color: #FDE68A !important; }
    .bg-lime-lt,   .bg-lime-lt i,   .bg-lime-lt svg   { color: #BEF264 !important; }
    .bg-green-lt,  .bg-green-lt i,  .bg-green-lt svg  { color: #86EFAC !important; }
    .bg-teal-lt,   .bg-teal-lt i,   .bg-teal-lt svg   { color: #5EEAD4 !important; }
    .bg-cyan-lt,   .bg-cyan-lt i,   .bg-cyan-lt svg   { color: #67E8F9 !important; }

    /* Bootstrap-semantic ("success/danger/warning/info/primary/secondary")
       variants — Tabler aliases these to hue-based colours but the rules are
       distinct in CSS. Give them the same dark-mode lift. */
    .bg-success-lt,   .bg-success-lt i,   .bg-success-lt svg   { color: #86EFAC !important; }
    .bg-danger-lt,    .bg-danger-lt i,    .bg-danger-lt svg    { color: #FCA5A5 !important; }
    .bg-warning-lt,   .bg-warning-lt i,   .bg-warning-lt svg   { color: #FCD34D !important; }
    .bg-info-lt,      .bg-info-lt i,      .bg-info-lt svg      { color: #67E8F9 !important; }
    .bg-primary-lt,   .bg-primary-lt i,   .bg-primary-lt svg   { color: #93C5FD !important; }
    .bg-secondary-lt, .bg-secondary-lt i, .bg-secondary-lt svg { color: #D1D5DB !important; }

    /* Tint background derived from currentColor so any future -lt
       utility inherits the same legibility profile automatically. */
    .bg-blue-lt,  .bg-azure-lt, .bg-indigo-lt, .bg-purple-lt,
    .bg-pink-lt,  .bg-red-lt,   .bg-orange-lt, .bg-yellow-lt,
    .bg-lime-lt,  .bg-green-lt, .bg-teal-lt,   .bg-cyan-lt,
    .bg-success-lt, .bg-danger-lt, .bg-warning-lt, .bg-info-lt,
    .bg-primary-lt, .bg-secondary-lt {
        background-color: color-mix(in srgb, currentColor 22%, transparent) !important;
    }

    /* Standalone text-<hue> utilities (no tinted fill around them).
       Used for inline annotations like "Creates new blueprint". */
    .text-azure  { color: #7DD3FC !important; }
    .text-blue   { color: #93C5FD !important; }
    .text-indigo { color: #A5B4FC !important; }
    .text-purple { color: #D8B4FE !important; }
    .text-pink   { color: #F9A8D4 !important; }
    .text-red    { color: #FCA5A5 !important; }
    .text-orange { color: #FDBA74 !important; }
    .text-yellow { color: #FDE68A !important; }
    .text-lime   { color: #BEF264 !important; }
    .text-green  { color: #86EFAC !important; }
    .text-teal   { color: #5EEAD4 !important; }
    .text-cyan   { color: #67E8F9 !important; }
}

/* ---------------------------------------------------------------- */
/* Number range form (customer carrier number-ranges page)          */
/* Column proportions: Start Number compact, Range Size narrowest,  */
/* Route to widest. Header and row both follow the same ratios.     */
/* ---------------------------------------------------------------- */
.number-range-form .nr-col-start,
.number-range-form .nr-col-size,
.number-range-form .nr-col-route {
    min-width: 0;
}

/* Route to grows into the leftover space instead of a fixed 50% —
   22% + 28% + 50% left zero room for the action-button col-auto,
   wrapping the delete button onto its own line. */
.number-range-form .nr-col-start { flex: 0 0 22%; max-width: 22%; }
.number-range-form .nr-col-size  { flex: 0 0 28%; max-width: 28%; }
.number-range-form .nr-col-route { flex: 1 1 0; max-width: none; }

/* Range Size select stays compact — don't stretch to fill its column. */
.number-range-form [data-number-range-target="sizeSelect"] {
    width: auto !important;
    flex: 0 0 auto !important;
}

.number-range-form [data-number-range-target="customEndInput"] {
    flex: 1 1 auto;
    min-width: 0;
}

/* Match Range Size and Route to selects to the Start Number input.
   Mirror form-control-sm's font/padding/line-height and let the natural
   min-height match. Avoid hard-coded `height` — caused the selects to
   render shorter than the input. */
.number-range-form .form-select-sm {
    padding: 0.25rem 1.75rem 0.25rem 0.5rem !important;
    font-size: 0.75rem !important;
    line-height: 1.4285714286 !important;
    height: 2rem !important;        /* ~30px, matches Tabler form-control-sm */
    min-height: 2rem !important;
}
/* Datatable "Show N entries" length-select: Tabler's .form-select-sm renders a
   touch taller than the adjacent .form-control-sm search box, so the two controls
   in the table toolbar don't line up. Pin the length-select to the same ~30px box
   as form-control-sm (same approach as the number-range selects above). */
.dt-length-select.form-select-sm {
    height: 2rem !important;        /* ~30px, matches Tabler .form-control-sm */
    min-height: 2rem !important;
    padding-top: 0.25rem !important;
    padding-bottom: 0.25rem !important;
    line-height: 1.4285714286 !important;
}

/* Identity Call Quality — clickable device cards */
.card.card-link-style {
  transition: transform 0.15s ease, box-shadow 0.15s ease;
  cursor: pointer;
}
.card.card-link-style:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 16px rgba(var(--tblr-primary-rgb), 0.15), 0 0 0 1px rgba(var(--tblr-primary-rgb), 0.4);
}

/* Async lazy-loading panel — see app/views/shared/_async_loader.html.erb */
.async-loader {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 6rem 1rem;
  gap: 1.25rem;
}
.async-loader__spinner {
  position: relative;
  width: 72px;
  height: 72px;
}
.async-loader__ring {
  position: absolute;
  inset: 0;
  border: 4px solid var(--tblr-border-color);
  border-top-color: var(--tblr-primary);
  border-radius: 50%;
  animation: async-loader-spin 0.9s linear infinite;
}
.async-loader__pulse {
  position: absolute;
  inset: 8px;
  background: var(--tblr-primary);
  opacity: 0.08;
  border-radius: 50%;
  animation: async-loader-pulse 1.6s ease-in-out infinite;
}
.async-loader__copy {
  text-align: center;
  max-width: 520px;
}
.async-loader__title {
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--tblr-body-color);
  margin-bottom: 0.25rem;
}
.async-loader__subtitle {
  font-size: 0.875rem;
  color: var(--tblr-secondary);
  line-height: 1.5;
}
@keyframes async-loader-spin {
  to { transform: rotate(360deg); }
}
@keyframes async-loader-pulse {
  0%, 100% { opacity: 0.05; transform: scale(0.9);  }
  50%      { opacity: 0.20; transform: scale(1.05); }
}

/* ---- Telephony Analytics dashboard grid -------------------------- */
/* 4-column CSS Grid wrapper for the analytics widgets. Each grid     */
/* item declares its own grid-column / grid-row span based on the     */
/* shape's SHAPE_SIZES entry. `grid-auto-flow: dense` packs items     */
/* tightly when later items are smaller than the gap they could fill. */
.dashboard-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: 72px;
  grid-auto-flow: dense;
  gap: 1rem;
  /* Keep a small margin under the filter row above */
  margin-bottom: 1rem;
}

.dashboard-grid-item {
  /* Allow children to shrink inside the grid cell — without this, an  */
  /* ApexCharts SVG can blow out the column to its intrinsic width.    */
  min-width: 0;
  position: relative;
}

.dashboard-grid-item > .lazy-async-frame,
.dashboard-grid-item > turbo-frame {
  display: block;
  height: 100%;
}

.dashboard-grid-item .card,
.dashboard-grid-item .card-sm {
  /* Cards stretch to their grid cell so a 2-row gauge / 4-row chart   */
  /* fills its full vertical span. Without this they'd sit at their    */
  /* intrinsic content height and leave gaps below.                    */
  height: 100%;
  display: flex;
  flex-direction: column;
  margin-bottom: 0;
}

/* Card body fills the remaining space below the optional card-header. */
.dashboard-grid-item .card > .card-body {
  flex: 1 1 auto;
  min-height: 0;
}

/* Chart bodies host the ApexCharts container as a flex child so the
   chart's percentage height resolves to the cell-derived pixel height.
   Padding tightened from Tabler's default ~1.25 rem so the chart has
   room for axis ticks + bottom legend without the card clipping it. */
.dashboard-grid-item .card > .card-body.chart-body {
  display: flex;
  flex-direction: column;
  padding: 0.5rem 0.75rem 0.875rem;
}
.dashboard-grid-item .card > .card-body.chart-body > [data-controller="apexchart"] {
  /* flex-basis 0 + grow:1 = the host fills the card-body purely from
     the flex parent's height, never from the chart's intrinsic SVG
     content height. Without this ApexCharts and the flex parent enter
     a feedback loop: SVG grows host, host grows parent, ResizeObserver
     fires (when watching the host), chart re-renders larger, repeat.
     We avoid `overflow: hidden` here so the bottom-positioned legend
     isn't clipped — the resize loop is already broken by:
       (a) basis 0 keeping the host's intrinsic content out of the
           flex calculation, and
       (b) the ResizeObserver in apexchart_controller.js targeting the
           parent cell (grid-driven, stable) with a 2 px tolerance. */
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  position: relative;
}

/* Tile-class cards (tile_stat / trend_tile / sparkline_tile / gauge):
   centre the single content row vertically inside the now-stretched
   card so the icon + headline don't sit at the top of a 1-row 72px
   cell. card-sm marks the simple stat tile. */
.dashboard-grid-item .card.card-sm > .card-body {
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Sparkline / gauge chart inside a tile fills the remaining vertical
   space *after* the tile_chrome card-body, never the whole card.
   flex-basis: 0 keeps the SVG out of the flex sizing calculation
   (same anti-feedback rule as `.chart-body` charts above). */
.dashboard-grid-item .card > .dashboard-tile-chart {
  flex: 1 1 0;
  min-height: 0;
  width: 100%;
  position: relative;
}

/* Table widgets scroll vertically inside the cell when the row count
   exceeds the cell's height (e.g. 25 worst-mos rows in a 4-row cell). */
.dashboard-grid-item .card > .card-body.card-no-padding {
  overflow: hidden;
  padding: 0;
}
.dashboard-grid-item .card > .card-body.card-no-padding > .table-responsive {
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
  overflow-y: auto;
}

/* Drag handle in the corner of every dashboard widget. Visible only on
   hover so the card chrome isn't cluttered when you're not dragging. */
.dashboard-grid-item .dashboard-drag-handle {
  position: absolute;
  top: 0.5rem;
  left: 0.5rem;
  /* Above Apex's internal overlay z-indexes (~10..100) so chart cards
     stay grabbable. Without this the handle sits under Apex's
     tooltip / zoom-scroll layers and pointerdown never fires on it. */
  z-index: 200;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--tblr-secondary);
  cursor: grab;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.dashboard-grid-item:hover .dashboard-drag-handle,
.dashboard-grid-item.sortable-chosen .dashboard-drag-handle {
  opacity: 0.7;
}

.dashboard-grid-item .dashboard-drag-handle:hover {
  opacity: 1;
}

.dashboard-grid-item .dashboard-drag-handle:active {
  cursor: grabbing;
}

/* Skeleton placeholder while a dashboard widget's lazy frame is
   fetching. Sits flush with the grid cell, hides the spinner +
   "Loading widget" text the legacy lazy_async_frame would have
   rendered, and shimmers softly so a dashboard with 18 cells
   doesn't look like a swarm of spinners on cold load. */
.dashboard-grid-item .dashboard-skeleton {
  position: relative;
  overflow: hidden;
}

.dashboard-grid-item .dashboard-skeleton .dashboard-skeleton-shimmer {
  position: absolute;
  inset: 0.75rem;
  border-radius: 4px;
  background: linear-gradient(
    90deg,
    rgba(0, 0, 0, 0.04) 0%,
    rgba(0, 0, 0, 0.08) 50%,
    rgba(0, 0, 0, 0.04) 100%
  );
  background-size: 200% 100%;
  animation: dashboard-skeleton-shimmer 1.6s ease-in-out infinite;
}

@keyframes dashboard-skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Inline card-header action icons (refresh + close). Borderless;
   colour-glow on hover instead of a button background. The two
   modifier classes give per-action accent: blue on refresh, red on
   close. */
.dashboard-card-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--tblr-secondary);
  border-radius: 4px;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.15s ease;
}

.dashboard-card-action:focus,
.dashboard-card-action:focus-visible {
  outline: none;
  box-shadow: none;
  background: transparent;
}

.dashboard-card-action:hover {
  background: transparent;
}

.dashboard-card-action--refresh:hover {
  color: var(--tblr-primary);
}

.dashboard-card-action--close:hover {
  color: var(--tblr-danger);
}

/* Close (hide) button in the top-right of every grid item. Same
   hover-only visibility pattern as the drag handle. Click removes
   the slot from this user's saved layout via dashboard_close_controller. */
.dashboard-grid-item .dashboard-close-handle {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  z-index: 200;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 0;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--tblr-secondary);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.15s ease;
}

.dashboard-grid-item:hover .dashboard-close-handle {
  opacity: 0.7;
}

.dashboard-grid-item .dashboard-close-handle:hover {
  opacity: 1;
  color: var(--tblr-danger);
}

/* Resize grip in the bottom-right corner. Same hover-only visibility
   pattern as the drag handle. The diagonal resize cursor signals
   width+height drag; the dashboard_resize_controller takes over on
   pointerdown and snaps to integer cell deltas. */
.dashboard-grid-item .dashboard-resize-handle {
  position: absolute;
  bottom: 0.25rem;
  right:  0.25rem;
  /* Above Apex's internal overlay z-indexes (~10..100) so chart cards
     stay resizable. */
  z-index: 200;
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.7);
  color: var(--tblr-secondary);
  cursor: nwse-resize;
  opacity: 0;
  transition: opacity 0.15s ease;
  user-select: none;
  touch-action: none;
}

.dashboard-grid-item:hover .dashboard-resize-handle,
.dashboard-grid-item--resizing .dashboard-resize-handle {
  opacity: 0.7;
}

.dashboard-grid-item .dashboard-resize-handle:hover {
  opacity: 1;
}

/* While a resize is in progress, outline the cell so the user can see
   which item they're sizing — gives explicit feedback in addition to
   the snap-to-cell layout reflow. */
.dashboard-grid-item--resizing {
  outline: 2px dashed var(--tblr-primary);
  outline-offset: -2px;
}

/* Prevent text-selection mid-drag on the rest of the page. */
.dashboard-grid-item--resizing,
.dashboard-grid-item--resizing * {
  user-select: none;
}

@media (max-width: 768px) {
  .dashboard-grid-item .dashboard-resize-handle {
    display: none;
  }
}

/* Mobile: collapse the grid to a single column with auto-row height. */
@media (max-width: 768px) {
  .dashboard-grid {
    grid-template-columns: 1fr;
    grid-auto-rows: auto;
  }
  .dashboard-grid-item {
    grid-column: 1 / -1 !important;
    grid-row: auto !important;
  }
}

/* Edit mode gating. Read mode (default): hide all handles, the [+]
   placeholder card, and the per-tab X. Edit mode (the page wrapper
   carries .dashboard--edit-mode via dashboard_edit_mode_controller):
   reveal them. */
.container-xl:not(.dashboard--edit-mode) .dashboard-grid-item .dashboard-drag-handle,
.container-xl:not(.dashboard--edit-mode) .dashboard-grid-item .dashboard-close-handle,
.container-xl:not(.dashboard--edit-mode) .dashboard-grid-item .dashboard-resize-handle {
  display: none !important;
}

/* [+] add-widget placeholder card sits at the top of the grid in edit
   mode; in read mode it disappears (no slot for it). */
.dashboard-add-placeholder { display: none; }
.dashboard--edit-mode .dashboard-add-placeholder { display: block; }
.dashboard-add-placeholder-btn {
  border-style: dashed !important;
  border-width: 2px !important;
  border-color: var(--tblr-border-color);
  background: transparent;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.dashboard-add-placeholder-btn:hover {
  border-color: var(--tblr-primary) !important;
  color: var(--tblr-primary) !important;
}

/* Gallery card tile in the "+ Dashboard" modal. Each card is a
   button wrapping an avatar + title + description. Without these
   styles the buttons render bare (no border, no padding) so the
   modal reads as a list of run-on text. Tile gives a consistent
   clickable target with a soft hover. h-100 on the column lets
   adjacent cards equalize height regardless of description length. */
.dashboard-gallery-card {
  display: block;
  width: 100%;
  height: 100%;
  padding: 1rem;
  border: 1px solid var(--tblr-border-color);
  border-radius: 8px;
  background: var(--tblr-card-bg);
  cursor: pointer;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.dashboard-gallery-card:hover {
  border-color: var(--tblr-primary);
  box-shadow: 0 0 0 1px var(--tblr-primary);
  background: rgba(var(--tblr-primary-rgb), 0.02);
}
.dashboard-gallery-card:focus,
.dashboard-gallery-card:focus-visible {
  outline: none;
  border-color: var(--tblr-primary);
  box-shadow: 0 0 0 2px rgba(var(--tblr-primary-rgb), 0.25);
}

/* Icon picker tile in the dashboard settings modal. Clickable label
   wraps a hidden radio + a Tabler icon — without these styles the
   labels were invisible (no border, no padding) so the user couldn't
   tell what was clickable. Active state uses :has(:checked) so the
   selected tile lights up in primary blue without any JS. */
.dashboard-icon-pick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--tblr-border-color);
  border-radius: 6px;
  color: var(--tblr-secondary);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.dashboard-icon-pick:hover {
  border-color: var(--tblr-primary);
  color: var(--tblr-primary);
}
.dashboard-icon-pick.is-active,
.dashboard-icon-pick:has(input:checked) {
  border-color: var(--tblr-primary);
  color: var(--tblr-primary);
  background: rgba(var(--tblr-primary-rgb), 0.1);
}
.dashboard-icon-pick i {
  font-size: 1.1rem;
  line-height: 1;
}

/* Edit toggle in the toolbar. Inherits btn-ghost-secondary in read
   mode; flips to primary blue with a soft tinted background when the
   wrapper is in edit mode so users see the toggle is active. */
.dashboard--edit-mode .dashboard-edit-toggle {
  color: var(--tblr-primary);
  background-color: rgba(var(--tblr-primary-rgb), 0.12);
}
.dashboard--edit-mode .dashboard-edit-toggle:hover {
  color: var(--tblr-primary);
  background-color: rgba(var(--tblr-primary-rgb), 0.2);
}

/* Per-tab X delete button. Hidden by default, shown in edit mode.
   Sits to the right of the tab's title text. */
.dashboard-tab-remove {
  display: none;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  margin-left: 0.5rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--tblr-secondary);
  cursor: pointer;
  vertical-align: middle;
  transition: background 0.15s ease, color 0.15s ease;
}
.dashboard-tab-remove:hover {
  background: rgba(var(--tblr-danger-rgb), 0.12);
  color: var(--tblr-danger);
}
.dashboard--edit-mode .dashboard-tab .dashboard-tab-remove {
  display: inline-flex;
}

/* Blueprint Assignments 30-day heatmap cells.
   `<a>`-wrapped cells get a subtle hover lift + cursor, so they
   read as clickable drill-downs into the Blueprint Activity page. */
.heatmap-cell {
  display: block;
  transition: transform 0.1s ease, filter 0.1s ease;
  cursor: pointer;
}
.heatmap-cell:hover,
.heatmap-cell:focus {
  transform: translateY(-1px);
  filter: brightness(1.1);
  outline: none;
}

/* ------------------------------------------------------------------
   Call Flow editor — left sidebar nav inside the node offcanvas
   (#callFlowNodePanel).

   Structure (full height of the offcanvas):
     +-------------+--------------+
     |  rail       | header       |
     |  (sticky    +--------------+
     |   buttons   | content      |
     |   fill      | (scrollable) |
     |   height)   |              |
     +-------------+--------------+
     | footer (sticky, full width) |
     +------------------------------+

   The rail is a vertical flipbook-style menu: each item is a full-rail-
   width button with a title + subtitle and an active state. Save lives
   in the sticky footer (uses `form="cf-aa-form"` to submit the form).
------------------------------------------------------------------ */
#callFlowNodePanel > turbo-frame {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  min-height: 0;
}
#callFlowNodePanel .offcanvas-body {
  padding: 1rem 1.25rem;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}
.cf-panel {
  display: flex;
  align-items: stretch;
  flex: 1 1 auto;
  min-height: 0;
}
.cf-panel-content {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}
.cf-panel-footer {
  /* Inline the Tabler card-footer treatment — the `.card-footer` class only
     applies its full styling when nested inside a `.card`, which this isn't. */
  flex-shrink: 0;
  padding: 0.75rem 1.25rem;
  background-color: var(--tblr-card-cap-bg, var(--tblr-bg-surface));
  border-top: 1px solid var(--tblr-border-color, #e6e7e9);
}

/* ---- Tom Select inside the call-flow offcanvas: make it look like a
        regular Tabler .form-control (border, padding, focus ring). ---- */
#callFlowNodePanel .ts-control {
  border: 1px solid var(--tblr-border-color, #e6e7e9);
  border-radius: var(--tblr-border-radius, 0.25rem);
  padding: 0.4375rem 0.75rem;
  background-color: var(--tblr-bg-surface, #fff);
  min-height: calc(1.5em + 0.875rem + 2px);
  box-shadow: none;
  transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out;
}
#callFlowNodePanel .ts-wrapper.focus .ts-control {
  border-color: var(--tblr-primary, #066fd1);
  box-shadow: 0 0 0 0.25rem rgba(var(--tblr-primary-rgb, 6, 111, 209), 0.15);
}
#callFlowNodePanel .ts-wrapper.multi .ts-control > .item {
  background: var(--tblr-bg-surface-secondary, #f4f5f7);
  color: var(--tblr-body-color, #232e3c);
  border: 1px solid var(--tblr-border-color, #e6e7e9);
  border-radius: 0.25rem;
  padding: 0.125rem 0.5rem;
  margin: 0.125rem;
}
#callFlowNodePanel .ts-wrapper.multi .ts-control > .item .remove {
  border-left: 1px solid var(--tblr-border-color, #e6e7e9);
  margin-left: 0.375rem;
  padding-left: 0.375rem;
}
#callFlowNodePanel .ts-dropdown {
  border: 1px solid var(--tblr-border-color, #e6e7e9);
  border-radius: var(--tblr-border-radius, 0.25rem);
  box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.08);
}

/* ---- Business hours heatmap ----
   Driven by app/javascript/controllers/business_hours_heatmap_controller.js.
   Days as rows (Mon-Sun), 48 half-hour cells across (00:00-23:30), 16×16
   squares. Per-row text-input ranges editor on the right. Paint by clicking
   or dragging; select a region with a single click; right-click a region
   to delete it; right-drag to erase a rectangle. */
.cf-heatmap-presets {
  display: flex;
  align-items: center;
  gap: .375rem;
  margin-bottom: .75rem;
  flex-wrap: wrap;
}
.cf-heatmap {
  width: max-content;
  border: 1px solid var(--tblr-border-color, #e6e7e9);
  border-radius: var(--tblr-border-radius, .25rem);
  background: var(--tblr-bg-surface, #fff);
  overflow: visible;
}
.cf-heatmap,
.cf-heatmap *:not(input) {
  user-select: none;
  -webkit-user-select: none;
  touch-action: none;
}
.cf-heatmap-headers,
.cf-heatmap-row {
  display: grid;
  grid-template-columns: 40px repeat(48, 14px) 130px;
  align-items: center;
  position: relative;
}
.cf-heatmap-headers {
  border-bottom: 1px solid var(--tblr-border-color, #e6e7e9);
  background: var(--tblr-bg-surface-secondary, #f4f5f7);
  padding: 6px 0;
}
.cf-heatmap-corner { width: 40px; }
.cf-heatmap-time-label {
  font-size: .75rem;
  font-weight: 500;
  color: var(--tblr-body-color, #232e3c);
  padding-left: 2px;
  line-height: 1;
}
.cf-heatmap-day-label {
  font-size: .8125rem;
  font-weight: 600;
  text-align: right;
  padding-right: 8px;
  color: var(--tblr-body-color, #232e3c);
}
.cf-heatmap-row { min-height: 20px; }
.cf-heatmap-row + .cf-heatmap-row {
  border-top: 1px solid var(--tblr-border-color, #e6e7e9);
}
.cf-heatmap-row-weekend { background: rgba(0, 0, 0, .02); }

/* Cells */
.cf-heatmap-cell {
  width: 14px;
  height: 18px;
  border-left: 1px solid rgba(0, 0, 0, .04);
  cursor: pointer;
  background: transparent;
  transition: background-color .08s ease, box-shadow .08s ease;
}
/* Stronger border at every full hour (cells tagged .cf-heatmap-cell-hour-start) */
.cf-heatmap-cell.cf-heatmap-cell-hour-start {
  border-left: 1px solid #adb5bd;
}
.cf-heatmap-cell:hover {
  background: var(--tblr-green-lt, rgba(47, 179, 68, .12));
}
.cf-heatmap-cell.cf-heatmap-cell-open {
  background: var(--tblr-green, #2fb344);
}

/* Pill grouping: contiguous open cells render as one continuous pill */
.cf-heatmap-cell-pill-first { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
.cf-heatmap-cell-pill-last  { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
.cf-heatmap-cell-pill-only  { border-radius: 4px; }
.cf-heatmap-cell-pill-middle,
.cf-heatmap-cell-pill-last  { border-left-color: transparent !important; }

/* Selection: single rounded outline + primary-blue fill for cells inside */
.cf-heatmap-selection-overlay {
  border: 2px solid var(--tblr-primary, #066fd1);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, .9),
    0 0 4px rgba(6, 111, 209, .35);
  border-radius: 5px;
  z-index: 4;
}
.cf-heatmap-cell.cf-heatmap-cell-selected-fill.cf-heatmap-cell-open {
  background: var(--tblr-primary, #066fd1) !important;
}
.cf-heatmap-cell.cf-heatmap-cell-selected-fill:not(.cf-heatmap-cell-open) {
  background: rgba(6, 111, 209, .08) !important;
}

/* Hover preselection: subtle dark outline */
.cf-heatmap-hover-overlay {
  border: 1.5px solid rgba(0, 0, 0, .45);
  border-radius: 5px;
  z-index: 3;
}

/* Drag previews */
.cf-heatmap-cell-preview {
  box-shadow: inset 0 0 0 2px var(--tblr-green, #2fb344);
  background: var(--tblr-green-lt, rgba(47, 179, 68, .22)) !important;
}
.cf-heatmap-cell-preview.cf-heatmap-cell-open {
  background: var(--tblr-green, #2fb344) !important;
  opacity: .8;
}
.cf-heatmap-cell-erase-preview {
  box-shadow: inset 0 0 0 1px var(--tblr-red, #dc3545);
  background: rgba(220, 53, 69, .22) !important;
}
.cf-heatmap-cell-erase-preview.cf-heatmap-cell-open {
  background: rgba(220, 53, 69, .55) !important;
  opacity: .85;
}

/* Per-row range editor */
.cf-heatmap-ranges-header {
  font-size: .625rem;
  color: var(--tblr-secondary, #6b7280);
  padding-left: 8px;
}
.cf-heatmap-ranges {
  width: calc(130px - 12px);
  margin: 0 6px;
  font-size: .6875rem;
  padding: .125rem .375rem;
  font-variant-numeric: tabular-nums;
}
.cf-heatmap-ranges::placeholder {
  color: var(--tblr-secondary, #adb5bd);
  font-style: italic;
}
.cf-heatmap-ranges:focus {
  box-shadow: 0 0 0 2px var(--tblr-green-lt, rgba(47, 179, 68, .25));
}

/* Legend below the grid */
.cf-heatmap-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem 1rem;
  margin-top: .625rem;
  font-size: .75rem;
  color: var(--tblr-secondary, #6b7280);
}
.cf-heatmap-legend-item {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
}
.cf-heatmap-legend-swatch {
  display: inline-block;
  width: 14px;
  height: 14px;
  border-radius: 3px;
  border: 1px solid var(--tblr-border-color, #e6e7e9);
}
.cf-heatmap-legend-swatch-open {
  background: var(--tblr-green, #2fb344);
  border-color: transparent;
}
.cf-heatmap-legend i { font-size: .875rem; }

/* ---- Sidebar nav (rail) ---- */
.cf-tab-rail {
  width: 240px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: var(--tblr-bg-surface-secondary);
  border-right: 1px solid var(--tblr-border-color);
  overflow-y: auto;
}
.cf-tab-btn {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  min-height: 68px;
  padding: 0.75rem 0.875rem;
  border: 0;
  border-left: 3px solid transparent;
  background: transparent;
  color: var(--tblr-body-color);
  text-align: left;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.cf-tab-btn:not(:last-child) {
  border-bottom: 1px solid var(--tblr-border-color);
}
.cf-tab-btn:hover {
  background: var(--tblr-bg-surface);
}
.cf-tab-btn i {
  font-size: 1.125rem;
  color: var(--tblr-secondary);
  flex-shrink: 0;
}
.cf-tab-text {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.cf-tab-title {
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.2;
  color: inherit;
}
.cf-tab-sub {
  font-size: 0.75rem;
  font-weight: 400;
  line-height: 1.2;
  color: var(--tblr-secondary);
}

.cf-tab-btn.active {
  background: var(--tblr-bg-surface);
  border-left-color: var(--tblr-primary);
  color: var(--tblr-primary);
}
.cf-tab-btn.active i,
.cf-tab-btn.active .cf-tab-sub {
  color: var(--tblr-primary);
}
.cf-tab-btn.active .cf-tab-sub {
  opacity: 0.75;
}

.cf-tab-content {
  flex: 1 1 auto;
  min-width: 0;
}

/* ==========================================================================
   Voice Connections - vertical list rows
   Shared by the PBX server rows (PbxServerCardComponent) and the carrier rows
   (CarrierCardComponent), so the two can later merge into one list. White
   "job-listing" style cards; corners follow the branding radius (.card uses
   var(--tblr-border-radius)). Only the hover lift + the display:contents lazy
   frame live here.
   ========================================================================== */
.vc-row {
  cursor: pointer;
  transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.vc-row:hover {
  border-color: var(--tblr-primary);
  box-shadow: 0 2px 10px rgba(24, 36, 51, 0.06);
}

/* A lazy Turbo Frame wrapping the identity + data band must not break the flex
   row grid, so it participates as if it were not there. */
.vc-frame {
  display: contents;
}

/* Small uppercase column label inside each row. */
.vc-col-label {
  font-size: 0.625rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 700;
}

/* Vertical "or" divider — a grey rule between two side-by-side choices with the
   word "or" floated in the middle. Mirrors Tabler's horizontal .hr-text but runs
   top-to-bottom. On small screens it collapses to a horizontal rule. */
.or-divider-vertical {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.or-divider-vertical::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--tblr-border-color);
}
.or-divider-vertical > span {
  position: relative;
  z-index: 1;
  padding: 0.5rem 0;
  background: var(--tblr-bg-surface);
  color: var(--tblr-secondary);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
@media (max-width: 767.98px) {
  .or-divider-vertical::before {
    top: 50%;
    bottom: auto;
    left: 0;
    right: 0;
    width: auto;
    height: 1px;
  }
  .or-divider-vertical > span {
    padding: 0 0.5rem;
  }
}

/* ==========================================================================
   Read-only "View as" impersonation overlay
   Floating pill centered at the bottom of the screen while a super-admin is
   viewing the app through another user's eyes. See
   app/views/layouts/shared/_impersonation_banner.html.erb
   ========================================================================== */
.impersonation-overlay {
  position: fixed;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  z-index: 1080; /* above page content + sticky headers, below modals (1055+ backdrop) */
  pointer-events: none; /* let clicks pass except on the pill itself */
  max-width: calc(100vw - 2rem);
}

.impersonation-overlay-inner {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem 0.5rem 1rem;
  border-radius: 999px;
  background: var(--tblr-orange, #f76707);
  color: #fff;
  border: 1px solid rgba(0, 0, 0, 0.08);
  animation: impersonation-overlay-in 0.18s ease-out;
}

.impersonation-overlay-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  font-size: 1rem;
  flex: 0 0 auto;
}

.impersonation-overlay-text {
  min-width: 0;
  line-height: 1.2;
}

.impersonation-overlay-text > div {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.impersonation-overlay-sub {
  font-size: 0.75rem;
  opacity: 0.85;
}

/* Translucent "on-color" pill that sits on the orange instead of a solid white block */
.impersonation-overlay-exit {
  flex: 0 0 auto;
  border-radius: 999px;
  padding: 0.25rem 0.85rem;
  font-weight: 600;
  color: #fff;
  background: rgba(255, 255, 255, 0.18);
  border: 1px solid rgba(255, 255, 255, 0.5);
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.impersonation-overlay-exit:hover,
.impersonation-overlay-exit:focus {
  color: #fff;
  background: rgba(255, 255, 255, 0.32);
  border-color: rgba(255, 255, 255, 0.85);
}

@keyframes impersonation-overlay-in {
  from { opacity: 0; transform: translateY(0.5rem); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Teams voice config: dim + disable the phone-number fields when the user has no
   Phone System (MCOEV) license (assigning a number would fail in Teams). */
.voice-fields-disabled {
  opacity: 0.5;
  pointer-events: none;
}

/* Setup wizard go_live step: neutral pill chips with a status dot for the
   collapsed "Configuration" summary, and a dashed frame around the lazily-loaded
   directory-user picker. Tabler has no ready-made equivalent for either. */
.wizard-summary-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  font-size: 0.6875rem;
  background: var(--tblr-bg-surface-tertiary);
  border-radius: 999px;
  padding: 0.1875rem 0.5rem 0.1875rem 0.375rem;
  color: var(--tblr-secondary-color);
}

.wizard-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  display: inline-block;
  flex-shrink: 0;
}

.wizard-picker-frame {
  border: 1px dashed var(--tblr-border-color);
  border-radius: 8px;
  padding: 0.75rem;
  background: var(--tblr-bg-surface-tertiary);
}

/* Trunk device tab: the right-hand card (tabs + element table) sits beside a
   fixed-height sidebar card via Bootstrap's default row stretch. .card is
   already display:flex/flex-direction:column (Tabler default) - this just lets
   that flex chain continue through tab-content into whichever tab-pane is
   active, so its card-footer (Verify/Push All/Delete All) pins to the bottom
   of the card's full stretched height instead of trailing right after a short
   table. Scoped to this one component so it doesn't touch tabs elsewhere. */
.trunk-device-tabs {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.trunk-device-tabs > .tab-pane.active {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.trunk-device-tabs > .tab-pane.active > .card-body {
  flex: 0 0 auto;
  min-height: 0;
}

/* The filler div between the table and the footer absorbs whatever height is
   left over, so the footer table sits flush at the bottom of the card instead
   of trailing right after a short table. */
.trunk-device-tabs > .tab-pane.active > .flex-grow-1 {
  flex: 1 1 auto;
  min-height: 0;
}

.trunk-device-tabs > .tab-pane.active > .card-footer {
  flex: 0 0 auto;
}

/* Action columns (Push/Delete/Configure/Verify) on the trunk & service device
   element tables: narrow and right-aligned so the buttons bunch together at
   the row's end instead of spreading out across wide centered columns. */
.device-action-col {
  padding-left: 0.35rem !important;
  padding-right: 0.5rem !important;
  white-space: nowrap;
}

/* Compact variant of Tabler's .status pill (no built-in "sm" size) so status
   badges sit at the same visual weight as the .badge-sm / code text used
   alongside them in the trunk device tables, instead of the default 1.5rem
   tall pill dominating the row. */
.status-sm {
  --tblr-status-height: 1.125rem;
  padding: 0.125rem 0.5rem;
  gap: 0.35rem;
  font-size: 0.75rem;
}
.status-sm .status-dot {
  --tblr-status-size: 0.4rem;
}

/* Marketplace cart button: floating notification badge (Tabler badge-notification)
   cut-out style - system background with a darker shade of the button color as
   border/text, so it reads as a punched-out counter on the button corner. */
.cart-notification-badge {
  background-color: var(--tblr-body-bg);
  color: color-mix(in srgb, var(--tblr-primary) 85%, black);
  border: 1px solid color-mix(in srgb, var(--tblr-primary) 70%, black);
}

/* Partner Finance - collapsible tenant rows on the Licenses tab */
.finance-tenant > summary { cursor: pointer; list-style: none; }
.finance-tenant > summary::-webkit-details-marker { display: none; }
.finance-tenant > summary .ti-chevron-right { transition: transform .15s ease; }
.finance-tenant[open] > summary .ti-chevron-right { transform: rotate(90deg); }

/* ==========================================================================
   ADMIN FORM SHEET  (FormSheetComponent)
   --------------------------------------------------------------------------
   One sheet of labelled sections instead of a stack of cards. The rules this
   layer enforces, because they are what made the old admin forms read as
   assembled rather than designed:

     - one surface per form, sections divided by a hairline, never nested cards
     - one type scale and one family (Tabler's) - weight and letter-spacing do
       the work that extra fonts used to
     - a fixed 12-column field grid, so two forms never disagree about how
       wide a "half" field is
     - the full window width is used, but the reading column stays bounded so
       a wide screen gives more room to the aside, not longer lines
   ========================================================================== */

.sheet-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem; align-items: start; }
.sheet-layout--split { grid-template-columns: minmax(0, 1fr) 420px; }
@media (max-width: 1300px) { .sheet-layout--split { grid-template-columns: minmax(0, 1fr); } }

/* No `overflow: hidden` here: it clips `position: sticky` and the action bar
   stops sticking. Corners are rounded on the first/last child instead. The
   sheet is also the container fields measure themselves against. */
.sheet {
  background-color: var(--tblr-bg-surface);
  border: 1px solid var(--tblr-border-color);
  border-radius: var(--tblr-border-radius);
  container: sheet / inline-size;
}

.sheet-head { padding: 1.15rem 1.5rem; border-bottom: 1px solid var(--tblr-border-color);
              border-radius: var(--tblr-border-radius) var(--tblr-border-radius) 0 0; }
.sheet-head__title { margin: 0; font-size: 1.05rem; font-weight: 600; letter-spacing: -0.01em; }
.sheet-head__sub { margin: 0.2rem 0 0; font-size: 0.85rem; color: var(--tblr-secondary); max-width: 70ch; }

/* The band: what the section is on the left, the fields on the right. The
   about column is fixed so every section on every form starts at the same
   x-position - that alignment is most of what "ordered" means here. */
.sheet-band {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  gap: 2rem;
  padding: 1.4rem 1.5rem;
  border-bottom: 1px solid var(--tblr-border-color);
}
.sheet-band:last-of-type { border-bottom: 0; }
.sheet-band__about h2 { margin: 0 0 0.25rem; font-size: 0.9rem; font-weight: 600; }
/* h2, not h3: the page h1 is the title, so sections are the next level down.
   Heading order is an accessibility requirement, not a styling choice - the
   size here is set deliberately so semantics and appearance can disagree. */
.sheet-band__about p { margin: 0; font-size: 0.8rem; line-height: 1.5; color: var(--tblr-secondary); }
@container sheet (max-width: 820px) {
  .sheet-band { grid-template-columns: minmax(0, 1fr); gap: 0.85rem; padding: 1.15rem; }
}

/* Fixed field grid. Spans are the only widths a form may choose from, which is
   what stops columns drifting between pages. */
.sheet-grid { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0.85rem 1rem; }
.sheet-grid > .sf-3  { grid-column: span 3; }
.sheet-grid > .sf-4  { grid-column: span 4; }
.sheet-grid > .sf-6  { grid-column: span 6; }
.sheet-grid > .sf-8  { grid-column: span 8; }
.sheet-grid > .sf-12 { grid-column: span 12; }
/* Fields re-flow by the width of their own CONTAINER, never the viewport: a
   viewport rule cannot see that an aside or a detail rail has taken the space,
   which is how a wider window ends up producing narrower fields. When a column
   narrows, fields drop to fewer per row - they are never squeezed. */
@container sheet (max-width: 1100px) { .sheet-grid > .sf-3 { grid-column: span 6; } }
@container sheet (max-width: 820px) {
  .sheet-grid > .sf-4, .sheet-grid > .sf-6, .sheet-grid > .sf-8 { grid-column: span 12; }
}
@container sheet (max-width: 560px) { .sheet-grid > .sf-3 { grid-column: span 12; } }
/* simple_form emits its own .mb-3 wrapper; the grid owns spacing here. */
.sheet-grid > [class^="sf-"] > .mb-3:last-child,
.sheet-grid > [class*=" sf-"] > .mb-3:last-child { margin-bottom: 0 !important; }

/* A value the form derives rather than accepts - shown so the consequence of a
   choice is visible, dashed so it reads as output, not input. */
.sheet-derived {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.65rem; border: 1px dashed var(--tblr-border-color);
  border-radius: var(--tblr-border-radius); background-color: var(--tblr-bg-surface-secondary);
  min-height: 2.25rem;
}
.sheet-derived__value { font-weight: 600; }
.sheet-derived__tag {
  font-size: 0.65rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--tblr-primary); margin-left: auto;
}

/* Action bar: stays on screen so the primary action never has to be scrolled for. */
.sheet-actions {
  position: sticky; bottom: 0; z-index: 2;
  border-radius: 0 0 var(--tblr-border-radius) var(--tblr-border-radius);
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.85rem 1.5rem;
  border-top: 1px solid var(--tblr-border-color);
  background-color: var(--tblr-bg-surface-secondary);
}
.sheet-actions .ms-auto { margin-left: auto; }
.sheet-actions__stat { font-size: 0.75rem; color: var(--tblr-secondary); line-height: 1.25; }
.sheet-actions__stat b { display: block; font-size: 0.95rem; color: var(--tblr-body-color); font-variant-numeric: tabular-nums; }

.sheet-aside { position: sticky; top: 1rem; display: flex; flex-direction: column; gap: 1rem; }
@media (max-width: 1300px) { .sheet-aside { position: static; } }

/* Editable line tables inside a sheet: ONE header row, figures right-aligned
   and tabular, inputs sized by the column rather than by their own labels. */
.sheet-lines { width: 100%; border-collapse: collapse; }
.sheet-lines thead th {
  font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--tblr-secondary); font-weight: 600; text-align: left;
  padding: 0 0.5rem 0.45rem; border-bottom: 1px solid var(--tblr-border-color);
}
.sheet-lines thead th.n { text-align: right; }
.sheet-lines tbody td { padding: 0.35rem 0.5rem; border-bottom: 1px solid var(--tblr-border-color); vertical-align: middle; }
.sheet-lines tbody tr:last-child td { border-bottom: 0; }
.sheet-lines td.n { text-align: right; }
.sheet-lines td.n .form-control { text-align: right; }
.sheet-lines td.amount { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.sheet-lines .form-control, .sheet-lines .form-select { min-width: 0; }
.sheet-lines-scroll { overflow-x: auto; }
.sheet-lines-scroll > .sheet-lines { min-width: 640px; }
/* First and last cell sit ON the content edge rather than a half-cell inside
   it, so the table's left edge lines up with the fields and boxes above it.
   These tables used to be wrapped in .sheet-flush, which bleeds 1.5rem past the
   band and put them left of everything else on the page. */
.sheet-lines thead th:first-child, .sheet-lines tbody td:first-child { padding-left: 0; }
.sheet-lines thead th:last-child, .sheet-lines tbody td:last-child { padding-right: 0; }
/* The quiet confirmation a self-saving row shows. */
.sheet-lines .row-status { font-size: .75rem; color: var(--tblr-secondary); }

/* --- a nested table is flush ---------------------------------------------
   Inside a detail tab a table never draws its own border: the card around it
   already has one. `flush: true` drops the border and radius; the rest of this
   makes the component's cells read the same as a `.sheet-lines` table, so a
   read-only list and an editable one are the same object with different
   contents rather than two table designs on neighbouring tabs.
   See .claude/rules/admin-design.md. */
.detail-content__body .simple-table--flush { background: transparent !important; }
.detail-content__body .simple-table--flush > table { margin-bottom: 0; }
.detail-content__body .simple-table--flush thead th {
  background: var(--tblr-bg-surface-tertiary); border-top: 0;
  font-size: .7rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--tblr-secondary); font-weight: 600;
  padding: .4rem .5rem; border-bottom: 1px solid var(--tblr-border-color);
}
/* A table the band above already names has no header bar, so its column labels
   ARE its top edge: quiet, on a hairline, and on the content edge so the columns
   line up with the fields and boxes above them. A titled one keeps the grey
   band, because it has a grey title bar to sit under. */
.detail-content__body .simple-table--flush:not(.simple-table--titled) thead th {
  background: transparent; padding: 0 .5rem .45rem;
}
.detail-content__body .simple-table--flush tbody td {
  padding: .5rem; border-bottom: 1px solid var(--tblr-border-color);
  border-top: 0; vertical-align: middle;
}
.detail-content__body .simple-table--flush tbody tr:last-child td { border-bottom: 0; }
/* First and last cell on the content edge, so the table lines up with the
   fields and boxes above it. Only where there is something above to line up
   with: a titled table keeps its cells inset to the same x as its title bar,
   because a grey band with its labels on the very edge hugs the card's border.
   The component already puts ps-3/pe-3 on those cells, so this is a matter of
   not taking it away. */
.detail-content__body .simple-table--flush:not(.simple-table--titled) thead th:first-child,
.detail-content__body .simple-table--flush:not(.simple-table--titled) tbody td:first-child { padding-left: 0; }
.detail-content__body .simple-table--flush:not(.simple-table--titled) thead th:last-child,
.detail-content__body .simple-table--flush:not(.simple-table--titled) tbody td:last-child { padding-right: 0; }
/* The title bar: the table's own name and count, on the same grey as the column
   heads under it, with no border around the whole thing - the card already has
   one, and a bordered panel inside a bordered card is a box in a box. */
.detail-content__body .simple-table--titled > .d-flex:first-child {
  border-bottom: 1px solid var(--tblr-border-color);
}

/* The commerce profile's segment tree: rows are divs, not a table (they nest and
   collapse), so they borrow the line table's head - one quiet row on a hairline,
   on the band's content edge like everything else in a band. */
.segment-grid__head {
  font-size: .7rem; letter-spacing: .06em; text-transform: uppercase;
  color: var(--tblr-secondary); font-weight: 600;
  padding-bottom: .45rem; border-bottom: 1px solid var(--tblr-border-color);
}

.sheet-total {
  display: flex; justify-content: flex-end; align-items: baseline; gap: 0.9rem;
  margin-top: 0.9rem; padding-top: 0.75rem; border-top: 2px solid var(--tblr-body-color);
}
.sheet-total__label { font-size: 0.75rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--tblr-secondary); }
.sheet-total__value { font-size: 1.2rem; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Taxonomy chip: a TYPE or category (TLS, DDI, 088, a version), not a status.
   Neutral by design so semantic badge colour keeps meaning one thing and stays
   learnable - mixing the two is how a palette stops teaching anything. */
.tag {
  display: inline-block;
  font-size: 0.7rem; font-weight: 600;
  padding: 0.1rem 0.5rem; border-radius: 4px;
  border: 1px solid var(--tblr-border-color);
  color: var(--tblr-secondary);
  background-color: var(--tblr-bg-surface);
}

/* Sticky table header for long lists (past ~25 rows), so a column's meaning
   survives scrolling. Opt-in: the scroll container needs a bounded height. */
.table-sticky-head thead th {
  position: sticky; top: 0; z-index: 3;
  background-color: var(--tblr-bg-surface-tertiary);
}
/* NO inner scroll box. A table extends to its full height and the PAGE scrolls;
   if that is too much at once, the answer is fewer rows per page, which the
   controls strip already offers. A box-within-a-box gave two scrollbars, a
   second thing to find the bottom of, and - because `.card .table-responsive`
   sets `overflow: visible` for the row-action dropdowns - a capped height with
   no scrollbar at all, so any table taller than the cap painted straight over
   its own footer.
   `position: sticky` on the header still works: with no scrolling ancestor it
   sticks to the viewport, which is the behaviour that was wanted anyway. */
.adm-scroll { max-height: none; }

/* A dashboard PANEL is the opposite case and keeps its box. It is a summary
   widget, not an index: the failures list runs to 75 rows in the All lens, and
   a summary that pushes the rest of the dashboard off the screen has stopped
   summarising. `.card .table-responsive { overflow: visible }` (for the row
   dropdowns) has to be beaten on specificity, hence the doubled class. */
.adm-scroll-panel.adm-scroll-panel {
  max-height: min(60vh, 34rem);
  overflow-y: auto;
  overflow-x: auto;
}

/* ============================================================================
   ADMIN INDEX TABLE  (DataTableComponent)

   One row, one line, one rhythm. The row height is set here rather than by
   whichever cell happens to wrap: a list is read by scanning down a column, and
   a column you have to scan in twos is not a column. Anything with more to say
   than fits on a line gets its own column, or lives on the record.
   See .claude/rules/admin-design.md, Index page.
   ========================================================================== */
.adm-table { --adm-row-h: 36px; }
.adm-table thead th {
  background-color: var(--tblr-bg-surface-tertiary);
  font-size: .7rem; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 600; color: var(--tblr-secondary);
  padding: .35rem .75rem; white-space: nowrap; vertical-align: middle;
}
.adm-table tbody td {
  height: var(--adm-row-h); padding: 0 .75rem; vertical-align: middle;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 1px; /* makes ellipsis effective under table-layout: auto */
}
/* Figures compare down the column, so they are right-aligned and tabular. */
.adm-table td.text-end, .adm-table th.text-end { font-variant-numeric: tabular-nums; }
/* The action cell holds controls, not prose, so it is exempt from the ellipsis
   machinery above. A truncated sentence is still readable; a truncated button
   is unreachable - with `overflow: hidden` and `max-width: 1px` inherited here,
   a row's second and third actions were clipped to nothing and the buttons
   simply were not there (seen on /admin/branding, which carries up to three).
   Text columns give way first: abbreviating a name costs less than hiding an
   action. `overflow: visible` also lets the actions dropdown escape the cell. */
.adm-table tbody tr > td:last-child {
  white-space: nowrap; overflow: visible; max-width: none; width: 1%;
}
/* Declared column widths are only hints under `table-layout: auto`: the browser
   sizes each column from the content it holds, so the columns jump every time a
   filter changes the result set. Fixed layout makes a declared width the width.
   Applied only to tables that declare widths (DataTableComponent adds the
   class) - a table with no widths would otherwise render all columns equal. */
.adm-table--fixed { table-layout: fixed; }
/* The ellipsis needs no max-width hack once the column width is authoritative. */
.adm-table--fixed tbody td { max-width: none; }
/* The action column is the one left unsized, so it takes what is left over.
   Under fixed layout "what is left over" can round to nothing, and a clipped
   button is unreachable - so it gets a floor.

   min-width, not width alone: width is only a starting size and any inline
   width beats it, so the column resizer - which pays for a drag by taking width
   from the last column first - could squeeze this one to the shared 48px floor
   and put the split button back outside its own cell. min-width is the floor
   the resizer reads (`floorFor`), so the cascade stops here and moves inward. */
.adm-table--fixed thead th:last-child { width: 6.5rem; min-width: 6.5rem; }

/* ---- Row height ---------------------------------------------------------
   .adm-table is the condensed row: 36px, one line, ellipsis. Comfortable is
   the same table with room to read rather than scan - a taller row, wrapping
   text and a full-size avatar. It is a working preference, remembered per
   table, so both shapes have to hold the same markup: only the metrics move,
   never the columns. */
/* Row-level state: a disabled identity is dimmed as a whole row, not in one
   cell. Set through DataTableComponent's row_class rather than an inline style
   so the table keeps one vocabulary. */
.adm-table tbody tr.adm-row--muted { opacity: .6; }
/* A row waiting on somebody. A marker on the leading edge rather than a wash
   across the row: the row already spends its colour on the status badge, and a
   tinted row outshouts it. Reading down the left edge, the flagged rows are the
   ones to work through. */
.adm-table tbody tr.adm-row--waiting > td:first-child {
  box-shadow: inset 3px 0 0 var(--tblr-warning);
}

/* The density toggle is icon-only, so the button has to carry its own size:
   .btn-icon collapses to the glyph (14px measured), which is neither a
   comfortable target nor readable. */
.dt-density-btn { min-width: 2rem; padding: .25rem .5rem; line-height: 1; }
.dt-density-btn i { font-size: 1.15rem; vertical-align: middle; }

/* The panel menu: one ⋮ holding view settings, column visibility, export and
   reset. Its segmented rows need to sit on one line with the label, and the
   menu must not close when a segment is clicked (data-bs-auto-close="outside"
   handles that side). */
.dt-panel-menu { min-width: 16rem; max-width: 20rem; }
.dt-menu-contents { display: contents; }

/* One grid for every row in the menu, whatever it is made of: an icon slot of a
   fixed width, then the label. The menu mixes three shapes - label-only rows
   (Rows, Width), Tabler `ti` items, and the export partial's `svg` icons with
   their own me-2 - and left to themselves each started at a different x. */
.dt-panel-menu .dropdown-item {
  display: flex; align-items: center; gap: .5rem;
  padding-left: 1rem; padding-right: 1rem;
}
.dt-panel-menu .dropdown-item > i,
.dt-panel-menu .dropdown-item > svg {
  flex: 0 0 1.15rem; width: 1.15rem; height: 1.15rem;
  margin: 0 !important; font-size: 1.05rem; line-height: 1; text-align: center;
}
.dt-panel-menu .dropdown-header { padding-left: 1rem; padding-right: 1rem; }

/* A settings row carries a control rather than an icon, so its label sits on
   the menu's own left edge with the section headers instead of being indented
   into the icon column - there is no icon there to line up with. */
.dt-panel-menu__row {
  display: flex; align-items: center; gap: .5rem;
  padding: .3rem 1rem;
}

/* Expandable rows: a narrow leading column holding the chevron, which turns
   when its detail row is open. */
.adm-table .dt-expand-col { width: 2.75rem; text-align: center; }
.adm-table .dt-expand { --tblr-btn-padding-y: .1rem; --tblr-btn-padding-x: .25rem; }
.adm-table .dt-expand i { transition: transform .15s ease; }
.adm-table .dt-expand.expanded i { transform: rotate(90deg); }
.adm-table tr.dt-detail-row > td { background: var(--tblr-bg-surface-secondary); }

/* A before/after cell in the audit trail's detail table. One line by default so
   a long JSON value cannot stretch the drawer, and the whole thing on hover -
   the point of opening a change is to read the value. Was inline <style> on the
   audit index, so the identity Activity tab, which renders the same partial,
   had no truncation at all. */
.dt-detail-row .audit-cell {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 0;
}
.dt-detail-row .audit-cell:hover {
  overflow: visible; white-space: normal; word-break: break-word;
}

/* Put a column away: revealed on hover of its header, so the header stays quiet
   until you are actually working on it. */
.adm-table thead th .dt-hide-column {
  position: absolute; top: 50%; right: 12px; transform: translateY(-50%);
  border: 0; background: transparent; padding: 0 .15rem; line-height: 1;
  color: var(--tblr-secondary); opacity: 0; transition: opacity .12s ease;
  cursor: pointer; z-index: 3;
}
.adm-table thead th:hover .dt-hide-column { opacity: .75; }
.adm-table thead th .dt-hide-column:hover { opacity: 1; color: var(--tblr-danger); }

/* The refresh control and its state light. Green means a stream is open; every
   other mode is a clock, which is a different claim and gets a neutral dot. The
   pulse is deliberately slow - a table that blinks at you is harder to read. */
/* Hover on the panel controls.
   Tabler's .btn-ghost-* fills the whole chip with grey on hover, which is a
   heavy answer to "is this a button?" - on a bar of five small controls it
   reads as five grey blocks appearing under the cursor. These say it with
   colour instead: the glyph takes the primary tint and the background gets a
   6% wash, enough to place the hit area without shouting. The ACTIVE state
   keeps its white chip, so selected still outranks hovered. */
.adm-controls .dt-density-btn.btn-ghost-secondary:hover,
.adm-controls .dt-density-btn.btn-ghost-secondary.show,
.card-header .dt-density-btn.btn-ghost-secondary:hover,
.card-header .dt-density-btn.btn-ghost-secondary.show,
.dt-panel-head .dt-density-btn.btn-ghost-secondary:hover,
.dt-panel-head .dt-density-btn.btn-ghost-secondary.show,
.dt-panel-menu .dt-density-btn.btn-ghost-secondary:hover,
.dt-refresh .dropdown-toggle:hover,
.dt-refresh .dropdown-toggle:focus-visible,
.dt-refresh .dropdown-toggle.show {
  background-color: color-mix(in srgb, var(--tblr-primary) 6%, transparent);
  color: var(--tblr-primary);
  border-color: transparent;
}
/* OPEN reads the same as hovered, deliberately. Bootstrap gives an open
   dropdown-toggle `.show` and its own active fill, which is the heavy grey
   these rules exist to avoid - so the ⋮ went grey the moment it was clicked
   while everything beside it stayed quiet. Same wash, and Tabler's
   `--tblr-btn-active-*` is overridden so nothing paints underneath it. */
.dt-panel-head .dt-density-btn.btn-ghost-secondary.show,
.adm-controls .dt-density-btn.btn-ghost-secondary.show,
.card-header .dt-density-btn.btn-ghost-secondary.show,
.dt-refresh .dropdown-toggle.show {
  --tblr-btn-active-bg: transparent;
  --tblr-btn-active-color: var(--tblr-primary);
  --tblr-btn-active-border-color: transparent;
}
/* The dots/ring keep their own meaning while the button is hovered. */
.dt-refresh .dropdown-toggle:hover .dt-live-dot.is-live { background: var(--tblr-green); }
.dt-refresh .dropdown-toggle:hover .dt-live-dot.is-off { background: var(--tblr-red); }

.dt-refresh .dropdown-toggle { display: inline-flex; align-items: center; gap: .4rem; }
/* The menu holds seven short labels and a dot; Bootstrap's default min-width is
   sized for sentences and left a wide empty gutter beside them. */
.dt-refresh .dropdown-menu { min-width: 7.5rem; }
.dt-refresh .dropdown-item { padding-left: .75rem; padding-right: .75rem; }
/* Bootstrap's caret carries a vertical-align meant for inline text, which sits
   it low inside a flex row. Centre it on the row and lift it a hair so it reads
   level with the label and the ring. */
.dt-refresh .dropdown-toggle::after {
  align-self: center; vertical-align: middle; margin-top: -.12em;
}
.dt-live-dot {
  width: .5rem; height: .5rem; border-radius: 50%; flex: 0 0 .5rem;
  background: var(--tblr-border-color-dark, #adb5bd);
}
.dt-live-dot.is-off { background: var(--tblr-red); }
.dt-live-dot.is-off { background: var(--tblr-red); }
.dt-live-dot.is-live {
  background: var(--tblr-green);
  box-shadow: 0 0 0 0 rgba(var(--tblr-green-rgb), .6);
  animation: dt-live-pulse 2.4s ease-out infinite;
}
@keyframes dt-live-pulse {
  70% { box-shadow: 0 0 0 .35rem rgba(var(--tblr-green-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--tblr-green-rgb), 0); }
}
@media (prefers-reduced-motion: reduce) {
  .dt-live-dot.is-off { background: var(--tblr-red); }
.dt-live-dot.is-live { animation: none; }
}

/* A running timer shows its remaining time as a ring where the dot would be, so
   the control answers "when next?" without being opened. Ring only, no number:
   at 20px across, "60s" does not fit - the exact seconds are on the title. */
.dt-countdown { position: relative; display: inline-flex; width: 20px; height: 20px; }
/* The ring starts at twelve o'clock and empties clockwise as the seconds go. */
.dt-countdown svg { transform: rotate(-90deg); }
.dt-countdown circle[data-auto-refresh-target="ring"] { transition: stroke-dashoffset 1s linear; }

/* Column re-ordering: the header is the handle. Grab cursor only on the label
   area - the resize strip keeps its own col-resize cursor, so the two
   interactions never fight over the same pixels. */
.adm-table thead th[draggable="true"] .adm-th { cursor: grab; }
.adm-table thead th.dt-dragging { opacity: .4; }
.adm-table thead th.dt-drop-before { box-shadow: inset 3px 0 0 var(--tblr-primary); }
.adm-table thead th.dt-drop-after { box-shadow: inset -3px 0 0 var(--tblr-primary); }

/* Column resize: the indicator, and the column it acts on.
   The handle is ALWAYS visible - a hairline between the header cells, so the
   places you can drag are readable without hunting for them. Pointing at it
   thickens it; dragging paints the whole column so you can see what you are
   sizing rather than guessing from the header alone. */
.adm-table thead th { position: relative; }
.dt-resize-handle {
  position: absolute; top: 20%; right: -1px; width: 9px; height: 60%;
  cursor: col-resize; user-select: none; z-index: 2;
  display: flex; align-items: center; justify-content: center;
}
.dt-resize-handle::before {
  content: ""; width: 2px; height: 100%; border-radius: 1px;
  background: var(--tblr-border-color);
  transition: background-color .12s ease, height .12s ease;
}
.dt-resize-handle:hover::before,
.dt-resize-handle.is-active::before {
  background: var(--tblr-primary); height: 130%;
}

/* The column under the cursor, top to bottom. Deliberately stronger than a
   hover tint: it answers "which column am I about to change?", and a whole
   column of rows needs more than a whisper to read as one thing. */
.adm-table th.dt-col-active,
.adm-table td.dt-col-active {
  background-color: color-mix(in srgb, var(--tblr-primary) 14%, transparent);
  box-shadow: inset -2px 0 0 var(--tblr-primary);
}
.adm-table th.dt-col-active { background-color: color-mix(in srgb, var(--tblr-primary) 22%, transparent); }

/* While dragging, the pointer owns the page: no text selection, no I-beam. */
body.dt-resizing, body.dt-resizing * { cursor: col-resize !important; user-select: none; }

.adm-table--comfortable { --adm-row-h: 56px; }
/* Condensed is ONE line - but it does not THROW AWAY the second line, it folds
   it onto the first. Hiding it made the row shorter by losing information;
   inline, the cell keeps everything it had and the row still cannot grow,
   because the cell's own nowrap + ellipsis truncates whatever does not fit. */
.adm-table:not(.adm-table--comfortable) .adm-cell__sub {
  display: inline; margin-left: .5rem;
}
.adm-table:not(.adm-table--comfortable) .adm-cell__sub::before {
  content: "·"; margin-right: .5rem; opacity: .45;
}
.adm-table--comfortable tbody td {
  padding: .5rem .75rem;
  white-space: normal;      /* a long value wraps instead of being clipped */
  overflow: visible;
  max-width: none;
}
/* A cell that spends the comfortable row's SECOND line on itself rather than on
   a supporting sub-line: two lines of text, then an ellipsis. For the one value
   in a row that is a sentence - a case subject, a description - where one line
   is not enough to tell two of them apart and unbounded wrapping is what used
   to set the row height from whichever record had the most to say.

   Condensed is deliberately left alone: the td there is already nowrap with an
   ellipsis, and the whole point of the 36px row is that it cannot grow. */
.adm-table--comfortable .adm-cell__clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.adm-table--comfortable .avatar { --tblr-avatar-size: 2rem; }
/* Condensed shrinks the avatar to fit the 36px line without setting the row
   height from its content. */
.adm-table:not(.adm-table--comfortable) .avatar { --tblr-avatar-size: 1.25rem; }

.adm-table .btn-sm { --tblr-btn-padding-y: .1rem; --tblr-btn-padding-x: .5rem; font-size: .75rem; }

/* ---- KPI strip ----------------------------------------------------------
   The sparkline fills its cell edge to edge and sits BEHIND the figure, so the
   cell reads as one thing rather than a number with a chart bolted under it.
   The card clips it so the strip keeps its rounded corners; nothing in here is
   sticky, so `overflow: hidden` costs nothing (see rule 8). */
.kpi-strip { overflow: hidden; }
.kpi-cell { position: relative; }
/* The body reserves more bottom padding than the sparkline is tall, so the
   quiet secondary line always sits ABOVE the drawn line rather than being
   crossed by it - a graph behind a figure is texture, a graph through a
   sentence is damage. */
.kpi-cell__body { position: relative; z-index: 1; padding-bottom: 3.25rem !important; }
.kpi-spark {
  position: absolute; inset: auto 0 0 0; width: 100%; height: 2.75rem;
  display: block; z-index: 0; pointer-events: none;
}
.kpi-spark__line { fill: none; stroke: var(--tblr-primary); stroke-width: 1.75; opacity: .9; }
.kpi-spark__fill { fill: var(--tblr-primary); opacity: .14; stroke: none; }
/* Bars for an event count. Same surface as the line, so the two styles read as
   one family - what differs is what is being counted, not the decoration. */
.kpi-spark__bar { fill: var(--tblr-primary); opacity: .45; }
/* Direction is carried by the arrow as well as the colour, so the trend is
   still readable without colour vision. */
.kpi-trend {
  display: inline-flex; align-items: center; gap: .15rem;
  font-size: .75rem; font-weight: 600; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.adm-controls { background-color: var(--tblr-bg-surface-tertiary); }

/* What is narrowing the list, above it, each removable in one click. A column
   search can be set from a cell (clicking a count drills into what it counts),
   so it has to be visible somewhere other than the head it came from. */
.adm-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  font-size: .75rem; padding: .1rem .25rem .1rem .5rem;
  background: var(--tblr-bg-surface); border: 1px solid var(--tblr-border-color);
  border-radius: 20px; color: var(--tblr-body-color); max-width: 22rem;
}
.adm-chip__label { color: var(--tblr-secondary); }
.adm-chip__clear {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  color: var(--tblr-secondary); line-height: 1;
}
.adm-chip__clear:hover { background: var(--tblr-bg-surface-secondary); color: var(--tblr-red); }

/* The head's own row: label, sort control, and the column's search. */
.adm-th { display: inline-flex; align-items: center; gap: .25rem; }
.adm-table thead th .sortable-header { padding-right: 18px !important; }

/* ---- a column's own search ---------------------------------------------
   The control belongs to the thing it narrows, so it opens from the head
   rather than living in a strip that names no column. Nothing is spent until
   it is used; an active one carries a dot. */
.col-filter { position: relative; display: inline-flex; }
.col-filter__toggle {
  background: none; border: 0; padding: 0 .1rem; line-height: 1; cursor: pointer;
  color: var(--tblr-secondary); position: relative; font-size: .8rem;
}
.col-filter__toggle:hover { color: var(--tblr-primary); }
.col-filter__toggle.is-active { color: var(--tblr-primary); }
.col-filter__toggle.is-active::after {
  content: ""; position: absolute; top: -1px; right: -2px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--tblr-primary);
}
.col-filter__pop {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 20;
  display: block; width: 15rem; padding: .6rem;
  background: var(--tblr-bg-surface); border: 1px solid var(--tblr-border-color);
  border-radius: 6px; box-shadow: 0 8px 24px rgba(4, 32, 69, .14);
  text-transform: none; letter-spacing: 0; font-weight: 400;
  white-space: normal;
}
.col-filter__pop[hidden] { display: none; }
.col-filter__label {
  display: block; font-size: .7rem; color: var(--tblr-secondary); margin-bottom: .25rem;
}
.col-filter__range { display: flex; gap: .35rem; }
.col-filter__foot {
  display: flex; justify-content: space-between; align-items: center; gap: .5rem; margin-top: .5rem;
}
/* The last columns' popovers would open past the panel's right edge. */
.adm-table thead th:nth-last-child(-n+2) .col-filter__pop { left: auto; right: 0; }


/* A nested-record table inside a band bleeds to the band edge rather than
   sitting in a padded inner box. This is the one sanctioned exception to the
   padding rule: a padded box here would be the card-inside-a-card the system
   forbids, and re-padding would misalign the table's columns from the fields
   above it. */
.sheet-flush { margin: 0 -1.5rem; border-top: 1px solid var(--tblr-border-color);
               border-bottom: 1px solid var(--tblr-border-color); }
.sheet-flush > .table-responsive > table { margin-bottom: 0; }
.sheet-flush thead th { background-color: var(--tblr-bg-surface-secondary); }
.sheet-flush thead th:first-child, .sheet-flush tbody td:first-child { padding-left: 1.5rem; }
.sheet-flush thead th:last-child,  .sheet-flush tbody td:last-child  { padding-right: 1.5rem; }
@container sheet (max-width: 820px) {
  .sheet-flush { margin: 0 -1.15rem; }
  .sheet-flush thead th:first-child, .sheet-flush tbody td:first-child { padding-left: 1.15rem; }
  .sheet-flush thead th:last-child,  .sheet-flush tbody td:last-child  { padding-right: 1.15rem; }
}

/* --- a block of reported facts ------------------------------------------- */
/* Observed values - what a device said about itself - rather than fields.
   
   Four tracks: label, value, label, value. So a row carries TWO readings side
   by side, read left to right, and the next row starts the next pair - while
   every label lines up on one of two edges and every value on one of two more.
   That alignment is what makes it scannable; the two pairs per row are what
   stop seven readings becoming seven lines of mostly empty space.
   
   Two failure modes this deliberately avoids: the label above its value (which
   doubles the height and puts nothing in a predictable place), and one pair per
   row (which reads as seven unrelated sentences).
   
   Every fact is listed whether or not there is a value: a missing one renders
   as `-`, because a hidden row makes "not reported" look like "not
   applicable". */
.sheet-facts { display: grid; margin: 0;
               grid-template-columns: minmax(6.5rem, 9rem) minmax(0, 1fr)
                                      minmax(6.5rem, 9rem) minmax(0, 1fr);
               gap: .4rem 1rem; align-items: baseline; }
.sheet-facts dt { font-size: .8125rem; color: var(--tblr-secondary); font-weight: 400; margin: 0; }
.sheet-facts dd { margin: 0; color: var(--tblr-body-color); font-variant-numeric: tabular-nums;
                  min-width: 0; overflow-wrap: anywhere; }
.sheet-facts code { font-size: .8125rem; }

/* One pair per row only when two genuinely will not fit - by container width,
   since the rail and the band's about column have already taken their share. */
@container sheet (max-width: 560px) {
  .sheet-facts { grid-template-columns: minmax(6.5rem, 9rem) minmax(0, 1fr); }
}

/* --- data boxes ---------------------------------------------------------- */
/* A reading as an object rather than a table row: label above, value large
   enough to be read at a glance. Use for a handful of headline values someone
   scans - not for a long list of attributes, where the label/value pair grid
   above is faster to read down.
   
   Boxes fill the row and re-flow by count, so five readings on a wide band
   become three-and-two on a narrow one rather than shrinking. */
.sheet-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
               gap: .5rem; margin: 0; }
.sheet-stat { padding: .6rem .85rem; border-radius: 6px;
              background: var(--tblr-bg-surface-secondary);
              border: 1px solid var(--tblr-border-color-light, rgba(4, 32, 69, .06));
              min-width: 0; }
.sheet-stat__label { font-size: .75rem; color: var(--tblr-secondary); margin-bottom: .15rem;
                     white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sheet-stat__value { font-size: 1.0625rem; font-weight: 600; color: var(--tblr-body-color);
                     line-height: 1.25; font-variant-numeric: tabular-nums;
                     overflow-wrap: anywhere; }
.sheet-stat__value .unit { font-size: .8125rem; font-weight: 400; color: var(--tblr-secondary); }
/* A consequence of the value, under it: what being connected or missing means
   for the run. Set on its own quiet line rather than trailing the value, where
   a sentence next to a badge outweighs the fact it is explaining. */
.sheet-stat__note { font-size: .75rem; color: var(--tblr-secondary); line-height: 1.35;
                    margin-top: .2rem; }
/* A machine value - a serial, a MAC - is smaller because it is copied, not
   compared. */
.sheet-stat__value.is-machine { font-size: .875rem; font-family: var(--tblr-font-monospace); }

/* The age of the reading, above the boxes. Quiet when fresh; the one thing
   that turns colour is a reading old enough that the values below may no longer
   be true - that is state, which is the only thing colour means here. */
.sheet-reading { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
                 margin-bottom: .6rem; font-size: .8125rem; color: var(--tblr-secondary); }
.sheet-reading__age { font-weight: 600; color: var(--tblr-body-color); }
.sheet-reading__age.is-stale { color: var(--tblr-orange); }
.sheet-reading__note { flex-basis: 100%; margin-top: -.15rem; color: var(--tblr-orange); }

/* An empty state inside a band: states the fact, points at the action that
   fills it, and never grows an action of its own. */
.sheet-empty { padding: 1rem 1.15rem; border: 1px dashed var(--tblr-border-color);
               border-radius: 4px; color: var(--tblr-secondary); font-size: .875rem;
               background: var(--tblr-bg-surface-secondary); }

/* --- a panel of machine-readable text ------------------------------------ */
/* Same idea as the table panel: a header bar carrying the controls, then the
   content inside one surface. The pills used to float loose above a dark slab,
   which read as two unrelated objects.

   Deliberately NOT .sheet-flush. A table goes flush because re-padding it
   misaligns its columns; a code panel has no columns to misalign, and bleeding
   it 1.5rem past the band left its edge 24px left of every other band's
   content. It sits on the content edge like the data boxes above it. */
.code-panel { border: 1px solid var(--tblr-border-color); border-radius: 6px;
              overflow: hidden; }
.code-panel__bar { display: flex; align-items: center; gap: .75rem;
                   padding: .5rem 1rem;
                   background: var(--tblr-bg-surface-secondary);
                   border-bottom: 1px solid var(--tblr-border-color); }

/* A segmented control, not tabs: these are two shapes of the same content, so
   the switch belongs beside the content it changes rather than above it.

   Neutral, not primary blue. INI vs JSON is taxonomy - which shape you are
   looking at - and semantic colour in this system means state only. A solid
   blue chip here claims a significance the choice does not have. */
.code-switch { display: inline-flex; gap: 2px; padding: 2px; border-radius: 6px;
               background: var(--tblr-border-color-light, rgba(4, 32, 69, .06)); }
.code-switch button { border: 0; background: transparent; border-radius: 4px;
                      padding: .15rem .7rem; font-size: .8125rem; font-weight: 500;
                      color: var(--tblr-secondary); line-height: 1.6; }
.code-switch button:hover { color: var(--tblr-body-color); }
.code-switch button.active { background: var(--tblr-bg-surface);
                             color: var(--tblr-body-color); font-weight: 600;
                             box-shadow: 0 1px 2px rgba(0, 0, 0, .06); }

/* Bounded height: the file runs to thousands of lines and the band should not.
   It scrolls in its own box, so the page never grows to fit a machine's output.
   Tabler's dark `pre` is kept - a file as the machine stores it is the one place
   a terminal surface belongs. */
.sheet-code { margin: 0; border-radius: 0; max-height: 32rem; overflow: auto; }

/* The operation log inside a console's code panel: the same terminal surface as
   a machine-stored file, bounded so a long run does not grow the page. Its
   height is set here rather than inline because the fullscreen toggle overrides
   it, and an override needs something to override. */
.console-log {
  height: 25rem; overflow-y: auto; padding: 0.75rem 1rem;
  background: var(--tblr-dark); color: #fff;
  font-family: var(--tblr-font-monospace); font-size: 0.8125rem; line-height: 1.5;
}

/* ============================================================================
   ADMIN DETAIL SHELL  (DetailShellComponent)

   The detail page: one card, a rail, a content column. Replaces the
   hand-rolled `col-md-3 border-end` + list-group that thirteen sections each
   grew their own copy of.

   The rail is a COLUMN OF THE CARD, not a second surface, so "one surface per
   page" still holds. See .claude/rules/admin-design.md, Part 3.
   ========================================================================= */

/* The card is the query container: the rail collapses when the CARD is narrow,
   not when the window is. A viewport rule cannot see that something else has
   taken the space - the mistake that once produced narrower fields on a wider
   screen. */
/* The container is the FRAME, not the card: an element cannot respond to its
   own container query, so with container-type on the grid itself the rail's
   border collapsed at narrow widths while the columns never did. */
.detail-shell-frame { container: detail / inline-size; }
.detail-shell { display: grid; grid-template-columns: 17rem minmax(0, 1fr); }

.detail-rail { border-right: 1px solid var(--tblr-border-color); padding: 1rem 0; min-width: 0; }
.detail-content { display: flex; flex-direction: column; min-width: 0; }
.detail-content__body { flex: 1 1 auto; min-width: 0; }

/* --- identity block: what am I looking at, before a tab is chosen --------- */
.detail-ident { padding: .35rem 1.15rem 1rem; border-bottom: 1px solid var(--tblr-border-color);
                margin-bottom: .75rem; }
.detail-ident__name { font-weight: 600; color: var(--tblr-body-color); line-height: 1.3;
                      word-break: break-word; }
.detail-ident__parent { font-size: .8125rem; color: var(--tblr-secondary); margin-top: .1rem;
                        word-break: break-word; }
.detail-ident__chips { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .6rem; }

/* --- grouped navigation -------------------------------------------------- */
.detail-rail__group { font-size: .6875rem; letter-spacing: .06em; text-transform: uppercase;
                      color: var(--tblr-secondary); font-weight: 600;
                      margin: 1rem 0 .35rem; padding: 0 1.15rem; }
.detail-rail__group:first-child { margin-top: 0; }

.detail-rail__item { display: flex; align-items: center; gap: .5rem;
                     padding: .45rem 1.15rem; color: var(--tblr-body-color);
                     text-decoration: none; border-left: 2px solid transparent; }
.detail-rail__item:hover { background: var(--tblr-bg-surface-secondary); color: var(--tblr-primary); }
.detail-rail__item.is-current { background: var(--tblr-bg-surface-secondary);
                                border-left-color: var(--tblr-primary);
                                color: var(--tblr-primary); font-weight: 600; }
.detail-rail__item i { font-size: 1rem; line-height: 1; flex: none; }
.detail-rail__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.detail-rail__count { margin-left: auto; flex: none; }

/* --- the record line ----------------------------------------------------- */
/* Identifier and dates, under the card rather than inside it. It is neither
   content nor part of any tab - it is the page's own footnote about which row
   you are looking at, so it sits outside the surface and stays quiet. */

.detail-record { margin: .6rem .25rem 0; font-size: .75rem; color: var(--tblr-secondary); }

/* A collapse toggle that says what it will do next, not what it did.
   Pair <span class="when-collapsed"> / <span class="when-expanded"> inside any
   [data-bs-toggle="collapse"] button; Bootstrap keeps aria-expanded true. */
.collapse-toggle[aria-expanded="false"] .when-expanded { display: none; }
.collapse-toggle[aria-expanded="true"] .when-collapsed { display: none; }

/* ---- Support case page (Direction B) ------------------------------------
   A strip that identifies the case, a timeline that records it, a rail of
   context. The timeline is typed: replies get a card and a face, everything
   else - files arriving, status and priority changes - gets one quiet line on
   a small node, so a skim of the left edge tells the shape of the case. */
.case-grid {
  display: grid;
  /* Half and half, full window: what IS the case, and what happened to it. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 1199.98px) {
  .case-grid { grid-template-columns: 1fr; }
}

.case-tl { position: relative; padding-left: 2.4rem; }
.case-tl::before {
  content: ""; position: absolute; left: 14px; top: 10px; bottom: 10px;
  width: 1px; background: var(--tblr-border-color);
}
.case-tl__item { position: relative; padding-bottom: 1.1rem; }
.case-tl__item:last-child { padding-bottom: .25rem; }

.case-tl__node {
  position: absolute; left: -2.4rem; top: 0;
  width: 28px; height: 28px; border-radius: 50%;
  background-size: cover; background-position: center;
  background-color: var(--tblr-bg-surface-secondary);
  border: 1px solid var(--tblr-border-color);
  /* The halo is what lifts the node off the spine. The PAGE background, not
     the card surface: the timeline floats on the page. */
  box-shadow: 0 0 0 3px var(--tblr-body-bg);
}
.case-tl__item--event .case-tl__node {
  width: 11px; height: 11px; left: calc(-2.4rem + 9px); top: 5px;
  background-color: var(--tblr-secondary); border: 0;
}
/* Colour is state: only a change that raised urgency earns it. */
.case-tl__item--warn .case-tl__node { background-color: var(--tblr-warning); }

.case-tl__meta { font-size: .78rem; color: var(--tblr-secondary); display: flex; gap: .45rem; align-items: baseline; flex-wrap: wrap; }
.case-tl__who { font-weight: 600; color: var(--tblr-body-color); font-size: .85rem; }
.case-tl__side {
  font-size: .68rem; border: 1px solid var(--tblr-border-color); border-radius: 3px;
  padding: 0 .3rem; color: var(--tblr-secondary);
}
.case-tl__item--agent .case-tl__side {
  border-color: transparent; background: var(--tblr-primary-lt); color: var(--tblr-primary);
}

.case-tl__card {
  margin-top: .4rem; border: 1px solid var(--tblr-border-color); border-radius: 6px;
  background: var(--tblr-bg-surface); padding: .65rem .8rem;
}
/* Support's replies are told apart by surface, not by flipping sides: a
   record is one column, not a two-sided chat. The tint mixes from the
   whitelabel primary, so a partner's own colour carries through. */
.case-tl__item--agent .case-tl__card {
  background: color-mix(in srgb, var(--tblr-primary) 7%, var(--tblr-bg-surface));
  border-color: color-mix(in srgb, var(--tblr-primary) 22%, var(--tblr-border-color));
}
.case-tl__event { font-size: .82rem; color: var(--tblr-secondary); }
.case-tl__event strong { color: var(--tblr-body-color); }


/* The timeline filter wears the branding colour: the chosen view is filled
   with the primary, the others are outlined in a darker mix of it, and the
   shared edges double as dividers. */
.case-filter .btn {
  color: var(--tblr-primary);
  background: transparent;
  border-color: color-mix(in srgb, var(--tblr-primary) 60%, var(--tblr-border-color));
}
.case-filter .btn:hover {
  background: color-mix(in srgb, var(--tblr-primary) 8%, transparent);
  border-color: color-mix(in srgb, var(--tblr-primary) 60%, var(--tblr-border-color));
  color: var(--tblr-primary);
}
.case-filter .btn.active {
  background: var(--tblr-primary);
  border-color: var(--tblr-primary);
  color: #fff;
}
.detail-record code { font-size: .75rem; }

/* --- action bar: content column only ------------------------------------- */
/* It acts on what is shown, not on the whole record - so it does not span the
   rail. Sticky for the same reason the sheet's is: not hunting for Save. */
.detail-actions { position: sticky; bottom: 0; z-index: 2;
                  display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
                  padding: .85rem 1.5rem;
                  border-top: 1px solid var(--tblr-border-color);
                  background: var(--tblr-bg-surface-secondary); }

/* A sheet inside the content column gives up its own edges: the detail card is
   already the surface, and a bordered sheet inside it is the card-in-a-card the
   system forbids. Descendant, not child - FormSheetComponent wraps the sheet in
   a layout div, so the child selector this started as never matched and the
   double border shipped invisibly. */
.detail-content__body .sheet { border: 0; border-radius: 0; }
.detail-content__body .sheet-layout { margin: 0; }

/* A table panel rendered as the tab's whole content is the content column's
   surface - it never becomes a card inside the card. */
.detail-content__body > .card { border: 0; border-radius: 0; box-shadow: none; margin-bottom: 0; }

/* --- consoles: cards that are already inside the card -------------------- */
/* Three tabs are operating panels rather than forms or lists - the service
   AudioCodes tab, its provisioning runner, and the trunk device tab. Each is a
   10-18-target Stimulus controller driving every row inside it, with ghost
   tables whose colgroups must stay byte-identical, so their markup is not
   rewritten (see .claude/rules/admin-design.md, Part 5).

   What that markup does have is `.card` chrome from before there was a card
   around it, which is the one thing the system bans hardest. Rather than edit
   1,400 lines of fragile console, the chrome is neutralised where it sits: the
   panels become plain blocks separated by a hairline, which is what a band
   looks like. */
.detail-content__body .card { border: 0; box-shadow: none; border-radius: 0;
                              background: transparent; }
.detail-content__body .card + .card { border-top: 1px solid var(--tblr-border-color);
                                      margin-top: .25rem; padding-top: .25rem; }
.detail-content__body .card > .card-header { background: transparent; border-bottom: 0;
                                             padding-left: 0; padding-right: 0;
                                             min-height: auto; }
.detail-content__body .card > .card-body { padding-left: 0; padding-right: 0; }
.detail-content__body .card > .card-footer { background: transparent; padding-left: 0;
                                             padding-right: 0; }
/* The log pane and any other deliberately dark surface keeps its own ground -
   it is output, not chrome. */
.detail-content__body .card > .card-body.bg-dark { padding: 1rem; border-radius: 4px; }
/* A flush table inside a console still needs its edges to reach the column. */
.detail-content__body .card > .card-body.card-no-padding { padding: 0; }

/* --- narrow card: rail above the content --------------------------------- */
@container detail (max-width: 900px) {
  .detail-shell { grid-template-columns: 1fr; }
  .detail-rail { border-right: 0; border-bottom: 1px solid var(--tblr-border-color);
                 padding-bottom: .5rem; }
  .detail-rail__item { border-left: 0; border-bottom: 2px solid transparent; }
  .detail-rail__item.is-current { border-left: 0; border-bottom-color: var(--tblr-primary); }
}

/* Migration browser: "Hide accounts without licences".
   The class lands on the tree root, so rows added by a later lazy level load are
   hidden too — a one-off JS sweep would only catch the rows present when it ran.
   A blank attribute (source server that does not report the count) matches
   nothing, so nothing is hidden rather than everything. */
.migrate-hide-empty .migrate-node[data-subtree-licenses="0"] {
  display: none;
}

/* ---------------------------------------------------------------------------
   Admin > Services: identity change timeline and sync-run block graph.
   Both are read-only inspection surfaces on the detail shell.
   --------------------------------------------------------------------------- */

/* One entry per recorded change, with its field diff table beneath. */
.adm-timeline__entry {
  padding: .5rem 0;
  border-bottom: 1px solid var(--tblr-border-color);
}
.adm-timeline__entry:last-child { border-bottom: 0; }
.adm-timeline__head { display: flex; align-items: center; flex-wrap: wrap; }
.adm-timeline__diff { margin-top: .25rem; }
.adm-timeline__diff td { padding: .15rem .5rem; border-top: 0; font-size: .8125rem; }
.adm-timeline__diff tr td:first-child { padding-left: 0; }

/* GitHub-style run grid: one block per sync run, oldest first. Colour is state
   (rule 5) - it is the only thing these blocks encode. */
.run-blocks { display: flex; flex-wrap: wrap; gap: 3px; }
.run-block {
  width: 11px; height: 11px; border-radius: 2px;
  background-color: var(--tblr-border-color);
  display: block; text-decoration: none;
}
.run-block--completed { background-color: var(--tblr-green); }
.run-block--failed    { background-color: var(--tblr-red); }
/* The workflow finished, but something inside it did not - a Teams policy sync
   that timed out, say. Neither green nor red is true of that run. */
.run-block--partial   { background-color: var(--tblr-orange); }
.run-block--running   { background-color: var(--tblr-blue); }
.run-block--cancelled { background-color: var(--tblr-secondary); opacity: .45; }
a.run-block:hover { outline: 2px solid var(--tblr-border-color-active); outline-offset: 1px; }
.run-blocks__legend { display: flex; align-items: center; gap: 1rem; font-size: .75rem; }
.run-blocks__key { display: inline-flex; align-items: center; gap: .35rem; }


/* ── The mirrored spine ──────────────────────────────────────────────────────
   A block for fields that exist on BOTH legs of a call: ingress left, the
   field name down the middle, egress right. It lives inside a band alongside
   .sheet-stats, which keeps the sections exactly as they were - the pairing
   changes how a field is read, not which section it belongs to.

   Why a spine rather than two lists: a leg-to-leg difference is the question
   the call record exists to answer, and two values in two separate lists never
   get compared. Across one label they do - production had a call carrying
   packet loss 10 on ingress against 58 on egress, which was invisible until
   the two sat either side of the same word.

   Re-flow is by CONTAINER, per admin-design.md rule 10: the sheet already
   declares `container: sheet / inline-size`, so a narrow card collapses each
   row to label-over-pair instead of squeezing three columns. */
.sheet-spine {
  display: grid;
  grid-template-columns: 1fr minmax(7rem, 11rem) 1fr;
  align-items: baseline;
  column-gap: 0;
}
.sheet-spine__head { display: contents; }
.sheet-spine__head > * {
  padding-bottom: .35rem;
  margin-bottom: .35rem;
  border-bottom: 1px solid var(--tblr-border-color);
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.sheet-spine__head .is-ingress { text-align: right; padding-right: .75rem; }
.sheet-spine__head .is-egress  { text-align: left;  padding-left: .75rem; }
/* Ingress and egress take a hue in the HEADERS ONLY. Colour is state (rule 5);
   a side is not a state, so the values below stay neutral and red on a figure
   keeps meaning "threshold", not "this leg". */
.sheet-spine__head .is-ingress { color: var(--tblr-indigo); }
.sheet-spine__head .is-egress  { color: var(--tblr-teal); }

.sheet-spine__row { display: contents; }
.sheet-spine__row > * { padding: .3rem 0; min-width: 0; }
.sheet-spine__row:hover > * { background: var(--tblr-bg-surface-tertiary); }

.sheet-spine__in,
.sheet-spine__out {
  font-size: .875rem;
  font-variant-numeric: tabular-nums;
  min-width: 0;
}
.sheet-spine__in > div,
.sheet-spine__out > div {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* What the value WAS, under the value that was used. Quiet, because the number
   actually carried is the one being read. */
.sheet-spine__note {
  font-size: .6875rem;
  color: var(--tblr-secondary);
  font-family: var(--tblr-font-sans-serif);
}
.sheet-spine__in  { text-align: right; padding-right: .75rem; }
.sheet-spine__out { text-align: left;  padding-left: .75rem; }
.sheet-spine__key {
  text-align: center;
  font-size: .75rem;
  color: var(--tblr-secondary);
}
/* Computed, not decorative: it appears only where the two legs actually
   disagree, so it can be trusted. Never colour alone - the glyph carries it. */
.sheet-spine__mark { color: var(--tblr-orange); font-weight: 700; margin-left: .15rem; }
.sheet-spine__row.is-differs .sheet-spine__in,
.sheet-spine__row.is-differs .sheet-spine__out { font-weight: 600; }

@container sheet (max-width: 640px) {
  .sheet-spine { grid-template-columns: 1fr 1fr; }
  .sheet-spine__head > * { margin-bottom: .2rem; }
  .sheet-spine__head .is-ingress { text-align: left; padding-right: 0; }
  .sheet-spine__row > * { padding-top: 0; }
  /* The label leads its own pair rather than sitting between two squeezed
     columns - the same content re-flowing, not a second layout. */
  .sheet-spine__key {
    grid-column: 1 / -1;
    text-align: left;
    padding-top: .5rem !important;
    border-top: 1px solid var(--tblr-border-color);
  }
  .sheet-spine__in { text-align: left; padding-right: .5rem; }
}


/* ── Raw field map ───────────────────────────────────────────────────────────
   The SDR line is positional: 88 pipe-separated values, none of them labelled.
   Listing the parsed name beside each value is what turns "copyable" into
   "readable", and it is the only place the ~30 fields the page does not
   promote to a column can be seen at all.

   Bounded and scrolling like the code panel it sits in - 57 rows would
   otherwise push the envelope below the fold on every call. */
.raw-fields {
  display: grid;
  grid-template-columns: minmax(11rem, auto) 1fr;
  gap: 0 1rem;
  max-height: 20rem;
  overflow: auto;
  padding: .5rem .75rem .75rem;
  font-family: var(--tblr-font-monospace);
  font-size: .75rem;
  line-height: 1.7;
}
.raw-fields__key { color: var(--tblr-secondary); white-space: nowrap; }
.raw-fields__val { color: var(--tblr-body-color); word-break: break-all; }
.raw-fields__val.is-empty { color: var(--tblr-secondary); opacity: .5; }
.code-panel__meta { margin-left: auto; font-size: .6875rem; color: var(--tblr-secondary); }


/* ── Call timeline ───────────────────────────────────────────────────────────
   Start ──ring── Connect ──talk── End, drawn to scale. The RATIO is what the
   block adds: 2 seconds of ringing before a 4-minute conversation is a
   different call from the reverse, and the two duration figures beneath say
   nothing about that on their own.

   Every label is anchored to its own point. Laying the feet out separately with
   space-between put CONNECT in the middle of the track while its dot sat 170px
   to the left - the timestamps labelled the wrong instants, which is worse than
   not drawing them.

   Segments grow by their millisecond count with a floor, so a sub-second ring
   stays visible instead of collapsing. Neutral throughout: a duration is not a
   state, so it takes no semantic colour (rule 5), and the ingress/egress hues
   are deliberately not reused - they mean a leg, not a span. */
.call-timeline { max-width: 54rem; }
/* The whole span, bracketed over the two it is made of - |‾‾‾‾‾‾| with the
   figure centred beneath. Bracketed rather than printed in a corner because
   the shape says "all of this", which a floating number does not. */
.call-timeline__total {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.call-timeline__total-bar {
  width: 100%;
  height: .45rem;
  border: 2px solid var(--tblr-border-color-active);
  border-bottom: 0;             /* ticks point down, at the track it measures */
  border-top-left-radius: 2px;
  border-top-right-radius: 2px;
}
.call-timeline__total-value {
  margin-top: .3rem;
  font-size: .9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.call-timeline__total-label {
  font-size: .6875rem;
  font-weight: 400;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tblr-secondary);
  margin-right: .25rem;
}

.call-timeline__track {
  display: flex;
  align-items: center;
  padding: 1.6rem 0 3rem;   /* room for the labels above and the feet below */
}
.call-timeline__point { position: relative; flex: 0 0 auto; line-height: 0; }
.call-timeline__dot {
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 50%;
  background: var(--tblr-body-bg);
  border: 3px solid var(--tblr-secondary);
}
.call-timeline__foot {
  position: absolute;
  top: 1rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  white-space: nowrap;
}
/* The end points align to the track's edges so they cannot overhang it. */
.call-timeline__point.is-first .call-timeline__foot { left: 0; transform: none; }
.call-timeline__point.is-last .call-timeline__foot { left: auto; right: 0; transform: none; text-align: right; }

.call-timeline__seg {
  flex: 1 1 auto;
  /* The floor is set by the LABELS, not by taste: each timestamp is ~52px and
     is centred on its dot, so two dots closer than ~6rem print their times on
     top of each other - a 2.3s ring inside a 4-minute call did exactly that
     ("10:37:3510:37:37"). A short segment is therefore drawn at the floor
     rather than to scale; the figures beneath remain exact. */
  min-width: 6.5rem;
  position: relative;
  /* 4px and a colour that actually reads: at 2px on --tblr-border-color the
     segments were invisible next to the dots, so the drawing showed three
     points and no spans - which is the opposite of its job. */
  height: 4px;
  border-radius: 2px;
  background: var(--tblr-secondary);
  display: flex;
  justify-content: center;
}
/* Ring is the lighter span, talk the solid one - weight, not hue, because a
   duration is not a state (rule 5). */
.call-timeline__seg:first-of-type { background: var(--tblr-border-color-active); }
.call-timeline__seg-label {
  position: absolute;
  bottom: .7rem;
  white-space: nowrap;
  font-size: .75rem;
  color: var(--tblr-secondary);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.call-timeline__foot-label {
  font-size: .6875rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--tblr-secondary);
}
.call-timeline__foot-time { font-size: .9375rem; font-variant-numeric: tabular-nums; }
.call-timeline__note { margin: .25rem 0 0; font-size: .75rem; color: var(--tblr-secondary); }

@container sheet (max-width: 520px) {
  .call-timeline__seg-label { font-size: .625rem; }
  .call-timeline__seg { min-width: 2.5rem; }
}


/* ── Call detail: compact boxes, linked boxes, continuous spine rule ─────────
   --compact uses auto-FILL rather than auto-fit: with auto-fit a lone box
   stretches across the whole band, which reads as a banner rather than as one
   fact among several. */
.sheet-stats--compact { grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr)); }

/* A box that names something with an admin page of its own IS the way to it.
   The arrow says so; without it a clickable box is a guess. */
a.sheet-stat { text-decoration: none; color: inherit; display: block; }
a.sheet-stat:hover { border-color: var(--tblr-primary); background: var(--tblr-bg-surface-tertiary); }
a.sheet-stat:focus-visible { outline: 2px solid var(--tblr-primary); outline-offset: 2px; }
.sheet-stat__link { font-size: .75rem; margin-left: .25rem; color: var(--tblr-secondary); vertical-align: baseline; }
a.sheet-stat:hover .sheet-stat__link { color: var(--tblr-primary); }
.sheet-spine__link { color: inherit; text-decoration: none; }
.sheet-spine__link:hover { color: var(--tblr-primary); text-decoration: underline; }

/* The head's rule was breaking in the middle: the centre cell is empty, so with
   baseline alignment it had no baseline to sit on and its border sat higher
   than its neighbours'. Aligning the row to its end and giving every cell the
   same minimum height makes one continuous hairline. */
.sheet-spine__head > * { align-self: end; min-height: 1.1rem; }

/* ---- Pulse table loader ---------------------------------------------------
   A 3px brand-gradient sweep (Live Orange -> Cyan Signal, the Pulse bar) shown
   while a DataTableComponent frame is fetching - filter change, sort, page or
   column search. Turbo sets [busy] on the frame for exactly that window, so
   this is CSS-only. The strip OVERLAYS the panel's top edge rather than
   sitting in flow - reserved height read as a band of card background
   splitting the title bar from the panel's border. The transition delay
   keeps an instant response from flashing the bar. */
turbo-frame:has(> .dt-loader) {
  display: block;
  position: relative;
}
.dt-loader {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 5;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease 0.15s;
}
turbo-frame[busy] .dt-loader { opacity: 1; }
.dt-loader::before {
  content: "";
  display: block;
  height: 100%;
  width: 40%;
  border-radius: 2px;
  background: linear-gradient(90deg, #ff5f00, #11b5e4);
  animation: dt-loader-sweep 1.1s ease-in-out infinite;
}
@keyframes dt-loader-sweep {
  from { transform: translateX(-110%); }
  to   { transform: translateX(360%); }
}

/* Full-page visits (a Narrowed-by bar chip clears via _top) show Turbo's own
   top progress bar - same brand gradient, so both loaders read as one system. */
.turbo-progress-bar {
  height: 3px;
  background: linear-gradient(90deg, #ff5f00, #11b5e4);
}

/* Teams drift modal: the choice is a whole column, so the selection reads as a
   vertical band down it rather than a checked box somewhere off to the side.
   Cells are clickable, hence the cursor. */
.drift-table .drift-col {
  cursor: pointer;
  transition: background-color 0.12s ease-in-out;
}

/* Each side is tinted in its own colour: Microsoft Teams in the Teams brand
   violet-blue (#6264A7), local in neutral grey. Semantic colour means state
   everywhere else in the product, so a column tint that meant "selected" in
   the palette's primary would read as a status - this is identity, not
   health. */
.drift-table .drift-col-selected[data-side="teams"] {
  background-color: rgba(98, 100, 167, 0.14);
  box-shadow: inset 2px 0 0 #6264A7, inset -2px 0 0 #6264A7;
}

.drift-table .drift-col-selected[data-side="local"] {
  background-color: var(--tblr-bg-surface-secondary);
  box-shadow: inset 2px 0 0 var(--tblr-border-color-active), inset -2px 0 0 var(--tblr-border-color-active);
}

/* The band has to close at both ends or it reads as an unfinished column. */
.drift-table thead .drift-col-selected {
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
}

.drift-table tfoot .drift-col-selected {
  border-bottom-left-radius: 4px;
  border-bottom-right-radius: 4px;
}

/* The armed action wears its column's colour, so the button and the band it
   sits under read as one choice. Teams brand violet-blue. */
.btn-teams {
  background-color: #6264A7;
  border-color: #6264A7;
  color: #fff;
}

.btn-teams:hover,
.btn-teams:focus,
.btn-teams:active {
  background-color: #4f5199;
  border-color: #4f5199;
  color: #fff;
}

/* Call-direction chips (/admin/telephony/call-records, /app/reporting): the
   Pulse Blue brand ink #001021 instead of pure black. `badge-ink` is the filled
   "out" chip; `badge-ink-outline` is the outlined "in" chip. Self-contained so
   neither depends on Tabler's bg-black / badge-outline. "local" stays blue-outline. */
.badge.badge-ink {
  background-color: #001021;
  border-color: #001021;
  color: #fff;
}
.badge.badge-ink-outline {
  background-color: transparent;
  color: #001021;
  border: var(--tblr-border-width) var(--tblr-border-style) #001021;
}

/* /app/v2 dashboard (variant homepage under test).
   Stat strip: identical light boxes - hairline border, semi-transparent
   surface tint (lighter than a card), one tinted icon avatar each. */
.dash-stat-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.dash-stat {
  flex: 1 1 0;
  min-width: 160px;
  /* Text left, the radial gauge right. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--tblr-border-color-translucent);
  border-radius: var(--tblr-border-radius);
  background: color-mix(in srgb, var(--tblr-bg-surface-secondary) 45%, transparent);
  color: inherit;
  text-decoration: none;
}
a.dash-stat:hover {
  border-color: var(--tblr-primary);
  color: inherit;
}
.dash-stat-label {
  font-size: 0.68rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--tblr-secondary);
}
.dash-stat-value {
  font-size: 1.12rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--tblr-body-color);
  line-height: 1.25;
}
.dash-stat-value small {
  font-size: 0.78rem;
  font-weight: 500;
  color: var(--tblr-secondary);
}
.dash-stat-delta {
  font-size: 0.78rem;
  font-weight: 600;
  margin-left: 0.25rem;
  white-space: nowrap;
}
.dash-stat-text {
  min-width: 0; /* let the value line truncate instead of pushing the ring out */
}
.dash-radial {
  flex: 0 0 auto;
}
/* Brand marks inline in a stat value line ("56 / 41 on [Teams]") - sized to
   the small text beside them, never the avatar grid's 24px. */
/* Layered stat tile: the sparkline is the card's BACKGROUND (bottom-aligned,
   behind the text), the radial floats right-centre, the content rides above.
   A graph in its own strip under the body was a second row the tile never
   needed - texture belongs behind. */
.dash-tile {
  position: relative;
  overflow: hidden;
}
.dash-tile-content {
  position: relative;
  z-index: 1;
}
.dash-tile-spark {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  align-items: flex-end;
  opacity: 0.55;
  pointer-events: none;
}
.dash-tile-spark > div {
  width: 100%;
}
.dash-tile-radial {
  position: absolute;
  top: 50%;
  right: 0.85rem;
  transform: translateY(-50%);
  z-index: 1;
}

/* Sync-run outcome grid on the Services tile: contribution-graph squares,
   one per run, wrapping into rows. Colors are STATUS (rule: colour is state). */
.dash-run-grid {
  /* Two fixed rows; each new run adds one dot HORIZONTALLY (column flow),
     so the strip grows left-to-right like a timeline. */
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, 8px);
  gap: 2px;
  justify-content: start;
  overflow: hidden;
}
.dash-run-cell {
  width: 8px;
  height: 8px;
  border-radius: 2px;
  display: inline-block;
  background: var(--tblr-border-color);
}
.dash-run-cell--completed { background: var(--tblr-green); }
.dash-run-cell--failed { background: var(--tblr-red); }
.dash-run-cell--running { background: var(--tblr-blue); }
.dash-run-cell--completed-with-errors { background: var(--tblr-yellow); }

/* The drift row's leading status light: a thumb-sized in-sync bar where a
   dot used to be - the dot said good/bad, the bar says how much. */
.dash-mini-bar {
  width: 2.6rem;
}

.dash-stat-mini-avatar {
  width: 1rem;
  height: 1rem;
  display: inline-block;
  vertical-align: text-bottom;
  border-radius: 3px;
  background-size: cover;
  font-size: 0.55rem;
  line-height: 1rem;
}
/* The Sync runs data box draws its blocks on ONE line filling the box: each
   block flexes to an equal share of the width, so the sequence always spans
   edge to edge and squeezes rather than wraps or hides. Scoped to the
   sequence container so the legend's swatches keep their fixed size. */
.dash-run-line .run-blocks {
  margin-bottom: 0 !important;
  flex-wrap: nowrap;
}
.dash-run-line .run-blocks .run-block {
  flex: 1 1 0;
  min-width: 4px;
}

/* Platform Services box: one brand avatar per service, its status as the
   avatar's badge dot. Height matches the sibling boxes' value line. */
.dash-stat-avatars {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 1.4rem;
  margin-top: 0.15rem;
}

/* Drill-down panels are unboxed: no card wrapper. The hero is a transparent
   row (avatar / name / description, status + configuration on the right);
   under it condensed data boxes (admin-design "data boxes": one fact each,
   quiet label, the value is the thing you came for), then content blocks on
   the same light tint as the stat strip. */
.dash-panel-hero {
  display: flex;
  align-items: flex-start;
  gap: 0.8rem;
  margin-bottom: 1rem;
}
.dash-panel-hero-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
  flex: none;
  flex-wrap: wrap;
}
.dash-data-boxes {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 1rem;
}
.dash-data-box {
  min-width: 118px;
  padding: 0.45rem 0.75rem;
  border: 1px solid var(--tblr-border-color-translucent);
  border-radius: var(--tblr-border-radius);
  background: color-mix(in srgb, var(--tblr-bg-surface-secondary) 45%, transparent);
}
.dash-data-box .dash-stat-label {
  font-size: 0.66rem;
}
.dash-data-box-value {
  font-size: 0.98rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.dash-data-box-value small {
  font-size: 0.74rem;
  font-weight: 500;
  color: var(--tblr-secondary);
}
/* Content sections inside panels are unboxed: a quiet uppercase title over a
   flush table, separated by whitespace - no border, no tint. Every list row
   follows one grammar: [when] [badge] [text] [trailing], so the three glance
   lists and the drill-down panels read as one design. */
.dash-section-title {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--tblr-secondary);
  margin-bottom: 0.35rem;
}
.dash-list td:first-child {
  width: 1%;
  white-space: nowrap;
  color: var(--tblr-secondary);
}

/* Glance / panel list boxes: a subtle light-blue border frames each list so
   it reads as a designed element rather than a table splashed on the page.
   Header carries the title and a right-aligned "View all". Rows stay
   single-line column layouts; state and change verbs are quiet -lt label
   badges; the trailing chevron opens the item's detail. */
.dash-box {
  border: 1px solid color-mix(in srgb, var(--tblr-primary) 25%, transparent);
  border-radius: var(--tblr-border-radius);
  background: transparent;
  padding: 0.65rem 0.85rem;
}
.dash-box-header {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.35rem;
}
.dash-box-header .dash-section-title {
  margin-bottom: 0;
}
.dash-box-link {
  font-size: 0.78rem;
  white-space: nowrap;
}
.dash-box-header .dash-box-link {
  margin-left: auto;
}
.dash-list .badge {
  display: inline-flex;
  justify-content: center;
  min-width: 46px;
}
/* The last row's rule would double the box's own bottom edge. */
.dash-list tbody tr:last-child td {
  border-bottom-width: 0;
}
/* ONE fixed row height for every list. Padding-based sizing let each row take
   the height of whatever it held - a 16px type icon, a badge, a chevron
   button - so sibling tables drifted apart. A fixed cell height with middle
   alignment absorbs all of those: every content variant is shorter than the
   row and centres inside it. */
.dash-list td {
  height: 2.1rem;
  padding-top: 0;
  padding-bottom: 0;
  vertical-align: middle;
}
.dash-list .btn-icon {
  width: 1.4rem;
  height: 1.4rem;
  min-height: 0;
  padding: 0;
  border: 0;
}

/* The same idea one size up, for a call table that IS a card rather than a box
   inside a panel (Call activity, Faulty calls, Worst quality). Tabler's default
   cell padding let the chevron button set the height, so those three tables
   drifted apart exactly as the glance lists once did. 2.5rem is dense without
   the fixed 2.1rem's squeeze - it still holds a leg avatar. */
.dash-card-list td {
  height: 2.5rem;
  padding-top: 0;
  padding-bottom: 0;
  vertical-align: middle;
}
.dash-card-list .btn-icon {
  width: 1.6rem;
  height: 1.6rem;
  min-height: 0;
  padding: 0;
  border: 0;
}

/* An empty widget sharing a `row-deck` row is stretched to its neighbour's
   height, and one grey line pinned to the top of a 500px box reads as a card
   that failed to load rather than one with nothing to report. */
.dash-empty {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* A chart whose points are links. ApexCharts draws its own SVG and does not
   know a slice leads anywhere, so the affordance has to be said here. */
.chart-clickable .apexcharts-pie-area,
.chart-clickable .apexcharts-bar-area {
  cursor: pointer;
}

/* A row of device facts (field-lock bands on the trunk Device and service
   AudioCodes tabs) lays its columns out at the width the value needs, instead
   of the 12-column split: three thirds of a wide band turn a nine-character
   SBC name into a field that reads as an empty form. A deliberate exception to
   "widths come only from sf-*" (.claude/rules/admin-design.md), written down
   here because the values are short and fixed - the grid still owns every
   other band. Columns wrap when the container is narrow, so the inversion test
   still holds: a field never gets squeezed, the row gets shorter. */
.sheet-grid.is-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem 1rem;
}
.sheet-grid.is-fields > * {
  width: 15rem;
  max-width: 100%;
}
.sheet-grid.is-fields > .is-wide {
  width: 22rem;
}

/* A band with a second row of blocks: the two grids are separate rows, not one
   flow, so they need the space a grid gap gives inside a row. Without it a
   locked field sits directly on top of the view field beneath it. */
.sheet-grid + .sheet-grid {
  margin-top: 1.1rem;
}

/* A datagrid in a narrow container (the CDR details page keeps two in col-6
   halves) cannot shrink below Tabler's 15rem item minimum and punches out of
   the page. Letting the minimum yield to the container is a no-op wherever
   the container is wider than 15rem - which is everywhere it worked before. */
.datagrid {
    --tblr-datagrid-item-width: min(15rem, 100%);
}

/* ==========================================================================
   Mobile pass (phones, below Bootstrap's md)
   Three measured failures on a 390px viewport drive every rule here:
   a fixed-layout table crushed 12 columns into 357px (12-24px cells), the
   footer's full page-number pagination held the whole document at 606px, and
   the chrome strips (panel head, controls, filters) refuse to wrap.
   ========================================================================== */

/* A column declared `mobile: false` on DataTableComponent. Its own class, not
   d-none: the column-visibility controller reads d-none as a user choice and
   would save the viewport into the session. */
@media (max-width: 767.98px) {
  .dt-mobile-hide {
    display: none;
  }

  /* The app header holds one row on a phone: hamburger, brand, sidebar
     toggle. Without nowrap the brand's width pushed the toggle onto a second
     row, where it sat on top of the opened collapse menu. */
  #site-header .navbar > .container-xl {
    flex-wrap: nowrap;
  }
  #site-header .navbar-brand {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
  }

  /* Phones get the horizontal scroll back that the desktop dropdown fix
     (.card .table-responsive { overflow: visible }) removed: on a phone an
     unreadable table costs more than a row-action dropdown clipped at the
     card edge. A table that names its phone columns (adm-table--mobile-tuned)
     fits the viewport; one that has not yet keeps a readable width and
     scrolls inside its card instead of crushing. */
  .card .table-responsive {
    overflow-x: auto;
  }
  .adm-table:not(.adm-table--mobile-tuned) {
    min-width: 640px;
  }

  /* A tuned table drops fixed layout on phones: the declared shares assume
     every column is present, so with half of them display:none the leftover
     would all fall to the unsized action column. Auto layout sizes the few
     remaining columns by content; max-width:1px is the fixed-layout ellipsis
     trick and would crush auto columns, so it comes off with it. */
  .adm-table--mobile-tuned {
    table-layout: auto;
  }
  .adm-table--mobile-tuned tbody td {
    /* Bounded, not none: the ellipsis needs a ceiling or one long value
       (a UPN under a name) stretches its column past the viewport. */
    max-width: 60vw;
  }

  /* Right-hand control clusters in card header bars (the admin timeline's
     search + lens group, and their kin) wrap instead of widening the page. */
  .card .ms-auto.d-flex.gap-2 {
    flex-wrap: wrap;
    justify-content: flex-end;
  }

  /* The controls strip's ONE LINE rule (see data_table_component.html.erb) is
     a laptop decision; on a phone the line wraps. Same for the panel head and
     the footer - the pagination is the widest element on the page, so it
     wraps rather than widening the document. */
  .dt-panel-head,
  .card-footer.d-flex {
    flex-wrap: wrap;
    row-gap: 0.5rem;
  }
  .adm-controls .row {
    row-gap: 0.5rem;
  }
  .card-footer .pagination {
    flex-wrap: wrap;
  }

  /* Filter bars above panels (TableFiltersComponent and the hand-rolled ones
     carrying the same .table-filters hook): the flex children crush their
     selects to two letters at phone width. Stack them full width instead.
     !important because the widths being beaten are inline max-widths. */
  .table-filters form {
    flex-wrap: wrap;
  }
  .table-filters form > div {
    flex: 1 1 100%;
    max-width: 100% !important;
  }
}



/* Routing-rule manage chips (admin/telephony/routing-rules/:id/manage): the
   label sits OVER the value - long monospace values (number ranges, device
   tags) need the full chip width - and the external-link icon / derived tag
   pins to the top-right corner instead of centering vertically. */
.sheet-derived--stacked { flex-direction: column; align-items: stretch; position: relative; gap: 0.15rem; }
.sheet-derived--stacked > div { min-width: 0; }
.sheet-derived--stacked .sheet-derived__value { overflow-wrap: anywhere; }
.sheet-derived--stacked .ti-external-link,
.sheet-derived--stacked .sheet-derived__tag { position: absolute; top: 0.45rem; right: 0.65rem; margin-left: 0; }

/* --- /app/v2 drill-down: clicking the diagram has to look like it did something
   ------------------------------------------------------------------------
   The panel below the topology is a Turbo Frame whose src is re-pointed on
   select. Until the response landed, the OLD panel sat there unchanged, so a
   click read as "nothing happened" and people clicked again.

   The stale panel is veiled rather than blanked: the frame keeps its height, so
   the page does not jump under the cursor, and what is on screen is visibly no
   longer current. Reuses the async loader's spin keyframe rather than a second
   animation.

   Applied only to a user's selection - the live sdr-events refresh re-fetches
   this same frame every few seconds, and nobody is waiting on that one. */
.dashboard-drilldown { position: relative; display: block; }
.dashboard-drilldown.is-loading > * {
  opacity: 0.4;
  transition: opacity 0.12s ease-out;
  pointer-events: none;
}
.dashboard-drilldown.is-loading::after {
  content: "";
  position: absolute;
  top: 2.5rem;
  left: 50%;
  width: 36px;
  height: 36px;
  margin-left: -18px;
  border: 3px solid var(--tblr-border-color);
  border-top-color: var(--tblr-primary);
  border-radius: 50%;
  animation: async-loader-spin 0.9s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .dashboard-drilldown.is-loading::after { animation-duration: 3s; }
}

/* ============================================================
   Sign-in duo (application_sign_in_new layout)
   One centered card, two panes: brand voice left, form right.
   Brand colors derive from --login-brand (whitelabel primary).
   ============================================================ */
.login-duo-container {
  max-width: 60rem;
}
.login-duo {
  display: flex;
  min-height: 30rem;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--tblr-border-color);
  background: var(--tblr-bg-surface);
  box-shadow: 0 30px 90px rgba(2, 20, 43, 0.16), 0 4px 14px rgba(2, 20, 43, 0.06);
}
.login-duo-voice {
  flex: 1 1 52%;
  position: relative;
  flex-direction: column;
  justify-content: space-between;
  padding: 2.6rem 2.8rem;
  color: #eaf2fa;
  background:
    radial-gradient(30rem 22rem at 20% 15%,
      color-mix(in srgb, var(--login-brand, #066fd1) 16%, transparent), transparent 60%),
    linear-gradient(160deg,
      color-mix(in srgb, var(--login-brand, #066fd1) 18%, #0a0e17),
      color-mix(in srgb, var(--login-brand, #066fd1) 36%, #070b12) 70%,
      color-mix(in srgb, var(--login-brand, #066fd1) 58%, #060a10));
}
.login-duo-logo img {
  height: 28px;
  filter: brightness(0) invert(1);
}
.login-duo-headline {
  font-size: 1.9rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0 0 0.8rem;
  color: #fff;
}
.login-duo-headline em {
  font-style: normal;
  color: color-mix(in srgb, var(--login-brand, #066fd1) 45%, #fff);
}
.login-duo-lede {
  color: rgba(214, 232, 248, 0.72);
  font-size: 0.95rem;
  margin: 0;
  max-width: 22rem;
}
.login-duo-lede p {
  margin: 0 0 0.5rem;
}
.login-duo-lede p:last-child {
  margin-bottom: 0;
}
.login-duo-lede a {
  color: color-mix(in srgb, var(--login-brand, #066fd1) 45%, #fff);
}
.login-duo-ticker {
  font-family: var(--tblr-font-monospace);
  font-size: 0.74rem;
  color: rgba(214, 232, 248, 0.55);
  display: flex;
  gap: 1.3rem;
}
.login-duo-ticker b {
  color: color-mix(in srgb, var(--login-brand, #066fd1) 45%, #fff);
  font-weight: 600;
}
.login-duo-form {
  flex: 1 1 48%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 2.5rem 1rem;
}
@media (max-width: 991.98px) {
  .login-duo {
    min-height: 0;
  }
}
