/*
    The AppBuilder skin for the vendored AdminLTE template.

    Loaded after adminlte.min.css and never instead of it. The template stays byte-for-byte upstream
    (see lib/adminlte/README.md) and everything that makes these pages look like AppBuilder rather than
    like the template lives here, so an AdminLTE upgrade is a file swap and this file is the only thing
    a brand change touches.

    Most of the work is done by re-pointing tokens rather than restating rules: Bootstrap's --bs-* and
    AdminLTE's --lte-* custom properties already drive every stock component, so setting them to the
    brand recolours buttons, links, dropdowns and the sidebar without fighting the template's
    selectors. Rules appear only where the template compiles a colour to a literal (a focus ring, a
    filled button) or where a component has no template equivalent at all.

    The palette is the client's (AppBuilderTheme.cs): the indigo-to-violet gradient, the deep indigo,
    the drawer's near-black and the slate surfaces, so the admin pages and the app read as one product.
    Every text colour below was measured, not chosen by eye, and holds at least 4.5:1 (WCAG 2.2 AA)
    against every surface it is painted on — including a status badge's text against its own tint.
*/

/* -------------------------------------------------------------------------------------------------
   Tokens
   ---------------------------------------------------------------------------------------------- */

:root,
[data-bs-theme=light] {
    /* The brand. A fill keeps the same indigo in both themes, because white on it measures 6.29:1;
       the brand painted as text needs a lighter step on a dark surface, so the two are separate. */
    --ab-brand: #4f46e5;
    --ab-brand-strong: #4338ca;
    --ab-brand-deeper: #3730a3;
    --ab-accent: #7c3aed;
    --ab-brand-text: #4f46e5;
    --ab-gradient: linear-gradient(120deg, #4f46e5 0%, #7c3aed 100%);

    /* The hero runs a step deeper than the brand gradient so the translucent white written over it
       stays above 5.5:1 at its violet end, where #7c3aed would only manage 4.35:1. */
    --ab-gradient-deep: linear-gradient(125deg, #4338ca 0%, #6d28d9 100%);

    --ab-page: #f4f6fb;
    --ab-surface: #ffffff;
    --ab-surface-muted: #f8fafc;
    --ab-surface-hover: #f1f4f9;
    --ab-border: #e3e8ef;
    --ab-border-strong: #cfd6e2;

    /* #5b697e rather than slate-500: #64748b drops to 4.40:1 on the page background. */
    --ab-text: #0f172a;
    --ab-text-muted: #5b697e;

    --ab-ring: 0 0 0 4px rgba(79, 70, 229, 0.18);

    --ab-shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.05);
    --ab-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 2px 8px -2px rgba(15, 23, 42, 0.06);
    --ab-shadow-md: 0 2px 4px -2px rgba(15, 23, 42, 0.06), 0 16px 32px -12px rgba(15, 23, 42, 0.12);
    --ab-shadow-lg: 0 4px 6px -4px rgba(15, 23, 42, 0.08), 0 24px 48px -16px rgba(15, 23, 42, 0.22);
    --ab-shadow-brand: 0 10px 24px -10px rgba(109, 40, 217, 0.6);

    --ab-radius-sm: 8px;
    --ab-radius: 10px;
    --ab-radius-lg: 14px;
    --ab-radius-xl: 20px;

    /* Status: a fill (dots, the breakdown bar — at least 3:1 against a card), and a text colour with
       the tint it sits on (badges — at least 4.5:1 between the two). */
    --ab-draft: #d97706;
    --ab-draft-text: #8a5a06;
    --ab-draft-tint: #fbf0df;
    --ab-progress: #4f46e5;
    --ab-progress-text: #4338ca;
    --ab-progress-tint: #ededfc;
    --ab-complete: #059669;
    --ab-complete-text: #0a7653;
    --ab-complete-tint: #e4f3ed;
    --ab-archived: #64748b;
    --ab-archived-text: #475569;
    --ab-archived-tint: #eceef1;

    --ab-danger-text: #b91c1c;
    --ab-danger-tint: #fef2f2;
    --ab-danger-border: #fecaca;
    --ab-warning-text: #8a5a06;
    --ab-warning-tint: #fffbeb;
    --ab-warning-border: #fde68a;
    --ab-success-text: #0a7653;
    --ab-success-tint: #ecfdf5;
    --ab-success-border: #a7f3d0;

    /* Bootstrap's own tokens, re-pointed so every stock component follows the brand. */
    --bs-primary: #4f46e5;
    --bs-primary-rgb: 79, 70, 229;
    --bs-primary-text-emphasis: #3730a3;
    --bs-primary-bg-subtle: #eef0fe;
    --bs-primary-border-subtle: #c7d2fe;
    --bs-link-color: #4f46e5;
    --bs-link-color-rgb: 79, 70, 229;
    --bs-link-hover-color: #3730a3;
    --bs-link-hover-color-rgb: 55, 48, 163;
    --bs-body-color: #0f172a;
    --bs-body-color-rgb: 15, 23, 42;
    --bs-emphasis-color: #020617;
    --bs-emphasis-color-rgb: 2, 6, 23;
    --bs-heading-color: #0f172a;
    --bs-secondary-color: #5b697e;
    --bs-secondary-color-rgb: 91, 105, 126;
    --bs-secondary-bg: #eef1f6;
    --bs-secondary-bg-rgb: 238, 241, 246;
    --bs-tertiary-bg: #f4f6fb;
    --bs-tertiary-bg-rgb: 244, 246, 251;
    --bs-border-color: #e3e8ef;
    --bs-border-color-translucent: rgba(15, 23, 42, 0.09);
    --bs-border-radius: 0.625rem;
    --bs-border-radius-sm: 0.5rem;
    --bs-border-radius-lg: 0.875rem;
    --bs-border-radius-xl: 1.25rem;
    --bs-focus-ring-color: rgba(79, 70, 229, 0.25);
    --bs-box-shadow-sm: var(--ab-shadow-sm);
    --bs-box-shadow: var(--ab-shadow-md);
    --bs-box-shadow-lg: var(--ab-shadow-lg);
}

[data-bs-theme=dark] {
    --ab-brand-text: #a5b4fc;
    --ab-gradient-deep: linear-gradient(125deg, #3730a3 0%, #5b21b6 100%);

    --ab-page: #0b1120;
    --ab-surface: #131b2e;
    --ab-surface-muted: #18223a;
    --ab-surface-hover: #1b2640;
    --ab-border: #222d45;
    --ab-border-strong: #2e3b57;

    --ab-text: #e6eaf2;
    --ab-text-muted: #9aa6b8;

    --ab-ring: 0 0 0 4px rgba(129, 140, 248, 0.28);

    --ab-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
    --ab-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3), 0 2px 8px -2px rgba(0, 0, 0, 0.35);
    --ab-shadow-md: 0 2px 4px -2px rgba(0, 0, 0, 0.4), 0 16px 32px -12px rgba(0, 0, 0, 0.55);
    --ab-shadow-lg: 0 4px 6px -4px rgba(0, 0, 0, 0.45), 0 24px 48px -16px rgba(0, 0, 0, 0.7);

    --ab-draft: #fbbf24;
    --ab-draft-text: #fbbf24;
    --ab-draft-tint: #2f2f2d;
    --ab-progress: #818cf8;
    --ab-progress-text: #a5b4fc;
    --ab-progress-tint: #252d4e;
    --ab-complete: #34d399;
    --ab-complete-text: #4ade80;
    --ab-complete-tint: #1a3238;
    --ab-archived: #94a3b8;
    --ab-archived-text: #cbd5e1;
    --ab-archived-tint: #252e41;

    --ab-danger-text: #fca5a5;
    --ab-danger-tint: #2a1a24;
    --ab-danger-border: #5b2530;
    --ab-warning-text: #fcd34d;
    --ab-warning-tint: #29241a;
    --ab-warning-border: #5a4718;
    --ab-success-text: #6ee7b7;
    --ab-success-tint: #12291f;
    --ab-success-border: #1f5240;

    /* The brand as a foreground. #4f46e5 on the dark card is 2.9:1; this step is 8.61:1. */
    --bs-primary: #818cf8;
    --bs-primary-rgb: 129, 140, 248;
    --bs-primary-text-emphasis: #c7d2fe;
    --bs-primary-bg-subtle: #20284a;
    --bs-primary-border-subtle: #3b4482;
    --bs-link-color: #a5b4fc;
    --bs-link-color-rgb: 165, 180, 252;
    --bs-link-hover-color: #c7d2fe;
    --bs-link-hover-color-rgb: 199, 210, 254;
    --bs-body-bg: #131b2e;
    --bs-body-bg-rgb: 19, 27, 46;
    --bs-body-color: #e6eaf2;
    --bs-body-color-rgb: 230, 234, 242;
    --bs-emphasis-color: #f8fafc;
    --bs-emphasis-color-rgb: 248, 250, 252;
    --bs-heading-color: #f1f5f9;
    --bs-secondary-color: #9aa6b8;
    --bs-secondary-color-rgb: 154, 166, 184;
    --bs-secondary-bg: #1b2640;
    --bs-secondary-bg-rgb: 27, 38, 64;
    --bs-tertiary-bg: #0b1120;
    --bs-tertiary-bg-rgb: 11, 17, 32;
    --bs-border-color: #222d45;
    --bs-border-color-translucent: rgba(148, 163, 184, 0.14);
    --bs-focus-ring-color: rgba(129, 140, 248, 0.35);
}

/* -------------------------------------------------------------------------------------------------
   Base
   ---------------------------------------------------------------------------------------------- */

body {
    background-color: var(--ab-page);
    color: var(--ab-text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

/* Selection uses the brand, as the client's does, so a copied value still shows which product the
   reader is in. */
::selection {
    background: rgba(124, 58, 237, 0.22);
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    letter-spacing: -0.015em;
}

/* One focus ring for every interactive element, keyboard only. The template's own rings are a mix
   of Bootstrap blue and a warning outline; this is the brand at a weight that clears 3:1 on both
   themes' surfaces. */
:focus-visible {
    outline: 2px solid var(--ab-brand-text);
    outline-offset: 2px;
}

.ab-tabular {
    font-variant-numeric: tabular-nums;
}

/* AdminLTE injects these. They stay off-screen until focused; this only gives them the brand. */
.skip-link {
    background: var(--ab-brand);
    border-radius: 0 0 var(--ab-radius-sm) var(--ab-radius-sm);
}

.skip-link:focus {
    outline-color: #fff;
}

/* -------------------------------------------------------------------------------------------------
   Components the template ships, refined
   ---------------------------------------------------------------------------------------------- */

.btn {
    --bs-btn-font-weight: 600;
    --bs-btn-padding-y: 0.5rem;
    --bs-btn-padding-x: 1rem;
    --bs-btn-border-radius: var(--ab-radius);
    --bs-btn-focus-box-shadow: var(--ab-ring);
    letter-spacing: 0.005em;
    transition: color 0.15s, background-color 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}

.btn-sm {
    --bs-btn-padding-y: 0.3125rem;
    --bs-btn-padding-x: 0.75rem;
    --bs-btn-border-radius: var(--ab-radius-sm);
}

.btn-lg {
    --bs-btn-padding-y: 0.75rem;
    --bs-btn-padding-x: 1.25rem;
    --bs-btn-font-size: 1.0625rem;
    --bs-btn-border-radius: var(--ab-radius);
}

/* The template compiles Bootstrap's blue into the button, so the variables are restated. */
.btn-primary {
    --bs-btn-bg: #4f46e5;
    --bs-btn-border-color: #4f46e5;
    --bs-btn-hover-bg: #4338ca;
    --bs-btn-hover-border-color: #4338ca;
    --bs-btn-active-bg: #3730a3;
    --bs-btn-active-border-color: #3730a3;
    --bs-btn-disabled-bg: #4f46e5;
    --bs-btn-disabled-border-color: #4f46e5;
    --bs-btn-focus-shadow-rgb: 79, 70, 229;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

/* The outline beside it — Save and continue — in the same brand, filling on hover. */
.btn-outline-primary {
    --bs-btn-color: var(--ab-brand-text);
    --bs-btn-border-color: color-mix(in srgb, var(--ab-brand) 55%, transparent);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: #4f46e5;
    --bs-btn-hover-border-color: #4f46e5;
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: #4338ca;
    --bs-btn-active-border-color: #4338ca;
    --bs-btn-disabled-color: var(--ab-brand-text);
    --bs-btn-disabled-border-color: color-mix(in srgb, var(--ab-brand) 55%, transparent);
    --bs-btn-focus-shadow-rgb: 79, 70, 229;
}

/* The gradient call to action, for the one primary action a screen exists for. */
.ab-btn-gradient {
    border: 0;
    color: #fff;
    background-image: var(--ab-gradient);
    background-size: 140% 100%;
    background-position: 0% 50%;
    box-shadow: var(--ab-shadow-brand), inset 0 1px 0 rgba(255, 255, 255, 0.18);
    transition: background-position 0.35s ease, box-shadow 0.2s, transform 0.15s;
}

.ab-btn-gradient:hover,
.ab-btn-gradient:focus-visible {
    color: #fff;
    background-position: 100% 50%;
    box-shadow: 0 14px 28px -12px rgba(109, 40, 217, 0.75), inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

.ab-btn-gradient:active {
    transform: translateY(1px);
}

.btn-outline-secondary,
.ab-btn-quiet {
    --bs-btn-color: var(--ab-text);
    --bs-btn-bg: var(--ab-surface);
    --bs-btn-border-color: var(--ab-border-strong);
    --bs-btn-hover-color: var(--ab-text);
    --bs-btn-hover-bg: var(--ab-surface-hover);
    --bs-btn-hover-border-color: var(--ab-border-strong);
    --bs-btn-active-color: var(--ab-text);
    --bs-btn-active-bg: var(--ab-surface-hover);
    --bs-btn-active-border-color: var(--ab-border-strong);
    box-shadow: var(--ab-shadow-xs);
}

.form-label {
    margin-bottom: 0.375rem;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--ab-text);
}

.form-control,
.form-select {
    min-height: 2.75rem;
    padding: 0.625rem 0.875rem;
    color: var(--ab-text);
    background-color: var(--ab-surface);
    border-color: var(--ab-border-strong);
    border-radius: var(--ab-radius);
    box-shadow: var(--ab-shadow-xs);
    transition: border-color 0.15s, box-shadow 0.15s, background-color 0.15s;
}

/* A select keeps the room on its right that its arrow is drawn in, which the padding above gives away:
   without it, a select sized to its longest choice writes that choice under the arrow. */
.form-select {
    padding-right: 2.25rem;
}

.form-control::placeholder {
    color: var(--ab-text-muted);
    opacity: 0.75;
}

.form-control:hover,
.form-select:hover {
    border-color: color-mix(in srgb, var(--ab-border-strong) 60%, var(--ab-text-muted));
}

/* The template's focus colours are literals compiled from Bootstrap's blue. */
.form-control:focus,
.form-select:focus {
    color: var(--ab-text);
    background-color: var(--ab-surface);
    border-color: var(--ab-brand);
    box-shadow: var(--ab-ring);
}

.form-check-input {
    border-color: var(--ab-border-strong);
}

.form-check-input:checked {
    background-color: #4f46e5;
    border-color: #4f46e5;
}

.form-check-input:focus {
    border-color: var(--ab-brand);
    box-shadow: var(--ab-ring);
}

.card {
    --bs-card-border-color: var(--ab-border);
    --bs-card-border-radius: var(--ab-radius-lg);
    --bs-card-inner-border-radius: calc(var(--ab-radius-lg) - 1px);
    --bs-card-bg: var(--ab-surface);
    --bs-card-cap-bg: transparent;
    --bs-card-spacer-x: 1.25rem;
    --bs-card-spacer-y: 1.25rem;
    --bs-card-cap-padding-x: 1.25rem;
    --bs-card-cap-padding-y: 1rem;
    box-shadow: var(--ab-shadow-sm);
}

.card > .card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    border-bottom-color: var(--ab-border);
}

.card > .card-header .card-title {
    float: none;
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.card > .card-header .ab-card-subtitle {
    margin: 0.125rem 0 0;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

.card > .card-header .ab-card-actions {
    margin-inline-start: auto;
    white-space: nowrap;
}

/* On a phone the header keeps its title and drops the aside, rather than wrapping it under itself. */
@media (max-width: 575.98px) {
    .card > .card-header .ab-card-actions {
        display: none;
    }
}

.dropdown-menu {
    --bs-dropdown-bg: var(--ab-surface);
    --bs-dropdown-color: var(--ab-text);
    --bs-dropdown-border-color: var(--ab-border);
    --bs-dropdown-border-radius: var(--ab-radius-lg);
    --bs-dropdown-padding-x: 0.375rem;
    --bs-dropdown-padding-y: 0.375rem;
    --bs-dropdown-item-padding-x: 0.625rem;
    --bs-dropdown-item-padding-y: 0.5rem;
    --bs-dropdown-item-border-radius: var(--ab-radius-sm);
    --bs-dropdown-link-color: var(--ab-text);
    --bs-dropdown-link-hover-color: var(--ab-text);
    --bs-dropdown-link-hover-bg: var(--ab-surface-hover);
    --bs-dropdown-link-active-color: var(--ab-brand-text);
    --bs-dropdown-link-active-bg: var(--bs-primary-bg-subtle);
    --bs-dropdown-divider-bg: var(--ab-border);
    --bs-dropdown-divider-margin-y: 0.375rem;
    --bs-dropdown-header-color: var(--ab-text-muted);
    box-shadow: var(--ab-shadow-lg);
}

.dropdown-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    font-size: 0.9375rem;
    font-weight: 500;
}

.dropdown-item > .bi {
    color: var(--ab-text-muted);
}

.dropdown-item.active > .bi,
.dropdown-item:active > .bi {
    color: inherit;
}

.alert {
    --bs-alert-border-radius: var(--ab-radius);
    --bs-alert-padding-x: 0.875rem;
    --bs-alert-padding-y: 0.75rem;
    display: flex;
    gap: 0.625rem;
    align-items: flex-start;
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.45;
}

.alert > .bi {
    flex-shrink: 0;
    margin-top: 0.0625rem;
    font-size: 1.0625rem;
}

.alert-danger {
    --bs-alert-color: var(--ab-danger-text);
    --bs-alert-bg: var(--ab-danger-tint);
    --bs-alert-border-color: var(--ab-danger-border);
}

.alert-warning {
    --bs-alert-color: var(--ab-warning-text);
    --bs-alert-bg: var(--ab-warning-tint);
    --bs-alert-border-color: var(--ab-warning-border);
}

.alert-success {
    --bs-alert-color: var(--ab-success-text);
    --bs-alert-bg: var(--ab-success-tint);
    --bs-alert-border-color: var(--ab-success-border);
}

/* -------------------------------------------------------------------------------------------------
   The brand mark
   ---------------------------------------------------------------------------------------------- */

/* The mark is drawn in currentColor (see _LogoMark), so the tile decides how it reads: white on
   the brand gradient, which is the mark as the client's app bar shows it. */
.ab-mark {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    color: #fff;
    background-image: var(--ab-gradient);
    border-radius: var(--ab-radius);
    box-shadow: var(--ab-shadow-brand), inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}

.ab-mark svg {
    width: 72%;
    height: 72%;
}

.ab-mark-lg {
    width: 3rem;
    height: 3rem;
    border-radius: var(--ab-radius-lg);
}

.ab-wordmark {
    font-size: 1.125rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--ab-text);
}

/* -------------------------------------------------------------------------------------------------
   Admin shell: sidebar
   ---------------------------------------------------------------------------------------------- */

:root {
    --lte-sidebar-width: 264px;
}

/* The drawer is dark in both themes, as the client's is. The variables are AdminLTE's own, set at a
   specificity that beats its `[data-bs-theme=dark] .app-sidebar` block rather than restating its
   selectors. The faint violet at the top is the client drawer's, mixed the same way. */
.app-wrapper > .app-sidebar {
    --lte-sidebar-color: #cbd5e1;
    --lte-sidebar-hover-color: #ffffff;
    --lte-sidebar-hover-bg: rgba(255, 255, 255, 0.06);
    --lte-sidebar-menu-active-color: #ffffff;
    --lte-sidebar-menu-active-bg: #5b45e6;
    --lte-sidebar-header-color: #8592a8;
    background-color: #121824;
    background-image:
        radial-gradient(120% 50% at 0% 0%, rgba(124, 58, 237, 0.22) 0%, transparent 60%),
        linear-gradient(180deg, color-mix(in srgb, #7c3aed 10%, #121824) 0%, #121824 34%);
    border-inline-end: 1px solid rgba(203, 213, 225, 0.08);
    box-shadow: none;
}

.app-sidebar .sidebar-brand {
    justify-content: flex-start;
    height: 4rem;
    padding: 0 1.25rem;
    border-bottom: 1px solid rgba(203, 213, 225, 0.08);
}

.app-sidebar .sidebar-brand .brand-link {
    gap: 0.75rem;
    min-width: 0;
}

.app-sidebar .brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    margin-left: 0;
    line-height: 1.15;
}

.app-sidebar .ab-brand-name {
    font-size: 1.0625rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: #ffffff;
}

.app-sidebar .ab-brand-tagline {
    margin-top: 0.1875rem;
    font-size: 0.75rem;
    font-weight: 500;
    color: #94a3b8;
}

.app-sidebar .sidebar-wrapper {
    padding: 0.75rem;
}

.app-sidebar .sidebar-menu .nav-header {
    padding: 1rem 0.75rem 0.5rem;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.app-sidebar .sidebar-menu .nav-header:first-child {
    padding-top: 0.25rem;
}

.app-sidebar .sidebar-menu .nav-link {
    align-items: center;
    gap: 0.75rem;
    min-height: 2.5rem;
    margin-bottom: 0.25rem;
    padding: 0.5rem 0.75rem;
    font-size: 0.9375rem;
    font-weight: 500;
    border-radius: var(--ab-radius);
    transition: color 0.15s, background-color 0.15s;
}

.app-sidebar .sidebar-menu .nav-link p {
    flex: 1 1 auto;
    min-width: 0;
    padding-left: 0;
}

.app-sidebar .sidebar-menu .nav-icon {
    font-size: 1.0625rem;
    color: #94a3b8;
    transition: color 0.15s;
}

.app-sidebar .sidebar-menu .nav-link:hover .nav-icon {
    color: #ffffff;
}

/* The current destination is the client's gradient pill with its dot: the one emphasised row. */
.app-sidebar .sidebar-menu .nav-link.active {
    background-image: var(--ab-gradient);
    box-shadow: 0 8px 20px -8px rgba(124, 58, 237, 0.7), inset 0 0 0 1px rgba(255, 255, 255, 0.2);
}

.app-sidebar .sidebar-menu .nav-link.active .nav-icon {
    color: #ffffff;
}

.app-sidebar .sidebar-menu .nav-link.active::after {
    content: "";
    flex-shrink: 0;
    width: 6px;
    height: 6px;
    background: #ffffff;
    border-radius: 50%;
}

.app-sidebar .sidebar-menu .ab-nav-external {
    flex-shrink: 0;
    font-size: 0.75rem;
    color: #8592a8;
}

.app-sidebar .ab-sidebar-footer {
    flex: 0 0 auto;
    padding: 0.875rem 1.25rem 1rem;
    font-size: 0.75rem;
    color: #8592a8;
    border-top: 1px solid rgba(203, 213, 225, 0.08);
}

.app-sidebar .ab-sidebar-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    white-space: nowrap;
}

.app-sidebar .ab-env {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-weight: 600;
    color: #cbd5e1;
}

.app-sidebar .ab-env::before {
    content: "";
    width: 6px;
    height: 6px;
    background: #34d399;
    border-radius: 50%;
    box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.18);
}

.app-sidebar .ab-env-development::before {
    background: #fbbf24;
    box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.18);
}

/* Collapsed to the icon rail, the rail keeps its icons and drops everything that needs width. The
   selectors mirror AdminLTE's own so the footer and the dot follow the same hover-to-peek rule. */
.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .sidebar-brand {
    justify-content: center;
    padding: 0;
}

.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .sidebar-menu .nav-link {
    justify-content: center;
}

.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .sidebar-menu .nav-link::after,
.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .ab-nav-external,
.sidebar-mini.sidebar-collapse .app-sidebar:not(:hover) .ab-sidebar-footer {
    display: none;
}

/* -------------------------------------------------------------------------------------------------
   Admin shell: header, page heading, footer
   ---------------------------------------------------------------------------------------------- */

/* Glass rather than a filled bar: the content scrolls under it and stays faintly visible, so the
   header reads as a layer above the page instead of a second page. */
.app-header {
    position: sticky;
    top: 0;
    min-height: 4rem;
    background-color: color-mix(in srgb, var(--ab-surface) 78%, transparent);
    border-bottom: 1px solid var(--ab-border);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    backdrop-filter: saturate(180%) blur(14px);
}

.app-header > .container-fluid {
    gap: 0.5rem;
    padding-inline: 1rem;
}

.app-header .nav-link {
    display: inline-flex;
    align-items: center;
    height: 2.5rem;
}

.ab-icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    font-size: 1.125rem;
    color: var(--ab-text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--ab-radius);
    transition: color 0.15s, background-color 0.15s;
}

.ab-icon-button:hover,
.ab-icon-button[aria-expanded=true] {
    color: var(--ab-text);
    background-color: var(--ab-surface-hover);
}

.ab-header-divider {
    width: 1px;
    height: 1.5rem;
    margin: 0 0.25rem;
    background: var(--ab-border);
}

.ab-env-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.625rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--ab-warning-text);
    background: var(--ab-warning-tint);
    border: 1px solid var(--ab-warning-border);
    border-radius: 999px;
}

.ab-env-badge::before {
    content: "";
    width: 6px;
    height: 6px;
    background: currentColor;
    border-radius: 50%;
}

.ab-user-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    height: 2.5rem;
    padding: 0.25rem 0.625rem 0.25rem 0.25rem;
    color: var(--ab-text);
    background: transparent;
    border: 0;
    border-radius: 999px;
    transition: background-color 0.15s;
}

.ab-user-toggle:hover,
.ab-user-toggle[aria-expanded=true] {
    background-color: var(--ab-surface-hover);
}

.ab-user-toggle .ab-user-name {
    font-size: 0.9375rem;
    font-weight: 600;
}

.ab-user-toggle .bi-chevron-down {
    font-size: 0.75rem;
    color: var(--ab-text-muted);
}

.ab-avatar {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: #fff;
    background-image: var(--ab-gradient);
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}

.ab-avatar-lg {
    width: 2.75rem;
    height: 2.75rem;
    font-size: 1rem;
}

.ab-user-menu {
    min-width: 16rem;
}

.ab-user-menu-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 0.625rem 0.75rem;
}

.ab-user-menu-header .ab-user-menu-name {
    font-weight: 700;
    line-height: 1.2;
}

.ab-user-menu-header .ab-user-menu-email {
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    word-break: break-all;
}

.ab-user-menu form {
    margin: 0;
}

.ab-user-menu .ab-sign-out,
.ab-user-menu .ab-sign-out > .bi {
    color: var(--ab-danger-text);
}

.ab-theme-menu {
    min-width: 10rem;
}

.ab-theme-menu .bi-check-lg {
    margin-inline-start: auto;
    color: var(--ab-brand-text);
}

.ab-theme-menu .dropdown-header {
    display: block;
    padding: 0.375rem 0.625rem 0.25rem;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.app-main {
    background: transparent;
}

.app-main .app-content-header {
    padding: 1.75rem 1rem 1rem;
}

.app-main .app-content {
    padding: 0 1rem 1.5rem;
}

.app-main .app-content-header .breadcrumb {
    --bs-breadcrumb-font-size: 0.8125rem;
    --bs-breadcrumb-divider-color: var(--ab-text-muted);
    --bs-breadcrumb-item-active-color: var(--ab-text-muted);
    --bs-breadcrumb-margin-bottom: 0.375rem;
    line-height: 1.5;
}

.app-main .app-content-header .breadcrumb a {
    color: var(--ab-text-muted);
    font-weight: 500;
}

.app-main .app-content-header .breadcrumb a:hover {
    color: var(--ab-brand-text);
}

.ab-page-heading {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
}

.ab-page-title {
    margin: 0;
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.025em;
}

.ab-page-description {
    max-width: 44rem;
    margin: 0.375rem 0 0;
    color: var(--ab-text-muted);
}

.app-footer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem 1rem;
    padding: 1rem 2rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    background: transparent;
    border-top: 1px solid var(--ab-border);
}

.app-footer a {
    color: inherit;
    text-decoration: none;
}

.app-footer a:hover {
    color: var(--ab-brand-text);
}

/* Shown over the page while the drawer is open on a narrow screen. A little blur makes the drawer
   the obvious thing to deal with. */
.sidebar-open .sidebar-overlay {
    background-color: rgba(2, 6, 23, 0.45) !important;
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
}

/* -------------------------------------------------------------------------------------------------
   Sign-in
   ---------------------------------------------------------------------------------------------- */

.ab-auth-body.login-page,
.ab-auth-body.register-page {
    display: block;
    min-height: 100vh;
    background-color: var(--ab-page);
}

.ab-auth {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    min-height: 100vh;
}

@media (min-width: 992px) {
    .ab-auth {
        grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr);
    }
}

/* The showcase: the brand at full strength, with nothing on it that needs to be read to sign in. */
.ab-auth-showcase {
    position: relative;
    display: none;
    flex-direction: column;
    padding: 2.5rem 3rem;
    overflow: hidden;
    color: #ffffff;
    isolation: isolate;
    background-color: #1e1b4b;
    background-image:
        radial-gradient(60% 50% at 12% 8%, rgba(99, 102, 241, 0.55) 0%, transparent 70%),
        radial-gradient(55% 55% at 92% 96%, rgba(139, 92, 246, 0.5) 0%, transparent 70%),
        linear-gradient(160deg, #1e1b4b 0%, #24125a 55%, #2e1065 100%);
}

@media (min-width: 992px) {
    .ab-auth-showcase {
        display: flex;
    }
}

/* A blueprint grid, faded out from the top left so it reads as texture rather than as a table. */
.ab-auth-showcase::before {
    position: absolute;
    inset: 0;
    z-index: -1;
    content: "";
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.07) 1px, transparent 1px);
    background-size: 44px 44px;
    -webkit-mask-image: radial-gradient(ellipse 80% 70% at 20% 15%, #000 20%, transparent 75%);
    mask-image: radial-gradient(ellipse 80% 70% at 20% 15%, #000 20%, transparent 75%);
}

.ab-auth-showcase .ab-wordmark {
    color: #ffffff;
}

.ab-auth-showcase-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

.ab-auth-pitch {
    max-width: 34rem;
    margin: auto 0;
    padding: 3rem 0;
}

.ab-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 1rem;
    padding: 0.3125rem 0.75rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #e0e7ff;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px;
}

.ab-auth-headline {
    margin: 0 0 1rem;
    font-size: clamp(2.25rem, 1.2rem + 2vw, 3.25rem);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.035em;
    text-wrap: balance;
}

/* The highlighted phrase is one thought, so it moves to the next line whole rather than leaving its
   first word stranded at the end of this one. */
.ab-gradient-text {
    white-space: nowrap;
    color: #c4b5fd;
    background-image: linear-gradient(100deg, #a5b4fc 0%, #e9d5ff 55%, #c4b5fd 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.ab-auth-lead {
    max-width: 30rem;
    margin: 0 0 2.25rem;
    font-size: 1.125rem;
    line-height: 1.6;
    color: #c7d2fe;
}

.ab-auth-points {
    display: grid;
    gap: 1.25rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ab-auth-points li {
    display: flex;
    gap: 0.875rem;
    align-items: flex-start;
}

.ab-auth-point-icon {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.125rem;
    color: #e0e7ff;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: var(--ab-radius);
}

.ab-auth-points strong {
    display: block;
    margin-bottom: 0.125rem;
    font-weight: 700;
    color: #ffffff;
}

.ab-auth-points span {
    color: #c7d2fe;
}

.ab-auth-showcase-foot {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    font-size: 0.8125rem;
    color: #a5b4fc;
}

/* The mark again, drawn huge and nearly transparent off the panel's corner. Decoration only. */
.ab-auth-motif {
    position: absolute;
    right: -6rem;
    bottom: -5rem;
    z-index: -1;
    width: 30rem;
    height: 30rem;
    color: #ffffff;
    opacity: 0.06;
    transform: rotate(-8deg);
    pointer-events: none;
}

.ab-auth-panel {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    padding: 1.25rem 1.25rem 1.5rem;
}

/* On a phone the showcase is gone, so a wash of the brand behind the form keeps the screen from
   being a white page with a box on it. */
.ab-auth-panel::before {
    position: absolute;
    inset: 0 0 auto;
    z-index: 0;
    height: 22rem;
    content: "";
    background:
        radial-gradient(70% 90% at 15% 0%, rgba(99, 102, 241, 0.16) 0%, transparent 70%),
        radial-gradient(60% 80% at 95% 10%, rgba(124, 58, 237, 0.14) 0%, transparent 70%);
    pointer-events: none;
}

@media (min-width: 992px) {
    .ab-auth-panel {
        padding: 2rem 2.5rem;
        background-color: var(--ab-surface);
    }

    .ab-auth-panel::before {
        display: none;
    }
}

.ab-auth-panel > * {
    position: relative;
    z-index: 1;
}

.ab-auth-panel-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.ab-auth-panel-brand {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    color: inherit;
    text-decoration: none;
}

@media (min-width: 992px) {
    .ab-auth-panel-brand {
        visibility: hidden;
    }
}

.ab-auth-body .login-box,
.ab-auth-body .register-box {
    width: 100%;
    max-width: 25rem;
    margin: auto;
    padding: 2rem 0;
}

/* On a phone the form sits near the top instead of floating mid-screen, which is also where it stays
   visible once the on-screen keyboard takes the bottom half. */
@media (max-width: 575.98px) {
    .ab-auth-body .login-box,
    .ab-auth-body .register-box {
        margin-top: 1.5rem;
        padding-top: 0;
    }
}

.ab-auth-body .login-box > .card,
.ab-auth-body .register-box > .card {
    border-radius: var(--ab-radius-xl);
    box-shadow: var(--ab-shadow-lg);
}

.ab-auth-body .login-card-body,
.ab-auth-body .register-card-body {
    padding: 2rem 1.5rem;
    color: var(--ab-text);
    background: transparent;
    border-radius: inherit;
}

/* Beside the showcase the panel is the card, so the form sits on it without a second box. */
@media (min-width: 992px) {
    .ab-auth-body .login-box > .card,
    .ab-auth-body .register-box > .card {
        background: transparent;
        border: 0;
        box-shadow: none;
    }

    .ab-auth-body .login-card-body,
    .ab-auth-body .register-card-body {
        padding: 0;
    }
}

.ab-auth-title {
    margin: 0 0 0.5rem;
    font-size: 1.875rem;
    font-weight: 700;
    letter-spacing: -0.03em;
}

.ab-auth-body .login-box-msg,
.ab-auth-body .register-box-msg {
    margin: 0 0 1.75rem;
    padding: 0;
    color: var(--ab-text-muted);
    text-align: left;
}

/* A field with its icon inside the input, and room on the right for the reveal control. */
.ab-field {
    position: relative;
}

.ab-field > .bi {
    position: absolute;
    top: 50%;
    left: 0.875rem;
    font-size: 1rem;
    color: var(--ab-text-muted);
    transform: translateY(-50%);
    pointer-events: none;
    transition: color 0.15s;
}

.ab-field:focus-within > .bi {
    color: var(--ab-brand-text);
}

.ab-field > .form-control {
    min-height: 3rem;
    padding-left: 2.625rem;
}

.ab-field-reveal > .form-control {
    padding-right: 3rem;
}

.ab-reveal {
    position: absolute;
    top: 50%;
    right: 0.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    color: var(--ab-text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--ab-radius-sm);
    transform: translateY(-50%);
}

.ab-reveal:hover {
    color: var(--ab-text);
    background: var(--ab-surface-hover);
}

.ab-field-hint {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.5rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ab-warning-text);
}

.ab-auth-options {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin: 1.25rem 0 1.5rem;
}

.ab-auth-options .form-check {
    margin: 0;
}

.ab-auth-options .form-check-label {
    color: var(--ab-text);
}

.ab-submit {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    width: 100%;
    min-height: 3rem;
}

.ab-submit .bi-arrow-right {
    transition: transform 0.2s;
}

.ab-submit:hover .bi-arrow-right {
    transform: translateX(3px);
}

/* The busy state is set by script on submit, so a second press cannot send a second sign-in. */
.ab-submit .spinner-border {
    display: none;
    width: 1rem;
    height: 1rem;
    border-width: 0.15em;
}

.ab-submit[aria-disabled=true] {
    pointer-events: none;
    opacity: 0.85;
}

.ab-submit[aria-disabled=true] .spinner-border {
    display: inline-block;
}

.ab-submit[aria-disabled=true] .bi-arrow-right {
    display: none;
}

/* The way to the neighbouring page — sign in from register, back from a reset — under the form. */
.ab-auth-switch {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: center;
    margin: 1.5rem 0 0;
    font-size: 0.9375rem;
    color: var(--ab-text-muted);
}

.ab-auth-switch a {
    font-weight: 600;
    text-decoration: none;
}

.ab-auth-switch a:hover {
    text-decoration: underline;
}

.ab-auth-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.25rem 1rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

/* Beside the showcase, whose own foot already says this, the panel's would be the same line twice. */
@media (min-width: 992px) {
    .ab-auth-footer {
        display: none;
    }
}

/* -------------------------------------------------------------------------------------------------
   Dashboard
   ---------------------------------------------------------------------------------------------- */

.ab-dashboard {
    display: grid;
    gap: 1.5rem;
    padding-top: 1.75rem;
}

.ab-hero {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 2rem;
    overflow: hidden;
    color: #ffffff;
    isolation: isolate;
    background-image: var(--ab-gradient-deep);
    border-radius: var(--ab-radius-xl);
    box-shadow: 0 20px 40px -24px rgba(67, 56, 202, 0.75);
}

.ab-hero::before {
    position: absolute;
    inset: 0;
    z-index: -1;
    content: "";
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.08) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.08) 1px, transparent 1px);
    background-size: 36px 36px;
    -webkit-mask-image: linear-gradient(100deg, transparent 30%, #000 100%);
    mask-image: linear-gradient(100deg, transparent 30%, #000 100%);
}

.ab-hero::after {
    position: absolute;
    top: -40%;
    right: -10%;
    z-index: -1;
    width: 26rem;
    height: 26rem;
    content: "";
    background: radial-gradient(circle, rgba(196, 181, 253, 0.45) 0%, transparent 65%);
}

.ab-hero-eyebrow {
    margin: 0 0 0.5rem;
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #e0e7ff;
}

.ab-hero-title {
    margin: 0;
    font-size: clamp(1.625rem, 1.2rem + 1.2vw, 2.125rem);
    font-weight: 700;
    letter-spacing: -0.03em;
    color: #ffffff;
}

.ab-hero-lead {
    max-width: 36rem;
    margin: 0.5rem 0 0;
    font-size: 1.0625rem;
    color: rgba(255, 255, 255, 0.86);
}

.ab-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.625rem;
    margin-top: 1.25rem;
}

.ab-hero .btn-light {
    --bs-btn-color: #3730a3;
    --bs-btn-bg: #ffffff;
    --bs-btn-border-color: #ffffff;
    --bs-btn-hover-color: #312e81;
    --bs-btn-hover-bg: #eef0fe;
    --bs-btn-hover-border-color: #eef0fe;
    box-shadow: 0 8px 20px -10px rgba(15, 23, 42, 0.6);
}

.ab-hero .ab-btn-glass {
    --bs-btn-color: #ffffff;
    --bs-btn-bg: rgba(255, 255, 255, 0.1);
    --bs-btn-border-color: rgba(255, 255, 255, 0.28);
    --bs-btn-hover-color: #ffffff;
    --bs-btn-hover-bg: rgba(255, 255, 255, 0.18);
    --bs-btn-hover-border-color: rgba(255, 255, 255, 0.4);
    --bs-btn-active-color: #ffffff;
    --bs-btn-active-bg: rgba(255, 255, 255, 0.22);
}

.ab-hero-panel {
    display: grid;
    gap: 0.75rem;
    min-width: 15rem;
    padding: 1rem 1.25rem;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: var(--ab-radius-lg);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

.ab-hero > :first-child {
    flex: 1 1 32rem;
    min-width: 0;
}

.ab-hero-panel-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    font-size: 0.875rem;
    color: rgba(255, 255, 255, 0.86);
}

.ab-hero-panel-row strong {
    font-weight: 700;
    color: #ffffff;
}

/* Below the width where the greeting and the panel fit side by side, the panel takes the full width
   under the greeting rather than hugging its left edge with a gap beside it — as a strip of three
   while there is room for one, and as a list on a phone. After the rules above, which it overrides. */
@media (max-width: 1199.98px) {
    .ab-hero-panel {
        flex: 1 1 100%;
        min-width: 0;
    }
}

@media (min-width: 576px) and (max-width: 1199.98px) {
    .ab-hero-panel {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .ab-hero-panel-row {
        flex-direction: column;
        align-items: flex-start;
        justify-content: flex-start;
        gap: 0.25rem;
    }
}

.ab-live {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.ab-live-dot {
    position: relative;
    width: 8px;
    height: 8px;
    background: #34d399;
    border-radius: 50%;
}

.ab-live-dot::after {
    position: absolute;
    inset: 0;
    content: "";
    background: inherit;
    border-radius: inherit;
    animation: ab-pulse 2s ease-out infinite;
}

.ab-live-dot.is-down {
    background: #fca5a5;
}

.ab-live-dot.is-down::after {
    animation: none;
}

@keyframes ab-pulse {
    0% { opacity: 0.7; transform: scale(1); }
    80%, 100% { opacity: 0; transform: scale(3); }
}

/* Two to a row, then four: never three, which would leave the fourth alone on a row of its own. */
.ab-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.5rem;
}

@media (min-width: 1200px) {
    .ab-stats {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

/* A tile is a reading, not a control, so it has no hover state: lifting on hover would promise a click
   that goes nowhere. */
.ab-stat {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
    padding: 1.25rem;
    overflow: hidden;
    background: var(--ab-surface);
    border: 1px solid var(--ab-border);
    border-radius: var(--ab-radius-lg);
    box-shadow: var(--ab-shadow-sm);
}

/* Two tiles to a row holds on a phone too, only tighter: four full-width tiles would push everything
   else a whole screen down, and a count does not need the width. */
@media (max-width: 575.98px) {
    .ab-dashboard {
        gap: 1rem;
        padding-top: 1rem;
    }

    .ab-hero {
        padding: 1.5rem 1.25rem;
    }

    .ab-stats {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 0.75rem;
    }

    .ab-stat {
        gap: 0.625rem;
        padding: 1rem;
    }

    .ab-stat-icon {
        width: 2rem;
        height: 2rem;
        font-size: 1rem;
    }

    .ab-stat-value {
        font-size: 1.625rem;
    }

    .ab-grid,
    .ab-stack {
        gap: 1rem;
    }
}

.ab-stat-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.ab-stat-label {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--ab-text-muted);
}

.ab-stat-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    font-size: 1.125rem;
    color: var(--ab-tone-text, var(--ab-brand-text));
    background: var(--ab-tone-tint, var(--bs-primary-bg-subtle));
    border-radius: var(--ab-radius);
}

.ab-stat-value {
    margin: 0;
    font-size: 2rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--ab-text);
    font-variant-numeric: tabular-nums;
}

.ab-stat-meta {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

/* A thin bar along the foot of a tile, in its status colour, measuring its share of the total. */
.ab-stat-meter {
    height: 4px;
    margin-top: auto;
    overflow: hidden;
    background: var(--ab-surface-hover);
    border-radius: 999px;
}

.ab-stat-meter > span {
    display: block;
    height: 100%;
    background: var(--ab-tone, var(--ab-brand));
    border-radius: inherit;
}

.ab-tone-brand { --ab-tone: var(--ab-brand); --ab-tone-text: var(--ab-progress-text); --ab-tone-tint: var(--ab-progress-tint); }
.ab-tone-draft { --ab-tone: var(--ab-draft); --ab-tone-text: var(--ab-draft-text); --ab-tone-tint: var(--ab-draft-tint); }
.ab-tone-progress { --ab-tone: var(--ab-progress); --ab-tone-text: var(--ab-progress-text); --ab-tone-tint: var(--ab-progress-tint); }
.ab-tone-complete { --ab-tone: var(--ab-complete); --ab-tone-text: var(--ab-complete-text); --ab-tone-tint: var(--ab-complete-tint); }
.ab-tone-archived { --ab-tone: var(--ab-archived); --ab-tone-text: var(--ab-archived-text); --ab-tone-tint: var(--ab-archived-tint); }

.ab-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
}

@media (min-width: 1200px) {
    .ab-grid {
        grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    }
}

.ab-stack {
    display: grid;
    gap: 1.5rem;
}

/* A status badge: the colour is never the only signal, because the word is always written. */
.ab-status {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.1875rem 0.625rem;
    font-size: 0.8125rem;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
    color: var(--ab-tone-text);
    background: var(--ab-tone-tint);
    border-radius: 999px;
}

.ab-status::before {
    content: "";
    width: 6px;
    height: 6px;
    background: var(--ab-tone);
    border-radius: 50%;
}

.ab-project-list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ab-project {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: 0.875rem;
    align-items: center;
    padding: 0.875rem 1.25rem;
    border-top: 1px solid var(--ab-border);
    transition: background-color 0.15s;
}

.ab-project:first-child {
    border-top: 0;
}

.ab-project:hover {
    background-color: var(--ab-surface-muted);
}

.ab-project-initial {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    font-weight: 700;
    color: var(--ab-tone-text);
    background: var(--ab-tone-tint);
    border-radius: var(--ab-radius);
}

.ab-project-name {
    margin: 0;
    overflow: hidden;
    font-weight: 600;
    color: var(--ab-text);
    white-space: nowrap;
    text-overflow: ellipsis;
}

.ab-project-description {
    margin: 0.125rem 0 0;
    overflow: hidden;
    font-size: 0.875rem;
    color: var(--ab-text-muted);
    white-space: nowrap;
    text-overflow: ellipsis;
}

.ab-project-side {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.25rem;
}

.ab-project-time {
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    white-space: nowrap;
}

@media (max-width: 575.98px) {
    .ab-project {
        grid-template-columns: auto minmax(0, 1fr);
    }

    .ab-project-side {
        grid-column: 2;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }
}

.ab-breakdown-bar {
    display: flex;
    gap: 3px;
    height: 0.75rem;
    margin-bottom: 1.25rem;
    overflow: hidden;
    background: var(--ab-surface-hover);
    border-radius: 999px;
}

.ab-breakdown-bar > span {
    min-width: 6px;
    background: var(--ab-tone);
}

.ab-breakdown-bar > span:first-child {
    border-radius: 999px 0 0 999px;
}

.ab-breakdown-bar > span:last-child {
    border-radius: 0 999px 999px 0;
}

.ab-breakdown-bar > span:only-child {
    border-radius: 999px;
}

.ab-legend {
    display: grid;
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ab-legend li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    gap: 0.625rem;
    align-items: center;
    font-size: 0.9375rem;
}

.ab-legend-swatch {
    width: 10px;
    height: 10px;
    background: var(--ab-tone);
    border-radius: 3px;
}

.ab-legend-count {
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.ab-legend-share {
    min-width: 3rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ab-facts {
    display: grid;
    margin: 0;
}

.ab-fact {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 0;
    border-top: 1px solid var(--ab-border);
}

.ab-fact:first-child {
    padding-top: 0;
    border-top: 0;
}

.ab-fact:last-child {
    padding-bottom: 0;
}

.ab-fact dt {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    font-weight: 500;
    color: var(--ab-text-muted);
}

.ab-fact dt .bi {
    font-size: 1rem;
}

.ab-fact dd {
    margin: 0;
    font-weight: 600;
    color: var(--ab-text);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.ab-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 2.5rem 1.5rem;
    text-align: center;
}

.ab-empty-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    margin-bottom: 1rem;
    font-size: 1.5rem;
    color: var(--ab-tone-text, var(--ab-brand-text));
    background: var(--ab-tone-tint, var(--bs-primary-bg-subtle));
    border-radius: 50%;
    box-shadow: 0 0 0 8px color-mix(in srgb, var(--ab-tone-tint, var(--bs-primary-bg-subtle)) 45%, transparent);
}

.ab-empty-title {
    margin: 0 0 0.375rem;
    font-size: 1.0625rem;
    font-weight: 700;
}

.ab-empty-body {
    max-width: 26rem;
    margin: 0;
    color: var(--ab-text-muted);
}

.ab-empty .btn {
    margin-top: 1.25rem;
}

/* -------------------------------------------------------------------------------------------------
   Motion
   ---------------------------------------------------------------------------------------------- */

/* Content settles in once, on arrival, a few milliseconds apart. It is never needed to read the page,
   so it is the first thing to go when the reader has asked for less motion. */
.ab-rise {
    animation: ab-rise 0.45s cubic-bezier(0.2, 0.7, 0.2, 1) both;
    animation-delay: calc(var(--ab-order, 0) * 60ms);
}

@keyframes ab-rise {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .ab-rise,
    .ab-live-dot::after {
        animation: none;
    }

    .ab-btn-gradient,
    .ab-submit .bi-arrow-right {
        transition: none;
        transform: none;
    }
}

/* -------------------------------------------------------------------------------------------------
   Workspace: one project's details, ideas and discussion on one card with three tabs
   ------------------------------------------------------------------------------------------------- */

.ab-notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
}

.ab-workspace {
    overflow: hidden;
}

/* The tabs on the left and, on the right, which project and idea the other tabs are showing. */
.ab-workspace-tabs {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 0.5rem 1.5rem;
    padding-bottom: 0;
}

.ab-workspace-tabs .nav-link {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
    color: var(--ab-text-muted);
}

.ab-workspace-tabs .nav-link.active {
    color: var(--ab-brand-text);
}

.ab-count {
    min-width: 1.5rem;
    padding: 0.0625rem 0.4375rem;
    font-size: 0.75rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: center;
    color: var(--ab-text-muted);
    background: var(--ab-surface-hover);
    border-radius: 999px;
}

.nav-link.active .ab-count {
    color: var(--ab-progress-text);
    background: var(--ab-progress-tint);
}

.ab-selection {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    padding-bottom: 0.625rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

.ab-selection a {
    max-width: 22ch;
    overflow: hidden;
    font-weight: 600;
    color: var(--ab-text);
    text-decoration: none;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ab-selection a:hover {
    color: var(--ab-brand-text);
}

.ab-workspace .tab-pane {
    padding: 1.25rem;
}

.ab-pane-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.ab-pane-bar-end {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-left: auto;
}

.ab-picker {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
}

.ab-picker .form-select {
    width: auto;
    min-width: 14rem;
    max-width: 28rem;
    font-weight: 600;
}

.ab-picker-label {
    margin: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ab-text-muted);
}

/* Filter chips. Pressed reads as the brand's tint, and aria-pressed says the same to a screen reader. */
.ab-filters {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.375rem;
}

.ab-filter {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.25rem 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ab-text-muted);
    background: var(--ab-surface);
    border: 1px solid var(--ab-border);
    border-radius: 999px;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.ab-filter:hover {
    color: var(--ab-text);
    background: var(--ab-surface-hover);
}

.ab-filter.is-active {
    color: var(--ab-progress-text);
    background: var(--ab-progress-tint);
    border-color: transparent;
}

.ab-filter-count {
    font-variant-numeric: tabular-nums;
    opacity: 0.8;
}

.ab-filter-empty,
.ab-thread-empty {
    margin: 1rem 0 0;
    padding: 1rem;
    text-align: center;
    color: var(--ab-text-muted);
}

/* The lists. */
.ab-table {
    --bs-table-bg: transparent;
    --bs-table-hover-bg: var(--ab-surface-hover);
}

.ab-table thead th {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--ab-text-muted);
    border-bottom-color: var(--ab-border);
}

.ab-table td {
    border-color: var(--ab-border);
}

.ab-cell-main {
    min-width: 14rem;
}

.ab-row-title {
    font-weight: 600;
    color: var(--ab-text);
    text-decoration: none;
}

.ab-row-title:hover {
    color: var(--ab-brand-text);
    text-decoration: underline;
}

.ab-row-sub {
    display: block;
    max-width: 48ch;
    overflow: hidden;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ab-cell-owner {
    font-size: 0.875rem;
    white-space: nowrap;
}

/* The project or idea the other tabs are showing: tinted, with a bar along its leading edge. */
.ab-row-selected > td {
    background: var(--ab-progress-tint);
}

.ab-row-selected > td:first-child {
    box-shadow: inset 3px 0 0 var(--ab-brand);
}

/* On hold and cancelled: kept and listed, but quietened, because the work leaves them out. The status
   control beside each still says which, so the opacity is never the only signal. */
.ab-set-aside > td,
.ab-set-aside > .ab-post-main,
.ab-set-aside > .ab-avatar {
    opacity: 0.62;
}

.ab-row-actions {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.25rem;
}

.ab-row-actions form {
    display: inline;
    margin: 0;
}

.ab-btn-danger:hover,
.ab-btn-danger:focus-visible {
    --bs-btn-hover-color: var(--ab-danger-text);
    --bs-btn-hover-bg: var(--ab-danger-tint);
    --bs-btn-hover-border-color: var(--ab-danger-border);
    color: var(--ab-danger-text);
}

/* A status that is also a control: the badge's colours on a select, so changing it is one click. */
.ab-status-form {
    margin: 0;
}

.ab-status-select {
    width: auto;
    min-width: 8.5rem;
    padding-top: 0.1875rem;
    padding-bottom: 0.1875rem;
    font-weight: 600;
    color: var(--ab-tone-text);
    background-color: var(--ab-tone-tint);
    border-color: transparent;
    border-radius: 999px;
}

.ab-set-aside-note {
    display: flex;
    gap: 0.5rem;
    margin: 0 0 1rem;
    padding: 0.625rem 0.875rem;
    font-size: 0.875rem;
    color: var(--ab-draft-text);
    background: var(--ab-draft-tint);
    border-radius: var(--ab-radius-sm);
}

/* The discussion: the idea opens it, and each comment follows in the order it was written, on a line
   that runs down the avatars so it reads as one conversation. */
.ab-thread {
    position: relative;
    display: grid;
    gap: 1rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ab-thread::before {
    position: absolute;
    top: 1rem;
    bottom: 1rem;
    left: calc(1rem - 1px);
    width: 2px;
    content: "";
    background: var(--ab-border);
}

.ab-post {
    position: relative;
    display: grid;
    grid-template-columns: 2rem minmax(0, 1fr);
    gap: 0.875rem;
    align-items: start;
}

.ab-post-main {
    padding: 0.875rem 1rem;
    background: var(--ab-surface);
    border: 1px solid var(--ab-border);
    border-radius: var(--ab-radius);
    box-shadow: var(--ab-shadow-xs);
}

.ab-post-opening .ab-post-main {
    background: var(--ab-surface-muted);
    border-color: var(--ab-border-strong);
}

.ab-post-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 0.625rem;
    margin-bottom: 0.375rem;
}

/* A post's status, at the end of its header: a control for whoever may change it, a badge otherwise. */
.ab-post-status {
    margin-left: auto;
}

.ab-post-meta {
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

.ab-post-title {
    margin: 0.25rem 0 0.5rem;
    font-size: 1.125rem;
    font-weight: 700;
}

/* Written text keeps the line breaks it was typed with. */
.ab-post-body {
    overflow-wrap: anywhere;
    white-space: pre-wrap;
}

/* Edit and delete stay small and below the text: the words are what a thread is read for. */
.ab-post-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.625rem;
}

.ab-post-actions .btn {
    --bs-btn-padding-y: 0.125rem;
    --bs-btn-padding-x: 0.5rem;
    --bs-btn-font-size: 0.8125rem;
    box-shadow: none;
}

.ab-post-actions form {
    margin: 0;
}

.ab-composer {
    margin-top: 1.25rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ab-border);
}

.ab-composer .form-label {
    font-weight: 600;
}

.ab-composer-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.625rem;
}

.ab-workspace-foot {
    display: flex;
    gap: 0.5rem;
    margin: 0;
    padding: 0.75rem 1.25rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
    background: var(--ab-surface-muted);
    border-top: 1px solid var(--ab-border);
}

.ab-dialog-context {
    margin: 0 0 0.75rem;
    font-size: 0.875rem;
    color: var(--ab-text-muted);
}

/* On a phone the pickers and filters take the full width, one to a line, rather than squeezing, and the
   three tabs keep to one row: their icons go, and the row scrolls sideways if it still does not fit. */
@media (max-width: 575.98px) {
    .ab-workspace .tab-pane {
        padding: 1rem;
    }

    .ab-workspace-tabs .nav-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .ab-workspace-tabs .nav-link {
        gap: 0.375rem;
        padding-inline: 0.625rem;
        white-space: nowrap;
    }

    .ab-workspace-tabs .nav-link > .bi {
        display: none;
    }

    .ab-pane-bar-end {
        margin-left: 0;
    }

    .ab-picker,
    .ab-picker .form-select {
        width: 100%;
        min-width: 0;
    }

    .ab-selection {
        display: none;
    }
}

/* -------------------------------------------------------------------------------------------------
   Users
   ------------------------------------------------------------------------------------------------- */

/* A card header with its title on the left and the list's filter on the right. */
.ab-card-header-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.ab-user-cell {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    min-width: 0;
}

.ab-self-note {
    display: flex;
    gap: 0.5rem;
    margin: 0;
    padding: 0.625rem 0.875rem;
    font-size: 0.875rem;
    color: var(--ab-progress-text);
    background: var(--ab-progress-tint);
    border-radius: var(--ab-radius-sm);
}

/* A quiet button that is unavailable reads as unavailable: fainter than its neighbours, never brighter.
   The template's disabled colours are its own and stand out on the dark theme. */
.ab-btn-quiet {
    --bs-btn-disabled-color: var(--ab-text-muted);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: var(--ab-border);
    --bs-btn-disabled-opacity: 0.5;
}

/* On a phone a list keeps what it is for — the name, its state, and what can be done — and drops the
   counts, dates and owners, which a wider screen shows, so the actions are on screen rather than off
   its edge. */
@media (max-width: 575.98px) {
    .ab-cell-main {
        min-width: 0;
    }

    .ab-row-sub {
        max-width: 26ch;
    }

    /* Each row becomes a small card: the name and its state on one line, its actions on the next. The
       headings name columns that are no longer side by side, so they are kept for screen readers only. */
    .ab-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .ab-table tbody tr {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        gap: 0.5rem 0.75rem;
        align-items: center;
        padding: 0.75rem 0.5rem;
        border-bottom: 1px solid var(--ab-border);
    }

    .ab-table tbody td {
        display: block;
        padding: 0;
        border: 0;
    }

    .ab-table tbody td:last-child {
        grid-column: 1 / -1;
    }

    .ab-table .ab-row-actions {
        justify-content: flex-start;
    }

    .ab-row-selected {
        background: var(--ab-progress-tint);
        box-shadow: inset 3px 0 0 var(--ab-brand);
    }

    .ab-row-selected > td:first-child {
        box-shadow: none;
    }
}

/* -------------------------------------------------------------------------------------------------
   Password fields and My account
   ------------------------------------------------------------------------------------------------- */

/* A password field with the eye that shows it, as on the sign-in page: the reveal control sits inside
   the right edge of the field, and the field leaves room for it — and for the refusal icon beside it. */
.ab-password {
    position: relative;
}

.ab-password > .form-control {
    padding-right: 3rem;
}

.ab-password > .form-control.is-invalid {
    padding-right: 4.75rem;
    background-position: right 3rem center;
}

.ab-account-summary {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.25rem;
}

.ab-avatar-xl {
    width: 3.5rem;
    height: 3.5rem;
    font-size: 1.25rem;
}

.ab-account-name {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 700;
}

.ab-account-email {
    margin: 0;
    color: var(--ab-text-muted);
}

/* A value shown in a form but not changeable there reads as fixed, not as a field waiting for input. */
.form-control[readonly] {
    color: var(--ab-text-muted);
    background-color: var(--ab-surface-muted);
    cursor: default;
}

.form-control[readonly]:focus {
    box-shadow: none;
}

/* An account's enabled switch, inline in its row. */
.ab-switch-form {
    margin: 0;
}

.ab-switch-form .form-check-label {
    font-size: 0.875rem;
    font-weight: 600;
    white-space: nowrap;
}

/* -------------------------------------------------------------------------------------------------
   Voice typing
   ------------------------------------------------------------------------------------------------- */

/* A text area with a microphone in its top corner; the text keeps clear of it. */
.ab-dictation {
    position: relative;
}

.ab-dictation > textarea.form-control {
    padding-right: 3rem;
}

.ab-dictate {
    position: absolute;
    top: 0.375rem;
    right: 0.375rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    font-size: 1.0625rem;
    color: var(--ab-text-muted);
    background: transparent;
    border: 0;
    border-radius: 50%;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.ab-dictate:hover {
    color: var(--ab-text);
    background: var(--ab-surface-hover);
}

/* Listening is unmistakable: red, with a ring that pulses outward — still for readers who ask for less
   motion, where the colour and the words beneath the field say it alone. */
.ab-dictate.is-listening {
    color: #fff;
    background: #dc2626;
    animation: ab-listening 1.4s ease-out infinite;
}

.ab-dictate.is-unavailable {
    cursor: not-allowed;
    opacity: 0.45;
}

@keyframes ab-listening {
    0% {
        box-shadow: 0 0 0 0 rgba(220, 38, 38, 0.55);
    }

    70% {
        box-shadow: 0 0 0 0.625rem rgba(220, 38, 38, 0);
    }

    100% {
        box-shadow: 0 0 0 0 rgba(220, 38, 38, 0);
    }
}

@media (prefers-reduced-motion: reduce) {
    .ab-dictate.is-listening {
        animation: none;
    }
}

.ab-dictation-status {
    display: block;
    margin-top: 0.375rem;
    font-size: 0.8125rem;
    color: var(--ab-text-muted);
}

.ab-dictation-status:empty {
    display: none;
}

.ab-dictation-status.is-listening {
    font-weight: 600;
    color: var(--ab-danger-text);
}

/* A field inside a wrapper — a microphone's or a password eye's — is not its error message's sibling,
   so Bootstrap's own rule cannot show the message; this shows it whenever the field inside is invalid,
   and hides it again the moment a dialog clears the field for its next use. */
.ab-dictation:has(> .is-invalid) ~ .invalid-feedback,
.ab-password:has(> .is-invalid) ~ .invalid-feedback {
    display: block;
}


/* -------------------------------------------------------------------------------------------------
   Data tables: a list searched, filtered, sorted and paged on the server
   ------------------------------------------------------------------------------------------------- */

.ab-datatable-head {
    padding-top: 0.875rem;
    padding-bottom: 0.875rem;
}

.ab-datatable-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem 1rem;
    width: 100%;
    margin: 0;
}

/* The search takes the room the filters leave, with its icon inside its leading edge. */
.ab-search {
    position: relative;
    flex: 1 1 18rem;
    max-width: 32rem;
}

.ab-search > .bi {
    position: absolute;
    top: 50%;
    left: 0.875rem;
    color: var(--ab-text-muted);
    transform: translateY(-50%);
    pointer-events: none;
}

.ab-search .form-control {
    padding-left: 2.5rem;
}

.ab-tool {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.ab-tool .form-select {
    width: auto;
}

.ab-tool-label {
    margin: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    color: var(--ab-text-muted);
}

/* While the next results are on their way, the current ones dim rather than vanish. */
.ab-datatable-body {
    transition: opacity 0.15s ease;
}

.ab-datatable-body.is-loading {
    opacity: 0.55;
}

/* A sortable heading: the whole label is the link, and the arrow says which way it sorts now. */
.ab-sort {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    color: inherit;
    text-decoration: none;
}

.ab-sort .bi {
    font-size: 0.6875rem;
    opacity: 0.45;
}

.ab-sort:hover,
.ab-sort.is-sorted {
    color: var(--ab-brand-text);
}

.ab-sort.is-sorted .bi {
    opacity: 1;
}

/* The same choices as the headings, for a phone, where the headings are hidden. */
.ab-sort-chips {
    display: none;
}

.ab-datatable-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.875rem 1.25rem;
    border-top: 1px solid var(--ab-border);
}

.ab-datatable-summary {
    margin: 0;
    font-size: 0.875rem;
    color: var(--ab-text-muted);
}

/* The template compiles Bootstrap's blue into the pager, so the variables are restated in the brand. */
.ab-pagination {
    --bs-pagination-color: var(--ab-text);
    --bs-pagination-bg: var(--ab-surface);
    --bs-pagination-border-color: var(--ab-border);
    --bs-pagination-hover-color: var(--ab-brand-text);
    --bs-pagination-hover-bg: var(--ab-surface-hover);
    --bs-pagination-hover-border-color: var(--ab-border);
    --bs-pagination-focus-color: var(--ab-brand-text);
    --bs-pagination-focus-bg: var(--ab-surface-hover);
    --bs-pagination-focus-box-shadow: none;
    --bs-pagination-active-color: #ffffff;
    --bs-pagination-active-bg: var(--ab-brand);
    --bs-pagination-active-border-color: var(--ab-brand);
    --bs-pagination-disabled-color: var(--ab-text-muted);
    --bs-pagination-disabled-bg: var(--ab-surface);
    --bs-pagination-disabled-border-color: var(--ab-border);
    margin: 0;
}

.ab-pagination .page-link {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .ab-datatable-body {
        transition: none;
    }
}

/* On a phone the tools stack, a row of sort links stands in for the hidden headings, and the pager keeps
   its previous, current and next pages but drops the numbers between them. */
@media (max-width: 575.98px) {
    .ab-search {
        flex-basis: 100%;
        max-width: none;
    }

    /* The search's hint is set a size smaller to fit a phone's width. What is typed stays full size,
       which also keeps a phone from zooming in on the field. */
    .ab-search .form-control::placeholder {
        font-size: 0.875rem;
    }

    /* A row each: "All statuses (38)" and "10 per page" do not both fit beside each other. */
    .ab-tool {
        flex: 1 1 100%;
        min-width: 0;
    }

    /* The choices say what they are — "All statuses", "10 per page" — so on a phone the labels beside
       them are kept for screen readers only, and each list has its row to itself. */
    .ab-tool > .ab-tool-label {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .ab-tool .form-select {
        width: 100%;
        min-width: 0;
    }

    .ab-sort-chips {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.375rem;
        padding: 0.75rem 1rem 0.25rem;
    }

    .ab-sort-chips .ab-sort {
        padding: 0.1875rem 0.625rem;
        font-size: 0.8125rem;
        font-weight: 600;
        color: var(--ab-text-muted);
        background: var(--ab-surface);
        border: 1px solid var(--ab-border);
        border-radius: 999px;
    }

    .ab-sort-chips .ab-sort.is-sorted {
        color: var(--ab-progress-text);
        background: var(--ab-progress-tint);
        border-color: transparent;
    }

    .ab-datatable-foot {
        justify-content: center;
        padding: 0.75rem 1rem;
    }

    .ab-datatable-summary {
        width: 100%;
        text-align: center;
    }

    .ab-pagination .page-item:not(:first-child):not(:last-child):not(.active) {
        display: none;
    }
}

/* -------------------------------------------------------------------------------------------------
   One project: its details form, the way to delete it, and the terms that cover it
   ------------------------------------------------------------------------------------------------- */

/* The details read as a form rather than a full-width sheet: a line of text is easier to follow. */
.ab-details-form,
.ab-danger-zone,
.ab-terms-card {
    max-width: 52rem;
}

.ab-details-form .form-label {
    font-weight: 600;
}

.ab-details-status {
    width: auto;
    min-width: 14rem;
}

.ab-form-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--ab-border);
}

/* Save, Save and continue and Cancel, in that order, at the foot of every create and edit form. */
.ab-save-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.ab-form-foot .ab-save-actions {
    margin-left: auto;
}

/* On a phone the three do not fit on a line, so they stack, each the full width and easy to reach. */
@media (max-width: 575.98px) {
    .ab-save-actions {
        width: 100%;
    }

    .ab-save-actions > .btn {
        flex: 1 1 100%;
    }
}

.ab-terms-note {
    display: flex;
    gap: 0.5rem;
    max-width: 34rem;
    margin: 0;
    font-size: 0.875rem;
    color: var(--ab-text-muted);
}

.ab-terms-note a {
    font-weight: 600;
}

/* Deleting the project is set apart below its form, in the danger colour, so it is never pressed for Save. */
.ab-danger-zone {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem 1.5rem;
    margin-top: 2rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--ab-danger-border);
    border-radius: var(--ab-radius);
}

.ab-danger-title {
    margin: 0 0 0.25rem;
    font-size: 1rem;
    font-weight: 700;
    color: var(--ab-danger-text);
}

.ab-danger-body {
    margin: 0;
    font-size: 0.875rem;
    color: var(--ab-text-muted);
}

/* The page header's status, a size up from the one in a row. */
.ab-status-lg {
    padding: 0.375rem 0.875rem;
    font-size: 0.875rem;
}

/* A tab that cannot be opened yet reads as waiting rather than broken, and still shows why on hover. */
.ab-workspace-tabs .nav-link:disabled {
    pointer-events: auto;
    cursor: not-allowed;
    opacity: 0.55;
}

/* The terms: a lead line, then three short sections, each under a heading with its icon. */
.ab-terms {
    display: grid;
    gap: 1.25rem;
}

.ab-terms-lead {
    margin: 0;
    color: var(--ab-text);
}

.ab-terms-section p {
    margin: 0 0 0.5rem;
}

.ab-terms-section p:last-child {
    margin-bottom: 0;
}

.ab-terms-heading {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.5rem;
    font-size: 1rem;
    font-weight: 700;
}

.ab-terms-heading .bi {
    color: var(--ab-brand-text);
}

.ab-terms-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 0.5rem;
    margin: 0.5rem 0 0;
    padding: 0;
    list-style: none;
}

.ab-terms-split li {
    padding: 0.625rem 0.875rem;
    background: var(--ab-surface-muted);
    border: 1px solid var(--ab-border);
    border-radius: var(--ab-radius-sm);
}

.ab-terms-split strong {
    color: var(--ab-brand-text);
}
