/* Tenex Command Center - Custom Styles */

/* Tailwind utilities not emitted by the current static tailwind.css build —
   added here so templates can use them without a rebuild. */
.gap-x-6   { column-gap: 1.5rem; }
.pt-8      { padding-top: 2rem; }
.pt-10     { padding-top: 2.5rem; }
.max-w-4xl  { max-width: 56rem; }
.max-w-5xl  { max-width: 64rem; }
.max-w-6xl  { max-width: 72rem; }
.max-w-7xl  { max-width: 80rem; }
.max-w-8xl  { max-width: 88rem; }
.max-w-9xl  { max-width: 96rem; }
.max-w-10xl { max-width: 104rem; }

/* Single source of truth for top-level page width. Driven by --page-max-width
   in variables.css so every page that wears this class updates together. */
.app-page-max {
    max-width: var(--page-max-width);
    margin-left: auto;
    margin-right: auto;
}

/* Form-only variant for Add / wizard pages (customer_info, customer_add,
   customer_user_add, tenant_add, deals_provisioning, send_invite, review).
   Driven by --form-page-max-width so forms can be tuned independently of
   detail / list pages. Change that one token to resize every form page. */
.app-form-page-max {
    max-width: var(--form-page-max-width);
    margin-left: auto;
    margin-right: auto;
}

/* ============================================================
   MODAL — fixed-header / scrollable-body / fixed-footer chrome.
   Single source of truth for modal styling across CC. Tweak any
   property here (padding, border, radius, max-width, etc.) to
   update every modal at once.

   Usage:
       <div class="modal-overlay" style="display: none;" onclick="closeFoo()">
           <div class="modal-card" onclick="event.stopPropagation()">
               <button class="modal-close" onclick="closeFoo()" aria-label="Close">
                   <svg>…X icon…</svg>
               </button>
               <div class="modal-header">
                   <div class="modal-header-icon">…</div>   <!-- hidden by default; opt-in -->
                   <div>
                       <h3 class="modal-title">…</h3>
                       <p class="modal-subtitle">…</p>
                   </div>
               </div>
               <div class="modal-body">…</div>
               <div class="modal-footer">
                   <button class="btn btn-outline">Cancel</button>
                   <button class="btn btn-primary">Save</button>
               </div>
           </div>
       </div>

   The overlay keeps `style="display: none"` inline so existing JS
   that toggles via `style.display = 'flex'` keeps working.
   ============================================================ */
.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 1000;
    align-items: center;
    justify-content: center;
}

/* Modifier for a modal that opens on top of another already-open modal
   (e.g. a confirm-changes step). Lifts above the parent overlay. */
.modal-overlay-nested {
    z-index: 1100;
}

.modal-card {
    background-color: var(--card);
    border: 1px solid var(--border);
    border-radius: 8px;
    max-width: 600px;
    width: 90%;
    max-height: 80vh;
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Storybook DialogClose: 16x16 X icon pinned to the modal's top-right
   corner, opacity-70 → 100% on hover, ring on focus-visible. Modals that
   add this button get a consistent close affordance — pair with the
   corresponding close handler on `onclick`. */
.modal-close {
    position: absolute;
    top: 1rem;
    right: 1rem;
    padding: 0.25rem;
    border: none;
    background: transparent;
    color: var(--foreground);
    opacity: 0.7;
    border-radius: var(--radius);
    cursor: pointer;
    transition: opacity 0.15s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

.modal-close:hover {
    opacity: 1;
}

.modal-close:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
    opacity: 1;
}

.modal-close svg {
    width: 1rem;
    height: 1rem;
}

.modal-card-sm { max-width: 500px; }
.modal-card-lg { max-width: 800px; }

/* Header / body / footer share one continuous surface — no internal borders,
   matching Storybook DialogContent. The 1.5rem padding on each section adds
   up to a single 1.5rem gap between adjacent sections, since the inner edges
   collapse to zero (header bottom = 0, body top/bottom = 1.5rem, footer top
   = 0). */
.modal-header {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 1.5rem 1.5rem 0;
    flex-shrink: 0;
}

/* Hidden by default to match Storybook DialogHeader, which has no avatar.
   Per-modal overrides can re-show it (e.g. for confirmation modals where
   tone — destructive vs primary — is communicated via the leading icon). */
.modal-header-icon {
    display: none;
    flex-shrink: 0;
    padding: 0.5rem;
    border-radius: 9999px;
    background-color: color-mix(in srgb, var(--primary) 15%, transparent);
    color: var(--primary);
    align-items: center;
    justify-content: center;
}

.modal-header-icon svg {
    width: 1.25rem;
    height: 1.25rem;
}

/* Storybook DialogTitle: text-lg font-semibold leading-none tracking-tight */
.modal-title {
    color: var(--foreground);
    font-size: 1.125rem;
    font-weight: 600;
    line-height: 1;
    letter-spacing: -0.025em;
    margin: 0;
}

/* Storybook DialogDescription: text-sm text-muted-foreground, with a
   space-y-1.5 (6px) gap from the title above it. */
.modal-subtitle {
    font-size: 0.875rem;
    color: var(--muted-foreground);
    margin: 0.375rem 0 0;
}

.modal-body {
    padding: 1.5rem;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}

/* Sticky table headers inside any scrollable modal body. Card-color
   background keeps the thead opaque so rows scroll under it cleanly.
   Per-modal inline styles can override (e.g. to use a different bg). */
.modal-body table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background-color: var(--card);
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0 1.5rem 1.5rem;
    flex-shrink: 0;
}

/* Lock the page scroll while any modal overlay is open. The overlay's
   inline style flips between `display: none` (closed) and `display: flex`
   (open) — substring-matching "flex" reliably distinguishes the two. */
body:has(.modal-overlay[style*="flex"]) {
    overflow: hidden;
}

/* Inline-validation error state for inputs/selects inside a modal. JS adds
   `.is-invalid` to fields that fail required-field checks; clearing the
   class is on the next save attempt or on input. */
input.is-invalid,
select.is-invalid,
textarea.is-invalid {
    border-color: var(--destructive) !important;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--destructive) 25%, transparent) !important;
}

/* Conditionally-shown form row that animates open/closed instead of
   display:none jumping. Uses the grid-template-rows 0fr → 1fr trick so the
   collapsed state carries no height contribution (and the surrounding
   space-y margin too — overridden via !important since Tailwind's
   .space-y-* selector wins on specificity otherwise). */
.entra-collapse {
    display: grid;
    grid-template-rows: 0fr;
    margin-top: 0 !important;
    opacity: 0;
    transition: grid-template-rows 0.22s cubic-bezier(0.4, 0, 0.2, 1),
                margin-top 0.22s cubic-bezier(0.4, 0, 0.2, 1),
                opacity 0.15s ease;
}

.entra-collapse > .entra-collapse-inner {
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.entra-collapse.is-open {
    grid-template-rows: 1fr;
    margin-top: 1rem !important;
    opacity: 1;
}

:root {
    --primary-color: #0066cc;
    --secondary-color: #6c757d;
    
    /* Tenex Brand Colors */
    --tenex-dark: hsl(220, 65%, 6%);
    --tenex-darker: hsl(220, 50%, 10%);
    --tenex-slate: hsl(215, 25%, 16%);
    --tenex-blue: hsl(213, 94%, 68%);
    --tenex-blue-light: hsl(213, 94%, 78%);
    --tenex-gray: hsl(215, 14%, 71%);
    --tenex-gray-light: hsl(215, 20%, 85%);
}

body {
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

main {
    min-height: calc(100vh - 200px);
}

.card {
    border: none;
    box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
    margin-bottom: 1.5rem;
}

.card-title {
    color: var(--primary-color);
    font-weight: 600;
}

.navbar-brand {
    font-weight: 700;
    font-size: 1.5rem;
}

.alert-sm {
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem;
}

code {
    background-color: #f4f4f4;
    padding: 0.2rem 0.4rem;
    border-radius: 3px;
    color: #d63384;
}

/* ========================================
   LOGIN PAGE STYLES
   ======================================== */

.login-page {
    min-height: 100vh;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 16px;
    padding-top: 48px;
    padding-bottom: 48px;
    background-color: var(--background);
    color: var(--foreground);
}

.login-card {
    width: 100%;
    max-width: 420px;
    border-radius: 12px;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
    padding: 48px;
    background-color: var(--card);
    border: 1px solid var(--border);
}

.login-logo-section {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 32px;
}

.login-logo {
    height: 20px;
    width: auto;
}

.login-logo-dark {
    display: none;
}

.login-heading {
    font-size: 30px;
    font-weight: 600;
    text-align: center;
    margin-bottom: 32px;
    color: var(--foreground);
}

.login-form {
    margin-bottom: 24px;
}

.login-button {
    width: 100%;
    padding: 16px 24px;
    border-radius: 9999px;
    font-weight: 600;
    font-size: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    transition: all 200ms;
    background-color: var(--card);
    border: 1px solid var(--border);
    color: var(--foreground);
    cursor: pointer;
}

.login-button:hover {
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
    opacity: 0.9;
}

.login-button:active {
    transform: scale(0.95);
}

.login-button:disabled {
    opacity: 0.7;
    cursor: not-allowed;
}

.login-button svg {
    width: 24px;
    height: 24px;
}

.login-footer {
    font-size: 14px;
    text-align: center;
    color: var(--muted-foreground);
}

.login-error {
    display: flex;
    gap: 12px;
    margin-bottom: 32px;
    padding: 16px;
    border-radius: 8px;
    background-color: color-mix(in srgb, var(--destructive) 10%, transparent);
    border: 1px solid var(--destructive);
}

.login-error-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    color: var(--destructive);
}

.login-error-text {
    font-size: 14px;
    font-weight: 500;
    color: var(--destructive);
}

.theme-toggle-btn {
    position: fixed;
    top: 24px;
    right: 24px;
    z-index: 50;
    padding: 8px;
    border-radius: 8px;
    background: transparent;
    border: none;
    cursor: pointer;
    color: var(--foreground);
    transition: background-color 200ms ease;
}

.theme-toggle-btn:hover {
    background-color: rgba(0, 0, 0, 0.05);
}

.theme-toggle-btn svg {
    width: 24px;
    height: 24px;
}

.spinner {
    width: 40px;
    height: 40px;
    border: 4px solid rgba(255, 255, 255, 0.1);
    border-top: 4px solid currentColor;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

.spinner-lg {
    width: 48px;
    height: 48px;
}

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* ========================================
   BUTTON SYSTEM
   Mirrors Nucleus shadcn/ui sizing: h-9 (36px), text-sm, 16px icons.
   Use: <button class="btn btn-primary">…</button>
   ======================================== */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;                    /* gap-2 */
    height: 2.25rem;                /* h-9 = 36px */
    padding: 0 1rem;                /* px-4 */
    border-radius: var(--radius);   /* rounded-md */
    font-size: 0.875rem;            /* text-sm */
    font-weight: 500;               /* font-medium */
    line-height: 1;
    white-space: nowrap;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color var(--transition-base),
                color var(--transition-base),
                border-color var(--transition-base),
                opacity var(--transition-base);
}

.btn:focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

.btn:disabled {
    pointer-events: none;
    opacity: 0.5;
}

.btn svg {
    width: 1rem;    /* size-4 */
    height: 1rem;
    flex-shrink: 0;
}

/* Size variants */
.btn-sm { height: 2rem;   padding: 0 0.75rem; gap: 0.375rem; }  /* h-8 */
.btn-lg { height: 2.5rem; padding: 0 1.5rem;  }                 /* h-10 */
.btn-icon    { width: 2.25rem; height: 2.25rem; padding: 0; }
.btn-icon-sm { width: 2rem;    height: 2rem;    padding: 0; }
.btn-icon-lg { width: 2.5rem;  height: 2.5rem;  padding: 0; }

/* Inline copy-id icon used in table rows next to truncated IDs. Hidden
   by default and only visible on row hover so resting rows aren't
   visually cluttered; rendered in the standard icon color (foreground)
   to match the rest of the row's iconography. Click confirmation
   (green check) is applied via inline style by showCopyConfirmation()
   and overrides these rules for its duration. */
[data-action="copy-id"] svg {
    color: var(--foreground);
    opacity: 0;
    transition: opacity 0.15s ease, color 0.15s ease;
}
/* Show on hover of the row (tables) or of the element itself (cards / any
   non-table context where the copy-id wrapper isn't inside a <tr>). */
tr:hover [data-action="copy-id"] svg,
[data-action="copy-id"]:hover svg {
    opacity: 1;
}

/* Chrome-style highlight applied by applySearchHighlight() in utils.js when
   a table search is active. Hardcoded yellow + black so the contrast is
   identical in light and dark mode (theme-token bg would invert with the
   surrounding row in dark mode and lose legibility). */
mark.search-highlight {
    background-color: #fef08a;          /* tailwind yellow-200 */
    color: #000;
    border-radius: 2px;
    padding: 0;
}

/* Responsive ID column cell with end truncation. The full ID renders in
   a single span; CSS ellipsizes from the right as the column narrows.
   At wide widths the full UUID shows; at narrow widths users see e.g.
   "c41d39fe-c4e4-…". The max-width clamp grows with the viewport so
   wider browsers reveal more, with a fixed minimum (~7rem ≈ 112px) for
   narrow viewports. */
.id-cell {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    cursor: pointer;
    max-width: clamp(7rem, 22vw, 24rem);
}

.id-cell-text {
    /* Default flex (`0 1 auto`) means: don't grow, can shrink, basis =
       content width. So the text takes its natural width up to the
       parent's max-width minus the icon, and the icon sits flush against
       the value instead of being pushed to the cell's right edge. The
       min-width: 0 lets it shrink below content width so text-overflow
       ellipsis fires when the column narrows. */
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.id-cell svg {
    flex-shrink: 0;
}


.btn-icon-tooltip {
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border-radius: var(--radius);
    border: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
    background-color: var(--background);
    color: var(--foreground);
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.btn-icon-tooltip:hover {
    background-color: color-mix(in srgb, var(--muted) 50%, transparent);
}

/* Color/intent variants */
.btn-primary {
    background-color: var(--primary);
    color: var(--primary-foreground);
}
.btn-primary:hover { background-color: color-mix(in srgb, var(--primary) 90%, transparent); }

.btn-secondary {
    background-color: var(--secondary);
    color: var(--secondary-foreground);
}
.btn-secondary:hover { background-color: color-mix(in srgb, var(--secondary) 80%, transparent); }

.btn-outline {
    background-color: var(--background);
    border-color: var(--border);
    color: var(--foreground);
}
.btn-outline:hover {
    background-color: var(--accent);
    color: var(--accent-foreground);
}

.btn-ghost {
    background-color: transparent;
    color: var(--foreground);
}
.btn-ghost:hover {
    background-color: var(--accent);
    color: var(--accent-foreground);
}

/* Link buttons use --control-accent-text, the darker text-safe variant
   of --control-accent. --control-accent itself is ~3.1:1 on white
   (passes 3:1 UI threshold for form controls but fails 4.5:1 body
   text); --control-accent-text is ~4.9:1 so link text clears WCAG AA.
   Dark mode resets to --primary via the variable. */
.btn-link {
    background-color: transparent;
    color: var(--control-accent-text);
    height: auto;
    padding: 0;
}
.btn-link:hover { text-decoration: underline; }

/* Destructive (filled) - for primary destructive actions like "Remove Access", "Delete" */
.btn-destructive {
    background-color: var(--destructive);
    color: var(--destructive-foreground);
    border-color: var(--destructive);
}
.btn-destructive:hover { opacity: 0.9; }

/* Destructive (outline) - for secondary destructive actions like "Cancel editing" */
.btn-destructive-outline {
    background-color: transparent;
    border-color: var(--destructive);
    color: var(--destructive);
}
.btn-destructive-outline:hover { opacity: 0.9; }

/* ========================================
   BASE INPUT / TEXTAREA
   Mirrors Nucleus shadcn Input/Textarea (src/components/ui/input.tsx):
     h-9 w-full rounded-md border border-input px-3 py-1 text-sm shadow-xs
     placeholder:text-muted-foreground disabled:opacity-50
     aria-invalid:border-destructive
   Tailwind utilities on an element (.h-10, .px-4, etc.) still win over these
   base element rules, so pages with explicit overrides are unaffected.
   ======================================== */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="tel"],
input[type="url"],
input[type="date"],
input[type="datetime-local"],
input[type="time"],
input[type="month"],
input[type="week"],
input:not([type]) {
    width: 100%;
    min-width: 0;
    height: 2.25rem;                 /* h-9 */
    padding: 0.25rem 0.75rem;        /* py-1 px-3 */
    font-family: inherit;
    font-size: 0.875rem;             /* text-sm */
    line-height: 1.5;
    color: var(--foreground);
    background-color: color-mix(in srgb, var(--input) 30%, transparent);
    border: 1px solid var(--input);
    border-radius: var(--radius);
    outline: none;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);     /* shadow-xs */
    transition: color var(--transition-base),
                border-color var(--transition-base),
                box-shadow var(--transition-base);
}

textarea {
    display: block;
    width: 100%;
    min-height: 4rem;                /* min-h-16 */
    padding: 0.5rem 0.75rem;         /* py-2 px-3 — taller than input per Nucleus */
    font-family: inherit;
    font-size: 0.875rem;             /* text-sm */
    line-height: 1.5;
    color: var(--foreground);
    background-color: color-mix(in srgb, var(--input) 30%, transparent);
    border: 1px solid var(--input);
    border-radius: var(--radius);
    outline: none;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    transition: color var(--transition-base),
                border-color var(--transition-base),
                box-shadow var(--transition-base);
    resize: vertical;
}

/* All input/textarea placeholders use the dimmer Nucleus tone
   (`placeholder:text-muted-foreground`, HSL 215 20.2% 65.1% ≈ slate-400). */
input::placeholder,
textarea::placeholder {
    color: var(--color-slate-400);
    opacity: 1;
}

input:disabled,
textarea:disabled {
    pointer-events: none;
    cursor: not-allowed;
    opacity: 0.5;
}

input[aria-invalid="true"],
textarea[aria-invalid="true"] {
    border-color: var(--destructive);
}
input[aria-invalid="true"]:focus,
input[aria-invalid="true"]:focus-visible,
textarea[aria-invalid="true"]:focus,
textarea[aria-invalid="true"]:focus-visible {
    border-color: var(--destructive);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--destructive) 20%, transparent);
}

/* ========================================
   INPUT GROUP
   Mirrors Nucleus shadcn InputGroup: icon + input inside a shared 36px pill.
   Use: <div class="input-group"><svg>…</svg><input …></div>
   ======================================== */
.input-group {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    height: 2.25rem;                /* h-9 */
    padding: 0 0.75rem;             /* px-3 */
    border: 1px solid var(--input);
    border-radius: var(--radius);
    background-color: color-mix(in srgb, var(--input) 30%, transparent);
    transition: border-color var(--transition-base),
                box-shadow var(--transition-base);
}

.input-group:focus-within {
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
}

.input-group > svg {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    color: var(--muted-foreground);
}

.input-group > input {
    flex: 1;
    height: 100%;
    min-width: 0;
    border: none;
    background: transparent;
    padding: 0;
    color: var(--foreground);
    font-size: 0.875rem;
}

/* Kill the standalone input's own focus ring — group handles it */
.input-group > input:focus,
.input-group > input:focus-visible {
    border: none;
    box-shadow: none;
    outline: none;
}

/* ========================================
   SELECT COMPONENT
   Custom dropdown matching Nucleus shadcn Select.
   Progressive enhancement over native <select>: select.js auto-wraps
   each <select> with this markup and hides the native. Form submission
   still works via the native element behind the scenes.
   ======================================== */
.select {
    position: relative;
    display: inline-block;
    width: 100%;
}

.select-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    height: 2.25rem;                     /* h-9 */
    padding: 0 0.75rem;                   /* px-3 */
    border: 1px solid var(--input);
    background-color: color-mix(in srgb, var(--input) 30%, transparent);
    color: var(--foreground);
    border-radius: var(--radius);
    font-family: inherit;
    font-size: 0.875rem;                  /* text-sm */
    font-weight: 400;
    line-height: 1;
    cursor: pointer;
    text-align: left;
    transition: border-color var(--transition-base),
                box-shadow var(--transition-base);
}

.select-trigger:focus-visible,
.select[data-state="open"] > .select-trigger {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
}

.select-trigger[data-placeholder="true"] > .select-value {
    color: var(--muted-foreground);
}

.select-trigger:disabled {
    pointer-events: none;
    opacity: 0.5;
}

.select-value {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.select-chevron {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    color: var(--muted-foreground);
    transition: transform var(--transition-base);
}

.select[data-state="open"] .select-chevron {
    transform: rotate(180deg);
}

/* Dropdown is portaled to <body> on open and positioned via inline styles from
   select.js so it escapes overflow:auto/hidden ancestors and doesn't push the
   page layout. Static CSS here just handles appearance. */
.select-content {
    position: fixed;
    max-height: 18rem;
    overflow-y: auto;
    padding: 0.25rem;
    background-color: var(--popover);
    color: var(--popover-foreground);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.3),
                                  0 2px 4px -1px rgba(0, 0, 0, 0.2));
    z-index: 1060;
}

.select-content[hidden] {
    display: none;
}

.select-option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.375rem 1.75rem 0.375rem 0.5rem;  /* room for check icon */
    position: relative;
    font-family: inherit;
    font-size: 0.875rem;
    color: inherit;
    background: transparent;
    border: none;
    border-radius: calc(var(--radius) - 2px);
    text-align: left;
    cursor: pointer;
    transition: background-color var(--transition-fast);
}

.select-option[data-highlighted="true"],
.select-option:hover {
    background-color: var(--accent);
    color: var(--accent-foreground);
}

.select-option[data-selected="true"]::after {
    content: "";
    position: absolute;
    right: 0.5rem;
    top: 50%;
    width: 0.875rem;
    height: 0.875rem;
    transform: translateY(-50%);
    background-color: currentColor;
    mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/contain no-repeat;
    -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>") center/contain no-repeat;
    color: var(--control-accent);
}

/* ========================================
   STANDALONE INPUT / TEXTAREA FOCUS
   Soft Nucleus halo (shadcn: ring-ring/50 ring-[3px]) for any <input>/<textarea>
   not wrapped in .input-group. Overrides the hard 1px Tailwind `focus:ring-1`
   utility some templates still apply inline.
   ======================================== */
input:focus,
input:focus-visible,
textarea:focus,
textarea:focus-visible {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
}

/* Checkable/range/file inputs don't render a box-shaped field — skip the halo. */
input[type="checkbox"]:focus,
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus,
input[type="radio"]:focus-visible,
input[type="range"]:focus,
input[type="range"]:focus-visible,
input[type="file"]:focus,
input[type="file"]:focus-visible {
    box-shadow: none;
    border-color: initial;
}

/* Nucleus parity radio — mirrors Storybook's RadioGroupItem (h-4 w-4 ring of
   var(--primary) with a 10px filled dot when checked; ring-2/ring-offset-2 on
   focus-visible; disabled at 50% opacity). Native input is restyled via
   appearance:none so we keep one DOM element per choice. */
input[type="radio"].radio {
    appearance: none;
    -webkit-appearance: none;
    width: 1rem;
    height: 1rem;
    margin: 0;
    border: 1px solid var(--control-accent);
    border-radius: 9999px;
    background-color: transparent;
    color: var(--control-accent);
    cursor: pointer;
    flex-shrink: 0;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}

input[type="radio"].radio:checked {
    /* 10px inner dot (matches h-2.5 w-2.5). 0.3125rem radius = 5px. */
    background-image: radial-gradient(circle, currentColor 0 0.3125rem, transparent 0.3125rem);
}

input[type="radio"].radio:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}

input[type="radio"].radio:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Nucleus parity checkbox — mirrors Storybook's shadcn-style Checkbox
   (h-4 w-4, rounded-sm, border-primary, bg-primary + primary-foreground
   check when checked, ring-2 ring-ring + ring-offset-2 on focus-visible,
   50% disabled). The check icon is a Lucide-shape inline SVG; the foreground
   color is hardcoded to #091722 (the value of --primary-foreground in every
   theme variant) since data URL fills can't reference CSS variables. */
input[type="checkbox"].checkbox {
    appearance: none;
    -webkit-appearance: none;
    width: 1rem;
    height: 1rem;
    margin: 0;
    border: 1px solid var(--control-accent);
    border-radius: 0.125rem;
    background-color: transparent;
    cursor: pointer;
    flex-shrink: 0;
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

/* Light-mode default: white check on the darker --control-accent
   (#06778a) so the contrast is ~5.2:1. Dark-mode override below
   switches to the original dark check on brand-cyan bg. */
input[type="checkbox"].checkbox:checked {
    background-color: var(--control-accent);
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
    background-size: 0.75rem 0.75rem;
    background-position: center;
    background-repeat: no-repeat;
}

/* Dark-mode override: brand-cyan bg pairs with dark check (the original
   --primary-foreground value #091722). The data URL stroke can't read
   from a CSS variable, so we redeclare the image here. */
:root.dark input[type="checkbox"].checkbox:checked,
.dark input[type="checkbox"].checkbox:checked {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23091722' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
}

input[type="checkbox"].checkbox:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}

input[type="checkbox"].checkbox:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Nucleus parity switch — mirrors Storybook's Switch (h-5 w-9 track,
   h-4 w-4 thumb, bg-input/off, bg-primary/on, translate-x-4 when checked,
   ring-2 ring-ring + ring-offset-2 on focus-visible, 50% disabled). The
   native checkbox is visually hidden but still drives state and submits. */
.switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    width: 2.25rem;
    height: 1.25rem;
    flex-shrink: 0;
    cursor: pointer;
    border-radius: 9999px;
    border: 2px solid transparent;
    background-color: var(--input);
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
    box-sizing: border-box;
}

.switch input[type="checkbox"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: inherit;
}

.switch:has(input:checked) {
    background-color: var(--control-accent);
}

.switch-thumb {
    pointer-events: none;
    display: block;
    width: 1rem;
    height: 1rem;
    border-radius: 9999px;
    background-color: var(--background);
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
                0 2px 4px -2px rgba(0, 0, 0, 0.1);
    transform: translateX(0);
    transition: transform 0.15s ease;
}

.switch:has(input:checked) .switch-thumb {
    transform: translateX(1rem);
}

.switch:has(input:focus-visible) {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}

.switch:has(input:disabled) {
    cursor: not-allowed;
    opacity: 0.5;
}

/* Screen-reader-only native select — stays in the DOM for form submission */
.select-native {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    pointer-events: none;
}

/* Table row hover — matches Nucleus `<tr className="hover:bg-muted/50">`.
   Translucent so it reads on any base surface (canvas or card). */
tbody tr {
    transition: background-color 0.15s ease;
}

tbody tr:hover {
    background-color: color-mix(in srgb, var(--muted) 50%, transparent);
}

/* Opt-out: the edit-user modal lists tenants with checkboxes + role selects
   inline, so the muted-row hover reads as accidental selection. */
#edit-cu-tenants-tbody tr:hover {
    background-color: transparent;
}

/* Inline-edit table rows — brighten borders so fields read against the card bg.
   Scoped to `tr[data-edit-mode="true"]` so no other inputs are affected. */
tr[data-edit-mode="true"] input,
tr[data-edit-mode="true"] textarea,
tr[data-edit-mode="true"] .select-trigger {
    border-color: var(--color-slate-700);
}

/* On focus, edit-mode fields adopt the same cyan ring + 3px/50% halo as
   standalone inputs. Needed because the rule above beats `input:focus`
   on specificity and would otherwise keep a grey border under the glow. */
tr[data-edit-mode="true"] input:focus,
tr[data-edit-mode="true"] input:focus-visible,
tr[data-edit-mode="true"] textarea:focus,
tr[data-edit-mode="true"] textarea:focus-visible {
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 50%, transparent);
    outline: none;
}

/* ========================================
   REQUIRED FIELD MARKER
   ======================================== */
.required-asterisk {
    color: var(--destructive);
}

/* ========================================
   STATUS BADGES
   Uses color-mix for transparent backgrounds
   ======================================== */

/* Mirrors Nucleus <Badge> subtle appearance: inline-flex, rounded-md,
   px-2.5 py-0.5, text-xs, font-semibold, transparent 1px border. Tone
   variants below set background and text color. */
.badge-status {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;                /* space between optional leading icon and label */
    padding: 0.125rem 0.625rem;
    border: 1px solid transparent;
    border-radius: 0.375rem;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.25;
    white-space: nowrap;
    transition: color var(--transition-base),
                background-color var(--transition-base),
                box-shadow var(--transition-base);
}

/* Focus ring (Storybook Badge: focus:ring-2 ring-ring + ring-offset-2). Useful
   when the badge is rendered as an interactive element. */
.badge-status:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}

/* Subtle-appearance tones — bumped from 10% to 18% mix so the color reads at
   roughly Tailwind 100-level saturation (which is what Storybook's subtle
   fallback uses, e.g. bg-cyan-100 text-cyan-700). */
/* Badge text colors are hardcoded per theme so they hit WCAG-AA on the
   tinted 18% backgrounds. Globals (--success, --warning) were darkened
   for light mode; --destructive and --primary stay at brand values for
   button use, so the error/info badges override text with a darker hex
   here and reset back to the brand token under dark mode. */
.badge-status-success {
    background-color: color-mix(in srgb, var(--success) 18%, transparent);
    color: var(--success);
}

.badge-status-error {
    background-color: color-mix(in srgb, var(--destructive) 18%, transparent);
    color: #b91c1c;             /* red-700, 6.1:1 on pale-red bg */
}

.badge-status-warning {
    background-color: color-mix(in srgb, var(--warning) 18%, transparent);
    color: var(--warning);
}

.badge-status-info {
    background-color: color-mix(in srgb, var(--primary) 18%, transparent);
    color: #0891b2;             /* cyan-600, 6.8:1 on pale-cyan bg */
}

/* Dark-mode resets — restore the lighter shades for legibility against
   the deep tinted backgrounds. */
:root.dark .badge-status-error,
.dark .badge-status-error { color: var(--destructive); }
:root.dark .badge-status-info,
.dark .badge-status-info { color: var(--primary); }

.badge-status-muted {
    background-color: color-mix(in srgb, var(--muted-foreground) 18%, transparent);
    color: var(--muted-foreground);
}

/* ========================================
   TOOLTIP
   Matches Nucleus <Tooltip>: rounded-md, popover bg, mono text-xs,
   shadow-md, fade-in. Rendered as a single body-level <div> positioned by
   initTooltips() (navbar.js); triggers are any element with a `title`
   attribute, intercepted so the browser's native tooltip doesn't appear.
   ======================================== */
.app-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10000;
    max-width: 20rem;
    padding: 0.375rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    background-color: color-mix(in srgb, var(--popover) 95%, transparent);
    color: var(--popover-foreground);
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 0.75rem;
    line-height: 1.25;
    box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
    pointer-events: none;
    opacity: 0;
    transform: scale(0.95);
    transition: opacity 0.12s ease-out, transform 0.12s ease-out;
    white-space: normal;
    word-wrap: break-word;
}

/* Numeric / UUID tooltip content keeps mono so digits stay column-aligned
   and easy to scan. Toggled by the JS below based on the tooltip text. */
.app-tooltip--mono {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}

.app-tooltip.is-visible {
    opacity: 1;
    transform: scale(1);
}

/* Purple accent — used for the Onboarding "Go-Live" phase. Hardcoded since
   the theme has no purple token. */
.badge-status-accent {
    background-color: color-mix(in srgb, #a855f7 12%, transparent);
    color: #a855f7;
}

/* Outline appearance — transparent fill, solid border + muted text. */
.badge-status-outline {
    background-color: transparent;
    color: var(--muted-foreground);
    border-color: var(--border);
}

/* Dashed outline — for "Coming Soon" / disabled / not-yet placeholders. */
.badge-status-dashed {
    background-color: transparent;
    color: var(--muted-foreground);
    border: 1px dashed var(--border);
}

/* Dashed purple — reserved for "Future State" speculative UI markers. */
.badge-status-future {
    background-color: transparent;
    color: #c084fc;
    border: 1px dashed #c084fc;
}

/* ========================================
   ALERT BACKGROUNDS
   Light tinted backgrounds for inline alerts
   ======================================== */

.alert-bg-error {
    background-color: color-mix(in srgb, var(--destructive) 10%, transparent);
    border: 1px solid var(--destructive);
}

.alert-bg-success {
    background-color: color-mix(in srgb, var(--success) 10%, transparent);
    border: 1px solid var(--success);
}

.alert-bg-warning {
    background-color: color-mix(in srgb, var(--warning) 10%, transparent);
    border: 1px solid var(--warning);
}

.alert-bg-info {
    background-color: color-mix(in srgb, var(--primary) 10%, transparent);
    border: 1px solid var(--primary);
}

/* Alert text colors that pair with alert backgrounds */
.alert-text-error {
    color: var(--destructive);
}

.alert-text-success {
    color: var(--success);
}

.alert-text-warning {
    color: var(--warning);
}

.alert-text-info {
    color: var(--primary);
}

/* ============================================================
   Left Navigation Sidebar — Nucleus-style
   ============================================================ */

:root {
    --sidebar-width: 16rem;
    --sidebar-width-icon: 3rem;
}

.app-layout {
    position: relative;
    display: flex;
    height: 100vh;
    overflow: hidden;
}

.app-sidebar {
    flex: 0 0 var(--sidebar-width);
    width: var(--sidebar-width);
    height: 100vh;
    display: flex;
    flex-direction: column;
    background-color: var(--sidebar-bg);
    color: var(--sidebar-foreground);
    border-right: 1px solid var(--sidebar-border);
    z-index: 30;
}

/* The width transition is opt-in — JS adds .has-transition after the initial
   collapsed-state class is applied, so the boot pass doesn't animate from
   full-width → collapsed (which read as a bounce on reload). */
.app-sidebar.has-transition {
    transition: flex-basis 0.2s ease, width 0.2s ease;
}

.app-sidebar.is-collapsed,
html.sidebar-collapsed .app-sidebar {
    flex: 0 0 var(--sidebar-width-icon);
    width: var(--sidebar-width-icon);
}

/* Collapsed state: show only icons, hide labels */
.app-sidebar.is-collapsed .sidebar-logo,
.app-sidebar.is-collapsed .sidebar-menu-button > span,
.app-sidebar.is-collapsed #theme-toggle-label {
    display: none;
}

.app-sidebar.is-collapsed .sidebar-brand-wordmark {
    display: none;
}

.app-sidebar:not(.is-collapsed) .sidebar-brand-badge,
.app-sidebar:not(.is-collapsed) .sidebar-brand-expand-icon {
    display: none;
}

/* Collapsed sidebar: the "T" badge is the default, but on hover/focus of
   the brand link it swaps for an expand-sidebar icon. Click expands the
   nav (handled inline on the brand <a>). */
.sidebar-brand-expand-icon {
    display: none;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    color: var(--foreground);
}

.sidebar-brand-expand-icon svg {
    width: 1rem;
    height: 1rem;
}

/* Trigger the swap on any hover within the collapsed sidebar, not just on
   the brand link — the user shouldn't have to aim for the "T" to discover
   the toggle. Keyboard focus on the brand still also triggers it. */
.app-sidebar.is-collapsed:hover .sidebar-brand-badge,
.app-sidebar.is-collapsed .sidebar-brand:focus-visible .sidebar-brand-badge {
    display: none;
}

.app-sidebar.is-collapsed:hover .sidebar-brand-expand-icon,
.app-sidebar.is-collapsed .sidebar-brand:focus-visible .sidebar-brand-expand-icon {
    display: inline-flex;
}

.app-sidebar.is-collapsed .sidebar-brand {
    padding: 0;
    justify-content: center;
    /* Square click target matching the "T" badge — gives the expand icon
       the same 32×32 hit area the badge has and a clear hover affordance. */
    width: 2rem;
    height: 2rem;
    border-radius: 9999px;
    transition: background-color 0.15s ease;
}

.app-sidebar.is-collapsed .sidebar-brand:hover {
    background-color: color-mix(in srgb, var(--accent) 60%, transparent);
}

/* Sidebar trigger is hidden when collapsed (rail handles toggling), so the
   brand is the only header child — recenter it instead of inheriting the
   expanded-state space-between layout that leaves it pinned to the left. */
.app-sidebar.is-collapsed .sidebar-header {
    justify-content: center;
}

.app-sidebar.is-collapsed .sidebar-menu-button {
    justify-content: center;
    padding: 0.5rem;
    gap: 0;
}

.sidebar-brand-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 9999px;
    background-color: var(--accent);
    color: var(--primary);
    font-weight: 700;
    font-size: 1rem;
    line-height: 1;
}

/* Click-to-toggle rail; straddles the sidebar's right edge.
   Custom cursor mimics macOS "move left / move right" — hints collapse direction. */
.sidebar-rail {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1rem;
    left: calc(var(--sidebar-width) - 0.5rem);
    padding: 0;
    margin: 0;
    background: transparent;
    border: none;
    z-index: 40;
    cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'><g fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M15 5 v10 M15 10 H5 M8 7 L5 10 L8 13' stroke='white' stroke-width='3'/><path d='M15 5 v10 M15 10 H5 M8 7 L5 10 L8 13' stroke='black' stroke-width='1.5'/></g></svg>") 10 10, w-resize;
}

/* Rail's left-position transition is opt-in like the sidebar width — JS adds
   .has-transition to the sibling sidebar after boot, so the boot pass doesn't
   slide a vertical line across the screen on a saved-collapsed reload. */
.app-sidebar.has-transition + .sidebar-rail {
    transition: left 0.2s ease;
}

.sidebar-rail::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 2px;
    transform: translateX(-50%);
    background-color: var(--border);
    transition: background-color 0.15s ease;
}

.sidebar-rail:hover::after,
.sidebar-rail:focus-visible::after {
    background-color: var(--muted-foreground);
}

.app-layout:has(.app-sidebar.is-collapsed) .sidebar-rail,
html.sidebar-collapsed .sidebar-rail {
    left: calc(var(--sidebar-width-icon) - 0.5rem);
    cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'><g fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M5 5 v10 M5 10 H15 M12 7 L15 10 L12 13' stroke='white' stroke-width='3'/><path d='M5 5 v10 M5 10 H15 M12 7 L15 10 L12 13' stroke='black' stroke-width='1.5'/></g></svg>") 10 10, e-resize;
}

.sidebar-header {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.5rem;
    /* Pinned header height = expanded brand (2.875rem) + vertical padding
       (1rem total). Keeps the menu below at the same vertical position
       when the sidebar collapses, where the brand shrinks to a 2rem badge
       and would otherwise pull the rest of the nav up by ~14px. */
    min-height: 3.875rem;
}

/* Hide the toggle when the sidebar is collapsed — the rail still toggles
   visually, and the badge centers on its own. */
.app-sidebar.is-collapsed .sidebar-trigger {
    display: none;
}

.sidebar-brand {
    display: flex;
    align-items: center;
    height: 2.875rem;
    padding: 0 0.5rem;
    text-decoration: none;
}

.sidebar-brand:hover,
.sidebar-brand:focus,
.sidebar-brand:focus-visible {
    text-decoration: none;
}

.sidebar-logo {
    height: 1.27rem;
    width: auto rem;
    display: block;
}

/* One-shot shimmer when the sidebar expands from collapsed. JS adds
   .is-shimmering to the wordmark; the ::after sweeps a soft white band
   left → right, clipped to the logo silhouette via a mask so the
   surrounding sidebar background is never tinted. Mask is permanent
   (same SVG as the underlying <img>, so resting state is identical) —
   adding/removing it on animationend caused a subpixel shift. */
.sidebar-brand-wordmark {
    position: relative;
    overflow: hidden;
    -webkit-mask-image: url("/static/images/tenex-logo-dark-blue.svg");
    mask-image: url("/static/images/tenex-logo-dark-blue.svg");
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
}

@keyframes sidebar-logo-shimmer {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

.sidebar-brand-wordmark.is-shimmering::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0) 0%,
        rgba(255, 255, 255, 0.65) 50%,
        rgba(255, 255, 255, 0) 100%
    );
    transform: translateX(-100%);
    animation: sidebar-logo-shimmer 1.1s ease-in-out forwards;
}

.sidebar-content {
    flex: 1 1 auto;
    overflow-y: auto;
    padding: 0.5rem;
}

.sidebar-menu {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.sidebar-menu-button {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    height: 2.25rem;
    padding: 0.5rem 0.625rem;
    background: transparent;
    border: none;
    border-radius: 0.375rem;
    color: var(--muted-foreground);
    font-size: 0.875rem;
    line-height: 1.25rem;
    font-weight: 400;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.sidebar-menu-button > svg {
    flex-shrink: 0;
    width: 1rem;
    height: 1rem;
}

.sidebar-menu-button > span {
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar-menu-button:hover {
    background-color: var(--sidebar-accent);
    color: var(--sidebar-accent-foreground);
}

.sidebar-menu-button.is-active {
    background-color: var(--sidebar-accent);
    color: var(--sidebar-accent-foreground);
    /* Visually bolder without the +100 weight reflow that shifts text width
       when the active state changes. A 0.4px text-shadow stroke approximates
       the extra weight while keeping glyph metrics identical. */
    text-shadow: 0 0 0.4px currentColor;
}

.sidebar-footer {
    padding: 0.5rem;
    border-top: 1px solid var(--sidebar-border);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.app-main {
    flex: 1 1 auto;
    min-width: 0;
    height: 100vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    background-color: var(--background);
}

/* ============================================================
   Scrollbars — thin, muted, hover-reveal (Nucleus parity).
   Transparent track + 20% border-color thumb by default; thumb
   rises to 80% on hover so the scrollbar only reads as a subtle
   accent against the page.
   ============================================================ */

/* WebKit (Chrome, Safari, Edge) */
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    opacity: 0;
    transition: opacity 0.3s ease-out;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background: color-mix(in oklab, var(--border) 20%, transparent);
    border-radius: 9999px;
    transition: background 0.3s ease-out;
}

*:hover::-webkit-scrollbar-thumb {
    background: color-mix(in oklab, var(--border) 80%, transparent);
}

*:hover::-webkit-scrollbar {
    opacity: 1;
}

/* Firefox — keep a faint thumb by default so scrollability is discoverable,
   then deepen on hover (matches the ~20% / 80% webkit opacities above). */
* {
    scrollbar-width: thin;
    scrollbar-color: color-mix(in oklab, var(--border) 20%, transparent) transparent;
}

*:hover {
    scrollbar-color: color-mix(in oklab, var(--border) 80%, transparent) transparent;
}

/* Default: simple scroll container for pages without a page-header
   (forms, details views, error pages). */
.app-main-inner {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    overscroll-behavior: contain;
    padding: 1.5rem;
}

/* Pages that render a page-header (directly or via a tab-content child) hand
   scroll off to `.app-page-scroll`, so the page-header can sit outside the
   scroll region and the scrollbar only runs alongside the content. */
.app-main-inner:has(> .page-header),
.app-main-inner:has(> .app-tab-content) {
    display: flex;
    flex-direction: column;
    overflow: hidden;
    padding: 0;
}

.app-page-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    overscroll-behavior: contain;
    /* No top padding — lets a sticky thead (or any first-child) sit flush
       against the page-header's bottom border. Horizontal + bottom gutters
       match the old .app-main-inner padding. */
    padding: 0 1.5rem 1.5rem;
}

/* Dashboard tabs — each tab-content is a flex column when active so its own
   page-header + .app-page-scroll pair lays out correctly. */
.app-tab-content {
    flex: 1 1 auto;
    min-height: 0;
    flex-direction: column;
}

/* Nucleus parity sub-tabs — horizontal underline tab bar used on the Case
   page (Intelligence Hub | Investigate | Alerts | …). Inactive labels sit
   in muted-foreground; the active tab gets foreground color + a 2px primary
   underline that spans only the label's width. The strip itself has a 1px
   border-bottom that the active underline overlaps. */
/* Storybook Tabs (underline variant) parity. Triggers carry their own
   px-4 horizontal padding — the active 2px primary underline spans that
   padded width naturally, giving the "extends past text" look without a
   pseudo-element overshoot. List uses gap-2 (8px) between triggers. */
.app-subtabs {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    padding: 0 1.5rem;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

/* Sticky variant — pins the subtab strip to the top of its scroll
   container (.app-page-scroll). Used on detail pages like Tenant Settings
   where the page header is already pinned outside the scroll area, so
   the next-most-important nav (the subtabs) should stay visible too.
   The bottom divider uses an inset box-shadow instead of the base
   border-bottom because sticky elements don't reliably paint a 1px
   border at their stuck edge in every browser — the shadow always
   renders on top of the painted background.

   Negative horizontal margins bleed past the .app-page-scroll's 1.5rem
   horizontal padding so the bg + bottom-divider extend to the full
   edges of the scroll container at every viewport width (page-max
   constrains the content below; the strip itself spans full width). */
.app-subtabs.is-sticky {
    position: sticky;
    top: 0;
    z-index: 20;
    background-color: var(--background);
    border-bottom: none;
    box-shadow: inset 0 -1px 0 var(--border);
    margin-left: -1.5rem;
    margin-right: -1.5rem;
    transition: box-shadow 0.15s ease-out;
}

/* On tabbed detail pages the page-header doesn't carry the scroll-lift
   shadow because the tabs sit between it and the content. When the user
   scrolls (the parent .app-page-scroll picks up .is-scrolled), the
   shadow goes here — on the tabs strip, the bottom-most sticky chrome
   — so the same depth cue happens but at the right position. */
.app-page-scroll.is-scrolled > .app-subtabs.is-sticky {
    box-shadow: inset 0 -1px 0 var(--border), 0 2px 4px rgba(0, 0, 0, 0.18);
}

/* Breathing room between sticky subtabs and the first content row.
   With subtabs pulled outside .app-page-max, the next sibling at the
   same scroll-level is the .app-page-max wrapper itself — top-pad it
   so each pane it contains gets the same breathing room. */
.app-subtabs.is-sticky + .app-page-max {
    padding-top: 1.5rem;
}

.app-subtab {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem 0.75rem;
    background: transparent;
    border: none;
    font-size: 0.875rem;
    font-weight: 500;
    /* Inactive tabs read as a softer gray than the bright var(--muted-foreground)
       slate-300 default — closer to Nucleus's Case-page inactive trigger color. */
    color: color-mix(in srgb, var(--muted-foreground) 60%, transparent);
    cursor: pointer;
    transition: color 0.15s ease;
}

.app-subtab:hover {
    color: var(--foreground);
}

.app-subtab.is-active {
    color: var(--foreground);
}

.app-subtab.is-active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background-color: var(--primary);
}

.app-subtab-pane {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.app-subtab-pane[hidden] {
    display: none !important;
}

/* ============================================================
   TABS — default (segmented pill) variant.
   Mirrors Nucleus shadcn Tabs (src/components/ui/tabs.tsx):
     TabsList:    h-9 inline-flex items-center justify-center
                  rounded-lg bg-muted p-1 text-muted-foreground
     TabsTrigger: text-sm font-medium rounded-md px-3 py-1
       [data-state=active]: bg-background text-foreground shadow-sm
   Usage:
     <div class="tabs-list">
       <button class="tabs-trigger" data-state="active">All</button>
       <button class="tabs-trigger" data-state="inactive">Set</button>
     </div>
   JS toggles each trigger's data-state attribute.
   ============================================================ */
.tabs-list {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 2.25rem;                        /* h-9 */
    padding: 0.25rem;                       /* p-1 */
    background-color: var(--muted);
    color: var(--muted-foreground);
    border-radius: var(--radius);
    gap: 0.125rem;
}

.tabs-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    height: 100%;
    padding: 0 0.75rem;                     /* px-3 */
    font-size: 0.875rem;                    /* text-sm */
    font-weight: var(--font-weight-medium);
    color: inherit;
    background-color: transparent;
    border: none;
    border-radius: calc(var(--radius) - 0.125rem);
    cursor: pointer;
    transition: background-color var(--transition-base),
                color var(--transition-base),
                box-shadow var(--transition-base);
}

.tabs-trigger:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}

.tabs-trigger[data-state="active"] {
    background-color: var(--background);
    color: var(--foreground);
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);     /* shadow-sm */
}

.tabs-trigger:disabled {
    pointer-events: none;
    opacity: 0.5;
}

/* Inner page-headers (rendered inside a subtab pane) repeat the breadcrumb
   row that the outer page already shows — hide the duplicate. The pane's
   own toolbar (search + primary action) still renders below. */
.app-subtab-pane .page-header-top {
    display: none;
}

/* Right-side action group on the subtab strip — pushes itself to the far end
   via margin-left:auto so the tabs stay flush-left with their natural gap. */
.app-subtabs-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    /* Vertical padding so the inputs/buttons don't crowd the strip's
       border-bottom. */
    padding: 0.375rem 0;
}

.app-subtabs-actions[hidden] {
    display: none !important;
}

/* Table wrappers break out of `.app-main-inner` padding to sit edge-to-edge,
   matching Nucleus. Non-card list pages only — card-wrapped pages keep the gutter. */
.table-bleed {
    margin-left: -1.5rem;
    margin-right: -1.5rem;
}

/* In-card variant of the main-page table: same muted sticky header, same
   first/last-cell gutter (1.5rem to align with the card's p-6 title block),
   but no negative-margin bleed since the card already provides the chrome.
   Use on tables that sit edge-to-edge inside a bordered card section. */
.card-table thead th {
    height: 2rem;
    padding: 0 0.5rem;
    vertical-align: middle;
    color: var(--muted-foreground);
    white-space: nowrap;
    background-color: color-mix(in srgb, var(--muted) 50%, var(--background));
    box-shadow: inset 0 -1px 0 var(--border);
    font-weight: var(--font-weight-medium);
    font-size: 0.75rem;            /* text-xs */
}

.card-table th:first-child,
.card-table td:first-child {
    padding-left: 1.5rem;
}

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

/* Align header horizontal padding with cell padding so column titles sit
   directly above their values. Templates use `py-3 px-3` on <th> and
   `p-2` on <td> — overriding the th horizontal to 0.5rem matches td while
   preserving the taller vertical header padding. The :first-child and
   :last-child gutter rules below have higher specificity, so the edge
   columns keep their 1.5rem flush-with-the-page-header padding. */
.table-bleed thead th {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}

/* First/last cells get an extra gutter so cell text aligns with the page
   header's 1.5rem horizontal padding, while row backgrounds still bleed. */
.table-bleed th:first-child,
.table-bleed td:first-child {
    padding-left: 1.5rem;
}

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

/* Match Nucleus `<th className="h-10 ... align-middle">` — fixed header row
   height, vertical padding replaced by line-centering. Sticky to the top of
   `.app-page-scroll` so the header row stays pinned while the body scrolls. */
.table-bleed thead th {
    height: 2.5rem;
    padding-top: 0;
    padding-bottom: 0;
    vertical-align: middle;
    color: var(--muted-foreground);
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: color-mix(in srgb, var(--muted) 50%, var(--background));
    box-shadow: inset 0 -1px 0 var(--border);
}

/* Scroll-lift: when the scroll container has scrolled off the top, a soft
   gradient fades in below the sticky thead, making the body feel like it's
   sliding under the header.

   A real <div> injected by initScrollShadows() (pseudo-elements rendered
   inconsistently across tabs). Sticky at 2.5rem — the thead height — so
   it bounces with the thead during rubber-band overscroll. JS sets
   width = .app-page-scroll.scrollWidth so a wide horizontally-scrolling
   table keeps the shadow covering the full viewport at any horizontal
   scroll position; negative horizontal margins bleed past the padding. */
.app-page-scroll-shadow {
    position: sticky;
    /* calc(… - 1px) closes the 1px gap caused by the thead's inset bottom border. */
    top: calc(2.5rem - 1px);
    height: 8px;
    /* Negative horizontal margins bleed past .app-page-scroll's padding; JS
       sets an explicit width to scrollWidth so the shadow spans the full
       scrollable content. margin-bottom keeps it out of the flow so table
       rows don't get pushed down. */
    margin: 0 -1.5rem -8px;
    background: linear-gradient(
        to bottom,
        rgba(0, 0, 0, 0.15),
        rgba(0, 0, 0, 0)
    );
    z-index: 9;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.15s ease-out;
}

.app-page-scroll.is-scrolled .app-page-scroll-shadow {
    opacity: 1;
}

/* Sticky th takes each cell out of the row's paint flow, so the sortable
   hover needs an opaque base — otherwise tbody rows show through. */
.table-bleed thead .sortable-th:hover {
    background-color: color-mix(
        in srgb,
        var(--foreground) 6%,
        color-mix(in srgb, var(--muted) 50%, var(--background))
    );
}

/* Suppress transitions during theme switch so hover background-color
   values (which reference CSS variables like --foreground) don't animate
   from the old theme's computed color to the new one. */
.theme-switching,
.theme-switching *,
.theme-switching *::before,
.theme-switching *::after {
    transition: none !important;
}

/* Universal boot-time motion gate. The inline head script in base.html
   sets `html.is-booting` before the first paint and removes it after two
   rAFs (or 200ms fallback). Any class flips that happen during this
   window — theme apply, sidebar collapse from saved state, etc. — snap
   instead of animating. One rule covers the whole tree so the sidebar
   width, the rail's left position, page-header borders, badge backgrounds
   and anything else can't slide on reload. */
.is-booting,
.is-booting *,
.is-booting *::before,
.is-booting *::after {
    transition: none !important;
    animation: none !important;
}

/* Sortable column headers — click to sort, hover for affordance. */
.sortable-th {
    cursor: pointer;
    user-select: none;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.sortable-th:hover {
    background-color: color-mix(in srgb, var(--foreground) 6%, transparent);
    color: var(--foreground);
}

.sortable-th-inner {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
}

.sort-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1rem;
    height: 1rem;
    color: inherit;
    flex-shrink: 0;
}

.sort-icon svg {
    width: 100%;
    height: 100%;
}

/* ============================================================
   Page Header — breadcrumb + title block. Sits outside the
   scroll region (see .app-page-scroll), so no sticky is needed.
   ============================================================ */

.page-header {
    flex: 0 0 auto;
    background-color: var(--background);
    border-bottom: 1px solid var(--border);
}

/* On detail pages with sticky subtabs, BOTH the page header and the
   tabs strip carry a divider — the page-header line separates the
   header chrome from the tab strip, and the tabs' own line separates
   the tabs from the scrolling content. The base .page-header rule
   already provides the header's border-bottom, so no override needed
   here. */

/* Tabless detail-page variant — the page-header takes the "scroll lift"
   role normally played by .app-page-scroll-shadow on table pages.
   Resting state shows only the 1px divider; once the user scrolls (the
   adjacent .app-page-scroll picks up .is-scrolled from initScrollShadows)
   a soft drop-shadow fades in to communicate that content has slid
   under the header.
   Position: relative + z-index keeps the shadow above the next sibling
   (.app-page-scroll) which would otherwise paint over it. */
.app-main-inner:not(:has(.app-subtabs.is-sticky)) > .page-header {
    position: relative;
    z-index: 10;
    transition: box-shadow 0.15s ease-out;
}

.app-main-inner:not(:has(.app-subtabs.is-sticky)):has(> .app-page-scroll.is-scrolled) > .page-header {
    border-bottom: none;
    box-shadow: 0 1px 0 var(--border), 0 2px 4px rgba(0, 0, 0, 0.18);
}

/* Suppress the table-page scroll-shadow on detail pages — the page-header
   already provides the lift effect, so the second sticky shadow inside
   the scroll container would just stack a duplicate band below the
   header. .app-page-scroll-shadow stays in use on list pages without
   a page-header where the sticky <thead> needs its own lift cue. */
.app-main-inner:has(> .page-header) .app-page-scroll-shadow {
    display: none;
}

.page-header-top {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1.5rem;
    border-bottom: 1px solid var(--border);
    min-height: 2.75rem;
}

.page-header:has(.page-header-main) .page-header-top {
    border-bottom: 1px solid var(--border);
}

.page-header:not(:has(.page-header-main)) .page-header-top {
    border-bottom: none;
}

.sidebar-trigger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    flex-shrink: 0;
    background: transparent;
    border: none;
    border-radius: 0.375rem;
    color: var(--foreground);
    cursor: pointer;
    transition: background-color 0.15s ease;
}

.sidebar-trigger:hover {
    background-color: var(--accent);
}

.sidebar-trigger svg {
    width: 1rem;
    height: 1rem;
}

.page-breadcrumb ol {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    color: var(--foreground);
}

/* Slash separator between breadcrumb segments. Inserted as a pseudo-element
   on every <li> after the first so callers don't need to render dividers. */
.page-breadcrumb ol li + li::before {
    content: '/';
    margin-right: 0.5rem;
    color: var(--muted-foreground);
}

/* Non-current segments are dimmer so the active page reads as the focal one. */
.page-breadcrumb ol li:not([aria-current="page"]) {
    color: var(--muted-foreground);
}

.page-header-main {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.25rem 1.5rem;
    /* Pinned to the Customer Administration height so list pages with a
       bare action (Tenant Management's search/Add) match pages whose
       actions wrap in .app-subtabs-actions (which adds 0.375rem y-padding).
       Math: 1.25rem padding ×2 + h-9 (2.25rem) action + 0.375rem padding ×2
       wrapper = 5.5rem. flex-wrap: nowrap prevents wrapping on narrow
       viewports; pages with a description grow naturally above this. */
    min-height: 5.5rem;
}

.page-header-heading {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-width: 0;
    flex: 1 1 auto;
}

.page-header-title {
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.025em;
    color: var(--foreground);
    margin: 0;
}

.page-header-description {
    font-size: 0.875rem;
    color: var(--muted-foreground);
    margin: 0;
}

.page-header-actions {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: flex-end;
    gap: 1rem;
    min-width: 0;
}

/* ============================================================
   Date & Time Pickers — Nucleus-style popovers
   ============================================================ */

.dt-date-picker,
.dt-time-picker {
    position: relative;
    display: block;
}

.dt-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    height: 2.25rem;
    padding: 0 0.75rem;
    background-color: color-mix(in srgb, var(--input) 30%, transparent);
    border: 1px solid var(--input);
    border-radius: var(--radius);
    color: var(--foreground);
    font-size: 0.875rem;
    line-height: 1.25rem;
    cursor: pointer;
    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.dt-trigger:hover {
    border-color: color-mix(in srgb, var(--foreground) 20%, var(--border));
}

.dt-trigger:focus-visible {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 25%, transparent);
}

.dt-trigger-value-placeholder {
    color: var(--muted-foreground);
}

.dt-trigger-icon {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    color: var(--muted-foreground);
}

.dt-popover {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    z-index: 50;
    background-color: var(--popover);
    color: var(--popover-foreground);
    border: 1px solid var(--border);
    border-radius: 0.5rem;
    box-shadow: 0 10px 24px -8px rgba(0, 0, 0, 0.25), 0 4px 8px -4px rgba(0, 0, 0, 0.1);
}

/* --- Calendar --- */

.dt-calendar {
    padding: 0.75rem;
    /* 7 cells × h-9 (2.25rem) + 0.75rem horizontal padding × 2. */
    width: calc(2.25rem * 7 + 1.5rem);
}

.dt-cal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.dt-cal-title {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--foreground);
}

.dt-cal-nav {
    display: flex;
    gap: 0.25rem;
}

.dt-cal-nav-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 0.375rem;
    color: var(--muted-foreground);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.dt-cal-nav-btn:hover {
    background-color: var(--accent);
    color: var(--foreground);
}

.dt-cal-nav-btn svg {
    width: 1rem;
    height: 1rem;
}

.dt-cal-weekdays,
.dt-cal-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 0;
}

.dt-cal-weekday {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 2.25rem;
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--muted-foreground);
}

.dt-cal-day {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: 0.375rem;
    color: var(--foreground);
    font-size: 0.875rem;
    font-weight: 400;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.dt-cal-day:hover:not(:disabled) {
    background-color: var(--accent);
}

.dt-cal-day-outside {
    color: var(--muted-foreground);
    opacity: 0.5;
    cursor: default;
}

.dt-cal-day-today {
    background-color: var(--accent);
    color: var(--accent-foreground);
}

.dt-cal-day-selected,
.dt-cal-day-selected:hover {
    background-color: var(--primary);
    color: var(--primary-foreground);
    font-weight: 500;
}

.dt-cal-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-top: 0.5rem;
    margin-top: 0.25rem;
    border-top: 1px solid var(--border);
}

.dt-cal-link {
    background: transparent;
    border: none;
    padding: 0.25rem 0.25rem;
    color: var(--primary);
    font-size: 0.875rem;
    cursor: pointer;
    border-radius: 0.25rem;
}

.dt-cal-link:hover {
    text-decoration: underline;
}

/* --- Time Picker (12-hour, inline segments) --- */

.dt-time-picker {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    position: relative;
}

.dt-time-icon {
    width: 1rem;
    height: 1rem;
    color: var(--muted-foreground);
    flex-shrink: 0;
    margin-right: 0.125rem;
}

.dt-time-seg {
    width: auto;
    min-width: 2.5rem;
    height: 2.25rem;
    padding: 0 0.5rem;
    background-color: var(--background);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    color: var(--foreground);
    font-size: 0.875rem;
    font-weight: 500;
    text-align: left;
    font-variant-numeric: tabular-nums;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.dt-time-seg::placeholder {
    color: var(--muted-foreground);
    font-weight: 400;
}

.dt-time-seg:focus {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 25%, transparent);
}

.dt-time-colon {
    color: var(--muted-foreground);
    font-weight: 500;
    user-select: none;
}

.dt-time-ap {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    height: 2.25rem;
    padding: 0 0.5rem 0 0.625rem;
    background-color: var(--background);
    border: 1px solid var(--border);
    border-radius: 0.375rem;
    color: var(--foreground);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.dt-time-ap:hover {
    border-color: color-mix(in srgb, var(--foreground) 20%, var(--border));
}

.dt-time-ap:focus-visible {
    outline: none;
    border-color: var(--ring);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ring) 25%, transparent);
}

.dt-time-ap svg {
    width: 0.875rem;
    height: 0.875rem;
    color: var(--muted-foreground);
}

