/* ============================================================================
   The shared component layer.

   Why this file exists. Before it, the same component was declared in several
   page stylesheets at once: .section-eyebrow in four, .empty-state and
   .btn-primary in three, .dash-card, .filter-tab* and the search list in two.
   Thirty-three selectors were defined in more than one page sheet, and almost
   none of the copies matched. That is what "the pages drifted" actually meant.

   How it relates to ui-system.css. That file is also a shared layer, but it
   works by enumeration: one :is() listing forty card class names, another
   listing sixteen label class names, and so on. It normalises whatever a page
   happens to have called things. It is effective and it stays, but it has two
   costs — every new component means editing an enumeration, and the
   :is(a, b) :is(c, d.e) form carries (0,3,0) specificity, so page CSS below it
   loses silently. (That is not hypothetical: it made a disabled Save button
   render as a live white one.)

   This file is the other half: components a page *uses* rather than ones it
   gets normalised into. It loads after ui-system.css so a named component wins,
   and it is where a shared thing belongs once more than one page needs it.

   The rule going forward: if two pages need it, it lives here. If one page
   needs it, it lives in that page's stylesheet with a page-specific prefix.
   ========================================================================= */

/* ----------------------------------------------------------- page header ---
   Lived in base.html's inline <style> and was copied into admin.css, which
   differed only by dropping the rule below it. Every authenticated surface
   opens with this. */

.page-header {
    margin-bottom: 32px;
    padding-bottom: 24px;
    border-bottom: 1px solid var(--border);
}

.page-header h1 {
    font-size: 3rem;
    font-weight: 800;
    margin-bottom: 12px;
    letter-spacing: -0.03em;
    color: #f2f5fa;
}

/* The full stop is the mark. It is a child of the h1 so it inherits the
   tracking; giving it its own flex slot is what once pushed it a space away
   from the word. */
.page-header h1 span,
.gradient-dot {
    color: var(--primary);
}

.page-header p {
    font-size: 1.125rem;
    line-height: 1.6;
    color: var(--text-light);
    max-width: 68ch;
}

@media (max-width: 768px) {
    .page-header {
        margin-bottom: 24px;
        padding-bottom: 16px;
    }

    .page-header h1 { font-size: 2rem; }
    .page-header p { font-size: 1rem; }
}

/* -------------------------------------------------------------- eyebrow ---
   ui-system.css already owns the type (size, weight, tracking, caps, colour)
   through its label enumeration. All four page copies were re-declaring that
   type and losing, so the only thing they actually contributed was the layout
   below and a margin that disagreed between them. */

.section-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin: var(--section-gap) 0 var(--space-4);
}

.section-eyebrow::before {
    content: '';
    width: 20px;
    height: 1px;
    background: rgba(255, 255, 255, 0.25);
}

.section-eyebrow:first-child { margin-top: 0; }

/* ---------------------------------------------------------- empty state ---
   Three pages had three different components under this one name: centred
   text (moderation), centred text with a big faded icon (music), and a
   bordered card with a heading and actions (server select). They are one
   component at three weights, so it is one component with a modifier. */

/* One empty state, two densities.

   Eleven of these were shipping. Measured paddings for the same job: 24px,
   32/24, 40/24, 40/8, 48/24, 64/24, 12px, 10/12 with its own border and
   radius, 18/16/22, and two 0px flex rows -- seven distinct paddings across
   nine class names. The bespoke names stay for now, because most of this
   markup is written by JS and renaming it is a separate change, but they all
   render as one component from here.

   `.is-inline` is the compact density, for an empty state inside a dropdown,
   a panel or a list rather than one owning a page. */
.empty-state,
.apps-empty,
.perm-empty,
.promo-empty {
    text-align: center;
    padding: 40px 24px;
    color: var(--text-light);
}

.empty-state.is-inline,
.cp-empty,
.pl-empty-note,
.cmdk-empty {
    text-align: center;
    padding: 18px 16px;
    color: var(--text-light);
    font-size: var(--text-md);
}

.empty-state.is-boxed {
    grid-column: 1 / -1;
    padding: 48px 24px;
    background: var(--surface-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
}

/* Small and quiet. A 5rem glyph at 0.2 opacity is a decoration pretending to
   be an illustration; it fills the box without saying anything. */
.empty-state > i,
.apps-empty > i,
.perm-empty > i,
.promo-empty > i {
    display: block;
    margin-bottom: 12px;
    font-size: 1.5rem;
    color: rgba(255, 255, 255, 0.28);
}

.empty-state h3,
.empty-state-title {
    margin-bottom: 6px;
    font-size: 1rem;
    font-weight: 700;
    color: var(--text);
}

.empty-state p {
    max-width: 46ch;
    margin: 0 auto;
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--text-light);
}

.empty-state-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
    flex-wrap: wrap;
    margin-top: 20px;
}

/* ------------------------------------------------------ segmented tabs ---
   AGENTS.md names this as the pattern for any segmented control: an absolutely
   positioned indicator on z-index 0 that slides on a cubic-bezier, with the
   labels above it. Marketplace and server select each had their own copy. */

.filter-tabs,
.cat-tabs {
    position: relative;
    display: flex;
    gap: 2px;
    padding: 4px;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-tile);
}

/* The highlight has to be the same shape as the track it slides in and the
   tab it sits behind. Both of those became capsules; this stayed at 8px, so
   every segmented control on the site had a square-cornered white block
   inside a rounded pill. */
.filter-tab-indicator,
.cat-indicator {
    position: absolute;
    top: 4px;
    bottom: 4px;
    left: 0;
    width: 0;
    z-index: 0;
    border-radius: var(--control-radius-pill, 999px);
    background: var(--primary);
    transform: translateX(0);
    transition: transform 0.32s var(--ease-emphasis), width 0.32s var(--ease-emphasis);
    pointer-events: none;
}

.filter-tab,
.cat-tab {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text-light);
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.2s var(--ease-standard);
}

.filter-tab:hover,
.cat-tab:hover { color: var(--text); }

/* The indicator underneath is --primary, which is white, so the selected
   label takes --on-accent. */
.filter-tab.active,
.cat-tab.active { color: var(--on-accent); }

@media (max-width: 768px) {
    .filter-tabs,
    .cat-tabs {
        width: 100%;
        overflow-x: auto;
        flex-wrap: nowrap;
    }

    .filter-tab,
    .cat-tab {
        flex: 1;
        justify-content: center;
        min-width: 0;
        padding: 8px 10px;
    }
}

/* ------------------------------------------------------------ dash card ---
   ui-system.css already gives every card its radius, border and background.
   What was duplicated between dashboard.css and music-dashboard.css is the
   header/body structure, at two padding scales that had no reason to differ.

   theme.css then turns every .dash-card into a band: no background, no box,
   one hairline on top, `padding: 22px 0 26px`. That pass had not reached the
   header and body, which kept 22px and 24px of horizontal padding from when
   there was a box to sit inside — so content floated an inch clear of the
   rule that was supposed to be aligning it, and the two indents did not even
   match each other. In a band the padding is vertical only. */

.dash-card { overflow: hidden; }

/* Descendant form on purpose. ui-system.css sets this padding through
   :is(.main-content, .app-container) :is(.dash-card-header, .card-header,
   .pl-modal-header, .modal-header) at (0,2,0), and the last two entries are
   modals — which still have boxes and still want the inset. So the token stays
   as it is and only a header sitting inside a band is pulled flush, which
   needs (0,2,0) and a later sheet rather than a flat class. */
.dash-card .dash-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 0;
    padding: 0 0 14px;
}

.dash-card .dash-card-body { padding: 0; }

/* Layout only. Size, weight and tracking belong to ui-system.css's title
   contract; the 17px/800 here was dead. */
.dash-card-title {
    display: flex;
    align-items: center;
    gap: 10px;
    color: #fff;
}

.dash-card-title i {
    font-size: 0.9375rem;
    color: var(--primary);
}


@media (max-width: 768px) {
    .dash-card-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
    }
}

/* --------------------------------------------------------------- switch ---
   Every settings surface in the product is rows of labelled toggles, so this
   is the single switch. */

.toggle-switch,
.bb-switch {
    position: relative;
    flex-shrink: 0;
    width: 44px;
    height: 24px;
    border-radius: 999px;
    background: var(--surface-3);
    border: 1px solid var(--border-strong);
    cursor: pointer;
    transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard);
}

.toggle-switch::after,
.bb-switch::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.45);
    transition: transform 0.2s var(--ease-emphasis), background 0.2s var(--ease-standard);
}

.toggle-switch.active,
.bb-switch.active,
.bb-switch[aria-checked="true"] {
    background: var(--primary);
    border-color: var(--primary);
}

/* The Dark Ink Rule. The track fills with --primary, which is white on every
   authenticated page, so the knob is --on-accent. moderation.css had this as
   #fff, which made an enabled toggle a blank white lozenge with no knob in
   it — the same failure as the white tick on a white checkbox. */
.toggle-switch.active::after,
.bb-switch.active::after,
.bb-switch[aria-checked="true"]::after {
    transform: translateX(20px);
    background: var(--on-accent);
}

/* The checkbox-driven form of the same switch. features.html and
   settings.html both use <label class="toggle"><input><span class="slider">,
   but .toggle and .slider were declared only in features.css — and
   settings.html links no page stylesheet at all, so every toggle on the
   settings page rendered as a bare span with a raw checkbox beside it. It is
   a shared control used by two pages, so it belongs here. */

.toggle {
    position: relative;
    flex-shrink: 0;
    width: 44px;
    height: 24px;
}

.toggle input {
    position: absolute;
    width: 0;
    height: 0;
    opacity: 0;
}

.slider {
    position: absolute;
    inset: 0;
    border-radius: 999px;
    background: var(--surface-3);
    border: 1px solid var(--border-strong);
    cursor: pointer;
    transition: background 0.2s var(--ease-standard), border-color 0.2s var(--ease-standard);
}

.slider::before {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.45);
    transition: transform 0.22s var(--ease-emphasis), background 0.2s var(--ease-standard);
}

.toggle input:checked + .slider {
    background: var(--primary);
    border-color: var(--primary);
}

/* The Dark Ink Rule again, and the third instance of it. The track fills with
   --primary, so a `background: white` knob disappears into it — a checked
   feature toggle was a blank white lozenge. The audit script missed this one
   because it is written as a sibling combinator rather than an .active class. */
.toggle input:checked + .slider::before {
    transform: translateX(20px);
    background: var(--on-accent);
}

.toggle input:disabled + .slider {
    opacity: 0.5;
    cursor: not-allowed;
}

.toggle input:focus-visible + .slider {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* ------------------------------------------------------------ field row ---
   Label, description, control. The unit every settings page is built from. */

.field-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 14px 0;
    border-bottom: 1px solid var(--border);
}

.field-row:last-child { border-bottom: none; }

.field-label {
    flex: 1;
    min-width: 0;
}

.field-label strong {
    display: block;
    margin-bottom: 2px;
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--text);
}

/* A control with no description is a control you have to guess at. */
.field-label span {
    display: block;
    font-size: 0.8125rem;
    line-height: 1.45;
    color: rgba(255, 255, 255, 0.38);
}

.field-control {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-shrink: 0;
}

@media (max-width: 600px) {
    .field-row {
        align-items: flex-start;
        flex-direction: column;
        gap: 10px;
    }

    .field-control { width: 100%; }
}


/* ----------------------------------------------------------------- toast ---
   Three copies (admin, features, subscribe) of one component, drifted: two
   used --card-bg and a hardcoded 8px radius, one used --surface-1 and
   --radius-tile, and the error variant had three different borders. The
   token-consuming version wins.

   Not extracted, and deliberately: .status-dot and .search-input each appear
   on three pages under one name while being three genuinely different
   components (a labelled status row vs a 10px dot vs a pulsing badge; a
   command-palette field with a dropdown vs an inline field in a search row).
   Collapsing a name collision breaks both sides — those want renaming, not
   sharing. */

.toast {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 2000;
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 240px;
    padding: 12px 16px;
    background: var(--surface-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-tile);
    box-shadow: var(--shadow-lg);
    font-size: 0.875rem;
    color: var(--text);
    opacity: 0;
    transform: translateY(20px);
    transition: opacity 0.2s var(--ease-standard), transform 0.2s var(--ease-standard);
    pointer-events: none;
}

.toast.show {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

.toast-success {
    border-color: rgba(16, 185, 129, 0.35);
    background: rgba(16, 185, 129, 0.06);
}

.toast-success i { color: var(--success); }

.toast-error {
    border-color: rgba(239, 68, 68, 0.35);
    background: rgba(239, 68, 68, 0.06);
}

.toast-error i { color: var(--danger); }


/* ----------------------------------------------------------------- modal ---
   ui-system.css already gives .auto-modal / .auto-modal-box their overlay,
   surface and radius. Automation and marketplace then each declared the whole
   header/body/footer skeleton again, identically apart from four deliberate
   differences (title size, box width, whether the body is a flex column, and
   whether the footer stretches its buttons or right-aligns them). The skeleton
   lives here; those four stay with their pages. */

.auto-modal { display: none; z-index: 10000; }

.auto-modal-box {
    max-width: 540px;
    max-height: 90vh;
    opacity: 0;
    transform: scale(0.96) translateY(8px);
    transition: transform 0.3s var(--ease-standard), opacity 0.3s var(--ease-standard);
}

.auto-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 20px 24px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
}

.auto-modal-header h2 {
    margin: 0;
    font-size: 1.25rem;
    font-weight: 800;
    line-height: 1.2;
    letter-spacing: -0.02em;
    color: #f2f5fa;
}

.auto-modal-close {
    flex-shrink: 0;
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: 10px;
    background: transparent;
    color: var(--text-light);
    font-size: 1.25rem;
    cursor: pointer;
    transition: background 0.2s var(--ease-standard), color 0.2s var(--ease-standard);
}

.auto-modal-close:hover {
    background: rgba(239, 68, 68, 0.1);
    color: var(--danger);
}

.auto-modal-body {
    padding: 24px;
    overflow-y: auto;
    min-height: 0;
}

.auto-modal-footer {
    display: flex;
    gap: 12px;
    padding: 16px 24px;
    border-top: 1px solid var(--border);
    flex-shrink: 0;
}
