/* 070 §6.9 — per-tenant fonts. The curated library: each --font-<key> is a SELF-HOSTED/system stack (no CDN).
   BrandingService.FONTS validates the key, and tenant_branding.jspf emits --brand-font-body/-heading:var(--font-<key>).
   To add a brand webfont: vendor its WOFF2, add an @font-face (e.g. in css/lato.css), add a --font-<key> here, and
   add the key to BrandingService.FONTS. Defaults preserve today's exact Lato body font. */
:root {
    --font-system-sans:  system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
    --font-system-serif: Georgia, Cambria, 'Times New Roman', Times, serif;
    --font-system-mono:  ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
}

body {
    font-family: var(--brand-font-body, 'Lato', sans-serif);
}
/* Headings follow the heading font when set, else the body font, else Lato (today's behaviour). */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--brand-font-heading, var(--brand-font-body, 'Lato', sans-serif));
}

.base-loader {
    border: 4px solid #f3f3f3;
    border-radius: 50%;
    animation: spin 1s linear infinite;
    display: none;
}

.loader {
    width: 24px;
    height: 24px;
    border-top-color: #3498db;
}

.gemini-loader,
.availability-loader {
    width: 20px;
    height: 20px;
    display: none;
    vertical-align: middle;
    margin-left: 8px;
}

.gemini-loader {
    border-top-color: #f59e0b;
}

.availability-loader {
    border-top-color: #22c55e;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 0.5rem;
}

th,
td {
    border: 1px solid #e2e8f0;
    padding: 0.75rem;
    font-size: 0.875rem;
    word-break: break-word;
}

/*
 * Default cell alignment lives in `@layer base` — NOT as a bare unlayered rule.
 * Same cascade-layers trap documented for `th{background-color}` below: an
 * unlayered `th,td{text-align:left}` beats Tailwind's `.text-right` / `.text-left`
 * / `.text-center` (which live in `@layer utilities`), so EVERY right-aligned
 * figure in every table silently rendered left. Tailwind declares `base` before
 * `utilities`, and this file loads after tailwind.built.css, so this joins the
 * base layer and correctly LOSES to the alignment utilities while still giving
 * unstyled (utility-less) legacy cells a sensible left default.
 */
@layer base {
    th,
    td {
        text-align: left;
    }
}

/*
 * `th { background-color: #f7fafc }` was previously here as a default header
 * tint, but it was an UNLAYERED bare-element selector — which under CSS
 * Cascade Layers (Tailwind v4 puts utilities in `@layer utilities`) beats
 * every utility class. That made `bg-red-600` / `bg-gray-50` toggled by the
 * dynamic validate*HeaderValidation functions silently no-op, leaving every
 * required header stuck on #f7fafc regardless of fill state. The thead
 * element itself already carries `bg-gray-50` for the row background, so
 * the per-TH default tint is redundant. Keeping only the font-weight.
 */
th {
    font-weight: 600;
}

.status-kids-text {
    color: #d97706;
}

.status-age-gated-text {
    color: #dc2626;
}

.status-open-text {
    color: #16a34a;
}

.status-unknown-text {
    color: #71717a;
}

/* Table Container & Sticky Header Logic */
.table-container {
    max-height: 600px;
    overflow-y: auto;
    overflow-x: auto;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    margin-bottom: 1.5rem;
}

#bulkVideosTable th,
#bulkVideosTableOut th,
#bulkTagsTable th,
#bulkLinksTable th,
#bulkPlaylistsTable th,
#bulkOtherTable th,
#bulkShortsTable th,
#bulkLiveTable th {
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: #f3f4f6;
    border-bottom: 2px solid #d1d5db;
    white-space: nowrap;
}

#bulkVideosTable td,
#bulkVideosTableOut td,
#bulkTagsTable td,
#bulkLinksTable td,
#bulkPlaylistsTable td,
#bulkShortsTable td,
#bulkLiveTable td {
    white-space: nowrap;
}

#bulkVideosTable td,
#bulkVideosTable th,
#bulkVideosTableOut td,
#bulkVideosTableOut th,
#bulkTagsTable td,
#bulkTagsTable th,
#bulkLinksTable td,
#bulkLinksTable th,
#bulkPlaylistsTable td,
#bulkPlaylistsTable th,
#bulkOtherTable th,
#bulkOtherTable td,
#bulkShortsTable td,
#bulkShortsTable th,
#bulkLiveTable td,
#bulkLiveTable th {
    min-width: 50px;
    padding-left: 1rem;
    padding-right: 1rem;
}

/* ── Thumbnail column: identical 170px width across Videos, Shorts, and Live ── */
#bulkVideosTable td[data-error-col="thumb"],
#bulkShortsTable td[data-error-col="thumb"],
#bulkLiveTable   td[data-error-col="thumb"],
#bulkVideosTable th[data-col="thumb"],
#bulkShortsTable th[data-col="thumb"],
#bulkLiveTable   th[data-col="thumb"] {
    width: 170px !important;
    min-width: 170px !important;
    max-width: 170px !important;
    padding-left: 0.5rem !important;
    padding-right: 0.5rem !important;
}

/* Ensure thumb wrapper div and image fill the cell consistently */
#bulkVideosTable td[data-error-col="thumb"] > div,
#bulkShortsTable td[data-error-col="thumb"] > div,
#bulkLiveTable   td[data-error-col="thumb"] > div {
    width: 160px !important;
    height: 90px !important;
    min-width: 160px !important;
    max-width: 160px !important;
    flex-shrink: 0 !important;
    overflow: hidden !important;
    border-radius: 0.75rem !important;
}

#bulkVideosTable td[data-error-col="thumb"] img,
#bulkShortsTable td[data-error-col="thumb"] img,
#bulkLiveTable   td[data-error-col="thumb"] img {
    width: 100% !important;
    height: 100% !important;
    object-fit: cover !important;
    display: block !important;
}

.summary-section {
    margin-bottom: 1.5rem;
}

.status-group-title {
    font-size: 1.125rem;
    font-weight: 600;
    color: #374151;
    margin-top: 1rem;
    margin-bottom: 0.5rem;
    padding-bottom: 0.25rem;
    border-bottom: 2px solid #e5e7eb;
}

.empty-group-message {
    padding: 1rem;
    text-align: center;
    color: #6b7280;
    background-color: #f9fafb;
    border: 1px dashed #d1d5db;
    border-radius: 0.5rem;
    margin-top: 0.5rem;
}

.error-mapping-active td[data-error-severity="1"] {
    background-color: #ffedd5 !important;
}

.error-mapping-active td[data-error-severity="2"] {
    background-color: #fee2e2 !important;
}

.rating-incompatible,
.rating-mismatch {
    background-color: #dc2626;
    color: white !important;
    font-weight: bold;
    padding: 0.25rem 0.75rem;
    border-radius: 0.375rem;
    display: inline-block;
    margin-top: 0.25rem;
}

.gemini-feature-section {
    margin-top: 0;
    padding: 1rem;
    border-radius: 0.5rem;
    background-color: #eef2ff;
    border: 1px solid #c7d2fe;
}

.gemini-feature-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--brand-primary, #1c5fa5);
    margin-bottom: 0.5rem;
}

.gemini-suggestions-list {
    list-style-type: disc;
    list-style-position: inside;
    padding-left: 0.5rem;
    font-size: 0.875rem;
    color: var(--brand-primary, #1c5fa5);
}

.gemini-suggestions-list li {
    margin-bottom: 0.25rem;
}

.suggested-rating-value {
    font-weight: bold;
    display: block;
    margin-bottom: 0.25rem;
}

.suggested-rating-reasoning {
    font-size: 0.875rem;
    color: #4b5563;
}

#apiQuotaSection {
    margin-top: 2rem;
    padding: 1rem;
    background-color: #f3f4f6;
    border-radius: 0.5rem;
    font-size: 0.75rem;
    color: #4b5563;
}

#apiQuotaSection h3 {
    font-weight: 600;
    color: #374151;
    margin-bottom: 0.5rem;
}

#originalTitleDisplay {
    margin-bottom: 0.75rem;
    font-size: 1rem;
    font-weight: 600;
    color: #4338ca;
}

/* Thumbnail CSS matching YouTube's 12px / rounded-xl */
#originalThumbnailDisplay img,
#generatedThumbnailDisplay img {
    max-width: 320px;
    height: auto;
    border-radius: 12px;
    display: block;
    margin-left: auto;
    margin-right: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
}

#originalThumbnailDisplay img {
    margin-bottom: 0.75rem;
    border: 1px solid #ddd;
}

#generatedThumbnailDisplay img {
    margin-top: 0.5rem;
    border: 1px solid #a5b4fc;
    cursor: pointer;
    transition: opacity 0.2s;
}

#generatedThumbnailDisplay img:hover {
    opacity: 0.8;
}

.thumbnail-idea-item {
    margin-bottom: 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid #ddd;
}

.thumbnail-idea-item:last-child {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 0;
}

#currentKeywordsDisplay {
    font-size: 0.875rem;
    color: #374151;
    margin-bottom: 0.75rem;
    background-color: #f9fafb;
    padding: 0.5rem;
    border-radius: 0.25rem;
    border: 1px solid #e5e7eb;
}

#problematicKeywordsOutput {
    margin-top: 1rem;
    padding-top: 0.75rem;
    border-top: 1px dashed #c7d2fe;
}

#problematicKeywordsOutput h4 {
    font-size: 0.875rem;
    font-weight: 600;
    color: #be123c;
    margin-bottom: 0.25rem;
}

#originalDescriptionText {
    white-space: pre-wrap;
    background-color: #f9fafb;
    padding: 0.5rem;
    border-radius: 0.25rem;
    border: 1px solid #e5e7eb;
    max-height: 200px;
    overflow-y: auto;
}

#toast-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 1050;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.toast {
    padding: 1rem 1.5rem;
    border-radius: 0.5rem;
    color: white;
    font-weight: 500;
    box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
    opacity: 0;
    transform: translateX(100%);
    transition: opacity 0.4s ease, transform 0.4s ease;
}

.toast.show {
    opacity: 1;
    transform: translateX(0);
}

.toast.info {
    background-color: #3b82f6;
}

.toast.error {
    background-color: #ef4444;
}

.toast.success {
    background-color: #22c55e;
}

/* Heatmap cell colors for upload frequency */
.freq-cell-0 {
    background-color: #f8fafc;
    color: #cbd5e1;
}

.freq-cell-1 {
    background-color: #dbeafe;
    color: #1e3a8a;
}

.freq-cell-2 {
    background-color: #93c5fd;
    color: #1e3a8a;
}

.freq-cell-3 {
    background-color: #3b82f6;
    color: #ffffff;
}

.freq-cell-4 {
    background-color: #1d4ed8;
    color: #ffffff;
}

.col-checkbox {
    width: 1rem;
    height: 1rem;
    color: #3b82f6;
    border-radius: 0.25rem;
}

details>summary {
    list-style: none;
}

details>summary::-webkit-details-marker {
    display: none;
}

/* Recent Audits Scrollbar */
#recentAuditsList::-webkit-scrollbar {
    height: 6px;
}

#recentAuditsList::-webkit-scrollbar-track {
    background: #f1f1f1;
    border-radius: 4px;
}

#recentAuditsList::-webkit-scrollbar-thumb {
    background: #cbd5e1;
    border-radius: 4px;
}

#recentAuditsList::-webkit-scrollbar-thumb:hover {
    background: #94a3b8;
}

.line-clamp-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ==========================================================================
   Tailwind v4 bg-opacity Polyfill
   bg-opacity-* was removed in Tailwind v4. These rules restore the original
   Tailwind v2/v3 behaviour so all modal overlays render as semi-transparent
   instead of solid.
   ========================================================================== */

/* Transparent overlay combinations used by modal backdrops site-wide */
.bg-gray-500.bg-opacity-75 { background-color: rgba(107, 114, 128, 0.75) !important; }
.bg-gray-600.bg-opacity-50 { background-color: rgba(75,  85,  99,  0.50) !important; }
.bg-gray-800.bg-opacity-50 { background-color: rgba(31,  41,  55,  0.50) !important; }
.bg-gray-900.bg-opacity-40 { background-color: rgba(17,  24,  39,  0.40) !important; }
.bg-gray-900.bg-opacity-50 { background-color: rgba(17,  24,  39,  0.50) !important; }
.bg-gray-900.bg-opacity-60 { background-color: rgba(17,  24,  39,  0.60) !important; }
.bg-gray-900.bg-opacity-70 { background-color: rgba(17,  24,  39,  0.70) !important; }
.bg-gray-900.bg-opacity-75 { background-color: rgba(17,  24,  39,  0.75) !important; }
.bg-black.bg-opacity-30    { background-color: rgba(0,   0,   0,   0.30) !important; }
.bg-black.bg-opacity-50    { background-color: rgba(0,   0,   0,   0.50) !important; }
.bg-black.bg-opacity-70    { background-color: rgba(0,   0,   0,   0.70) !important; }
.bg-black.bg-opacity-75    { background-color: rgba(0,   0,   0,   0.75) !important; }
.bg-black.bg-opacity-80    { background-color: rgba(0,   0,   0,   0.80) !important; } 

/* ==========================================================================
   Material Symbols font-variation-settings utilities
   Replaces inline style="font-variation-settings: ..." attributes to allow
   removal of 'unsafe-inline' from the style-src CSP directive.
   ========================================================================== */

.ms-default { font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24; }
.ms-sm      { font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 20; }
.ms-fill    { font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 24; }
.ms-22      { font-size: 22px; }

/* ==========================================================================
   Delivery.php layout rules — moved from inline <style> block
   ========================================================================== */

.linked-row-bottom td { border-bottom-width: 0px !important; }
.linked-row-top    td { border-top-width:    0px !important; }

.kanban-col-blocked {
    opacity: 0.45;
    position: relative;
    cursor: not-allowed;
}
.kanban-col-blocked::after {
    content: '\26D4';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.75rem;
    background: rgba(239, 68, 68, 0.06);
    border-radius: inherit;
    pointer-events: none;
    z-index: 10;
}
.kanban-col-valid {
    box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.35);
}
.kanban-col-blocked .kanban-drop-zone {
    pointer-events: none;
}

/* ==========================================================================
   Admin Seeder — moved from inline <style> block
   ========================================================================== */

.cm-hidden { display: none; }

/* ==========================================================================
   Dynamic layout helpers — replace one-off inline style= dimension attributes
   ========================================================================== */

/* Progress bar zero-width start state (replaces style="width:0%") */
.w-zero { width: 0%; }

/* Drawer panel sizing via CSS custom property (replaces inline style= on #pmDetailDrawerPanel) */
#pmDetailDrawerPanel {
    top: var(--pm-drawer-top, 0px);
    height: calc(100% - var(--pm-drawer-top, 0px));
}

/* Horizontal scroll containers with thin scrollbar (no Tailwind equivalent for scrollbar-width) */
.scroll-x-thin {
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}

/* Seasonal insights mini-loader (replaces style="width:12px;height:12px;border-width:2px") */
.loader-xs {
    width: 12px !important;
    height: 12px !important;
    border-width: 2px !important;
}

/* Max-height modal body / panel utilities (replace one-off inline style= values) */
.max-h-90vh { max-height: 90vh; }
.max-h-65vh { max-height: 65vh; }
.max-h-calc-280 { max-height: calc(100vh - 280px); }

/* Min-height section utilities */
.min-h-80vh { min-height: 80vh; }
  
/* CR Channel Modal Table - Phase 2 */ 

/* ==========================================================================
   Client Records Channel Modal Table - replacing inline style= attributes
   (admin.php ~6370-6543)
   ========================================================================== */
.cr-modal-container { background: #fff; border-radius: 1rem; box-shadow: 0 25px 60px rgba(0,0,0,.25); width: 100%; max-width: 1440px; max-height: 90vh; display: flex; flex-direction: column; overflow: hidden; margin: 1.5rem; }
.cr-modal-header { padding: 1.25rem 1.5rem; border-bottom: 1px solid #dbeafe; background: #eff6ff; display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-shrink: 0; }
.cr-modal-header-left  { display: flex; align-items: center; gap: .75rem; }
.cr-modal-header-right { display: inline-flex; align-items: center; gap: .5rem; }
.cr-icon-box { width: 2.25rem; height: 2.25rem; border-radius: .75rem; background: #bfdbfe; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.cr-icon-box svg   { width: 1.1rem; height: 1.1rem; }
.cr-modal-title    { font-size: .875rem; font-weight: 700; color: #111827; margin: 0; }
.cr-modal-subtitle { font-size: .7rem; color: #1d4ed8; font-weight: 600; margin: .1rem 0 0; }
.cr-edit-btn { padding: .3rem .75rem; font-size: .7rem; font-weight: 600; border-radius: .5rem; border: 1.5px solid #1d4ed8; background: #fff; color: #1d4ed8; cursor: pointer; display: inline-flex; align-items: center; gap: .35rem; line-height: 1; }
.cr-close-btn { padding: .4rem; border-radius: .5rem; border: none; background: transparent; cursor: pointer; color: #6b7280; line-height: 1; }
.cr-table-scroll { overflow-y: auto; overflow-x: auto; flex: 1; }
.cr-table        { width: 100%; border-collapse: collapse; font-size: .75rem; min-width: 700px; }
.cr-thead-tr     { background: #f0f9ff; position: sticky; top: 0; z-index: 1; border-bottom: 2px solid #bfdbfe; }
.cr-th           { padding: .6rem 1rem; text-align: left; font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: #1e40af; white-space: nowrap; }
.cr-th-center    { text-align: center; }
.cr-td           { padding: .65rem 1rem; vertical-align: middle; }
.cr-td-nowrap    { white-space: nowrap; }
.cr-td-center    { text-align: center; }
.cr-td-gray      { color: #374151; }
.cr-row-even     { background: #fff; border-bottom: 1px solid #f0f9ff; }
.cr-row-odd      { background: rgba(240,249,255,.33); border-bottom: 1px solid #f0f9ff; }
.cr-row-even:hover, .cr-row-odd:hover { background: #eff6ff; }
.cr-channel-name-wrap { display: inline-flex; align-items: center; gap: .4rem; }
.cr-channel-name      { font-weight: 600; color: #1e3a5f; }
.cr-channel-id-wrap   { display: inline-flex; align-items: center; gap: .3rem; }
.cr-channel-id-link   { font-family: monospace; background: #dbeafe; color: #1e40af; border-radius: .25rem; padding: .15rem .35rem; white-space: nowrap; display: inline-block; text-decoration: none; }
.cr-channel-id-link:hover { text-decoration: underline; }
.cr-channel-copy-btn  { padding: .15rem; border: none; background: transparent; cursor: pointer; color: #9ca3af; line-height: 1; border-radius: .25rem; flex-shrink: 0; }
.cr-channel-copy-btn:hover { color: #1e40af; }
.cr-client-id-badge   { font-family: monospace; background: #e0e7ff; color: #3730a3; border-radius: .25rem; padding: .15rem .35rem; white-space: nowrap; display: inline-block; }
.cr-topic-none  { color: #9ca3af; font-style: italic; }
.cr-topic-dash  { color: #d1d5db; }
.cr-wiki-link   { color: #2563eb; text-decoration: none; }
.cr-wiki-link:hover { text-decoration: underline; }
.cr-modal-footer     { padding: .75rem 1.5rem; background: #f0f9ff; border-top: 1px solid #dbeafe; display: flex; justify-content: flex-end; flex-shrink: 0; }
.cr-modal-close-btn  { padding: .4rem 1rem; font-size: .75rem; font-weight: 600; border-radius: .5rem; border: none; background: #1d4ed8; color: #fff; cursor: pointer; }

/* CR Channels outer overlay (replaces inline style= on #crChannelsModal) */
.cr-modal-overlay { display: none; position: fixed; inset: 0; z-index: 9999; align-items: center; justify-content: center; background: rgba(17,24,39,0.65); }

/* ==========================================================================
   Progress bar fill — CSP-safe dynamic width via CSS custom property
   Usage: <div class="pb-fill" style="--pb-w:73%">
   Setting a CSS *custom property* via style= does NOT trigger 'unsafe-inline'
   because browsers do not parse custom properties as CSS property values.
   ========================================================================== */
.pb-fill { width: var(--pb-w, 0%); }

/* ==========================================================================
   Material Symbols — font-variation-settings presets (CSP-safe classes)
   Replaces inline style="font-variation-settings:..." attributes.
   ========================================================================== */
.ms-default { font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 24; }
.ms-fill    { font-variation-settings: 'FILL' 1, 'wght' 300, 'GRAD' 0, 'opsz' 24; }
.ms-sm      { font-variation-settings: 'FILL' 0, 'wght' 300, 'GRAD' 0, 'opsz' 20; }

/* ==========================================================================
   Score tier classes — used in JS innerHTML to replace dynamic style= colours.
   Three tiers: high (≥80), mid (60-79), low (<60).
   ========================================================================== */
/* Badge background+text (used on score pills with white text) */
.sc-bg-high  { background: #15803d; }
.sc-bg-mid   { background: #b45309; }
.sc-bg-low   { background: #b91c1c; }
/* Text-only colour (used on inline score text) */
.sc-txt-high { color: #15803d; }
.sc-txt-mid  { color: #b45309; }
.sc-txt-low  { color: #b91c1c; }
/* Light background (used on leaderboard row badges) */
.sc-lbg-high { background: #dcfce7; color: #15803d; }
.sc-lbg-mid  { background: #fef3c7; color: #b45309; }
.sc-lbg-low  { background: #fee2e2; color: #b91c1c; }
/* AI legend colour dots */
.ai-col-0 { background: #1c5fa5; }
.ai-col-1 { background: #7c3aed; }
.ai-col-2 { background: #9ca3af; }
.ai-col-3 { background: #059669; }
.ai-col-4 { background: #f59e0b; }
/* KPI card text colours (renderKpi) */
.kpi-col-0 { color: #1c5fa5; }
.kpi-col-1 { color: #0290E3; }
.kpi-col-2 { color: #7c3aed; }
/* Category Manager tree indentation — CSP-safe via CSS custom property */
.cm-indent { padding-left: var(--indent, 16px); }

/* ==========================================================================
   System Message Banner — CSP-safe classes (replaces inline style= attributes)
   Used by renderSystemMessageBanner() in core-auth.js and the PHP banner in
   admin.php. Must be class-based because style-src 'unsafe-inline' is removed.
   ========================================================================== */
#systemMessageBanner,
#adminSystemMessageBanner {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 99999;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 16px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    font-family: 'Lato', sans-serif;
    box-sizing: border-box;
}
#systemMessageBanner span,
#adminSystemMessageBanner span {
    flex: 1;
    text-align: center;
    padding-right: 32px;
}
#sysMessageDismissBtn,
#adminSysMessageDismissBtn {
    flex-shrink: 0;
    background: none;
    border: none;
    cursor: pointer;
    opacity: .75;
    padding: 0;
    color: inherit;
    line-height: 1;
}
/* Type colour variants */
.sysmsg-critical    { background: #dc2626; color: #fff; }
.sysmsg-warning     { background: #f97316; color: #fff; }
.sysmsg-information { background: #dbeafe; color: #1e3a5f; border-bottom: 1px solid #93c5fd; }
.sysmsg-resumed     { background: #22c55e; color: #fff; }

/* ==========================================================================
   Project Management — Action Dropdown Menu (#pmMenu_<id> / .pm-action-menu)
   JS-generated via template literals so Tailwind scanner can't detect these
   classes. Placed here (styles.css) which is served with ?v=time() so it is
   never cached and always reaches the browser fresh.
   !important used on container background/border because Tailwind v4's CSS
   layer cascade can override non-!important rules at equal specificity.
   ========================================================================== */
.pm-action-menu,
[id^="pmMenu_"] {
    background-color: #ffffff !important;
    background: #ffffff !important;
    border: 1px solid #e5e7eb !important;
    border-radius: 0.5rem !important;
    box-shadow: 0 10px 15px -3px rgba(0,0,0,.10), 0 4px 6px -4px rgba(0,0,0,.10) !important;
    opacity: 1 !important;
}
.pm-action-menu button,
[id^="pmMenu_"] button {
    display: block !important;
    width: 100%;
    text-align: left;
    padding: 0.5rem 0.75rem;
    font-size: 0.75rem;
    line-height: 1rem;
    background: transparent !important;
    background-color: transparent !important;
    color: #374151 !important;
    border: none !important;
    cursor: pointer;
    transition: background-color 0.15s;
}
.pm-action-menu button:hover,
[id^="pmMenu_"] button:hover                                             { background-color: #f9fafb !important; }
.pm-action-menu button[data-action="pm-new-dependent"],
[id^="pmMenu_"] button[data-action="pm-new-dependent"]                   { color: #4338ca !important; font-weight: 600 !important; }
.pm-action-menu button[data-action="pm-new-dependent"]:hover,
[id^="pmMenu_"] button[data-action="pm-new-dependent"]:hover             { background-color: #eef2ff !important; }
.pm-action-menu button[data-action="pm-complete"],
[id^="pmMenu_"] button[data-action="pm-complete"]                        { color: #15803d !important; }
.pm-action-menu button[data-action="pm-complete"]:hover,
[id^="pmMenu_"] button[data-action="pm-complete"]:hover                  { background-color: #f0fdf4 !important; }
.pm-action-menu button[data-action="pm-cancel"],
[id^="pmMenu_"] button[data-action="pm-cancel"]                          { color: #dc2626 !important; }
.pm-action-menu button[data-action="pm-cancel"]:hover,
[id^="pmMenu_"] button[data-action="pm-cancel"]:hover                    { background-color: #fef2f2 !important; }
.pm-action-menu hr,
[id^="pmMenu_"] hr { margin: 0.25rem 0; border-color: #f3f4f6; }

/* Dark-mode overrides for the action menu. The block above hardcodes light
   hex with !important, so dark needs matching !important at higher specificity
   (the html.dark prefix) to win. Literal dark hex (not vars) — this block is
   html.dark-scoped, and a var inside a `background` shorthand failed to resolve
   here before, so we stay with longhand background-color + literals. */
html.dark .pm-action-menu,
html.dark [id^="pmMenu_"] {
    background-color: #1e293b !important;
    border-color: #334155 !important;
    box-shadow: 0 10px 15px -3px rgba(0,0,0,.45), 0 4px 6px -4px rgba(0,0,0,.45) !important;
}
html.dark .pm-action-menu button,
html.dark [id^="pmMenu_"] button                                         { color: #e2e8f0 !important; }
html.dark .pm-action-menu button:hover,
html.dark [id^="pmMenu_"] button:hover                                   { background-color: #172033 !important; }
html.dark .pm-action-menu button[data-action="pm-new-dependent"],
html.dark [id^="pmMenu_"] button[data-action="pm-new-dependent"]         { color: #a5b4fc !important; }
html.dark .pm-action-menu button[data-action="pm-new-dependent"]:hover,
html.dark [id^="pmMenu_"] button[data-action="pm-new-dependent"]:hover   { background-color: rgba(99,102,241,.15) !important; }
html.dark .pm-action-menu button[data-action="pm-complete"],
html.dark [id^="pmMenu_"] button[data-action="pm-complete"]              { color: #4ade80 !important; }
html.dark .pm-action-menu button[data-action="pm-complete"]:hover,
html.dark [id^="pmMenu_"] button[data-action="pm-complete"]:hover        { background-color: rgba(34,197,94,.14) !important; }
html.dark .pm-action-menu button[data-action="pm-cancel"],
html.dark [id^="pmMenu_"] button[data-action="pm-cancel"]                { color: #f87171 !important; }
html.dark .pm-action-menu button[data-action="pm-cancel"]:hover,
html.dark [id^="pmMenu_"] button[data-action="pm-cancel"]:hover          { background-color: rgba(239,68,68,.14) !important; }
html.dark .pm-action-menu hr,
html.dark [id^="pmMenu_"] hr                                             { border-color: #334155 !important; }

/* PM live empty-state icon — font-size set here because Tailwind's build scanner
   misses classes used only inside JS template literals (e.g. text-8xl). */
.pm-live-empty-icon {
    font-size: 3rem !important;   /* matches text-5xl (48px) used on the briefcase */
    line-height: 1 !important;
}

/* -- Benchmark Intelligence band colours (bm-*) -------------------------
   Classes used in JS template literals - must live in styles.css since
   CSP style-src 'unsafe-inline' is disabled (no inline style= attrs). */
.bm-hero-green  { color:#16a34a }
.bm-hero-blue   { color:#2563eb }
.bm-hero-amber  { color:#d97706 }
.bm-hero-red    { color:#dc2626 }
.bm-hero-gray   { color:#1f2937 }
.bm-badge-green { color:#16a34a; background:rgba(22,163,74,.08) }
.bm-badge-blue  { color:#2563eb; background:rgba(37,99,235,.08) }
.bm-badge-amber { color:#d97706; background:rgba(217,119,6,.08)  }
.bm-badge-red   { color:#dc2626; background:rgba(220,38,38,.08)  }

/* ==========================================================================
   Audit Result Tab Navigation
   ========================================================================== */

.audit-tab-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.45rem 1rem;
    font-size: 1.2rem;
    font-weight: 600;
    color: #6b7280;
    background: transparent;
    border: none;
    border-bottom: 3px solid transparent;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
    white-space: nowrap;
    outline: none;
    margin-bottom: -1px; /* overlap the nav border-b */
}
.audit-tab-btn.hidden {
    display: none !important;
}
.audit-tab-btn:hover {
    color: var(--brand-primary, #1c5fa5);
    border-bottom-color: #93c5fd;
}
.audit-tab-btn.active {
    color: var(--brand-primary, #1c5fa5);
    border-bottom-color: var(--brand-primary, #1c5fa5);
    background: transparent;
}
.audit-tab-count {
    font-size: 0.7rem;
    font-weight: 700;
    background: #e5e7eb;
    color: #374151;
    border-radius: 9999px;
    padding: 0.05rem 0.45rem;
    min-width: 1.4rem;
    text-align: center;
    display: inline-block;
}
.audit-tab-btn.active .audit-tab-count {
    background: #dbeafe;
    color: var(--brand-primary, #1c5fa5);
}
.audit-tab-panel {
    /* overflow:hidden creates a block formatting context, preventing the global
       table { margin-top: 0.5rem } from collapsing through the panel edge
       and causing the thin artifact strip seen at the top of Shorts/Live tabs. */
    overflow: hidden;
    min-height: 100px;
}

/* ==========================================================================
   Executive Summary — Content Mix Bar
   Classes used in JS template literals → must live here, not Tailwind build.
   ========================================================================== */
.mix-bar     { display: flex; gap: 2px; border-radius: 6px; overflow: hidden; height: 20px; margin-top: 10px; }
.mix-segment { display: flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 600; color: #fff; transition: flex 0.4s ease; white-space: nowrap; overflow: hidden; min-width: 0; padding: 0 6px; }
.mix-videos  { background: #1c5fa5; }
.mix-shorts  { background: #7c3aed; }
.mix-live    { background: #059669; }

/* ==========================================================================
   Modal overlay z-index — must clear system banner (z-index: 99999)
   Body-level fixed overlays appended by showXxxModal() and the thumbnail
   browser would otherwise sit beneath the banner, blocking close buttons.
   ========================================================================== */
body > div.fixed[class*="bg-black"],
body > div.fixed[class*="bg-gray-8"] {
    z-index: 100000;
}

/* ==========================================================================
   070 §6.9 dual-palette — dark-mode form controls. The brand utilities theme
   the shell, but bare <input>/<select>/<textarea> carry no bg/text class, so in
   dark mode they rendered light-on-light (unreadable hex fields in the branding
   editor + every other dark form). Scoped to html.dark so LIGHT mode is
   byte-identical. Colour/checkbox/radio/file/button inputs are excluded.
   ========================================================================== */
html.dark input:not([type=color]):not([type=checkbox]):not([type=radio]):not([type=file]):not([type=range]):not([type=submit]):not([type=button]),
html.dark select,
html.dark textarea {
    background-color: var(--brand-surface-subtle, #1e293b);
    color: var(--brand-text, #f1f5f9);
    border-color: var(--brand-border, #334155);
}
html.dark input::placeholder,
html.dark textarea::placeholder {
    color: var(--brand-text-secondary, #94a3b8);
}

/* ── Display-settings menu + width preference (redesign shell) ────────────────
   The ph-lego dropdown in the shared top-nav (shared/settings_menu.jspf). Styled
   here (not via Tailwind utilities) so it renders regardless of the prebuilt
   tailwind.built.css. Colours use --color-brand-* so they track light/dark + WL.
   ──────────────────────────────────────────────────────────────────────────── */
.ui-set { position: relative; display: inline-block; }
.ui-set-menu {
    position: absolute; right: 0; top: calc(100% + 8px); width: 280px;
    background: var(--color-brand-surface-subtle); border: 1px solid var(--color-brand-border);
    border-radius: 12px; box-shadow: 0 12px 30px rgba(0, 0, 0, .18); padding: 8px; z-index: 60;
}
.ui-set-menu[hidden] { display: none; }
.ui-set-h {
    font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--color-brand-text-secondary); padding: 6px 8px 8px; display: flex; align-items: center; gap: 7px;
}
.ui-set-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px; }
.ui-set-row > span { font-size: 14px; font-weight: 700; color: var(--color-brand-text); }
.ui-seg { display: inline-flex; border: 1px solid var(--color-brand-border); border-radius: 8px; overflow: hidden; }
.ui-seg button {
    border: 0; background: var(--color-brand-surface); color: var(--color-brand-text-secondary);
    font-family: inherit; font-weight: 700; font-size: 12px; padding: 7px 12px; cursor: pointer; transition: background-color .15s;
}
.ui-seg button + button { border-left: 1px solid var(--color-brand-border); }
.ui-seg button[aria-pressed="true"] { background: var(--color-brand-primary); color: #fff; }

/* Width preference: "narrow" caps the page containers (.ui-page-w) at 1920px on
   ultrawide displays; "full" (default) keeps the existing max-w-[98%]. Unlayered
   rule → wins over the Tailwind utility without !important. */
html[data-width="narrow"] .ui-page-w { max-width: min(98%, 1920px); }

/* Density preference: "compact" trims the rem basis ~12.5%, so all rem-based Tailwind spacing,
   row heights and the type scale shrink proportionally — a safe, uniform compaction with no
   per-component targeting. "comfortable" (default) keeps the base 16px. Phones force comfortable
   so touch targets stay legible (design-system §6/§7 guardrail). */
html[data-density="compact"] { font-size: 14px; }
@media (max-width: 640px) { html[data-density="compact"] { font-size: 16px; } }

/* Dark-sweep utilities the prebuilt Tailwind doesn't emit (divide-brand-border / bg-brand-border
   weren't used before the retheme, so they were never compiled). Defined here, token-backed, so the
   ~70 dividers/separators the sweep introduced render + flip in dark — no Tailwind rebuild needed. */
.divide-brand-border > :not([hidden]) ~ :not([hidden]) { border-color: var(--color-brand-border); }
.bg-brand-border { background-color: var(--color-brand-border); }
/* Also pre-existing-but-uncompiled: hover:bg-brand-primary-strong on primary buttons (content.jspf,
   core-state.js) — the darken-on-hover never rendered. Define it (theme-correct via the token). */
.bg-brand-primary-strong { background-color: var(--color-brand-primary-strong); }
.hover\:bg-brand-primary-strong:hover { background-color: var(--color-brand-primary-strong); }

/* ── Quick-access drawer (redesign shell) ────────────────────────────────────
   Right-side off-canvas panel (shared/quick_access_drawer.jspf) listing the user's
   My Links / Favourites / Watched on every page. Colours via --color-brand-* so it
   tracks light/dark + white-label. ──────────────────────────────────────────── */
.qa-scrim { position: fixed; inset: 0; background: rgba(0, 0, 0, .4); z-index: 100001; display: none; }
.qa-scrim.open { display: block; }
.qa-drawer {
    position: fixed; top: 0; right: 0; bottom: 0; width: 344px; max-width: 90vw;
    background: var(--color-brand-surface-subtle); border-left: 1px solid var(--color-brand-border);
    box-shadow: -6px 0 24px rgba(0, 0, 0, .2); z-index: 100002; transform: translateX(100%);
    transition: transform .2s ease; overflow-y: auto; padding: 16px;
}
.qa-drawer.open { transform: none; }
.qa-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 2px; }
.qa-head strong { font-size: 15px; display: flex; align-items: center; gap: 8px; color: var(--color-brand-text); }
.qa-close { border: 0; background: transparent; color: var(--color-brand-text-secondary); font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 8px; border-radius: 6px; }
.qa-close:hover { background: var(--color-brand-border); }
.qa-sub { font-size: 11px; color: var(--color-brand-text-secondary); margin: 0 4px 4px; }
.qa-sec h4 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--color-brand-text-secondary); margin: 16px 4px 6px; display: flex; align-items: center; gap: 7px; }
.qa-row { display: flex; align-items: center; gap: 11px; padding: 9px 8px; border-radius: 8px; text-decoration: none; color: var(--color-brand-text); font-size: 14px; }
.qa-row:hover { background: color-mix(in srgb, var(--color-brand-primary) 8%, transparent); }
.qa-row > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qa-row i.lead { font-size: 18px; color: var(--color-brand-primary); flex: 0 0 auto; }
.qa-row .meta { margin-left: auto; font-size: 11px; color: var(--color-brand-text-secondary); flex: 0 0 auto; }
.qa-row .meta.alert { color: var(--color-brand-danger); font-weight: 700; }
.qa-empty { font-size: 13px; font-style: italic; color: var(--color-brand-text-secondary); padding: 6px 8px; }
.qa-more { display: block; text-align: center; font-size: 12px; font-weight: 700; color: var(--color-brand-primary); text-decoration: none; padding: 10px; border-top: 1px solid var(--color-brand-border); margin-top: 10px; }

/* ───────────────────────────────────────────────────────────────────────────
   Dark-mode functional colour tints (redesign follow-up, 2026-07-01).
   The --brand-* tokens theme in dark, but status/semantic colour across the app
   uses hundreds of hardcoded Tailwind tints (bg-red-50, text-amber-700,
   bg-green-50, bg-indigo-50 …) — identical light hues in both schemes, so on the
   dark surface they render as glaring pastel boxes + low-contrast text (see the
   Flagged banner + Projects cards). Tailwind here is prebuilt (no JIT) and these
   classes are used far too widely to swap per element, so we REMAP the semantic
   scales under html.dark: light tint bg → low-alpha coloured wash, -200 borders →
   mid-alpha, and text -500..-800 → a lighter, readable shade of the same hue.
   Solid -500/-600 button fills are intentionally left alone (they read fine on
   dark). Non-layered rules loaded after tailwind.built.css, so these win. ------ */
html.dark .bg-red-50    { background-color: rgba(239,68,68,.10); }
html.dark .bg-red-100   { background-color: rgba(239,68,68,.16); }
html.dark .border-red-200   { border-color: rgba(239,68,68,.30); }
html.dark .text-red-500, html.dark .text-red-600 { color: #f87171; }
html.dark .text-red-700, html.dark .text-red-800 { color: #fca5a5; }
html.dark .hover\:bg-red-50:hover  { background-color: rgba(239,68,68,.16); }
html.dark .hover\:bg-red-100:hover { background-color: rgba(239,68,68,.22); }

html.dark .bg-amber-50  { background-color: rgba(245,158,11,.10); }
html.dark .bg-amber-100 { background-color: rgba(245,158,11,.16); }
html.dark .border-amber-200 { border-color: rgba(245,158,11,.30); }
html.dark .text-amber-600, html.dark .text-amber-700, html.dark .text-amber-800 { color: #fbbf24; }
html.dark .hover\:bg-amber-50:hover { background-color: rgba(245,158,11,.16); }

html.dark .bg-green-50  { background-color: rgba(34,197,94,.10); }
html.dark .bg-green-100 { background-color: rgba(34,197,94,.16); }
html.dark .border-green-200 { border-color: rgba(34,197,94,.30); }
html.dark .text-green-600, html.dark .text-green-700 { color: #4ade80; }
html.dark .hover\:bg-green-50:hover { background-color: rgba(34,197,94,.16); }

html.dark .bg-emerald-50  { background-color: rgba(16,185,129,.10); }
html.dark .bg-emerald-100 { background-color: rgba(16,185,129,.16); }
html.dark .border-emerald-200 { border-color: rgba(16,185,129,.30); }
html.dark .text-emerald-600, html.dark .text-emerald-700 { color: #34d399; }

html.dark .bg-blue-50   { background-color: rgba(59,130,246,.10); }
html.dark .bg-blue-100  { background-color: rgba(59,130,246,.16); }
html.dark .border-blue-200  { border-color: rgba(59,130,246,.30); }
html.dark .text-blue-600, html.dark .text-blue-700 { color: #60a5fa; }
html.dark .hover\:bg-blue-50:hover { background-color: rgba(59,130,246,.16); }

html.dark .bg-indigo-50  { background-color: rgba(99,102,241,.12); }
html.dark .bg-indigo-100 { background-color: rgba(99,102,241,.18); }
html.dark .border-indigo-200 { border-color: rgba(99,102,241,.32); }
html.dark .text-indigo-600, html.dark .text-indigo-700 { color: #818cf8; }
html.dark .hover\:bg-indigo-50:hover { background-color: rgba(99,102,241,.18); }

/* NOTE: the Projects (⋯) action-menu dark theming lives up in the
   "Project Management — Action Dropdown Menu" block (html.dark overrides right
   after the base rules) — it must sit next to the base !important rules it beats.
   The .pm-item-* classes on the buttons are cosmetic redundancy; the base block's
   button[data-action="…"] rules carry the real colours. */

/* ───────────────────────────────────────────────────────────────────────────
   Dark-mode NEUTRAL fallback (redesign follow-up, 2026-07-01, part 2).
   Several table-heavy views the token sweep didn't fully reach (Client
   Financials / Services / Customer Assignments, Delivery → Content) still carry
   raw Tailwind neutrals (bg-white, bg-gray-50, text-gray-700, border-gray-200 …)
   — on the dark page frame those render as glaring light tables. Remap the whole
   neutral scale under html.dark so ANY un-swept content themes correctly. (These
   are non-layered + load after tailwind.built.css, so they win; genuinely
   must-stay-white cases are rare and can opt back with a token class.) --------- */
html.dark .bg-white  { background-color: var(--color-brand-surface-subtle); }
html.dark .bg-gray-50  { background-color: #172033; }
html.dark .bg-gray-100 { background-color: var(--color-brand-surface-subtle); }
html.dark .bg-gray-200 { background-color: #334155; }
html.dark .bg-gray-300 { background-color: #475569; }
html.dark .hover\:bg-gray-50:hover  { background-color: rgba(148,163,184,.07); }
html.dark .hover\:bg-gray-100:hover { background-color: rgba(148,163,184,.12); }
html.dark .hover\:bg-gray-200:hover { background-color: rgba(148,163,184,.16); }
html.dark .text-gray-400 { color: #94a3b8; }
html.dark .text-gray-500 { color: #94a3b8; }
html.dark .text-gray-600 { color: #cbd5e1; }
html.dark .text-gray-700 { color: #e2e8f0; }
html.dark .text-gray-800, html.dark .text-gray-900 { color: #f1f5f9; }
html.dark .border-gray-100 { border-color: #273244; }
html.dark .border-gray-200 { border-color: var(--color-brand-border); }
html.dark .border-gray-300 { border-color: #3f4b5f; }
html.dark .divide-gray-100 > :not([hidden]) ~ :not([hidden]),
html.dark .divide-gray-200 > :not([hidden]) ~ :not([hidden]) { border-color: var(--color-brand-border); }

/* Purple / violet functional scale (Policies overview card + policy modal). */
html.dark .bg-purple-50  { background-color: rgba(168,85,247,.12); }
html.dark .bg-purple-100 { background-color: rgba(168,85,247,.18); }
html.dark .border-purple-100, html.dark .border-purple-200 { border-color: rgba(168,85,247,.30); }
html.dark .text-purple-600, html.dark .text-purple-700, html.dark .text-purple-800 { color: #c084fc; }
html.dark .hover\:bg-purple-100:hover { background-color: rgba(168,85,247,.22); }

/* Overview cards use -100 borders (bg-blue-50 border-blue-100 …). */
html.dark .border-red-100    { border-color: rgba(239,68,68,.28); }
html.dark .border-amber-100  { border-color: rgba(245,158,11,.28); }
html.dark .border-green-100  { border-color: rgba(34,197,94,.28); }
html.dark .border-blue-100   { border-color: rgba(59,130,246,.28); }
html.dark .border-indigo-100 { border-color: rgba(99,102,241,.28); }

/* ==========================================================================
   Managed Vocabularies (Admin → rights_vocab) — align the 4-col tables.
   Each vocab group renders its own <table>; with the default auto layout every
   table sized Code/Name/State/Manage independently, so the stacked tables never
   lined up. table-layout:fixed + a shared <colgroup> pins identical widths, so
   all groups' columns align. Widths are in styles.css (not Tailwind w-* / inline
   style=) because Tailwind is prebuilt (arbitrary widths need JIT) and CSP blocks
   inline styles. Name is the flexible column (fills the row); it holds the
   w-44 rename input so it stays comfortably wide on the 1080p admin layout. ---- */
.vocab-table              { table-layout: fixed; }
.vocab-table col.vt-code   { width: 13rem; }
.vocab-table col.vt-state  { width: 7rem;  }
.vocab-table col.vt-manage { width: 11rem; }
/* Code cells can carry long snake_case identifiers (e.g. broadcast_transmission);
   let them wrap inside the fixed width instead of forcing overflow. */
.vocab-table td.vt-code-cell { overflow-wrap: anywhere; }
