/* ================================================
   Components — reusable UI building blocks.
   ================================================ */

/* --- Buttons --- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    border-radius: var(--radius-md);
    transition: background var(--transition), border-color var(--transition), opacity var(--transition);
    white-space: nowrap;
    line-height: 1.4;
}

/* Icons inside buttons size to the label's font-size — matches the design's
   `<Icons.X size={13} />` pattern. Without this rule SVGs without intrinsic
   width/height collapse to 0×0 inside the inline-flex layout. */
.btn svg {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-primary {
    background: var(--color-primary);
    color: var(--color-on-primary);
}

.btn-primary:hover:not(:disabled) {
    background: var(--color-primary-hover);
}

/* Brand-accent CTA — Claim button on Up-for-grabs rows, anywhere else the
   action needs to read as "brand moment" rather than "destructive" or
   "primary CTA". Same chrome as .btn-primary, accent color tokens. */
.btn-accent {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

.btn-accent:hover:not(:disabled) {
    background: var(--color-accent-hover);
}

/* Pill-shaped button — overrides the default --radius-md (8px). Pair with
   any btn variant: .btn .btn-accent .btn-pill .btn-sm gives a compact
   accent-colored pill. */
.btn-pill {
    border-radius: var(--radius-full);
}

.btn-danger {
    background: var(--color-danger);
    color: var(--color-text-inverse);
}

.btn-danger:hover:not(:disabled) {
    opacity: 0.9;
}

.btn-ghost {
    background: transparent;
    color: var(--color-text);
    border: var(--border);
}

.btn-ghost:hover:not(:disabled) {
    background: var(--color-bg);
}

.btn-ghost-danger {
    background: transparent;
    color: var(--color-danger);
    border: 1px solid var(--color-danger);
}

.btn-ghost-danger:hover:not(:disabled) {
    background: var(--color-danger-bg);
}

.btn-link {
    background: none;
    color: var(--color-text-link);
    padding: 0;
    font-size: var(--font-size-sm);
}

.btn-link:hover {
    text-decoration: underline;
}

.btn-sm {
    padding: var(--space-1) var(--space-3);
    font-size: var(--font-size-xs);
}

/* Icon-only square button — used for row toolbars (Lists like/edit/delete,
   etc). Bordered at rest; the icon inside sizes via the global .btn svg
   rule (1em = 13px at default font-size). The .btn-icon-ghost modifier
   removes the border for floating contexts (chat message action menu). */
.btn-icon {
    width: 26px;
    height: 26px;
    background: transparent;
    border: var(--border);
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.btn-icon:hover {
    background: var(--color-bg-hover);
    color: var(--color-text);
}

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

.btn-icon-ghost {
    border: none;
    border-radius: 5px;
}

.btn-icon-ghost:focus-visible {
    outline-offset: 1px;
}

/* Danger variant — picks up the destructive-action affordance on hover.
   Same chrome as .btn-icon at rest; reads as a normal icon button until
   the user hovers, then shifts to red. Use for delete/remove actions. */
.btn-icon-danger:hover {
    color: var(--color-danger);
    border-color: var(--color-danger);
    background: var(--color-danger-bg);
}

.btn-icon:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

.btn-icon svg {
    width: 13px;
    height: 13px;
    flex-shrink: 0;
}

/* Round/larger variant — 32×32 pill, scaled SVG. Used for prominent
   header actions like the back button + delete button on TaskDetail
   and ChatThread titlebars, and the action pair on the Lists card. */
.btn-icon-round {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-full);
}

.btn-icon-round svg {
    width: 16px;
    height: 16px;
}

/* --- Inputs --- */
.input {
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font-size: var(--font-size-base);
    background: var(--color-surface);
    border: var(--border);
    border-radius: var(--radius-md);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.input:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--color-primary-soft);
}

.input::placeholder {
    color: var(--color-text-muted);
}

select.input {
    cursor: pointer;
    appearance: none;
    /* Chevron fill tracks --color-text-faint. URL-encoded inline because data:
       URIs can't reference CSS custom properties; light-dark() swaps the
       whole URL based on the resolved color-scheme. Light = #75757f, dark
       = #8a8a93. */
    background-image: light-dark(
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%2375757f' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E"),
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238a8a93' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E")
    );
    background-repeat: no-repeat;
    background-position: right 0.75rem center;
    padding-right: 2rem;
}

textarea.input {
    resize: vertical;
    min-height: 80px;
}

.input-sm {
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-sm);
}

/* Form-field label — used by labels above inputs in TaskDetail and similar
   form contexts. */
.form-label {
    display: block;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-text-muted);
    margin-bottom: var(--space-1);
}

/* --- Cards --- */
.card {
    background: var(--color-surface);
    border: var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

/* --- Kanban board layout.
   <768px (phone): vertical stack of full-width cards.
   768–1023px (tablet/laptop): 2-up grid — recovers screen real-estate vs the
     phone stack without forcing the full kanban metaphor; cards keep their
     reading order (card 1 = top-left, card 2 = top-right, ...).
   ≥1024px (desktop): horizontal kanban with per-column scroll + horizontal
     overflow (Trello-style).
   Collapsible-per-card stays meaningful at every breakpoint — a folded card
   shrinks to its header. */
.kanban-board {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

@media (min-width: 768px) and (max-width: 1023.98px) {
    .kanban-board {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
    }
}

@media (min-width: 1024px) {
    .kanban-board {
        flex-direction: row;
        /* Cards align to the top with natural heights (not stretched) — a
           column with 3 items doesn't look bizarrely tall just because the
           board has room. Tall columns hit the per-card max-height below and
           scroll internally instead. */
        align-items: flex-start;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x proximity;
        /* flex:1 fills remaining vertical space so the horizontal scroll-bar
           pins to the board's bottom edge (= viewport bottom on the desktop
           Tasks page). min-width:0 lets the flex item shrink below its
           content's min-content size; without it, the board widens
           page-content, the document scrolls horizontally, and dragging the
           scrollbar moves the page title sideways. */
        flex: 1;
        min-height: 320px;
        min-width: 0;
        /* Asymmetric horizontal margins. Left side keeps a chat-style inset
           from the sidebar — combined with the inner padding-left this puts
           the first column 32px from the sidebar edge (= same inset other
           pages have via page-content's padding). Right side bleeds through
           page-content's right padding so the last column reaches close to
           the viewport edge instead of leaving an awkward 32px gap. Safe
           because page-content has `overflow-x: clip` — any bleed is
           contained at the page-content bounds. */
        margin-left: calc(var(--space-4) * -1);
        margin-right: calc(var(--space-8) * -1);
        padding: var(--space-1) var(--space-4);
    }

    .kanban-board > .card {
        flex: 0 0 auto;
        width: 320px;
        scroll-snap-align: start;
        /* Cap each column at the board's height — header + body scroll
           together when the column has few items; body scrolls alone via
           overflow-y:auto when it has many. */
        max-height: 100%;
        display: flex;
        flex-direction: column;
        min-height: 0;
    }

    .kanban-board > .card > .card-header {
        flex-shrink: 0;
    }

    .kanban-board > .card > .card-body {
        /* No flex:1 — body sizes to content. When content fits, the card is
           naturally short; when it doesn't, the card's max-height takes
           effect and the body shrinks via the default flex-shrink:1, with
           overflow-y:auto activating an internal scroll. */
        overflow-y: auto;
        min-height: 0;
    }

    /* Collapsed columns shrink to header-only naturally (max-height limits
       full height but content is short). No separate rule needed now that
       align-items is flex-start. */
}

/* Trailing actions below the kanban (e.g. "+ New assignee"). Spacing
   independent of the .kanban-board's internal gap so it doesn't get pulled
   into the horizontal scroll track. */
.kanban-after {
    margin-top: var(--space-4);
}

/* Accent variant — tinted soft fill + dashed accent border. Used for the
   Up-for-grabs pool to flag claimable work without competing with row CTAs. */
.card-accent {
    background: var(--color-accent-soft);
    border: 1px dashed var(--color-accent-ring);
    box-shadow: none;
}

.card-accent .card-header {
    border-bottom-color: var(--color-accent-ring);
}

.card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-6);
    border-bottom: var(--border);
}

/* Tighter header for dense list-style cards (e.g. checklist with rows
   underneath). Pair with .card-flush so the rows can clip to the rounded
   corners without a shadow lifting them off the page. */
.card-header-compact {
    padding: var(--space-3) var(--space-4);
}

.card-flush {
    box-shadow: none;
    overflow: hidden;
}

/* Flush cards (TaskCards on the kanban) put their rows directly against the
   card border — the row's own padding handles the content offset, so the
   body wrapper shouldn't add another 24px inset on top. */
.card-flush > .card-body {
    padding: 0;
}

.card-header-title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
}

/* Card collapse toggle — chevron + title in a single button so the whole
   title region is the tap target. HeaderActions stay outside this button
   (siblings in .card-header), so their clicks don't propagate to toggle. */
.card-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    background: transparent;
    border: none;
    padding: 0;
    margin: 0;
    color: inherit;
    font-family: inherit;
    cursor: pointer;
    flex: 1;
    min-width: 0;
    text-align: left;
}

.card-toggle:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 4px;
    border-radius: var(--radius-sm);
}

.card-toggle-chevron {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: var(--color-text-muted);
    transition: transform var(--transition);
}

.card-toggle-collapsed .card-toggle-chevron {
    transform: rotate(-90deg);
}

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

.card-title {
    font-family: var(--font-display);
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-medium);
    letter-spacing: -0.01em;
    margin-bottom: var(--space-6);
}

/* --- List --- */
.list {
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    border: var(--border);
    border-radius: var(--radius-lg);
    overflow: hidden;
}

/* Inside a card body, list items render as individual cards with a gap
   between them — matches the design's per-row "card" treatment instead of
   the flush, shared-border list used standalone (Members, Sessions, etc.). */
.card-body .list {
    border: none;
    border-radius: 0;
    background: transparent;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Rows inside a card-body list sit flush in the card with a hairline above
   every row (including the first, so it parts from the card header). No
   per-row border or radius — that double-bordered the rows and ate into
   the row's horizontal space. */
.card-body .list > .list-item {
    border-top: var(--border);
}

/* Accent-variant cards (Up-for-grabs pool) tint the row separator with the
   accent ring so it reads against the soft-accent background. */
.card-accent .card-body .list > .list-item {
    border-top-color: var(--color-accent-ring);
}

/* Three-column row: [checkbox | content (grows) | meta/actions]. The middle
   1fr track absorbs all spare width so titles extend up to the actions
   column. align-items keeps the fixed-size checkbox + actions vertically
   centered against the multi-line content block. */
.list-item {
    padding: var(--space-2) var(--space-3);
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: var(--space-2);
    min-height: 40px;
    transition: background var(--transition);
}

.list-item:hover {
    background: var(--color-bg-hover);
}

.list-item + .list-item {
    border-top: var(--border);
}

/* min-width: 0 is required so the title's ellipsis truncation works inside
   the 1fr track — without it the implicit min-content keeps the cell wide
   enough to fit the full title and you never see the ellipsis. */
.list-item-content {
    min-width: 0;
}

.list-item-title {
    font-weight: var(--font-weight-medium);
    letter-spacing: -0.1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.list-item-title-complete {
    color: var(--color-text-faint);
    text-decoration: line-through;
}

.list-completed {
    margin-top: var(--space-4);
    opacity: 0.55;
}

.list-completed-label {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding-inline: var(--space-3);
    color: var(--color-text-muted);
    margin-bottom: var(--space-2);
}

.list-item-sub {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Meta row split into two inner slots: actions-left (due + repeat) and
   actions-right (assigner pill or Claim). The outer block is the third
   grid column on .list-item — auto-sized, so it's only as wide as its
   content; that's what lets the content column claim the rest. */
.list-item-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.list-item-actions-left,
.list-item-actions-right {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

.list-item-actions-left {
    margin-right: auto;
}

/* --- Task checkbox (Orbit-style) --- */
.task-check {
    width: 18px;
    height: 18px;
    border-radius: var(--radius-sm);
    border: 1.5px solid var(--color-border-strong);
    background: transparent;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    cursor: pointer;
    /* Relative + overflow:hidden anchor the in-progress half-fill pseudo and
       clip it to the rounded corners. No effect on the centered check svg
       (10×10 inside an 18×18 box stays well within bounds). */
    position: relative;
    overflow: hidden;
    transition: border-color var(--transition), background var(--transition);
}

.task-check:hover {
    border-color: var(--color-primary);
}

.task-check-checked {
    background: var(--color-accent);
    border-color: var(--color-accent);
}

.task-check-checked:hover {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
}

/* Half-fill the bottom of the checkbox like a glass filling up — top half
   stays transparent (canvas shows through), bottom half is accent. Conveys
   "task in progress: partway there" more literally than a centered inner
   square. Parent's overflow:hidden + border-radius clips the fill to the
   rounded bottom corners. */
.task-check-inprogress::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 50%;
    background: var(--color-accent);
}

.task-check-readonly {
    cursor: default;
}

.task-check-readonly:hover {
    border-color: var(--color-border-strong);
}

.task-check svg { width: 10px; height: 10px; }

/* --- Assigner pill (avatar + name; row meta when someone else assigned the task) --- */
.assigner-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 10px 2px 2px;
    background: var(--color-bg-hover);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    white-space: nowrap;
}

.assigner-pill-name {
    line-height: 1;
}

/* --- Due-date badge --- */
.due-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.due-badge-overdue {
    color: var(--color-danger);
}

.due-badge svg { width: 12px; height: 12px; }

/* --- Recurring-task badge --- */
.recurring-badge {
    display: inline-flex;
    align-items: center;
    color: var(--color-text-muted);
    opacity: 0.7;
}

.recurring-badge svg { width: 12px; height: 12px; }

.day-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    border: none;
    padding: 0;
    margin: 0;
}

/* Single-letter circular chip — M T W T F S S. Active = ink fill, canvas
   text. The native checkbox stays in the DOM for keyboard + screen readers
   but is visually hidden; the label itself is the chip. */
.day-choice {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    user-select: none;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.day-choice:hover {
    border-color: var(--color-primary);
}

.day-choice:has(input:checked) {
    background: var(--color-text);
    border-color: var(--color-text);
    color: var(--color-surface);
    font-weight: var(--font-weight-bold);
}

.day-choice input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.day-choice:focus-within {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.day-picker-hint {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* --- Repeats pill row (TaskDetail).
   Independent pills, no track. Active = accent-soft fill + accent border +
   accent text + leading repeat icon. Differs deliberately from .form-segments
   (the Status segmented track) — Status is a 3-way control where the track
   reads as "current state", Repeats is a row of indepedent recurrence options
   where one is picked. */
.repeat-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    border: none;
    padding: 0;
    margin: 0 0 var(--space-2) 0;
}

.repeat-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 5px 11px;
    border-radius: var(--radius-full);
    background: transparent;
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    user-select: none;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.repeat-pill:hover {
    border-color: var(--color-primary);
}

.repeat-pill:has(input:checked) {
    background: var(--color-accent-soft);
    border-color: var(--color-accent);
    color: var(--color-accent);
    font-weight: var(--font-weight-semibold);
}

.repeat-pill input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.repeat-pill:focus-within {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.repeat-pill-icon {
    width: 10px;
    height: 10px;
    flex-shrink: 0;
}

/* --- Read-only form field (assignee viewing a task they didn't assign) --- */
.form-readonly {
    padding: 10px 14px;
    min-height: 40px;
    background: var(--color-bg-hover);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    color: var(--color-text);
    line-height: 1.4;
}

.form-readonly-multiline {
    white-space: pre-wrap;
    min-height: calc(1.4em * 3 + 20px);
}

/* --- Status segments: 3-state pill track at the top of TaskDetail.
   Track = surface-card rounded pill; active segment lifts to a canvas pill
   with a soft inner shadow. Each segment carries a colored dot whose tone
   maps to the status — muted for Todo, primary for InProgress, success for
   Done. Distinct from .form-segments (used by Repeats) which is a row of
   independent pills with no track. */
.status-segments {
    display: inline-flex;
    padding: 3px;
    gap: 2px;
    background: var(--color-neutral-100);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    margin: 0;
}

.status-segment {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 11px;
    border-radius: var(--radius-full);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--color-text-muted);
    cursor: pointer;
    user-select: none;
    background: transparent;
    transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}

.status-segment:has(input:checked) {
    background: var(--color-surface);
    color: var(--color-text);
    font-weight: var(--font-weight-semibold);
    box-shadow: var(--shadow-sm);
}

.status-segment input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.status-segment:focus-within {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    opacity: 0.55;
}

.status-segment:has(input:checked) .status-dot {
    opacity: 1;
}

.status-dot-pending    { background: var(--color-text-muted); }
.status-dot-inprogress { background: var(--color-accent); }
.status-dot-complete   { background: var(--color-badge-complete); }

/* --- "Assigned by" pill on TaskDetail (avatar + name + date). Bigger
   than the row-level .assigner-pill — sized to read as a primary surface
   under the title rather than meta in a row's action area. */
.assigned-by-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px 6px 6px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-full);
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    align-self: flex-start;
    width: fit-content;
    white-space: nowrap;
}

.assigned-by-pill-name {
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
}

.assigned-by-pill-date {
    color: var(--color-text-muted);
}

/* --- Task title display input — bigger, soft-bordered, display-family.
   Used in place of the default .input when the title is the primary
   editable element on the page. */
.task-title-input {
    font-family: var(--font-display);
    font-size: 1.5rem;
    font-weight: var(--font-weight-medium);
    letter-spacing: -0.01em;
    line-height: 1.2;
    padding: 12px 14px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    color: var(--color-text);
    width: 100%;
    box-sizing: border-box;
    outline: none;
}

.task-title-input:focus {
    border-color: var(--color-primary);
}

.task-notes-input {
    min-height: 80px;
    line-height: 1.45;
}

/* --- Input with leading icon (date pickers, etc.).
   Wraps a native input with a chrome row so the icon reads as part of the
   field, not a separate adornment. Clicking the label triggers the input.
   The native picker stays — typing dates is a UX hole we don't want to
   reopen — but the field reads like the rest of the form. */
.input-with-icon {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: text;
    width: 100%;
    box-sizing: border-box;
    transition: border-color var(--transition);
}

.input-with-icon:focus-within {
    border-color: var(--color-primary);
}

.input-with-icon > svg {
    width: 14px;
    height: 14px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.input-with-icon > input {
    flex: 1;
    min-width: 0;
    border: none;
    background: transparent;
    color: var(--color-text);
    font-size: var(--font-size-base);
    font-family: inherit;
    outline: none;
    padding: 0;
}

/* --- Activity timeline (TaskDetail).
   Cheap V1: 1-3 events from existing TaskResponse fields (assigned / claimed
   / completed). No audit log, no edit history. Each entry = small avatar +
   "[Who] [action]" + timestamp. */
.activity-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 4px;
}

.activity-entry {
    display: flex;
    align-items: flex-start;
    gap: 10px;
}

.activity-entry-body {
    flex: 1;
    min-width: 0;
}

.activity-entry-text {
    font-size: var(--font-size-sm);
    color: var(--color-text);
    line-height: 1.45;
}

.activity-entry-who {
    font-weight: var(--font-weight-semibold);
}

.activity-entry-time {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
}

/* --- TaskDetail shell.
   Single DOM tree that adapts to two layouts via media query:
     <1024px: full-viewport panel (immersive full-screen detail), backdrop
              hidden because there's nothing to see behind it.
     ≥1024px: right-aligned 540px slide-out with dimmed backdrop; the kanban
              board stays visible behind so the user keeps context.
   The Tasks board (`/tasks` route) renders Tasks.razor below in both cases;
   on mobile the panel covers it, on desktop it shows through the backdrop. */
.task-detail-shell {
    position: fixed;
    inset: 0;
    z-index: 50;
}

.task-detail-backdrop {
    display: none;
}

.task-detail-panel {
    position: absolute;
    inset: 0;
    /* Grey panel body so the titlebar + actionbar (both white) and the
       individual inputs/pills (also white) read as floating cards on a
       light surface — matches the design's task-edit treatment. The panel
       itself doesn't scroll; the form inside it does. That way titlebar
       (top) and actionbar (bottom) stay locked to the panel edges and
       only the middle body scrolls. */
    background: var(--color-surface-alt);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

@media (min-width: 1024px) {
    .task-detail-backdrop {
        display: block;
        position: absolute;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        cursor: pointer;
        animation: task-detail-fade-in 120ms ease-out;
    }

    .task-detail-panel {
        inset: 0 0 0 auto;
        width: 540px;
        max-width: 100vw;
        box-shadow: -8px 0 32px rgba(0, 0, 0, 0.18);
        animation: task-detail-slide-in 220ms cubic-bezier(0.32, 0.72, 0, 1);
    }

    @keyframes task-detail-slide-in {
        from { transform: translateX(100%); }
        to   { transform: translateX(0); }
    }

    @keyframes task-detail-fade-in {
        from { opacity: 0; }
        to   { opacity: 1; }
    }

    @media (prefers-reduced-motion: reduce) {
        .task-detail-panel,
        .task-detail-backdrop {
            animation: none;
        }
    }
}

/* Titlebar inside the panel — white strip with a bottom border that sits
   over the panel's grey body, mirroring the bottom .task-actionbar. */
.task-detail-panel > .page-titlebar {
    padding: var(--space-4);
    margin-bottom: 0;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}

/* The form is the scrolling middle of the panel — flex:1 fills the space
   between titlebar (top) and actionbar (bottom), overflow-y:auto scrolls
   long content internally without pushing the actionbar off-screen. */
.task-detail-form {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-4);
}

/* Bottom action bar — a flex-item sibling of .task-detail-form inside the
   panel (NOT a form child). The panel's flex column lays it out as the
   final row, naturally pinned to the panel bottom; no sticky tricks. The
   Save button uses form="task-detail-form" to submit the sibling form. */
.task-actionbar {
    flex-shrink: 0;
    padding: 12px var(--space-4);
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

.task-actionbar-save {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.task-actionbar-save svg {
    width: 14px;
    height: 14px;
}

/* Page-titlebar back-vs-close icons — left arrow on mobile (back to board),
   X on desktop (close overlay). Same GoBack handler. */
.page-titlebar-back-mobile { display: inline; }
.page-titlebar-back-desktop { display: none; }

@media (min-width: 1024px) {
    .page-titlebar-back-mobile { display: none; }
    .page-titlebar-back-desktop { display: inline; }
}

/* --- Assignee row on TaskDetail.
   Compact picker pattern: Up-for-grabs (always first) + 3 most-recent
   assignees + Search pill that opens the AssigneePicker overlay. The row
   stays the same width regardless of group size — 4-person family or
   40-person sports club look identical. */
.assignee-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

.assignee-pill {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px 5px 5px;
    border-radius: var(--radius-full);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    user-select: none;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.assignee-pill:hover {
    border-color: var(--color-primary);
}

/* Filled-ink selected state — matches the design's recents pill. Stays
   visually distinct from the picker rows (which use filled-accent for their
   selected state — a deliberate split so the inline pill reads as "this is
   the choice" and the picker row reads as "this option matches"). */
.assignee-pill-selected {
    background: var(--color-text);
    border-color: var(--color-text);
    color: var(--color-surface);
    font-weight: var(--font-weight-semibold);
}

/* Up-for-grabs pill — dashed accent border + sparkle icon avatar. First-class
   open-pool option, kept distinct from individual assignment. Border uses the
   single `--color-accent-ring` token (≈33% alpha) so every "open" dashed
   border in the system reads at the same intensity. Text + sparkle stay full
   accent for legibility. */
.assignee-pill-open {
    border-style: dashed;
    border-color: var(--color-accent-ring);
    color: var(--color-accent);
}

/* Up-for-grabs in its selected state keeps the accent identity rather than
   flipping to ink — the dashed border is the brand mark for "open pool". */
.assignee-pill-open.assignee-pill-selected {
    background: var(--color-accent-soft);
    border-color: var(--color-accent-ring);
    color: var(--color-accent);
}

.assignee-pill-avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: inline-grid;
    place-items: center;
    flex-shrink: 0;
    color: var(--color-text-inverse);
    font-size: 10.5px;
    font-weight: 600;
    line-height: 1;
}

.assignee-pill-avatar-open {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

/* Search pill — surface-card fill telegraphs "this opens something". */
.assignee-pill-search {
    background: var(--color-neutral-100);
    color: var(--color-text-muted);
    padding-left: 12px;
}

.assignee-pill-search svg {
    width: 12px;
    height: 12px;
}

/* --- Colour-swatch picker.
   Used by Profile (avatar colour) and Members (group colour) — the same
   row-of-swatches pattern with a focus/active outline. Outline-and-fill
   structure: the button is a transparent frame so a focus/active outline
   can ring it without washing the colour out; the inner span carries the
   actual colour fill. */
.color-swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

.color-swatch {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: none;
    padding: 0;
    cursor: pointer;
    border-radius: var(--radius-full);
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    outline: 2px solid transparent;
    outline-offset: 2px;
    transition: outline-color var(--transition);
}

.color-swatch:disabled { cursor: default; opacity: 0.6; }

.color-swatch-fill {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-full);
    box-shadow: inset 0 0 0 1px var(--color-border);
}

.color-swatch-active { outline-color: var(--color-text); }

.color-swatch:focus-visible { outline-color: var(--color-primary); }

/* --- Confirm dialog ---
   Centered modal with backdrop for destructive-action confirmations (delete
   list, delete group, etc). Smaller and tighter than .picker-sheet — no
   scrolling content, just title + message + two buttons. */
.confirm-dialog-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 100;
    animation: confirm-dialog-fade-in 120ms ease-out;
}

.confirm-dialog {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-5);
    width: 360px;
    max-width: calc(100vw - 32px);
    z-index: 101;
    animation: confirm-dialog-fade-in 120ms ease-out;
}

.confirm-dialog-title {
    font-family: var(--font-display);
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    margin: 0 0 var(--space-2);
    letter-spacing: -0.01em;
}

.confirm-dialog-message {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    margin: 0 0 var(--space-5);
    line-height: 1.5;
}

.confirm-dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

@keyframes confirm-dialog-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .confirm-dialog,
    .confirm-dialog-backdrop {
        animation: none;
    }
}

/* --- Enable-push pre-prompt modal ---
   One-shot post-login dialog asking the user to opt into web push. Shares
   the centered-card shape with .confirm-dialog but carries a third tier of
   action ("Don't ask again") below the primary row, so it gets its own
   class set rather than reusing the confirm-dialog kit. */
.enable-push-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 100;
    animation: enable-push-modal-fade-in 120ms ease-out;
}

.enable-push-modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-5);
    width: 400px;
    max-width: calc(100vw - 32px);
    z-index: 101;
    animation: enable-push-modal-fade-in 120ms ease-out;
}

.enable-push-modal-title {
    font-family: var(--font-display);
    font-size: var(--font-size-xl);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    margin: 0 0 var(--space-2);
    letter-spacing: -0.01em;
}

.enable-push-modal-message {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
    margin: 0 0 var(--space-4);
    line-height: 1.5;
}

.enable-push-modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

.enable-push-modal-tertiary {
    display: flex;
    justify-content: center;
    margin-top: var(--space-3);
}

@keyframes enable-push-modal-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .enable-push-modal,
    .enable-push-modal-backdrop {
        animation: none;
    }
}

/* --- Picker-sheet primitive.
   Bottom sheet on mobile (one-handed reach), centered modal on desktop —
   single DOM tree, positioning swapped via media query. Used by the assignee
   picker on TaskDetail and the new-message picker on the chat index; same
   shape, different content rows. The backdrop is a separate sibling so click
   events on it don't bubble through the sheet. */
.picker-sheet-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 100;
    animation: picker-sheet-fade-in 120ms ease-out;
}

.picker-sheet {
    position: fixed;
    background: var(--color-surface);
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.18);
    z-index: 101;
    display: flex;
    flex-direction: column;
    /* Mobile: bottom sheet with drag-handle-friendly rounded top corners. */
    left: 0;
    right: 0;
    bottom: 0;
    max-height: 78vh;
    border-top-left-radius: var(--radius-xl, 16px);
    border-top-right-radius: var(--radius-xl, 16px);
    animation: picker-sheet-slide-up 180ms ease-out;
}

@media (min-width: 768px) {
    .picker-sheet {
        left: 50%;
        right: auto;
        bottom: auto;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 540px;
        max-width: calc(100vw - 32px);
        max-height: 78vh;
        border-radius: var(--radius-lg);
        box-shadow: 0 24px 48px rgba(0, 0, 0, 0.24);
        animation: picker-sheet-fade-in 120ms ease-out;
    }
}

@keyframes picker-sheet-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes picker-sheet-slide-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .picker-sheet,
    .picker-sheet-backdrop {
        animation: none;
    }
}

.picker-sheet-handle {
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--color-border-strong);
    margin: 8px auto 4px;
    flex-shrink: 0;
}

@media (min-width: 768px) {
    .picker-sheet-handle { display: none; }
}

.picker-sheet-search {
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}

.picker-sheet-search-input {
    width: 100%;
    padding: 10px 14px;
    background: var(--color-neutral-100);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    color: var(--color-text);
    outline: none;
    box-sizing: border-box;
}

.picker-sheet-search-input:focus {
    border-color: var(--color-primary);
}

.picker-sheet-list {
    flex: 1;
    overflow-y: auto;
    padding: 8px 0 12px;
}

.picker-sheet-section-label {
    padding: 12px 18px 4px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-muted);
}

.picker-sheet-row {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 11px 16px;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    color: var(--color-text);
    transition: background var(--transition);
}

.picker-sheet-row:hover {
    background: var(--color-bg-hover);
}

.picker-sheet-row-selected {
    background: var(--color-accent-soft);
}

.picker-sheet-row-body {
    flex: 1;
    min-width: 0;
}

.picker-sheet-row-name {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-medium);
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.picker-sheet-row-selected .picker-sheet-row-name {
    font-weight: var(--font-weight-semibold);
}

.picker-sheet-row-hint {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    margin-top: 2px;
}

.picker-sheet-row-check {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--color-accent);
    color: var(--color-text-inverse);
    display: grid;
    place-items: center;
    flex-shrink: 0;
}

.picker-sheet-empty {
    padding: 24px 16px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* Assignee-specific Up-for-grabs sparkle avatar. Lives next to the picker
   chrome since it's the only row that needs accent-dashed treatment.
   Dashed border uses --color-accent-ring to match the system-wide dashed
   "open" intensity. */
.picker-sheet-row-avatar-open {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    border: 1px dashed var(--color-accent-ring);
}

/* Tappable content area — title block opens the inline edit form. */
.list-item-content-clickable {
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.list-item-content-clickable:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* --- Badges --- */
.badge {
    display: inline-block;
    padding: var(--space-1) var(--space-2);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    border-radius: var(--radius-full);
    line-height: 1;
}

.badge-accent {
    background: var(--color-success-bg);
    color: var(--color-badge-complete);
}

/* --- Alerts --- */
.alert {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    margin-top: var(--space-4);
}

.alert-success {
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.alert-danger {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.alert-warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}

.alert-info {
    background: var(--color-info-bg);
    color: var(--color-info);
}

/* --- Spinner --- */
.spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid var(--color-border);
    border-top-color: var(--color-accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

.spinner-lg {
    width: 32px;
    height: 32px;
    border-width: 3px;
}

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

.loading {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

/* --- Tabs --- */
.tabs {
    display: flex;
    gap: var(--space-1);
    border-bottom: var(--border);
    margin-bottom: var(--space-6);
}

.tab {
    padding: var(--space-2) var(--space-4);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-medium);
    color: var(--color-text-muted);
    border-bottom: 2px solid transparent;
    transition: color var(--transition), border-color var(--transition);
    margin-bottom: -1px;
}

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

.tab-active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

/* --- Empty state --- */
.empty {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    padding: var(--space-4);
}

/* --- Inline row --- */
.inline-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* --- Conflict resolution cards (ConflictDiffView + per-type sibling cards).
   Lives in the global sheet because TaskEditConflictCard and
   ChecklistItemEditConflictCard reuse these classes without their own
   scoped CSS — Blazor scoping would isolate them from the dispatcher. --- */
.conflict-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-3);
}

.conflict-card-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
}

.conflict-card-kind {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--color-text);
}

.conflict-card-meta {
    font-size: var(--font-size-sm);
    color: var(--color-text-muted);
}

.conflict-diff {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}

.conflict-diff-side {
    padding: var(--space-3);
    background: var(--color-surface-alt);
    border-radius: var(--radius-sm);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.conflict-diff-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
}

.conflict-diff-row {
    display: flex;
    gap: var(--space-2);
    font-size: var(--font-size-sm);
}

.conflict-diff-key {
    flex: 0 0 5.5rem;
    color: var(--color-text-muted);
}

.conflict-diff-val {
    flex: 1 1 auto;
    color: var(--color-text);
    word-break: break-word;
}

.conflict-card-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

@media (max-width: 600px) {
    .conflict-diff {
        grid-template-columns: 1fr;
    }
}

/* --- Section header --- */
.section-title {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-medium);
    margin-top: var(--space-8);
    margin-bottom: var(--space-4);
}

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

/* --- Toast notification --- */
.toast {
    position: fixed;
    bottom: var(--space-6);
    right: var(--space-6);
    background: var(--color-text);
    color: var(--color-bg);
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius-lg);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    box-shadow: var(--shadow-md);
    z-index: 100;
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 0.3s ease, transform 0.3s ease;
    max-width: 400px;
}

.toast-visible {
    opacity: 1;
    transform: translateY(0);
}

.toast-success {
    background: var(--color-success-bg);
    color: var(--color-success-text);
    border: 1px solid var(--color-success);
}

.toast-error {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
    border: 1px solid var(--color-danger);
}

.toast-info {
    background: var(--color-info-bg);
    color: var(--color-text);
    border: 1px solid var(--color-info);
}

/* --- Blazor framework error overlay --- */
#blazor-error-ui {
    color-scheme: light only;
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    padding: var(--space-3) var(--space-5);
    background: var(--color-warning-bg);
    color: var(--color-text);
    font-size: var(--font-size-sm);
    box-shadow: var(--shadow-md);
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: var(--space-2);
    right: var(--space-3);
    cursor: pointer;
}

/* --- Error boundary --- */
.error-boundary {
    max-width: 32rem;
    margin: var(--space-8) auto;
    padding: var(--space-6);
    background: var(--color-surface);
    border: 1px solid var(--color-danger);
    border-radius: var(--radius-lg);
    text-align: center;
}

.error-boundary h2 {
    margin: 0 0 var(--space-2);
    color: var(--color-danger);
}

.error-boundary p {
    margin: 0 0 var(--space-5);
    color: var(--color-text-muted);
}

.error-boundary-actions {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
}
