/* ===========================================================================
   Zhopwe components
   ===========================================================================
   Shared across the marketing site, both dashboards and the storefront chrome.
   Each component covers its full set of states — default, hover, active,
   focus-visible, disabled, loading, error — because a control that looks fine
   until it is pressed is the thing that makes software feel unfinished.
   =========================================================================== */

/* ===========================================================================
   Buttons
   =========================================================================== */

.btn {
    --btn-bg: transparent;
    --btn-fg: var(--text-primary);
    --btn-border: transparent;
    --btn-bg-hover: var(--surface-sunken);

    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--tap-target);
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    line-height: 1.2;
    letter-spacing: var(--tracking-normal);
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    border: 1px solid var(--btn-border);
    border-radius: var(--radius-md);
    background-color: var(--btn-bg);
    color: var(--btn-fg);
    cursor: pointer;
    user-select: none;
    transition: background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                transform var(--duration-instant) var(--ease-out);
}

.btn:hover:not(:disabled):not([aria-disabled="true"]) { background-color: var(--btn-bg-hover); }

/* A 1px push on press. Small enough to read as a physical response rather
   than an animation, and it is the thing that makes a button feel clicked on
   a touch screen where there is no hover state at all. */
.btn:active:not(:disabled):not([aria-disabled="true"]) { transform: translateY(1px); }

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

.btn-primary {
    --btn-bg: var(--surface-brand);
    --btn-fg: var(--text-on-brand);
    --btn-border: var(--surface-brand);
    --btn-bg-hover: var(--jade-700);
    box-shadow: var(--shadow-xs);
}
.btn-primary:hover:not(:disabled) { border-color: var(--jade-700); }

.btn-secondary {
    --btn-bg: var(--surface-card);
    --btn-fg: var(--text-primary);
    --btn-border: var(--border-default);
    --btn-bg-hover: var(--surface-sunken);
    box-shadow: var(--shadow-xs);
}
.btn-secondary:hover:not(:disabled) { border-color: var(--border-strong); }

.btn-ghost {
    --btn-bg: transparent;
    --btn-fg: var(--text-secondary);
    --btn-bg-hover: var(--surface-sunken);
}
.btn-ghost:hover:not(:disabled) { color: var(--text-primary); }

.btn-danger {
    --btn-bg: var(--danger-600);
    --btn-fg: #fff;
    --btn-border: var(--danger-600);
    --btn-bg-hover: var(--danger-700);
}

/* Destructive but not the primary action on the page: outlined, so Delete does
   not compete visually with Save. */
.btn-danger-outline {
    --btn-bg: var(--surface-card);
    --btn-fg: var(--danger-600);
    --btn-border: var(--danger-100);
    --btn-bg-hover: var(--danger-50);
}

.btn-accent {
    --btn-bg: var(--amber-600);
    --btn-fg: #fff;
    --btn-border: var(--amber-600);
    --btn-bg-hover: var(--amber-700);
}

.btn-link {
    --btn-fg: var(--text-link);
    --btn-bg-hover: transparent;
    min-height: auto;
    padding: 0;
    text-decoration: underline;
    text-underline-offset: 0.18em;
}

.btn-sm {
    min-height: 2rem;
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
}

.btn-lg {
    min-height: 3.25rem;
    padding: var(--space-3) var(--space-6);
    font-size: var(--text-lg);
    border-radius: var(--radius-lg);
}

.btn-block { width: 100%; }

.btn-icon {
    padding: 0;
    width: var(--tap-target);
    min-width: var(--tap-target);
}
.btn-icon.btn-sm { width: 2rem; min-width: 2rem; }

/* Loading state. The label is kept in place and dimmed rather than replaced,
   so the button does not change width and the layout does not jump. */
.btn[data-loading="true"] {
    position: relative;
    color: transparent;
    pointer-events: none;
}
.btn[data-loading="true"]::after {
    content: "";
    position: absolute;
    inset: 0;
    margin: auto;
    width: 1.1em;
    height: 1.1em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    color: var(--btn-fg);
    animation: spin 600ms linear infinite;
}

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

.btn-group {
    display: inline-flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* ===========================================================================
   Cards
   =========================================================================== */

.card {
    background: var(--surface-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xs);
}

.card-padded { padding: var(--space-6); }

.card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-6);
    border-bottom: 1px solid var(--border-subtle);
}

.card-header h2, .card-header h3 { font-size: var(--text-lg); margin: 0; }

.card-header-text { min-width: 0; }

.card-description {
    margin-top: var(--space-1);
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-wrap: pretty;
}

.card-body { padding: var(--space-6); }
.card-body-tight { padding: var(--space-4); }

.card-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-6);
    border-top: 1px solid var(--border-subtle);
    background: var(--surface-sunken);
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}

.card-footer-split { justify-content: space-between; }

/* An interactive card (a theme in the gallery, a plan). The lift is small;
   large hover transforms on a grid of cards make a page feel unstable. */
.card-interactive {
    transition: border-color var(--duration-normal) var(--ease-out),
                box-shadow var(--duration-normal) var(--ease-out),
                transform var(--duration-normal) var(--ease-out);
}
.card-interactive:hover {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}
.card-interactive:focus-within {
    border-color: var(--border-brand);
    box-shadow: var(--shadow-focus);
}

.card-selected {
    border-color: var(--border-brand);
    box-shadow: var(--shadow-focus);
}

/* ===========================================================================
   Forms
   =========================================================================== */

.field { margin-bottom: var(--space-5); }

.field-label {
    display: block;
    margin-bottom: var(--space-2);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    color: var(--text-primary);
}

/* The asterisk is decorative; the real signal for assistive technology is the
   `required` attribute plus aria-describedby on the input. */
.field-required::after {
    content: "*";
    margin-inline-start: 0.2em;
    color: var(--danger-600);
}

.field-optional {
    margin-inline-start: var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--weight-regular);
    color: var(--text-muted);
}

/* Help text sits ABOVE the input, not below it. Below, it is read after the
   field has already been filled in — too late to help. */
.field-help {
    margin-bottom: var(--space-2);
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-wrap: pretty;
}

.input, .select, .textarea {
    display: block;
    width: 100%;
    min-height: var(--tap-target);
    padding: var(--space-2) var(--space-3);
    /* 16px on mobile specifically: iOS Safari zooms the whole page when a
       focused input's font-size is below 16px, which looks like a bug. */
    font-size: var(--text-md);
    line-height: 1.4;
    color: var(--text-primary);
    background-color: var(--surface-card);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-xs) inset;
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.input::placeholder, .textarea::placeholder {
    color: var(--text-muted);
    opacity: 1;
}

.input:hover:not(:disabled), .select:hover:not(:disabled), .textarea:hover:not(:disabled) {
    border-color: var(--border-strong);
}

.input:focus, .select:focus, .textarea:focus {
    outline: none;
    border-color: var(--border-brand);
    box-shadow: var(--shadow-focus);
}

.input[aria-invalid="true"], .select[aria-invalid="true"], .textarea[aria-invalid="true"] {
    border-color: var(--danger-500);
}
.input[aria-invalid="true"]:focus {
    box-shadow: 0 0 0 3px rgb(207 59 46 / 0.2);
}

.select {
    appearance: none;
    padding-inline-end: var(--space-10);
    /* Chevron drawn as a background image so no icon font or inline SVG is
       needed, and so it inherits the text colour via currentColor encoding. */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5l5-5' fill='none' stroke='%2381817a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--space-3) center;
}
[dir="rtl"] .select { background-position: left var(--space-3) center; }

.field-error {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--danger-600);
}

/* Error text is never colour alone: it carries an icon and sits in an
   aria-live region, so it is perceivable without colour vision. */
.field-error::before {
    content: "";
    flex-shrink: 0;
    width: 1.1em;
    height: 1.1em;
    margin-top: 0.1em;
    background-color: currentColor;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M8 1.5 15 14H1L8 1.5Zm0 4v4m0 2v1' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

.field-row {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}

/* Input with a fixed prefix or suffix (currency, units, a domain). */
.input-group {
    display: flex;
    align-items: stretch;
}
.input-group .input { border-radius: 0; }
.input-group > :first-child { border-start-start-radius: var(--radius-md); border-end-start-radius: var(--radius-md); }
.input-group > :last-child { border-start-end-radius: var(--radius-md); border-end-end-radius: var(--radius-md); }
.input-group .input:not(:first-child) { margin-inline-start: -1px; }

.input-addon {
    display: inline-flex;
    align-items: center;
    padding-inline: var(--space-3);
    font-size: var(--text-sm);
    color: var(--text-muted);
    background: var(--surface-sunken);
    border: 1px solid var(--border-default);
    white-space: nowrap;
}

/* Checkbox and radio: the whole row is the label, so the tap target is the
   full line rather than a 16px box. */
.check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    min-height: var(--tap-target);
    padding-block: var(--space-2);
    cursor: pointer;
}

.check input[type="checkbox"], .check input[type="radio"] {
    flex-shrink: 0;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: 0.15rem;
    accent-color: var(--surface-brand);
    cursor: pointer;
}

.check-text { font-size: var(--text-base); }
.check-description {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.fieldset { border: none; padding: 0; margin: 0 0 var(--space-6); }
.fieldset-legend {
    padding: 0;
    margin-bottom: var(--space-3);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
}

/* ===========================================================================
   Badges and status indicators
   =========================================================================== */

.badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0.15rem var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    line-height: 1.5;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    white-space: nowrap;
}

.badge-neutral { background: var(--surface-sunken); color: var(--text-secondary); border-color: var(--border-default); }
.badge-success { background: var(--success-50); color: var(--success-700); border-color: var(--success-100); }
.badge-warning { background: var(--warning-50); color: var(--warning-700); border-color: var(--warning-100); }
.badge-danger  { background: var(--danger-50);  color: var(--danger-700);  border-color: var(--danger-100); }
.badge-info    { background: var(--info-50);    color: var(--info-700);    border-color: var(--info-100); }
.badge-brand   { background: var(--surface-brand-soft); color: var(--text-brand); border-color: var(--jade-200); }

/* A status dot alongside the word. The word is what conveys the state; the dot
   only reinforces it, so meaning never depends on the colour. */
.status {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}

.status::before {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.status-live     { color: var(--success-600); }
.status-pending  { color: var(--warning-600); }
.status-error    { color: var(--danger-600); }
.status-inactive { color: var(--text-muted); }
.status-info     { color: var(--info-600); }

/* A pending state that is actively working gets a pulse. Removed under
   prefers-reduced-motion by the global rule in base.css. */
.status-pending::before { animation: pulse 2s var(--ease-in-out) infinite; }

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}

/* ===========================================================================
   Alerts
   =========================================================================== */

.alert {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--border-default);
    border-inline-start-width: 3px;
    border-radius: var(--radius-md);
    background: var(--surface-card);
    font-size: var(--text-base);
}

.alert-title { font-weight: var(--weight-semibold); margin-bottom: var(--space-1); }
.alert-body { color: var(--text-secondary); text-wrap: pretty; }
.alert-body :last-child { margin-bottom: 0; }
.alert-actions { margin-top: var(--space-3); display: flex; gap: var(--space-2); flex-wrap: wrap; }

.alert-success { background: var(--success-50); border-color: var(--success-100); border-inline-start-color: var(--success-500); }
.alert-warning { background: var(--warning-50); border-color: var(--warning-100); border-inline-start-color: var(--warning-500); }
.alert-danger  { background: var(--danger-50);  border-color: var(--danger-100);  border-inline-start-color: var(--danger-500); }
.alert-info    { background: var(--info-50);    border-color: var(--info-100);    border-inline-start-color: var(--info-500); }

.alert-icon {
    flex-shrink: 0;
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.1rem;
}

/* ===========================================================================
   Tables
   =========================================================================== */

.table-wrap {
    /* Horizontal scroll with a keyboard-reachable container: a table that
       overflows must be scrollable by keyboard, not only by trackpad. */
    overflow-x: auto;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--surface-card);
}

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

.table { font-size: var(--text-base); }

.table caption {
    padding: var(--space-4) var(--space-5);
    text-align: start;
    font-size: var(--text-sm);
    color: var(--text-muted);
    border-bottom: 1px solid var(--border-subtle);
}

.table th {
    position: sticky;
    top: 0;
    z-index: 1;
    padding: var(--space-3) var(--space-4);
    text-align: start;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-muted);
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--border-default);
    white-space: nowrap;
}

.table td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-subtle);
    vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: none; }

.table tbody tr {
    transition: background-color var(--duration-instant) var(--ease-out);
}
.table-hover tbody tr:hover { background: var(--surface-sunken); }

.table .numeric { text-align: end; font-variant-numeric: tabular-nums; }
.table .actions { text-align: end; white-space: nowrap; }

.table-sort {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: inherit;
    font: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
}
.table-sort:hover { color: var(--text-primary); }

/* ===========================================================================
   Empty, loading and unavailable states
   ===========================================================================
   The brief asks for these to be complete rather than afterthoughts. An empty
   state always says three things: what would be here, why it is not, and the
   one action that changes that.
   =========================================================================== */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-16) var(--space-6);
    text-align: center;
}

.empty-state-icon {
    width: 3rem;
    height: 3rem;
    color: var(--text-muted);
    opacity: 0.6;
}

.empty-state-title { font-size: var(--text-lg); font-weight: var(--weight-semibold); }

.empty-state-body {
    max-width: 34rem;
    color: var(--text-muted);
    text-wrap: pretty;
}

/* Skeleton placeholders match the shape of the content that replaces them, so
   the page does not reflow when real data arrives. */
.skeleton {
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 25%,
        color-mix(in srgb, var(--surface-sunken) 60%, var(--surface-card)) 37%,
        var(--surface-sunken) 63%
    );
    background-size: 400% 100%;
    animation: shimmer 1.4s ease-in-out infinite;
    border-radius: var(--radius-sm);
}

@keyframes shimmer {
    0%   { background-position: 100% 0; }
    100% { background-position: 0 0; }
}

.skeleton-text { height: 0.85em; margin-block: 0.3em; }
.skeleton-title { height: 1.4em; width: 45%; }
.skeleton-line-short { width: 60%; }

@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; background: var(--surface-sunken); }
}

/* A control that exists but cannot be used yet. Shown, not hidden, with the
   reason attached — hiding it leaves the merchant wondering whether the
   feature exists at all. */
.unavailable {
    position: relative;
    opacity: 0.6;
    pointer-events: none;
}

.unavailable-note {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-2);
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-sm);
    color: var(--text-secondary);
    background: var(--surface-sunken);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-sm);
}

/* ===========================================================================
   Navigation
   =========================================================================== */

.nav-list {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    list-style: none;
    margin: 0;
    padding: 0;
}

.nav-link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-height: var(--tap-target);
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    color: var(--text-secondary);
    text-decoration: none;
    border-radius: var(--radius-md);
    transition: background-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.nav-link:hover { background: var(--surface-sunken); color: var(--text-primary); }

/* The current page is marked with aria-current="page", and the style hangs off
   that attribute rather than a class — so the visual state and the
   accessibility state cannot drift apart. */
.nav-link[aria-current="page"] {
    background: var(--surface-brand-soft);
    color: var(--text-brand);
    font-weight: var(--weight-semibold);
}

.nav-icon { width: 1.15rem; height: 1.15rem; flex-shrink: 0; }

.breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
}

.breadcrumb li { display: flex; align-items: center; gap: var(--space-2); }
.breadcrumb li:not(:last-child)::after {
    content: "/";
    color: var(--border-strong);
}
.breadcrumb a { color: var(--text-muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--text-primary); text-decoration: underline; }

/* ===========================================================================
   Tabs
   =========================================================================== */

.tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: 1px solid var(--border-subtle);
    overflow-x: auto;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tab {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-base);
    font-weight: var(--weight-medium);
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    transition: color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}

.tab:hover { color: var(--text-primary); }

.tab[aria-selected="true"], .tab[aria-current="page"] {
    color: var(--text-brand);
    border-bottom-color: var(--surface-brand);
    font-weight: var(--weight-semibold);
}

/* ===========================================================================
   Dialogs
   ===========================================================================
   Built on <dialog>, so focus trapping, Escape to close and the top layer are
   the browser's job rather than ours — which means they behave correctly with
   screen readers without us reimplementing any of it.
   =========================================================================== */

dialog.dialog {
    width: min(32rem, calc(100vw - var(--space-8)));
    max-height: calc(100vh - var(--space-16));
    padding: 0;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-xl);
    background: var(--surface-card);
    color: var(--text-primary);
    box-shadow: var(--shadow-xl);
    overflow: hidden;
}

dialog.dialog::backdrop {
    background: rgb(18 18 17 / 0.5);
    backdrop-filter: blur(2px);
}

dialog.dialog[open] {
    animation: dialog-in var(--duration-normal) var(--ease-out);
}

@keyframes dialog-in {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

.dialog-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-6);
    border-bottom: 1px solid var(--border-subtle);
}

.dialog-title { font-size: var(--text-lg); font-weight: var(--weight-semibold); }
.dialog-body { padding: var(--space-6); overflow-y: auto; }
.dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-6);
    border-top: 1px solid var(--border-subtle);
    background: var(--surface-sunken);
}

/* ===========================================================================
   Toasts
   =========================================================================== */

.toast-region {
    position: fixed;
    inset-block-end: var(--space-5);
    inset-inline-end: var(--space-5);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    max-width: min(26rem, calc(100vw - var(--space-8)));
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--surface-card);
    border: 1px solid var(--border-default);
    border-inline-start: 3px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
    animation: toast-in var(--duration-normal) var(--ease-spring);
}

.toast-success { border-inline-start-color: var(--success-500); }
.toast-danger  { border-inline-start-color: var(--danger-500); }
.toast-warning { border-inline-start-color: var(--warning-500); }

@keyframes toast-in {
    from { opacity: 0; transform: translateX(12px); }
    to   { opacity: 1; transform: translateX(0); }
}

/* ===========================================================================
   Pagination
   =========================================================================== */

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-4);
    flex-wrap: wrap;
}

.pagination-summary { font-size: var(--text-sm); color: var(--text-muted); }
.pagination-controls { display: flex; align-items: center; gap: var(--space-2); }

/* ===========================================================================
   Layout helpers
   =========================================================================== */

.stack > * + * { margin-top: var(--stack-gap, var(--space-4)); }
.stack-sm { --stack-gap: var(--space-2); }
.stack-lg { --stack-gap: var(--space-6); }
.stack-xl { --stack-gap: var(--space-10); }

.row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}
.row-between { justify-content: space-between; }
.row-end { justify-content: flex-end; }
.row-nowrap { flex-wrap: nowrap; }

.grid { display: grid; gap: var(--space-5); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr)); }

.section { padding-block: var(--space-20); }
.section-sm { padding-block: var(--space-12); }

.eyebrow {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-caps);
    text-transform: uppercase;
    color: var(--text-brand);
}

.lede {
    font-size: var(--text-lg);
    line-height: var(--leading-relaxed);
    color: var(--text-secondary);
    text-wrap: pretty;
}

.muted { color: var(--text-muted); }
.small { font-size: var(--text-sm); }
.strong { font-weight: var(--weight-semibold); }
.center { text-align: center; }

/* A measure cap for readable prose. ~70 characters is the upper end of
   comfortable; beyond that the eye loses the line on return. */
.prose { max-width: 38rem; }
.prose p + p { margin-top: var(--space-4); }
.prose h2 { margin-top: var(--space-10); margin-bottom: var(--space-3); }
.prose h3 { margin-top: var(--space-8); margin-bottom: var(--space-2); }
.prose ul, .prose ol { margin-block: var(--space-4); }
.prose li + li { margin-top: var(--space-2); }
