/* ==========================================================================
   Editorial Command Centre — component recipes
   docs/design-system.md §7 base layer + §9.1–§9.16 primitives.

   The spec ships these as Tailwind utility strings. This project has no
   Tailwind, so each recipe is written out as a class with the SAME computed
   values — every length, duration, opacity and colour is the spec's, converted
   from Tailwind's 0.25rem scale (h-9 = 2.25rem, px-2.5 = 0.625rem, and so on).

   Class names carry an `ec-` prefix because five legacy names (.btn, .badge,
   .stat, .avatar, .empty) are still live in theme.css until Phase 4 migrates
   the pages off them. The spec names no CSS classes for components, so the
   prefix costs no fidelity.

   Load order: tokens.css → theme.css → components.css (this file wins).
   ========================================================================== */

/* ── §7 base layer ──────────────────────────────────────────────────────────
   Parked during Phase 1 because it changes how every page looks. Unparked now.
   ------------------------------------------------------------------------- */

*, *::before, *::after { border-color: var(--rule); }

html { -webkit-text-size-adjust: 100%; }

body {
    background-color: var(--paper);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: var(--text-body);
    line-height: var(--text-body-lh);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Inter: contextual alternates, disambiguated 1/l, open digits */
    font-feature-settings: "cv05" 1, "cv08" 1, "ss03" 1;
}

h1, h2, h3, h4 { text-wrap: balance; }
p { text-wrap: pretty; }

/* One global ring. A double ring reads on any background, light or dark,
   without per-component work (§15). */
:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
}

::selection { background-color: var(--coral); color: #fff; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    /* the 3px paper border is what turns a 10px bar into a thin floating pill */
    background: var(--rule-strong);
    border: 3px solid var(--paper);
    border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover { background: var(--ink-faint); }

/* ── Type-scale helpers ─────────────────────────────────────────────────── */

.font-serif { font-family: var(--font-serif); }
.font-sans  { font-family: var(--font-sans); }
.font-mono  { font-family: var(--font-mono); }

.text-display { font-size: var(--text-display); line-height: var(--text-display-lh); letter-spacing: var(--text-display-ls); }
.text-figure  { font-size: var(--text-figure);  line-height: var(--text-figure-lh);  letter-spacing: var(--text-figure-ls); }
.text-h1      { font-size: var(--text-h1);      line-height: var(--text-h1-lh);      letter-spacing: var(--text-h1-ls); }
.text-h2      { font-size: var(--text-h2);      line-height: var(--text-h2-lh);      letter-spacing: var(--text-h2-ls); }
.text-h3      { font-size: var(--text-h3);      line-height: var(--text-h3-lh);      letter-spacing: var(--text-h3-ls); }
.text-body    { font-size: var(--text-body);    line-height: var(--text-body-lh); }
.text-sm      { font-size: var(--text-sm);      line-height: var(--text-sm-lh); }
.text-xs      { font-size: var(--text-xs);      line-height: var(--text-xs-lh); }

.text-ink-soft  { color: var(--ink-soft); }
.text-ink-mute  { color: var(--ink-mute); }
.text-ink-faint { color: var(--ink-faint); }

/* ── §9.1 Button ────────────────────────────────────────────────────────────
   Solid fills take -strong, never the base: bg-coral carries white type at
   3.5:1, bg-coral-strong at 5.14:1.
   ------------------------------------------------------------------------- */

.ec-btn {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
    user-select: none;
    align-items: center;
    justify-content: center;
    white-space: nowrap;
    font-family: var(--font-sans);
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    /* An explicit property list, never `all` — animating `all` would animate
       the button's width when the loading spinner swaps in. */
    transition: background-color 160ms var(--ease-out-editorial),
                border-color 160ms var(--ease-out-editorial),
                box-shadow 160ms var(--ease-out-editorial),
                color 160ms var(--ease-out-editorial);
}

.ec-btn svg { flex-shrink: 0; }

.ec-btn:disabled,
.ec-btn[aria-disabled="true"] {
    pointer-events: none;
    opacity: 0.45;
}

/* sizes */
.ec-btn--sm {
    height: 1.75rem;
    padding-inline: 0.625rem;
    font-size: var(--text-xs);
    gap: 0.375rem;
    border-radius: var(--radius-sm);
}
.ec-btn--md {
    height: 2.25rem;
    padding-inline: 0.875rem;
    font-size: var(--text-sm);
    gap: 0.5rem;
    border-radius: var(--radius-md);
}
.ec-btn--sm svg { width: 0.875rem; height: 0.875rem; }
.ec-btn--md svg { width: 1rem; height: 1rem; }

/* icon-only: square, and every one requires an aria-label */
.ec-btn--icon.ec-btn--sm { width: 1.75rem; padding-inline: 0; }
.ec-btn--icon.ec-btn--md { width: 2.25rem; padding-inline: 0; }

/* variants */
.ec-btn--primary {
    background-color: var(--coral-strong);
    color: #fff;
    border: 1px solid transparent;
}
.ec-btn--primary:hover,
.ec-btn--primary:active {
    background-color: var(--coral-hover);
    box-shadow: var(--shadow-raise);
}

.ec-btn--secondary {
    background-color: var(--teal-strong);
    color: #fff;
    border: 1px solid transparent;
}
.ec-btn--secondary:hover,
.ec-btn--secondary:active {
    background-color: var(--teal-hover);
    box-shadow: var(--shadow-raise);
}

.ec-btn--ghost {
    background-color: var(--surface);
    color: var(--ink);
    border: 1px solid var(--rule);
}
.ec-btn--ghost:hover {
    border-color: var(--rule-strong);
    background-color: var(--surface-alt);
    box-shadow: var(--shadow-raise);
}

.ec-btn--quiet {
    background-color: transparent;
    color: var(--ink-soft);
    border: 1px solid transparent;
}
.ec-btn--quiet:hover {
    color: var(--ink);
    background-color: var(--surface-alt);
}

.ec-btn--danger {
    background-color: var(--crimson-strong);
    color: #fff;
    border: 1px solid transparent;
}
.ec-btn--danger:hover,
.ec-btn--danger:active {
    background-color: var(--crimson-hover);
    box-shadow: var(--shadow-raise);
}

/* Loading keeps the label in place and overlays a centred spinner, so the
   button's width never jumps. */
.ec-btn.is-loading .ec-btn__label { visibility: hidden; }
.ec-btn__spinner {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
}
.ec-btn__spinner::after {
    content: "";
    width: 0.875rem;
    height: 0.875rem;
    border: 1.5px solid currentColor;
    border-top-color: transparent;
    border-radius: 9999px;
    animation: ec-spin 620ms linear infinite;
}

@keyframes ec-spin { to { transform: rotate(360deg); } }

/* ── §9.2 Badge ─────────────────────────────────────────────────────────────
   Always tint + tint-border + strong-text. Never a solid fill — forty rows of
   solid badges turn a table into confetti.
   ------------------------------------------------------------------------- */

.ec-badge {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    white-space: nowrap;
    border-width: 1px;
    border-style: solid;
    font-weight: 500;
    gap: 0.375rem;
    border-radius: var(--radius-sm);
}

.ec-badge--sm {
    height: 18px;
    padding-inline: 0.375rem;
    font-size: var(--text-label);
    line-height: var(--text-label-lh);
    letter-spacing: var(--text-label-ls);
    text-transform: uppercase;
    font-weight: 600;
}

.ec-badge--md {
    height: 22px;
    padding-inline: 0.5rem;
    font-size: var(--text-xs);
}

/* the optional dot uses the BASE tone — it is a graphic, not text */
.ec-badge__dot {
    width: 5px;
    height: 5px;
    flex-shrink: 0;
    border-radius: 9999px;
}

/* ── §9.3 Panel ─────────────────────────────────────────────────────────────
   Padding is fixed: header px-4 py-3, body px-4 py-4, footer px-4 py-2.5.
   ------------------------------------------------------------------------- */

.ec-panel {
    border-radius: var(--radius-lg);
    border: 1px solid var(--rule);
    background-color: var(--surface);
}

.ec-panel--interactive {
    transition: box-shadow 160ms var(--ease-out-editorial),
                border-color 160ms var(--ease-out-editorial);
}
.ec-panel--interactive:hover {
    border-color: var(--rule-strong);
    box-shadow: var(--shadow-raise);
}

.ec-panel__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    border-bottom: 1px solid var(--rule);
    padding: 0.75rem 1rem;
}

/* the panel header title is the caps label — serif is reserved for page and
   dialog level */
.ec-panel__header-copy { min-width: 0; }
.ec-panel__title { color: var(--ink); }
.ec-panel__description {
    margin-top: 0.25rem;
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--ink-mute);
}
.ec-panel__action {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    gap: 0.375rem;
}

.ec-panel__body { padding: 1rem; }
.ec-panel__body--flush { padding: 0; }

/* the footer sits on surface-alt — a small recession that closes the panel */
.ec-panel__footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    border-top: 1px solid var(--rule);
    background-color: var(--surface-alt);
    padding: 0.625rem 1rem;
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--ink-mute);
}

/* ── §9.4 Stat / StatRow — the boxless KPI band ─────────────────────────────
   No card, no background, no border around a stat. Only vertical hairlines
   between them, and only from md up: a hairline between stacked cells reads as
   a table, not as columns.
   ------------------------------------------------------------------------- */

.ec-stat { min-width: 0; }

.ec-stat__label { color: var(--ink-mute); }

/* The sublabel wraps under the figure when the column is tight rather than
   competing with it for the line. A five-digit total used to squeeze "all
   time" down to "a." — the figure is the point of the tile, and the caption
   is worth a second line. */
.ec-stat__value-row {
    margin-top: 0.5rem;
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.125rem 0.5rem;
}

.ec-stat__value {
    font-family: var(--font-serif);
    font-size: var(--text-figure);
    line-height: var(--text-figure-lh);
    letter-spacing: var(--text-figure-ls);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    color: var(--ink);
}

.ec-stat__sublabel {
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--ink-mute);
}

.ec-stat__delta {
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
}
.ec-stat__delta svg { width: 0.875rem; height: 0.875rem; flex-shrink: 0; }
.ec-stat__delta-value {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    font-weight: 500;
}
.ec-stat__delta-caption {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--ink-mute);
}

.ec-stat__spark { margin-top: 0.75rem; }

.ec-statrow {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 1.5rem;
    row-gap: 1.75rem;
}
.ec-statrow__item { min-width: 0; }

@media (min-width: 768px) {
    .ec-statrow {
        display: flex;
        gap: 0;
    }
    .ec-statrow__item {
        flex: 1 1 0%;
        padding-inline: 1.5rem;
    }
    /* flush with the content column at both ends — without this the band
       looks inset and wrong */
    .ec-statrow__item:first-child { padding-left: 0; }
    .ec-statrow__item:last-child  { padding-right: 0; }
    .ec-statrow__item + .ec-statrow__item {
        border-left: 1px solid var(--rule);
    }
}

/* ── §9.5 Table ─────────────────────────────────────────────────────────────
   Zebra striping is not used. The hairline is enough.
   ------------------------------------------------------------------------- */

.ec-table-scroll {
    width: 100%;
    overflow-x: auto;
}

/* Body text one step up the ramp from --text-sm: a queue is read for minutes
   at a time, and Outfit sets a touch smaller than the Inter this scale was
   drawn against. The column header follows it up so the two stay related. */
.ec-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-body);
    line-height: var(--text-body-lh);
}

/* the double-weight rule is what separates header from body without a fill */
.ec-table th {
    font-size: 0.6875rem;
    line-height: var(--text-label-lh);
    letter-spacing: var(--text-label-ls);
    text-transform: uppercase;
    font-weight: 600;
    border-bottom: 2px solid var(--rule);
    padding: 0.625rem 0.75rem;
    color: var(--ink-mute);
    text-align: left;
}
.ec-table th:first-child { padding-left: 1rem; }
.ec-table th:last-child  { padding-right: 1rem; }

.ec-table td {
    padding: 0.625rem 0.75rem;
    color: var(--ink);
}
.ec-table td:first-child { padding-left: 1rem; }
.ec-table td:last-child  { padding-right: 1rem; }

.ec-table tbody tr {
    border-bottom: 1px solid var(--rule);
}
.ec-table tbody tr:last-child { border-bottom: 0; }

.ec-table tbody tr.is-interactive {
    cursor: pointer;
    transition: background-color 160ms var(--ease-out-editorial);
}
.ec-table tbody tr.is-interactive:hover { background-color: var(--surface-alt); }

.ec-table tbody tr[aria-selected="true"] { background-color: var(--royal-soft); }

/* numeric columns: text-right on both th and td, tabular on the cell */
.ec-table .is-numeric { text-align: right; }
.ec-table td.is-numeric {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* sortable headers are a button inside the th, with aria-sort on the th */
.ec-table__sort {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    color: inherit;
    cursor: pointer;
}
.ec-table .is-numeric .ec-table__sort { flex-direction: row-reverse; }
.ec-table__sort svg { width: 0.75rem; height: 0.75rem; opacity: 0.4; }
.ec-table th[aria-sort="ascending"] .ec-table__sort svg,
.ec-table th[aria-sort="descending"] .ec-table__sort svg {
    opacity: 1;
    color: var(--coral);
}

/* ── §9.6 Tabs — the sliding underline ──────────────────────────────────── */

.ec-tabs {
    position: relative;
    display: flex;
    align-items: flex-end;
    gap: 1.5rem;
    overflow-x: auto;
    border-bottom: 1px solid var(--rule);
}

.ec-tab {
    position: relative;
    margin-bottom: -1px;
    display: flex;
    flex-shrink: 0;
    align-items: center;
    gap: 0.5rem;
    white-space: nowrap;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    padding: 0.25rem 0 0.625rem;
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--ink-mute);
    cursor: pointer;
    transition: color 160ms var(--ease-out-editorial);
}
.ec-tab:hover { color: var(--ink-soft); }
.ec-tab[aria-selected="true"] { color: var(--ink); }
.ec-tab svg { width: 1rem; height: 1rem; flex-shrink: 0; }

.ec-tab__count {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    border-radius: var(--radius-sm);
    padding-inline: 0.25rem;
    font-size: var(--text-label);
    line-height: var(--text-label-lh);
    letter-spacing: var(--text-label-ls);
    background-color: var(--surface-alt);
    color: var(--ink-mute);
}
.ec-tab[aria-selected="true"] .ec-tab__count {
    background-color: var(--coral-soft);
    color: var(--coral);
}

/* one measured element, sliding — not a per-tab border */
.ec-tabs__marker {
    pointer-events: none;
    position: absolute;
    bottom: 0;
    left: 0;
    height: 0.125rem;
    background-color: var(--coral);
    transition: transform 240ms var(--ease-out-editorial),
                width 240ms var(--ease-out-editorial);
}

/* ── §9.7 Inputs ────────────────────────────────────────────────────────── */

.ec-control {
    width: 100%;
    border-radius: var(--radius-md);
    border: 1px solid var(--rule);
    background-color: var(--surface);
    color: var(--ink);
    font-family: var(--font-sans);
    transition: border-color 160ms var(--ease-out-editorial),
                background-color 160ms var(--ease-out-editorial);
}
.ec-control::placeholder { color: var(--placeholder); }
.ec-control:hover { border-color: var(--rule-strong); }
.ec-control:disabled {
    cursor: not-allowed;
    background-color: var(--surface-alt);
    color: var(--ink-mute);
}
.ec-control[aria-invalid="true"] {
    border-color: var(--crimson);
    background-color: var(--crimson-soft);
}

.ec-input {
    height: 2.25rem;
    padding-inline: 0.625rem;
    font-size: var(--text-body);
}
.ec-input--with-icon { padding-left: 2rem; }

.ec-textarea {
    resize: vertical;
    padding: 0.5rem 0.625rem;
    font-size: var(--text-body);
    line-height: var(--text-body-lh);
}

.ec-select-wrap { position: relative; }
.ec-select {
    height: 2.25rem;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    padding: 0 2rem 0 0.625rem;
    font-size: var(--text-body);
}
.ec-select-wrap__chevron {
    position: absolute;
    right: 0.625rem;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    color: var(--ink-mute);
    width: 1rem;
    height: 1rem;
}

.ec-input-wrap { position: relative; }
.ec-input-wrap__icon {
    position: absolute;
    left: 0.625rem;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    color: var(--ink-mute);
    width: 1rem;
    height: 1rem;
}

.ec-checkbox-wrap {
    position: relative;
    display: inline-grid;
    place-items: center;
}
.ec-checkbox {
    width: 1rem;
    height: 1rem;
    appearance: none;
    -webkit-appearance: none;
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule-strong);
    background-color: var(--surface);
    margin: 0;
    cursor: pointer;
    transition: background-color 160ms var(--ease-out-editorial),
                border-color 160ms var(--ease-out-editorial);
}
.ec-checkbox:checked {
    border-color: var(--coral);
    background-color: var(--coral);
}
.ec-checkbox-wrap__tick {
    position: absolute;
    width: 0.75rem;
    height: 0.75rem;
    color: #fff;
    opacity: 0;
    pointer-events: none;
    transition: opacity 160ms var(--ease-out-editorial);
}
.ec-checkbox:checked + .ec-checkbox-wrap__tick { opacity: 1; }

.ec-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    height: 1.25rem;
    width: 2.25rem;
    flex-shrink: 0;
    border-radius: 9999px;
    border: 1px solid var(--rule-strong);
    background-color: var(--surface-alt);
    padding: 0;
    cursor: pointer;
    transition: background-color 160ms var(--ease-out-editorial),
                border-color 160ms var(--ease-out-editorial);
}
.ec-switch[aria-checked="true"] {
    border-color: var(--teal);
    background-color: var(--teal);
}
.ec-switch__knob {
    position: absolute;
    left: 0;
    width: 0.875rem;
    height: 0.875rem;
    border-radius: 9999px;
    background-color: var(--surface);
    box-shadow: var(--shadow-raise);
    transform: translateX(2px);
    transition: transform 160ms var(--ease-out-editorial);
}
.ec-switch[aria-checked="true"] .ec-switch__knob { transform: translateX(18px); }

/* Field wrapper — the accessibility contract, in one place.
   Form labels are the caps label, not sentence case: a form should read like a
   printed record card. */
.ec-field {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}
.ec-field__label { color: var(--ink-mute); }
.ec-field__required { margin-left: 0.25rem; color: var(--coral); }
.ec-field__description {
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--ink-mute);
}
.ec-field__error {
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--crimson);
}

/* ── §9.8 Dialog ────────────────────────────────────────────────────────── */

/* navy at 35% with a 2px blur — never black/50, it keeps the overlay inside
   the brand family */
.ec-backdrop {
    position: fixed;
    inset: 0;
    background-color: color-mix(in srgb, var(--chrome) 35%, transparent);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    animation: fade 180ms var(--ease-out-editorial) both;
}

.ec-overlay-root {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}
.ec-overlay-root[hidden] { display: none; }

.ec-dialog {
    position: relative;
    display: flex;
    width: 100%;
    flex-direction: column;
    overflow: hidden;
    border-radius: var(--radius-lg);
    border: 1px solid var(--rule);
    background-color: var(--surface);
    box-shadow: var(--shadow-float);
    animation: rise 240ms var(--ease-out-editorial) both;
}
.ec-dialog--sm { max-width: 400px; }
.ec-dialog--md { max-width: 520px; }
.ec-dialog--lg { max-width: 720px; }

.ec-dialog__header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.25rem 1.25rem 0.75rem;
}
.ec-dialog__title {
    font-family: var(--font-serif);
    font-size: var(--text-h2);
    line-height: var(--text-h2-lh);
    letter-spacing: var(--text-h2-ls);
    color: var(--ink);
    margin: 0;
}
.ec-dialog__description {
    margin-top: 0.375rem;
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
    color: var(--ink-soft);
}
.ec-dialog__close { margin-right: -0.25rem; margin-top: -0.25rem; }

.ec-dialog__body {
    max-height: 60vh;
    overflow-y: auto;
    padding: 0.25rem 1.25rem;
    font-size: var(--text-body);
    line-height: var(--text-body-lh);
    color: var(--ink-soft);
}

.ec-dialog__footer {
    margin-top: 1rem;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    border-top: 1px solid var(--rule);
    background-color: var(--surface-alt);
    padding: 0.75rem 1.25rem;
}

/* ── §9.9 Drawer — detail without losing your place ─────────────────────── */

.ec-drawer-root {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    justify-content: flex-end;
}
.ec-drawer-root[hidden] { display: none; }

.ec-drawer {
    position: relative;
    display: flex;
    height: 100%;
    width: 100%;
    flex-direction: column;
    border-left: 1px solid var(--rule);
    background-color: var(--surface);
    box-shadow: var(--shadow-drawer);
    animation: slide-in 260ms var(--ease-out-editorial) both;
}
@media (min-width: 640px) {
    .ec-drawer     { max-width: 520px; }
    .ec-drawer--lg { max-width: 720px; }
}

.ec-drawer__header {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    border-bottom: 1px solid var(--rule);
    padding: 1rem 1.25rem;
}
.ec-drawer__title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--font-serif);
    font-size: var(--text-h2);
    line-height: var(--text-h2-lh);
    letter-spacing: var(--text-h2-ls);
    color: var(--ink);
    margin: 0;
}
.ec-drawer__subtitle {
    margin-top: 0.25rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
    color: var(--ink-mute);
}
.ec-drawer__body {
    min-height: 0;
    flex: 1 1 0%;
    overflow-y: auto;
}
.ec-drawer__footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    border-top: 1px solid var(--rule);
    background-color: var(--surface-alt);
    padding: 0.75rem 1.25rem;
}

/* ── §9.11 Toast ────────────────────────────────────────────────────────── */

.ec-toast-region {
    pointer-events: none;
    position: fixed;
    bottom: 1.25rem;
    right: 1.25rem;
    z-index: 60;
    display: flex;
    width: min(360px, calc(100vw - 2.5rem));
    flex-direction: column;
    gap: 0.625rem;
}

.ec-toast {
    pointer-events: auto;
    overflow: hidden;
    border-radius: var(--radius-lg);
    border: 1px solid var(--rule);
    background-color: var(--surface);
    box-shadow: var(--shadow-float);
    animation: rise 240ms var(--ease-out-editorial) both;
}
.ec-toast__inner {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.75rem 0.875rem;
}
.ec-toast__icon {
    margin-top: 1px;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
}
.ec-toast__content { min-width: 0; flex: 1 1 0%; }
.ec-toast__title {
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
    font-weight: 500;
    color: var(--ink);
    margin: 0;
}
.ec-toast__description {
    margin: 0.25rem 0 0;
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--ink-mute);
}
.ec-toast__action {
    margin-top: 0.5rem;
    background: none;
    border: 0;
    padding: 0;
    border-radius: var(--radius-sm);
    color: var(--coral);
    cursor: pointer;
}
.ec-toast__action:hover { color: var(--coral-hover); }
.ec-toast__dismiss { color: var(--ink-faint); }
.ec-toast__dismiss:hover { color: var(--ink); }

/* the timer is a 1px hairline, scaleX driven by rAF so hover pauses exactly */
.ec-toast__timer {
    height: 1px;
    transform-origin: left;
}

/* ── §9.12 Tooltip ──────────────────────────────────────────────────────────
   Navy in both themes — a tooltip is chrome, not canvas.
   ------------------------------------------------------------------------- */

.ec-tooltip {
    pointer-events: none;
    position: fixed;
    z-index: 70;
    max-width: 240px;
    border-radius: var(--radius-md);
    background-color: var(--chrome);
    padding: 0.25rem 0.5rem;
    font-size: var(--text-xs);
    line-height: var(--text-xs-lh);
    color: var(--chrome-text-strong);
    box-shadow: var(--shadow-float);
    animation: fade 180ms var(--ease-out-editorial) both;
}
.ec-tooltip[hidden] { display: none; }

/* ── §9.13 Avatar ───────────────────────────────────────────────────────────
   A tint avatar — soft ground, line border, strong text — consistent with
   badges. Hue is derived from the stable id, not the name.
   ------------------------------------------------------------------------- */

.ec-avatar {
    position: relative;
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: 9999px;
    border-width: 1px;
    border-style: solid;
    font-weight: 600;
    user-select: none;
    font-variant-numeric: tabular-nums;
}
.ec-avatar--xs { width: 1.25rem; height: 1.25rem; font-size: var(--text-label); }
.ec-avatar--sm { width: 1.5rem;  height: 1.5rem;  font-size: var(--text-label); }
.ec-avatar--md { width: 2rem;    height: 2rem;    font-size: var(--text-xs); }
.ec-avatar--lg { width: 2.75rem; height: 2.75rem; font-size: var(--text-sm); }

.ec-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 9999px;
}
.ec-avatar--photo { border-color: var(--rule); }

/* for overlapping stacks */
.ec-avatar--ring { box-shadow: 0 0 0 2px var(--surface); }

/* the topbar's own avatar is the exception: a solid fill with white text,
   because it sits in chrome */
.ec-avatar--chrome {
    background-color: var(--coral-strong);
    border-color: transparent;
    color: #fff;
}

/* ── §9.14 Progress and score bars ──────────────────────────────────────── */

.ec-progress {
    height: 0.375rem;
    overflow: hidden;
    border-radius: 9999px;
    background-color: var(--surface-sunken);
}

/* the fill grows from zero on first paint using @starting-style — a mount
   animation with no client runtime at all */
.ec-progress__fill {
    display: block;
    height: 100%;
    width: var(--fill, 0%);
    border-radius: 9999px;
    transition: width 500ms var(--ease-out-editorial);
}
@starting-style {
    .ec-progress__fill { width: 0; }
}

.ec-progress-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.75rem;
}
.ec-progress-row__count { font-weight: 500; color: var(--ink); }
.ec-progress-row__divider { color: var(--ink-faint); }

.ec-scorebar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}
.ec-scorebar__label { width: 7rem; flex-shrink: 0; }
.ec-scorebar__track { flex: 1 1 0%; }
.ec-scorebar__value {
    width: 2.25rem;
    flex-shrink: 0;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* ── §9.15 Empty state ──────────────────────────────────────────────────── */

.ec-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 4rem 1.5rem;
    text-align: center;
}
.ec-empty--compact { padding-block: 2.5rem; }

.ec-empty__illustration { color: var(--ink-faint); }

.ec-empty__title {
    margin: 1.5rem 0 0;
    font-family: var(--font-serif);
    font-size: var(--text-h2);
    line-height: var(--text-h2-lh);
    letter-spacing: var(--text-h2-ls);
    color: var(--ink);
}
.ec-empty__description {
    margin: 0.5rem 0 0;
    max-width: 46ch;
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
    color: var(--ink-mute);
}
.ec-empty__action { margin-top: 1.25rem; }

/* Three templates pair .ec-empty with the older .empty-icon/.empty-title/
   .empty-text names, which only ever existed scoped under
   .empty-state-curator — so that markup rendered completely unstyled, and in
   dark mode the heading and body text fell back to whatever they inherited.
   Alias them onto the §9.15 values until those templates move to the BEM
   names. */
.ec-empty .empty-icon {
    width: 2rem;
    height: 2rem;
    color: var(--ink-faint);
}
.ec-empty .empty-title {
    margin: 1rem 0 0;
    font-family: var(--font-serif);
    font-size: var(--text-h2);
    line-height: var(--text-h2-lh);
    letter-spacing: var(--text-h2-ls);
    color: var(--ink);
}
.ec-empty .empty-text {
    margin: 0.5rem 0 0;
    max-width: 46ch;
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
    color: var(--ink-mute);
}

/* ── §9.16 Skeleton ─────────────────────────────────────────────────────────
   A loading state must be shaped like the content that is arriving, with
   UNEVEN dimensions. Identical bars read as a loader; uneven ones read as data.
   ------------------------------------------------------------------------- */

.ec-skeleton {
    height: 0.75rem;
    border-radius: var(--radius-sm);
    background-image: linear-gradient(90deg,
        var(--surface-alt) 0%, var(--rule) 45%, var(--surface-alt) 90%);
    background-size: 200% 100%;
    animation: shimmer 1600ms linear infinite;
}
.ec-skeleton--text   { height: 0.75rem; }
.ec-skeleton--title  { height: 1rem; }
.ec-skeleton--avatar { height: 2rem; width: 2rem; border-radius: 9999px; }

/* ── §9.24 Error and forbidden states ───────────────────────────────────────
   The tinted status medallion is reused for any full-page state.
   ------------------------------------------------------------------------- */

.ec-state {
    margin-inline: auto;
    display: flex;
    max-width: 28rem;
    flex-direction: column;
    align-items: center;
    padding-block: 6rem;
    text-align: center;
}
.ec-medallion {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: 9999px;
    border-width: 1px;
    border-style: solid;
}
.ec-medallion svg { width: 1.25rem; height: 1.25rem; }

.ec-state__title {
    margin: 1.25rem 0 0;
    font-family: var(--font-serif);
    font-size: var(--text-h2);
    line-height: var(--text-h2-lh);
    letter-spacing: var(--text-h2-ls);
    font-weight: 500;
    color: var(--ink);
}
.ec-state__body {
    margin: 0.5rem 0 0;
    font-size: var(--text-sm);
    line-height: 1.625;
    color: var(--ink-soft);
}

/* always surface the reference code in mono — it turns "it broke" into
   something actionable */
.ec-reference {
    margin-top: 1rem;
    display: inline-block;
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule);
    background-color: var(--surface-alt);
    padding: 0.25rem 0.625rem;
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-mute);
}

/* ── Shared helpers used by the recipes above ───────────────────────────── */

.ec-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* body scroll lock compensates for the scrollbar, so the page does not shift
   sideways when it disappears (§9.10) */
body.ec-scroll-locked { overflow: hidden; }

/* Skeleton widths (§9.16) — uneven by design. Identical bars read as a loader;
   uneven ones read as data. Picked as widths[(row + col) % 6]. */
.ec-w-3-4 { width: 75%; }
.ec-w-1-2 { width: 50%; }
.ec-w-2-3 { width: 66.666667%; }
.ec-w-5-6 { width: 83.333333%; }
.ec-w-1-3 { width: 33.333333%; }
.ec-w-3-5 { width: 60%; }

/* ── Flash messages ─────────────────────────────────────────────────────────
   An alert panel is the tint triple, like a badge: -soft ground, -line border,
   -strong text. Never a solid fill.
   ------------------------------------------------------------------------- */

.ec-messages {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1.5rem;
}

.ec-alert {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    border-radius: var(--radius-md);
    border-width: 1px;
    border-style: solid;
    padding: 0.625rem 0.875rem;
    font-size: var(--text-sm);
    line-height: var(--text-sm-lh);
}
.ec-alert svg { width: 1rem; height: 1rem; flex-shrink: 0; margin-top: 1px; }

/* ── §9.23 Filter bar ───────────────────────────────────────────────────────
   Filters sit in a ruled strip between the header and the content, like a
   masthead rule. The border-y band is the visual signature.
   ------------------------------------------------------------------------- */

.ec-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    border-block: 1px solid var(--rule);
    padding-block: 0.75rem;
}
.ec-filter-bar__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
}
.ec-filter-bar__search { width: 14rem; }
.ec-filter-bar__views {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
}
.ec-filter-bar .ec-select-wrap { min-width: 9rem; }

/* ── Table cell helpers ─────────────────────────────────────────────────── */

/* A row that is really a link: a transparent overlay anchor keeps
   right-click, middle-click and ctrl-click working natively, and gives the row
   one accessible name instead of nine. */
/* The overlay must span the WHOLE ROW. Scoping `position: relative` to the
   first cell made only the ticket number clickable. */
.ec-row-link-host { position: relative; }
.ec-row-link { position: absolute; inset: 0; z-index: 1; }
/* Cells holding their own controls sit above the overlay so their buttons and
   links still receive the click. */
.ec-row-link-host td.ec-cell-action,
.ec-row-link-host td.ec-cell-select { position: relative; z-index: 2; }

.ec-cell-truncate {
    display: block;
    max-width: 22ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ec-cell-stack { display: flex; flex-direction: column; line-height: 1.35; }
.ec-cell-person { display: flex; align-items: center; gap: 0.5rem; }
.ec-cell-action { text-align: right; white-space: nowrap; }

/* Things that must not be broken across lines: a reference code, a date, a
   person's name. A ticket number carries hyphens, so left to itself it wraps
   into three lines and stops looking like one identifier. The table already
   scrolls horizontally (.ec-table-scroll), which is the right place for the
   pressure to go. */
.ec-table .font-mono,
.ec-table .tabular,
.ec-cell-person span { white-space: nowrap; }

/* ── Form composition inside dialogs and pages ──────────────────────────── */

.ec-form-section { display: flex; flex-direction: column; gap: 1rem; }
.ec-form-section + .ec-form-section {
    margin-top: 1.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
}
.ec-form-grid { display: grid; gap: 1rem; }
@media (min-width: 640px) {
    .ec-form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ── Pagination ─────────────────────────────────────────────────────────── */

.ec-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
}
.ec-pagination__pages { display: flex; align-items: center; gap: 0.25rem; }
.ec-pagination__page {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.75rem;
    height: 1.75rem;
    padding-inline: 0.375rem;
    border-radius: var(--radius-sm);
    font-variant-numeric: tabular-nums;
    font-size: var(--text-xs);
    color: var(--ink-soft);
    text-decoration: none;
    transition: background-color 160ms var(--ease-out-editorial),
                color 160ms var(--ease-out-editorial);
}
.ec-pagination__page:hover { background-color: var(--surface); color: var(--ink); }
.ec-pagination__page[aria-current="page"] {
    background-color: var(--coral-soft);
    color: var(--coral-strong);
    font-weight: 600;
}

/* ── §10 Charts ─────────────────────────────────────────────────────────────
   Every paint is var(--token), so a chart recolours itself the instant
   [data-theme] flips — no JavaScript, no re-render.
   ------------------------------------------------------------------------- */

.ec-chart { position: relative; width: 100%; }
.ec-chart__svg { display: block; overflow: visible; }
.ec-chart__svg:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* serif, tabular, 10.5px — chart ticks are figures, not interface text */
.ec-chart__tick {
    font-family: var(--font-serif);
    font-size: 10.5px;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.012em;
    fill: var(--ink-mute);
}
.ec-chart__empty {
    font-family: var(--font-serif);
    font-size: var(--text-xs);
    fill: var(--ink-mute);
}
.ec-chart__guide { pointer-events: none; }
.ec-chart__band { cursor: default; }

/* squares, not dots — they read as swatches from a printed key */
.ec-chart__legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.75rem;
}
.ec-chart__legend-item {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: var(--text-xs);
    color: var(--ink-mute);
}
.ec-chart__swatch {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 1px;
    flex-shrink: 0;
}

/* the navy tooltip chip — read-outs are chrome, in BOTH themes */
.ec-chart-chip {
    position: fixed;
    z-index: 70;
    min-width: 9rem;
    pointer-events: none;
    border-radius: var(--radius-md);
    border: 1px solid var(--chrome-rule);
    background-color: var(--chrome);
    box-shadow: var(--shadow-float);
    padding: 0.5rem 0.75rem;
    animation: fade 120ms var(--ease-out-editorial) both;
}
.ec-chart-chip[hidden] { display: none; }
.ec-chart-chip__title {
    font-size: var(--text-label);
    line-height: var(--text-label-lh);
    letter-spacing: var(--text-label-ls);
    text-transform: uppercase;
    font-weight: 600;
    color: var(--chrome-text-mute);
    margin-bottom: 0.25rem;
}
.ec-chart-chip__row {
    display: flex;
    align-items: center;
    gap: 0.375rem;
    margin-top: 0.125rem;
}
.ec-chart-chip__swatch {
    width: 8px;
    height: 8px;
    border-radius: 1px;
    flex-shrink: 0;
}
.ec-chart-chip__label {
    font-size: var(--text-xs);
    color: var(--chrome-text);
}
.ec-chart-chip__value {
    margin-left: auto;
    font-family: var(--font-serif);
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    color: var(--chrome-text-strong);
}

/* ── Small layout helpers used by page composition ─────────────────────── */

.ec-mt-5 { margin-top: 1.25rem; }
.ec-inline-form { display: flex; align-items: center; gap: 0.5rem; margin: 0; }

/* A plain list of rows inside a flush panel. */
.ec-list { list-style: none; margin: 0; padding: 0; }
.ec-list > li + li { border-top: 1px solid var(--rule); }
.ec-list__row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1rem;
    text-decoration: none;
    color: inherit;
    transition: background-color 160ms var(--ease-out-editorial);
}
.ec-list__row:hover { background-color: var(--surface-alt); }
.ec-list__copy { display: flex; flex-direction: column; min-width: 0; }
.ec-list__title {
    font-size: var(--text-sm);
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ec-list__meta {
    font-size: var(--text-xs);
    color: var(--ink-mute);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ec-list__count {
    margin-left: auto;
    font-family: var(--font-serif);
    font-size: var(--text-h3);
    color: var(--ink);
}

/* Reorderable table rows. */
.ec-col-grip { width: 2.5rem; }
.ec-grip {
    cursor: grab;
    text-align: center;
    color: var(--ink-faint);
}
.ec-grip svg { width: 0.875rem; height: 0.875rem; }
tr.drag-over { background-color: var(--coral-soft); }

/* A checkbox with its label on one row. */
.ec-check-row {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    cursor: pointer;
}

/* ── Notification rows ──────────────────────────────────────────────────── */

/* A capped measure, centred in the canvas. Without the auto margins the page
   sat hard against the rail and left a band of empty ground down the right,
   which read as a layout that had failed rather than as a deliberate column. */
.ec-page--narrow { max-width: 56rem; width: 100%; margin-inline: auto; }

.ec-notif {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.875rem 1rem;
    transition: background-color 160ms var(--ease-out-editorial);
}
.ec-list > li.is-unread .ec-notif { background-color: var(--surface-alt); }
.ec-notif:hover { background-color: var(--surface-alt); }

.ec-notif__icon {
    display: grid;
    place-items: center;
    width: 1.75rem;
    height: 1.75rem;
    flex-shrink: 0;
    border-radius: var(--radius-md);
}
.ec-notif__icon svg { width: 0.875rem; height: 0.875rem; }

.ec-notif__copy { min-width: 0; flex: 1 1 0%; }
.ec-notif__title {
    display: block;
    font-size: var(--text-sm);
    color: var(--ink);
    text-decoration: none;
}
.ec-notif__title:hover { color: var(--coral-strong); }
.ec-notif__body {
    margin: 0.25rem 0 0;
    font-size: var(--text-xs);
    color: var(--ink-soft);
}
.ec-notif__meta {
    margin: 0.25rem 0 0;
    font-size: var(--text-xs);
    color: var(--ink-mute);
}
.ec-notif__controls {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex-shrink: 0;
}
.ec-notif__controls form { margin: 0; }
.ec-notif__dot {
    width: 6px;
    height: 6px;
    border-radius: 9999px;
    flex-shrink: 0;
}

.ec-sep { color: var(--ink-faint); }
.ec-link { color: var(--ink); text-decoration: none; }
.ec-link:hover { color: var(--coral-strong); }

/* Lucide icons ship inline at strokeWidth 1.5 (§12). Default size-4; call
   sites that need another size pass it explicitly. */
.ec-icon {
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    display: inline-block;
    vertical-align: middle;
}

/* ── Bulk selection ─────────────────────────────────────────────────────────
   The bar is hidden until something is selected, so the page is unchanged for
   anyone not using it. It floats above the canvas but below overlays.
   ------------------------------------------------------------------------- */

.ec-col-select { width: 2.75rem; }
.ec-cell-select { width: 2.75rem; }

.ec-bulkbar {
    position: sticky;
    bottom: 1.25rem;
    z-index: 40;
    margin: 1.25rem auto 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    width: fit-content;
    max-width: 100%;
    border-radius: var(--radius-lg);
    border: 1px solid var(--rule);
    background-color: var(--surface);
    box-shadow: var(--shadow-float);
    padding: 0.625rem 0.875rem;
    animation: rise 240ms var(--ease-out-editorial) both;
}
.ec-bulkbar[hidden] { display: none; }

.ec-bulkbar__count {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    white-space: nowrap;
}
.ec-bulkbar__count strong { color: var(--ink); font-weight: 600; }

.ec-bulkbar__rule {
    width: 1px;
    align-self: stretch;
    background-color: var(--rule);
    margin-inline: 0.25rem;
}
.ec-bulkbar__select { min-width: 11rem; }
.ec-bulkbar .ec-select { height: 1.75rem; font-size: var(--text-xs); }
.ec-bulkbar .ec-select-wrap__chevron { width: 0.875rem; height: 0.875rem; }

/* A selected row reads as selected, not merely hovered. */
.ec-table tbody tr[aria-selected="true"] { background-color: var(--royal-soft); }
