/* Blaas — shared component CSS.
 *
 * Loaded by base.html and portal/base.html via /static/css/app.css.
 *
 * NOTE: Internal pages that already define the same rules inline
 * (sites/list.html, public/list.html) keep their inline copies — those rules
 * win by DOM order, behavior unchanged. New shared usage (portal/*, admin/*)
 * relies on these definitions and avoids inline duplication.
 */

/* ==================== chip-picker ==================== */
.chip-picker { position: relative; }
.chip-picker .cp-box {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
    min-height: 38px; padding: 5px 8px;
    background: rgba(15, 23, 42, 0.6); border: 1px solid #1f2937; border-radius: 8px;
    cursor: text; font-size: 13px;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.chip-picker .cp-box:hover { border-color: #334155; background: rgba(15, 23, 42, 0.85); }
.chip-picker.cp-open .cp-box {
    border-color: #10b981;
    box-shadow: 0 0 0 3px rgba(16,185,129,0.15);
    background: #0f172a;
}
.chip-picker .cp-chip {
    display: inline-flex; align-items: center; gap: 5px;
    background: linear-gradient(180deg, rgba(16,185,129,0.18), rgba(16,185,129,0.12));
    color: #a7f3d0;
    padding: 3px 4px 3px 9px;
    border: 1px solid rgba(16,185,129,0.35);
    border-radius: 6px;
    font-size: 12px; font-weight: 500; line-height: 1.2;
    transition: background .15s ease, border-color .15s ease;
}
.chip-picker .cp-chip:hover { border-color: rgba(16,185,129,0.6); }
.chip-picker .cp-chip-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px;
    cursor: pointer; opacity: 0.6;
    border-radius: 4px;
    font-size: 14px; line-height: 1;
    transition: all .12s ease;
}
.chip-picker .cp-chip-x:hover { opacity: 1; color: #fecaca; background: rgba(248, 113, 113, 0.18); }
.chip-picker .cp-input {
    flex: 1; min-width: 60px; background: transparent; border: none; outline: none;
    color: #f3f4f6; font-size: 13px; padding: 2px 0;
}
.chip-picker .cp-input::placeholder { color: #64748b; font-style: italic; }
.chip-picker .cp-dropdown {
    position: absolute; z-index: 50; left: 0; right: 0; top: 100%;
    margin-top: 6px; max-height: 280px; overflow-y: auto;
    background: #0b1220; border: 1px solid #1f2937; border-radius: 10px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.6), 0 4px 12px rgba(0,0,0,0.3);
    padding: 4px;
    animation: cp-fadein .12s ease-out;
}
@keyframes cp-fadein { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.chip-picker .cp-option {
    padding: 8px 10px; cursor: pointer; font-size: 13px;
    display: flex; align-items: center; gap: 8px; border-radius: 6px;
    transition: background .1s ease;
}
.chip-picker .cp-option:hover, .chip-picker .cp-option.cp-active { background: #1e293b; }
.chip-picker .cp-option.cp-selected { color: #a7f3d0; }
.chip-picker .cp-check {
    width: 16px; height: 16px; flex-shrink: 0;
    border-radius: 4px; border: 1px solid #334155;
    display: inline-flex; align-items: center; justify-content: center;
    color: #10b981; font-size: 11px; font-weight: 700;
}
.chip-picker .cp-option.cp-selected .cp-check { background: #10b981; border-color: #10b981; color: #022c22; }
.chip-picker .cp-empty { padding: 16px; text-align: center; color: #64748b; font-size: 12px; }
.chip-picker .cp-dropdown::-webkit-scrollbar { width: 6px; }
.chip-picker .cp-dropdown::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }

/* ==================== range-input ==================== */
.range-input { display: flex; align-items: center; gap: 6px; margin-top: 4px; }
.range-input .ri-field { position: relative; flex: 1 1 0; }
.range-input .ri-prefix {
    position: absolute; left: 8px; top: 50%; transform: translateY(-50%);
    font-size: 12px; color: #64748b; pointer-events: none;
    font-variant-numeric: tabular-nums;
}
.range-input input {
    width: 100%; background: #0a0f1d; border: 1px solid #1f2937;
    color: #f3f4f6; border-radius: 6px; padding: 6px 8px;
    font-size: 13px; font-variant-numeric: tabular-nums;
    -moz-appearance: textfield;
    transition: border-color .15s ease, background .15s ease;
}
.range-input input.ri-prefixed { padding-left: 18px; }
/* Prefixo de 2+ chars (R$) — sem isto ele sobrepõe o placeholder. */
.range-input input.ri-prefixed-wide { padding-left: 28px; }
.range-input input::placeholder { color: #475569; }
.range-input input:hover { border-color: #334155; }
.range-input input:focus { border-color: #10b981; background: #0f172a; outline: none; }
.range-input input.ri-active { border-color: #34d399; background: rgba(16, 185, 129, 0.06); color: #34d399; }
.range-input input::-webkit-outer-spin-button,
.range-input input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.range-input .ri-sep { color: #475569; font-size: 12px; user-select: none; }

/* ==================== filter-dd (pill button + popover) ==================== */
.filter-dd { position: relative; display: inline-flex; }
.filter-dd-trigger {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px;
    background: rgba(15, 23, 42, 0.6);
    border: 1px solid #1f2937;
    border-radius: 8px;
    color: #d1d5db;
    font-size: 12.5px; font-weight: 500; line-height: 1.2;
    cursor: pointer; white-space: nowrap;
    transition: all .15s ease;
}
.filter-dd-trigger:hover { background: #1f2937; border-color: #334155; color: #f3f4f6; }
.filter-dd.fd-open .filter-dd-trigger {
    border-color: #10b981; background: #0f172a; color: #f3f4f6;
    box-shadow: 0 0 0 3px rgba(16,185,129,0.12);
}
.filter-dd.fd-active .filter-dd-trigger {
    border-color: rgba(16,185,129,0.5); background: rgba(16,185,129,0.08); color: #6ee7b7;
}
.filter-dd-plus {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px; border-radius: 50%;
    background: #1f2937; color: #6b7280;
    font-size: 12px; font-weight: 700;
    transition: all .15s ease;
}
.filter-dd.fd-active .filter-dd-plus { background: #34d399; color: #022c22; }
.filter-dd-badge {
    align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 9px;
    background: #34d399; color: #022c22;
    font-weight: 700; font-size: 10px;
    margin-left: 2px;
}
.filter-dd-badge:not(.hidden) { display: inline-flex; }
.filter-dd-panel {
    position: absolute; top: calc(100% + 6px); left: 0;
    z-index: 30;
    min-width: 260px; max-width: 320px;
    background: #0b1220;
    border: 1px solid #1f2937;
    border-radius: 12px;
    padding: 12px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.6), 0 4px 12px rgba(0,0,0,0.3);
    animation: cp-fadein .12s ease-out;
}
.filter-dd-panel.fd-align-right { left: auto; right: 0; }
/* Bare <select> inside a filter panel (the single-select Country/Category
   pickers on /lists/{id} and /list/{token}). Without this the native control
   renders unstyled — a bare white box on a dark panel. This rule used to live
   only in public/list.html's inline <style>, which is why /lists/{id} looked
   broken while /list/{token} did not. */
.filter-dd-panel select {
    width: 100%;
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-subtle);
    color: var(--color-text-primary);
    border-radius: var(--radius-md);
    padding: 8px 10px;
    font-size: 13px;
}

/* ==================== filter-presets (range buckets) ==================== */
.filter-presets { display: flex; flex-direction: column; gap: 2px; }
.filter-presets-row {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 8px; border-radius: 6px;
    color: #d1d5db; font-size: 13px;
    cursor: pointer; transition: background .12s ease;
    background: transparent; text-align: left; width: 100%;
    user-select: none;
}
.filter-presets-row:hover { background: #1e293b; }
.filter-presets-row .fp-label { flex: 1; display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
.filter-presets-row .fp-range { color: #64748b; font-size: 11px; font-variant-numeric: tabular-nums; flex-shrink: 0; }
.filter-presets-row .fp-dot {
    width: 18px; height: 18px; border-radius: 4px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 10px; font-weight: 700;
    background: #1f2937; color: #9ca3af;
    flex-shrink: 0;
}
.filter-presets-row .fp-cb {
    appearance: none; -webkit-appearance: none;
    width: 16px; height: 16px;
    border: 1.5px solid #475569; border-radius: 4px;
    background: transparent; cursor: pointer; margin: 0;
    flex-shrink: 0; position: relative;
    transition: all .15s ease;
}
.filter-presets-row .fp-cb:hover { border-color: #64748b; }
.filter-presets-row .fp-cb:checked { background: #10b981; border-color: #10b981; }
.filter-presets-row .fp-cb:checked::after {
    content: ''; position: absolute;
    left: 4px; top: 0;
    width: 5px; height: 10px;
    border-right: 2px solid #022c22; border-bottom: 2px solid #022c22;
    transform: rotate(45deg);
}
.filter-presets-row.is-checked { background: rgba(16,185,129,0.10); color: #d1fae5; }
.filter-presets-row.is-checked .fp-range { color: #34d399; }
.filter-presets-divider {
    font-size: 10px; color: #64748b;
    margin: 10px 0 6px;
    text-transform: uppercase; letter-spacing: 0.05em;
    font-weight: 600;
}

/* ==================== filter-toggle (segmented button group) ==================== */
.filter-toggle-group {
    display: inline-flex;
    background: rgba(15, 23, 42, 0.6);
    border: 1px solid #1f2937;
    border-radius: 8px;
    padding: 3px; gap: 0;
}
.filter-toggle {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 12px;
    border: none; background: transparent;
    border-radius: 6px;
    color: #94a3b8; font-size: 12px; font-weight: 500;
    cursor: pointer;
    transition: all .15s ease;
    white-space: nowrap;
}
.filter-toggle:hover { color: #e5e7eb; }
.filter-toggle.active { background: #1e293b; color: #34d399; box-shadow: 0 1px 3px rgba(0,0,0,0.4); }

/* ==================== app-cb (custom checkbox) ====================
   The native control styled out and replaced with a square + CSS checkmark.
   This base rule used to live ONLY in sites/list.html's inline <style>, so
   every other surface that renders a checkbox — /portal/sites, /list/{token},
   /catalog — fell back to the browser default. Same failure mode as the
   `.filter-dd-panel select` rule. Values are byte-identical to that inline
   copy, which stays in place and simply wins by DOM order on /sites. */
.filter-presets-row .fp-cb,
.app-cb {
    appearance: none; -webkit-appearance: none;
    width: 16px; height: 16px;
    border: 1.5px solid #475569;
    border-radius: 4px;
    background: transparent;
    cursor: pointer;
    margin: 0;
    flex-shrink: 0;
    position: relative;
    transition: all .15s ease;
    vertical-align: middle;
}
.filter-presets-row .fp-cb:hover,
.app-cb:hover { border-color: #64748b; }
.filter-presets-row .fp-cb:checked,
.app-cb:checked { background: #10b981; border-color: #10b981; }
.filter-presets-row .fp-cb:checked::after,
.app-cb:checked::after {
    content: '';
    position: absolute;
    left: 4px; top: 0;
    width: 5px; height: 10px;
    border-right: 2px solid #022c22;
    border-bottom: 2px solid #022c22;
    transform: rotate(45deg);
}

/* ==================== app-cb indeterminate (header select-all) ==================== */
.app-cb:indeterminate { background: #10b981; border-color: #10b981; }
.app-cb:indeterminate::after {
    content: ''; position: absolute;
    left: 3px; top: 6px;
    width: 8px; height: 2px;
    background: #022c22; border: none;
    transform: none;
}

/* ==================== flag-pill (country) ==================== */
.flag-pill {
    display: inline-block;
    width: 1.125rem; height: 0.84375rem;
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 1px 2px rgba(0,0,0,0.4);
    background-size: cover; background-position: center;
    vertical-align: -2px; margin-right: 6px;
    flex-shrink: 0;
}

/* ==================== col-toggle (column visibility dropdown) ==================== */
.col-hidden { display: none !important; }
.col-toggle-dd { position: relative; display: inline-block; }
.col-toggle-trigger {
    background: transparent; color: #9ca3af; border: 1px solid #374151;
    padding: 6px 10px; border-radius: 8px; font-size: 12px;
    display: inline-flex; align-items: center; gap: 6px;
    cursor: pointer;
}
.col-toggle-trigger:hover { background: #1f2937; color: #f3f4f6; }
.col-toggle-panel {
    position: absolute; top: calc(100% + 6px); right: 0;
    min-width: 220px; max-height: 70vh; overflow-y: auto;
    background: #111827; border: 1px solid #374151; border-radius: 8px;
    padding: 8px; z-index: 50;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}
.col-toggle-panel.hidden { display: none; }
.col-toggle-row {
    display: flex; align-items: center; gap: 8px;
    padding: 6px 8px; border-radius: 6px; cursor: pointer;
    color: #d1d5db; font-size: 13px;
}
.col-toggle-row:hover { background: #1f2937; }
.col-toggle-footer {
    margin-top: 4px; padding-top: 6px; border-top: 1px solid #1f2937;
    display: flex; justify-content: space-between; gap: 6px;
}
.col-toggle-footer button {
    flex: 1; padding: 6px 10px; border-radius: 6px; font-size: 12px;
    background: #1f2937; color: #d1d5db; border: 1px solid #374151;
    cursor: pointer;
}
.col-toggle-footer button:hover { background: #374151; color: #f3f4f6; }

/* ==================== sortable header indicator ==================== */
th.sortable { cursor: pointer; }
th.sortable:hover { background: #374151; }
th.sortable .sort-icon {
    display: inline-block; margin-left: 4px;
    width: 10px; height: 10px;
    vertical-align: -1px;
    color: #4b5563;
    transition: color .15s ease;
}
th.sortable:hover .sort-icon { color: #9ca3af; }
th.sortable.sort-active .sort-icon { color: #34d399; }

/* ==================== layout helpers ==================== */
body.has-selection-bar { padding-bottom: 80px; }

/* Tooltip variant — anchor to right edge for headers near the viewport border. */
[data-tooltip].tooltip-right::after { left: auto; right: 0; }
[data-tooltip].tooltip-right::before { left: auto; right: 12px; }

/* ==================== Toast (bottom-right) ==================== */
#toast-container {
    position: fixed; bottom: 1rem; right: 1rem; z-index: 100;
    display: flex; flex-direction: column; gap: 0.5rem; align-items: flex-end;
    pointer-events: none;
}
#toast-container .toast-item {
    pointer-events: auto;
    padding: 0.75rem 1rem;
    border-radius: 0.5rem; border: 1px solid;
    font-size: 0.875rem;
    max-width: 24rem;
    box-shadow: 0 10px 15px -3px rgba(0,0,0,0.4);
    transition: opacity .22s ease, transform .22s ease;
    opacity: 0; transform: translateY(8px);
}
#toast-container .toast-item.is-shown { opacity: 1; transform: translateY(0); }
#toast-container .toast-success { background: rgba(6,78,59,0.92); border-color: #047857; color: #d1fae5; }
#toast-container .toast-error   { background: rgba(127,29,29,0.92); border-color: #b91c1c; color: #fee2e2; }
#toast-container .toast-info    { background: rgba(31,41,55,0.95); border-color: #4b5563; color: #f3f4f6; }

/* ============================================================
 * Design System primitives — Fase C
 * Backing CSS for macros in app/templates/_components/*.j2.
 * ALL declarations reference var(--*) tokens (see tokens.css).
 * ============================================================ */

/* ── Card ─────────────────────────────────────────────────── */
.ds-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}
.ds-card--p-none { padding: 0; }
.ds-card--p-sm   { padding: var(--space-sm); }
.ds-card--p-md   { padding: var(--space-md); }
.ds-card--p-lg   { padding: var(--space-xl); }

/* ── Buttons ──────────────────────────────────────────────── */
.ds-btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--space-sm);
    padding: 8px 14px;
    border-radius: var(--radius-md);
    font-family: var(--font-sans);
    font-size: var(--text-md);
    font-weight: 500;
    line-height: var(--leading-tight);
    cursor: pointer;
    border: 1px solid transparent;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--transition), border-color var(--transition),
                color var(--transition), box-shadow var(--transition);
}
.ds-btn:focus-visible {
    outline: none;
    box-shadow: var(--shadow-focus-ring);
}
.ds-btn:disabled,
.ds-btn[aria-disabled="true"] {
    opacity: 0.55; cursor: not-allowed;
}
.ds-btn-icon { display: inline-flex; flex-shrink: 0; }
.ds-btn--sm { padding: 5px 10px; font-size: var(--text-sm); gap: var(--space-xs); }

.ds-btn--primary {
    background: var(--color-brand-hover);
    color: #fff;
    border-color: var(--color-brand-hover);
}
.ds-btn--primary:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--color-brand);
    border-color: var(--color-brand);
}

.ds-btn--secondary {
    background: var(--color-bg-elevated);
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}
.ds-btn--secondary:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--color-bg-nav);
    color: var(--color-text-primary);
}

.ds-btn--danger {
    background: var(--color-danger-bg-soft);
    color: var(--color-danger-fg);
    border-color: var(--color-danger-border);
}
.ds-btn--danger:hover:not(:disabled):not([aria-disabled="true"]) {
    background: rgba(127, 29, 29, 0.5);
    color: #fff;
}

.ds-btn--ghost {
    background: transparent;
    color: var(--color-text-tertiary);
    border-color: transparent;
}
.ds-btn--ghost:hover:not(:disabled):not([aria-disabled="true"]) {
    background: var(--color-bg-elevated);
    color: var(--color-text-primary);
}

/* ── Badges ───────────────────────────────────────────────── */
.ds-badge {
    display: inline-flex; align-items: center;
    gap: var(--space-xs);
    padding: 2px 8px;
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: var(--leading-tight);
    border-radius: var(--radius-full);
    border: 1px solid transparent;
    white-space: nowrap;
}
.ds-badge--md { padding: 4px 10px; font-size: var(--text-sm); }

.ds-badge--brand {
    background: var(--color-brand-bg-medium);
    color: var(--color-brand-light);
    border-color: rgba(16, 185, 129, 0.30);
}
.ds-badge--neutral {
    background: var(--color-bg-elevated);
    color: var(--color-text-tertiary);
    border-color: var(--color-border);
}
.ds-badge--success {
    background: var(--color-success-bg-soft);
    color: var(--color-success-fg);
    border-color: rgba(16, 185, 129, 0.30);
}
.ds-badge--warn {
    background: var(--color-warn-bg-soft);
    color: var(--color-warn-fg);
    border-color: rgba(251, 191, 36, 0.30);
}
.ds-badge--danger {
    background: var(--color-danger-bg-soft);
    color: var(--color-danger-fg);
    border-color: var(--color-danger-border);
}
.ds-badge--info {
    background: var(--color-info-bg-soft);
    color: var(--color-info-fg);
    border-color: var(--color-info-border);
}

/* ── Filter card ──────────────────────────────────────────── */
.ds-filter-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: var(--space-md);
    margin-bottom: var(--space-lg);
}
.ds-filter-card-row {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--space-sm);
}
.ds-filter-card-filters {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: var(--space-sm);
    flex: 1; min-width: 0;
}
.ds-filter-card-actions {
    display: flex; align-items: center;
    gap: var(--space-sm);
    flex-shrink: 0;
    margin-left: auto;
}
/* "Clear filters" — lives next to the filter pills (see the filter_clear macro),
   never in the actions block. Quiet so it doesn't compete with the pills, but
   the same 29px box as .filter-dd-trigger so the row keeps one baseline. */
.ds-filter-clear {
    display: inline-flex; align-items: center;
    gap: 6px;
    padding: 6px 10px;
    font-size: var(--text-sm);
    line-height: var(--leading-tight);
    color: var(--color-text-muted);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: color var(--transition-fast),
                background var(--transition-fast),
                border-color var(--transition-fast);
}
.ds-filter-clear:hover {
    color: var(--color-text-secondary);
    background: var(--color-bg-elevated);
    border-color: var(--color-border-subtle);
}
.ds-filter-clear svg { width: 14px; height: 14px; flex-shrink: 0; }
/* Nothing filtered = nothing to clear. Toggled by updateFilterClearVisibility()
   in app.js. Visible is the default so a JS failure degrades to the old
   always-on button rather than to no button at all. */
.ds-filter-clear--idle { display: none; }

/* ── Data table ───────────────────────────────────────────── */
.ds-table-wrap {
    /* NO overflow here on purpose. `position: sticky` anchors to the nearest
       scroll-container ancestor, and per spec setting `overflow-x: auto` forces
       the other axis off `visible` too — which silently made this wrap a scroll
       container the header stuck to instead of the viewport. Since the wrap is
       exactly as tall as its table it never scrolled, so the header just left
       with the page (measured -3,519px on /catalog).
       Horizontal scrolling is now opt-in via .is-scroll-x, applied by
       `initTableScrollX()` in app.js only when the table really is too wide. In
       the common case there is no scroll container at all and the sticky header
       pins to the top of the window as you scroll the page. */
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
}
/* Only when the table genuinely overflows. Sticky degrades here — a scroll
   container is unavoidable if you want horizontal scrolling — but this is the
   rare case (a wide table on a narrow screen, or /sites with every column on). */
.ds-table-wrap.is-scroll-x { overflow-x: auto; }
.ds-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-md);
    color: var(--color-text-primary);
}
.ds-table thead th {
    background: var(--color-bg-elevated);
    color: var(--color-text-tertiary);
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 10px 12px;
    border-bottom: 1px solid var(--color-border-subtle);
    text-align: left;
    user-select: none;
}
.ds-table--sticky thead th {
    position: sticky; top: 0; z-index: 10;
}
.ds-table tbody td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--color-border-subtle);
    vertical-align: middle;
}
.ds-table tbody tr:last-child td { border-bottom: none; }
.ds-table tbody tr:hover td { background: rgba(31, 41, 55, 0.4); }
.ds-table-empty {
    padding: var(--space-2xl) var(--space-lg) !important;
    text-align: center;
    color: var(--color-text-muted);
}

/* ── Pagination ───────────────────────────────────────────── */
.ds-pagination {
    display: flex; align-items: center; justify-content: space-between;
    flex-wrap: wrap; gap: var(--space-md);
    margin-top: var(--space-md);
    color: var(--color-text-tertiary);
    font-size: var(--text-md);
}
.ds-pagination-left,
.ds-pagination-right {
    display: flex; align-items: center;
    gap: var(--space-md);
    flex-wrap: wrap;
}
.ds-pagination-right { gap: var(--space-xs); }
.ds-pagination-pagesize {
    display: inline-flex; align-items: center;
    gap: var(--space-sm);
}
.ds-pagination-pagesize select {
    background: var(--color-bg-input);
    color: var(--color-text-primary);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius);
    padding: 4px 8px;
    font-size: var(--text-md);
    font-family: inherit;
    cursor: pointer;
}
.ds-pagination-pagesize select:hover { border-color: var(--color-border-hover); }
.ds-pagination-pagesize select:focus {
    outline: none;
    border-color: var(--color-brand);
    box-shadow: var(--shadow-focus-ring);
}

.ds-page-btn {
    min-width: 32px; height: 32px;
    padding: 0 var(--space-sm);
    background: transparent;
    color: var(--color-text-tertiary);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius);
    cursor: pointer;
    font-family: inherit;
    font-size: var(--text-md);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.ds-page-btn:hover:not(:disabled) {
    background: var(--color-bg-elevated);
    color: var(--color-text-primary);
}
.ds-page-btn.is-active {
    background: var(--color-brand-bg-medium);
    color: var(--color-brand-light);
    border-color: rgba(16, 185, 129, 0.40);
}
.ds-page-btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── List card (used in /lists, /portal/lists) ────────────── */
.ds-list-card {
    background: var(--color-bg-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: var(--space-xl);
    transition: border-color var(--transition), box-shadow var(--transition);
}
.ds-list-card:hover {
    border-color: var(--color-border);
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
}
.ds-list-card-row {
    display: flex; align-items: center;
    gap: var(--space-md);
    padding-top: var(--space-md);
    margin-top: var(--space-md);
    border-top: 1px solid var(--color-border-subtle);
}

/* ── Inline action link inside list cards (not a full button) */
.ds-action-link {
    display: inline-flex; align-items: center;
    font-size: var(--text-md);
    font-weight: 500;
    color: var(--color-text-tertiary);
    cursor: pointer;
    background: none; border: none; padding: 0;
    transition: color var(--transition);
    text-decoration: none;
}
.ds-action-link:hover { color: var(--color-text-primary); }
.ds-action-link--primary { color: var(--color-brand-soft); }
.ds-action-link--primary:hover { color: var(--color-brand-light); }
.ds-action-link--success { color: var(--color-success-fg); }
.ds-action-link--success:hover { color: #d1fae5; }
.ds-action-link--warn    { color: var(--color-warn-fg); }
.ds-action-link--warn:hover    { color: #fef3c7; }
.ds-action-link--danger  { color: var(--color-danger-fg); }
.ds-action-link--danger:hover  { color: #fee2e2; }
.ds-action-link--xs { font-size: var(--text-sm); font-weight: 500; }

/* ── Public link snippet inside list cards ────────────────── */
.ds-public-link {
    background: var(--color-bg-elevated);
    border-radius: var(--radius-md);
    padding: var(--space-sm) var(--space-md);
}
.ds-public-link-label {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin-bottom: 2px;
}
.ds-public-link-row {
    display: flex; align-items: center;
    gap: var(--space-sm);
}
.ds-public-link-code {
    font-family: ui-monospace, SFMono-Regular, monospace;
    font-size: var(--text-xs);
    color: var(--color-brand-soft);
    flex: 1; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── Saved filters (internal /sites) ───────────────────────
   Pill + panel driven by app/static/js/saved_filters.js. The pill reuses the
   .filter-dd shell every other filter pill uses (so it opens/closes with them);
   only the panel body below is new surface. */
.filter-dd.sf-pill .sf-trigger-label {
    display: inline-block;
    max-width: 150px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    vertical-align: bottom;
}
/* FilterDropdown.refresh() writes getCount() into .filter-dd-badge. The saved
   pill carries the filter *name* in its label, so a numeric "1" adds nothing —
   keep the element for structural parity with the other pills, hide it. */
.filter-dd.sf-pill .filter-dd-badge { display: none; }

/* "Modified" = a saved filter is applied but the current filters no longer
   match it. Amber, so it reads as a warning next to the emerald fd-active. */
.filter-dd.sf-pill .sf-mod-chip { display: none; }
.filter-dd.sf-pill.sf-modified .sf-mod-chip {
    display: inline-flex; align-items: center;
    height: 16px; padding: 0 6px;
    border-radius: var(--radius-full);
    background: var(--color-warn-bg-soft);
    color: var(--color-warn-strong);
    font-size: 9.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.04em;
}
.filter-dd.sf-pill.sf-modified .filter-dd-trigger {
    border-color: rgba(251, 191, 36, 0.45);
    background: var(--color-warn-bg-soft);
    color: var(--color-warn-fg);
}
.filter-dd.sf-pill.sf-modified .filter-dd-plus {
    background: var(--color-warn-strong);
    color: #422006;
}

.filter-dd-panel.sf-panel { min-width: 300px; max-width: 340px; padding: 10px; }

.sf-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-sm);
    margin-bottom: var(--space-xs);
}
.sf-head-label { margin-bottom: 0; color: var(--color-text-muted); }
.sf-link {
    background: none; border: none; padding: 0;
    font-family: inherit; font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    cursor: pointer;
}
.sf-link:hover { color: var(--color-text-primary); text-decoration: underline; }

.sf-list {
    display: flex; flex-direction: column; gap: 2px;
    max-height: 240px; overflow-y: auto;
    margin: 0 -2px; padding: 0 2px;
}
.sf-list::-webkit-scrollbar { width: 6px; }
.sf-list::-webkit-scrollbar-thumb { background: var(--color-border-hover); border-radius: 3px; }

.sf-empty {
    padding: 14px 8px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}
.sf-empty--error { color: var(--color-danger-fg); }

.sf-row {
    display: flex; align-items: center; gap: var(--space-xs);
    padding-right: var(--space-xs);
    border-radius: var(--radius);
    transition: background var(--transition-fast);
}
.sf-row:hover { background: var(--color-bg-row-hover); }
.sf-row.is-active { background: var(--color-brand-bg-soft); }
.sf-row-main {
    flex: 1; min-width: 0;
    display: flex; align-items: center;
    gap: var(--space-sm);
    padding: 7px var(--space-sm);
    background: none; border: none;
    text-align: left; cursor: pointer;
    font-family: inherit; font-size: var(--text-md);
    color: var(--color-text-secondary);
}
.sf-row.is-active .sf-row-main { color: var(--color-brand-light); font-weight: 500; }
.sf-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sf-row-author {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px;
    border-radius: var(--radius-full);
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
    font-size: 9.5px; font-weight: 700;
    cursor: default;
}
.sf-row-actions {
    flex-shrink: 0;
    display: inline-flex; gap: var(--space-2xs);
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.sf-row:hover .sf-row-actions,
.sf-row:focus-within .sf-row-actions { opacity: 1; }
.sf-icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; padding: 0;
    background: transparent; border: none;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.sf-icon-btn svg { width: 14px; height: 14px; }
.sf-icon-btn:hover { background: var(--color-bg-elevated); color: var(--color-text-primary); }
.sf-icon-btn--danger:hover { background: var(--color-danger-bg-soft); color: var(--color-danger-fg); }
.sf-icon-btn--ok:hover { background: var(--color-success-bg-soft); color: var(--color-success-fg); }
.sf-row.is-editing { gap: var(--space-2xs); padding: 2px; }

.sf-divider {
    height: 1px;
    background: var(--color-border-subtle);
    margin: var(--space-md) 0;
}

.sf-save-label {
    display: block;
    font-size: 10px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--color-text-muted);
    margin-bottom: var(--space-xs);
}
.sf-save-row { display: flex; align-items: center; gap: var(--space-xs); }
.sf-input {
    flex: 1; min-width: 0;
    padding: 6px var(--space-sm);
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius);
    color: var(--color-text-primary);
    font-family: inherit; font-size: var(--text-md);
    transition: border-color var(--transition), background var(--transition);
}
.sf-input::placeholder { color: var(--color-text-placeholder); }
.sf-input:hover { border-color: var(--color-border-hover); }
.sf-input:focus { outline: none; border-color: var(--color-brand); background: var(--color-bg-nav); }

.sf-modified-row {
    margin-bottom: var(--space-md);
    padding: var(--space-sm);
    background: var(--color-warn-bg-soft);
    border: 1px solid rgba(251, 191, 36, 0.25);
    border-radius: var(--radius);
}
.sf-note {
    margin: 0 0 var(--space-xs);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-warn-fg);
}
.sf-modified-actions { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.sf-modified-actions .ds-btn { flex: 1 1 auto; }

/* ── Catalog visitor lists (public /catalog, unlocked only) ─
   Pill + panel + two modals driven by app/static/js/catalog_lists.js. The pill
   reuses the .filter-dd shell every filter pill uses (so FilterDropdown owns
   open/close + ESC); everything below is new surface. Deliberately NOT the
   .sf-* rules above: the saved-filters panel is an internal /sites surface and
   the two must be able to drift apart.

   Nothing here is scoped to the locked state on purpose — in the locked state
   the controller is never instantiated, so none of these elements have content
   (and .catalog-locked below hides the selection column outright). */
.filter-dd.cvl-pill .cvl-trigger-label {
    display: inline-block;
    max-width: 150px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    vertical-align: bottom;
}
.filter-dd.cvl-pill .cvl-count {
    align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: var(--radius-full);
    background: var(--color-bg-elevated);
    color: var(--color-text-tertiary);
    font-weight: 700; font-size: 10px;
    margin-left: 2px;
}
.filter-dd.cvl-pill .cvl-count:not(.hidden) { display: inline-flex; }
/* A list is open/last used: tint the pill without borrowing .fd-active, which
   means "this filter is filtering" everywhere else. */
.filter-dd.cvl-pill.cvl-has-active .filter-dd-trigger {
    border-color: rgba(16, 185, 129, 0.35);
    color: var(--color-brand-light);
}
.filter-dd.cvl-pill.cvl-has-active .cvl-count {
    background: var(--color-brand-bg-medium);
    color: var(--color-brand-light);
}

.filter-dd-panel.cvl-panel { min-width: 300px; max-width: 340px; padding: 10px; }

.cvl-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-sm);
    margin-bottom: var(--space-xs);
}
.cvl-head-label {
    font-size: 10px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--color-text-muted);
}
.cvl-head-count {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.cvl-list {
    display: flex; flex-direction: column; gap: 2px;
    max-height: 260px; overflow-y: auto;
    margin: 0 -2px; padding: 0 2px;
}
.cvl-list::-webkit-scrollbar { width: 6px; }
.cvl-list::-webkit-scrollbar-thumb { background: var(--color-border-hover); border-radius: 3px; }

.cvl-empty {
    padding: 14px 8px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}
.cvl-empty--error { color: var(--color-danger-fg); }

.cvl-row {
    display: flex; align-items: center; gap: var(--space-xs);
    padding-right: var(--space-xs);
    border-radius: var(--radius);
    transition: background var(--transition-fast);
}
.cvl-row:hover { background: var(--color-bg-row-hover); }
.cvl-row.is-active { background: var(--color-brand-bg-soft); }
.cvl-row.is-editing { gap: var(--space-2xs); padding: 2px; }
.cvl-row-main {
    flex: 1; min-width: 0;
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-sm);
    padding: 7px var(--space-sm);
    background: none; border: none;
    text-align: left; cursor: pointer;
    font-family: inherit; font-size: var(--text-md);
    color: var(--color-text-secondary);
}
.cvl-row.is-active .cvl-row-main { color: var(--color-brand-light); font-weight: 500; }
.cvl-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cvl-row-count {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 18px; padding: 0 6px;
    border-radius: var(--radius-full);
    background: var(--color-bg-elevated);
    color: var(--color-text-muted);
    font-size: 10px; font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.cvl-row-actions {
    flex-shrink: 0;
    display: inline-flex; gap: var(--space-2xs);
    opacity: 0;
    transition: opacity var(--transition-fast);
}
.cvl-row:hover .cvl-row-actions,
.cvl-row:focus-within .cvl-row-actions { opacity: 1; }
.cvl-icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; padding: 0;
    background: transparent; border: none;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.cvl-icon-btn svg { width: 14px; height: 14px; }
.cvl-icon-btn:hover { background: var(--color-bg-elevated); color: var(--color-text-primary); }
.cvl-icon-btn--danger:hover { background: var(--color-danger-bg-soft); color: var(--color-danger-fg); }
.cvl-icon-btn--ok:hover { background: var(--color-success-bg-soft); color: var(--color-success-fg); }

.cvl-divider {
    height: 1px;
    background: var(--color-border-subtle);
    margin: var(--space-md) 0;
}
.cvl-label {
    display: block;
    font-size: 10px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--color-text-muted);
    margin-bottom: var(--space-xs);
}
.cvl-create-row { display: flex; align-items: center; gap: var(--space-xs); }
.cvl-input {
    flex: 1; min-width: 0;
    padding: 6px var(--space-sm);
    background: var(--color-bg-input);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius);
    color: var(--color-text-primary);
    font-family: inherit; font-size: var(--text-md);
    transition: border-color var(--transition), background var(--transition);
}
.cvl-input::placeholder { color: var(--color-text-placeholder); }
.cvl-input:hover { border-color: var(--color-border-hover); }
.cvl-input:focus { outline: none; border-color: var(--color-brand); background: var(--color-bg-nav); }
.cvl-input:disabled { opacity: 0.55; cursor: not-allowed; }
.cvl-note {
    margin: var(--space-xs) 0 0;
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

/* Selection bar hint — "select all" only ever covers the rows already loaded
   by the infinite scroll, and the copy must not imply otherwise. */
.cvl-selection-note {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/* ── Modals (native <dialog>) ─────────────────────────────── */
dialog.cvl-modal {
    border: none; padding: 0;
    width: 100%;
    max-height: 88vh;
    background: var(--color-bg-elevated);
    color: var(--color-text-primary);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-popover-lg);
    overflow: hidden;
}
dialog.cvl-modal::backdrop { background: rgba(0, 0, 0, 0.6); }
dialog.cvl-modal--sm { max-width: 26rem; }
dialog.cvl-modal--lg { max-width: 62rem; }
/* The flex column is what lets the detail table scroll inside a capped modal
   instead of pushing the footer off-screen. */
dialog.cvl-modal[open] { display: flex; flex-direction: column; }

.cvl-modal-head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-lg) var(--space-xl);
    border-bottom: 1px solid var(--color-border-subtle);
    flex-shrink: 0;
}
.cvl-modal-heading { min-width: 0; }
.cvl-modal-title {
    margin: 0;
    font-size: var(--text-lg); font-weight: 700;
    color: var(--color-text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cvl-modal-sub {
    margin: 2px 0 0;
    font-size: var(--text-md);
    color: var(--color-text-tertiary);
}
.cvl-modal-close {
    flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; padding: 0;
    background: transparent; border: none;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.cvl-modal-close svg { width: 16px; height: 16px; }
.cvl-modal-close:hover { background: var(--color-bg-nav); color: var(--color-text-primary); }
.cvl-modal-body {
    padding: var(--space-lg) var(--space-xl) var(--space-xl);
    overflow-y: auto;
    min-height: 0;
}
.cvl-modal-body--flush { padding: 0; }
.cvl-modal-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-md);
    padding: var(--space-md) var(--space-xl);
    border-top: 1px solid var(--color-border-subtle);
    flex-shrink: 0;
}
.cvl-total { font-size: var(--text-md); color: var(--color-text-tertiary); }
.cvl-total strong { color: var(--color-text-primary); }

.cvl-options {
    display: flex; flex-direction: column; gap: var(--space-xs);
    max-height: 40vh; overflow-y: auto;
}
.cvl-option {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-md);
    width: 100%;
    padding: 10px var(--space-md);
    background: transparent;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    color: var(--color-text-primary);
    font-family: inherit; font-size: var(--text-md);
    text-align: left; cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.cvl-option:hover:not(:disabled) {
    background: var(--color-bg-nav);
    border-color: var(--color-brand-bg-medium);
}
.cvl-option:disabled { opacity: 0.5; cursor: not-allowed; }
.cvl-option-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cvl-option-meta {
    flex-shrink: 0;
    display: inline-flex; align-items: center; gap: var(--space-sm);
}
.cvl-option-note { font-size: var(--text-xs); color: var(--color-warn-fg); }
.cvl-option-count { font-size: var(--text-xs); color: var(--color-text-muted); }

.cvl-detail-wrap { max-height: 58vh; overflow: auto; }
.cvl-detail-wrap .ds-table thead th { background: var(--color-bg-nav); }

/* ── Locked catalogue: no selection, ever ─────────────────────
   The locked teaser is hand-rendered without a selection cell, and the
   controller (the only thing that can create checkboxes or show the
   selection bar) is never instantiated. This rule is the third layer: while
   #catalog-table carries .catalog-locked the select column cannot be seen at
   all, including the select-all box the data_table macro renders in <thead>.
   The class is ON in the template and only startUnlocked() removes it, so a
   JS failure fails closed. */
.catalog-locked [data-col="select"] { display: none; }

/* Remove-from-list action in the visitor list detail modal. Quiet until the row
   is hovered, so a read-through of the list is not peppered with red. */
.cvl-row-remove-cell { width: 40px; text-align: right; }
.cvl-row-remove {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text-disabled);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--transition-fast), color var(--transition-fast),
                background var(--transition-fast), border-color var(--transition-fast);
}
.cvl-row-remove svg { width: 13px; height: 13px; }
.cvl-detail-wrap tr:hover .cvl-row-remove,
.cvl-row-remove:focus-visible { opacity: 1; }
.cvl-row-remove:hover {
    color: #fecaca;
    background: rgba(248, 113, 113, 0.16);
    border-color: rgba(248, 113, 113, 0.35);
}
