/* ============================================
   REVIEW SYSTEM - PREMIUM UI
   ============================================
   Modern, glassmorphism-styled review dashboard
   Matches FairlyRemote premium design language
   ============================================ */

/* ============================================
   CSS CUSTOM PROPERTIES FOR REVIEW SYSTEM
   ============================================ */
:root {
    --review-card-bg: rgba(255, 255, 255, 0.85);
    --review-card-border: rgba(203, 213, 225, 0.5);
    --review-card-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
    --review-accent: var(--primary-500);
    --review-accent-light: var(--primary-50);

    /* Status tokens — anchored to STATUS_PRESENTATION
       (js/shared/event-status-utils.js). Keep these in sync. */
    --rc-status-available: #22c55e;  /* available */
    --rc-status-working:   #16a34a;  /* working */
    --rc-status-tentative: #f59e0b;  /* tentative */
    --rc-status-busy:      #dc2626;  /* busy / blocking */
    --rc-status-away:      #d97706;  /* away / leave — fuchsia, distinct from sleep */

    /* Non-status accent: "attention required" (banners, action prompts).
       Distinct from --rc-status-tentative because the semantics differ —
       tentative = "this might happen"; attention = "you must do something".
       Keeps the orange-as-attention signal without being treated as drift
       in future status-color audits. */
    --rc-accent-attention:        #f97316;
    --rc-accent-attention-strong: #ea580c;

    /* CONFLICT RED (2026-08-15). The one red the conflict surfaces are
       allowed to paint: the overlap column, the panel's warning glyph and
       the count badge riding the Scheduled tab.
       Do NOT reach for var(--danger) here. On 2026-08-15 the owner split
       busy into two tokens (view-mode-strip.css): --shading-busy #dc2626 is
       the RED that busy TIME is shaded, while --status-busy is now SLATE
       #64748b for busy-visibility event blocks. --danger is still aliased to
       --status-busy, so it silently resolves to SLATE and a var(--danger)
       overlap band would have painted grey on a grey track: the warning
       would have disappeared. Anchor on --shading-busy, the canonical red. */
    --rc-conflict-red: var(--shading-busy, #dc2626);

    /* Legacy aliases — kept as deliberate semantic shortcuts after Stage C.
       They read more naturally at call sites ("border-color: var(--review-urgent)"
       conveys intent better than "border-color: var(--rc-status-busy)" on a
       deadline-warning border). Anchored to canonical tokens so a single
       palette change still propagates everywhere. */
    --review-urgent: var(--rc-status-busy);
    --review-urgent-light: rgba(220, 38, 38, 0.1);
    --review-warning: var(--rc-status-tentative);
    --review-warning-light: rgba(245, 158, 11, 0.1);
    --review-success: var(--rc-status-available);
    --review-success-light: rgba(34, 197, 94, 0.1);

    /* Design tokens — type scale & spacing */
    --rc-font-heading: 14px;
    --rc-font-body: 13px;
    --rc-font-secondary: 12px;
    --rc-font-caption: 11px;
    --rc-font-tiny: 10px;
    --rc-spacing-card: 10px; /* 12→10 (2026-07-12 densify): tighter card padding fits more per screen */
    --rc-spacing-section: 8px;
    --rc-spacing-inline: 6px;
    --rc-radius-card: 8px;
    --rc-radius-pill: 12px;
    --rc-radius-btn: 6px;
}

body.dark-mode {
    --review-card-bg: rgba(30, 41, 59, 0.85);
    --review-card-border: rgba(71, 85, 105, 0.5);
    --review-card-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}

/* ============================================
   REVIEW CENTER BUTTON RESET
   Override global .btn { flex: 1; min-height: 44px }
   so buttons size to their content inside the panel
   ============================================ */
.review-modal .btn,
.review-dashboard-content .btn {
    flex: 0 0 auto;
    min-height: unset;
    padding: 6px 14px;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    border-radius: var(--rc-radius-btn, 6px);
}

/* Shared disabled state for every Review Center button. This is an
   action-heavy surface where handlers disable buttons in-flight (accept,
   submit-vote, schedule-winner, withdraw, undo…); without this they looked
   identical to enabled. Covers the bespoke action/vote/visibility buttons
   too, which aren't plain .btn. */
.review-dashboard-content .btn:disabled,
.review-dashboard-content .btn[disabled],
.review-dashboard-content button:disabled,
.synced-vis-btn:disabled,
.synced-sweep-btn:disabled,
.submit-vote-btn:disabled,
.accept-all-btn:disabled,
.schedule-winner-btn:disabled,
.rc-withdraw-btn:disabled,
.undo-accept-btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    box-shadow: none;
}

/* IN-FLIGHT state. `disabled` above only dims a button; on the join-request
   buttons that dim covers two chained network hops plus a refetch, which read
   as "the button is dead" rather than "the work is running" (user 2026-08-22:
   "accepting or declining a request is slow, perhaps a loading ... visual
   response is necessary"). aria-busy is the trigger so the state is announced
   to assistive tech as well as drawn, matching .sync-action[aria-busy] in
   calendar-views.css.

   The spinner is an ::after and the label is untouched: .request-actions .btn
   is white-space:nowrap inside a flex row, so swapping the text for
   "Approving…" would resize the button and shift the row under the cursor.
   The busy button keeps FULL opacity — it is working, not unavailable — while
   its disabled siblings stay dimmed by the rule above.

   ⚠️Must out-specify that :disabled rule — a busy button IS disabled, so both
   match, and `cursor: not-allowed` won on source order (caught in the live
   render 2026-08-22: the working button read as a blocked one). The :disabled
   qualifier raises specificity to (0,3,1) so progress wins. */
.review-dashboard-content button[aria-busy="true"],
.review-dashboard-content button[aria-busy="true"]:disabled {
    position: relative;
    opacity: 1;
    cursor: progress;
}

.review-dashboard-content button[aria-busy="true"] > i,
.review-dashboard-content button[aria-busy="true"] > svg {
    visibility: hidden;
}

/* Sits over the hidden leading icon, so the button's width never changes. */
.review-dashboard-content button[aria-busy="true"]::after {
    content: '';
    position: absolute;
    left: 12px;
    top: 50%;
    width: 12px;
    height: 12px;
    margin-top: -6px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

/* Buttons with no leading icon (the spinner would otherwise overlap the label):
   centre it and hide the text instead. */
.review-dashboard-content button[aria-busy="true"].rc-busy-center {
    color: transparent;
}

.review-dashboard-content button[aria-busy="true"].rc-busy-center::after {
    left: 50%;
    margin-left: -6px;
    border-color: var(--text-secondary, #64748b);
    border-right-color: transparent;
}

@media (prefers-reduced-motion: reduce) {
    /* Keep the affordance, drop the motion — a static ring still marks the
       button as busy for anyone who has asked the OS for less animation. */
    .review-dashboard-content button[aria-busy="true"]::after {
        animation: none;
        opacity: 0.6;
    }
}

/* ============================================
   COARSE-POINTER TAP-FLOOR EXEMPTION
   The global WCAG floor (style.css ~975 + responsive.css ~382) slaps
   min-width:44px; min-height:44px on every <button>/[role=button]/.chip
   on touch devices. Inside the Review Center that floor inflates a whole
   family of intentionally-tiny inline controls into 44px boxes, and
   because several of them set width/height but no min-height, the floor
   distorts them into tall ovals (e.g. the 26x26 rank circle became 26x44).
   This is invisible under desktop emulation, so it only shows on a real
   phone. Mirror the existing team-status-bar exemption (style.css ~988):
   release the floor for these specific small controls; genuine tap targets
   (action buttons, the main tabs, sub-tabs) keep the 44px floor. */
@media (hover: none) and (pointer: coarse) {
    /* The rank circle is a true square indicator — collapse it fully to its
       intrinsic size so the 44px floor stops stretching it into a tall oval. */
    .review-modal .rank-circle,
    #review-dashboard-modal .rank-circle {
        min-width: 0 !important;
        min-height: 0 !important;
    }

    /* The inline X controls (mark-unavailable, dismiss batch tip, clear search)
       were also collapsed to 0, leaving 22px tap targets a thumb keeps missing.
       Keep the 44px-WIDTH floor released (it broke the inline rows) but give
       them a comfortable 32px tap HEIGHT, matching the chip row below. */
    .review-modal .rc-batch-tip-close,
    .review-modal .rc-search-clear,
    #review-dashboard-modal .rc-batch-tip-close,
    #review-dashboard-modal .rc-search-clear {
        min-width: 0 !important;
        min-height: 32px !important;
    }

    /* Inline pill/segment controls that still want a comfortable tap height
       but must not be forced to a 44px *width* (which broke the chip rows). */
    .review-modal .privacy-filter-chip,
    .review-modal .rc-triage-seg,
    .review-modal .btn-xs,
    .review-modal .conflict-action-btn,
    #review-dashboard-modal .privacy-filter-chip,
    #review-dashboard-modal .rc-triage-seg,
    #review-dashboard-modal .btn-xs,
    #review-dashboard-modal .conflict-action-btn {
        min-width: 0 !important;
        min-height: 32px !important;
    }

    /* The ⋮ "more actions" button is the ONLY path to Tentative / Delegate;
       its 36px box is below the comfortable touch floor. It is a genuine tap
       target (not an inline micro-control), so give it the full 44px. */
    .review-modal .more-options-btn,
    #review-dashboard-modal .more-options-btn {
        min-width: 44px !important;
        min-height: 44px !important;
    }

    /* Main tabs + sub-tabs are the primary navigation; they get a 44px floor
       in the ≤480 block, but the 481–768 coarse range (tablets, landscape
       phones) fell between the ≤480 rule and the pointer-keyed exemption and
       got neither. Key the floor to the pointer so every touch device matches. */
    .review-tab {
        min-height: 44px;
        padding: 10px 14px;
    }
    .review-sub-tabs .sub-tab {
        min-height: 44px;
        padding: 10px 14px;
    }
}

/* ============================================
   SHARED CARD LAYOUT UTILITIES (rc-*)
   Reusable layout classes for consistent card
   structure across all Review Center tabs
   ============================================ */

/* Card header — flex column container for title/meta/status rows */
.rc-card-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: var(--rc-spacing-section);
}

/* Title row — badge + title LEFT, key info RIGHT */
.rc-title-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* Right-aligned zone in title row */
.rc-title-right {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    flex-shrink: 0;
    white-space: nowrap;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

/* Meta line — dot-separated items LEFT, counts/badges RIGHT */
.rc-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

.rc-meta-line > span {
    white-space: nowrap;
}

.rc-meta-line > span:not(:last-child):not(.rc-meta-right)::after {
    content: "\00B7";
    margin: 0 6px;
    color: var(--slate-300);
}

/* Right-aligned zone in meta line. Wraps cleanly on narrow widths —
   trust + quota fall onto a second row instead of overflowing the card. */
.rc-meta-right {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-left: auto;
    flex-wrap: wrap;
    row-gap: 4px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

.rc-meta-right > span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* Location folded inline into the meta line (2026-07-12 densify — was its own
   .rc-location-line row). Caps its width + ellipsises so a long venue degrades
   gracefully instead of shoving the trust chip off the row. The full location
   still lives in the "see details" panel (hasCardDetails includes item.location). */
.rc-meta-location {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 40%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rc-meta-location i { color: var(--slate-400, #94a3b8); font-size: 11px; }

/* Status line — auto-accept countdown · "Pending your review".
   Uses gap-based separators (not &middot;) so wrap doesn't strand a dot. */
.rc-status-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-600);
    /* padding 2px 0 → 0 (2026-07-12 densify): the row-gap on the card body
       already spaces this row; the extra 2px top/bottom just added height. */
    padding: 0;
}

/* A1 — conflict line: the recipient is double-booked at this time.
   Uses the canonical busy token (not a raw hex) so the traffic-light red
   stays consistent across the surface. */
.rc-conflict-line {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--rc-status-busy, #dc2626);
    font-weight: 600;
}
.rc-conflict-line i { font-size: 11px; }

/* Compact conflict chip on the card face: signals THAT a clash exists (the
   decision-critical part) without spelling out the events — that detail lives
   in the disclosure panel. Keeps the canonical busy hue so the semantic is
   intact, but reads as a small count, not a full alarm. */
.rc-conflict-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    color: var(--rc-status-busy, #dc2626);
    background: var(--cmx-6126d85598);
    background: color-mix(in srgb, var(--rc-status-busy, #dc2626) 10%, transparent);
}
.rc-conflict-chip i { font-size: 10px; }
body.dark-mode .rc-conflict-chip {
    color: #f87171;
    background: var(--cmx-2828f1eaf9);
    background: color-mix(in srgb, #f87171 16%, transparent);
}

/* A4 — leave coverage: teammates already away. Informational, amber. */
.rc-coverage-line {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--rc-status-tentative, #f59e0b);
    font-weight: 600;
}
.rc-coverage-line i { font-size: 11px; }

/* Tier 2 — the running off-hours cost of accepting, for the recipient. Amber
   (accommodation hue), distinct from the busy-red conflict line. */
.rc-fairness-consequence {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: #b45309;
    font-weight: 600;
}
.rc-fairness-consequence i { font-size: 11px; color: var(--rc-status-tentative, #f59e0b); }

body.dark-mode .rc-conflict-line { color: #f87171; }
body.dark-mode .rc-coverage-line { color: #fbbf24; }
body.dark-mode .rc-fairness-consequence { color: #fbbf24; }

/* Location line — icon + address */
.rc-location-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

.rc-location-line i {
    font-size: 10px;
    color: var(--slate-400);
}

/* Actions bar — secondary LEFT, primary RIGHT */
.rc-actions-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding-top: var(--rc-spacing-section);
    border-top: 1px solid var(--slate-200);
}

/* Dark mode overrides for shared classes */
body.dark-mode .rc-title-right {
    color: var(--slate-400);
}

body.dark-mode .rc-meta-line {
    color: var(--slate-400);
}

body.dark-mode .rc-meta-line > span:not(:last-child):not(.rc-meta-right)::after {
    color: var(--slate-600);
}

body.dark-mode .rc-meta-right {
    color: var(--slate-400);
}

body.dark-mode .rc-status-line {
    color: var(--slate-400);
}

body.dark-mode .rc-location-line {
    color: var(--slate-400);
}

body.dark-mode .rc-location-line i {
    color: var(--slate-500);
}

body.dark-mode .rc-actions-bar {
    border-top-color: var(--slate-600);
}

/* ============================================
   REVIEW PILL (Calendar Row)
   Single pill with label and notification icons
   ============================================ */
.review-icons-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    height: 44px;
    padding: 4px 10px;
    /* 2026-07-23 (user: "the review center indicators in the main area sometimes
       fall downwards due to width"). The conflict ⚠ and urgent-deadline 🕑 icons
       are hidden at rest and appear INLINE in the icon row only when there's
       something to flag — each adds ~26px, swinging this pill 121px → up to 162px.
       In the already-tight .proposer-calendar-row (nowrap, shrinkable siblings)
       that surge was enough to bump a neighbour onto a second line. Reserve the
       worst-case footprint (conflict w/ 2-digit count + urgent icon + 4 tabs) so
       the pill's width is CONSTANT regardless of alert state — the centred header
       + row just fill the reserved slot when alerts light up, and never perturb
       the toolbar. flex-shrink:0 so a crowded row can't squeeze it either. Mobile
       lays this out as a compact row-chip with its own intrinsic width + shrink
       guard (style.css ~9112), so it's reset to width:auto there. */
    /* 2026-07-27 (user: "the review center button needs a little perfecting
       regarding space, make it look more designed like the mobile one").
       MEASURED on desktop (1283px): worst case (both alert icons
       forced visible) renders at 163px, the resting pill at 122px. So 162px was
       NOT over-reserved — it is genuinely the alert-state width, and shrinking
       it would clip an alert. The slack at rest is inherent to reserving a
       constant slot, so the honest fix is to tighten the INTERNAL rhythm (label
       size, symmetric padding) rather than the outer box. 162 → 163 so the
       worst case fits exactly instead of losing a subpixel. */
    width: 163px;
    flex-shrink: 0;
    background: linear-gradient(135deg, rgba(148, 163, 184, 0.08) 0%, rgba(148, 163, 184, 0.04) 100%);
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-lg);
    transition: all var(--transition-base);
}

.review-icons-wrapper:hover {
    background: linear-gradient(135deg, rgba(148, 163, 184, 0.12) 0%, rgba(148, 163, 184, 0.06) 100%);
}

/* Traffic light color coding: 1+ green, 5+ amber, 10+ red
   Tokens come from STATUS_PRESENTATION (available/tentative/busy). */
.review-icons-wrapper.level-green {
    border-color: var(--rc-status-available);
    box-shadow: 0 0 0 1px rgba(34, 197, 94, 0.2);
}

.review-icons-wrapper.level-amber {
    border-color: var(--rc-status-tentative);
    box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.2);
}

.review-icons-wrapper.level-red {
    border-color: var(--rc-status-busy);
    box-shadow: 0 0 0 1px rgba(220, 38, 38, 0.2);
}

.review-icons-wrapper.level-green:hover { border-color: var(--rc-status-working); }
.review-icons-wrapper.level-amber:hover { border-color: var(--rc-status-tentative); }
.review-icons-wrapper.level-red:hover { border-color: var(--rc-status-busy); }

.review-icons-header {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    width: 100%;
    padding: 0 1px;
    padding-left: 6px;
}

.review-icons-label {
    /* 0.5rem (8px) was below the practical floor for uppercase tracked text —
       it read as a grey smudge rather than a label, which is a big part of why
       the desktop pill looked less designed than the mobile chip. 0.625rem
       (10px) is legible while still clearly subordinate to the icon row, and
       the pill has vertical room for it inside its fixed 44px height.
       (2026-07-27) */
    font-size: 0.625rem;
    font-weight: 600;
    color: var(--slate-500);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    line-height: 1;
}

/* Conflict icon — header-level alert. Hidden at rest: it only appears
   when there's a real conflict to resolve, and when it appears it always
   carries its count badge (so it's never a bare, numberless triangle).
   Sits in the header strip next to the urgent-deadline icon, NOT inline
   with the four tab icons below (those are queue surfaces; this is a
   cross-cutting alert that spans the panel). */
.review-conflict-icon {
    /* Hidden at rest — JS adds .has-items (display:inline-flex) only when
       conflictCount > 0. A zero count means no icon at all. */
    display: none;
    align-items: center;
    justify-content: center;
    color: var(--slate-400, #94a3b8);
    font-size: 0.7rem;
    /* No leading margin (2026-07-04, user: "reduce the inner white space on the
       left on the desktop"). The conflict icon is the LEADING item in the
       centre-justified .review-icons-row; a margin-left here shifted the whole
       chip group right, leaving ~6px more inner whitespace on the LEFT of the
       pill than on the right (measured 17px left vs 11px right). Dropping it
       balances the row (11px each side). The 4px row `gap` still separates the
       conflict icon from the next tab icon, so they don't touch. */
    margin-left: 0;
    cursor: pointer;
    position: relative;
    transition: transform 0.15s ease, color 0.15s ease;
    /* DESKTOP: give the icon the SAME 22px box as the four tab buttons
       (.review-icon-btn, ~618). Without a fixed box this span shrink-wraps the
       ~11px triangle glyph, so the position:absolute count badge (anchored
       bottom:0; right:-3px below) lands directly ON TOP of the glyph instead of
       offset into the corner like the tab badges do (user 2026-06-26: "the
       conflict icon count is on the icon"). The 22px box pushes the badge's
       bottom-right anchor clear of the centred glyph — same corner offset as the
       tabs — making the alert consistent with the row it sits in. The glyph stays
       centred via align-items/justify-content:center. (The coarse/≤768px block
       overrides this back to a column for the mobile under-count layout.) */
    width: 22px;
    height: 22px;
}

.review-conflict-icon.has-items {
    display: inline-flex;
    color: var(--rc-status-busy);
}

/* SAME SHAPE AS ITS NEIGHBOURS (2026-08-16, user: "make the tab more
   beautiful. warning icon with text comparative to others when selected. the
   red seems too big").
   From the screenshot: a solid ~46px red DISC swallowing the ⚠ and its count,
   sitting inside the Events tab and crushing its label to "E…", while the
   sibling counts (2 / 1 / 4) are small tinted pills. The desktop badge idiom
   was never meant to be read at that scale on a phone.
   Make it a chip in the same family as the others: a soft red tint, red ink,
   the glyph and the number on ONE line, sized to the row rather than to a
   22px box that forces the count into a corner. */
.review-conflict-icon.has-items {
    width: auto;
    height: auto;
    gap: 4px;
    padding: 2px 7px;
    border-radius: var(--radius-full, 9999px);
    background: var(--cmx-ab65022fe5);
    background: color-mix(in srgb, var(--rc-status-busy, #dc2626) 12%, transparent);
    font-size: 0.6875rem;
    line-height: 1.2;
}

.review-conflict-icon.has-items .review-conflict-count {
    /* In flow beside the glyph — not an absolutely-positioned corner badge, so
       nothing needs a fixed box to anchor against and nothing can land on top
       of the triangle. */
    position: static;
    inset: auto;
    margin: 0;
    min-width: 0;
    height: auto;
    padding: 0;
    background: none;
    box-shadow: none;
    color: inherit;
    font-size: inherit;
    font-weight: 700;
}
body.dark-mode .review-conflict-icon.has-items {
    background: rgba(248, 113, 113, 0.16);
    color: #f87171;
}

/* ⚠️PHONE ROW: NO PILL. The chip above assumes ONE line (its 2px/7px padding is
   sized for a glyph and digit side by side), but on the phone toolbar
   style.css forces this element to flex-direction:column — deliberately, so the
   digit lands on the same line as the four tab buttons' under-glyph counts.
   Those two intents are incompatible, and the column wins on mobile: the pill
   ended up wrapped around TWO stacked rows, so it rendered as a 25x26 near
   square (aspect 0.96 — the "red circle" shape again, just tinted) with the
   count spilling 2px BELOW its own background. Measured at 390px/dpr2, 08-23.

   The row's own idiom for a count is a flat coloured digit under the glyph with
   no chrome (see the coarse ≤640px rule further down, which already strips the
   count's pill). So strip the container's pill too and let the glyph + digit
   read as a column, consistent with its four neighbours. The red ink alone
   marks the alert — which is what "the red seems too big" asked for. */
@media (max-width: 640px) {
    .review-conflict-icon.has-items,
    /* Dark mode sets its own tint on .has-items at a higher specificity, so it
       has to be named here or the pill comes back on dark phones only. */
    body.dark-mode .review-conflict-icon.has-items {
        padding: 0;
        border-radius: 0;
        background: none;
        gap: 0;
    }
}

/* Count badge — DESKTOP: matched to the tab-icon badge (.review-icon-badge, ~566)
   idiom so the conflict alert is visually CONSISTENT with the four queue-tab badges
   in the same row: bottom-anchored "under" the glyph, pill radius, soft shadow, same
   size/weight. (Was top-anchored, radius:6px, no shadow — out of step with the row.)
   The badge is position:absolute against .review-conflict-icon (position:relative). */
.review-conflict-icon .review-conflict-count {
    position: absolute;
    bottom: 0px;
    right: -3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 12px;
    height: 12px;
    padding: 0 2px;
    border-radius: var(--radius-full);
    background: var(--rc-status-busy, #dc2626);
    color: #fff;
    font-size: 0.5rem;
    font-weight: 700;
    line-height: 1;
    text-align: center;
    box-sizing: border-box;
    pointer-events: none;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
    animation: badgePop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
body.dark-mode .review-conflict-icon .review-conflict-count {
    background: #ef4444;
}

/* ============================================================================
   ALERT DOT — folded-in conflict / deadline signal (2026-07-23)
   ============================================================================
   Replaces the separate inline ⚠ conflict + 🕒 deadline icons, which were
   display:none→inline-flex and so ADDED ~22px to the Review cluster the instant
   an alert appeared — breaking the toolbar row (user: "no space when there's a
   conflict"). This dot lives permanently in the DOM at the top-LEFT corner of the
   Scheduled tab icon (the bottom-right corner holds the pending-count badge, so
   they never collide) and only toggles VISIBILITY via .has-alert. Because it's
   absolutely positioned inside the already-22px tab button, it changes the
   cluster width by ZERO whether lit or dark — the whole point of the change.
   Red (.is-conflict) = a scheduling conflict; amber = deadline-only. */
/* NO DOT (2026-08-16, user: "I dont like the red dot on review center also.
   remove and consider either bundling with the event notifications or changing
   the icon of events to red itself of conflicts").
   Taking the second option: the GLYPH carries the alert, so the signal
   survives without adding a separate mark to a row that already rejects dots
   (its sibling .review-icon-alert-count was retired for the same reason).
   The element stays in the DOM because the click router keys the
   conflicts-panel route off `.is-conflict` — it simply paints nothing now. */
.review-icon-alert-dot {
    display: none;
}

/* The alert lives on the icon itself. Amber = a deadline wants attention;
   red = an actual scheduling conflict, which is one of the few places red is
   correct (conflicts/danger), per the busy-is-slate split. */
#review-icon-scheduled.has-alert i {
    color: var(--rc-status-tentative, #f59e0b);
}
#review-icon-scheduled.has-alert:has(.review-icon-alert-dot.is-conflict) i {
    color: var(--rc-status-busy, #dc2626);
}
body.dark-mode #review-icon-scheduled.has-alert:has(.review-icon-alert-dot.is-conflict) i {
    color: #ef4444;
}

/* Coarse-pointer / phone: on a phone the four tab counts are NOT pills — they render
   as static, transparent-background, under-glyph hue-coloured text (style.css ~8340,
   `.proposer-calendar-row .review-icon-btn .review-icon-badge` inside @media
   max-width:640px). To be CONSISTENT with that row on mobile, the conflict count reads
   the same way: a flat red number tucked in the lower band of the icon's 26px box (the
   box gets align-items:flex-start on mobile so the glyph rides up top, leaving that
   band — style.css ~8551). KEEP bottom-anchoring (do NOT re-anchor to top — a pill on
   top would be the opposite of the row) and drop the pill chrome: flat red text, no
   fill/shadow/radius, a touch larger for legibility. The desktop .review-icon-badge
   coarse rule re-anchors to TOP only because its 44px touch target pushes a bottom
   badge off-frame; the conflict count is not inside that touch target, so bottom on the
   26px box is safe and is exactly where the tab numbers sit. */
/* Width-gated to ≤640px (added 2026-06-26): this static-flow restyle ASSUMES the
   icon is the flex COLUMN created only inside the @media(max-width:640px) block
   (flex-direction:column + align-items:flex-start in style.css ~8636, which puts
   the glyph on top and leaves the band below for the digit). On a coarse-pointer
   device at a WIDE viewport — a phone with "Request desktop site" on, or a tablet
   — that ≤640px column layout does NOT apply, so the icon stays a ROW; making the
   count position:static there flowed the digit INLINE beside the ⚠ instead of
   beneath it, mis-seating it vs the tab numbers (user 2026-06-26: "conflict
   indicator and other icons broken on the desktop view in mobile … misaligned").
   Requiring max-width:640px keeps this restyle paired with the column layout it
   depends on; wide touch viewports keep the self-consistent desktop pill badge. */
@media (hover: none) and (pointer: coarse) and (max-width: 640px) {
  .review-conflict-icon .review-conflict-count {
    /* Lay the conflict count out the SAME WAY as the four tab counts on mobile
       (user 2026-06-26: "conflict icon and digits in review button seem slightly
       wrongly aligned"). The tab badges (style.css ~8360,
       .proposer-calendar-row .review-icon-btn .review-icon-badge) are position:STATIC
       + margin-top:1px — they FLOW under the glyph, centred in the column. The
       conflict count was position:absolute; bottom:1px; right:-2px, so it sat a hair
       low AND shifted right of centre vs the tab numbers. Match the tabs exactly:
       static flow, centred, same margin-top / size / weight / line-height. The icon's
       inline-flex column (align-items:flex-start + padding-top:2px on the parent) puts
       the glyph on top; this number then sits beneath it like the tabs. */
    position: static;
    top: auto;
    right: auto;
    bottom: auto;
    left: auto;
    margin-top: 1px;
    min-width: 0;
    height: auto;
    padding: 0;
    font-size: 0.625rem;
    font-weight: 800;
    /* MATCH THE TAB NUMBERS' FONT (user 2026-06-26: "conflict digits still seem too
       bold compared to the other digits"). The four tab counts live inside <button>
       elements, which the UA renders in its default Arial — so the tab numbers are
       Arial 800. The conflict ⚠ is a <span>, so it inherited the app's Inter from
       .review-icons-row, and Inter@800 reads visibly HEAVIER than Arial@800. Pin the
       conflict count to the same Arial stack so the five numbers read identically. */
    font-family: Arial, sans-serif;
    line-height: 1;
    background: transparent;
    color: var(--rc-status-busy, #dc2626);
    border-radius: 0;
    box-shadow: none;
  }
  body.dark-mode .review-conflict-icon .review-conflict-count {
    background: transparent;
    color: #ef4444;
  }
}

.review-conflict-icon.has-items i {
    /* Softened 2026-06-25 (user: "the conflict warning number seems bolder than the
       other review center button indicators"). The font-weights already match the row
       (count 700 desktop / 800 mobile = the tab badges; icons 900), so the extra
       "heaviness" came from this red drop-shadow HALO around the ⚠ — no other tab icon
       has one, so the conflict indicator bloomed heavier than its neighbours. Cut the
       glow radius + alpha right down so the icon reads at the same weight as the rest;
       the red hue alone already marks it as the alert. */
    filter: drop-shadow(0 0 1px rgba(220, 38, 38, 0.35));
}

.review-conflict-icon:hover {
    transform: scale(1.25);
}

.review-conflict-icon.has-items:hover {
    color: #b91c1c;
}

body.dark-mode .review-conflict-icon {
    color: var(--slate-500, #64748b);
}

body.dark-mode .review-conflict-icon.has-items {
    color: #f87171;
}

body.dark-mode .review-conflict-icon.has-items:hover {
    color: #fca5a5;
}

/* Urgent icon - AMBER, subtle, deadline warnings (ignorable) */
.review-urgent-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Amber-700 is a darker variant of --rc-status-tentative;
       keep hardcoded — no canonical -strong token yet. */
    color: #d97706;
    font-size: 0.55rem;
    margin-left: 2px;
    opacity: 0.8;
    cursor: pointer;
    transition: transform 0.15s ease, opacity 0.15s ease, color 0.15s ease;
}

.review-urgent-icon:hover {
    transform: scale(1.25);
    opacity: 1;
    color: #b45309;
}

.review-urgent-icon i {
    filter: drop-shadow(0 0 1px rgba(217, 119, 6, 0.3));
}

body.dark-mode .review-urgent-icon {
    color: #fbbf24;
    opacity: 0.7;
}

body.dark-mode .review-urgent-icon:hover {
    color: #fcd34d;
    opacity: 1;
}

.review-total-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 6px;
    margin-left: 4px;
    margin-right: 1px;
    font-size: 0.55rem;
    font-weight: 700;
    color: var(--primary-600);
    background: white;
    border-radius: 10px;
    line-height: 1;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}

.review-icons-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
}

.review-icon-btn {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: all var(--transition-fast);
}

/* TOUCH TARGET (2026-09-03 launch audit). Rendered 28x28 with padding:0 and no
   expanded hit area, well under the 44px Apple / 48px Google minimum. These are
   four adjacent icons in the Review strip, so the hit area is grown to 40px
   (not 44) and only VERTICALLY beyond that — a 44px-wide surface on a ~28px
   pitch would overlap its neighbours and steal their taps, which is a worse bug
   than a small target. Visual size and layout are unchanged.

   ⭐ 2026-09-16 — THE RULING ABOVE STANDS; IT WAS JUST LEAVING PIXELS ON THE
   TABLE. Owner reported the Review cluster wrong on an iPad. Hit-testing the
   LIVE buttons (walking elementFromPoint outward from each centre, so the
   ::after pseudo is included) measured the real tappable area at 35x43 on a
   MEASURED 40px pitch — so min-width:34px was under-filling the space that
   actually exists by 5px per icon.

   Widened to exactly the pitch (40px, still NOT 44 — the 2026-09-03 reasoning
   is correct and 44 would overlap), and the height to 44 to meet the vertical
   minimum. Re-measured after the change: hit area 39x43, and a neighbour-theft
   check (hit-testing every OTHER icon's centre and asserting it still resolves
   to its own button) returned ZERO steals. Visual size and layout are still
   unchanged — this moves only the invisible pseudo.
   ⛔ Do not raise min-width past the pitch; re-run the theft check if the
   strip's spacing ever changes. */
.review-icon-btn::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: 100%;
    min-width: 40px;
    height: 44px;
    transform: translate(-50%, -50%);
}

.review-icon-btn i {
    font-size: 0.75rem;
    transition: all var(--transition-fast);
}

/* ── MOBILE-DESKTOP MODE (coarse pointer, 641–768px wide) — TOP-ANCHOR the icons
   ───────────────────────────────────────────────────────────────────────────
   2026-06-26 (user: "the icons don't sit at the top like they should" in mobile
   desktop mode). At 641–768px coarse the WCAG touch floor (style.css ~60778)
   makes .review-icon-btn 44px tall and lays it out flex-direction:column, and
   style.css:60785 INTENDS justify-content:flex-start to seat the glyph+count at
   the top. But the base .review-icon-btn rule above (justify-content:center,
   same 0,1,0 specificity) loses that tie only on SOURCE ORDER — and review-
   inbox.css loads AFTER style.css, so the base center wins and the glyph floats
   in the middle of the 44px box. Re-assert the top-anchor HERE (last word in the
   later file) for exactly the coarse-wide range. NOT ≤640 (the phone keeps its
   own .proposer-calendar-row 28px-pill treatment, style.css ~8452) and NOT fine-
   pointer desktop (the centred 22px chip is correct there). The "REVIEW" text
   label + header stay visible and seated at the top of the column — kept, not
   removed (user clarified "the label should go higher, not omit … we have enough
   vertical space"); the wrapper's column layout already puts the header above the
   icon row, and top-anchoring the icons tightens the whole cluster upward. */
@media (hover: none) and (pointer: coarse) and (min-width: 641px) {
    .review-icon-btn {
        /* Icons back to vertically centred within their box. The earlier
           flex-start + padding-top:3px top-seated them, but combined with the
           flex-start wrapper below that read as "the Review text sits too high"
           on desktop-mode-viewed-on-mobile (user 2026-07-06). Centre is the
           balanced seat. */
        justify-content: center;
    }
    /* Centre the stacked cluster (label/header + icon row) in the 44px pill so
       the spare vertical space splits evenly above and below, rather than the
       whole cluster jammed against the top edge. */
    .review-icons-wrapper {
        justify-content: center;
    }
}

.review-icon-btn:hover {
    background: rgba(255, 255, 255, 0.5);
}

.review-icon-btn:active {
}

/* Badge overlay positioned bottom-right - offset to not obscure icon */
.review-icon-badge {
    position: absolute;
    bottom: 0px;
    right: -3px;
    display: none;
    align-items: center;
    justify-content: center;
    min-width: 12px;
    height: 12px;
    padding: 0 2px;
    font-size: 0.5rem;
    font-weight: 700;
    line-height: 1;
    color: white;
    border-radius: var(--radius-full);
    pointer-events: none;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}

/* Coarse-pointer / phone: make the count figure bigger and legible — the 12px
   0.5rem desktop badge reads as an unlabelled dot on a phone (user 2026-06-18:
   "review center is also missing its figures"). Slightly larger min-width +
   font so the number is actually readable at the row's 36px mobile height.

   Anchor to the TOP-right, not the bottom. The mobile touch-target floor
   (style.css: .review-icon-btn min-height:44px under coarse/≤768px) makes the
   button box taller than the centred icon glyph and stretches it to the very
   bottom edge of the rounded Review frame. A bottom-anchored badge then hangs
   below that frame and gets clipped by its border (user 2026-06-18: counts
   "no longer show on mobile" — they'd drifted off the bottom edge). Pinning to
   the top keeps the figure over the icon's upper corner, safely inside the
   frame, with headroom from the alert/header row above. */
@media (hover: none) and (pointer: coarse) {
  .review-icon-badge {
    min-width: 15px;
    height: 15px;
    font-size: 0.62rem;
    top: -2px;
    bottom: auto;
    right: -5px;
  }
}

.review-icon-badge.visible {
    display: inline-flex;
    animation: badgePop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes badgePop {
    0% { transform: scale(0); }
    70% { transform: scale(1.15); }
    100% { transform: scale(1); }
}

/* Color themes matching Review Center tabs (kept in lockstep with the dashboard
   TABS hues: scheduled=teal #14b8a6, proposals=blue #3b82f6). The old amber/
   violet drifted from the tabs they jump to. The legacy amber/violet selectors
   are kept as aliases so any stale class still paints the right hue. */
/* Teal - Scheduled (was amber) */
.review-icon-btn.review-icon-teal i,
.review-icon-btn.review-icon-amber i { color: #14b8a6; }
.review-icon-btn.review-icon-teal .review-icon-badge,
.review-icon-btn.review-icon-amber .review-icon-badge { background: #14b8a6; }
.review-icon-btn.review-icon-teal:hover i,
.review-icon-btn.review-icon-amber:hover i { color: #0d9488; }

/* Blue - Proposals (was violet) */
.review-icon-btn.review-icon-blue i,
.review-icon-btn.review-icon-violet i { color: #3b82f6; }
.review-icon-btn.review-icon-blue .review-icon-badge,
.review-icon-btn.review-icon-violet .review-icon-badge { background: #3b82f6; }
.review-icon-btn.review-icon-blue:hover i,
.review-icon-btn.review-icon-violet:hover i { color: #2563eb; }

/* Emerald - Requested/Outgoing */
.review-icon-btn.review-icon-emerald i { color: #10b981; }
.review-icon-btn.review-icon-emerald .review-icon-badge { background: #10b981; }
.review-icon-btn.review-icon-emerald:hover i { color: #059669; }

/* Cyan - retired (was Publish, then the old cyan Privacy tab). Kept as an
   alias painting the current Privacy purple so any stale class still lands
   near the right identity. */
.review-icon-btn.review-icon-cyan i { color: #8b5cf6; }
.review-icon-btn.review-icon-cyan .review-icon-badge { background: #8b5cf6; }
.review-icon-btn.review-icon-cyan:hover i { color: #7c3aed; }

/* Purple - Privacy (2026-08-04 identity unification). Purple is the app-wide
   privacy hue (privacy-visual-config.js). Replaces the old amber fa-inbox
   "Incoming" slot, whose colour+glyph matched nothing inside the dashboard. */
.review-icon-btn.review-icon-purple i { color: #8b5cf6; }
.review-icon-btn.review-icon-purple .review-icon-badge { background: #8b5cf6; }
.review-icon-btn.review-icon-purple:hover i { color: #7c3aed; }
body.dark-mode .review-icon-btn.review-icon-purple i { color: #a78bfa; }

/* Indigo - Requests (join requests). Deliberately not the `.review-icon-violet`
   legacy class: that one is an alias kept pointing at the Proposals blue
   (#3b82f6, see above), so reusing it would have painted Requests identically
   to Proposals. Also deliberately NOT #7c3aed — that violet is a reserved
   STATUS_PRESENTATION hue (deep sleep) locked by the canonical-shading
   ratchet, and a category icon must not paint a status colour.
   (Requests wore the `purple` class name until 2026-08-04; the class was
   renamed to match its actual hue when Privacy claimed real purple.) */
.review-icon-btn.review-icon-indigo i { color: #6366f1; }
.review-icon-btn.review-icon-indigo .review-icon-badge { background: #6366f1; }
.review-icon-btn.review-icon-indigo:hover i { color: #4f46e5; }
body.dark-mode .review-icon-btn.review-icon-indigo i { color: #a5b4fc; }

/* ---- Phone overflow chip (2026-07-30) ------------------------------------
   A phone toolbar cannot seat five 28px category icons plus the 26px conflict
   alert (~186px at the 4px gap). updateBadges() keeps the three most pressing
   categories and rolls the rest into this "+N" chip, so the remainder is still
   STATED rather than silently dropped. Always in the DOM; revealed by .visible
   only on a phone, and only when the hidden categories actually carry items. */
.review-overflow-chip {
    display: none;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    padding: 0 6px;
    font-size: 0.68rem;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--slate-600, #475569);
    background: var(--slate-100, #f1f5f9);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    cursor: pointer;
    font-family: inherit;
}
.review-overflow-chip.visible { display: inline-flex; }
.review-overflow-chip:hover {
    color: var(--slate-800, #1e293b);
    border-color: var(--slate-300, #cbd5e1);
}
body.dark-mode .review-overflow-chip {
    color: var(--slate-300, #cbd5e1);
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
/* Categories that lost their slot. display:none (not visibility:hidden)
   because the whole point is to reclaim their WIDTH — a hidden-but-laid-out
   icon would defeat the exercise. */
.review-icon-btn.rc-icon-slotted-out { display: none !important; }

/* Urgency-based badge backgrounds (override category colors)
   Tokens come from STATUS_PRESENTATION. */
.review-icon-badge.urgency-green {
    background: var(--rc-status-available) !important;
}

/* The urgency colour on the badge IS the signal; the pop animation cues that
   the count changed. The extra glow ring was a third layer on the same badge,
   so it's gone — the coloured fill carries urgency on its own. */
.review-icon-badge.urgency-amber {
    background: var(--rc-status-tentative) !important;
    animation: badgePop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.review-icon-badge.urgency-red {
    background: var(--rc-status-busy) !important;
    animation: badgePop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* Dark Mode */
body.dark-mode .review-icons-wrapper {
    background: linear-gradient(135deg, rgba(51, 65, 85, 0.5) 0%, rgba(51, 65, 85, 0.3) 100%);
    border-color: var(--slate-600);
}

body.dark-mode .review-icons-wrapper:hover {
    background: linear-gradient(135deg, rgba(51, 65, 85, 0.6) 0%, rgba(51, 65, 85, 0.4) 100%);
    border-color: var(--slate-500);
}

body.dark-mode .review-icons-label {
    color: var(--slate-300);
}

body.dark-mode .review-total-count {
    color: var(--primary-400);
    background: var(--slate-700);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}

body.dark-mode .review-icon-btn:hover {
    background: rgba(255, 255, 255, 0.1);
}

body.dark-mode .review-icon-btn.review-icon-teal i,
body.dark-mode .review-icon-btn.review-icon-amber i { color: #2dd4bf; }
body.dark-mode .review-icon-btn.review-icon-blue i,
body.dark-mode .review-icon-btn.review-icon-violet i { color: #60a5fa; }
body.dark-mode .review-icon-btn.review-icon-cyan i { color: #a78bfa; }
body.dark-mode .review-icon-btn.review-icon-emerald i { color: #34d399; }

/* ============================================
   REVIEW MODAL - FULL-SCREEN PREMIUM CONTAINER
   ============================================ */
.review-modal .modal-content {
    /* Full-bleed review surface: the modal earns its size with data, not chrome.
       Wider + taller than the old 1400px/94vh so the queue gets the room. */
    max-width: min(1600px, 96vw);
    width: 96%;
    min-height: 70vh;
    max-height: 96vh;
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 250, 252, 0.98) 100%);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid rgba(203, 213, 225, 0.4);
    border-radius: var(--radius-2xl);
    box-shadow: var(--shadow-xl);
    overflow: hidden;
    padding: 0;
    display: flex;
    flex-direction: column;
}

/* Old modal-header is removed from HTML; hide if still present */
.review-modal .modal-header {
    display: none;
}

/* ============================================
   INTEGRATED DASHBOARD HEADER (rendered by JS)
   ============================================ */
/* ============================================
   REVIEW CENTER HEADER
   ============================================
   Clean modal-header style matching other modals
   (Propose, Event, Insights).
   ============================================ */
.review-dashboard-header {
    display: flex;
    align-items: center;
    gap: 10px;
    /* Phase D: the header scrolls away now, so its only job is to orient the
       user at the top — slimmed 12/10 → 8/8 vertical pad + removed the bottom
       border (the sticky bar below carries its own divider once scrolled), so
       the band is ~14px shorter and the tab bar sits right beneath it. */
    padding: 8px 20px;
    flex-shrink: 0;
    background: var(--surface-primary, #fff);
    position: relative;
}

.review-dashboard-header::before {
    display: none;
}

.review-dashboard-header .rdh-title {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--slate-800, #1e293b);
    letter-spacing: -0.01em;
    margin: 0;
    white-space: nowrap;
}

.review-dashboard-header .rdh-title i {
    font-size: 15px;
    color: var(--primary-500, #14b8a6);
}

/* Filter tabs - mode-pill colored style (matches Propose modal) */
.review-dashboard-header .rdh-filters {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
    gap: 2px;
    padding: 3px;
    background: var(--slate-100, #f1f5f9);
    border-radius: var(--radius-full, 9999px);
}

.review-dashboard-header .rdh-filters .filter-tab {
    background: transparent;
    color: var(--slate-500, #64748b);
    border: none;
    border-radius: var(--radius-full, 9999px);
    padding: 5px 12px;
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
    display: flex;
    align-items: center;
    gap: 4px;
    font-family: inherit;
}

/* Semantic category colour AT REST — the filter carries the same colour the
   rest of the app uses for work (blue) / personal (pink) events, so the
   segmented control reads as "these are your work / personal calendars",
   not a third neutral palette. All stays the app's primary teal. The icon
   is always tinted; the muted label inherits a desaturated tint so the
   inactive segments don't shout. Canonical tokens (css/main.css:109-119)
   keep this in lockstep with event chips + privacy badges. */
.review-dashboard-header .rdh-filters .filter-tab[data-filter="work"] i {
    color: var(--category-work, #3b82f6);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"] i {
    color: var(--category-personal, #f472b6);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="all"] i {
    color: var(--primary-500, #14b8a6);
}

.review-dashboard-header .rdh-filters .filter-tab:hover:not(.active) {
    color: var(--slate-700, #334155);
    background: rgba(255, 255, 255, 0.55);
}

/* ACTIVE — each segment fills with a tint of its own category colour and
   sets its text/icon to the darker on-tint shade. Scoped per data-filter so
   no single neutral rule can flatten them (the old override did exactly
   that — it forced teal on every active segment via specificity). */
.review-dashboard-header .rdh-filters .filter-tab.active {
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.06));
    font-weight: 600;
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="all"].active {
    background: var(--surface-primary, #fff);
    color: var(--primary-600, #0d9488);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="work"].active {
    background: var(--cmx-a12eb5b117);
    background: color-mix(in srgb, var(--category-work, #3b82f6) 14%, #fff);
    color: var(--category-work-darker, #1d4ed8);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="work"].active i {
    color: var(--category-work-darker, #1d4ed8);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"].active {
    background: var(--cmx-cefd2d492b);
    background: color-mix(in srgb, var(--category-personal, #f472b6) 16%, #fff);
    color: var(--category-personal-darker, #db2777);
}
.review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"].active i {
    color: var(--category-personal-darker, #db2777);
}

/* Review Center sync button — now uses shared `.sync-action sync-action--icon`
   token (see css/calendar-views.css). The legacy `.rdh-sync-btn` rules used
   to live here. */

.review-dashboard-header .rdh-help-btn {
    background: transparent;
    color: var(--slate-400, #94a3b8);
    border: none;
    border-radius: var(--radius-full, 9999px);
    width: 28px;
    height: 28px;
    padding: 0;
    font-size: 13px;
    cursor: pointer;
    transition: all 0.15s ease;
    display: flex;
    align-items: center;
    justify-content: center;
}

.review-dashboard-header .rdh-help-btn:hover {
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-600, #475569);
}

body.dark-mode .review-dashboard-header .rdh-help-btn {
    color: var(--slate-400, #94a3b8);
}

body.dark-mode .review-dashboard-header .rdh-help-btn:hover {
    background: var(--slate-700, #334155);
    color: var(--slate-200, #e2e8f0);
}

/* ---- Header overflow ("⋯") menu (2026-07-11 chrome slim-down) -------------
   Low-frequency header actions (keyboard shortcuts, and whatever else outgrows
   the top row later) live behind a single "⋯" button so the header carries
   only title + status chips + Sync + overflow + close. */
.rc-tabrow .rdh-overflow { position: relative; display: inline-flex; }
.rc-tabrow .rdh-overflow-btn {
    background: transparent;
    color: var(--slate-400, #94a3b8);
    border: none;
    border-radius: var(--radius-full, 9999px);
    width: 32px;
    height: 32px;
    padding: 0;
    font-size: 15px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.rc-tabrow .rdh-overflow-btn:hover,
.rc-tabrow .rdh-overflow-btn[aria-expanded="true"] {
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-600, #475569);
}
.rdh-overflow-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 30;
    min-width: 200px;
    padding: 6px;
    background: var(--surface-primary, #fff);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
}
.rdh-overflow-menu[hidden] { display: none; }
/* ONE REFRESH PER VIEWPORT (2026-08-29, user: "Policy standard and refresh seem
   legacy and unnecessary").
   Refresh exists twice: the inline #rdh-sync-btn on the strip, and #rdh-sync-item
   in this menu. That is deliberate but VIEWPORT-SPLIT, not simultaneous — the
   inline button is hidden below 480px (see the .rc-navrow rule ~13687) so the
   menu row can take over on a phone. Above that breakpoint both were rendered,
   so the menu opened onto a Refresh sitting one click deeper than the Refresh
   already on screen. Hide the deeper copy wherever the inline one is showing.
   `display:none` (not visibility/opacity) so it leaves the tab order and the
   screen-reader menu too — a duplicate announced twice is the same bug. */
@media (min-width: 481px) {
    .rdh-overflow-menu #rdh-sync-item { display: none; }
}
.rdh-overflow-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    background: transparent;
    border: none;
    border-radius: 8px;
    font-size: 13px;
    font-family: inherit;
    color: var(--slate-700, #334155);
    cursor: pointer;
    text-align: left;
    transition: background-color 0.12s ease;
}
.rdh-overflow-item:hover,
.rdh-overflow-item:focus-visible { background: var(--slate-100, #f1f5f9); outline: none; }
.rdh-overflow-item i { width: 16px; text-align: center; color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-tabrow .rdh-overflow-btn:hover,
body.dark-mode .rc-tabrow .rdh-overflow-btn[aria-expanded="true"] {
    background: var(--slate-700, #334155);
    color: var(--slate-200, #e2e8f0);
}
body.dark-mode .rdh-overflow-menu {
    background: var(--surface-secondary, #1e293b);
    border-color: var(--slate-700, #334155);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
body.dark-mode .rdh-overflow-item { color: var(--slate-200, #e2e8f0); }
body.dark-mode .rdh-overflow-item:hover,
body.dark-mode .rdh-overflow-item:focus-visible { background: var(--slate-700, #334155); }

/* The team-fairness chip lives INSIDE this menu as a row (it was an inline
   header pill). It still carries its own pill class (.rdh-fairness-chip) so the
   dot·band·score internals keep their styling — but the OUTER pill chrome
   (rounded bg, border, fixed height, margin-left) must yield to the full-width
   menu-row layout. These overrides strip the pill shell while leaving the inner
   spans intact, and the fairness band word keeps its canonical hue.
   (.rdh-sent-chip selectors dropped 2026-08-06 — no renderer emits that chip;
   "Sent by me" is a sub-tab of Proposals.) */
.rdh-overflow-menu .rdh-fairness-chip {
    /* Re-assert the .rdh-overflow-item row layout over the pill rules. */
    display: flex;
    width: 100%;
    height: auto;
    margin: 0;
    padding: 8px 10px;
    gap: 10px;
    border: none;
    background: transparent;
    border-radius: 8px;
    justify-content: flex-start;
}
.rdh-overflow-menu .rdh-fairness-chip:hover,
.rdh-overflow-menu .rdh-fairness-chip:focus-visible {
    background: var(--slate-100, #f1f5f9);
    border: none;
    box-shadow: none;
    outline: none;
}
body.dark-mode .rdh-overflow-menu .rdh-fairness-chip {
    background: transparent;
    border: none;
}
body.dark-mode .rdh-overflow-menu .rdh-fairness-chip:hover,
body.dark-mode .rdh-overflow-menu .rdh-fairness-chip:focus-visible {
    background: var(--slate-700, #334155);
}
/* Fairness dot/label/band/score: keep the canonical hue; push the numeric score
   to the right edge of the row so it reads like a menu value. */
.rdh-overflow-menu .rdh-fairness-chip .rc-tf-band { margin-left: 2px; }
.rdh-overflow-menu .rdh-fairness-chip .rc-tf-score { margin-left: auto; }

.rc-tabrow .rdh-close-btn {
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-500, #64748b);
    border: none;
    border-radius: var(--radius-full, 9999px);
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.15s ease;
    font-size: 13px;
    flex-shrink: 0;
}

.rc-tabrow .rdh-close-btn:hover {
    background: var(--slate-200, #e2e8f0);
    color: var(--slate-700, #334155);
}

body.dark-mode .review-dashboard-header {
    background: var(--slate-900, #0f172a);
}

body.dark-mode .review-dashboard-header .rdh-title {
    color: var(--slate-200, #e2e8f0);
}

body.dark-mode .review-dashboard-header .rdh-filters {
    background: var(--slate-800, #1e293b);
}

body.dark-mode .review-dashboard-header .rdh-filters .filter-tab {
    color: var(--slate-400, #94a3b8);
}

body.dark-mode .review-dashboard-header .rdh-filters .filter-tab:hover:not(.active) {
    color: var(--slate-300, #cbd5e1);
    background: var(--slate-700, #334155);
}

/* Dark-mode rest tints — lighter category shades for contrast on slate. */
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="work"] i {
    color: var(--category-work-light, #60a5fa);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"] i {
    color: var(--category-personal-light, #f9a8d4);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="all"] i {
    color: var(--primary-300, #5eead4);
}

/* Dark-mode active — per-segment category tint on slate, matching the light
   rule's semantics so Work/Personal keep their colour identity. */
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab.active {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="all"].active {
    background: var(--slate-700, #334155);
    color: var(--primary-300, #5eead4);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="work"].active {
    background: var(--cmx-975aadb6ad);
    background: color-mix(in srgb, var(--category-work, #3b82f6) 26%, var(--slate-800, #1e293b));
    color: var(--category-work-lighter, #93c5fd);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="work"].active i {
    color: var(--category-work-lighter, #93c5fd);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"].active {
    background: var(--cmx-f8372d5314);
    background: color-mix(in srgb, var(--category-personal, #f472b6) 26%, var(--slate-800, #1e293b));
    color: var(--category-personal-lighter, #fbcfe8);
}
body.dark-mode .review-dashboard-header .rdh-filters .filter-tab[data-filter="personal"].active i {
    color: var(--category-personal-lighter, #fbcfe8);
}

body.dark-mode .rc-tabrow .rdh-close-btn {
    background: var(--slate-700, #334155);
    color: var(--slate-400, #94a3b8);
}

body.dark-mode .rc-tabrow .rdh-close-btn:hover {
    background: var(--slate-600, #475569);
    color: var(--slate-200, #e2e8f0);
}

/* Dashboard content fills remaining space */
.review-dashboard-content {
    /* The modal body. Does NOT scroll itself — it clips to the modal height and
       hands its full box to `.review-dashboard`, which is the scroll viewport
       (Phase D: header scrolls away, sticky bar pins). overflow:hidden keeps the
       rounded modal corners clean over the inner scroll. */
    flex: 1;
    min-height: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* Fill the modal body outright. Was calc(94vh - 48px) — the 94vh was coupled
       to .modal-content's old max-height and the -48px was dead slack from the
       now display:none .modal-header. As a flex:1/min-height:0/overflow:hidden
       child of a column flex parent, 100% is well-defined and decouples from vh
       so the shell height can change without a second edit here. */
    max-height: 100%;
    padding: 0;
}

#close-review-modal {
    position: relative;
    z-index: 100;
    cursor: pointer !important;
    pointer-events: auto !important;
}

body.dark-mode .review-modal .modal-content {
    background: linear-gradient(180deg, rgba(30, 41, 59, 0.98) 0%, rgba(15, 23, 42, 0.98) 100%);
    border-color: rgba(71, 85, 105, 0.4);
}

.review-dashboard {
    /* Phase D: the WHOLE panel is the scroll viewport now — the header scrolls
       AWAY with the content and only the .rc-sticky-bar pins. min-height:0 +
       flex:1 lets it fill the modal body and own overflow. (Was a non-scrolling
       flex column wrapping a pinned .rc-frame; that kept ~130px of chrome
       permanently above the fold.) */
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0;
    display: block;
}

/* Sticky navigation: tab bar · triage · search · progress. Scrolls UP with the
   header, then pins at the top of the viewport so the controls reached mid-task
   stay one glance away at any depth. The header above it scrolls fully away.
   z-index keeps cards from showing through; the surface bg + a hairline border
   (only once scrolled) separate it from the list beneath. */
.rc-sticky-bar {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--surface-primary, #fff);
    padding: 10px 18px 0;
    border-bottom: 1px solid transparent;
    transition: padding 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}

/* Compact state — applied by attachScrollShrink() once the header has scrolled
   out of view. Tightens vertical padding and reveals the divider/shadow so the
   pinned strip reads as ~36px of chrome over the data, not a tall band. */
.rc-sticky-bar.rc-scrolled {
    padding: 6px 18px 6px;
    border-bottom-color: var(--slate-200, #e2e8f0);
    box-shadow: 0 2px 6px rgba(15, 23, 42, 0.06);
}
body.dark-mode .rc-sticky-bar {
    background: var(--slate-900, #0f172a);
}
body.dark-mode .rc-sticky-bar.rc-scrolled {
    border-bottom-color: var(--slate-700, #334155);
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

/* The card list — normal flow under the sticky bar. No longer owns overflow
   (the whole panel scrolls); keeps only horizontal padding + bottom gap. */
.review-tab-content {
    padding: 8px 18px 16px;
}

/* Sync spin animation */
@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* ============================================
   FILTER TABS (All/Work/Personal)
   Base styles used by header filter tabs
   ============================================ */
.filter-tab {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 5px 12px;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--slate-500);
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}

.filter-tab:hover:not(.active) {
    color: var(--slate-700);
    background: rgba(255, 255, 255, 0.5);
}

.filter-tab.active {
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
}

/* Base (unscoped) semantic active treatment — fallback for any .filter-tab
   used outside the Review Center header. Routes through canonical category
   tokens (css/main.css:109-119) so work=blue / personal=pink stay in lockstep
   with event chips + privacy badges across the app. */
/* All filter - teal */
.filter-tab[data-filter="all"].active {
    color: var(--primary-600);
    background: rgba(20, 184, 166, 0.1);
    border: 1px solid rgba(20, 184, 166, 0.2);
}

/* Work filter - blue */
.filter-tab[data-filter="work"].active {
    color: var(--category-work-darker, #1d4ed8);
    background: var(--cmx-efd825d9d7);
    background: color-mix(in srgb, var(--category-work, #3b82f6) 10%, transparent);
    border: 1px solid var(--cmx-0ad10c60c1);
    border: 1px solid color-mix(in srgb, var(--category-work, #3b82f6) 22%, transparent);
}

/* Personal filter - pink */
.filter-tab[data-filter="personal"].active {
    color: var(--category-personal-darker, #db2777);
    background: var(--cmx-44378ca558);
    background: color-mix(in srgb, var(--category-personal, #f472b6) 11%, transparent);
    border: 1px solid var(--cmx-b46ec5452f);
    border: 1px solid color-mix(in srgb, var(--category-personal, #f472b6) 24%, transparent);
}

.filter-tab i {
    font-size: 0.75rem;
    flex-shrink: 0;
}

body.dark-mode .filter-tab {
    color: var(--slate-400);
}

body.dark-mode .filter-tab:hover:not(.active) {
    color: var(--slate-200);
    background: var(--slate-700);
}

body.dark-mode .filter-tab.active {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}

body.dark-mode .filter-tab[data-filter="all"].active {
    color: var(--primary-300);
    background: rgba(20, 184, 166, 0.15);
    border: 1px solid rgba(20, 184, 166, 0.25);
}

body.dark-mode .filter-tab[data-filter="work"].active {
    color: #93c5fd;
    background: rgba(59, 130, 246, 0.15);
    border: 1px solid rgba(59, 130, 246, 0.25);
}

body.dark-mode .filter-tab[data-filter="personal"].active {
    color: #f9a8d4;
    background: rgba(244, 114, 182, 0.15);
    border: 1px solid rgba(244, 114, 182, 0.25);
}

/* ============================================
   TRIAGE STRIP (Phase B) — cross-tab "what needs me now?" band, sits
   between the search/date bar and the tab bar. It now renders ONLY when
   there's something urgent (expiring / conflicts); otherwise the JS returns
   '' and this band takes zero space. The fairness chip that used to live
   here — and forced the band to always reserve height — moved to the header.
   ============================================ */
/* Tab row: the tab bar on the left and the triage chips ("2 expiring soon",
   conflicts) right-aligned opposite them on the SAME row, so the urgent
   cross-tab counts read as a status readout beside the navigation rather than
   as a separate band that adds height. The row wraps on narrow widths so the
   chips drop below the tabs instead of overflowing. */
.rc-tabrow {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
    margin-bottom: 10px;
    transition: margin-bottom 0.18s ease;
}
.rc-scrolled .rc-tabrow {
    margin-bottom: 0;
}

/* ============================================================
   TWO-ROW STICKY STRIP (2026-08-04)
   ============================================================
   The strip was ONE wrap-enabled row carrying five independent control groups
   (brand, tabs, triage, search+filters, actions). With `flex-wrap: wrap` and no
   priority between them, WHICH group dropped to a second line depended on the
   viewport and the current label lengths — so the layout was emergent, and the
   31 media queries in this file spent most of their effort hiding one control
   at a time to keep everything on one line.

   Now there are two rows with distinct jobs, and each is allowed to be simple:
     .rc-navrow  — WHERE YOU ARE: brand, tabs, close. Never wraps.
     .rc-toolrow — WHAT YOU'RE LOOKING AT: triage, search, filters.

   This removes the contention rather than suppressing its symptoms, which is
   what lets the phone rules stay short.
   ============================================================ */
.rc-navrow {
    flex-wrap: nowrap;          /* the whole point — no emergent second line */
    gap: 8px;
    min-width: 0;
}
/* Tabs take the flexible middle and SCROLL if their labels ever exceed it,
   instead of wrapping the action cluster onto its own line. min-width:0 is
   required for a flex child to be allowed to shrink below its content size. */
.rc-navrow .review-tabs {
    /* ⭐COMPACT TABS (owner 2026-09-01: "the tabs in review center are now
       bloated and large comparative to previous"). SUPERSEDES the 2026-08-06
       "buttons fill the track" pass. That pass solved a real problem — a
       track stretched to 770px with 360px of tabs bunched left and 410px of
       dead grey pill — by GROWING the buttons. The growth was then masked
       for weeks by the inflated inline-controls cluster hoarding the row;
       when the 2026-09-01 navrow fix reclaimed that space, the tabs
       ballooned into it and read as bloat. Solve the dead-track problem from
       the other side: the track now HUGS its content-sized tabs (grow 0),
       so there is no stretched grey pill to leave dead space in. shrink 1 +
       min-width:0 + overflow-x keep the designed scroll fallback when the
       nav row runs tight. */
    flex: 0 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
}
.rc-navrow .review-tabs::-webkit-scrollbar { display: none; }
/* Content-sized pills (2026-09-01 compact ruling — see the track note above;
   this replaces the 2026-08-06 `flex: 1 0 auto` grow).
     flex-grow: 0   — a tab is as wide as its icon+label, never wider. This is
                      what "not bloated" means; the equal-share centring the
                      08-06 pass bought is deliberately given up.
     flex-shrink: 0 — KEPT. The 28px-stub regression (every label ellipsised
                      whole near 769px) is the thing this row must never do;
                      when the track can't seat the labels it scrolls instead
                      (the overflow-x fallback above).
     flex-basis: auto — content width is the floor.
   No breakpoint on purpose: reads the space actually available. The phone
   block (max-width:480px) keeps its own active/icon treatment. */
.rc-navrow .review-tab {
    flex: 0 0 auto;
}
/* The action cluster is fixed-size and always last — it must never be the
   thing that gets squeezed, since it owns Close. */
.rc-navrow .rdh-actions {
    flex: 0 0 auto;
    margin-left: auto;
}
/* Search + filters MIGRATE onto the nav row when this tab has no triage anchors
   (2026-08-04 follow-up). The tool row only earns its vertical space when it
   carries triage on the left; with nothing to balance against it was two
   right-floated icon buttons alone in a full-height band — the "unnecessary
   white space" the user reported. Here they sit between the tabs and the action
   cluster, both fixed-size, so the scrollable tabs absorb the difference. */
.rc-navrow .rc-inline-controls {
    flex: 0 0 auto;
    margin-left: auto;
    gap: 6px;
}
/* When the controls ride the nav row, the ACTION cluster no longer needs its
   own auto margin — the controls already pushed everything right, and a second
   auto margin would open a dead gutter between them. */
.rc-navrow .rc-inline-controls ~ .rdh-actions {
    margin-left: 0;
}

.rc-toolrow {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    min-width: 0;
    margin-bottom: 10px;
    transition: margin-bottom 0.18s ease;
}
/* Triage leads, controls trail. Triage may shrink (its segments self-hide when
   nothing is urgent); the controls keep their width so the filter affordance
   stays tappable. */
.rc-toolrow .rc-triage-strip {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
}
.rc-toolrow .rc-inline-controls {
    flex: 0 0 auto;
    margin-left: auto;
}
/* Scrolled state: the tool row is the FIRST thing to go. Once the user is
   reading the list they are oriented, so the pinned strip collapses to the nav
   row alone (tabs + close) and the list gets those pixels back. */
.rc-scrolled .rc-toolrow {
    margin-bottom: 0;
}

/* ============================================
   IN-STRIP BRAND + ACTIONS (2026-07-12 full-modal redesign)
   The old .review-dashboard-header row was dissolved into this single pinned
   strip. renderStripBrand() leads the row (brand), renderHeaderActions() trails
   it (sync · ⋯ · close). .rc-inline-controls (search+filters) carries
   margin-left:auto (see below), so everything from the controls onward clusters
   at the right edge while brand + tabs stay left.
   ============================================ */
.rc-tabrow .rdh-title {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--slate-800, #1e293b);
    letter-spacing: -0.01em;
    margin: 0;
    white-space: nowrap;
    flex-shrink: 0;
}
.rc-tabrow .rdh-title i {
    font-size: 15px;
    color: var(--primary-500, #14b8a6);
}
body.dark-mode .rc-tabrow .rdh-title {
    color: var(--slate-200, #e2e8f0);
}

/* Right-aligned action cluster: sync, overflow, close. Sits after the inline
   controls (which own margin-left:auto), so it hugs the strip's right edge. */
.rdh-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.rc-triage-strip {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 8px;
    padding: 0 2px;
    /* Push the chips to the right edge, opposite the tabs on the same row. */
    margin-left: auto;
}

.rc-triage-seg {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    border-radius: var(--radius-full, 9999px);
    border: 1px solid transparent;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    cursor: pointer;
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.rc-triage-seg .rc-triage-count { font-weight: 800; font-variant-numeric: tabular-nums; }
.rc-triage-seg i { font-size: 11px; }

/* Expiring-soon = amber (act before it auto-decides). */
.rc-triage-urgent {
    color: #92400e;
    background: var(--cmx-b67fc5d103);
    background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 14%, transparent);
    border-color: var(--cmx-08c34c09fd);
    border-color: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 30%, transparent);
}
.rc-triage-urgent i { color: var(--rc-status-tentative, #f59e0b); }
.rc-triage-urgent:hover { background: var(--cmx-fd17811893); background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 22%, transparent); }

/* Conflicts = busy red (canonical token, no hue drift). */
.rc-triage-conflict {
    color: var(--rc-status-busy, #dc2626);
    background: var(--cmx-c5a025da25);
    background: color-mix(in srgb, var(--rc-status-busy, #dc2626) 12%, transparent);
    border-color: var(--cmx-976ecac082);
    border-color: color-mix(in srgb, var(--rc-status-busy, #dc2626) 28%, transparent);
}
.rc-triage-conflict:hover { background: var(--cmx-3ddff277fd); background: color-mix(in srgb, var(--rc-status-busy, #dc2626) 20%, transparent); }

/* Awaiting = the plain backlog count. Calm slate, not an urgency colour — it
   only rides along when the strip is already up for a time-sensitive reason. */
.rc-triage-awaiting {
    color: var(--slate-600, #475569);
    background: rgba(148, 163, 184, 0.14);
    border-color: rgba(148, 163, 184, 0.32);
}
.rc-triage-awaiting i { color: var(--slate-500, #64748b); }
.rc-triage-awaiting:hover { background: rgba(148, 163, 184, 0.22); }
body.dark-mode .rc-triage-awaiting { color: var(--slate-300, #cbd5e1); }

.rc-triage-seg:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}

/* Compact team-fairness chip — lives in the HEADER row now (was the triage
   strip). Sits in the previously-dead space between the title and the
   calendar filter. Sized to the header's 28px control height. The .rc-tf-*
   inner classes are shared with nothing else now but kept stable.

   Colour: the chip CARRIES the canonical band hue rather than reducing it to
   a lone 8px dot on a flat-grey field. --rc-tf-color is set inline to the
   band's --fv-l* token (review-dashboard.js renderHeaderFairnessChip), so the
   tint, border, and band word all derive from the SAME canonical source the
   per-card .rc-fairness-chip and the fairness dashboard use — no bespoke
   palette. The "Fairness" label + score stay neutral slate so the score reads
   as a number, not a verdict, and the hue lands on the one word it describes. */
.rdh-fairness-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* Sit just after the title, in what used to be dead space. The filter
       group keeps its own margin-left:auto and stays hard-right, so this
       chip simply fills the gap between title and filters. When there's no
       fairness history the chip isn't rendered and the gap returns to the
       filter's auto-margin — no layout breakage either way. */
    margin-left: 14px;
    padding: 4px 10px;
    height: 28px;
    border-radius: var(--radius-full, 9999px);
    /* Soft tint of the canonical band colour — same color-mix grammar as the
       per-card .rc-fairness-chip, kept light so the header stays calm. */
    border: 1px solid var(--cmx-38ce2da2bf);
    border: 1px solid color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 28%, transparent);
    background: var(--cmx-507737dab3);
    background: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 9%, var(--slate-50, #f8fafc));
    font-size: var(--rc-font-secondary, 12px);
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.rdh-fairness-chip:hover {
    background: var(--cmx-ea280d6c9d);
    background: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 15%, var(--slate-50, #f8fafc));
    border-color: var(--cmx-1e4b2ebac1);
    border-color: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 42%, transparent);
}
.rdh-fairness-chip:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
/* Fallback for browsers without color-mix() — quiet slate chrome, band hue
   still carried by the dot + band word below (which use the token directly). */
@supports not (background: color-mix(in srgb, red 9%, white)) {
    .rdh-fairness-chip {
        background: var(--slate-50, #f8fafc);
        border-color: var(--slate-200, #e2e8f0);
    }
    .rdh-fairness-chip:hover {
        background: var(--slate-100, #f1f5f9);
        border-color: var(--slate-300, #cbd5e1);
    }
}
.rc-tf-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--rc-tf-color, var(--primary-500, #14b8a6));
}
/* "Fairness" — quiet leading label so the number reads as a fairness score,
   not a bare count. Kept lighter than the band word so it doesn't compete. */
.rc-tf-label { color: var(--slate-500); font-weight: 600; }
/* Band word carries the canonical hue — it's the qualitative read; the score
   beside it is the quantitative one and stays neutral. The hue is DARKENED
   toward slate-900 so the mid-ramp amber (--fv-l5 #fbbf24) reads on the near-
   white chip; every band keeps its own hue, just deepened for text contrast
   (the dot + tint carry the lighter, true token value). */
.rc-tf-band {
    font-weight: 700;
    color: var(--cmx-407e725c4c);
    color: color-mix(in srgb, var(--rc-tf-color, var(--slate-700)) 70%, var(--slate-900, #0f172a));
}
@supports not (color: color-mix(in srgb, red 70%, black)) {
    .rc-tf-band { color: var(--rc-tf-color, var(--slate-700)); }
}
.rc-tf-score { font-weight: 800; color: var(--slate-800); font-variant-numeric: tabular-nums; }

/* Dark mode: --fv-l* already resolves to its dark-mode hex (fairness-view.css
   owns both), so the same color-mix grammar lands a readable tint on the
   slate-800 header. Higher mix % than light mode because the dark surface
   needs more colour to register. */
body.dark-mode .rdh-fairness-chip {
    background: var(--cmx-0bf14ddd14);
    background: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 16%, var(--slate-800, #1e293b));
    border-color: var(--cmx-70747dd299);
    border-color: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 38%, transparent);
}
body.dark-mode .rdh-fairness-chip:hover {
    background: var(--cmx-80c6b26dc8);
    background: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 24%, var(--slate-800, #1e293b));
    border-color: var(--cmx-beee4cfeba);
    border-color: color-mix(in srgb, var(--rc-tf-color, var(--primary-500, #14b8a6)) 50%, transparent);
}
@supports not (background: color-mix(in srgb, red 9%, white)) {
    body.dark-mode .rdh-fairness-chip {
        background: var(--slate-800, #1e293b);
        border-color: var(--slate-700, #334155);
    }
    body.dark-mode .rdh-fairness-chip:hover { background: var(--slate-700, #334155); }
}
body.dark-mode .rc-tf-label { color: var(--slate-400); }
body.dark-mode .rc-tf-score { color: var(--slate-100); }
/* Dark mode: the --fv-l* tokens are already lightened for dark surfaces, so
   the band word uses the raw token (no darkening — that would sink it into
   the dark chip). */
body.dark-mode .rc-tf-band { color: var(--rc-tf-color, var(--slate-200)); }

/* On narrow headers, drop the band word so the chip can't crowd the filter
   group. The "Fairness" label INTENTIONALLY stays (it's the thing that makes
   the score legible) so the chip reads "Fairness 68", never a bare "68". */
@media (max-width: 560px) {
    .rdh-fairness-chip .rc-tf-band { display: none; }
    .rdh-fairness-chip { margin-left: 8px; }
}
body.dark-mode .rc-triage-urgent { color: #fbbf24; }
body.dark-mode .rc-triage-conflict { color: #f87171; }

/* ============================================
   MAIN TAB NAVIGATION (mode-pill style)
   ============================================ */
.review-tabs {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    /* No bottom margin: the enclosing .rc-tabrow now owns the gap below the
       tab/triage row (the triage chips sit on the same line, right-aligned). */
    margin-bottom: 0;
    background: var(--slate-100, #f1f5f9);
    border-radius: var(--radius-full, 9999px);
}

.review-tab {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 6px 12px;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--slate-500);
    background: transparent;
    border: none;
    border-radius: var(--radius-full, 9999px);
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
}

.review-tab .tab-icon {
    font-size: 0.7rem;
}

/* Tab labels are always shown now. Previously they were hidden below 480px
   (icon-only tabs), which made the row cryptic on a phone — you couldn't tell
   what 📅/☰/👁 meant. With the "Requested" tab demoted to a "Sent by me" view
   there are three tabs (Scheduled / Proposals / Privacy), and three labelled
   tabs fit a 390px row cleanly (the mobile layout below makes them share the
   width evenly). See the @media (max-width:480px) tab block further down.

   ⚠️`display` DELIBERATELY NOT SET HERE (2026-09-21). This said
   `display: inline`, which the STEADY TABS block below already superseded
   with `inline-grid` (same specificity, later in source). Harmless in the
   cascade but actively misleading: `inline` is the exact value that unstacks
   the bold ghost and doubles the label, and leaving a dead copy of it on the
   base rule invites someone to "restore" it. The label's display now has one
   owner — the STEADY TABS block. */
.review-tab .tab-label {
    /* display: see the STEADY TABS block (inline-grid, stacks the ghost). */
}

.review-tab:hover {
    color: var(--slate-700);
    background: rgba(255, 255, 255, 0.5);
}

.review-tab.active {
    color: var(--white, #fff);
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.06));
    font-weight: 600;
}

/* Default active color if no color class specified */
.review-tab.active:not([class*="tab-"]) {
    background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
    color: var(--white, #fff);
    border: none;
}

.review-tab .tab-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    font-size: 0.6rem;
    font-weight: 700;
    color: white;
    background: rgba(255, 255, 255, 0.3);
    border-radius: var(--radius-full);
    margin-left: 2px;
}

/* Conflict warning riding the Scheduled tab (2026-08-15 redesign). This
   replaced the triage strip's conflicts segment so a conflict costs ZERO
   extra rows of chrome. Red is deliberate and exclusive: in this product
   red means conflict/danger and nothing else, so the one red chip on the
   resting layout is the conflict warning. Always a COUNT, never a bare
   dot (user rule 2026-08-11). Identity-tint rules below must not
   recolor it, hence the explicit colour on every state.
   2026-08-17, owner: "conflict tab still uses an ugly red circle". The
   solid red fill read as a near-circular disc next to the SOFT-TINT
   sibling badges (.tab-badge is translucent; the active tabs themselves
   are 14% tints). The mobile treatment of this same warning was already
   moved to the tint idiom (.review-conflict-icon.has-items, ~546); this
   brings the desktop badge into the same family: soft red tint, red ink,
   glyph and count on one line. Ink is DARKER than the tint's own red
   because the tint composites over light tab underlays; #991b1b clears
   4.5:1 even on the active teal tab, where #b91c1c does not. */
/* ⭐A CONFLICT CUE IS STRUCTURE, NOT AN ERROR STATE (owner 2026-09-21: "the
   red conflict area reads as an app issue rather than attractive structure,
   perhaps something more subtle" → "just a yellow conflict icon and remove
   the heavy red circle").

   Was a RED chip: #991b1b ink on a filled red-12% pill. Two things made it
   read as breakage rather than information — the enclosing filled circle
   (the shape the app uses for error badges everywhere else) and the danger
   hue. Both go. What remains is the TRIANGLE ITSELF in amber plus its count,
   with no background: the glyph carries the meaning and the colour carries
   the severity, so nothing has to be drawn around it.

   ⚠️THE COUNT STAYS — "COUNT, never a bare dot" is a standing user rule
   (2026-08-11, see renderConflictTabBadge). This change is hue + chrome only;
   the number, the click target and the aria-label are untouched.

   Amber not red: --rc-status-tentative is the app's existing "needs a look"
   hue, and a scheduling conflict is a thing to resolve, not a failure. Ink is
   #b45309 (amber-700) rather than the raw token so the count clears 4.5:1 on
   both the slate track and the active tab's own tint — #f59e0b itself
   measures ~2.1:1 there and the digit would have been the weakest part. */
.review-tab .tab-conflict-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    height: 16px;
    /* No filled pill, so no side padding to balance — the glyph sits directly
       next to the label. The trailing room is still reserved by
       .tab-can-conflict, so dropping the fill cannot move the tab. */
    padding: 0 2px;
    margin-left: 3px;
    font-size: 0.6rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: #b45309;
    background: transparent;
    border-radius: var(--radius-full);
    cursor: pointer;
    white-space: nowrap;
}
.review-tab .tab-conflict-badge i {
    font-size: 0.62rem;
    /* The triangle is the signal now that the fill is gone, so it takes the
       full amber while the count stays the darker, more legible ink. */
    color: var(--rc-status-tentative, #f59e0b);
}
/* Hover affordance is a ring, not a deeper fill: the active-tab idiom in
   this row is an inset ring, and a ring leaves the composited background
   (and so the count's contrast) untouched in both modes. */
/* Hover now has to supply the affordance the resting fill used to: a faint
   amber wash appears only on hover, so the control still announces itself as
   clickable without carrying a badge shape at rest. */
.review-tab .tab-conflict-badge:hover {
    background: var(--cmx-ee45cdc4c7);
    background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 16%, transparent);
}
.review-tab .tab-conflict-badge:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--rc-status-tentative, #f59e0b);
}
/* On the active tab the badge keeps the same treatment — there is no fill left
   to re-tint, and the ink already clears contrast on that tab's tint. */
.review-tab.active .tab-conflict-badge {
    color: #b45309;
    background: transparent;
}
/* Dark mode leans on the lighter conflict red (#f87171, the same hue the
   dark triage chip and dark focus ring already use) because the canonical
   red tints to near-black over slate-800. Ink is red-300 rather than
   red-400: the badge can sit on the ACTIVE teal tab's own tint, and only
   #fca5a5 keeps the count above 4.5:1 on that composite. */
/* Dark mode takes amber-300 for the count (amber-700 goes muddy on slate) and
   amber-400 for the triangle, keeping the light-mode relationship where the
   glyph is the brighter of the two. */
body.dark-mode .review-tab .tab-conflict-badge {
    color: #fcd34d;
    background: transparent;
}
body.dark-mode .review-tab .tab-conflict-badge i {
    color: #fbbf24;
}
body.dark-mode .review-tab .tab-conflict-badge:hover {
    background: rgba(251, 191, 36, 0.18);
}
body.dark-mode .review-tab .tab-conflict-badge:focus-visible {
    box-shadow: 0 0 0 2px var(--slate-900, #0f172a), 0 0 0 4px #fbbf24;
}

/* ============================================
   STEADY TABS (2026-09-21, owner: "Can the review center tabs not be a little
   smaller and more elegant. Currently clicking between event's conflicts and
   other areas changes the size")
   ============================================
   The row reflowed on EVERY click because a tab is content-sized
   (.rc-navrow .review-tab { flex: 0 0 auto }, the 2026-09-01 compact ruling)
   while three things about it change with selection:
     1. font-weight 500 -> 600 on .active. Bold text is wider, so the selected
        tab grew and shoved its neighbours sideways. This is the big one: it
        fires on every tab, every click.
     2. .tab-badge is in-flow and refreshTabBadges() ADDS/REMOVES the node as a
        count crosses zero, so a tab's width jumped by the badge's box.
     3. .tab-conflict-badge (Events only) is in-flow too, and
        updateConflictTabBadge() reconciles it on the same schedule — the
        "event's conflicts" case the owner named.
   Fix each at the source rather than pinning a width: reserve the WIDEST
   state's space at rest, so selection changes ink, never geometry.

   The paint still follows the action (the pill fill, the ring, the weight all
   still change on click) — only the box stops moving. */

/* 1. Reserve the bold width. The ghost carries the SAME string at the active
      weight, at zero height, so the label box is already as wide as its bold
      self while resting; switching weight then repaints inside a box that
      does not move. `data-label` is set by renderTabs(). */
.review-tab .tab-label {
    display: inline-grid;
    grid-template-columns: auto;
    justify-items: center;
    line-height: 1.2;
}
/* Both the real text and the ghost occupy the SAME single cell, so the cell
   (and therefore the tab) is sized by the wider of the two — the bold ghost. */
.review-tab .tab-label > * ,
.review-tab .tab-label::after {
    grid-row: 1;
    grid-column: 1;
}
.review-tab .tab-label::after {
    content: attr(data-label);
    height: 0;
    overflow: hidden;
    visibility: hidden;
    font-weight: 600;
    pointer-events: none;
}

/* 2. + 3. The badges must not RESIZE the tab when they appear. A badge is
      added/removed by refreshTabBadges() and updateConflictTabBadge() on every
      tab switch, so an in-flow badge moved the row on exactly the clicks the
      owner described ("event's conflicts").
      They are taken OUT OF FLOW and hung off the pill's trailing edge instead:
      the tab reserves the room once, via trailing padding, and the badge then
      appears and disappears inside space that is already there. The tab keeps
      its own width whether it carries a badge, two badges or none.
      Measured: without this, dropping the conflict chip shrank the Events tab
      119.27 -> 94.64px and dropping the count took it to 68.64px. */
.review-tab {
    position: relative;
}
.review-tab .tab-badge,
.review-tab .tab-conflict-badge {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0;
}
/* The room is keyed to what a tab CAN carry, never to what it is carrying
   right now — `.tab-can-badge` / `.tab-can-conflict` are written by
   renderTabs() from the tab's definition, so they are STATIC for the life of
   the strip. Keying off the badge itself (`:has(.tab-badge)`) was tried and
   rejected: the padding then vanishes with the last badge and the tab
   shrinks 115.64 -> 96.44px, which is the very jump this block exists to stop.
   The count rides the trailing edge; the conflict chip sits inboard of it. */
.review-tab.tab-can-badge {
    /* 28px seats a TWO-DIGIT count without touching the label; 24px was tried
       and a "99" overlapped it. Three digits are not reachable here — the
       badge itself caps its own text elsewhere. */
    padding-right: 28px;
}
.review-tab .tab-badge {
    right: 6px;
}
/* Events carries BOTH. Reading order stays count-then-conflict, exactly as the
   in-flow markup rendered it (the count is the tab's own subject; the red
   conflict chip is the outboard exception) — taking the badges out of flow
   must not silently reverse them. So the CONFLICT chip sits on the trailing
   edge and the COUNT sits inboard of it. */
.review-tab.tab-can-conflict {
    padding-right: 62px;
}
.review-tab.tab-can-conflict .tab-badge {
    right: 38px;
}
.review-tab.tab-can-conflict .tab-conflict-badge {
    right: 6px;
}

/* 4. Smaller and more elegant. The desktop pill was 6px/12px at 0.75rem with a
      16px badge — heavier than the chrome around it. Tighten the box, calm the
      badge, and let the label carry the tab instead of the padding.
      Touch devices are untouched: the (pointer: coarse) block above keeps its
      44px floor, and the <=480px blocks keep their own treatment. */
@media (hover: hover) and (pointer: fine) {
    .review-tabs {
        gap: 1px;
        padding: 2px;
    }
    .review-tab {
        gap: 5px;
        padding: 5px 10px;
        font-size: 0.72rem;
        letter-spacing: 0.005em;
    }
    .review-tab .tab-icon {
        font-size: 0.68rem;
    }
    .review-tab .tab-badge {
        min-width: 14px;
        height: 14px;
        padding: 0 3px;
        font-size: 0.57rem;
        margin-left: 1px;
    }
    .review-tab .tab-conflict-badge {
        height: 14px;
        padding: 0 5px;
        margin-left: 2px;
        font-size: 0.57rem;
    }
    .review-tab .tab-conflict-badge i {
        font-size: 0.52rem;
    }
}

body.dark-mode .review-tabs {
    background: var(--slate-800, #1e293b);
}

body.dark-mode .review-tab {
    color: var(--slate-400);
}

body.dark-mode .review-tab:hover {
    color: var(--slate-200);
    background: var(--slate-700);
}

body.dark-mode .review-tab.active {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

body.dark-mode .review-tab.active:not([class*="tab-"]) {
    background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
    color: var(--white, #fff);
    border: none;
}

/* ============================================
   COLOR-CODED TABS (2026-08-04 unified redesign)

   Tab identity comes from js/config/review-tab-identity.js — the SAME
   registry the launcher icon strip renders from, so a `tab-<color>` block
   must exist here for every color the registry names (teal / blue /
   emerald / purple / indigo). These are categorical (tab identity), not
   status — kept hardcoded so a status palette refresh can't recolor them.

   ONE flat treatment for every tab (the old mix of three gradients + one
   tinted style read as four unrelated controls, and two themes were
   referenced but never defined so those tabs rendered unthemed):
   - inactive: muted label, identity-colored icon
   - active:   identity tint bg + inset identity ring + dark identity text
   The ring is an inset box-shadow, not a border, so activation never
   changes layout width.
   ============================================ */

/* Teal — Events (scheduled for you) */
.review-tab.tab-teal .tab-icon { color: var(--primary-500); }
.review-tab.tab-teal:hover:not(.active) {
    color: var(--primary-600);
    background: rgba(20, 184, 166, 0.08);
}
.review-tab.tab-teal.active {
    background: rgba(20, 184, 166, 0.14);
    color: var(--primary-700, #0f766e);
    box-shadow: inset 0 0 0 1.5px rgba(20, 184, 166, 0.55);
}
.review-tab.tab-teal.active .tab-icon { color: var(--primary-700, #0f766e); }
.review-tab.tab-teal.active .tab-badge {
    background: rgba(20, 184, 166, 0.2);
    color: var(--primary-700, #0f766e);
}
.review-tab.tab-teal .tab-badge {
    background: rgba(20, 184, 166, 0.15);
    color: var(--primary-700);
}

/* Blue — Proposals (voting) */
.review-tab.tab-blue .tab-icon { color: #3b82f6; }
.review-tab.tab-blue:hover:not(.active) {
    color: #2563eb;
    background: rgba(59, 130, 246, 0.08);
}
.review-tab.tab-blue.active {
    background: rgba(59, 130, 246, 0.14);
    color: #1d4ed8;
    box-shadow: inset 0 0 0 1.5px rgba(59, 130, 246, 0.55);
}
.review-tab.tab-blue.active .tab-icon { color: #1d4ed8; }
.review-tab.tab-blue.active .tab-badge {
    background: rgba(59, 130, 246, 0.2);
    color: #1d4ed8;
}
.review-tab.tab-blue .tab-badge {
    background: rgba(59, 130, 246, 0.15);
    color: #1d4ed8;
}

/* Emerald — Sent by me (sub-tab identity; also its launcher icon) */
.review-tab.tab-emerald .tab-icon { color: #10b981; }
.review-tab.tab-emerald:hover:not(.active) {
    color: #059669;
    background: rgba(16, 185, 129, 0.08);
}
.review-tab.tab-emerald.active {
    background: rgba(16, 185, 129, 0.14);
    color: #047857;
    box-shadow: inset 0 0 0 1.5px rgba(16, 185, 129, 0.55);
}
.review-tab.tab-emerald.active .tab-icon { color: #047857; }
.review-tab.tab-emerald.active .tab-badge {
    background: rgba(16, 185, 129, 0.2);
    color: #047857;
}
.review-tab.tab-emerald .tab-badge {
    background: rgba(16, 185, 129, 0.15);
    color: #047857;
}

/* Purple — Privacy. Purple is the app-wide privacy hue
   (privacy-visual-config.js: purple = private/protective). */
.review-tab.tab-purple .tab-icon { color: var(--visibility-private, #8b5cf6); }
.review-tab.tab-purple:hover:not(.active) {
    color: #7c3aed;
    background: rgba(139, 92, 246, 0.08);
}
.review-tab.tab-purple.active {
    background: rgba(139, 92, 246, 0.14);
    color: #6d28d9;
    box-shadow: inset 0 0 0 1.5px rgba(139, 92, 246, 0.55);
}
.review-tab.tab-purple.active .tab-icon { color: #6d28d9; }
.review-tab.tab-purple.active .tab-badge {
    background: rgba(139, 92, 246, 0.2);
    color: #6d28d9;
}
.review-tab.tab-purple .tab-badge {
    background: rgba(139, 92, 246, 0.15);
    color: #6d28d9;
}

/* Indigo — Requests (people asking to join). Indigo, not violet:
   #7c3aed is a reserved status hue (canonical-shading-guards). */
.review-tab.tab-indigo .tab-icon { color: #6366f1; }
.review-tab.tab-indigo:hover:not(.active) {
    color: #4f46e5;
    background: rgba(99, 102, 241, 0.08);
}
.review-tab.tab-indigo.active {
    background: rgba(99, 102, 241, 0.14);
    color: #4338ca;
    box-shadow: inset 0 0 0 1.5px rgba(99, 102, 241, 0.55);
}
.review-tab.tab-indigo.active .tab-icon { color: #4338ca; }
.review-tab.tab-indigo.active .tab-badge {
    background: rgba(99, 102, 241, 0.2);
    color: #4338ca;
}
.review-tab.tab-indigo .tab-badge {
    background: rgba(99, 102, 241, 0.15);
    color: #4338ca;
}

/* Dark Mode - Color-coded tabs
   Same flat model; tints deepen, text lightens for contrast.
   ============================================ */
body.dark-mode .review-tab.tab-teal .tab-icon { color: var(--primary-400); }
body.dark-mode .review-tab.tab-teal:hover:not(.active) {
    color: var(--primary-300);
    background: rgba(20, 184, 166, 0.12);
}
body.dark-mode .review-tab.tab-teal.active {
    background: rgba(20, 184, 166, 0.22);
    color: #5eead4;
    box-shadow: inset 0 0 0 1.5px rgba(45, 212, 191, 0.5);
}
body.dark-mode .review-tab.tab-teal.active .tab-icon { color: #5eead4; }
body.dark-mode .review-tab.tab-teal.active .tab-badge {
    background: rgba(20, 184, 166, 0.3);
    color: #5eead4;
}
body.dark-mode .review-tab.tab-teal .tab-badge {
    background: rgba(20, 184, 166, 0.25);
    color: var(--primary-300, #5eead4);
}

body.dark-mode .review-tab.tab-blue .tab-icon { color: #60a5fa; }
body.dark-mode .review-tab.tab-blue:hover:not(.active) {
    color: #93c5fd;
    background: rgba(59, 130, 246, 0.12);
}
body.dark-mode .review-tab.tab-blue.active {
    background: rgba(59, 130, 246, 0.22);
    color: #93c5fd;
    box-shadow: inset 0 0 0 1.5px rgba(96, 165, 250, 0.5);
}
body.dark-mode .review-tab.tab-blue.active .tab-icon { color: #93c5fd; }
body.dark-mode .review-tab.tab-blue.active .tab-badge {
    background: rgba(59, 130, 246, 0.3);
    color: #93c5fd;
}
body.dark-mode .review-tab.tab-blue .tab-badge {
    background: rgba(59, 130, 246, 0.25);
    color: #93c5fd;
}

body.dark-mode .review-tab.tab-emerald .tab-icon { color: #34d399; }
body.dark-mode .review-tab.tab-emerald:hover:not(.active) {
    color: #6ee7b7;
    background: rgba(16, 185, 129, 0.12);
}
body.dark-mode .review-tab.tab-emerald.active {
    background: rgba(16, 185, 129, 0.22);
    color: #6ee7b7;
    box-shadow: inset 0 0 0 1.5px rgba(52, 211, 153, 0.5);
}
body.dark-mode .review-tab.tab-emerald.active .tab-icon { color: #6ee7b7; }
body.dark-mode .review-tab.tab-emerald.active .tab-badge {
    background: rgba(16, 185, 129, 0.3);
    color: #6ee7b7;
}
body.dark-mode .review-tab.tab-emerald .tab-badge {
    background: rgba(16, 185, 129, 0.25);
    color: #6ee7b7;
}

body.dark-mode .review-tab.tab-purple .tab-icon { color: #a78bfa; }
body.dark-mode .review-tab.tab-purple:hover:not(.active) {
    color: #c4b5fd;
    background: rgba(139, 92, 246, 0.12);
}
body.dark-mode .review-tab.tab-purple.active {
    background: rgba(139, 92, 246, 0.22);
    color: #c4b5fd;
    box-shadow: inset 0 0 0 1.5px rgba(167, 139, 250, 0.5);
}
body.dark-mode .review-tab.tab-purple.active .tab-icon { color: #c4b5fd; }
body.dark-mode .review-tab.tab-purple.active .tab-badge {
    background: rgba(139, 92, 246, 0.3);
    color: #c4b5fd;
}
body.dark-mode .review-tab.tab-purple .tab-badge {
    background: rgba(139, 92, 246, 0.25);
    color: #c4b5fd;
}

body.dark-mode .review-tab.tab-indigo .tab-icon { color: #a5b4fc; }
body.dark-mode .review-tab.tab-indigo:hover:not(.active) {
    color: #c7d2fe;
    background: rgba(99, 102, 241, 0.12);
}
body.dark-mode .review-tab.tab-indigo.active {
    background: rgba(99, 102, 241, 0.22);
    color: #c7d2fe;
    box-shadow: inset 0 0 0 1.5px rgba(165, 180, 252, 0.5);
}
body.dark-mode .review-tab.tab-indigo.active .tab-icon { color: #c7d2fe; }
body.dark-mode .review-tab.tab-indigo.active .tab-badge {
    background: rgba(99, 102, 241, 0.3);
    color: #c7d2fe;
}
body.dark-mode .review-tab.tab-indigo .tab-badge {
    background: rgba(99, 102, 241, 0.25);
    color: #c7d2fe;
}

/* ============================================
   CONFLICTS PANEL
   ============================================ */
.conflicts-panel {
    padding: 16px;
}

/* ONE header row (2026-08-15 redesign): back + title + live count share a
   single line. The old chrome stacked this header on a second full-width
   progress band ("Conflict N of M" + bar), so the first card started two
   bands down; the count is a short phrase and rides the line instead. */
.conflicts-panel-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
}

.conflicts-panel-header h3 {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    color: var(--slate-800);
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

/* The warning glyph on the title is the panel's identity: red, because red
   means exactly conflict/danger in this product. */
.conflicts-title-icon {
    color: var(--rc-conflict-red, #dc2626);
    font-size: 0.85rem;
}

.conflicts-header-count {
    margin-left: auto;
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--slate-500);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.back-to-scheduled-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: none;
    border-radius: var(--radius-full);
    background: var(--slate-100);
    color: var(--slate-600);
    font-size: 0.75rem;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
}

.back-to-scheduled-btn:hover {
    background: var(--slate-200);
    color: var(--slate-800);
}

.conflicts-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

/* Conflict card uses standard Review chrome (translucent white + slate
   border) so it sits in the same visual family as scheduled/proposals/
   outgoing cards. Red is reserved for the left-border accent + the
   overlap pill + the timeline overlap band, mirroring the per-category
   accent pattern used elsewhere. */
.conflict-item {
    position: relative;
    background: var(--review-card-bg);
    /* Glass, radius, padding and hover now come from the SAME tokens
       .review-item uses (2026-08-22, user: "the conflict tab ... clashes with
       my app in style"). Previously this card reached past the review-center
       scale into the global one for --radius-lg (16px, twice every other card
       in the panel), hard-coded its padding to 12px/14px, dropped the
       backdrop-filter every sibling card has, and defined no :hover at all —
       so conflict cards read as inert tiles from a different product. */
    backdrop-filter: blur(8px);
    border: 1px solid var(--review-card-border);
    border-left: 3px solid var(--rc-status-busy);
    border-radius: var(--rc-radius-card);
    padding: var(--rc-spacing-card);
    box-shadow: var(--review-card-shadow);
    display: flex;
    flex-direction: column;
    gap: var(--rc-spacing-section);
    transition: all 0.15s ease;
}

/* Same lift + teal ring as .review-item. The left border stays red — it is the
   category accent for "overlap" — but the hover affordance is now shared, so a
   conflict card behaves like every other card in the Review Center. */
.conflict-item:hover {
    border-color: var(--primary-300);
    border-left-color: var(--rc-status-busy);
    box-shadow: var(--shadow-md), 0 0 0 2px rgba(20, 184, 166, 0.08);
    transform: translateY(-1px);
}

/* Compact header: overlap pill + date/range meta + the whole-conflict
   "Keep both" action, all on ONE row, like Find Times' best-time-card
   #rank + info row.
   THREE children now (2026-08-15 redesign: Keep both left its own footer
   band), so this packs from the start and lets .keep-both-btn claim the
   right edge with margin-left:auto. space-between would have stranded
   .conflict-card-meta in the middle of the line. */
.conflict-card-header {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    flex-wrap: wrap;
}

.conflict-overlap-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border-radius: var(--radius-full);
    background: rgba(220, 38, 38, 0.08);
    border: 1px solid rgba(220, 38, 38, 0.25);
    color: var(--rc-status-busy);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    white-space: nowrap;
}

.conflict-overlap-pill i {
    font-size: 0.72rem;
}

.conflict-card-meta {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    font-variant-numeric: tabular-nums;
    color: var(--slate-600);
}

.conflict-card-date {
    font-size: 0.72rem;
    font-weight: 500;
    color: var(--slate-500);
}

.conflict-card-range {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--slate-700);
}

/* ============================================
   CONFLICT MINI-BOARD (2026-08-15 redesign)
   The card body speaks the BOARD's grammar: one row per event, exactly
   like a timezone row (name on the left, hour track on the right), plus
   that row's actions at the far end. Colour canon (owner rule):
     - event spans  = BUSY slate  var(--status-busy)  #64748b
       (a proposed/tentative event keeps the stripe treatment, in slate;
        event blocks are NEVER orange/amber here)
     - empty track  = var(--status-empty)  #e2e8f0 (never white)
     - RED is reserved for the ONE conflict element: the overlap column
       crossing both rows, like the board's scanner column.
   ============================================ */
.rcx-board {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* [label | track | actions]: the track column is shared by every row of
   the board (including the axis row), so bars and ticks stay aligned. */
.rcx-row {
    display: grid;
    grid-template-columns: minmax(76px, 148px) minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
}

/* The label column now carries TWO lines: the event's identity, and when it
   runs (2026-09-21, owner: "conflict diagrams should have full labels
   regarding start end time etc"). The times moved here OUT of the bar,
   because a label sized by the bar it sits in clips to "1..." on a short
   event — see renderConflictMiniBoard. */
.rcx-row-label {
    display: inline-flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 1px;
    min-width: 0;
}

.rcx-row-head {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    max-width: 100%;
}

/* start–end · duration, and the date when the row needs one. Tabular figures
   so the two rows' times align vertically and can be compared at a glance. */
.rcx-row-when {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    min-width: 0;
    max-width: 100%;
    font-size: 0.7rem;
    line-height: 1.25;
    color: var(--slate-500, #64748b);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.rcx-row-range { font-weight: 600; color: var(--slate-600, #475569); }
.rcx-row-duration {
    font-weight: 600;
    padding: 0 5px;
    border-radius: var(--radius-full, 999px);
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-600, #475569);
}
/* The date is the LEAST important of the three (the card header already says
   the day; this only earns its place when the two events differ), so it is
   the one that gives way first. */
.rcx-row-date {
    color: var(--slate-400, #94a3b8);
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
body.dark-mode .rcx-row-when { color: var(--slate-400, #94a3b8); }
body.dark-mode .rcx-row-range { color: var(--slate-300, #cbd5e1); }
body.dark-mode .rcx-row-duration {
    background: var(--slate-700, #334155);
    color: var(--slate-200, #e2e8f0);
}
body.dark-mode .rcx-row-date { color: var(--slate-500, #64748b); }

.rcx-row-name {
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--slate-800);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Tiny status swatch tying the label to its bar. Same tokens as the bar. */
.rcx-row-swatch {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 2px;
    background: var(--status-busy, #64748b);
}
.rcx-row-swatch[data-status="tentative"] {
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.5) 0 2px,
        transparent 2px 4px
    );
}

.rcx-row-attendees {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    flex-shrink: 0;
    font-size: 0.66rem;
    font-weight: 600;
    color: var(--slate-500);
}
.rcx-row-attendees i { font-size: 0.6rem; }

/* Proposal rows in a conflict card (2026-08-19): the entry is a proposed time,
   not a booking — the pill says so inline, using the SAME "Proposed" noun the
   event modal status line and chips use (no new vocabulary). */
.rcx-row-proposal-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.65rem;
    font-weight: 600;
    color: var(--slate-500);
    padding: 1px 6px;
    border-radius: var(--radius-full);
    border: 1px dashed var(--slate-300, #cbd5e1);
    white-space: nowrap;
    flex-shrink: 0;
}
body.dark-mode .rcx-row-proposal-pill {
    color: var(--slate-400, #94a3b8);
    border-color: var(--slate-600, #475569);
}

/* The hour track: the board's empty tint, hour gridlines, one bar. */
.rcx-track {
    position: relative;
    height: 22px;
    min-width: 0;
    background: var(--status-empty, #e2e8f0);
    border-radius: 4px;
    overflow: hidden;
}

/* PER-HOUR CELLS, THE SAME ONES THE BOARD DRAWS (2026-09-23, owner: "Touching
   including per hour cells as normal in my ui. Make jt relatable to my
   timelines").

   The track used to be a flat bar with white hairlines painted over it — a
   chart. The product's timeline is a row of discrete hour cells separated by
   `--cell-grid-border` (style.css `.timeline-cell`: `border-right: 1px solid
   var(--cell-grid-border)`), so these cells use THAT token rather than a
   literal. One surface, one grid vocabulary, and dark mode follows for free
   because the token already flips.

   Background layer only: the spans and the red overlap column still position
   themselves by percentage above it, so the geometry that was already right is
   untouched. */
.rcx-hour-cells {
    position: absolute;
    inset: 0;
    pointer-events: none;
}
.rcx-hour-cell {
    position: absolute;
    top: 0;
    bottom: 0;
    /* The divider IS the cell edge, exactly as on the board. The last cell's
       own right border lands on the track's edge, where `overflow: hidden`
       clips it — so the run never ends in a stray rule. */
    border-right: 1px solid var(--cell-grid-border, rgba(15, 23, 42, 0.12));
}
/* The hour numeral inside each cell (2026-09-23). It is BACKGROUND furniture,
   like the numerals on the board's own timeline: quiet enough that the spans
   and the red overlap column still read first, but present so the diagram says
   WHICH hours it covers without the reader going back to the header.
   Sits at the cell's top-left, against its own leading edge, so the number
   belongs to the hour it starts — not centred, which reads as belonging to the
   divider. The whole layer is already pointer-events:none and aria-hidden. */
.rcx-hour-cell-label {
    position: absolute;
    /* ⭐THE NUMERAL SITS ON THE FLOOR, NOT THE CEILING (2026-09-23). At
       `top: 1px` it shared a line with `.rcx-span-label` — the time printed
       INSIDE the bar, which is vertically centred — and MEASURED, the "7" was
       covered by 4.6px of it. The two are different kinds of fact (the grid vs
       this event's own range), so they get different bands: the hour numerals
       run along the cell's baseline, the bar keeps the middle. */
    bottom: 1px;
    left: 3px;
    font-size: 0.5rem;
    font-weight: 600;
    line-height: 1;
    color: var(--slate-400, #94a3b8);
    opacity: 0.75;
    user-select: none;
}
body.dark-mode .rcx-hour-cell-label {
    color: var(--slate-500, #64748b);
}
/* ⭐THE NUMERAL MUST SURVIVE THE BAR (2026-09-23, owner: "we should have
   regular cells behind and each one labeled per hour").
   The cells ARE labelled — but MEASURED on the built bundle, 3 of the 4
   numerals on a 06:00–10:00 row were invisible: `.rcx-hour-cells` is the first
   child of the track and nothing in `.rcx-*` sets a z-index, so the solid
   `.rcx-span` (top:2px → bottom:2px of a 22px track) and the red overlap wash
   paint straight over a numeral that sits at top:1px/height:8px. Only the hours
   OUTSIDE the event were readable — so the card still could not say WHICH hours
   the overlap covered, which is the one question it exists to answer.
   The hours are the grid the bars are read against, so they go ON TOP: the
   layer is raised above the span and the overlap, and the numeral carries a
   halo sized to its own weight so it holds over the empty tint, the slate fill
   and the red band alike. The layer stays pointer-events:none + aria-hidden,
   so nothing about interaction or the accessible name changes.
   ⛔Do not solve this by dimming the span: the bar's solidity IS the event. */
.rcx-hour-cells {
    z-index: 2;
}
.rcx-hour-cell-label {
    /* ⭐SLATE ON SLATE IS NO CONTRAST. Raising the layer was necessary but not
       sufficient: MEASURED, the numerals then painted on top of the bar and
       were still unreadable, because `--slate-400 #94a3b8` at 8px over the
       `--status-busy #64748b` fill is barely a shade apart.
       The numeral cannot know which of three backgrounds it lands on — empty
       tint, slate fill, or red wash — and one flat colour cannot serve all
       three. So it stops relying on colour and takes a HALO: a white ring wide
       enough to seat the glyph against the two dark fills, over a numeral
       darkened to slate-600 so it still reads against that ring on the pale
       empty tint. The ring is what makes it legible everywhere; the darker ink
       is what keeps it from looking like a smudge where there is no bar.
       Halo stays ≤2px at 8px text — wider and the numerals bloom. */
    color: var(--slate-600, #475569);
    text-shadow:
        0 0 2px #fff,
        0 0 2px #fff,
        0 0 3px rgba(255, 255, 255, 0.9);
    opacity: 1;
}
body.dark-mode .rcx-hour-cell-label {
    /* Mirrored: pale ink on a dark ring. */
    color: var(--slate-300, #cbd5e1);
    text-shadow:
        0 0 2px rgba(2, 6, 23, 0.98),
        0 0 2px rgba(2, 6, 23, 0.98),
        0 0 3px rgba(2, 6, 23, 0.9);
    opacity: 1;
}
/* A long range packs the cells too tight for a numeral. renderConflictBoard
   stamps data-dense="1" past the hour count that still fits, and the whole
   numeral layer drops rather than clipping — the same demote-don't-clip rule
   the team bar's segment labels follow. */
.rcx-hour-cells[data-dense="1"] .rcx-hour-cell-label {
    display: none;
}
.rcx-gridline {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: rgba(255, 255, 255, 0.75);
}

/* The event's span: solid busy slate, exactly the board's busy block. */
.rcx-span {
    position: absolute;
    top: 2px;
    bottom: 2px;
    border-radius: 3px;
    background: var(--status-busy, #64748b);
    min-width: 2px;
}
/* Proposed/tentative keeps the board's stripe treatment IN SLATE. Never
   amber/orange: those are not this product's event-block vocabulary, and
   red must stay unique to the conflict column. */
.rcx-span[data-status="tentative"] {
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.35) 0 4px,
        transparent 4px 8px
    );
}

/* `.rcx-span-label` REMOVED 2026-09-23 — nothing emits it any more. It printed
   the range a second time (the row label already carries it) and its centred
   9.5px line was the one thing the per-hour numerals could not clear on a 22px
   track. Its sibling `.rcx-span-label-out` went in 2026-09-21 for the same
   underlying reason: a label sized by the bar it sits in.
   ⛔The range belongs to the row label, which is never sized by the bar. */
/* .rcx-span-label-out REMOVED 2026-09-21. It floated the time past a narrow
   bar's right edge — but .rcx-track is overflow:hidden, so on a short event it
   was simply cut off. Its replacement is not another placement rule: the time
   now lives in the row label, which is never sized by the bar. Nothing emits
   this class any more. */

/* THE red element: the overlap column. A wash + dashed edges over the
   track, repeated at the same coordinates on both rows so it reads as one
   column crossing them, the way the board's scanner column crosses rows. */
.rcx-overlap {
    position: absolute;
    top: 0;
    height: 100%;
    background: var(--cmx-0914566690);
    background: color-mix(in srgb, var(--rc-conflict-red, #dc2626) 22%, transparent);
    border-left: 1.5px dashed var(--rc-conflict-red, #dc2626);
    border-right: 1.5px dashed var(--rc-conflict-red, #dc2626);
    pointer-events: none;
    /* Centre the band's own size label (2026-09-21). */
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* THE BAND STATES ITS OWN SIZE. Until now the overlap's magnitude lived only
   in the card header pill, so the one element the whole card is built around
   said nothing.

   ⚠️IT MUST VANISH RATHER THAN CLIP. This label IS sized by the band it sits
   in — the exact trap the event times were moved out of the bar to escape —
   so it cannot be allowed to truncate to "3..." . A container query on the
   band hides it below the width that fits ~5 characters ("1h 40m" at 9px is
   ~34px), and `overflow: hidden` on .rcx-overlap is the backstop. The header
   pill always carries the same number in full, so hiding costs nothing. */
.rcx-overlap {
    container-type: inline-size;
    container-name: rcx-overlap;
}
.rcx-overlap-label {
    font-size: 9px;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    color: var(--rc-conflict-red, #dc2626);
    font-variant-numeric: tabular-nums;
    padding: 1px 3px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.82);
    /* Hidden by default; the container query below opts it back in. An engine
       without container queries therefore shows no band label at all — which
       is the SAFE direction, since the header pill still states the overlap. */
    display: none;
}
@container rcx-overlap (min-width: 38px) {
    .rcx-overlap-label { display: inline-block; }
}
body.dark-mode .rcx-overlap-label {
    color: #fecaca;
    background: rgba(15, 23, 42, 0.78);
}

/* Hour axis under the tracks; sits in the same grid column as the tracks
   so its ticks line up with the gridlines above. */
.rcx-axis-row { min-height: 14px; }
.rcx-axis {
    position: relative;
    height: 14px;
    min-width: 0;
}
.rcx-tick {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    font-size: 10px;
    color: var(--slate-500);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    white-space: nowrap;
}
.rcx-tick.is-edge:first-child .rcx-tick-label {
    display: inline-block;
    transform: translateX(calc(50% - 1px));
}
.rcx-tick.is-edge:last-child .rcx-tick-label {
    display: inline-block;
    transform: translateX(calc(-50% + 1px));
}

/* PHONES: THE ROW STACKS SO ITS BUTTONS CAN KEEP THEIR WORDS (2026-09-21).
   This block used to hold the row to one line by making the actions
   icon-only — [label | track | ↺ ✕]. The owner's screenshot showed the
   result: three unlabelled glyphs where ✕ reads as "close this card" rather
   than "decline this event", and ↺ vs the proposal tray is a coin-flip.
   That is the SAME complaint already ruled on for "Keep both" (2026-08-16,
   user: "accept overlap button is not well understandable"), which was
   exempted from the icon-only collapse for exactly this reason; the other
   three were simply never revisited.
   So: identity+time on one line, the track full width beneath it, actions
   on their own line WITH their labels. Taller, but every control says what
   it does. Interior ticks still go; edge ticks stay. */
@media (max-width: 480px) {
    /* ⭐THE TWO LANES MUST TOUCH (2026-09-23, owner screenshot + "Touching …
       Make jt relatable to my timelines").

       The one-column stack above put each row's ACTION BUTTONS between the two
       tracks: MEO's lane, then Reschedule/Decline, then a divider, then
       Meeting's label, then its lane. The lanes were ~200px apart, so the one
       thing the card exists to show — that the red columns line up — could not
       be seen. They were correctly aligned and invisibly so.

       Fix without touching the DOM (every handler, the highlight scroll and the
       resolution flows find rows by `.conflict-event[data-event-id]`): make the
       BOARD the grid and let each row contribute its parts to it. The label and
       track of row 1, then row 2, sit in adjacent bands; both action blocks
       follow underneath. `order` does the reordering, so markup and JS are
       unchanged.

       Actions keep their own line per event, labelled — the 2026-08-16 ruling
       ("accept overlap button is not well understandable") means these controls
       must say which event they act on, so they are NOT merged into one block. */
    .rcx-board {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        gap: 0;
    }
    .rcx-row {
        display: contents;
    }
    /* Bands, in visual order:
         label₁ · label₂ · track₁ · track₂ · axis · actions₁ · actions₂
       ⚠️BOTH LABELS COME BEFORE BOTH TRACKS. The obvious order —
       label₁ track₁ label₂ track₂ — looks right written down and does NOT
       work: label₂ then occupies a band BETWEEN the two tracks and holds them
       22px apart (measured). Anything at all between the lanes defeats the
       point, so each track is preceded only by the other track. Each label
       still names its event and sits directly above the pair it belongs to. */
    .rcx-row-label { order: 1; }
    .rcx-row:nth-of-type(2) .rcx-row-label { order: 2; }
    .rcx-track { order: 3; }
    .rcx-row:nth-of-type(2) .rcx-track { order: 4; }
    .rcx-axis-row .rcx-axis { order: 5; }
    .rcx-actions { order: 6; }
    .rcx-row:nth-of-type(2) .rcx-actions { order: 7; }

    /* TOUCHING, LIKE CELLS — not two rounded pills with a gap. The lanes share
       one outer radius: the first track rounds its top corners, the second its
       bottom, and the seam between them is square so the red overlap column
       reads as ONE column crossing both. */
    .rcx-track {
        border-radius: 0;
        margin: 0;
    }
    .rcx-row:nth-of-type(1) .rcx-track {
        border-radius: 4px 4px 0 0;
    }
    .rcx-row:nth-of-type(2) .rcx-track {
        border-radius: 0 0 4px 4px;
        /* The hairline seam between the lanes, in the grid's own token so it
           matches the vertical hour dividers rather than competing with them. */
        border-top: 1px solid var(--cell-grid-border, rgba(15, 23, 42, 0.12));
    }
    /* Breathing room around the pair, applied to the BANDS rather than the row
       (which is now `display: contents` and cannot carry padding). */
    .rcx-row-label { padding-top: 6px; }
    .rcx-actions { padding-bottom: 2px; }
    /* The label goes back to ONE line here: with the full row width to itself
       the title and its times fit side by side, and two stacked lines would
       cost a band for nothing. */
    .rcx-row-label {
        /* `flex`, not the base `inline-flex`: as a direct grid item an
           inline-flex box shrink-wraps and the grid's default centring pulls
           the title to the middle of the card. Each label must sit hard left,
           directly above the lane it names. */
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: flex-start;
        flex-wrap: wrap;
        gap: 4px 8px;
        width: 100%;
    }
    .rcx-row-name { font-size: 0.76rem; }
    .rcx-row-when { font-size: 0.68rem; }
    .rcx-actions { justify-content: flex-start; width: 100%; }
    /* ⭐WHICH ROW DO THESE BUTTONS BELONG TO? (2026-09-23, owner: "now the
       buttons appear as duplicates").
       On desktop each row's actions sit BESIDE that row, so they need no
       caption. Here the band order above deliberately puts both tracks
       together (label₁ label₂ track₁ track₂ axis actions₁ actions₂), which
       leaves the two action groups stacked at the bottom, detached from the
       rows they act on. When BOTH rows are proposals they render the same
       "Open proposal" button, so the pair reads as one control duplicated —
       and nothing on screen says which proposal each opens. They are NOT
       duplicates: each carries its own data-proposal-id.
       Caption each group with its row's own time range, from the stamp
       renderConflictBoard writes, so the button names its subject. */
    .rcx-actions[data-row-range]::before {
        content: attr(data-row-range);
        align-self: center;
        margin-right: 6px;
        font-size: 0.62rem;
        font-weight: 600;
        font-variant-numeric: tabular-nums;
        color: var(--slate-500, #64748b);
        white-space: nowrap;
    }
    body.dark-mode .rcx-actions[data-row-range]::before {
        color: var(--slate-400, #94a3b8);
    }
    /* The axis row's placeholder is display:none above, but it carries no
       stamp anyway — belt and braces so an empty caption can never paint. */
    .rcx-axis-row .rcx-actions::before { content: none; }
    .rcx-tick:not(.is-edge) { display: none; }
    /* The axis row contributes only its axis to the board grid; its empty
       label/actions placeholders would otherwise take bands of their own.
       (`.rcx-axis-row` itself is `display: contents` like every row, so it has
       no box left to pad — the spacing lives on the bands above.) */
    .rcx-axis-row .rcx-row-label,
    .rcx-axis-row .rcx-actions { display: none; }
    .rcx-axis { order: 5; margin-top: 2px; }
    /* (The phone-only `.rcx-span-label { display: none }` is gone 2026-09-23 —
       that rule had already found the in-bar time to be "pure duplication";
       the renderer now omits it at EVERY width, so there is nothing to hide.) */
}

.conflict-event-actions {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    min-width: 0;
}

/* Action buttons echo Find Times' compact vote-button sizing — smaller
   than the old 36px chunky version, but still keeps the text label at
   desktop widths so Cancel vs Decline stays unambiguous. */
.conflict-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    height: 28px;
    padding: 0 10px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
    white-space: nowrap;
}

.conflict-action-btn i {
    font-size: 0.72rem;
}

.conflict-action-btn.reschedule-btn {
    background: var(--slate-50);
    color: var(--slate-700);
    border-color: var(--slate-200);
}

.conflict-action-btn.reschedule-btn:hover {
    background: #eff6ff;
    color: #1d4ed8;
    border-color: #bfdbfe;
}

.conflict-action-btn.decline-conflict-btn,
.conflict-action-btn.cancel-conflict-btn {
    background: var(--slate-50);
    color: var(--slate-700);
    border-color: var(--slate-200);
}

.conflict-action-btn.decline-conflict-btn:hover,
.conflict-action-btn.cancel-conflict-btn:hover {
    background: #fef2f2;
    color: #b91c1c;
    border-color: #fecaca;
}

/* "Keep both" applies to the whole conflict, so it rides the card header
   line (right edge) instead of buying a footer band of its own. */
.conflict-card-header .keep-both-btn {
    margin-left: auto;
}

/* The overlap pill now also names WHEN the overlap is, so the diagram
   does not need a floating label of its own. */
.conflict-overlap-pill-range {
    font-weight: 500;
    font-variant-numeric: tabular-nums;
}

/* ⛔NO ICON-ONLY COLLAPSE HERE (2026-09-21, owner: "perhaps we need an
   explaination of those icons", with a phone screenshot of the conflict
   cards).

   This block used to hide EVERY .conflict-action-btn's word below 480px,
   exempting only "Keep both" — and its comment claimed "Reschedule (a
   clock-arrow) and Decline (an ×) are conventional enough to survive as
   glyphs". The screenshot disproved that: a bare ✕ on a card reads as
   "dismiss this card", not "decline this event", and ↺ against the proposal
   row's inbox-tray is a coin-flip. Three unlabelled glyphs, two of which
   mutate someone else's calendar.

   The 2026-08-16 ruling that saved "Keep both" — a glyph must never be the
   ONLY carrier of meaning — was always the general rule; it was just applied
   to one button. It now applies to all of them. The row stacks on phones (see
   the .rcx-row block above) precisely so there is room for the words, so this
   costs a line of height, not legibility.

   ⚠️If a future width squeeze tempts you back here: shorten the WORD, or
   stack further. Do not re-hide it. */
@media (max-width: 480px) {
    .conflict-action-btn {
        padding: 0 10px;
        min-width: 0;
    }
    .conflict-action-btn span { display: inline; }
}

/* Dark mode conflicts panel */
body.dark-mode .conflicts-panel-header h3 {
    color: var(--slate-200);
}

body.dark-mode .back-to-scheduled-btn {
    background: var(--slate-700);
    color: var(--slate-300);
}

body.dark-mode .back-to-scheduled-btn:hover {
    background: var(--slate-600);
    color: var(--slate-100);
}

body.dark-mode .conflict-item {
    background: var(--review-card-bg);
    border-color: var(--review-card-border);
    border-left-color: var(--rc-status-busy);
}

body.dark-mode .conflict-overlap-pill {
    background: rgba(248, 113, 113, 0.1);
    border-color: rgba(248, 113, 113, 0.3);
    color: #f87171;
}

body.dark-mode .conflict-card-date {
    color: var(--slate-400);
}

body.dark-mode .conflict-card-range {
    color: var(--slate-200);
}

/* Mini-board dark mode: the board's own dark inversion. Busy must LIFT
   (#94a3b8) and empty must DARKEN (#334155): "faint" on a dark board means
   darker, not lighter (2026-08-14 palette decision). */
body.dark-mode .rcx-row-name {
    color: var(--slate-200);
}
body.dark-mode .rcx-track {
    background: var(--slate-700, #334155);
}
body.dark-mode .rcx-gridline {
    background: rgba(15, 23, 42, 0.55);
}
body.dark-mode .rcx-span,
body.dark-mode .rcx-row-swatch {
    background-color: var(--slate-400, #94a3b8);
}
body.dark-mode .rcx-overlap {
    background: var(--cmx-8438616126);
    background: color-mix(in srgb, var(--rc-conflict-red, #dc2626) 32%, transparent);
    border-left-color: rgba(248, 113, 113, 0.9);
    border-right-color: rgba(248, 113, 113, 0.9);
}
body.dark-mode .rcx-tick {
    color: var(--slate-400);
}
body.dark-mode .rcx-row-attendees {
    color: var(--slate-400);
}

body.dark-mode .conflict-action-btn.reschedule-btn,
body.dark-mode .conflict-action-btn.decline-conflict-btn,
body.dark-mode .conflict-action-btn.cancel-conflict-btn {
    background: var(--slate-800);
    color: var(--slate-300);
    border-color: var(--slate-700);
}

body.dark-mode .conflict-action-btn.reschedule-btn:hover {
    background: rgba(59, 130, 246, 0.18);
    color: #93c5fd;
    border-color: rgba(59, 130, 246, 0.4);
}

body.dark-mode .conflict-action-btn.decline-conflict-btn:hover,
body.dark-mode .conflict-action-btn.cancel-conflict-btn:hover {
    background: rgba(248, 113, 113, 0.18);
    color: #fca5a5;
    border-color: rgba(248, 113, 113, 0.4);
}

/* (The full-width CONFLICT PROGRESS band, "Conflict N of M" text + bar +
   stats, was retired in the 2026-08-15 redesign. Its information lives in
   .conflicts-header-count on the single header row.) */

/* Conflict completion state */
.conflicts-complete {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 48px 24px;
    text-align: center;
}

.conflicts-complete i {
    font-size: 3rem;
    color: var(--primary-500);
    animation: successPop 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.conflicts-complete span {
    font-size: 0.9375rem;
    font-weight: 500;
    color: var(--slate-600);
}

@keyframes successPop {
    0% { transform: scale(0); }
    50% { transform: scale(1.1); }
    100% { transform: scale(1); }
}

/* ============================================
   CONFLICT RESOLUTION ANIMATIONS
   ============================================ */

/* Resolution overlay appears on decline — red/declined style */
/* Confirmation that the action landed. Was a 93%-opaque red sheet across the
   whole card — an alarm-coloured takeover for what is usually a routine,
   expected choice, and louder than anything else in the Review Center (a
   .review-item confirms with a small inline strip). Toned to a translucent
   wash so the card underneath stays readable while it slides out: the user
   should be able to see WHAT they just resolved, not have it blanked out. */
.conflict-item .resolution-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    background: rgba(254, 242, 242, 0.92);
    color: #b91c1c;
    font-size: var(--rc-font-body, 13px);
    font-weight: 600;
    border-radius: inherit;
    box-shadow: inset 0 0 0 1px rgba(220, 38, 38, 0.25);
    z-index: 10;
    animation: overlayIn 200ms ease-out;
}

.conflict-item .resolution-overlay i {
    font-size: 1.25rem;
}

.conflict-item .resolution-overlay span {
    max-width: 260px;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
}

/* Dim the declined event side in a conflict pair for clarity */
.conflict-event.declined-side {
    opacity: 0.3;
    filter: grayscale(0.5);
    transition: opacity 200ms ease, filter 200ms ease;
}

@keyframes overlayIn {
    from { opacity: 0; transform: scale(0.95); }
    to { opacity: 1; transform: scale(1); }
}

/* Card slides out after resolution */
.conflict-item.resolved {
    animation: slideOutLeft 400ms ease-out forwards;
    pointer-events: none;
}

@keyframes slideOutLeft {
    0% { transform: translateX(0); opacity: 1; }
    100% {
        transform: translateX(-100%);
        opacity: 0;
        height: 0;
        margin: 0;
        padding: 0;
        border-width: 0;
    }
}

/* Next card entrance animation */
.conflict-item.entering {
    animation: slideInRight 300ms ease-out;
}

/* Highlight pulse plays briefly when arriving from a conflict badge.
   The animation is the entire effect — no persistent box-shadow on the
   base rule, so the card returns to its normal Review chrome once the
   pulse finishes. (Previously the rule baked in a permanent red 2px
   ring, which made the panel look like a fire alarm.) */
.conflict-item.conflict-highlight {
    animation: conflictPulse 1.5s ease-out;
}

@keyframes conflictPulse {
    0%   { box-shadow: 0 0 0 4px rgba(220, 38, 38, 0.55), 0 2px 16px rgba(220, 38, 38, 0.35); }
    60%  { box-shadow: 0 0 0 2px rgba(220, 38, 38, 0.35), 0 2px 10px rgba(220, 38, 38, 0.2); }
    100% { box-shadow: 0 0 0 0 rgba(220, 38, 38, 0), 0 0 0 rgba(220, 38, 38, 0); }
}

@keyframes slideInRight {
    from { transform: translateX(30px); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

body.dark-mode .conflicts-complete span {
    color: var(--slate-300);
}

body.dark-mode .conflict-item .resolution-overlay {
    /* Was rgba(20,184,166,.9) — TEAL. The same "Declined"/"Cancelled" overlay
       read as danger in light mode and as success in dark mode; a theme must
       not change what an action MEANS. Same red channel as light, darkened. */
    background: rgba(69, 10, 10, 0.92);
    color: #fca5a5;
    box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.3);
}

/* ============================================
   SUB-TABS (Awaiting / Actioned toggle)
   Matches Propose Modal pill-style tabs
   ============================================ */
.review-sub-tabs {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    /* 12px→8px (2026-07-12 densify): shaves a little vertical chrome above the
       card list without crowding the sub-tab pill. */
    margin-bottom: 8px;
    background: var(--slate-100);
    border-radius: var(--radius-full, 9999px);
}

.sub-tab {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 5px 14px;
    border-radius: var(--radius-full, 9999px);
    font-size: 0.75rem;
    font-weight: 500;
    background: transparent;
    border: none;
    color: var(--slate-500);
    cursor: pointer;
    transition: all 0.15s ease;
}

.sub-tab:hover:not(.active) {
    background: rgba(255, 255, 255, 0.5);
    color: var(--slate-700);
}

.sub-tab.active {
    background: white;
    color: var(--primary-600);
    font-weight: 600;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}

.sub-tab i {
    font-size: 0.65rem;
}

.sub-tab .sub-tab-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 5px;
    border-radius: 8px;
    font-size: 0.625rem;
    font-weight: 600;
    background: var(--slate-200);
    color: var(--slate-600);
}

.sub-tab.active .sub-tab-count {
    background: var(--primary-100);
    color: var(--primary-700);
}

/* "Accept all safe (N)" one-shot — the headline velocity affordance. Full-width
   and primary so it reads as "clear the boring ones," sitting above the list. */
.rc-accept-safe-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin: 4px 0 2px;
    padding: 11px 14px;
    min-height: 44px;
    font-size: 14px;
    font-weight: 600;
}
.rc-accept-safe-hint {
    margin: 0 0 12px;
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    text-align: center;
}
body.dark-mode .rc-accept-safe-hint { color: #94a3b8; }

/* Requests bulk-approve bar (2026-08-06). Same shape as the "Accept all safe"
   affordance above so the two bulk controls in the panel read as one idea:
   button centred, quiet explanatory hint beneath naming exactly what it acts
   on. */
.rc-requests-bulk {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 4px 0 12px;
}
.rc-requests-bulk-hint {
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    text-align: center;
    max-width: 44ch;
}
body.dark-mode .rc-requests-bulk-hint { color: #94a3b8; }

/* States what a capped organizational group is hiding, rather than truncating
   silently — a shortened list that says nothing reads as the whole list. */
.review-group-truncated {
    margin: 4px 12px 10px;
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    text-align: center;
}
body.dark-mode .review-group-truncated { color: #94a3b8; }

/* "N snoozed · Bring back" — a quiet row so snoozed items stay discoverable. */
.rc-snoozed-link {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    margin: 0 0 12px;
    padding: 8px 12px;
    min-height: 40px;
    background: transparent;
    border: 1px dashed var(--slate-300, #cbd5e1);
    border-radius: var(--radius-md, 8px);
    color: var(--text-secondary, #64748b);
    font-size: 13px;
    cursor: pointer;
}
.rc-snoozed-link:hover { background: var(--slate-50, #f8fafc); }
.rc-snoozed-link .rc-snoozed-action {
    margin-left: auto;
    font-weight: 600;
    color: var(--primary-600, #0d9488);
}
body.dark-mode .rc-snoozed-link { border-color: var(--slate-600, #475569); color: #94a3b8; }
body.dark-mode .rc-snoozed-link:hover { background: var(--slate-800, #1e293b); }

/* RSVP single-tap actions for 1-2-slot proposals — full-width stacked buttons,
   each commits on click (no rank step, no Submit). Replaces the ranked ballot's
   checkbox + Submit row when there are only one or two times to choose between. */
.rc-rsvp-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
    width: 100%;
}
.rc-rsvp-actions .rc-rsvp-yes,
.rc-rsvp-actions .rc-rsvp-no {
    width: 100%;
    justify-content: center;
    min-height: 44px;
}

/* "Deal with these first" — compact cross-source urgency strip at the top of the
   awaiting view. The merged-feed value (most-urgent across scheduled+proposals)
   without a 4th tab. Each row jumps to its source. */
.rc-urgent-strip {
    margin: 0 0 14px;
    padding: 10px 12px;
    background: var(--cmx-0243c1feaa);
    background: color-mix(in srgb, var(--primary-500, #14b8a6) 7%, var(--surface-primary, #fff));
    border: 1px solid var(--cmx-37a5f65dee);
    border: 1px solid color-mix(in srgb, var(--primary-500, #14b8a6) 22%, transparent);
    border-radius: var(--radius-md, 8px);
}
.rc-urgent-head {
    display: flex; align-items: center; gap: 6px;
    font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.3px;
    color: var(--primary-700, #0f766e);
    margin-bottom: 6px;
}
.rc-urgent-row {
    display: flex; align-items: center; gap: 8px;
    width: 100%;
    padding: 8px 6px;
    min-height: 40px;
    background: transparent; border: none;
    border-top: 1px solid var(--cmx-8f919dd39c);
    border-top: 1px solid color-mix(in srgb, var(--primary-500, #14b8a6) 12%, transparent);
    cursor: pointer;
    font-size: 13px;
    color: var(--text-primary, #0f172a);
    text-align: left;
}
.rc-urgent-row:first-of-type { border-top: none; }
.rc-urgent-row:hover { background: var(--cmx-832ceb870d); background: color-mix(in srgb, var(--primary-500, #14b8a6) 8%, transparent); }
.rc-urgent-glyph { color: var(--primary-600, #0d9488); font-size: 12px; flex-shrink: 0; }
.rc-urgent-title { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-urgent-due { flex-shrink: 0; font-weight: 600; color: var(--rc-status-warning, #b45309); font-size: 12px; }
.rc-urgent-row .fa-chevron-right { flex-shrink: 0; font-size: 11px; color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-urgent-strip { background: var(--cmx-edba9505bf); background: color-mix(in srgb, var(--primary-500, #14b8a6) 12%, var(--slate-800, #1e293b)); }
body.dark-mode .rc-urgent-row { color: var(--slate-200, #e2e8f0); }

/* The "Sent by me" header chip (.rdh-sent-chip / -count / -label / -empty, plus
   its dark-mode and ≤420px blocks) was REMOVED 2026-08-06 — about 55 lines
   styling markup no renderer emitted. "Sent by me" is the third sub-tab of
   Proposals (SUB_TABS.proposals.sent); its count rides the Proposals tab badge
   and the sub-tab's own count, so nothing is lost. */

/* "Back" link + view title for the demoted "Sent by me" view (no tab button
   is active there, so the user needs an explicit way back to the tabs). */
.rc-back-to-tabs {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 10px 6px 6px;
    min-height: 40px;
    margin-bottom: 4px;
    background: transparent;
    border: none;
    color: var(--slate-500, #64748b);
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    border-radius: var(--radius-md, 8px);
}
.rc-back-to-tabs:hover { background: var(--slate-100, #f1f5f9); color: var(--slate-700, #334155); }
.rc-view-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    font-size: 15px;
    font-weight: 600;
    color: var(--slate-700, #334155);
}
.rc-view-title i { color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-back-to-tabs { color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-back-to-tabs:hover { background: var(--slate-700, #334155); color: var(--slate-200, #e2e8f0); }
body.dark-mode .rc-view-title { color: var(--slate-200, #e2e8f0); }

/* Dark mode sub-tabs */
body.dark-mode .review-sub-tabs {
    background: var(--slate-800);
}

body.dark-mode .sub-tab {
    color: var(--slate-400);
}

body.dark-mode .sub-tab:hover:not(.active) {
    background: var(--slate-700);
    color: var(--slate-200);
}

body.dark-mode .sub-tab.active {
    background: var(--slate-700);
    color: var(--primary-400);
}

body.dark-mode .sub-tab .sub-tab-count {
    background: var(--slate-700);
    color: var(--slate-300);
}

body.dark-mode .sub-tab.active .sub-tab-count {
    background: rgba(20, 184, 166, 0.2);
    color: var(--primary-300);
}

/* ============================================
   REVIEW ITEMS
   ============================================ */
.review-items {
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Reset native ul/li styling — semantic list, no bullets/indent */
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Phase C (C4): use the width on large screens — the awaiting card list goes
   2-up. align-items:start keeps cards their natural height (no stretch-to-match
   ragged rows). If ANY card is expanded (settings/details open) the grid drops
   back to a single column so the open card gets full width — :has() guards
   against a cramped expansion.
   2026-07-12 full-modal redesign: breakpoint lowered 1200px→1040px so the 2-up
   density win kicks in on laptops now that the shell is full-bleed (up to
   min(1600px,96vw)), not just wide desktops. This is the single biggest lever
   for "more decisions per screen". */
@media (min-width: 1040px) {
    .review-items {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
        gap: 10px 14px;
    }
    /* .is-expanded is set beside the inline display (review-dashboard.js),
       never the style attribute: that restyles the page on every inline write. */
    .review-items:has(.review-item-settings-panel.is-expanded),
    .review-items:has(.rc-card-details.is-expanded) {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Conflicts go 2-up too, so entering the panel doesn't halve the number of
   decisions on screen — the one place where seeing MORE at once matters most,
   since the complaint is about handling many in one session. Held to a wider
   breakpoint than .review-items (1040px): each card carries a two-lane timeline
   plus an axis, which needs real width before it can be split in half.

   ⚠️THE BREAKPOINT MUST MEASURE THE CONTAINER, NOT THE VIEWPORT (2026-09-21,
   owner: "the cards are unneccessarily narrow compared to available space").
   These cards live inside the Review Center modal, which is
   `max-width: min(1600px, 96vw)` (see .review-modal-content) minus padding —
   so a 1280px VIEWPORT leaves only ~1062px of list. Splitting that in half
   gave each card 524px, of which the hour track got 159px: the three-column
   row spends 148px on the label and ~190px on the action buttons, so the
   diagram the card exists for was the SMALLEST part of it, at 1.3px/minute.
   Measured 2026-09-21: full width gives the track 696px at 5.8px/min — 4.4x
   the resolution of the overlap band.

   A container query asks the right question: split only when the list is
   genuinely wide enough, whatever the viewport is doing. 1180px is the
   measured floor at which each column still clears ~580px (the width where
   the track finally out-measures the label+actions chrome beside it).

   ⚠️A container query measures the container's CONTENT box, so this 1180px
   is .conflicts-panel's inner width — its border box flips at 1180 + 2*16px
   padding = 1212px. Verified live 2026-09-21: 1211px border box → one column,
   1212px → two. Do not "correct" this to 1212: the threshold that matters is
   the space the CARDS actually get, which is the content box.

   ⚠️The container is the PANEL, not the list: an element cannot be sized by
   a container query it declares itself (the query would depend on the result
   it produces). .conflicts-panel is .conflicts-list's parent and the same
   inline width minus its 16px padding, so it measures the same question. */
.conflicts-panel {
    container-type: inline-size;
    container-name: conflicts-panel;
}

@container conflicts-panel (min-width: 1180px) {
    .conflicts-list {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        align-items: start;
        gap: 10px 14px;
    }
}

/* Fallback for engines without container queries: keep the old viewport rule,
   but at the width where the MODAL (96vw, capped 1600px) can actually afford
   two ~580px columns rather than the width where the viewport can. */
@supports not (container-type: inline-size) {
    @media (min-width: 1480px) {
        .conflicts-list {
            display: grid;
            grid-template-columns: repeat(2, minmax(0, 1fr));
            align-items: start;
            gap: 10px 14px;
        }
    }
}

.review-item {
    background: var(--review-card-bg);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--review-card-border);
    border-radius: var(--rc-radius-card);
    padding: var(--rc-spacing-card);
    box-shadow: var(--review-card-shadow);
    transition: all 0.15s ease;
}

.review-item:hover {
    border-color: var(--primary-300);
    box-shadow: var(--shadow-md), 0 0 0 2px rgba(20, 184, 166, 0.08);
    transform: translateY(-1px);
}

.review-item.urgent {
    border-color: var(--review-urgent);
    border-left-width: 4px;
    background: linear-gradient(135deg, var(--review-urgent-light) 0%, var(--review-card-bg) 100%);
}

.review-item.urgent:hover {
    box-shadow: var(--shadow-md), 0 0 0 3px rgba(239, 68, 68, 0.15);
}

/* Item Header — compact title row */
.review-item-header {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 4px;
    flex-wrap: wrap;
}

/* Meta line for review items (dot-separated) */
.review-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    font-size: var(--rc-font-secondary);
    color: var(--slate-500);
    margin-bottom: 4px;
}

.review-meta-line > span {
    white-space: nowrap;
}

.review-meta-line > span:not(:last-child):not(.review-meta-right)::after {
    content: "\00B7";
    margin: 0 6px;
    color: var(--slate-300);
}

/* Right-aligned zone in review meta line (trust/quota/auto-accept badges) */
.review-meta-right {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    flex-shrink: 0;
}

body.dark-mode .review-meta-line {
    color: var(--slate-400);
}

body.dark-mode .review-meta-line > span:not(:last-child)::after {
    color: var(--slate-600);
}

/* Urgency Badges */
.urgency-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 7px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 500;
    border-radius: var(--radius-full);
    letter-spacing: 0.2px;
}

.urgency-badge.urgency-today {
    background: rgba(239, 68, 68, 0.08);
    color: var(--rc-status-busy);
    border: 1px solid rgba(239, 68, 68, 0.2);
}

.urgency-badge.urgency-tomorrow {
    background: rgba(245, 158, 11, 0.08);
    /* Darker amber for legibility on light background — see --rc-status-tentative
       comment in :root; future -strong token would replace this. */
    color: #b45309;
    border: 1px solid rgba(245, 158, 11, 0.2);
}

.urgency-badge.urgency-later {
    background: var(--slate-100);
    color: var(--slate-500);
    border: 1px solid var(--slate-200);
}

body.dark-mode .urgency-badge.urgency-today {
    background: linear-gradient(135deg, rgba(185, 28, 28, 0.3) 0%, rgba(220, 38, 38, 0.2) 100%);
    color: #fca5a5;
    border-color: rgba(248, 113, 113, 0.4);
}

body.dark-mode .urgency-badge.urgency-tomorrow {
    background: linear-gradient(135deg, rgba(194, 65, 12, 0.3) 0%, rgba(234, 88, 12, 0.2) 100%);
    color: #fdba74;
    border-color: rgba(251, 146, 60, 0.4);
}

body.dark-mode .urgency-badge.urgency-later {
    background: var(--slate-700);
    color: var(--slate-400);
    border-color: var(--slate-600);
}

/* Fairness chip on review-card title row.
   Echoes the Find Times .best-time-fairness-badge grammar — same icon
   (scale-balanced), same 5-band color ramp via --fv-l*, same Excellent/
   Balanced/Mixed/Stretched/Overloaded vocabulary. Sized smaller than
   the propose-modal version so it sits next to the urgency badge
   without dominating the card header. */
.rc-fairness-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 7px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 500;
    border-radius: var(--radius-full);
    letter-spacing: 0.2px;
    color: var(--rc-fairness-color, var(--slate-700));
    background: var(--cmx-807bac6820);
    background: color-mix(in srgb, var(--rc-fairness-color, var(--slate-500)) 10%, transparent);
    border: 1px solid var(--cmx-ea052bf3ba);
    border: 1px solid color-mix(in srgb, var(--rc-fairness-color, var(--slate-500)) 25%, transparent);
    white-space: nowrap;
}

.rc-fairness-chip i {
    font-size: 0.7em;
    opacity: 0.85;
}

/* Fallback for browsers without color-mix() — use the tint directly,
   which is darker but still respects the band's hue identity. */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
    .rc-fairness-chip {
        background: transparent;
        border-color: currentColor;
        opacity: 0.85;
    }
}

body.dark-mode .rc-fairness-chip {
    /* In dark mode, color-mix lightens the chip background just enough
       to stay readable on the slate-800 card surface. */
    background: var(--cmx-81f737dd87);
    background: color-mix(in srgb, var(--rc-fairness-color, var(--slate-400)) 15%, transparent);
    border-color: var(--cmx-4421c15b62);
    border-color: color-mix(in srgb, var(--rc-fairness-color, var(--slate-400)) 35%, transparent);
}

/* Review item border accents for urgency */
.review-item.urgency-today {
    border-left: 3px solid var(--rc-status-busy);
}

.review-item.urgency-tomorrow {
    /* Routed through the attention accent (Stage A) rather than tentative
       amber — original code chose orange deliberately for the "act soon"
       prompt; the token preserves intent. */
    border-left: 3px solid var(--rc-accent-attention-strong);
}

.status-select {
    padding: 4px 24px 4px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--slate-600);
    background: var(--slate-100);
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 10 10'%3E%3Cpath fill='%2364748b' d='M2.5 4L5 6.5L7.5 4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
}

body.dark-mode .status-select {
    color: var(--slate-300);
    background-color: var(--slate-700);
    border-color: var(--slate-600);
}

.review-item-title {
    font-size: var(--rc-font-heading);
    font-weight: 600;
    color: var(--slate-800);
    line-height: 1.3;
}

body.dark-mode .review-item-title {
    color: var(--slate-100);
}


.scheduled-by-prominent {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--slate-600);
}

.scheduled-by-prominent i {
    color: var(--primary-500);
    font-size: 13px;
}

.scheduled-by-prominent strong {
    color: var(--slate-800);
}

body.dark-mode .scheduled-by-prominent {
    color: var(--slate-400);
}

body.dark-mode .scheduled-by-prominent strong {
    color: var(--slate-100);
}

/* Trust Indicator */
.trust-indicator {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    font-size: 11px;
    font-weight: 500;
    border-radius: var(--radius-full);
}

.trust-indicator i {
    font-size: 10px;
}

.trust-label {
    font-weight: 600;
}

.trust-auto-accept {
    opacity: 0.8;
    font-weight: 400;
}

/* Trust level colors. green/amber reuse traffic-light tokens for visual
   consistency with the rest of the status surfaces; blue is categorical
   (organizational); slate is the unknown/default. */
.trust-indicator.trust-green {
    color: var(--rc-status-working);
    background: rgba(34, 197, 94, 0.12);
}

.trust-indicator.trust-blue {
    color: #2563eb;
    background: rgba(59, 130, 246, 0.12);
}

.trust-indicator.trust-amber {
    /* Darker amber for legibility — see urgency-tomorrow comment. */
    color: #d97706;
    background: rgba(245, 158, 11, 0.12);
}

.trust-indicator.trust-slate {
    color: var(--slate-600);
    background: rgba(100, 116, 139, 0.12);
}

body.dark-mode .trust-indicator.trust-green {
    color: #4ade80;
    background: rgba(34, 197, 94, 0.2);
}

body.dark-mode .trust-indicator.trust-blue {
    color: #60a5fa;
    background: rgba(59, 130, 246, 0.2);
}

body.dark-mode .trust-indicator.trust-amber {
    color: #fbbf24;
    background: rgba(245, 158, 11, 0.2);
}

body.dark-mode .trust-indicator.trust-slate {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.15);
}

/* Trust level styling on review item.
   organizational uses brand blue (categorical, not status); trusted/known
   reuse the available/tentative status tokens to read consistently with
   the rest of the traffic-light surfaces. */
.review-item.trust-level-organizational {
    border-left: 4px solid #3b82f6;
}

.review-item.trust-level-trusted {
    border-left: 4px solid var(--rc-status-available);
}

.review-item.trust-level-known {
    border-left: 4px solid var(--rc-status-tentative);
}

.review-item.trust-level-unknown {
    border-left: 4px solid var(--slate-400);
}

/* Quota Badge */
/* Quota summary strip — sits once above the awaiting list. */
.rc-quota-summary {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 8px;
}
.rc-quota-summary .quota-badge { margin-left: 0; }

.quota-badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    border-radius: var(--radius-full);
    margin-left: auto;
}

.quota-badge i {
    font-size: 10px;
}

.quota-badge.quota-green {
    color: var(--rc-status-working);
    background: rgba(34, 197, 94, 0.12);
}

.quota-badge.quota-amber {
    /* Darker amber for legibility — see urgency-tomorrow note. */
    color: #d97706;
    background: rgba(245, 158, 11, 0.12);
}

.quota-badge.quota-red {
    color: var(--rc-status-busy);
    background: rgba(239, 68, 68, 0.12);
}

body.dark-mode .quota-badge.quota-green {
    color: #4ade80;
    background: rgba(34, 197, 94, 0.2);
}

body.dark-mode .quota-badge.quota-amber {
    color: #fbbf24;
    background: rgba(245, 158, 11, 0.2);
}

body.dark-mode .quota-badge.quota-red {
    color: #f87171;
    background: rgba(239, 68, 68, 0.2);
}

/* Quota Over State */
.quota-badge.quota-over {
    font-weight: 700;
}

/* Pending Hours Hint */
/* Muted "Schedulable:" qualifier so "Xh available" can't be read as the
   app's separate free-to-meet availability concept. */
.quota-badge-label {
    font-weight: 500;
    opacity: 0.7;
    margin-right: 1px;
}

.quota-pending-hint {
    font-size: 10px;
    font-weight: 500;
    opacity: 0.7;
    margin-left: 2px;
}

/* Enhanced Tooltip Styling */
.quota-badge[title] {
    cursor: help;
    position: relative;
}

.quota-badge[title]:hover::after {
    content: attr(title);
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    padding: 8px 12px;
    background: var(--slate-900);
    color: white;
    font-size: 11px;
    font-weight: 500;
    border-radius: var(--radius-md);
    white-space: nowrap;
    z-index: var(--z-dropdown);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
    pointer-events: none;
}

.quota-badge[title]:hover::before {
    content: '';
    position: absolute;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    border-top-color: var(--slate-900);
    z-index: var(--z-dropdown);
    pointer-events: none;
}

body.dark-mode .quota-badge[title]:hover::after {
    background: var(--slate-100);
    color: var(--slate-900);
}

body.dark-mode .quota-badge[title]:hover::before {
    border-top-color: var(--slate-100);
}

/* Status Badge */
.review-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 600;
    border-radius: var(--radius-full);
    white-space: nowrap;
}

.review-status-badge.in-review {
    color: var(--review-warning);
    background: var(--review-warning-light);
}

.review-status-badge.in-review::before {
    content: '';
    width: 8px;
    height: 8px;
    background: var(--review-warning);
    border-radius: 50%;
}

.review-status-badge.accepted {
    color: var(--review-success);
    background: var(--review-success-light);
}

.review-status-badge.accepted::before {
    content: '\f00c';
    font-family: 'Font Awesome 6 Free';
    font-weight: 900;
    font-size: 10px;
}

@keyframes statusPulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}


.review-date {
    font-weight: 500;
}

.review-time {
    color: var(--slate-500);
}

.review-event-type {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 10px;
    font-weight: 600;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    text-transform: capitalize;
}

.review-type-work {
    background: rgba(59, 130, 246, 0.1);
    color: #2563eb;
    border: 1px solid rgba(59, 130, 246, 0.25);
}

.review-type-personal {
    background: rgba(244, 114, 182, 0.1);
    color: #db2777;
    border: 1px solid rgba(244, 114, 182, 0.25);
}

/* Quota-assigned shift items use the shift palette (amber) so they're
   distinguishable from generic work meetings in the inbox. */
.review-type-shift {
    background: rgba(245, 158, 11, 0.12);
    color: #b45309;
    border: 1px solid rgba(245, 158, 11, 0.3);
}
.review-item:has(.review-type-shift) {
    border-left: 3px solid rgba(245, 158, 11, 0.55);
}

/* Time-off proposals from the workbench (eventType: 'leave'). Teal so
   they're visually distinct from shift items, which they'll often sit
   next to in the inbox. */
.review-type-leave {
    background: rgba(20, 184, 166, 0.12);
    color: #0f766e;
    border: 1px solid rgba(20, 184, 166, 0.3);
}
.review-item:has(.review-type-leave) {
    border-left: 3px solid rgba(20, 184, 166, 0.55);
}

.review-duration {
    font-size: 11px;
    color: var(--slate-400);
    display: inline-flex;
    align-items: center;
    gap: 3px;
}

body.dark-mode .review-type-work {
    background: rgba(59, 130, 246, 0.2);
    color: #60a5fa;
    border-color: rgba(59, 130, 246, 0.35);
}

body.dark-mode .review-type-personal {
    background: rgba(244, 114, 182, 0.2);
    color: #f9a8d4;
    border-color: rgba(244, 114, 182, 0.35);
}

body.dark-mode .review-type-shift {
    background: rgba(245, 158, 11, 0.22);
    color: #fbbf24;
    border-color: rgba(245, 158, 11, 0.4);
}

body.dark-mode .review-type-leave {
    background: rgba(20, 184, 166, 0.22);
    color: #5eead4;
    border-color: rgba(20, 184, 166, 0.4);
}

body.dark-mode .review-duration {
    color: var(--slate-500);
}

.review-time::before {
    content: '·';
    margin-right: 8px;
}

/* Item Meta */
.review-item-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    font-size: 13px;
}

.scheduled-by {
    color: var(--slate-500);
}

.scheduled-by::before {
    content: '\f007';
    font-family: 'Font Awesome 6 Free';
    font-weight: 900;
    margin-right: 6px;
    font-size: 11px;
}

body.dark-mode .scheduled-by {
    color: var(--slate-400);
}

/* Auto-accept Timer */
.auto-accept-timer {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-600);
    background: var(--slate-100);
    border-radius: var(--radius-full);
}

.auto-accept-timer.urgent {
    color: var(--review-urgent);
    background: var(--review-urgent-light);
    font-weight: 600;
}

body.dark-mode .auto-accept-timer {
    color: var(--slate-300);
    background: var(--slate-700);
}

/* Review Note */
.review-note {
    padding: 6px 10px;
    margin-bottom: var(--rc-spacing-section);
    font-size: var(--rc-font-secondary);
    color: var(--slate-600);
    background: var(--slate-50);
    border-radius: var(--rc-radius-btn);
    border-left: 3px solid var(--primary-400);
}

.review-note .note-label {
    font-weight: 600;
    color: var(--slate-700);
}

body.dark-mode .review-note {
    color: var(--slate-300);
    background: rgba(255, 255, 255, 0.05);
}

body.dark-mode .review-note .note-label {
    color: var(--slate-200);
}

/* ============================================
   SETTINGS TOGGLE & PANEL
   ============================================ */
.settings-toggle-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    width: 100%;
    padding: 5px 10px;
    margin-bottom: 4px;
    font-size: var(--rc-font-caption);
    font-weight: 600;
    color: var(--slate-500);
    background: var(--slate-50);
    border: 1px solid var(--slate-200);
    border-radius: var(--rc-radius-btn);
    cursor: pointer;
    transition: all 0.15s ease;
}

.settings-toggle-btn:hover {
    color: var(--primary-700);
    background: var(--primary-50);
    border-color: var(--primary-300);
}

.settings-toggle-btn i:first-child {
    font-size: var(--rc-font-caption);
    color: var(--slate-400);
    transition: color 0.15s ease;
}

.settings-toggle-btn:hover i:first-child {
    color: var(--primary-500);
}

.settings-chevron {
    margin-left: auto;
    font-size: 12px;
    transition: transform var(--transition-base);
}

.settings-chevron.expanded {
    transform: rotate(180deg);
}

body.dark-mode .settings-toggle-btn {
    color: var(--slate-300);
    background: linear-gradient(135deg, var(--slate-800) 0%, var(--slate-700) 100%);
    border-color: var(--slate-600);
}

body.dark-mode .settings-toggle-btn:hover {
    color: var(--primary-300);
    background: linear-gradient(135deg, rgba(20, 184, 166, 0.1) 0%, var(--slate-700) 100%);
    border-color: var(--primary-500);
}

/* Settings Panel */
.review-item-settings-panel {
    padding: 20px;
    margin-bottom: 16px;
    background: linear-gradient(135deg, var(--slate-50) 0%, white 100%);
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-lg);
    animation: slideDown 0.2s ease-out;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.settings-section {
    padding: 16px 0;
    border-bottom: 1px solid var(--slate-100);
}

.settings-section:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.settings-section:first-child {
    padding-top: 0;
}

.settings-label {
    display: block;
    font-size: 12px;
    font-weight: 700;
    color: var(--slate-500);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 12px;
}

/* A teammate-scheduled event: the sharing decision belongs to the scheduler,
   so the card says so instead of offering a control that persists nowhere. */
.rc-vis-owned-elsewhere .review-section-hint {
    margin: 0;
    font-size: 13px;
    line-height: 1.4;
    color: var(--slate-600);
}
body.dark-mode .rc-vis-owned-elsewhere .review-section-hint {
    color: var(--slate-300);
}

body.dark-mode .review-item-settings-panel {
    background: linear-gradient(135deg, var(--slate-800) 0%, var(--slate-700) 100%);
    border-color: var(--slate-600);
}

body.dark-mode .settings-section {
    border-color: var(--slate-600);
}

body.dark-mode .settings-label {
    color: var(--slate-400);
}

/* ============================================
   VISIBILITY OPTIONS
   ============================================ */
/* SEGMENTED, like the synced rows (2026-08-16, user: "Clicking the options
   could be better designed"). Three detached outlined buttons separated by a
   10px gap read as three independent toggles; this is ONE question with three
   answers, so it gets one track. Excludes .rc-vis-apply-btn, which also
   carries .visibility-btn but is a CONFIRM, not a choice. */
.visibility-options {
    display: flex;
    gap: 0;
    /* NO WRAP (2026-08-16, measured on the live render): the inherited
       flex-wrap:wrap let the three segments overlap each other by 81px at
       390px, because each kept its natural width inside a narrower track.
       A segmented control is one row by definition — let the segments SHARE
       the width instead of overflowing it. */
    flex-wrap: nowrap;
    width: 100%;
    padding: 2px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
    background: var(--slate-100, #f1f5f9);
}
.visibility-options .visibility-btn:not(.rc-vis-apply-btn) {
    /* Equal shares of the track, and allowed to shrink below content width. */
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    white-space: nowrap;
    border-color: transparent;
    background: transparent;
    border-radius: 6px;
    font-weight: 500;
}
.visibility-options .visibility-btn:not(.rc-vis-apply-btn) + .visibility-btn:not(.rc-vis-apply-btn) {
    box-shadow: inset 1px 0 0 var(--slate-200, #e2e8f0);
}
/* Drop the divider on the segment AFTER the selected/hovered one, and on the
   selected one ITSELF. box-shadow does not stack across rules — the later or
   more specific declaration REPLACES it — so without this the adjacent-sibling
   divider above out-specified the active rule and the selected segment lost
   its lift entirely (measured on the live render: inset divider only, no
   elevation). Suppress the divider here so the lift below is what survives. */
.visibility-options .visibility-btn.active + .visibility-btn:not(.rc-vis-apply-btn),
.visibility-options .visibility-btn:hover + .visibility-btn:not(.rc-vis-apply-btn),
.visibility-options .visibility-btn.active:not(.rc-vis-apply-btn) + .visibility-btn:not(.rc-vis-apply-btn) {
    box-shadow: none;
}
.visibility-options .visibility-btn:not(.rc-vis-apply-btn):hover {
    background: rgba(255, 255, 255, 0.6);
}
/* Selected lifts on a white card and keeps its level hue in the text/icon,
   rather than flooding the row with a saturated teal fill.
   `:not(.rc-vis-apply-btn):not(:disabled)` is doing SPECIFICITY work, not
   filtering: the adjacent-sibling divider rule above is a compound selector
   and would otherwise replace this box-shadow, leaving the selection with no
   elevation at all. */
.visibility-options .visibility-btn.active:not(.rc-vis-apply-btn):not(:disabled) {
    background: var(--bg-primary, #fff);
    border-color: var(--border-color, #e2e8f0);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
    font-weight: 600;
}
.visibility-options .visibility-btn.active[data-visibility="hidden"] { color: #7c3aed; }
.visibility-options .visibility-btn.active[data-visibility="busy"] { color: #b45309; }
.visibility-options .visibility-btn.active[data-visibility="full"] { color: var(--rc-status-working); }
.visibility-options .visibility-btn.active:not(.rc-vis-apply-btn) i { color: inherit; }

body.dark-mode .visibility-options {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
body.dark-mode .visibility-options .visibility-btn.active:not(.rc-vis-apply-btn) {
    background: var(--slate-700, #334155);
    border-color: var(--slate-600, #475569);
}

.visibility-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-600);
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.visibility-btn:hover {
    color: var(--slate-800);
    background: var(--slate-50);
    border-color: var(--slate-300);
}

.visibility-btn.active {
    color: white;
    background: var(--primary-500);
    border-color: transparent;
    box-shadow: 0 2px 8px rgba(20, 184, 166, 0.3);
}

.visibility-btn i {
    font-size: 11px;
}

/* Visibility level colours (2026-08-04): Purple=Private, Amber=Busy,
   Green=Shared — the app-wide privacy scale from privacy-visual-config.js
   (purple = private/protective, matching the day-panel pill and board
   presets). Replaces the old traffic-light red for "hidden", which read as
   error/danger here while every other surface painted the level purple. */
.visibility-btn[data-visibility="hidden"] i {
    color: var(--visibility-private, #8b5cf6);
}

.visibility-btn[data-visibility="busy"] i {
    color: var(--rc-status-tentative);
}

.visibility-btn[data-visibility="full"] i {
    color: var(--rc-status-available);
}

/* Hidden/Private - Purple */
.visibility-btn[data-visibility="hidden"]:hover {
    color: #7c3aed;
    background: rgba(139, 92, 246, 0.08);
    border-color: rgba(139, 92, 246, 0.3);
}

.visibility-btn[data-visibility="hidden"].active {
    color: white;
    background: var(--visibility-private, #8b5cf6);
    border-color: transparent;
    box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
}

/* Busy - Amber */
.visibility-btn[data-visibility="busy"]:hover {
    /* Darker amber for legibility on tinted background — see urgency-tomorrow note. */
    color: #d97706;
    background: rgba(245, 158, 11, 0.08);
    border-color: rgba(245, 158, 11, 0.3);
}

.visibility-btn[data-visibility="busy"].active {
    color: white;
    background: var(--rc-status-tentative);
    border-color: transparent;
    box-shadow: 0 2px 8px rgba(245, 158, 11, 0.3);
}

/* Full/Open - Green */
.visibility-btn[data-visibility="full"]:hover {
    color: var(--rc-status-working);
    background: rgba(34, 197, 94, 0.08);
    border-color: rgba(34, 197, 94, 0.3);
}

.visibility-btn[data-visibility="full"].active {
    color: white;
    background: var(--rc-status-available);
    border-color: transparent;
    box-shadow: 0 2px 8px rgba(34, 197, 94, 0.3);
}

body.dark-mode .visibility-btn {
    color: var(--slate-300);
    background: var(--slate-700);
    border-color: var(--slate-600);
}

body.dark-mode .visibility-btn:hover {
    color: var(--slate-100);
    background: var(--slate-600);
}

/* Dark mode visibility colours (purple / amber / green) */
body.dark-mode .visibility-btn[data-visibility="hidden"] i {
    color: #a78bfa;
}

body.dark-mode .visibility-btn[data-visibility="busy"] i {
    color: #fbbf24;
}

body.dark-mode .visibility-btn[data-visibility="full"] i {
    color: #4ade80;
}

body.dark-mode .visibility-btn[data-visibility="hidden"]:hover {
    color: #a78bfa;
    background: rgba(139, 92, 246, 0.15);
    border-color: rgba(167, 139, 250, 0.4);
}

body.dark-mode .visibility-btn[data-visibility="hidden"].active {
    color: white;
    background: var(--visibility-private, #8b5cf6);
}

body.dark-mode .visibility-btn[data-visibility="busy"]:hover {
    color: #fbbf24;
    background: rgba(245, 158, 11, 0.15);
    border-color: rgba(245, 158, 11, 0.4);
}

body.dark-mode .visibility-btn[data-visibility="busy"].active {
    color: white;
    background: var(--rc-status-tentative);
}

body.dark-mode .visibility-btn[data-visibility="full"]:hover {
    color: #4ade80;
    background: rgba(34, 197, 94, 0.15);
    border-color: rgba(34, 197, 94, 0.4);
}

body.dark-mode .visibility-btn[data-visibility="full"].active {
    color: white;
    background: var(--rc-status-available);
}

/* ============================================
   AUTO-ACCEPT CONFIG
   ============================================ */
.auto-accept-config {
    display: flex;
    align-items: center;
    gap: 24px;
    flex-wrap: wrap;
}

/* Premium Toggle Switch */
.toggle-switch {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
}

.toggle-switch input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: relative;
    width: 44px;
    height: 24px;
    background: var(--slate-200);
    border-radius: var(--radius-full);
    transition: all var(--transition-fast);
}

.toggle-slider::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 18px;
    height: 18px;
    background: white;
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
    transition: transform var(--transition-fast);
}

.toggle-switch input[type="checkbox"]:checked + .toggle-slider {
    background: var(--primary-500);
}

.toggle-switch input[type="checkbox"]:checked + .toggle-slider::after {
    transform: translateX(20px);
}

.toggle-switch input[type="checkbox"]:focus + .toggle-slider {
    box-shadow: var(--focus-ring-shadow);
}

.toggle-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--slate-700);
}

body.dark-mode .toggle-slider {
    background: var(--slate-600);
}

body.dark-mode .toggle-slider::after {
    background: var(--slate-200);
}

body.dark-mode .toggle-label {
    color: var(--slate-200);
}

/* Period Selector */
.auto-accept-period {
    display: flex;
    align-items: center;
    gap: 10px;
    transition: opacity var(--transition-fast);
}

.auto-accept-period.disabled {
    opacity: 0.4;
    pointer-events: none;
}

.auto-accept-period label {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--slate-600);
}

.auto-accept-period-select {
    padding: 6px 28px 6px 10px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-700);
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M3 4.5L6 7.5L9 4.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    transition: all var(--transition-fast);
}

.auto-accept-period-select:hover {
    border-color: var(--slate-300);
}

.auto-accept-period-select:focus {
    outline: none;
    border-color: var(--primary-400);
    box-shadow: var(--focus-ring-shadow);
}

body.dark-mode .auto-accept-period label {
    color: var(--slate-400);
}

body.dark-mode .auto-accept-period-select {
    color: var(--slate-200);
    background-color: var(--slate-700);
    border-color: var(--slate-600);
}

/* ============================================
   PERMISSION CONFIG
   ============================================ */
.permission-config {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.permission-level-select {
    padding: 6px 30px 6px 10px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-700);
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: 6px;
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M3 4.5L6 7.5L9 4.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    min-width: 180px;
    transition: all var(--transition-fast);
}

.permission-level-select:hover {
    border-color: var(--slate-300);
}

.permission-level-select:focus {
    outline: none;
    border-color: var(--primary-400);
    box-shadow: var(--focus-ring-shadow);
}

body.dark-mode .permission-level-select {
    color: var(--slate-200);
    background-color: var(--slate-700);
    border-color: var(--slate-600);
}

/* Revoke Access Button */
.revoke-access-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 6px 14px;
    font-size: 12px;
    font-weight: 500;
    color: var(--review-urgent);
    background: var(--review-urgent-light);
    border: 1px solid rgba(239, 68, 68, 0.2);
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
}

.revoke-access-btn:hover {
    color: white;
    background: var(--review-urgent);
    border-color: var(--review-urgent);
}

.revoke-access-btn i {
    font-size: 11px;
}

body.dark-mode .revoke-access-btn {
    background: rgba(239, 68, 68, 0.15);
    border-color: rgba(239, 68, 68, 0.3);
}

/* ============================================
   ACTION BUTTONS & INPUT
   ============================================ */
.review-item-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.review-note-input {
    width: 100%;
    padding: 6px 10px;
    font-size: 12px;
    font-family: inherit;
    color: var(--slate-700);
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    transition: all var(--transition-fast);
}

.review-note-input::placeholder {
    color: var(--slate-400);
}

.review-note-input:focus {
    outline: none;
    border-color: var(--primary-400);
    box-shadow: var(--focus-ring-shadow);
}

body.dark-mode .review-note-input {
    color: var(--slate-200);
    background: var(--slate-700);
    border-color: var(--slate-600);
}

body.dark-mode .review-note-input::placeholder {
    color: var(--slate-500);
}

/* Action buttons row (trust-based) */
.review-actions-row {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    flex-wrap: wrap;
    /* 10→6px (2026-07-12 densify): the card body's own gap already separates the
       actions from the status line; the extra 4px just grew every card. */
    margin-top: 6px;
}

/* .review-actions-row .btn sizing now inherits from .review-panel .btn reset */

.review-actions-row .btn i {
    margin-right: 4px;
    font-size: 11px;
}

/* Accepted (actioned) card: a read-only status + Undo, left-aligned so it
   reads as a state rather than a fresh decision. */
.review-actions-actioned {
    justify-content: space-between;
}
.rc-actioned-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--rc-status-available, #16a34a);
}
.rc-actioned-badge i { font-size: 12px; }
body.dark-mode .rc-actioned-badge { color: #4ade80; }
/* "Not answered, meeting passed" (2026-09-24): history, not a success. Neutral
   slate, not the green of Accepted. */
.rc-actioned-badge.rc-missed-badge { color: var(--text-secondary, #64748b); }
body.dark-mode .rc-actioned-badge.rc-missed-badge { color: var(--slate-400, #94a3b8); }
/* Tentative accepts read amber (the tentative status hue), not green. */
.rc-actioned-badge.rc-actioned-tentative { color: var(--rc-status-tentative, #f59e0b); }
body.dark-mode .rc-actioned-badge.rc-actioned-tentative { color: #fbbf24; }

/* Danger outline button for Decline */
.btn.btn-danger-outline {
    color: var(--rc-status-busy);
    background: transparent;
    border: 1px solid rgba(239, 68, 68, 0.3);
}

.btn.btn-danger-outline:hover {
    color: white;
    background: var(--rc-status-busy);
    border-color: var(--rc-status-busy);
}

body.dark-mode .btn.btn-danger-outline {
    color: #f87171;
    border-color: rgba(248, 113, 113, 0.3);
}

body.dark-mode .btn.btn-danger-outline:hover {
    color: white;
    background: #ef4444;
}

/* Icon button for more options */
.btn.btn-icon {
    width: 36px;
    height: 36px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--slate-100);
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    color: var(--slate-500);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.btn.btn-icon:hover {
    background: var(--slate-200);
    color: var(--slate-700);
}

body.dark-mode .btn.btn-icon {
    background: var(--slate-700);
    border-color: var(--slate-600);
    color: var(--slate-400);
}

body.dark-mode .btn.btn-icon:hover {
    background: var(--slate-600);
    color: var(--slate-200);
}

/* More options dropdown */
.more-options-dropdown {
    position: relative;
    z-index: 10;
}

.dropdown-menu {
    position: absolute;
    bottom: 100%;
    right: 0;
    margin-bottom: 4px;
    min-width: 200px;
    padding: 6px;
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 100;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    /* Explicit props, not `all`: animating `all` on a visibility-toggled
       element animates `visibility` and can leave the menu lingering /
       flickering (the presets-visibility-desync trap). */
    transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
}

.dropdown-menu.show {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

body.dark-mode .dropdown-menu {
    background: var(--slate-800);
    border-color: var(--slate-700);
}

.dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 12px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-700);
    background: transparent;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    text-align: left;
    transition: all 0.15s ease;
}

.dropdown-item:hover {
    background: var(--slate-100);
    color: var(--slate-900);
}

.dropdown-item i {
    width: 14px;
    font-size: 11px;
    color: var(--slate-500);
}

body.dark-mode .dropdown-item {
    color: var(--slate-300);
}

body.dark-mode .dropdown-item:hover {
    background: var(--slate-700);
    color: var(--slate-100);
}

body.dark-mode .dropdown-item i {
    color: var(--slate-400);
}

/* Legacy action buttons */
.action-buttons {
    display: flex;
    gap: 6px;
}

.action-buttons .btn {
    padding: 6px 14px;
    font-size: 12px;
    font-weight: 500;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}

.action-buttons .btn-primary {
    color: white;
    background: var(--primary-500);
    border: none;
}

.action-buttons .btn-primary:hover {
    background: var(--primary-600);
}

.action-buttons .btn i {
    margin-right: 4px;
    font-size: 11px;
}

.action-buttons .btn-secondary {
    color: var(--slate-600);
    background: white;
    border: 1px solid var(--slate-200);
}

.action-buttons .btn-secondary:hover {
    color: var(--slate-800);
    background: var(--slate-50);
    border-color: var(--slate-300);
}

body.dark-mode .action-buttons .btn-secondary {
    color: var(--slate-300);
    background: var(--slate-700);
    border-color: var(--slate-600);
}

body.dark-mode .action-buttons .btn-secondary:hover {
    color: var(--slate-100);
    background: var(--slate-600);
}

/* Review Hint */
.review-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 12px;
    font-size: 12px;
    color: var(--slate-400);
    font-style: italic;
}

.review-hint i {
    color: var(--slate-300);
    font-size: 11px;
}

body.dark-mode .review-hint {
    color: var(--slate-500);
}

body.dark-mode .review-hint i {
    color: var(--slate-500);
}

/* ============================================
   REVIEW GROUP (Accept All)
   ============================================ */
.review-group {
    margin-bottom: 24px;
}

.review-group-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    margin-bottom: 12px;
    background: rgba(20, 184, 166, 0.05);
    border: 1px solid var(--primary-200);
    border-radius: var(--radius-md);
}

.group-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.group-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--slate-800);
}

.group-count {
    font-size: 11px;
    color: var(--slate-500);
}

.accept-all-btn {
    padding: 6px 14px;
    font-size: 12px;
    font-weight: 600;
    color: white;
    background: var(--primary-500);
    border: none;
    border-radius: 6px;
    cursor: pointer;
    transition: all 0.15s ease;
}

.accept-all-btn:hover {
    background: var(--primary-600);
}

.accept-all-btn i {
    margin-right: 4px;
}

.group-name i {
    color: var(--primary-500);
    margin-right: 6px;
}

body.dark-mode .review-group-header {
    background: linear-gradient(135deg, rgba(20, 184, 166, 0.15) 0%, var(--slate-800) 100%);
    border-color: var(--primary-600);
}

body.dark-mode .group-name {
    color: var(--slate-100);
}

body.dark-mode .group-count {
    color: var(--slate-400);
}

/* ============================================
   EMPTY STATES
   ============================================ */
.review-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 36px 20px;
    text-align: center;
}

/* ⭐ 2026-09-04 — A FAILED READ MUST NOT LOOK LIKE AN EMPTY QUEUE.
   Three sites render `.review-empty .review-error` for a genuine failure: the
   join-requests service missing, its fetch failing, and the privacy queue read
   throwing. Each writes honest copy ("This is a connection problem, not an
   empty queue — nobody has been turned away") — but `.review-error` was never
   defined in any stylesheet, so all three inherited the muted grey of "No
   pending requests" and read as "nobody is waiting". On the Requests tab that
   means an admin skims past people actually waiting to join; on Privacy it
   means a failed read of the queue looks like a clean queue. Carry the
   distinction visually, at the same weight as the copy already claims. */
.review-empty.review-error {
    border: 1px solid var(--warning-300, #fcd34d);
    border-radius: 10px;
    background: var(--warning-50, #fffbeb);
    color: var(--warning-900, #78350f);
}
.review-empty.review-error .empty-icon i { color: var(--warning-600, #d97706); }
.review-error-detail {
    margin-top: 6px;
    font-size: 12px;
    opacity: 0.85;
    max-width: 40ch;
}
body.dark-mode .review-empty.review-error {
    border-color: var(--warning-700, #b45309);
    background: rgba(180, 83, 9, 0.12);
    color: var(--warning-100, #fef3c7);
}

/* Retry button in the per-tab error state (was inline-styled in the JS). */
.review-retry-btn {
    margin-top: 8px;
    padding: 6px 16px;
    border-radius: 6px;
    border: 1px solid var(--border-color, #e2e8f0);
    background: var(--bg-secondary, #f8fafc);
    color: var(--text-primary, #334155);
    font-weight: 600;
    cursor: pointer;
}
.review-retry-btn:hover { background: var(--slate-200, #e2e8f0); }
body.dark-mode .review-retry-btn {
    border-color: var(--slate-700, #334155);
    background: var(--slate-800, #1e293b);
    color: var(--slate-200, #e2e8f0);
}

.review-empty .empty-icon {
    font-size: 36px;
    margin-bottom: 12px;
    opacity: 0.5;
}

.review-empty .empty-icon i {
    color: var(--primary-400);
}

.review-empty p {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--slate-500);
}

body.dark-mode .review-empty p {
    color: var(--slate-400);
}

/* ============================================
   PROPOSALS TAB
   ============================================ */

/* All caught up message */
.proposals-all-voted {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 16px;
    margin-bottom: 14px;
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.03) 100%);
    border: 1px solid rgba(16, 185, 129, 0.2);
    border-radius: var(--radius-lg);
    text-align: center;
}

.all-voted-icon {
    font-size: 28px;
    color: var(--rc-status-available);
    margin-bottom: 8px;
}

.all-voted-text {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--slate-800);
    margin: 0 0 2px 0;
}

.all-voted-subtext {
    font-size: 0.75rem;
    color: var(--slate-500);
    margin: 0;
}

body.dark-mode .proposals-all-voted {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(16, 185, 129, 0.08) 100%);
    border-color: rgba(16, 185, 129, 0.3);
}

body.dark-mode .all-voted-icon {
    color: #34d399;
}

body.dark-mode .all-voted-text {
    color: var(--slate-100);
}

body.dark-mode .all-voted-subtext {
    color: var(--slate-400);
}

/* Proposals Sections (Needs Vote / Already Voted) */
.proposals-section {
    margin-bottom: 24px;
}

.proposals-section:last-child {
    margin-bottom: 0;
}

.proposals-section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    margin-bottom: 16px;
    background: var(--slate-50);
    border-radius: var(--radius-lg);
    border-left: 4px solid var(--review-warning);
}

.proposals-section-pending .proposals-section-header {
    border-left-color: var(--review-warning);
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, var(--slate-50) 100%);
}

.proposals-section-voted .proposals-section-header {
    border-left-color: var(--review-success);
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, var(--slate-50) 100%);
}

.section-icon {
    font-size: 14px;
    color: var(--review-warning);
}

.section-icon.voted {
    color: var(--review-success);
}

.section-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--slate-700);
    flex: 1;
}

.section-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    font-size: 12px;
    font-weight: 700;
    color: white;
    background: var(--review-warning);
    border-radius: var(--radius-full);
}

.proposals-section-voted .section-count {
    background: var(--review-success);
}

body.dark-mode .proposals-section-header {
    background: var(--slate-800);
}

body.dark-mode .proposals-section-pending .proposals-section-header {
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.12) 0%, var(--slate-800) 100%);
}

body.dark-mode .proposals-section-voted .proposals-section-header {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, var(--slate-800) 100%);
}

body.dark-mode .section-title {
    color: var(--slate-200);
}

body.dark-mode .section-icon {
    color: #fbbf24;
}

body.dark-mode .section-icon.voted {
    color: #34d399;
}

.proposals-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* The first card carries the priority badge (.proposal-priority-badge,
       top:-6px overhang). Without clearance the badge tucks up under the
       sub-tab strip above and its top is clipped. 8px lets the overhang
       sit clear of the strip. */
    padding-top: 8px;
}

.proposal-item {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--rc-spacing-section);
    padding: var(--rc-spacing-card);
    background: var(--review-card-bg, white);
    border: 1px solid var(--slate-200);
    border-radius: var(--rc-radius-card);
    transition: all 0.2s ease;
}

.proposal-item:hover {
    border-color: var(--primary-200);
    box-shadow: 0 2px 8px rgba(20, 184, 166, 0.08);
}

.proposal-header {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: var(--rc-spacing-section);
}

/* Title row: [Type Badge] Title [Voted] [Mode] */
.proposal-title-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* Right-aligned deadline in title row */
.proposal-title-right {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-left: auto;
    flex-shrink: 0;
    white-space: nowrap;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

body.dark-mode .proposal-title-right {
    color: var(--slate-400);
}

/* Meta line */
.proposal-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    font-size: var(--rc-font-secondary);
    color: var(--slate-500);
}

.proposal-meta-line > span {
    white-space: nowrap;
}

.proposal-meta-line > span:not(:last-child):not(.proposal-meta-right)::after {
    content: "\00B7";
    margin: 0 6px;
    color: var(--slate-300);
}

/* Right-aligned zone in meta line (participants + voted) */
.proposal-meta-right {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-left: auto;
    flex-shrink: 0;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

.proposal-meta-right > span {
    white-space: nowrap;
}

body.dark-mode .proposal-meta-right {
    color: var(--slate-400);
}

/* Vote progress chip — bar + count, sits in proposal-meta-right */
.proposal-vote-progress {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.proposal-vote-progress-bar {
    display: inline-block;
    width: 48px;
    height: 4px;
    background: var(--slate-200);
    border-radius: 2px;
    overflow: hidden;
}

.proposal-vote-progress-fill {
    display: block;
    height: 100%;
    background: var(--rc-status-available);
    border-radius: 2px;
    transition: width 200ms ease;
}

.proposal-vote-progress-text {
    font-size: var(--rc-font-caption, 11px);
    font-variant-numeric: tabular-nums;
}

body.dark-mode .proposal-vote-progress-bar {
    background: var(--slate-600);
}

/* Deadline area (used in proposal meta-right) */
.proposal-deadline-area {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
    white-space: nowrap;
}

.proposal-deadline-area > i {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-400);
}

.proposal-deadline-label {
    font-weight: 500;
    color: var(--slate-500);
}

.proposal-deadline-value {
    font-weight: 600;
    color: var(--slate-700);
}

/* Urgency colors on deadline area. high uses red-500 for the surrounding
   text and red-600 (--rc-status-busy) for the strong value, deliberately
   one ramp step apart for emphasis. */
.proposal-deadline-area.urgency-high {
    color: #ef4444;
}

.proposal-deadline-area.urgency-high > i,
.proposal-deadline-area.urgency-high .proposal-deadline-label {
    color: #ef4444;
}

.proposal-deadline-area.urgency-high .proposal-deadline-value {
    color: var(--rc-status-busy);
    font-weight: 700;
}

.proposal-deadline-area.urgency-medium {
    color: var(--rc-status-tentative);
}

.proposal-deadline-area.urgency-medium > i,
.proposal-deadline-area.urgency-medium .proposal-deadline-label {
    /* Darker amber for legibility — see urgency-tomorrow comment. */
    color: #d97706;
}

.proposal-deadline-area.urgency-medium .proposal-deadline-value {
    color: #b45309;
    font-weight: 700;
}

/* Dark mode deadline area */
body.dark-mode .proposal-deadline-area {
    color: var(--slate-400);
}

body.dark-mode .proposal-deadline-area > i {
    color: var(--slate-500);
}

body.dark-mode .proposal-deadline-label {
    color: var(--slate-400);
}

body.dark-mode .proposal-deadline-value {
    color: var(--slate-200);
}

body.dark-mode .proposal-deadline-area.urgency-high,
body.dark-mode .proposal-deadline-area.urgency-high > i,
body.dark-mode .proposal-deadline-area.urgency-high .proposal-deadline-label {
    color: #f87171;
}

body.dark-mode .proposal-deadline-area.urgency-high .proposal-deadline-value {
    color: #fca5a5;
}

/* Location line */
.proposal-location-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
    padding: 0;
}

.proposal-location-line i {
    font-size: 10px;
    color: var(--slate-400);
}

body.dark-mode .proposal-location-line {
    color: var(--slate-400);
}

body.dark-mode .proposal-location-line i {
    color: var(--slate-500);
}

/* Status line: voting status + you voted indicator */
.proposal-status-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-600);
    padding: 2px 0;
}

/* A5 — team golden-window banner. A calm, full-width context strip that
   frames the proposal's options against the team's best window. Tinted by
   window quality but deliberately low-key (it's context, not an action). */
.rc-golden-window {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin: 4px 0 2px;
    padding: 5px 10px;
    border-radius: var(--rc-radius-btn, 8px);
    font-size: var(--rc-font-secondary, 12px);
    background: color-mix(in srgb, var(--rc-gw-tone, #14b8a6) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--rc-gw-tone, #14b8a6) 28%, transparent);
    color: var(--slate-700);
}
.rc-golden-window > i { color: var(--rc-gw-tone, #14b8a6); }
.rc-gw-label { color: var(--slate-500); }
.rc-gw-time { font-weight: 700; color: var(--slate-800); }
.rc-gw-reach { margin-left: auto; color: var(--slate-500); font-weight: 600; }
/* Muted "flexible: 8am-8pm" suffix — the wider workable span as quiet context
   on the SAME line as the best-fit window, never a second coloured strip. It
   sits to the right of reach; on a narrow card it wraps under, still grey. */
.rc-gw-flex {
    color: var(--slate-400);
    font-weight: 500;
    font-size: 11px;
}
.rc-gw-excellent { --rc-gw-tone: #16a34a; }
.rc-gw-good      { --rc-gw-tone: #22c55e; }
.rc-gw-fair      { --rc-gw-tone: #f59e0b; }
.rc-gw-limited   { --rc-gw-tone: #f97316; }

/* No-fit fallback: shown when no window the full length of the meeting clears
   the quality bar. It is the widest workable SPAN, not a fitting slot, so it
   reads calm and non-committal — a flat wash and no star, the icon already
   signals "range" rather than "the answer". */
.rc-golden-window--span {
  background: color-mix(in srgb, var(--rc-gw-tone, #14b8a6) 8%, transparent);
}
.rc-golden-window--span .rc-gw-label { font-weight: 600; }

body.dark-mode .rc-golden-window { color: var(--slate-200); }
body.dark-mode .rc-gw-time { color: var(--slate-100); }
body.dark-mode .rc-gw-label,
body.dark-mode .rc-gw-reach { color: var(--slate-400); }

/* ── "Why these times" disclosure (hierarchy pass) ───────────────────────────
   The golden-window banner, the fairness note, and the per-slot impact /
   who's-free chips are CONTEXT, not the decision. They collapse behind one
   quiet toggle so the card face leads with the rankable slots. */
.proposal-why-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 4px 0 2px;
    padding: 4px 10px;
    background: var(--slate-50, #f8fafc);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    color: var(--slate-500, #64748b);
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 500;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.proposal-why-toggle > i:first-child { font-size: 11px; color: var(--slate-400, #94a3b8); }
.proposal-why-toggle:hover {
    background: var(--slate-100, #f1f5f9);
    border-color: var(--slate-300, #cbd5e1);
    color: var(--slate-700, #334155);
}
.proposal-why-toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
.proposal-why-chev { font-size: 10px; transition: transform 0.18s ease; }
.proposal-why-toggle[aria-expanded="true"] .proposal-why-chev { transform: rotate(180deg); }
.proposal-why-detail {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
body.dark-mode .proposal-why-toggle {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .proposal-why-toggle:hover { background: var(--slate-700, #334155); color: var(--slate-200, #e2e8f0); }

/* Per-slot impact + who's-free (and their column headers) are detail: hidden
   on the card face, revealed in step with the "Why these times" toggle. The
   rank circle, date, time, and the live vote bar stay on the face. */
.proposal-detail-collapsed .option-info-detail { display: none; }

/* A6 — fairness-impact note: a gentle heads-up that someone's been
   carrying off-hours load. Amber, low-key, info-not-alarm. */
.rc-fairness-note {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 2px 0;
    padding: 4px 10px;
    border-radius: var(--rc-radius-btn, 8px);
    font-size: var(--rc-font-secondary, 12px);
    color: #92400e;
    background: var(--cmx-b69538af4e);
    background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 12%, transparent);
}
.rc-fairness-note > i { color: var(--rc-status-tentative, #f59e0b); }
body.dark-mode .rc-fairness-note { color: #fbbf24; background: rgba(245, 158, 11, 0.12); }

.status-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.status-dot.status-open {
    background: var(--rc-status-available);
    box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2);
}

.status-dot.status-closed {
    background: var(--slate-400);
}

.status-dot.status-pending {
    background: var(--rc-status-tentative);
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.2);
}

.status-dot.status-expired {
    background: var(--rc-status-busy);
    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
}

.status-sep {
    color: var(--slate-300);
    margin: 0 2px;
}

.status-you-voted {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--rc-status-available);
    font-weight: 500;
}

.status-you-voted.unavailable {
    color: var(--rc-status-busy);
}

.status-you-voted i {
    font-size: 10px;
}

body.dark-mode .proposal-status-line {
    color: var(--slate-400);
}

body.dark-mode .status-dot.status-open {
    box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.3);
}

body.dark-mode .status-dot.status-closed {
    background: var(--slate-500);
}

body.dark-mode .status-dot.status-pending {
    box-shadow: 0 0 0 2px rgba(245, 158, 11, 0.3);
}

body.dark-mode .status-dot.status-expired {
    box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.3);
}

body.dark-mode .status-sep {
    color: var(--slate-600);
}

body.dark-mode .status-you-voted {
    color: #34d399;
}

body.dark-mode .status-you-voted.unavailable {
    color: #f87171;
}

/* Description line — truncated, expandable */
.proposal-description-line {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
    line-height: 1.3;
    cursor: pointer;
    padding: 2px 0;
}

.proposal-description-line i {
    font-size: 10px;
    color: var(--slate-400);
    flex-shrink: 0;
}

.desc-expand-hint {
    font-size: var(--rc-font-tiny, 10px);
    color: var(--slate-400);
    font-style: italic;
    margin-left: 4px;
    flex-shrink: 0;
}

.proposal-description-line:hover {
    color: var(--slate-700);
}

.proposal-description-line:hover .desc-expand-hint {
    color: var(--primary-500);
}

body.dark-mode .proposal-description-line {
    color: var(--slate-400);
}

body.dark-mode .proposal-description-line:hover {
    color: var(--slate-300);
}

body.dark-mode .desc-expand-hint {
    color: var(--slate-500);
}

/* Slots never arrived (2026-08-06). Replaces the ballot chrome on a proposal
   whose times failed to sync, so the card stops asking the user to rank times
   it never showed. Amber, not red: nothing is broken for the user to fix, the
   data just has not landed yet. */
.proposal-slots-missing {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: var(--rc-spacing-section, 10px);
    padding: 12px;
    border: 1px solid var(--cmx-ccf2900c31);
    border: 1px solid color-mix(in srgb, var(--status-tentative, #f59e0b) 40%, transparent);
    border-radius: 8px;
    background: var(--cmx-5425b90798);
    background: color-mix(in srgb, var(--status-tentative, #f59e0b) 8%, transparent);
}
.proposal-slots-missing > i {
    color: var(--status-tentative, #f59e0b);
    flex-shrink: 0;
}
.proposal-slots-missing-copy {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
}
.proposal-slots-missing-copy > strong {
    font-size: var(--rc-font-body, 13px);
    line-height: 1.35;
}
.proposal-slots-missing-copy > span {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-600);
    line-height: 1.35;
}
.proposal-slots-refresh {
    flex-shrink: 0;
    white-space: nowrap;
}
body.dark-mode .proposal-slots-missing-copy > span { color: var(--slate-400); }
@media (max-width: 520px) {
    /* The button drops below the copy rather than squeezing it to a few
       characters on a phone, where this card is most likely to be seen. */
    .proposal-slots-missing { flex-wrap: wrap; }
    .proposal-slots-refresh { width: 100%; justify-content: center; }
}

/* Options section — contains header, options, tip */
.proposal-options-section {
    border-top: 1px solid var(--slate-200);
    padding-top: var(--rc-spacing-section, 10px);
}

body.dark-mode .proposal-options-section {
    border-top-color: var(--slate-600);
}

/* Column headers above option rows */
.proposal-options-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 12px 6px 12px;
    /* wrap so .options-header-hint can take a full line of its own below the
       label + column headers rather than squeezing them (2026-08-06) */
    flex-wrap: wrap;
}

.options-header-label {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
    font-weight: 500;
}

/* One-line legend teaching the rank circle's cycle (2026-08-06). Takes a full
   line of its own beneath the label so it never competes with the column
   headers for the same row, and reads as guidance (lighter, smaller) rather
   than as another control. */
.options-header-hint {
    flex-basis: 100%;
    order: 3;
    font-size: var(--rc-font-tertiary, 11px);
    color: var(--slate-400);
    line-height: 1.35;
    margin-top: 2px;
}

body.dark-mode .options-header-hint {
    color: var(--slate-500);
}

.options-header-columns {
    display: flex;
    align-items: center;
    gap: 16px;
}

.col-header {
    font-size: var(--rc-font-tiny, 10px);
    color: var(--slate-400);
    font-weight: 500;
    text-transform: lowercase;
    letter-spacing: 0.3px;
}

.col-header.col-fairness {
    min-width: 50px;
    text-align: center;
}

.col-header.col-reachable {
    min-width: 56px;
    text-align: center;
}

.col-header.col-votes {
    min-width: 70px;
    text-align: center;
}

body.dark-mode .options-header-label {
    color: var(--slate-400);
}

body.dark-mode .col-header {
    color: var(--slate-500);
}

/* Tip text below options */
.proposal-tip-text {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px 0;
    font-size: var(--rc-font-tiny, 10px);
    color: var(--slate-400);
    font-style: italic;
}

.proposal-tip-text i {
    font-size: 10px;
    color: #f59e0b;
}

body.dark-mode .proposal-tip-text {
    color: var(--slate-500);
}

body.dark-mode .proposal-meta-line {
    color: var(--slate-400);
}

body.dark-mode .proposal-meta-line > span:not(:last-child):not(.proposal-meta-right)::after {
    color: var(--slate-600);
}

/* Voted badge in header */
.proposal-voted-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 8px;
    font-size: var(--rc-font-tiny);
    font-weight: 600;
    /* ONE green per pill (2026-08-29). The fill and border are emerald
       #10b981 (= `--success`, the hue this sheet's whole "emerald" tab family
       already uses for a done/succeeded signal), but the glyph was
       `--rc-status-available` #22c55e — a different green — so an 8px pill
       carried two of them. "You voted" is a SUCCESS state, not an availability
       reading, so the emerald is the correct channel here and the text was the
       outlier. Matched to --success rather than repainting the fill. */
    color: var(--success, #10b981);
    background: rgba(16, 185, 129, 0.12);
    border: 1px solid rgba(16, 185, 129, 0.25);
    border-radius: var(--rc-radius-pill);
    white-space: nowrap;
}

.proposal-voted-badge i {
    font-size: 10px;
}

.proposal-voted-badge.unavailable {
    color: var(--slate-500);
    background: rgba(100, 116, 139, 0.12);
    border-color: rgba(100, 116, 139, 0.25);
}

body.dark-mode .proposal-voted-badge {
    color: #34d399;
    background: rgba(16, 185, 129, 0.2);
    border-color: rgba(16, 185, 129, 0.35);
}

body.dark-mode .proposal-voted-badge.unavailable {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.15);
    border-color: rgba(148, 163, 184, 0.3);
}

/* Voted proposal styling */
.proposal-item.proposal-voted {
    opacity: 0.85;
    border-color: rgba(16, 185, 129, 0.3);
}

.proposal-item.proposal-voted:hover {
    opacity: 1;
}

/* Priority/Urgency visual differentiation for proposals */
/* High urgency (deadline within 24h) - Red indicator */
/* Urgency is carried by the border-left alone — the coloured glow ring that
   used to layer on top was a second accent competing for the same signal, so
   the card kept the plain shadow and reads with one clear urgency cue. */
.proposal-item.urgency-high {
    border-left: 4px solid var(--rc-status-busy);
}


/* Medium urgency (deadline 1-3 days) - Amber indicator */
.proposal-item.urgency-medium {
    border-left: 4px solid var(--rc-status-tentative);
}


/* Low urgency (deadline > 3 days) - Default/subtle indicator */
.proposal-item.urgency-low {
    border-left: 4px solid var(--accent, #14b8a6);
}

/* Priority number badge for ranked items. The NUMERAL conveys rank, so a
   single calm slate badge does the job — the previous three per-rank gradient
   hues (orange/teal/purple) were three more competing accents fighting the
   urgency border and fairness chip for the eye. One quiet token, every rank. */
.proposal-priority-badge {
    position: absolute;
    top: -6px;
    left: -6px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--slate-600, #475569);
    color: white;
    font-size: 11px;
    font-weight: 700;
    border-radius: 50%;
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.15));
    z-index: 2;
}

body.dark-mode .proposal-priority-badge {
    background: var(--slate-500, #64748b);
}

/* Dark mode proposal cards */
body.dark-mode .proposal-item {
    background: var(--slate-800);
    border-color: var(--slate-700);
}

body.dark-mode .proposal-item:hover {
    border-color: var(--primary-600);
}

/* Dark mode urgency */
body.dark-mode .proposal-item.urgency-high {
    border-left-color: #f87171;
}


body.dark-mode .proposal-item.urgency-medium {
    border-left-color: #fbbf24;
}


body.dark-mode .proposal-item.urgency-low {
    border-left-color: var(--accent, #2dd4bf);
}

/* Voted confirmation in actions */
.voted-confirmation {
    display: flex;
    align-items: center;
    gap: 5px;
    margin-right: auto;
    font-size: var(--rc-font-secondary);
    font-weight: 500;
    color: var(--rc-status-available);
}

.voted-confirmation i {
    font-size: 14px;
}

body.dark-mode .voted-confirmation {
    color: #34d399;
}

/* A DECLINE IS NOT GREEN (2026-09-22, owner: "for done they should maintain
   the vote look ie red in the one I put as can't attend").
   renderProposalCard has always emitted `.unavailable` on this span for a
   cannotAttend vote — and NOTHING styled it, so "Marked as unavailable"
   inherited the available-green above and read as a cheerful confirmation of
   a refusal. The card head already says "Can't Attend" in red; the footer
   must not contradict it.
   Uses the same busy token the conflict surfaces use, so the decline reads in
   ONE red across the product rather than a new hex. */
.voted-confirmation.unavailable {
    color: var(--rc-status-busy, #dc2626);
}
body.dark-mode .voted-confirmation.unavailable {
    color: #f87171;
}

/* Your Rankings Section (Already Voted) */
.proposal-your-votes {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px 12px;
    background: var(--slate-50);
    border-radius: var(--rc-radius-btn);
    margin-bottom: var(--rc-spacing-section);
    border: 1px solid var(--slate-200);
}

.your-votes-label {
    font-size: var(--rc-font-caption);
    font-weight: 600;
    color: var(--slate-600);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    display: flex;
    align-items: center;
    gap: 6px;
}

.your-votes-label i {
    color: var(--amber-500);
}

.your-votes-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.vote-rank {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px;
    font-size: 0.875rem;
    font-weight: 500;
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    color: var(--slate-700);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}

.vote-rank-disc {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--slate-500);
    background: var(--slate-100);
    border: 2px solid var(--slate-300);
}

.vote-rank-ordinal {
    font-weight: 700;
    font-size: 0.75rem;
    color: var(--slate-500);
    min-width: 28px;
}

.vote-rank-time {
    flex: 1;
    font-weight: 500;
}

/* Vote-rank medal palette (gold/silver/bronze). Intrinsic medal colors,
   not status — kept hardcoded so the status palette can change without
   recoloring podium positions. */
.vote-rank.rank-gold {
    background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
    border-color: #f59e0b;
    color: #78350f;
}
.vote-rank.rank-gold .vote-rank-disc {
    background: linear-gradient(135deg, #fbbf24, #f59e0b);
    border-color: #d97706;
    color: #78350f;
}
.vote-rank.rank-gold .vote-rank-ordinal { color: #b45309; }

.vote-rank.rank-silver {
    background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
    border-color: #94a3b8;
    color: #334155;
}
.vote-rank.rank-silver .vote-rank-disc {
    background: linear-gradient(135deg, #cbd5e1, #94a3b8);
    border-color: #64748b;
    color: #334155;
}
.vote-rank.rank-silver .vote-rank-ordinal { color: #475569; }

.vote-rank.rank-bronze {
    background: linear-gradient(135deg, #fef4e8 0%, #fed7aa 100%);
    border-color: #b45309;
    color: #9a3412;
}
.vote-rank.rank-bronze .vote-rank-disc {
    background: linear-gradient(135deg, #d97706, #b45309);
    border-color: #92400e;
    color: white;
}
.vote-rank.rank-bronze .vote-rank-ordinal { color: #c2410c; }

.vote-rank.rank-other .vote-rank-disc {
    background: var(--slate-100);
    border-color: var(--slate-300);
    color: var(--slate-500);
}

body.dark-mode .proposal-your-votes {
    background: var(--slate-800);
    border-color: var(--slate-700);
}

body.dark-mode .your-votes-label {
    color: var(--slate-400);
}

body.dark-mode .vote-rank {
    background: var(--slate-700);
    border-color: var(--slate-600);
    color: var(--slate-200);
}

body.dark-mode .vote-rank-disc {
    background: var(--slate-800);
    border-color: var(--slate-500);
    color: var(--slate-300);
}

body.dark-mode .vote-rank-ordinal {
    color: var(--slate-400);
}

body.dark-mode .vote-rank.rank-gold {
    background: linear-gradient(135deg, rgba(251, 191, 36, 0.18) 0%, rgba(251, 191, 36, 0.28) 100%);
    border-color: #fbbf24;
    color: #fcd34d;
}
body.dark-mode .vote-rank.rank-gold .vote-rank-ordinal { color: #fbbf24; }

body.dark-mode .vote-rank.rank-silver {
    background: linear-gradient(135deg, rgba(148, 163, 184, 0.18) 0%, rgba(148, 163, 184, 0.28) 100%);
    border-color: #94a3b8;
    color: #cbd5e1;
}
body.dark-mode .vote-rank.rank-silver .vote-rank-ordinal { color: #cbd5e1; }

body.dark-mode .vote-rank.rank-bronze {
    background: linear-gradient(135deg, rgba(251, 146, 60, 0.18) 0%, rgba(251, 146, 60, 0.28) 100%);
    border-color: #fb923c;
    color: #fed7aa;
}
body.dark-mode .vote-rank.rank-bronze .vote-rank-ordinal { color: #fdba74; }

/* Closed proposal summary (shown when voting is closed — replaces the
   voting surface with a winning-slot recap or a "no votes" note) */
.proposal-closed-summary {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    margin: 8px 0 12px 0;
    background: var(--slate-50, #f8fafc);
    border: 1px solid var(--slate-200);
    border-radius: var(--rc-radius-btn);
}
.proposal-closed-winner {
    background: rgba(34, 197, 94, 0.06);
    border-color: rgba(34, 197, 94, 0.35);
}
.closed-summary-icon {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--slate-200);
    color: var(--slate-600);
    font-size: 13px;
}
.closed-summary-winner-icon {
    background: linear-gradient(135deg, #fbbf24, #f59e0b);
    color: #78350f;
}
.closed-summary-body {
    flex: 1;
    min-width: 0;
}
.closed-summary-headline {
    font-size: 13px;
    font-weight: 600;
    color: var(--slate-700);
    letter-spacing: 0.01em;
}
.closed-summary-sub {
    font-size: 12px;
    color: var(--slate-500);
    margin-top: 2px;
}
.closed-summary-winner-line {
    margin-top: 4px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 13px;
    color: var(--slate-800);
}
.closed-winner-date { font-weight: 600; }
.closed-winner-sep { color: var(--slate-400); }
.closed-winner-time { font-weight: 500; }
.closed-winner-votes {
    margin-left: auto;
    font-size: 11px;
    font-weight: 600;
    color: #15803d;
    padding: 2px 8px;
    background: rgba(34, 197, 94, 0.12);
    border-radius: 999px;
}
body.dark-mode .proposal-closed-summary {
    background: var(--slate-800);
    border-color: var(--slate-700);
}
body.dark-mode .proposal-closed-winner {
    background: rgba(34, 197, 94, 0.1);
    border-color: rgba(34, 197, 94, 0.4);
}
body.dark-mode .closed-summary-icon {
    background: var(--slate-700);
    color: var(--slate-300);
}
body.dark-mode .closed-summary-headline { color: var(--slate-200); }
body.dark-mode .closed-summary-sub { color: var(--slate-400); }
body.dark-mode .closed-summary-winner-line { color: var(--slate-100); }
body.dark-mode .closed-winner-votes {
    color: #4ade80;
    background: rgba(34, 197, 94, 0.15);
}

/* Change vote button */
.change-vote-btn {
    padding: 6px 14px;
    font-size: 12px;
    font-weight: 500;
    color: var(--slate-600);
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
}

.change-vote-btn:hover {
    color: var(--primary-700);
    background: var(--primary-50);
    border-color: var(--primary-300);
}

.change-vote-btn i {
    margin-right: 5px;
}

body.dark-mode .change-vote-btn {
    color: var(--slate-300);
    background: var(--slate-700);
    border-color: var(--slate-600);
}

body.dark-mode .change-vote-btn:hover {
    color: var(--primary-300);
    background: rgba(20, 184, 166, 0.1);
    border-color: var(--primary-500);
}

.proposal-title {
    font-size: var(--rc-font-heading);
    font-weight: 600;
    color: var(--slate-800);
}


body.dark-mode .proposal-title {
    color: var(--slate-100);
}

.sync-badge {
    display: inline-flex;
    align-items: center;
    margin-left: 6px;
    font-size: 12px;
    vertical-align: middle;
}
.sync-badge.sync-offline {
    color: var(--amber-500, #f59e0b);
}
.sync-badge.sync-error {
    color: var(--red-500, #ef4444);
}
body.dark-mode .sync-badge.sync-offline {
    color: var(--amber-400, #fbbf24);
}
body.dark-mode .sync-badge.sync-error {
    color: var(--red-400, #f87171);
}

.proposal-proposer {
    font-size: 12px;
    color: var(--slate-400);
}


body.dark-mode .proposal-proposer {
    color: var(--slate-500);
}

/* Polling Mode Badges */
.mode-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 7px;
    font-size: var(--rc-font-tiny);
    font-weight: 500;
    border-radius: var(--rc-radius-pill);
    margin-left: 4px;
}

.mode-badge i {
    font-size: 9px;
}

/* "Which group is this?" badge (2026-07-06). A quiet neutral pill shown on
   proposal + privacy items ONLY in the aggregate Home view, so a multi-group
   user can tell which board an item belongs to when several boards' items mix
   in one list. Deliberately understated (slate, not a brand colour) — it's
   context, not an action. */
.rc-group-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    font-size: var(--rc-font-tiny);
    font-weight: 500;
    border-radius: var(--rc-radius-pill);
    margin-left: 4px;
    color: var(--slate-600, #475569);
    background: var(--slate-100, #f1f5f9);
    border: 1px solid var(--slate-200, #e2e8f0);
    max-width: 12rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rc-group-badge i {
    font-size: 9px;
    flex-shrink: 0;
    opacity: 0.75;
}
/* On the privacy rows the badge sits under the meta line rather than inline. */
.synced-row-info .rc-group-badge {
    margin-left: 0;
    margin-top: 4px;
}
body.dark-mode .rc-group-badge {
    color: var(--slate-300, #cbd5e1);
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}

.mode-manager {
    color: #6366f1;
    background: rgba(99, 102, 241, 0.1);
    border: 1px solid rgba(99, 102, 241, 0.2);
}

.mode-collaborative {
    color: #0891b2;
    background: rgba(8, 145, 178, 0.1);
    border: 1px solid rgba(8, 145, 178, 0.2);
}

body.dark-mode .mode-manager {
    color: #a5b4fc;
    background: rgba(99, 102, 241, 0.15);
    border-color: rgba(99, 102, 241, 0.3);
}

body.dark-mode .mode-collaborative {
    color: #67e8f9;
    background: rgba(8, 145, 178, 0.15);
    border-color: rgba(8, 145, 178, 0.3);
}

/* Proposal Description */
.proposal-description {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    font-size: 12px;
    color: var(--slate-500);
    padding: 6px 10px;
    background: var(--slate-50);
    border-radius: 6px;
    border-left: 2px solid var(--slate-200);
    line-height: 1.4;
}

.proposal-description i {
    color: var(--slate-400);
    font-size: 11px;
    margin-top: 2px;
}

body.dark-mode .proposal-description {
    color: var(--slate-400);
    background: var(--slate-800);
    border-color: var(--slate-600);
}


/* Proposal Type Badges */
.proposal-type-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 7px;
    border-radius: var(--rc-radius-pill);
    font-size: var(--rc-font-tiny);
    font-weight: 600;
    text-transform: capitalize;
    flex-shrink: 0;
}

.proposal-type-badge i {
    font-size: 9px;
}

/* Work badge - Blue (canonical) */
.proposal-type-work {
    background: rgba(59, 130, 246, 0.1);
    color: #2563eb;
    border: 1px solid rgba(59, 130, 246, 0.3);
}

/* Personal badge - Pink (canonical) */
.proposal-type-personal {
    background: rgba(244, 114, 182, 0.1);
    color: #db2777;
    border: 1px solid rgba(244, 114, 182, 0.3);
}

body.dark-mode .proposal-type-work {
    background: rgba(59, 130, 246, 0.2);
    color: #60a5fa;
    border-color: rgba(59, 130, 246, 0.4);
}

body.dark-mode .proposal-type-personal {
    background: rgba(244, 114, 182, 0.2);
    color: #f9a8d4;
    border-color: rgba(244, 114, 182, 0.4);
}

/* Proposal Duration & Participants */
.proposal-duration,
.proposal-participants {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: var(--rc-font-secondary);
    color: var(--slate-500);
}

.proposal-duration i,
.proposal-participants i {
    font-size: var(--rc-font-tiny);
    color: var(--slate-400);
}

body.dark-mode .proposal-duration,
body.dark-mode .proposal-participants {
    color: var(--slate-400);
}

body.dark-mode .proposal-duration i,
body.dark-mode .proposal-participants i {
    color: var(--slate-500);
}

/* Slot Fairness Badges - Numeric score */
.slot-fairness {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 7px;
    border-radius: var(--rc-radius-pill);
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    white-space: nowrap;
    border: 1px solid transparent;
    transition: all 0.15s ease;
    min-width: 40px;
    justify-content: center;
}

.slot-fairness i {
    font-size: 8px;
}

/* Slot fairness ramp — derived from traffic-light palette so fairness
   reads the same as every other status surface. Differentiation comes
   from lightness, not hue-shift.
   Text colors (#15803d, #b45309, #c2410c, #b91c1c) are darker variants
   for legibility on the soft tinted backgrounds; they do not match the
   --rc-status-* tokens 1:1 and stay hardcoded until the canonical palette
   grows -strong variants. */
/* Canonical 5-band tints — derived from --fv-l2/l3/l5/l7/l8.
   Slot keys (excellent/good/fair/challenging/difficult) preserved for
   backward CSS compatibility; the 5 canonical bands map onto them in order:
       excellent  ← excellent  (--fv-l2 deep green)
       good       ← steady     (--fv-l3 canonical green)
       fair       ← mixed      (--fv-l5 canonical amber)
       challenging ← stretched (--fv-l7 light red)
       difficult  ← overloaded (--fv-l8 canonical busy red)
   No lime, no orange — pure traffic-light vocabulary. */
.slot-fairness-excellent {
    background: rgba(21, 128, 61, 0.14);
    color: #15803d;
    border-color: rgba(21, 128, 61, 0.35);
}
.slot-fairness-good {
    background: rgba(22, 163, 74, 0.14);
    color: #15803d;
    border-color: rgba(22, 163, 74, 0.35);
}
.slot-fairness-fair {
    background: rgba(251, 191, 36, 0.16);
    color: #b45309;
    border-color: rgba(251, 191, 36, 0.4);
}
.slot-fairness-challenging {
    background: rgba(239, 68, 68, 0.14);
    color: #b91c1c;
    border-color: rgba(239, 68, 68, 0.35);
}
.slot-fairness-difficult {
    background: rgba(220, 38, 38, 0.14);
    color: #b91c1c;
    border-color: rgba(220, 38, 38, 0.4);
}
/* Unscored slot: clearly NOT a rating on the impact scale. Neutral slate with
   a dashed border so it reads as "no data yet", not as a good-or-bad verdict. */
.slot-fairness-unknown {
    background: rgba(148, 163, 184, 0.12);
    color: var(--slate-500, #64748b);
    border: 1px dashed rgba(148, 163, 184, 0.5);
    font-style: italic;
}
body.dark-mode .slot-fairness-unknown {
    background: rgba(148, 163, 184, 0.16);
    color: var(--slate-400, #94a3b8);
    border-color: rgba(148, 163, 184, 0.45);
}

body.dark-mode .slot-fairness-excellent {
    background: rgba(21, 128, 61, 0.22);
    color: #4ade80;
    border-color: rgba(34, 197, 94, 0.5);
}
body.dark-mode .slot-fairness-good {
    background: rgba(22, 163, 74, 0.2);
    color: #86efac;
    border-color: rgba(22, 163, 74, 0.45);
}
body.dark-mode .slot-fairness-fair {
    background: rgba(251, 191, 36, 0.2);
    color: #fbbf24;
    border-color: rgba(251, 191, 36, 0.45);
}
body.dark-mode .slot-fairness-challenging {
    background: rgba(239, 68, 68, 0.22);
    color: #fca5a5;
    border-color: rgba(239, 68, 68, 0.5);
}
body.dark-mode .slot-fairness-difficult {
    background: rgba(220, 38, 38, 0.22);
    color: #f87171;
    border-color: rgba(220, 38, 38, 0.5);
}

/* Proposal Options — Compact Single-Row Layout */
.proposal-options {
    display: flex;
    flex-direction: column;
    gap: var(--rc-spacing-inline);
    margin-bottom: var(--rc-spacing-card);
}

.proposal-option {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px 10px;
    padding: 8px 12px;
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--rc-radius-btn);
    transition: background-color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
    cursor: pointer;
    font-size: var(--rc-font-body);
}

/* A3 — reachable-count chip beside the slot fairness badge */
.slot-reachable {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    white-space: nowrap;
}
.slot-reachable-all  { color: var(--rc-status-available, #22c55e); }
.slot-reachable-some { color: var(--rc-status-tentative, #f59e0b); }
.slot-reachable-none { color: var(--rc-status-busy, #dc2626); }
.slot-reachable i { font-size: 10px; opacity: 0.85; }

/* Inline unit word ("votes" / "free") appended on header-less surfaces
   (My-Proposals) so two adjacent X/Y chips self-distinguish without hover.
   Muted + smaller so the number stays the dominant glyph; inherits the
   chip's colour so the reachable tone still reads. */
.slot-chip-unit {
    margin-left: 3px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 500;
    opacity: 0.65;
    text-transform: lowercase;
}

/* A2 — worst-hour participant peek: a full-width second line under the
   slot's main row (flex-wrap makes flex-basis:100% drop to its own line). */
.slot-participant-peek {
    flex-basis: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    margin-top: 2px;
    padding-left: 2px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}
.slot-participant-peek > i { font-size: 10px; opacity: 0.7; }

/* Phase 4 — the "who's affected" peeks are collapsed by default so the option
   rows read as a clean comparison; the per-proposal toggle reveals them. */
.proposal-options.peek-collapsed .slot-participant-peek { display: none; }
.proposal-peek-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 2px 0 0;
    padding: 3px 8px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: var(--slate-500);
    border-radius: var(--radius-md, 6px);
}
.proposal-peek-toggle:hover { color: var(--slate-700); background: rgba(148, 163, 184, 0.12); }
.proposal-peek-toggle i { font-size: 11px; }
body.dark-mode .proposal-peek-toggle { color: var(--slate-400); }
body.dark-mode .proposal-peek-toggle:hover { color: var(--slate-200); }
.slot-peek-person {
    display: inline-flex;
    align-items: baseline;
    gap: 3px;
    white-space: nowrap;
}
.slot-peek-time { color: var(--slate-400); font-variant-numeric: tabular-nums; }
.slot-peek-hard .slot-peek-time,
.slot-peek-hard { color: var(--rc-status-busy, #dc2626); font-weight: 600; }
.slot-peek-more { color: var(--slate-400); }

/* "Already accommodating" running count — amber (accommodation hue), distinct
   from the busy-red hard-hour marker. Heavy = this person is a top contributor
   right now, so the running cost reads stronger. */
.slot-peek-load {
    display: inline-flex;
    align-items: center;
    margin-left: 4px;
    padding: 0 6px;
    border-radius: var(--radius-full, 9999px);
    font-size: 11px;
    font-weight: 600;
    color: #b45309;
    background: var(--cmx-be4c072515);
    background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 14%, transparent);
}
.slot-peek-load-heavy {
    color: #92400e;
    background: var(--cmx-700426e65c);
    background: color-mix(in srgb, var(--rc-status-tentative, #f59e0b) 24%, transparent);
}

body.dark-mode .slot-participant-peek { color: var(--slate-400); }
body.dark-mode .slot-peek-time { color: var(--slate-500); }
body.dark-mode .slot-peek-hard,
body.dark-mode .slot-peek-hard .slot-peek-time { color: #f87171; }
body.dark-mode .slot-peek-load { color: #fbbf24; background: rgba(245, 158, 11, 0.18); }
body.dark-mode .slot-peek-load-heavy { color: #fcd34d; background: rgba(245, 158, 11, 0.28); }

.proposal-option:hover {
    background: var(--slate-50);
    border-color: var(--slate-300);
}

body.dark-mode .proposal-option {
    background: var(--slate-700);
    border-color: var(--slate-600);
}

body.dark-mode .proposal-option:hover {
    background: var(--slate-600);
}

/* ── Temp-group poll cards (2026-08-23) ─────────────────────────────
   Host-side resolution of a "Guests vote on times" poll, rendered in the
   Proposals tab. Inherits .proposal-item / .proposal-option; these rules
   only add what a poll needs: the accept button pushed to the row's end,
   a leader highlight, and the Poll badge tone (teal — distinct from the
   blue work / pink personal event-type badges). */
.rc-poll-option {
    cursor: default; /* the ACTION is the button, the row itself is inert */
}

.rc-poll-option .rc-poll-accept {
    margin-left: auto;
    flex-shrink: 0;
    padding: 5px 12px;
    font-size: var(--rc-font-secondary, 12px);
}

/* Leader = the option most guests can make. One calm highlight, not a
   celebration: the host still decides. */
.rc-poll-option.rc-poll-top {
    border-color: var(--primary-400, #38bdf8);
    box-shadow: 0 0 0 1px var(--primary-400, #38bdf8) inset;
}

body.dark-mode .rc-poll-option.rc-poll-top {
    border-color: var(--primary-500, #0ea5e9);
    box-shadow: 0 0 0 1px var(--primary-500, #0ea5e9) inset;
}

/* Past options (2026-10-03): kept for their tally, muted, not pickable. */
.rc-poll-option.rc-poll-option--past {
    opacity: 0.6;
}

.rc-poll-past-chip {
    margin-left: auto;
    flex-shrink: 0;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-600);
    background: var(--slate-100);
    border: 1px solid var(--slate-200);
}

body.dark-mode .rc-poll-past-chip {
    color: var(--slate-300);
    background: var(--slate-700);
    border-color: var(--slate-600);
}

.rc-poll-all-past {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 12px;
    margin: 8px 0;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-700);
}

body.dark-mode .rc-poll-all-past {
    color: var(--slate-200);
}

.rc-poll-all-past .rc-poll-propose-new {
    min-height: 44px;
}

.proposal-poll-badge {
    background: rgba(20, 184, 166, 0.1);
    color: #0d9488;
    border: 1px solid rgba(20, 184, 166, 0.3);
}

body.dark-mode .proposal-poll-badge {
    background: rgba(20, 184, 166, 0.16);
    color: #2dd4bf;
    border-color: rgba(45, 212, 191, 0.35);
}

.rc-poll-status {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
}

body.dark-mode .rc-poll-status {
    color: var(--slate-400);
}

/* Narrow screens: the accept button drops to its own full-width line so the
   date/time and tally keep their room (WCAG 2.5.8: the target grows, never
   shrinks, when space is tight). */
@media (max-width: 480px) {
    .rc-poll-option .rc-poll-accept {
        margin-left: 0;
        flex-basis: 100%;
        justify-content: center;
    }
}

.option-label {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 14px;
    font-weight: 500;
    color: var(--slate-700);
    cursor: pointer;
}

body.dark-mode .option-label {
    color: var(--slate-200);
}

.option-label input[type="radio"] {
    width: 18px;
    height: 18px;
    accent-color: var(--primary-500);
}

/* Compact Rank Circle */
.rank-circle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    min-width: 26px;
    border-radius: 50%;
    border: 2px dashed var(--slate-300);
    background: var(--slate-50);
    color: var(--slate-400);
    font-size: var(--rc-font-caption);
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s ease;
    padding: 0;
    font-family: inherit;
    flex-shrink: 0;
}

.rank-circle:hover {
    border-color: var(--primary-400);
    background: var(--primary-50);
    color: var(--primary-600);
}

.rank-circle.rank-selected {
    border: 2px solid var(--primary-500);
    background: var(--primary-500);
    color: white;
}

.rank-circle.rank-unavailable {
    border: 2px solid rgba(239, 68, 68, 0.4);
    background: rgba(239, 68, 68, 0.1);
    color: var(--rc-status-busy);
}

.rank-circle .rank-number {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    font-size: var(--rc-font-secondary);
    font-weight: 700;
    color: inherit;
    background: none;
    border-radius: 0;
}

/* The compact "mark unavailable" ✕ button was REMOVED 2026-08-06. Each slot
   carried two controls that both expressed availability - this ✕ and the rank
   circle - and the circle already rendered a ✕ of its own for the unavailable
   state, so the same glyph appeared twice per row, once as a readout and once
   as an action. The circle now cycles through unavailable instead.

   Worth recording why this button was also a liability on its own terms: it sat
   at `opacity: 0` until the row was hovered, so on desktop a core voting action
   was invisible at rest, and the @media (hover: none) fallback that fixed it on
   phones meant the two platforms showed different controls for the same task. */

/* Rank circles used to fill with gold / silver / bronze gradients for the top
   three choices. On a voting card that read as celebration — three more
   competing accents above the rank number that already conveys order. Hierarchy
   pass: every ranked circle now uses the SAME calm primary fill (inherited from
   .rank-selected above); the NUMBER carries the rank. The #1 row keeps the
   faintest primary wash so the top choice still stands out at a glance.
   (The Find Times results podium keeps its medals — those live on
   .vm-results-rank-badge, a different element, and are intentional there.) */
.rank-circle.rank-gold,
.rank-circle.rank-silver,
.rank-circle.rank-bronze {
    border: 2px solid var(--primary-500);
    background: var(--primary-500);
    color: white;
}

.proposal-option.rank-gold {
    background: var(--cmx-9df19582ef);
    background: color-mix(in srgb, var(--primary-500) 5%, transparent);
    border-color: var(--cmx-62b589edda);
    border-color: color-mix(in srgb, var(--primary-500) 28%, transparent);
}

.proposal-option.rank-silver,
.proposal-option.rank-bronze {
    background: transparent;
    border-color: var(--slate-200, #e2e8f0);
}

/* Dark mode rank circle */
body.dark-mode .rank-circle {
    background: var(--slate-700);
    border-color: var(--slate-500);
    color: var(--slate-400);
}

body.dark-mode .rank-circle:hover {
    background: var(--slate-600);
    border-color: var(--primary-400);
    color: var(--slate-200);
}

body.dark-mode .rank-circle.rank-selected {
    background: var(--primary-500);
    border-color: var(--primary-400);
    color: white;
}

body.dark-mode .rank-circle.rank-unavailable {
    background: rgba(239, 68, 68, 0.15);
    border-color: rgba(239, 68, 68, 0.5);
    color: #f87171;
}

/* Dark mode medal circles */
body.dark-mode .rank-circle.rank-gold {
    background: linear-gradient(135deg, #fbbf24, #f59e0b);
    border-color: #fbbf24;
    color: #78350f;
}

body.dark-mode .proposal-option.rank-gold {
    background: rgba(251, 191, 36, 0.1);
    border-color: rgba(251, 191, 36, 0.4);
}

body.dark-mode .rank-circle.rank-silver {
    background: linear-gradient(135deg, #e2e8f0, #cbd5e1);
    border-color: #94a3b8;
    color: #1e293b;
}

body.dark-mode .proposal-option.rank-silver {
    background: rgba(148, 163, 184, 0.08);
    border-color: rgba(148, 163, 184, 0.35);
}

body.dark-mode .rank-circle.rank-bronze {
    background: linear-gradient(135deg, #f59e0b, #d97706);
    border-color: #d97706;
    color: white;
}

body.dark-mode .proposal-option.rank-bronze {
    background: rgba(217, 119, 6, 0.08);
    border-color: rgba(217, 119, 6, 0.35);
}


/* Compact inline option elements */
.option-date {
    font-size: var(--rc-font-secondary);
    color: var(--slate-500);
    white-space: nowrap;
    min-width: 70px;
}

.option-date-sep {
    color: var(--slate-300);
    font-size: var(--rc-font-secondary);
}

.option-time {
    flex: 1;
    font-size: var(--rc-font-body);
    font-weight: 600;
    color: var(--slate-900);
    white-space: nowrap;
    letter-spacing: -0.01em;
}

.option-duration {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-500);
    white-space: nowrap;
    min-width: 30px;
    text-align: right;
}

/* Compact vote info (inline) — aligns with column headers */
.option-info-compact {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
}

.vote-bar-compact {
    display: inline-block;
    width: 40px;
    height: 5px;
    background: var(--slate-200);
    border-radius: 3px;
    overflow: hidden;
}

.vote-bar-compact .vote-fill {
    display: block;
    height: 100%;
    background: var(--primary-500);
    border-radius: 2px;
    transition: width 0.2s ease;
}

.vote-count-compact {
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: var(--slate-600);
    white-space: nowrap;
}

/* Shared per-slot vote bar wrapper (renderSlotVoteBar) — used by both the
   voting card and the My-Proposals card so the spread reads identically. */
.slot-vote {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

body.dark-mode .option-time {
    color: var(--slate-100);
}

body.dark-mode .option-date-sep {
    color: var(--slate-600);
}

body.dark-mode .option-date {
    color: var(--slate-400);
}

body.dark-mode .option-duration {
    color: var(--slate-500);
}

body.dark-mode .vote-bar-compact {
    background: var(--slate-600);
}

body.dark-mode .vote-count-compact {
    color: var(--slate-400);
}

/* ============================================
   RED DECLINE PANEL - Slide from right
   ============================================ */
.decline-panel-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.3);
    z-index: 200;
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
}

.decline-panel {
    width: 380px;
    max-width: 90%;
    background: white;
    border-left: 4px solid var(--rc-status-busy);
    box-shadow: -8px 0 30px rgba(0, 0, 0, 0.15);
    display: flex;
    flex-direction: column;
    transform: translateX(100%);
    transition: transform 0.25s ease;
}

.decline-panel.open {
    transform: translateX(0);
}

.decline-panel-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    background: linear-gradient(135deg, rgba(239, 68, 68, 0.08) 0%, rgba(220, 38, 38, 0.04) 100%);
    border-bottom: 1px solid rgba(239, 68, 68, 0.15);
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--rc-status-busy);
}

.decline-panel-header i {
    font-size: 14px;
}

.decline-panel-close {
    margin-left: auto;
    background: none;
    border: none;
    color: var(--slate-400);
    cursor: pointer;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-full, 9999px);
    transition: all 0.15s ease;
}

.decline-panel-close:hover {
    background: rgba(239, 68, 68, 0.1);
    color: var(--rc-status-busy);
}

.decline-panel-body {
    flex: 1;
    padding: 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.decline-event-summary {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px;
    background: rgba(239, 68, 68, 0.05);
    border: 1px solid rgba(239, 68, 68, 0.15);
    border-radius: 8px;
}

.decline-event-summary strong {
    font-size: 0.8125rem;
    color: var(--slate-800);
}

.decline-event-time {
    font-size: 13px;
    color: var(--slate-500);
}

.decline-reason-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.decline-reason-group label {
    font-size: 13px;
    font-weight: 500;
    color: var(--slate-600);
}

.decline-reason-input {
    padding: 10px 12px;
    border: 1px solid var(--slate-200);
    border-radius: 8px;
    font-size: 13px;
    resize: vertical;
    font-family: inherit;
    transition: border-color 0.15s;
}

.decline-reason-input:focus {
    outline: none;
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
}

.decline-suggest-alt label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--slate-600);
    cursor: pointer;
}

.decline-alt-time-picker {
    display: flex;
    gap: 8px;
    margin-top: 8px;
}

.decline-alt-time-picker input {
    padding: 6px 10px;
    border: 1px solid var(--slate-200);
    border-radius: 6px;
    font-size: 13px;
    font-family: inherit;
}

.decline-panel-actions {
    display: flex;
    gap: 6px;
    padding: 12px 16px;
    border-top: 1px solid rgba(239, 68, 68, 0.15);
    justify-content: flex-end;
}

.decline-panel-cancel-btn {
    background: var(--slate-100);
    color: var(--slate-600);
    border: 1px solid var(--slate-200);
    padding: 6px 14px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
}

.decline-panel-cancel-btn:hover {
    background: var(--slate-200);
}

/* This pair (base red-500 → hover red-600) intentionally differs by one
   ramp step. Tokenizing the base would collapse the hover differentiation,
   so both sides stay hardcoded until --rc-status-busy gains a -strong sibling. */
.decline-panel-confirm-btn {
    background: #ef4444;
    color: white;
    border: none;
    padding: 6px 14px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    transition: all 0.15s;
}

.decline-panel-confirm-btn:hover {
    background: #dc2626;
}

body.dark-mode .decline-panel {
    background: var(--slate-800);
    border-left-color: #f87171;
}

body.dark-mode .decline-panel-header {
    background: linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(220, 38, 38, 0.08) 100%);
    color: #f87171;
}

body.dark-mode .decline-event-summary {
    background: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.2);
}

body.dark-mode .decline-event-summary strong {
    color: var(--slate-100);
}

body.dark-mode .decline-reason-input {
    background: var(--slate-700);
    border-color: var(--slate-600);
    color: var(--slate-100);
}

body.dark-mode .decline-reason-input:focus {
    border-color: #f87171;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
}

/* Unavailable Button */
.unavailable-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: transparent;
    border: 1px solid var(--slate-300);
    border-radius: var(--radius-md);
    color: var(--slate-400);
    cursor: pointer;
    transition: all 0.15s ease;
}

/* Hover/active state uses red-500 (--review-urgent legacy hex) consistently
   to give the action a slightly punchier red than the canonical busy red-600.
   Kept hardcoded; a future --rc-status-busy-soft would replace it. */
.unavailable-btn:hover {
    background: rgba(239, 68, 68, 0.1);
    border-color: #ef4444;
    color: #ef4444;
}

.unavailable-btn.active {
    background: #ef4444;
    border-color: #ef4444;
    color: white;
}

body.dark-mode .unavailable-btn {
    border-color: var(--slate-500);
    color: var(--slate-500);
}

body.dark-mode .unavailable-btn:hover {
    background: rgba(239, 68, 68, 0.2);
    border-color: #f87171;
    color: #f87171;
}

body.dark-mode .unavailable-btn.active {
    background: #dc2626;
    border-color: #dc2626;
    color: white;
}

/* Proposal Option States */
.proposal-option.rank-selected {
    background: var(--primary-50);
    border-color: var(--primary-300);
}

.proposal-option.rank-unavailable {
    background: rgba(239, 68, 68, 0.05);
    border-color: rgba(239, 68, 68, 0.3);
    opacity: 0.7;
}

body.dark-mode .proposal-option.rank-selected {
    background: rgba(20, 184, 166, 0.1);
    border-color: var(--primary-500);
}

body.dark-mode .proposal-option.rank-unavailable {
    background: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.4);
}

/* Vote Summary */
.proposal-vote-summary {
    padding: 6px 10px;
    background: var(--slate-50);
    border-radius: var(--rc-radius-btn);
    margin-bottom: var(--rc-spacing-section);
}

.vote-summary-text {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary);
    font-weight: 500;
    color: var(--primary-600);
}

.vote-summary-text i {
    color: var(--primary-500);
}

.vote-summary-text.vote-summary-empty {
    color: var(--slate-500);
}

.vote-summary-text.vote-summary-empty i {
    display: none;
}

body.dark-mode .proposal-vote-summary {
    background: var(--slate-700);
}

body.dark-mode .vote-summary-text {
    color: var(--primary-400);
}

body.dark-mode .vote-summary-text i {
    color: var(--primary-400);
}

body.dark-mode .vote-summary-text.vote-summary-empty {
    color: var(--slate-400);
}

.option-votes {
    flex: 1;
    display: flex;
    align-items: center;
    gap: 12px;
}

.vote-bar {
    flex: 1;
    height: 8px;
    background: var(--slate-100);
    border-radius: var(--radius-full);
    overflow: hidden;
}

body.dark-mode .vote-bar {
    background: var(--slate-600);
}

.vote-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--primary-400) 0%, var(--primary-500) 100%);
    border-radius: var(--radius-full);
    transition: width var(--transition-slow);
}

.vote-count {
    font-size: 12px;
    font-weight: 600;
    color: var(--slate-500);
    min-width: 40px;
    text-align: right;
}

body.dark-mode .vote-count {
    color: var(--slate-400);
}

/* Collaborative Suggest Time (for collaborative mode polls) */
.proposal-suggest-time {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 0;
    border-top: 1px dashed var(--slate-200);
    margin-top: 8px;
}

body.dark-mode .proposal-suggest-time {
    border-top-color: var(--slate-600);
}

.suggest-time-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 6px 14px;
    border: 1px dashed var(--primary-300);
    border-radius: 6px;
    background: transparent;
    color: var(--primary-600);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s ease;
    font-family: inherit;
}

.suggest-time-btn:hover {
    background: var(--primary-50);
    border-style: solid;
}

body.dark-mode .suggest-time-btn {
    border-color: var(--primary-600);
    color: var(--primary-400);
}

body.dark-mode .suggest-time-btn:hover {
    background: rgba(20, 184, 166, 0.1);
}

.suggest-time-picker {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.suggest-date-input,
.suggest-time-input {
    padding: 8px 12px;
    border: 1px solid var(--slate-300);
    border-radius: var(--radius-md, 8px);
    font-size: 0.8125rem;
    background: white;
    color: var(--slate-800);
    font-family: inherit;
}

.suggest-date-input:focus,
.suggest-time-input:focus {
    outline: none;
    border-color: var(--primary-400);
}

body.dark-mode .suggest-date-input,
body.dark-mode .suggest-time-input {
    background: var(--slate-800);
    border-color: var(--slate-600);
    color: var(--slate-200);
}

.suggest-add-btn,
.suggest-cancel-btn {
    padding: 8px 12px;
    border: none;
    border-radius: var(--radius-md, 8px);
    font-size: 0.8125rem;
    cursor: pointer;
    transition: all 0.15s ease;
    font-family: inherit;
}

.suggest-add-btn {
    background: var(--primary-500);
    color: white;
}

.suggest-add-btn:hover {
    background: var(--primary-600);
}

.suggest-cancel-btn {
    background: var(--slate-200);
    color: var(--slate-600);
}

.suggest-cancel-btn:hover {
    background: var(--slate-300);
}

body.dark-mode .suggest-cancel-btn {
    background: var(--slate-700);
    color: var(--slate-300);
}

body.dark-mode .suggest-cancel-btn:hover {
    background: var(--slate-600);
}

/* Proposal Actions */
.proposal-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding-top: var(--rc-spacing-section);
    border-top: 1px solid var(--slate-200);
}

body.dark-mode .proposal-actions {
    border-color: var(--slate-600);
}

/* A group admin's Schedule/Delete on a teammate's proposal (2026-09-30). */
.proposal-admin-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed var(--slate-200);
}
.proposal-admin-actions .proposal-admin-note {
    flex: 1 1 100%;
    font-size: var(--rc-font-secondary);
    color: var(--slate-600);
}
.proposal-admin-actions .btn { min-height: 44px; }
body.dark-mode .proposal-admin-actions { border-color: var(--slate-600); }
body.dark-mode .proposal-admin-actions .proposal-admin-note { color: var(--slate-300); }

.cant-attend {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--rc-font-secondary);
    color: #ef4444;
    cursor: pointer;
    transition: color 0.2s ease;
}

.cant-attend:hover {
    color: #dc2626;
}

body.dark-mode .cant-attend {
    color: #f87171;
}

body.dark-mode .cant-attend:hover {
    color: #fca5a5;
}

.cant-attend-checkbox {
    width: var(--checkbox-size, 18px);
    height: var(--checkbox-size, 18px);
    accent-color: #ef4444;
}

.submit-vote-btn {
    padding: 6px 16px;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: white;
    background: var(--primary-500);
    border: none;
    border-radius: var(--rc-radius-btn, 6px);
    cursor: pointer;
    transition: all 0.15s ease;
}

.submit-vote-btn:hover {
    background: var(--primary-600);
}

.submit-vote-btn i {
    margin-right: 4px;
    font-size: 11px;
}

.cant-attend i {
    color: #ef4444;
    margin-left: 4px;
}

body.dark-mode .cant-attend i {
    color: #f87171;
}

/* ============================================
   PERMISSION BADGES (Traffic Light Colors)
   ============================================ */
.permission-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: var(--radius-full);
    font-size: 12px;
    font-weight: 600;
}

.permission-badge i {
    font-size: 11px;
}

.permission-teal,
.permission-indigo,
.permission-red {
    background: rgba(20, 184, 166, 0.12);
    color: #0d9488;
    border: 1px solid rgba(20, 184, 166, 0.25);
}

.permission-amber {
    background: rgba(245, 158, 11, 0.12);
    color: #d97706;
    border: 1px solid rgba(245, 158, 11, 0.25);
}

.permission-green {
    background: rgba(34, 197, 94, 0.12);
    color: #16a34a;
    border: 1px solid rgba(34, 197, 94, 0.25);
}

.permission-slate {
    background: rgba(100, 116, 139, 0.12);
    color: #475569;
    border: 1px solid rgba(100, 116, 139, 0.25);
}

/* Dark mode permission badges */
body.dark-mode .permission-teal,
body.dark-mode .permission-indigo,
body.dark-mode .permission-red {
    background: rgba(20, 184, 166, 0.18);
    color: #5eead4;
    border-color: rgba(20, 184, 166, 0.35);
}

body.dark-mode .permission-amber {
    background: rgba(245, 158, 11, 0.18);
    color: #fbbf24;
    border-color: rgba(245, 158, 11, 0.35);
}

body.dark-mode .permission-green {
    background: rgba(34, 197, 94, 0.18);
    color: #4ade80;
    border-color: rgba(34, 197, 94, 0.35);
}

body.dark-mode .permission-slate {
    background: rgba(148, 163, 184, 0.15);
    color: #94a3b8;
    border-color: rgba(148, 163, 184, 0.3);
}

/* ============================================
   MEMBER CHIP — inline person badge with avatar,
   status dot, name, and optional fairness arc
   ============================================ */

.rc-member-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 1px 8px 1px 2px;
    border-radius: 12px;
    background: rgba(100, 116, 139, 0.08);
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-700, #334155);
    line-height: 1;
    vertical-align: middle;
    white-space: nowrap;
    max-width: 220px;
    transition: background 0.15s ease;
}

.rc-member-chip:hover {
    background: rgba(100, 116, 139, 0.14);
}

.rc-member-chip-avatar {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 7px;
    font-weight: 700;
    color: #fff;
    flex-shrink: 0;
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
    position: relative;
}

.rc-member-chip-status {
    position: absolute;
    bottom: -1px;
    right: -1px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    border: 1.5px solid var(--bg-secondary, #fff);
}

/* Status dots derive from the canonical status palette where a token
   exists (working/busy/tentative), so they can't drift from the rest of
   the app. sleep keeps its distinct indigo (no rc-status token for it). */
.rc-member-chip-status.status-working   { background: var(--rc-status-available, #22c55e); }
.rc-member-chip-status.status-extended  { background: var(--rc-status-tentative, #fbbf24); }
.rc-member-chip-status.status-off       { background: var(--rc-status-tentative, #fbbf24); }
.rc-member-chip-status.status-sleep     { background: #a78bfa; }
.rc-member-chip-status.status-busy      { background: var(--rc-status-busy, #ef4444); }
.rc-member-chip-status.status-tentative { background: var(--rc-status-tentative, #f59e0b); }

.rc-member-chip-prefix {
    color: var(--slate-400, #94a3b8);
    font-weight: 400;
}

.rc-member-chip-name {
    font-weight: 600;
    color: var(--slate-700, #334155);
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 130px;
}

.rc-member-chip .admin-inline-badge {
    margin-left: 2px;
}

/* .rc-member-chip-fairness{,-value} removed 2026-09-04. They styled the ring +
   numeral that showed ANOTHER member's balance score in every Review Center
   byline — removed by owner ruling on 2026-09-01 ("showing fairness of others
   throughout my app is not encouraged"), then re-added as collateral by
   a92e5c76, a commit about sleep windows and identity. Dead CSS for a banned
   surface is how the surface grows back, so it goes with the markup. */

/* Dark mode */
body.dark-mode .rc-member-chip {
    background: rgba(148, 163, 184, 0.12);
    color: var(--slate-200, #e2e8f0);
}

body.dark-mode .rc-member-chip:hover {
    background: rgba(148, 163, 184, 0.2);
}

body.dark-mode .rc-member-chip-prefix {
    color: var(--slate-400, #94a3b8);
}

body.dark-mode .rc-member-chip-name {
    color: var(--slate-200, #e2e8f0);
}

body.dark-mode .rc-member-chip-status {
    border-color: var(--bg-secondary, #1e293b);
}

/* Responsive: tighter on small screens */
@media (max-width: 480px) {
    .rc-member-chip {
        max-width: 160px;
        gap: 3px;
        padding: 1px 6px 1px 1px;
    }
    .rc-member-chip-avatar {
        width: 16px;
        height: 16px;
        font-size: 6px;
    }
    .rc-member-chip-name {
        max-width: 90px;
    }
    /* (the mobile .rc-member-chip-fairness svg sizing went with the badge —
       see the removal note above) */
}

/* Danger outline button for Hide action */
.btn-danger-outline {
    color: #dc2626;
    background: transparent;
    border: 1px solid rgba(220, 38, 38, 0.3);
    border-radius: 8px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    transition: all 0.15s ease;
}

.btn-danger-outline:hover {
    background: rgba(220, 38, 38, 0.08);
    border-color: rgba(220, 38, 38, 0.5);
}

body.dark-mode .btn-danger-outline {
    color: #f87171;
    border-color: rgba(248, 113, 113, 0.3);
}

body.dark-mode .btn-danger-outline:hover {
    background: rgba(248, 113, 113, 0.12);
    border-color: rgba(248, 113, 113, 0.5);
}

/* ============================================
   OUTGOING/REQUESTED EVENTS TAB
   ============================================ */
.outgoing-events {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* All accepted message */
.outgoing-all-accepted {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 24px;
    margin-bottom: 24px;
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.05) 100%);
    border: 1px solid rgba(16, 185, 129, 0.2);
    border-radius: var(--radius-xl);
    text-align: center;
}

.all-accepted-icon {
    font-size: 28px;
    color: #10b981;
    margin-bottom: 8px;
}

.all-accepted-text {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--slate-800);
    margin: 0 0 2px 0;
}

.all-accepted-subtext {
    font-size: 0.75rem;
    color: var(--slate-500);
    margin: 0;
}

body.dark-mode .outgoing-all-accepted {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(16, 185, 129, 0.08) 100%);
    border-color: rgba(16, 185, 129, 0.3);
}

body.dark-mode .all-accepted-icon {
    color: #34d399;
}

body.dark-mode .all-accepted-text {
    color: var(--slate-100);
}

body.dark-mode .all-accepted-subtext {
    color: var(--slate-400);
}

/* Outgoing Sections */
.outgoing-section {
    margin-bottom: 24px;
}

.outgoing-section:last-child {
    margin-bottom: 0;
}

.outgoing-section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    margin-bottom: 16px;
    background: var(--slate-50);
    border-radius: var(--radius-lg);
    border-left: 4px solid var(--review-warning);
}

.outgoing-section-pending .outgoing-section-header {
    border-left-color: var(--review-warning);
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.08) 0%, var(--slate-50) 100%);
}

.outgoing-section-accepted .outgoing-section-header {
    border-left-color: var(--review-success);
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, var(--slate-50) 100%);
}

.outgoing-section-header .section-icon {
    font-size: 14px;
    color: var(--review-warning);
}

.outgoing-section-header .section-icon.accepted {
    color: var(--review-success);
}

.outgoing-section-header .section-count {
    background: var(--review-warning);
}

.outgoing-section-accepted .outgoing-section-header .section-count {
    background: var(--review-success);
}

body.dark-mode .outgoing-section-header {
    background: var(--slate-800);
}

body.dark-mode .outgoing-section-pending .outgoing-section-header {
    background: linear-gradient(135deg, rgba(245, 158, 11, 0.12) 0%, var(--slate-800) 100%);
}

body.dark-mode .outgoing-section-accepted .outgoing-section-header {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.12) 0%, var(--slate-800) 100%);
}

.outgoing-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Status badges for outgoing events */
.outgoing-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 8px;
    font-size: var(--rc-font-tiny);
    font-weight: 600;
    border-radius: var(--rc-radius-pill);
    white-space: nowrap;
}

.outgoing-status-badge i {
    font-size: 9px;
}

.outgoing-status-badge.pending {
    color: var(--review-warning);
    background: var(--review-warning-light);
}

.outgoing-status-badge.accepted {
    color: #10b981;
    background: rgba(16, 185, 129, 0.12);
}

.outgoing-status-badge.declined {
    color: #ef4444;
    background: rgba(239, 68, 68, 0.1);
}

body.dark-mode .outgoing-status-badge.pending {
    color: #fbbf24;
    background: rgba(245, 158, 11, 0.15);
}

body.dark-mode .outgoing-status-badge.accepted {
    color: #34d399;
    background: rgba(16, 185, 129, 0.2);
}

body.dark-mode .outgoing-status-badge.declined {
    color: #f87171;
    background: rgba(239, 68, 68, 0.15);
}

.outgoing-event {
    padding: var(--rc-spacing-card);
    background: var(--review-card-bg);
    border: 1px solid var(--review-card-border);
    border-radius: var(--rc-radius-card);
    box-shadow: var(--review-card-shadow);
    margin-bottom: 6px;
}

.outgoing-event.accepted {
    border-left: 3px solid var(--review-success);
}

.outgoing-event.in-review {
    border-left: 3px solid var(--review-warning);
}

/* outgoing-event-header replaced by rc-card-header + rc-title-row */

.outgoing-event .event-title {
    font-size: var(--rc-font-heading);
    font-weight: 600;
    color: var(--slate-800);
}

body.dark-mode .outgoing-event .event-title {
    color: var(--slate-100);
}

.outgoing-event .status-indicator {
    font-size: 0.75rem;
}

/* Outgoing meta line (dot-separated) */
.outgoing-meta-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    font-size: var(--rc-font-secondary);
    color: var(--slate-500);
}

.outgoing-meta-line > span {
    white-space: nowrap;
}

.outgoing-meta-line > span:not(:last-child):not(.outgoing-meta-right)::after {
    content: "\00B7";
    margin: 0 6px;
    color: var(--slate-300);
}

/* Right-aligned zone in outgoing meta line */
.outgoing-meta-right {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    flex-shrink: 0;
}

body.dark-mode .outgoing-meta-line {
    color: var(--slate-400);
}

body.dark-mode .outgoing-meta-line > span:not(:last-child)::after {
    color: var(--slate-600);
}


.recipient-note {
    margin-top: 6px;
    padding: 6px 10px;
    font-size: var(--rc-font-secondary);
    color: var(--slate-600);
    background: var(--slate-50);
    border-radius: var(--rc-radius-btn);
    border-left: 3px solid var(--primary-400);
}

.recipient-note .note-label {
    font-weight: 600;
    color: var(--slate-700);
}

body.dark-mode .recipient-note {
    color: var(--slate-300);
    background: rgba(255, 255, 255, 0.05);
}

body.dark-mode .recipient-note .note-label {
    color: var(--slate-200);
}

/* ============================================
   RESPONSIVE STYLES
   ============================================ */
@media (max-width: 768px) {
    .review-modal .modal-content {
        max-width: 100%;
        min-height: 100%;
        max-height: 100%;
        border-radius: 0;
        margin: 0;
    }

    .review-dashboard-header {
        padding: 12px 14px 10px;
        gap: 8px;
    }

    .review-dashboard-header .rdh-title {
        font-size: 13px;
    }

    .review-dashboard-header .rdh-title i {
        font-size: 14px;
    }

    .review-dashboard {
        padding: 12px;
    }

    .review-tabs {
        gap: 2px;
        padding: 2px;
    }

    .review-tab {
        padding: 5px 8px;
        font-size: 0.7rem;
    }

    .review-item {
        padding: 12px 14px;
    }

    .review-item-header {
        flex-direction: column;
        align-items: flex-start;
    }

    /* STAY HORIZONTAL ON MOBILE (2026-08-16, user: "space inefficient and
       stacked rather than horizontal"). Stacking three mutually-exclusive
       options into a column costs three rows for one question and breaks the
       segmented track into three loose bars. Private/Busy/Shared are short
       enough to share one row at 390px — verified on the live render. */
    .visibility-options {
        flex-direction: row;
    }

    .visibility-btn {
        justify-content: center;
    }
    /* The confirm button is NOT a segment; it still wants its own full line. */
    .visibility-options .rc-vis-apply-btn {
        width: 100%;
    }

    .auto-accept-config {
        flex-direction: column;
        align-items: flex-start;
        gap: 16px;
    }

    .permission-config {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
    }

    .permission-level-select {
        width: 100%;
        min-width: auto;
    }

    .revoke-access-btn {
        justify-content: center;
    }

    .action-buttons {
        flex-direction: column;
    }

}

@media (max-width: 480px) {
    /* 2026-07-12 full-modal redesign: the separate .review-dashboard-header row
       is gone — brand + actions now ride inside the single pinned .rc-tabrow.
       On phones the strip's action cluster (sync · ⋯ · close) hugs the right and
       must not be squeezed by the tabs; the brand text collapses to icon-only to
       save the width. No absolute-pinning needed anymore — the flex row + the
       clipped brand keep everything on one line. */
    .rc-tabrow .rdh-actions {
        margin-left: auto;
        flex-shrink: 0;
    }

    /* NO BRAND MARK ON PHONES (2026-07-30, user: "remove the bell").
       The concierge glyph was pure decoration inside a modal the user just
       opened — they know where they are — and it cost a whole row: at 44px it
       could not share the line with the 48px tab pill, so it wrapped and sat
       alone above the tabs with the rest of the row empty. Screen-reader users
       lose nothing: the <h2> is the modal's aria-labelledby target, so it stays
       in the DOM, visually hidden, with its full "Review Center" text intact
       (previously the TEXT was clipped and only the meaningless glyph showed —
       so this is strictly better for a11y as well as for space). */
    .rc-tabrow .rdh-title {
        position: absolute;
        width: 1px;
        height: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        white-space: nowrap;
        border: 0;
        min-height: 0;
    }
    /* The text is no longer clipped separately — the whole heading is offscreen
       now, so let it read naturally to assistive tech. */
    .rc-tabrow .rdh-title .rdh-title-text {
        position: static;
        width: auto;
        height: auto;
        margin: 0;
        clip: auto;
        clip-path: none;
    }

    .review-item-title {
        font-size: 12px;
    }

    .review-group-header {
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
    }

    .accept-all-btn {
        width: 100%;
    }

    .proposal-header {
        gap: 3px;
    }

    .proposal-title-row {
        gap: 4px;
    }

    .proposal-meta-line {
        font-size: var(--rc-font-caption);
    }

    /* Compact voting options on mobile: stack time + date */
    .proposal-option {
        flex-wrap: wrap;
        gap: 4px 8px;
    }

    .option-date-sep {
        display: none;
    }

    .option-duration {
        margin-left: 0;
    }

    .proposal-actions {
        flex-wrap: wrap;
        justify-content: flex-end;
        gap: 8px;
    }

    /* Review meta line wraps on mobile */
    .review-meta-line {
        font-size: var(--rc-font-caption);
    }

    /* Decline panel full-width on mobile */
    .decline-panel {
        width: 100%;
        max-width: 100%;
        border-left: none;
        border-top: 4px solid #ef4444;
    }

    .decline-panel-overlay {
        align-items: flex-end;
    }

    /* ---- Scheduled tab ---- */
    /* Lay the action row (Accept / Suggest another time / Decline / ⋮More) out
       as an even 2-up grid instead of ragged right-wrapping. The old
       `.action-buttons{flex-direction:column}` rule targeted a class this row
       doesn't use, so the stack never applied here. Direct-child .btn only, so
       the icon trigger nested in .more-options-dropdown stays compact. */
    .review-actions-row {
        justify-content: stretch;
    }
    .review-actions-row > .btn {
        flex: 1 1 calc(50% - 4px);
        justify-content: center;
    }
    /* The ⋮ More trigger stays a compact square at the end of the row. */
    .review-actions-row > .more-options-dropdown {
        flex: 0 0 auto;
    }
    /* Long titles ellipsize within the card instead of overflowing it. The
       title row is allowed to wrap so the right-zone (date/time) drops under
       the title rather than forcing horizontal overflow. */
    .rc-title-row .event-title,
    .rc-title-row .review-item-title,
    .review-item-title {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
    }
    .rc-title-right {
        white-space: normal;
    }
    .trust-indicator {
        flex-wrap: wrap;
    }

    /* ---- Proposals tab ---- */
    /* The fixed-min-width column header (fairness / who's free / votes) can't
       fit beside its label at 390px and de-aligns once option rows wrap, so
       hide it on mobile; each option row already labels its own stats. */
    .proposal-options-header {
        display: none;
    }

    /* ---- Requested tab ---- */
    .my-proposal-title {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
    }
    /* Stats cluster (vote bar + reachable + fairness + N unavailable) wraps
       instead of overflowing the option row. */
    .time-option-stats {
        flex-wrap: wrap;
    }
    /* ⛔THE `left: 0` PHONE OVERRIDE IS GONE (2026-09-23). It existed only to
       pull the absolutely-positioned puck back inside the card edge — but that
       placed it ON the date instead of beside it. The badge is now in flow (see
       `.time-option .rc-standing` above), so it is inside the card and spaced
       from the date at EVERY width, and this rule has nothing left to correct.
       ⛔Do not reintroduce a `left`/`position` here: it would lift the badge
       back out of flow and the collision returns. */
}

/* ============================================
   ACCESSIBILITY
   ============================================ */
@media (prefers-reduced-motion: reduce) {
    .review-btn,
    .review-item,
    .filter-tab,
    .review-tab,
    .visibility-btn,
    .settings-toggle-btn,
    .toggle-slider,
    .toggle-slider::after,
    .action-buttons .btn,
    .accept-all-btn,
    .submit-vote-btn {
        transition: none;
    }

    .review-count-badge.visible,
    .review-count-badge.urgent,
    .review-status-badge.in-review::before,
    .auto-accept-timer.urgent,
    .review-btn.has-items {
        animation: none;
    }
}

/* Focus visible states for keyboard navigation */
.review-btn:focus-visible,
.filter-tab:focus-visible,
.review-tab:focus-visible,
.visibility-btn:focus-visible,
.settings-toggle-btn:focus-visible,
.action-buttons .btn:focus-visible,
.accept-all-btn:focus-visible,
.submit-vote-btn:focus-visible,
/* Previously-unfocusable interactive controls (keyboard users got no ring). */
.review-actions-row .btn:focus-visible,
.conflict-action-btn:focus-visible,
.rank-circle:focus-visible,
.proposal-option:focus-visible,
.dropdown-item:focus-visible,
.sub-tab:focus-visible,
.change-vote-btn:focus-visible,
.schedule-winner-btn:focus-visible,
.view-details-btn:focus-visible,
.cancel-proposal-btn:focus-visible,
.remind-voters-btn:focus-visible,
.undo-accept-btn:focus-visible,
.proposal-description-line:focus-visible,
.timeoff-approve-btn:focus-visible,
.timeoff-reject-btn:focus-visible,
.more-options-btn:focus-visible,
.rc-card-expand-btn:focus-visible,
/* Destructive + sub-flow controls that previously had NO focus ring. */
.revoke-access-btn:focus-visible,
.btn-danger-outline:focus-visible,
.decline-panel-cancel-btn:focus-visible,
.decline-panel-confirm-btn:focus-visible,
.decline-panel-close:focus-visible,
.suggest-time-btn:focus-visible,
.suggest-add-btn:focus-visible,
.suggest-cancel-btn:focus-visible,
.unavailable-btn:focus-visible,
.rc-batch-cb:focus-visible,
.review-retry-btn:focus-visible,
.rc-pd-close:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring-shadow);
}

/* ============================================
   MY PROPOSALS SECTION (Outgoing Tab)
   ============================================ */
.my-proposals-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.my-proposal-card {
    background: var(--review-card-bg);
    border: 1px solid var(--review-card-border);
    border-radius: var(--rc-radius-card);
    padding: var(--rc-spacing-card);
    box-shadow: var(--review-card-shadow);
    transition: all 0.15s ease;
}

.my-proposal-card:hover {
    border-color: rgba(16, 185, 129, 0.3);
    box-shadow: 0 2px 12px rgba(16, 185, 129, 0.08);
}

.my-proposal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-bottom: 4px;
}

.my-proposal-title-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.my-proposal-title {
    font-size: var(--rc-font-heading);
    font-weight: 600;
    color: var(--slate-800);
    line-height: 1.3;
}

body.dark-mode .my-proposal-title {
    color: var(--slate-100);
}

/* Proposed Dates - compact */
.proposed-dates {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    font-size: var(--rc-font-caption);
    font-weight: 600;
    color: #0369a1;
    background: rgba(14, 165, 233, 0.1);
    border: 1px solid rgba(14, 165, 233, 0.25);
    border-radius: var(--rc-radius-pill);
}

.proposed-dates i {
    font-size: 9px;
    color: #0ea5e9;
}

body.dark-mode .proposed-dates {
    color: #38bdf8;
    background: rgba(14, 165, 233, 0.15);
    border-color: rgba(14, 165, 233, 0.35);
}

body.dark-mode .proposed-dates i {
    color: #38bdf8;
}

/* Options count badge */
.proposal-options-count {
    display: inline-flex;
    align-items: center;
    padding: 3px 8px;
    font-size: 0.7rem;
    font-weight: 500;
    color: var(--slate-500);
    background: rgba(148, 163, 184, 0.1);
    border-radius: var(--radius-full);
}

body.dark-mode .proposal-options-count {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.15);
}

.my-proposal-status {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    font-size: var(--rc-font-tiny);
    font-weight: 600;
    border-radius: var(--rc-radius-pill);
    text-transform: uppercase;
    letter-spacing: 0.3px;
    white-space: nowrap;
}

.my-proposal-status.open {
    background: rgba(16, 185, 129, 0.1);
    color: #059669;
    border: 1px solid rgba(16, 185, 129, 0.25);
}

.my-proposal-status.closed {
    background: rgba(100, 116, 139, 0.1);
    color: #64748b;
    border: 1px solid rgba(100, 116, 139, 0.25);
}

body.dark-mode .my-proposal-status.open {
    background: rgba(16, 185, 129, 0.2);
    color: #34d399;
}

body.dark-mode .my-proposal-status.closed {
    background: rgba(100, 116, 139, 0.2);
    color: #94a3b8;
}

.my-proposal-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-bottom: var(--rc-spacing-section);
}

/* Push voting progress badge to the right */
.my-proposal-meta .voting-progress-badge {
    margin-left: auto;
}

/* Compact Voting Progress Badge */
.voting-progress-badge {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--slate-600);
    background: rgba(148, 163, 184, 0.1);
    border: 1px solid rgba(148, 163, 184, 0.2);
    border-radius: var(--radius-full);
    overflow: hidden;
}

.voting-progress-bar {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    background: linear-gradient(90deg, rgba(16, 185, 129, 0.2) 0%, rgba(20, 184, 166, 0.15) 100%);
    border-radius: var(--radius-full);
    transition: width 0.3s ease;
}

.voting-progress-text {
    position: relative;
    z-index: 1;
}

.voting-progress-badge.complete {
    background: rgba(16, 185, 129, 0.1);
    border-color: rgba(16, 185, 129, 0.3);
    color: #059669;
}

.voting-progress-badge.complete .voting-progress-bar {
    background: linear-gradient(90deg, rgba(16, 185, 129, 0.25) 0%, rgba(16, 185, 129, 0.2) 100%);
}

body.dark-mode .voting-progress-badge {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.15);
    border-color: rgba(148, 163, 184, 0.25);
}

body.dark-mode .voting-progress-badge.complete {
    color: #34d399;
    background: rgba(16, 185, 129, 0.15);
    border-color: rgba(16, 185, 129, 0.35);
}

/* Leading Option */
.my-proposal-leading {
    background: rgba(16, 185, 129, 0.06);
    border: 1px solid rgba(16, 185, 129, 0.15);
    border-radius: var(--radius-md);
    padding: 14px;
    margin-bottom: 16px;
}

.leading-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    color: #059669;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    margin-bottom: 8px;
}

.leading-label i {
    margin-right: 6px;
    color: #f59e0b;
}

body.dark-mode .leading-label {
    color: #34d399;
}

.leading-slot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.leading-time {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--slate-800);
}

body.dark-mode .leading-time {
    color: var(--slate-100);
}

.leading-votes {
    font-size: 0.75rem;
    color: var(--slate-600);
    padding: 2px 8px;
    background: rgba(148, 163, 184, 0.1);
    border-radius: var(--radius-full);
}

body.dark-mode .leading-votes {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.2);
}

/* My Proposal Time Options List */
/* ⭐ ONE BOX, NOT THREE (2026-08-06, user: "this layout could be better").
   The time options were a bordered CONTAINER holding bordered ROWS holding a
   bordered TIME CHIP — three nested boxes to render nine words. On a 430px
   phone "Thu 30 Jul" wrapped to two lines beside "1:30 PM (London, UK)" which
   also wrapped, so a single option cost ~200px of height and read as a form
   rather than a list.
   The container is now a plain grouping with no chrome of its own; the row
   below keeps the single border, and the time chip loses its box entirely. */
.my-proposal-times {
    padding: 0;
    margin-bottom: 16px;
}

.times-label {
    display: block;
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--slate-500);
    text-transform: uppercase;
    letter-spacing: 0.3px;
    margin-bottom: 10px;
}

.times-label i {
    margin-right: 6px;
    color: var(--primary-500);
}

body.dark-mode .times-label {
    color: var(--slate-400);
}

.times-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.time-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 10px 12px;
    background: white;
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-md);
    position: relative;
    transition: all 0.15s ease;
}

.time-option:hover {
    border-color: var(--slate-300);
}

.time-option.leading {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.04) 100%);
    border-color: rgba(16, 185, 129, 0.25);
}

body.dark-mode .time-option {
    background: rgba(30, 41, 59, 0.6);
    border-color: var(--slate-600);
}

body.dark-mode .time-option.leading {
    background: linear-gradient(135deg, rgba(16, 185, 129, 0.15) 0%, rgba(16, 185, 129, 0.08) 100%);
    border-color: rgba(16, 185, 129, 0.35);
}

.time-option-datetime {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
}

.time-option-date {
    font-size: 13px;
    font-weight: 600;
    color: var(--slate-700);
}

body.dark-mode .time-option-date {
    color: var(--slate-200);
}

/* The time is the most important token on the row, so it earns WEIGHT — not a
   box. The tinted, bordered chip was the third nested border and it forced the
   long "(London, UK)" suffix to wrap inside its own frame. */
.time-option-time {
    font-size: 14px;
    font-weight: 700;
    color: var(--slate-900);
}
/* Keep date · time on ONE line and let it shrink rather than wrap; the row is
   a scannable list item, not a paragraph. */
.time-option-datetime {
    min-width: 0;
    flex-wrap: nowrap;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* ⚠️THE END TIME IS THE FIRST THING TRUNCATED, AND IT IS THE POINT
   (2026-09-23, owner: "This Area still looks inconsistent", on cards reading
   "9:53 AM (Pineapple)–3:3…" and "4:46 PM (London, UK)–6:…").

   The ellipsis above is right for the ROW, but it ate the wrong end: the date
   label sits FIRST and took its full width, so the clipping always landed on
   the time range — the one value the reader came for. A slot that cannot say
   when it ends is not a slot.

   The time keeps its whole width and the DATE gives way instead: it is the
   repeated, guessable half (every slot in a card shares a day or two), and it
   still shows a readable prefix before its own ellipsis. Nothing wraps, so the
   scannable-list-item intent above is intact. */
.time-option-date {
    white-space: nowrap;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.time-option-time {
    flex: 0 0 auto;   /* never the thing that gets clipped */
}
body.dark-mode .time-option-time { color: var(--slate-100); }

body.dark-mode .time-option-time {
    color: var(--slate-100);
    background: linear-gradient(135deg, rgba(20, 184, 166, 0.15) 0%, rgba(20, 184, 166, 0.08) 100%);
    border-color: rgba(20, 184, 166, 0.3);
}

.time-option-stats {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Per-slot "no" tally for the organiser: how many teammates ruled this time
   out. Calm and neutral (slate) — it informs the scheduling decision without
   shouting; the positive vote bar stays the focal stat. */
.slot-decline-count {
    font-size: 11px;
    color: var(--slate-500);
    padding: 1px 7px;
    border-radius: var(--radius-full);
    background: rgba(148, 163, 184, 0.12);
    white-space: nowrap;
}
body.dark-mode .slot-decline-count {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.18);
}

.time-option-votes {
    font-size: 12px;
    color: var(--slate-500);
    padding: 2px 8px;
    background: rgba(148, 163, 184, 0.1);
    border-radius: var(--radius-full);
}

body.dark-mode .time-option-votes {
    color: var(--slate-400);
    background: rgba(148, 163, 184, 0.2);
}

/* The Sent-by-me time rows carry the standing as a POSITIONED BADGE on the row
   edge, not as inline text — the row's stats already fill the line. Inherited
   verbatim from .leading-indicator (2026-08-06), which this replaced: dropping
   the positioning would have left the medal floating in the stats flow and
   re-wrapping the row on a phone.
   The metal tone comes from .rc-standing--* above; only 1st gets the filled
   gradient puck, because a solid gold disc IS the "winning" signal and three
   competing pucks would flatten it back into decoration. */
/* ⭐THE PUCK SITS IN FLOW (2026-09-23, owner: "the trophy looks odd").
   It was `position: absolute; left: -8px` — lifted OUT of the row, so the date
   never reserved space for it. On desktop it hung 7.3px off the card's left
   edge; a phone rule then pulled it to `left: 0`, which did not fix the class
   of bug, it only moved the puck ON TOP of the date ("🏆Mon 21 Sept", measured
   touching at 0px gap).
   ⛔An absolutely-positioned marker in a flex row is the bug: there is no
   width at which a lifted element and the text beside it are both correct.
   The row is already `display: flex; align-items: center; gap: 8px 12px`, so
   an in-flow badge is spaced by the row's own gap and PUSHES the date aside —
   at every width, with no media query and nothing to keep in sync.
   `order: -1` puts it first visually without moving it in the DOM, so the
   markup keeps the medal next to the vote count it describes (and screen
   readers still read it after the time it qualifies). */
.time-option .rc-standing,
.my-proposal-time .rc-standing {
    order: -1;
    width: 20px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    font-size: 10px;
    flex-shrink: 0;
}
.time-option .rc-standing--gold,
.my-proposal-time .rc-standing--gold {
    /* Amber gradient — gold-medal nod for the leading vote;
       not status, kept as gradient. */
    background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
    color: white;
    box-shadow: 0 2px 6px rgba(245, 158, 11, 0.4);
}
/* Runners-up read as a quiet ring rather than a filled puck. */
.time-option .rc-standing--silver,
.my-proposal-time .rc-standing--silver,
.time-option .rc-standing--bronze,
.my-proposal-time .rc-standing--bronze {
    background: var(--surface-primary, #fff);
    box-shadow: 0 0 0 1px var(--border-color, #e2e8f0);
}
body.dark-mode .time-option .rc-standing--silver,
body.dark-mode .my-proposal-time .rc-standing--silver,
body.dark-mode .time-option .rc-standing--bronze,
body.dark-mode .my-proposal-time .rc-standing--bronze {
    background: var(--slate-800, #1e293b);
    box-shadow: 0 0 0 1px var(--slate-700, #334155);
}

/* My Proposal Actions */
.my-proposal-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    padding-top: var(--rc-spacing-section);
    border-top: 1px solid var(--review-card-border);
}

/* .my-proposal-actions .btn sizing now inherits from .review-panel .btn reset */

.schedule-winner-btn {
    background: #10b981;
    color: white;
    border: none;
}

.schedule-winner-btn:hover {
    background: #059669;
}

.view-details-btn {
    background: rgba(148, 163, 184, 0.08);
    color: var(--slate-600);
    border: 1px solid var(--slate-200);
}

.view-details-btn:hover {
    background: rgba(148, 163, 184, 0.15);
    color: var(--slate-700);
}

body.dark-mode .view-details-btn {
    background: rgba(148, 163, 184, 0.1);
    color: var(--slate-300);
    border-color: var(--slate-600);
}

body.dark-mode .view-details-btn:hover {
    background: rgba(148, 163, 184, 0.2);
    color: var(--slate-200);
}

.cancel-proposal-btn {
    background: transparent;
    color: var(--slate-500);
    border: 1px solid transparent;
}

.cancel-proposal-btn:hover {
    background: rgba(239, 68, 68, 0.08);
    color: #dc2626;
    border-color: rgba(239, 68, 68, 0.2);
}

body.dark-mode .cancel-proposal-btn:hover {
    background: rgba(239, 68, 68, 0.15);
    color: #f87171;
}

/* ============================================
   SCHEDULED EVENTS SECTION
   ============================================ */
.scheduled-events-section {
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.scheduled-events-group {
    background: var(--review-card-bg);
    backdrop-filter: blur(8px);
    border: 1px solid var(--review-card-border);
    border-radius: var(--radius-lg);
    padding: 16px;
}

.events-group-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--slate-600);
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--review-card-border);
}

.events-group-header i {
    color: var(--slate-400);
}

body.dark-mode .events-group-header {
    color: var(--slate-400);
}

.group-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    font-size: 0.7rem;
    font-weight: 700;
    color: white;
    background: var(--slate-500);
    border-radius: var(--radius-full);
    margin-left: auto;
}

/* Empty state subtext */
.empty-subtext {
    font-size: 0.8rem;
    color: var(--slate-400);
    margin-top: 4px;
}

/* ============================================
   SCHEDULER INFO MODAL
   "Who is this?" feature
   ============================================ */

.scheduler-info-modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: var(--z-modal);
    animation: fadeIn 0.2s ease-out;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

.scheduler-info-modal {
    background: var(--review-card-bg, #fff);
    border-radius: var(--radius-xl, 16px);
    box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
    max-width: 420px;
    width: calc(100% - 32px);
    max-height: calc(100vh - 64px);
    overflow-y: auto;
    animation: slideUp 0.3s ease-out;
}

@keyframes slideUp {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.scheduler-info-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--review-card-border, rgba(0,0,0,0.1));
}

.scheduler-info-header h3 {
    margin: 0;
    font-size: 1.1rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-primary, #1e293b);
}

.scheduler-info-header h3 i {
    color: var(--primary-500, #6366f1);
}

.close-scheduler-info {
    background: transparent;
    border: none;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--text-secondary, #64748b);
    transition: background 0.2s, color 0.2s;
}

.close-scheduler-info:hover {
    background: rgba(0, 0, 0, 0.05);
    color: var(--text-primary, #1e293b);
}

.scheduler-info-body {
    padding: 20px;
}

.scheduler-info-section {
    margin-bottom: 20px;
}

.scheduler-info-section:last-child {
    margin-bottom: 0;
}

.scheduler-info-section:first-child {
    display: flex;
    gap: 16px;
    align-items: flex-start;
}

.scheduler-avatar {
    flex-shrink: 0;
}

.scheduler-avatar img {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
    border: 3px solid var(--primary-100, #e0e7ff);
}

.scheduler-avatar-placeholder {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--primary-100, #e0e7ff) 0%, var(--primary-200, #c7d2fe) 100%);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--primary-500, #6366f1);
    font-size: 28px;
}

.scheduler-details {
    flex: 1;
    min-width: 0;
}

.scheduler-name {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text-primary, #1e293b);
    margin-bottom: 4px;
}

.scheduler-email,
.scheduler-org {
    font-size: 0.875rem;
    color: var(--text-secondary, #64748b);
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
}

.scheduler-email i,
.scheduler-org i {
    width: 14px;
    text-align: center;
    color: var(--text-tertiary, #94a3b8);
}

.scheduler-info-section h4 {
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-tertiary, #94a3b8);
    margin: 0 0 8px 0;
}

.scheduler-relationship {
    font-size: 0.9375rem;
    color: var(--text-primary, #1e293b);
    padding: 10px 14px;
    background: var(--primary-50, #eef2ff);
    border-radius: var(--radius-md, 8px);
}

.scheduler-history {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
    gap: 12px;
}

.history-stat {
    text-align: center;
    padding: 12px;
    background: rgba(0, 0, 0, 0.02);
    border-radius: var(--radius-md, 8px);
}

.history-value {
    display: block;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--primary-600, #4f46e5);
    line-height: 1.2;
}

.history-label {
    display: block;
    font-size: 0.75rem;
    color: var(--text-secondary, #64748b);
    margin-top: 4px;
}

.authority-badge {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    background: linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%);
    border: 1px solid rgba(99, 102, 241, 0.2);
    border-radius: var(--radius-md, 8px);
    font-size: 0.875rem;
    color: var(--primary-700, #4338ca);
}

.authority-badge i {
    font-size: 1.25rem;
}

.warning-section {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 14px;
    background: rgba(245, 158, 11, 0.1);
    border: 1px solid rgba(245, 158, 11, 0.25);
    border-radius: var(--radius-md, 8px);
    font-size: 0.875rem;
    color: #92400e;
}

.warning-section i {
    color: #f59e0b;
    flex-shrink: 0;
    margin-top: 2px;
}

.scheduler-info-actions {
    display: flex;
    gap: 8px;
    padding: 12px 16px;
    border-top: 1px solid var(--review-card-border, rgba(0,0,0,0.1));
    background: rgba(0, 0, 0, 0.02);
}

.scheduler-info-actions .btn {
    flex: 0 0 auto;
    padding: 6px 14px;
    font-size: 12px;
    border-radius: 6px;
}

.scheduler-info-actions #block-scheduler {
    flex: 0 0 auto;
    color: #dc2626;
}

.scheduler-info-actions #block-scheduler:hover {
    background: rgba(220, 38, 38, 0.1);
}

/* Dark mode adjustments */
body.dark-mode .scheduler-info-modal {
    background: var(--review-card-bg, #1e293b);
}

body.dark-mode .scheduler-info-header h3 {
    color: var(--text-primary, #f1f5f9);
}

body.dark-mode .close-scheduler-info {
    color: var(--text-secondary, #94a3b8);
}

body.dark-mode .close-scheduler-info:hover {
    background: rgba(255, 255, 255, 0.1);
    color: var(--text-primary, #f1f5f9);
}

body.dark-mode .scheduler-name {
    color: var(--text-primary, #f1f5f9);
}

body.dark-mode .scheduler-relationship {
    background: rgba(99, 102, 241, 0.15);
    color: var(--text-primary, #f1f5f9);
}

body.dark-mode .history-stat {
    background: rgba(255, 255, 255, 0.05);
}

body.dark-mode .warning-section {
    background: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

body.dark-mode .scheduler-info-actions {
    background: rgba(0, 0, 0, 0.2);
}

/* ============================================
   ACTION CONFIRMATION FEEDBACK
   ============================================ */

.review-item.action-complete {
    transition: all 200ms ease-out;
}

.review-item.fade-out {
    opacity: 0;
    max-height: 0;
    margin-bottom: 0;
    padding: 0;
    overflow: hidden;
}

.review-item .action-confirmation {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 10px 14px;
    font-size: 12px;
    font-weight: 500;
    border-radius: 6px;
}

.review-item .action-confirmation i {
    font-size: 12px;
}

.review-item .action-confirmation.accepted {
    background: rgba(34, 197, 94, 0.1);
    color: #166534;
}

.review-item .action-confirmation.declined {
    background: rgba(239, 68, 68, 0.08);
    color: #991b1b;
}

.review-item .action-confirmation.tentative {
    background: rgba(245, 158, 11, 0.1);
    color: #92400e;
}

.review-item .action-confirmation.resolved,
.conflict-pair .action-confirmation.resolved {
    background: rgba(20, 184, 166, 0.1);
    color: #0f766e;
}

/* ============================================
   COMPLETION STATE
   ============================================ */

.review-completion-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    text-align: center;
    animation: reviewFadeIn 200ms ease-out;
}

@keyframes reviewFadeIn {
    from {
        opacity: 0;
        transform: translateY(8px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.review-completion-state .completion-icon {
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(34, 197, 94, 0.1);
    border-radius: 50%;
    margin-bottom: 16px;
}

.review-completion-state .completion-icon i {
    font-size: 24px;
    color: #22c55e;
}

.review-completion-state .completion-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary, #1e293b);
    margin-bottom: 6px;
}

.review-completion-state .completion-stats {
    font-size: 0.75rem;
    color: var(--text-secondary, #64748b);
    margin-bottom: 14px;
}

.review-completion-state .completion-actions {
    display: flex;
    gap: 10px;
    justify-content: center;
}

/* Done is the ONLY action here now (the "Continue to {tab}" link was removed
   2026-08-19 — tabs do the navigating), so it gets a full-size tap target
   instead of the 12px afterthought it was next to the link. */
.review-completion-state .completion-actions .btn {
    padding: 12px 40px;
    font-size: 15px;
    font-weight: 600;
    border-radius: 8px;
    min-height: 44px; /* comfortable touch target */
}

/* Dark mode */
body.dark-mode .review-item .action-confirmation.accepted {
    background: rgba(34, 197, 94, 0.15);
    color: #86efac;
}

body.dark-mode .review-item .action-confirmation.declined {
    background: rgba(100, 116, 139, 0.2);
    color: #cbd5e1;
}

body.dark-mode .review-item .action-confirmation.tentative {
    background: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
}

body.dark-mode .review-item .action-confirmation.resolved,
body.dark-mode .conflict-pair .action-confirmation.resolved {
    background: rgba(20, 184, 166, 0.15);
    color: #5eead4;
}

body.dark-mode .review-completion-state .completion-icon {
    background: rgba(34, 197, 94, 0.15);
}

body.dark-mode .review-completion-state .completion-title {
    color: var(--text-primary, #f1f5f9);
}

body.dark-mode .review-completion-state .completion-stats {
    color: var(--text-secondary, #94a3b8);
}

/* ============================================
   LOADING SKELETON
   ============================================ */

.review-loading {
    padding: 16px;
}

.review-skeleton-row {
    display: flex;
    gap: 8px;
    margin-bottom: 20px;
}

.review-skeleton-tab {
    flex: 1;
    height: 40px;
    border-radius: var(--radius-md, 8px);
    background: linear-gradient(90deg, rgba(203, 213, 225, 0.3) 25%, rgba(203, 213, 225, 0.5) 50%, rgba(203, 213, 225, 0.3) 75%);
    background-size: 200% 100%;
    animation: reviewShimmer 1.5s ease-in-out infinite;
}

.review-skeleton-card {
    height: 80px;
    border-radius: var(--radius-md, 8px);
    margin-bottom: 12px;
    background: linear-gradient(90deg, rgba(203, 213, 225, 0.2) 25%, rgba(203, 213, 225, 0.4) 50%, rgba(203, 213, 225, 0.2) 75%);
    background-size: 200% 100%;
    animation: reviewShimmer 1.5s ease-in-out infinite;
}

.review-skeleton-card.short {
    height: 56px;
    width: 75%;
}

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

body.dark-mode .review-skeleton-tab,
body.dark-mode .review-skeleton-card {
    background: linear-gradient(90deg, rgba(71, 85, 105, 0.3) 25%, rgba(71, 85, 105, 0.5) 50%, rgba(71, 85, 105, 0.3) 75%);
    background-size: 200% 100%;
    animation: reviewShimmer 1.5s ease-in-out infinite;
}

/* ============================================
   ENHANCED EMPTY STATES
   ============================================ */

.review-empty .empty-subtext {
    font-size: 12px;
    color: var(--slate-500, #64748b);
    margin-top: 4px;
    line-height: 1.4;
    max-width: 280px;
    margin-left: auto;
    margin-right: auto;
}

body.dark-mode .review-empty .empty-subtext {
    color: var(--slate-400, #94a3b8);
}
/* Dark mode: slate-300 on the dark empty surface clears AA there. */
body.dark-mode .review-empty .empty-hint {
    color: var(--slate-300, #cbd5e1);
}

/* Single source of truth for the empty-state hint. Was defined twice with
   conflicting, both-failing colours: 11px teal @ opacity .8 (~2.8:1) here and
   12px slate-tertiary (~2.6:1) at :9050. Consolidated to slate-600 @ 12px,
   which clears WCAG AA (~7:1 on white) — no opacity (it re-fails contrast). */
.review-empty .empty-hint {
    font-size: 12px;
    color: var(--slate-600, #475569);
    margin-top: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}

.review-empty .empty-hint i {
    font-size: 12px;
}

/* ============================================
   MOBILE BOTTOM-SHEET BEHAVIOR
   ============================================ */

@media (max-width: 480px) {
    /* 2026-07-21 full-screen redesign: the Review Center was a floating
       bottom-sheet (rounded top, grabber, ~90vh, slide-up over a dimmed
       backdrop). The user's call: make it EDGE-TO-EDGE full-screen like the
       Create-board and Propose modals — one uninterrupted surface, no wasted
       backdrop strip above, no sheet chrome. Match board-modals.css's phone
       rule exactly: fill the viewport, top-aligned, square corners. */
    #review-dashboard-modal {
        align-items: stretch;
        /* Zero the inherited .modal { padding: var(--space-5) } so the surface
           sits flush to all four screen edges. */
        padding: 0;
    }

    #review-dashboard-modal .review-modal-content {
        position: relative;
        width: 100%;
        max-width: 100%;
        /* Full-bleed: fill the viewport height (dvh so the mobile URL bar's
           show/hide doesn't clip the bottom action) rather than a short sheet.
           The ≤768px rule already sets min-height:100% — restate in vh/dvh so
           this beats it and pins the surface to the full screen. */
        min-height: 100vh;
        min-height: 100dvh;
        max-height: 100vh;
        max-height: 100dvh;
        border-radius: 0;
        margin: 0;
        /* Standard modal fade (same as Create/Propose) — no bottom-sheet
           slide-up now that this is a full-screen surface. */
        animation: modalFadeIn 0.2s ease-out;
        padding-bottom: env(safe-area-inset-bottom, 0);
    }

    /* No grabber handle: the ::before pill read as a draggable sheet affordance.
       A full-screen modal has none, so suppress it. */
    #review-dashboard-modal .review-modal-content::before {
        content: none;
    }

    /* Mobile tab row (2026-07-21 wasted-space pass): the old rule stacked
       .rc-tabrow into a COLUMN, so brand / tabs / search+Filters / sync+⋯+×
       each got its own full-width row — five stacked bands of chrome (two of
       them, the right-aligned control + action clusters, wasting the whole
       left half of the screen) before any card showed.

       New layout is a compact TWO-row wrap that fills the width:
         Row 1: [bell]  [ tabs — flex, fill ]  [ sync · ⋯ · × ]
         Row 2: [ 🔍 search ]  [ Filters ▾ ]
       The row-2 controls left-align (their desktop margin-left:auto is dropped
       below) so there's no empty gutter. flex-wrap + `order` do the grouping
       against the fixed DOM order (brand·tabs·triage·controls·actions) without
       any markup change. */
    .rc-tabrow {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        row-gap: 8px;
        column-gap: 8px;
    }
    .rc-tabrow .rdh-title { order: 1; }
    /* flex:1 with basis auto so the tabs eat the whole rest of row 1 — only the
       close (×) shares this row with them (see the display:contents split
       below), which leaves enough width for all three labels IN FULL rather
       than the "Sch… Pro… Pri…" truncation you get when sync+⋯+× all crowd in.
       min-width:0 still lets them ellipsis-shrink as a last resort. */
    /* max-width leaves room for the close button so it can SHARE row 1 rather
       than wrapping below (2026-07-30). With the brand mark removed the tabs had
       the whole row to themselves and their intrinsic width pushed × down onto
       row 2, where it landed first and read as part of the filter cluster. The
       reserved 52px is the 44px button plus the row's 8px column-gap; the tabs
       keep flex:1 so they still fill whatever is left. */
    .review-tabs {
        order: 2;
        /* grow 1 → 0 (2026-09-01 compact ruling): the shrink half is what
           keeps × on row 1 (the track yields width and scrolls); the grow
           half only stretched the grey pill past the last tab. */
        flex: 0 1 auto;
        min-width: 0;
        max-width: calc(100% - 52px);
    }

    /* Split the action cluster so ONLY the close button rides row 1 (top-right,
       standard modal spot) while sync + ⋯ drop to row 2 next to the filters.
       display:contents dissolves .rdh-actions as a box so its three children
       become direct flex items of .rc-tabrow and can be ordered individually.
       (.rdh-overflow keeps its own position:relative, so the ⋯ menu still
       anchors correctly.) */
    .rc-tabrow .rdh-actions { display: contents; }
    .rc-tabrow #close-review-modal { order: 3; margin-left: 0; flex-shrink: 0; }

    /* Row 2: search + Filters (left) then sync + ⋯ (right).

       DEAD-GUTTER FIX (2026-07-30). The controls used to size to their content
       while `margin-left:auto` on the sync button shoved the sync/⋯ pair to the
       right edge — which parked a ~60px hole in the MIDDLE of the row (measured
       at 390px: controls ended at x=255.4, sync began at x=263.3, with nothing
       in between). The row read as two clusters adrift rather than one
       deliberate strip.

       Now `.rc-inline-controls` OWNS the slack (flex-grow:1) and distributes it
       INSIDE itself via space-between, so Search sits hard left, Filters trails
       it, and the growth lands as one honest gap that ends exactly where the
       actions begin. Dropping margin-left:auto lets sync/⋯ butt straight up
       against the controls' right edge at the standard 8px column-gap, so the
       whole row is edge-to-edge with no orphan space. */
    .rc-tabrow .rc-inline-controls {
        order: 4;
        flex: 1 1 auto;
        margin-left: 0;                /* kill the desktop right-push gutter */
        justify-content: flex-start;
        /* TWO READABLE GROUPS (2026-07-30, user: "still looks random and
           cluttered … buttons could have more logical placement"). Row 2 was six
           controls in three different shapes with no grouping, so it scanned as
           a random scatter. Now it reads left-to-right as FIND (search +
           Filters, tight 6px so they bind as one unit) then, after the row's
           8px column-gap, ACT (sync + ⋯). Two groups, one gap between them. */
        gap: 6px;
    }
    /* Uniform 36px circular targets for the icon-only controls so the row is one
       consistent shape language instead of circle/pill/bare-glyph mixed. The
       Filters button keeps its wider pill — it carries a text label, so it is
       legitimately a different control, and that difference now reads as
       meaningful rather than accidental. */
    .rc-tabrow #rdh-sync-btn,
    .rc-tabrow .rdh-overflow-btn,
    .rc-tabrow .rc-search-inline .rc-search-toggle {
        width: 36px;
        min-width: 36px;
        height: 36px;
        border-radius: var(--radius-full, 9999px);
        flex-shrink: 0;
    }
    .rc-tabrow #rdh-sync-btn { order: 5; margin-left: 0; flex-shrink: 0; }
    .rc-tabrow .rdh-overflow { order: 6; flex-shrink: 0; }

    /* The Filters button now lives on the LEFT of row 2, so the popover must
       open rightward (left:0) — the desktop right:0 anchor pushed its left edge
       to ~-77px, clipping Work/Personal/Sort off the screen.

       2026-07-30: `left:0` anchors the panel's LEFT edge to the Filters BUTTON,
       and that button sits at the right end of row 2 — so with min-width:220px
       the panel ran straight off the right edge ("Work" clipped to "W", Sort and
       Date Range cut off; user screenshot). max-width could not save it because
       the overflow came from the anchor POSITION, not the width.

       Anchor it to the MODAL instead of the button: pinned to both edges with a
       12px inset, so it is viewport-width by construction and cannot clip on
       either side no matter where the trigger ends up. `position:fixed` would
       escape the modal's transform context, so stay absolute and let the
       stretched inset do the work. */
    #review-dashboard-modal .rc-filters-panel {
        left: 12px;
        right: 12px;
        width: auto;
        min-width: 0;
        max-width: none;
        /* The panel is a child of the small .rc-filters-menu box, so stretch it
           against the sticky bar rather than that box. With the menu made
           static below, `top` now resolves against the BAR — so drop the sheet
           just under the whole header instead of under the button, which is
           where a full-width panel belongs anyway. */
        position: absolute;
        top: calc(100% + 6px);
    }
    /* Give the popover a positioning context that spans the strip, so the
       left/right insets above resolve against the full width. The ≤480px block
       already un-sticks .rc-sticky-bar (position:static) for the anti-vibration
       fix, so relative is safe here — it restores a containing block without
       re-pinning the bar. */
    #review-dashboard-modal .rc-sticky-bar { position: relative; }
    #review-dashboard-modal .rc-tabrow .rc-filters-menu { position: static; }

    /* THE ⋯ MENU NEEDS THE SAME TREATMENT (2026-08-16, user: "The three dot
       menu in review center on mobile hangs off the page").
       It kept the desktop `right:0` + `min-width:200px` anchored to a 36px
       button, while .rdh-actions is dissolved to `display:contents` here and
       the button's final x-position comes from an `order` value — so a 200px
       panel hung off whichever edge the trigger landed near. Same fix the
       Filters panel got above: anchor to the MODAL, not the button, so it is
       viewport-width by construction and cannot clip on either side. */
    #review-dashboard-modal .rc-tabrow .rdh-overflow { position: static; }
    #review-dashboard-modal .rdh-overflow-menu {
        left: 12px;
        right: 12px;
        width: auto;
        min-width: 0;
        max-width: none;
    }

    .review-tabs {
        overflow-x: visible;   /* no scroll needed: 1 labelled + 3 icon tabs fit */
        gap: 4px;
        /* ⭐SHRINK-WRAP again (owner 2026-09-01 compact ruling). The 2026-07-30
           full-width pill existed so three equal-growing labelled tabs made
           genuine thirds of the row; the equal-shares design is gone (tabs are
           content-sized everywhere now, only the active tab is labelled), so a
           full-width track would just trail empty grey past the last icon tab.
           The base `display:inline-flex` hugs the pills. */
        display: inline-flex;
        width: auto;
    }

    /* FOUR tabs on a phone (2026-08-04: Requests became permanent, so the row
       is Events / Proposals / Privacy / Requests). Four labelled tabs do NOT
       fit 390px — the 2026-06-29 lesson — so only the ACTIVE tab shows its
       label; inactive tabs collapse to icon + count. The icons are the same
       glyph+colour as the launcher strip (ReviewTabIdentity), so an icon-only
       tab is still identifiable, and the active tab always names itself. */
    .review-tab {
        flex: 0 1 auto;
        min-width: 0;
        width: auto;
        min-height: 44px;
        padding: 9px 10px;
        gap: 4px;
        justify-content: center;
    }
    .review-tab.active {
        /* shrink 0 (2026-09-01 morning, owner: "the tabs are broken here" on
           Proposals): the active tab was the row's only shrinkable item and
           measured 20px under a 46px deliberately-untruncatable label. KEPT.
           grow 1 → 0 (2026-09-01 evening, owner: "the tabs are now bloated
           and large comparative to previous"): with the navrow control
           cluster fixed the active tab was stretching into the reclaimed
           space. The active pill is now exactly its label's width; leftover
           row space stays background, and a too-tight row scrolls the track
           (the designed overflow-x fallback). */
        flex: 0 0 auto;
    }

    .review-tab:not(.active) .tab-label {
        display: none;
    }
    .review-tab .tab-icon {
        font-size: 0.85rem;   /* icon is the whole identity when unlabelled */
    }

    .review-tab .tab-label {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 100%;
        font-size: 11.5px;
    }

    /* ⭐2026-08-20 (owner: "Even when selected proposal tabs never read the
       full label. If selected you should see the level. Make the area
       sufficient or remove the icon to give space when clicked").
       The active tab is the ONE tab that must name itself, but it was still
       competing with its own icon for a ~100px box, so "Proposals" ellipsised
       to "Propos…". Take the owner's second option: the SELECTED tab drops
       its icon (its colour + position already identify it, and the icon stays
       on every inactive tab) and gives the whole width to the word, which is
       then explicitly un-truncatable. Inactive tabs are unchanged. */
    .review-tab.active .tab-icon {
        display: none;
    }
    .review-tab.active .tab-label {
        overflow: visible;
        text-overflow: clip;
        max-width: none;
        flex: 0 0 auto;
    }

    /* Sub-tabs touch-friendly */
    .review-sub-tabs .sub-tab {
        min-height: 44px;
        padding: 10px 14px;
    }

    /* Un-stick the tab chrome on mobile (2026-07-06). On a phone the pinned
       .rc-sticky-bar + .review-sub-tabs ate a large slice of the already-short
       bottom-sheet and, stacked with the shrink transition, read as a jittery
       band that fought the content for space. The user's call: let the tab rows
       scroll away with the content ("scrolling up is fine") — they're one short
       swipe from the top anyway. Desktop keeps the pinned behaviour. Also drop
       the scroll-shrink padding/border so the un-pinned bar can't flash chrome
       mid-scroll, and neutralise the sticky bulk-action bar the same way. */
    .rc-sticky-bar,
    .rc-sticky-bar.rc-scrolled,
    .review-sub-tabs,
    .rc-batch-bar-active {
        position: static;
        z-index: auto;
    }
    .rc-sticky-bar.rc-scrolled {
        padding: 10px 18px 0;
        border-bottom-color: transparent;
        box-shadow: none;
    }

    /* ANTI-VIBRATION (2026-07-30). The rule above neutralised the bar's own
       padding/border/shadow, but `.rc-scrolled .rc-tabrow { margin-bottom: 0 }`
       (see ~1542) was left live — and THAT is what still moved the layout. With
       the bar un-stuck to position:static it sits in the scroll flow, so the
       180ms margin-bottom transition changed the content height above the
       viewport on every frame: scrollTop drifted under the finger, re-firing the
       scroll listener, re-testing the threshold, and the top of the screen
       shuddered. Measured live at 390px: bar 120.7px <-> 110.7px, scrollHeight
       1487 <-> 1477.

       Hold the margin at its resting value in BOTH states and drop the
       transition, so the tab row's box is invariant on a phone no matter what
       the scroll handler does. attachScrollShrink() also bails out early at this
       width; this is the belt-and-braces half — it keeps the geometry frozen
       even if the class is applied by some other path (or lingers from a resize
       across the breakpoint). */
    .rc-tabrow,
    .rc-scrolled .rc-tabrow {
        margin-bottom: 10px;
        transition: none;
    }
    /* The tool row (2026-08-04) is a SECOND animated-margin row inside the same
       sticky bar, so it needs the identical freeze — otherwise it reintroduces
       exactly the vibration this block exists to kill, one row lower. Frozen at
       its own phone resting value (8px). */
    .rc-toolrow,
    .rc-scrolled .rc-toolrow {
        margin-bottom: 8px;
        transition: none;
    }
    /* The tool row (2026-08-04) is a SECOND animated-margin row in the same
       sticky bar, so it needs the identical freeze — otherwise it reintroduces
       exactly the vibration this block exists to kill, just one row lower.
       Frozen at its own resting value (8px, set in the phone block below). */
    .rc-toolrow,
    .rc-scrolled .rc-toolrow {
        margin-bottom: 8px;
        transition: none;
    }

    /* Cards: swipe hint. The swipe JS (setupSwipeGestures) toggles these classes
       on the rendered card, which is `.review-item` — the old selectors only
       matched `.review-card`, so swipe-to-accept/decline fired the action with
       ZERO visual feedback (felt broken/unconfirmed). Include `.review-item` so
       the card follows the finger + tints green(right=accept)/red(left=decline). */
    .review-card,
    .review-item {
        transition: transform 0.2s ease, opacity 0.2s ease;
    }

    .review-card.swiping-right,
    .review-item.swiping-right {
        transform: translateX(40px);
        opacity: 0.7;
        background: rgba(34, 197, 94, 0.1);
    }

    .review-card.swiping-left,
    .review-item.swiping-left {
        transform: translateX(-40px);
        opacity: 0.7;
        background: rgba(239, 68, 68, 0.1);
    }

    /* Empty states responsive */
    .review-empty {
        padding: 32px 16px;
    }

    .review-empty .empty-icon {
        font-size: 36px;
    }
}

/* Tablet responsive adjustments (2026-07-21 full-screen redesign).
   The Review Center is now edge-to-edge full-screen across the whole ≤768px
   range — same as the Create-board / Propose modals — instead of the old
   centered 90%/85vh card that floated on a dimmed backdrop. The ≤768px rule
   above already sets max-width:100% + border-radius:0; restate the height in
   vh/dvh here so the surface fills the viewport rather than sitting short. */
@media (min-width: 481px) and (max-width: 768px) {
    #review-dashboard-modal {
        align-items: stretch;
        padding: 0;
    }

    #review-dashboard-modal .review-modal-content {
        width: 100%;                   /* override .review-modal .modal-content's 96% */
        max-width: 100%;
        min-height: 100vh;
        min-height: 100dvh;
        max-height: 100vh;
        max-height: 100dvh;
        border-radius: 0;
        margin: 0;
    }

    /* Compact two-row header (same reflow as the ≤480px block): brand + full
       tab labels + close on row 1; search/Filters + sync/⋯ on row 2. Without
       this the tablet range kept the desktop single-row strip, which at 481-560
       squeezed everything onto one overflowing line. */
    .rc-tabrow {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        row-gap: 8px;
        column-gap: 8px;
    }
    .rc-tabrow .rdh-title { order: 1; }
    /* grow 0 (2026-09-01 compact ruling): shrink keeps the row packing. */
    .review-tabs { order: 2; flex: 0 1 auto; min-width: 0; }
    .rc-tabrow .rdh-actions { display: contents; }
    .rc-tabrow #close-review-modal { order: 3; margin-left: 0; flex-shrink: 0; }
    .rc-tabrow .rc-inline-controls {
        order: 4;
        flex: 1 1 auto;
        margin-left: 0;
        justify-content: flex-start;
    }
    /* margin-left:0, matching the ≤480px block: `auto` here pushed sync/⋯ to the
       right edge while the controls sized to their content, leaving a dead hole
       in the middle of row 2. The controls own the slack instead. */
    .rc-tabrow #rdh-sync-btn { order: 5; margin-left: 0; flex-shrink: 0; }
    .rc-tabrow .rdh-overflow { order: 6; flex-shrink: 0; }

    /* Filters popover opens rightward from its now-left-positioned button and is
       capped to the viewport so it can't spill off either edge. */
    #review-dashboard-modal .rc-filters-panel {
        left: 0;
        right: auto;
        max-width: calc(100vw - 24px);
    }
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
    .review-skeleton-tab,
    .review-skeleton-card {
        animation: none;
    }

    #review-dashboard-modal .review-modal-content {
        animation: none;
    }

    .review-card {
        transition: none;
    }

    /* Suppress the continuously-looping, attention-grabbing animations and
       the tab-crossfade for users who prefer reduced motion. These are
       exactly the effects that motion-sensitivity guidance targets. */
    .proposal-deadline-badge.urgency-high,
    .rc-timer-amber,
    .rc-timer-critical,
    .rc-tab-enter,
    .rc-tab-exit,
    .highlight-pulse {
        animation: none !important;
    }
}

/* ============================================
   VOTING PROGRESS IN PROPOSAL META
   ============================================ */
.proposal-vote-progress {
    font-weight: 600;
    color: var(--teal-600, #0d9488);
}

/* Prominent deadline badge in proposal meta */
.proposal-deadline-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: var(--rc-font-caption, 11px);
    font-weight: 600;
    background: rgba(245, 158, 11, 0.1);
    /* Darker amber text for legibility — see urgency-tomorrow comment. */
    color: #b45309;
    border: 1px solid rgba(245, 158, 11, 0.2);
}

.proposal-deadline-badge.urgency-high {
    background: rgba(239, 68, 68, 0.1);
    color: var(--rc-status-busy);
    border-color: rgba(239, 68, 68, 0.25);
    animation: deadlinePulse 2s ease-in-out infinite;
}

.proposal-deadline-badge.urgency-medium {
    background: rgba(245, 158, 11, 0.1);
    /* Amber-700 strong variant. */
    color: #d97706;
    border-color: rgba(245, 158, 11, 0.25);
}

.proposal-deadline-badge.urgency-low {
    background: rgba(34, 197, 94, 0.08);
    color: var(--rc-status-working);
    border-color: rgba(34, 197, 94, 0.15);
}

@keyframes deadlinePulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

body.dark-mode .proposal-deadline-badge {
    background: rgba(245, 158, 11, 0.15);
    color: #fcd34d;
    border-color: rgba(245, 158, 11, 0.3);
}

body.dark-mode .proposal-deadline-badge.urgency-high {
    background: rgba(239, 68, 68, 0.15);
    color: #f87171;
    border-color: rgba(239, 68, 68, 0.3);
}

body.dark-mode .proposal-deadline-badge.urgency-low {
    background: rgba(34, 197, 94, 0.12);
    color: #86efac;
    border-color: rgba(34, 197, 94, 0.2);
}

body.dark-mode .proposal-vote-progress {
    color: #5eead4;
}

/* Participant voting status row */
.proposal-voters-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    padding: 4px 0;
    font-size: var(--rc-font-caption, 11px);
}

.proposal-voters-row .voter-label {
    color: var(--text-secondary, #64748b);
    font-weight: 500;
}

.voter-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px 6px;
    border-radius: 10px;
    font-size: 10px;
    font-weight: 500;
}

.voter-chip.voted {
    background: rgba(34, 197, 94, 0.1);
    color: #16a34a;
}

.voter-chip.not-voted {
    background: rgba(100, 116, 139, 0.1);
    color: #64748b;
}

body.dark-mode .voter-chip.voted {
    background: rgba(34, 197, 94, 0.15);
    color: #86efac;
}

body.dark-mode .voter-chip.not-voted {
    background: rgba(100, 116, 139, 0.15);
    color: #94a3b8;
}

body.dark-mode .proposal-voters-row .voter-label {
    color: #94a3b8;
}

/* ============================================
   SUGGEST TIME INLINE PICKER
   ============================================ */
.suggest-time-inline {
    margin-top: 8px;
    animation: slideDown 0.2s ease-out;
}

.suggest-time-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.suggest-time-row input[type="date"],
.suggest-time-row input[type="time"] {
    padding: 5px 8px;
    font-size: 12px;
    border: 1px solid var(--slate-300, #cbd5e1);
    border-radius: 6px;
    background: var(--review-card-bg, #fff);
    color: var(--text-primary, #1e293b);
}

.suggest-time-row .btn {
    padding: 5px 10px;
    font-size: 11px;
}

body.dark-mode .suggest-time-row input[type="date"],
body.dark-mode .suggest-time-row input[type="time"] {
    background: var(--slate-700);
    border-color: var(--slate-600);
    color: var(--slate-100);
}

/* ============================================
   HIGHLIGHT PULSE (View Details navigation)
   ============================================ */
.proposal-item.highlight-pulse {
    animation: highlightPulse 1.5s ease-out;
}

@keyframes highlightPulse {
    0% { box-shadow: 0 0 0 0 rgba(20, 184, 166, 0.4); }
    30% { box-shadow: 0 0 0 6px rgba(20, 184, 166, 0.2); }
    100% { box-shadow: 0 0 0 0 rgba(20, 184, 166, 0); }
}

/* ============================================
   REVIEW CARD DESCRIPTION (collapsible)
   ============================================ */
.review-card-description {
    font-size: var(--rc-font-caption, 11px);
    color: #64748b;
    padding: 4px 0;
    line-height: 1.4;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

.review-card-description i {
    margin-right: 4px;
    font-size: 10px;
}

body.dark-mode .review-card-description {
    color: #94a3b8;
}

/* P2.2 — "Why this shift was assigned to me" line. Single source of vocabulary
   is QuotaAssignmentService.formatSelectionReason. Visual weight matches the
   description line so it reads as supporting context, not a new section. */
.review-card-assignment-reason {
    font-size: var(--rc-font-caption, 11px);
    color: var(--text-secondary, #64748b);
    padding: 4px 0;
    line-height: 1.4;
    display: flex;
    align-items: center;
    gap: 4px;
}
.review-card-assignment-reason i {
    margin-right: 2px;
    font-size: 10px;
    opacity: 0.75;
}
body.dark-mode .review-card-assignment-reason {
    color: var(--text-secondary, #94a3b8);
}

/* ============================================
   REVIEW CARD LOCATION (inline in meta line)
   ============================================ */
.review-card-location {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    color: #64748b;
    font-size: var(--rc-font-caption, 11px);
}

.review-card-location i {
    color: #ef4444;
    font-size: 10px;
}

body.dark-mode .review-card-location {
    color: #94a3b8;
}

/* ============================================
   AUTO-ACCEPT BADGE
   ============================================ */
.auto-accept-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    white-space: nowrap;
}

.auto-accept-badge i {
    font-size: 9px;
}

.auto-accept-badge.urgency-high {
    background: rgba(239, 68, 68, 0.12);
    color: #ef4444;
}

.auto-accept-badge.urgency-medium {
    background: rgba(249, 115, 22, 0.12);
    color: #f97316;
}

.auto-accept-badge.urgency-low {
    background: rgba(34, 197, 94, 0.12);
    color: #22c55e;
}

body.dark-mode .auto-accept-badge.urgency-high {
    background: rgba(239, 68, 68, 0.2);
}

body.dark-mode .auto-accept-badge.urgency-medium {
    background: rgba(249, 115, 22, 0.2);
}

body.dark-mode .auto-accept-badge.urgency-low {
    background: rgba(34, 197, 94, 0.2);
}

/* ============================================
   PROPOSAL VOTING CLOSED BADGE
   ============================================ */
.proposal-voting-closed-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    background: rgba(100, 116, 139, 0.12);
    color: #64748b;
    white-space: nowrap;
}

.proposal-voting-closed-badge i {
    font-size: 9px;
}

body.dark-mode .proposal-voting-closed-badge {
    background: rgba(148, 163, 184, 0.15);
    color: #94a3b8;
}

/* ============================================
   PROPOSAL EXPIRED BADGE & STATE
   ============================================ */
.proposal-expired-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    background: rgba(239, 68, 68, 0.1);
    color: var(--rc-status-busy);
    white-space: nowrap;
}

.proposal-expired-badge i {
    font-size: 9px;
}

/* ⭐ ONE STATUS CHIP (2026-08-06). The single treatment that says what state a
   proposal is in. Replaces the expired PILL + coloured DEADLINE + dot-and-label
   row, which all fired together and read as three separate problems. */
.rc-status-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    white-space: nowrap;
}
.rc-status-chip i { font-size: 9px; }
.rc-status-chip--expired {
    background: rgba(239, 68, 68, 0.1);
    color: var(--rc-status-busy);
}
.rc-status-chip--closed {
    background: rgba(100, 116, 139, 0.12);
    color: var(--slate-600);
}
body.dark-mode .rc-status-chip--expired { background: rgba(239, 68, 68, 0.15); color: #f87171; }
body.dark-mode .rc-status-chip--closed  { background: rgba(148, 163, 184, 0.15); color: var(--slate-300); }

/* ⭐ONE SHAPE FOR THE CARD'S STATE (2026-09-23, owner: "This Area still looks
   inconsistent. Redesign", on two Sent-by-me cards photographed together).

   The card's top-right corner answers ONE question - "how long have I got?" -
   but rendered it as two different components: `.rc-status-chip` (a rounded
   red pill) when the proposal had expired, and bare `.proposal-deadline-area`
   text when it had not. Two cards in one list therefore read as two different
   components, which is exactly what the owner photographed. Worse, an expired
   card rendered BOTH, stacking the chip above a deadline line.

   `.rc-state-chip` is that one shape. The markup now puts EITHER the status
   chip OR the deadline inside it (review-dashboard.js, both the voter card and
   the Sent-by-me card), so the corner is always a pill of the same size and
   radius. When a real .rc-status-chip is nested inside it, this wrapper must
   add NO chrome of its own - the chip already carries its own background - so
   the padding/background here apply only when the wrapper holds plain text.

   ⛔Do not give this a background unconditionally: that double-pills the
   expired state, the exact stacking this replaced. */
.proposal-deadline-area.rc-state-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    white-space: nowrap;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
}
/* Only when the wrapper is carrying the deadline itself (no nested chip) does
   it wear the pill's own padding + tint. :has() is supported everywhere the
   app ships; the :not() form degrades to "no tint", never to a double pill. */
.proposal-deadline-area.rc-state-chip:not(:has(.rc-status-chip)) {
    padding: 2px 8px;
    background: rgba(100, 116, 139, 0.10);
}
.proposal-deadline-area.rc-state-chip:not(:has(.rc-status-chip)).urgency-high {
    /* the urgency nudge keeps its voice, now as the chip's own tint */
    background: rgba(239, 68, 68, 0.10);
}
.proposal-deadline-area.rc-state-chip .proposal-deadline-value,
.proposal-deadline-area.rc-state-chip .proposal-deadline-label {
    font-size: inherit;
    font-weight: inherit;
}
body.dark-mode .proposal-deadline-area.rc-state-chip:not(:has(.rc-status-chip)) {
    background: rgba(148, 163, 184, 0.15);
}
body.dark-mode .proposal-deadline-area.rc-state-chip:not(:has(.rc-status-chip)).urgency-high {
    background: rgba(239, 68, 68, 0.18);
}

/* Once a proposal has ended, the deadline is a DATE, not an alarm. Urgency
   colour is a nudge to act; there is nothing left to act on. */
.proposal-deadline-area.urgency-past,
.proposal-deadline-area.urgency-past .proposal-deadline-value {
    color: var(--slate-500);
    font-weight: 500;
}
body.dark-mode .proposal-deadline-area.urgency-past,
body.dark-mode .proposal-deadline-area.urgency-past .proposal-deadline-value {
    color: var(--slate-400);
}

.proposal-expired {
    position: relative;
}

/* The card was dimmed to 0.65 AND its times to 0.5 — compounding to ~0.33, so
   the one thing a user still needs from an expired proposal (WHEN was it for)
   was the least readable text on screen. The status chip already says it has
   ended; the content should stay legible. Interaction stays disabled. */
.proposal-expired .proposal-options-section,
.proposal-expired .my-proposal-times {
    pointer-events: none;
}

body.dark-mode .proposal-expired-badge {
    background: rgba(239, 68, 68, 0.15);
    color: #f87171;
}

/* ============================================
   PROPOSAL CREATED TIME
   ============================================ */
.proposal-created-time {
    color: #94a3b8;
    font-size: var(--rc-font-caption, 11px);
    font-style: italic;
}

body.dark-mode .proposal-created-time {
    color: #64748b;
}

/* ============================================
   TIME-OFF REQUEST CARDS
   ============================================ */
/* Time-off cards now render as <li class="timeoff-card review-item">, so the
   .review-item chassis (bg / border / radius / padding / hover lift) governs
   the shell uniformly with the other review cards. Only the list spacing is
   timeoff-specific here. */
.timeoff-card {
    margin-bottom: 8px;
}

/* Time-off header row */
.timeoff-header {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.timeoff-type-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: var(--rc-radius-pill, 12px);
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    background: color-mix(in srgb, var(--timeoff-color) 12%, transparent);
    color: var(--timeoff-color);
    white-space: nowrap;
}

.timeoff-type-badge i {
    font-size: 11px;
}

body.dark-mode .timeoff-type-badge {
    background: color-mix(in srgb, var(--timeoff-color) 20%, transparent);
}

.timeoff-member-name {
    font-size: var(--rc-font-body, 13px);
    font-weight: 600;
    color: #1e293b;
    flex: 1;
}

body.dark-mode .timeoff-member-name {
    color: #f1f5f9;
}

.timeoff-actions {
    display: flex;
    gap: 6px;
    margin-left: auto;
}

/* .timeoff-actions .btn sizing now inherits from .review-panel .btn reset */

/* Your own request (2026-09-29): why the buttons are withheld, or the
   only-admin note. Full width under the buttons so it wraps at 412px. */
.timeoff-actions-bar:has(.timeoff-self-note) {
    flex-wrap: wrap;
}

.timeoff-self-note {
    flex-basis: 100%;
    margin: 6px 0 0;
    font-size: var(--rc-font-caption, 12px);
    color: #475569;
}

body.dark-mode .timeoff-self-note {
    color: #cbd5e1;
}

.timeoff-actions .btn[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
}

/* Time-off meta line */
.timeoff-meta-line {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 6px;
    font-size: var(--rc-font-caption, 11px);
    color: #64748b;
}

.timeoff-meta-line > span::before {
    content: '·';
    margin-right: 8px;
    color: #cbd5e1;
}

.timeoff-meta-line > span:first-child::before {
    display: none;
}

body.dark-mode .timeoff-meta-line {
    color: #94a3b8;
}

body.dark-mode .timeoff-meta-line > span::before {
    color: #475569;
}

/* Time-off coverage warning */
.timeoff-coverage {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    padding: 6px 10px;
    background: rgba(249, 115, 22, 0.08);
    border-radius: 6px;
    font-size: var(--rc-font-caption, 11px);
    color: #f97316;
}

.timeoff-coverage i {
    font-size: 11px;
    flex-shrink: 0;
}

body.dark-mode .timeoff-coverage {
    background: rgba(249, 115, 22, 0.15);
    color: #fb923c;
}

/* Time-off reject reason panel */
.timeoff-reject-reason {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid #e2e8f0;
}

.timeoff-reject-reason .review-note-input {
    flex: 1;
}

.timeoff-reject-reason .btn {
    padding: 4px 12px;
    font-size: var(--rc-font-secondary, 12px);
    white-space: nowrap;
}

body.dark-mode .timeoff-reject-reason {
    border-top-color: #334155;
}

/* ============================================
   OUTGOING AUTHORITY BADGE
   ============================================ */
.outgoing-authority-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 8px;
    border-radius: 10px;
    font-size: var(--rc-font-tiny, 10px);
    font-weight: 600;
    background: rgba(59, 130, 246, 0.1);
    color: #3b82f6;
    white-space: nowrap;
}

.outgoing-authority-badge i {
    font-size: 9px;
}

body.dark-mode .outgoing-authority-badge {
    background: rgba(59, 130, 246, 0.2);
    color: #60a5fa;
}

/* ============================================
   UX IMPROVEMENTS: TAB DIMMING
   ============================================ */
/* 2026-09-21, owner: "Invisible tabs when entering review center from invite
   icon." The dimming is RELATIVE emphasis — it exists to push one or two quiet
   tabs behind the busy ones. Entering from the invite icon (and any signed-out
   view) every count is zero, so EVERY tab but the active one earned .tab-empty
   and the whole strip faded to 0.45 opacity + 40% grayscale: the navigation
   disappeared and only the active pill read. Measured on the real sheet — four
   of five tabs ghosted.

   Two corrections, both keeping the original intent where it still applies:
     - the floor rises to 0.72. An unvisited tab should read as QUIETER, never
       as disabled or absent, and 0.45 on the pale strip is below legible.
     - when nothing is emphasised, nothing is de-emphasised. `.review-tabs`
       carries .all-empty (written by refreshTabBadges) once no tab has a
       count, and the dimming is suspended wholesale — a strip with no work in
       it is a normal strip, not a greyed-out one. */
.review-tab.tab-empty {
    opacity: 0.72;
    filter: grayscale(25%);
    transition: opacity 0.2s ease, filter 0.2s ease;
}
.review-tab.tab-empty:hover {
    opacity: 1;
    filter: none;
}
.review-tabs.all-empty .review-tab.tab-empty {
    opacity: 1;
    filter: none;
}

/* ============================================
   SEARCH / FILTER / SORT BAR
   ============================================ */
.rc-search-filter-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    flex-wrap: wrap;
    /* Animatable collapse for the compact state (see .rc-scrolled rules). */
    overflow: hidden;
    transition: max-height 0.2s ease, opacity 0.15s ease, margin-bottom 0.2s ease;
}

/* ---- Compact-state search toggle (Phase D) -------------------------------
   At the top of the list the full search row is shown and this toggle is
   hidden. Once the sticky bar shrinks (.rc-scrolled) the row collapses and the
   toggle takes its place — a single icon that expands the row inline on click.
   The toggle sits in the sticky bar's top-right, aligned with the tab row. */
.rc-search-toggle {
    display: none;               /* shown only in compact state */
    position: absolute;
    top: 6px;
    right: 16px;
    width: 30px;
    height: 30px;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    background: var(--surface-primary, #fff);
    color: var(--slate-500, #64748b);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.rc-search-toggle:hover {
    color: var(--slate-700, #334155);
    border-color: var(--slate-300, #cbd5e1);
}
.rc-search-toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
.rc-search-toggle .rc-filter-dot {
    position: absolute;
    top: 4px;
    right: 4px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--primary-500, #14b8a6);
    box-shadow: 0 0 0 2px var(--surface-primary, #fff);
}
.rc-search-toggle.rc-filter-on {
    color: var(--primary-600, #0d9488);
    border-color: var(--cmx-0fde834ff0);
    border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent);
}

/* ---- Mobile "Filters" toggle (2026-06-29 de-intimidation) ----------------
   On a phone the Sort + Date dropdowns (power-user controls defaulting to
   Urgency / All dates) tuck behind this toggle so the at-rest surface is just
   search → content. On wider screens the toggle is hidden and the dropdowns
   show inline as before, so this is mobile-only progressive disclosure.
   DESKTOP DEFAULT: toggle hidden, dropdowns always visible. */
.rc-filters-toggle { display: none; }
.rc-filter-dropdowns { display: contents; }   /* dropdowns flow in the bar as before on desktop */

/* COMPACT: collapse the full search row and surface the toggle. */
.rc-scrolled .rc-search-filter-bar {
    max-height: 0;
    opacity: 0;
    margin-bottom: 0;
    pointer-events: none;
}
.rc-scrolled .rc-search-toggle {
    display: inline-flex;
}
/* COMPACT + EXPANDED: user tapped the toggle — reveal the row inline again. */
.rc-scrolled.rc-search-expanded .rc-search-filter-bar {
    max-height: 220px;
    opacity: 1;
    margin-bottom: 10px;
    pointer-events: auto;
}
.rc-scrolled.rc-search-expanded .rc-search-toggle {
    color: var(--primary-600, #0d9488);
    border-color: var(--cmx-cbab6ebede);
    border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent);
    background: var(--cmx-8cf97f5530);
    background: color-mix(in srgb, var(--primary-500, #14b8a6) 8%, #fff);
}

body.dark-mode .rc-search-toggle {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .rc-search-toggle:hover {
    color: var(--slate-200, #e2e8f0);
    border-color: var(--slate-600, #475569);
}
body.dark-mode .rc-search-toggle .rc-filter-dot {
    box-shadow: 0 0 0 2px var(--slate-800, #1e293b);
}
.rc-search-input-wrap {
    position: relative;
    flex: 1;
    min-width: 160px;
}
.rc-search-icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-tertiary, #94a3b8);
    font-size: 12px;
    pointer-events: none;
}
.rc-search-input {
    width: 100%;
    padding: 7px 30px 7px 30px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
    background: var(--bg-secondary, #f8fafc);
    font-size: 13px;
    color: var(--text-primary, #1e293b);
    outline: none;
    transition: border-color 0.15s;
}
.rc-search-input:focus {
    border-color: var(--primary-color, #3b82f6);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}
.rc-search-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-tertiary, #94a3b8);
    cursor: pointer;
    padding: 4px;
    font-size: 12px;
}
.rc-search-clear:hover {
    color: var(--text-primary, #1e293b);
}
/* Custom dropdown that mirrors .boards-dropdown chrome
   (see css/quota-assignment.css:144). Replaces the previous native
   .rc-sort-select / .rc-date-filter <select> elements. */
.rc-dropdown {
    position: relative;
    display: inline-block;
}

.rc-dropdown-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
    background: var(--bg-secondary, #f8fafc);
    font-size: 12px;
    color: var(--text-primary, #1e293b);
    cursor: pointer;
    font-family: inherit;
}

.rc-dropdown-trigger:hover {
    border-color: var(--slate-300, #cbd5e1);
}

.rc-dropdown-trigger:focus-visible {
    outline: 2px solid var(--review-accent, var(--primary-500, #14b8a6));
    outline-offset: 2px;
}

.rc-dropdown-trigger[aria-expanded="true"] {
    border-color: var(--review-accent, var(--primary-500, #14b8a6));
}

.rc-dropdown-chevron {
    font-size: 10px;
    color: var(--slate-500, #64748b);
    transition: transform 0.15s ease;
}

.rc-dropdown-trigger[aria-expanded="true"] .rc-dropdown-chevron {
    transform: rotate(180deg);
}

.rc-dropdown-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 180px;
    background: var(--surface-primary, #fff);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.12);
    z-index: calc(var(--z-dropdown) + 40);
    padding: 4px 0;
    display: flex;
    flex-direction: column;
}

.rc-dropdown-menu[hidden] {
    display: none;
}

.rc-dropdown-option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    border: none;
    background: none;
    width: 100%;
    text-align: left;
    font-family: inherit;
    font-size: 13px;
    color: var(--slate-700, #334155);
    cursor: pointer;
    transition: background 0.12s ease;
}

.rc-dropdown-option:hover,
.rc-dropdown-option:focus-visible {
    background: var(--slate-50, #f8fafc);
    outline: none;
}

.rc-dropdown-option.active {
    color: var(--primary-700, #0f766e);
    font-weight: 600;
}

.rc-dropdown-tick {
    margin-left: auto;
    font-size: 10px;
    color: var(--review-accent, var(--primary-500, #14b8a6));
}

body.dark-mode .rc-search-input {
    background: var(--dark-bg-secondary, #1e293b);
    border-color: var(--dark-border, #334155);
    color: var(--dark-text, #e2e8f0);
}

body.dark-mode .rc-dropdown-trigger {
    background: var(--dark-bg-secondary, #1e293b);
    border-color: var(--dark-border, #334155);
    color: var(--dark-text, #e2e8f0);
}

body.dark-mode .rc-dropdown-menu {
    background: var(--surface-secondary, #1e293b);
    border-color: var(--slate-700, #334155);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.4);
}

body.dark-mode .rc-dropdown-option {
    color: var(--slate-200, #e2e8f0);
}

body.dark-mode .rc-dropdown-option:hover,
body.dark-mode .rc-dropdown-option:focus-visible {
    background: rgba(255, 255, 255, 0.05);
}

/* ============================================================================
   TAB-ROW INLINE CONTROLS (2026-07-11 chrome slim-down)
   Search + all filters moved out of their own full-width band and onto the
   RIGHT of the tab row. A magnifier expands to an inline input; a single
   "Filters ▾" popover holds the calendar filter + sort + date range. This
   reclaims a whole vertical band and consolidates every list filter into one
   affordance. Overrides the old absolute-positioned .rc-search-toggle.
   ============================================================================ */
.rc-inline-controls {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;               /* sit opposite the tabs on the same row */
    flex-shrink: 0;
}
/* When the triage strip is present it already took margin-left:auto; keep the
   controls tight to it rather than pushing them off-row. */
.rc-triage-strip + .rc-inline-controls { margin-left: 8px; }

/* ---- Inline search (collapses to an icon) -------------------------------- */
.rc-search-inline {
    display: inline-flex;
    align-items: center;
    position: relative;
}
/* The magnifier toggle. Reset the legacy absolute positioning that placed it
   in the sticky bar's top-right corner — it's an inline button now. */
.rc-search-toggle {
    display: inline-flex;
    position: static;
    top: auto;
    right: auto;
    width: 32px;
    height: 32px;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    background: var(--surface-primary, #fff);
    color: var(--slate-500, #64748b);
    cursor: pointer;
    flex-shrink: 0;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.rc-search-toggle:hover {
    color: var(--slate-700, #334155);
    border-color: var(--slate-300, #cbd5e1);
}
.rc-search-toggle:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
.rc-search-toggle .rc-filter-dot {
    position: absolute;
    top: 2px;
    right: 2px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--primary-500, #14b8a6);
    box-shadow: 0 0 0 2px var(--surface-primary, #fff);
}
.rc-search-toggle.rc-filter-on {
    color: var(--primary-600, #0d9488);
    border-color: var(--cmx-0fde834ff0);
    border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent);
}
/* The input is 0-width at rest and animates open when .rc-search-open is set.
 *
 * AN INVISIBLE 160px CLICK-TRAP (2026-08-11 button sweep).
 *
 * An EARLIER `.rc-search-input-wrap` rule (see the toolbar block above) sets
 * `flex: 1; min-width: 160px`. min-width is resolved AFTER max-width in the
 * CSS box algorithm, so it wins outright: the collapsed wrapper measured 160px
 * wide with max-width:0 and opacity:0 still applied. The result was a
 * fully-transparent, 160px-wide, still-hit-testable strip sitting permanently
 * in the toolbar, and a search input that measured 220px while the toggle
 * reported aria-expanded="false".
 *
 * Symptom as reported: the magnifier button "does nothing". It was toggling
 * the class correctly and focusing the input each time — the input simply
 * never hid, so pressing it twice looked identical to pressing it never.
 *
 * Reset min-width and flex here, where the collapse intent lives, rather than
 * editing the toolbar rule (which is load-bearing for the EXPANDED layout and
 * for the mobile override at ~13160). `flex-basis: 0` stops flex:1 re-inflating
 * the box once min-width no longer floors it. */
.rc-search-input-wrap {
    position: relative;
    overflow: hidden;
    max-width: 0;
    min-width: 0;
    flex: 0 1 0;
    opacity: 0;
    margin-left: 0;
    /* Not hit-testable while collapsed: an opacity:0 box still takes clicks. */
    pointer-events: none;
    transition: max-width 0.2s ease, opacity 0.15s ease, margin-left 0.2s ease;
}
.rc-search-inline.rc-search-open .rc-search-input-wrap {
    max-width: 240px;
    min-width: 160px;
    flex: 1 1 auto;
    opacity: 1;
    margin-left: 6px;
    pointer-events: auto;
}
.rc-search-input {
    width: 220px;
    max-width: 46vw;
    padding: 7px 28px 7px 12px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    background: var(--bg-secondary, #f8fafc);
    font-size: 13px;
    color: var(--text-primary, #1e293b);
    outline: none;
    transition: border-color 0.15s;
}
.rc-search-input:focus {
    border-color: var(--primary-color, #3b82f6);
    box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
}

/* ---- "Filters ▾" popover ------------------------------------------------- */
.rc-filters-menu { position: relative; display: inline-block; }
.rc-filters-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    position: relative;
    padding: 6px 10px;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: var(--radius-full, 9999px);
    background: var(--surface-primary, #fff);
    color: var(--slate-600, #475569);
    font-size: 12px;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.rc-filters-btn:hover { border-color: var(--slate-300, #cbd5e1); color: var(--slate-800, #1e293b); }
.rc-filters-btn:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
.rc-filters-btn[aria-expanded="true"] { border-color: var(--review-accent, var(--primary-500, #14b8a6)); }
.rc-filters-btn.rc-filter-on { color: var(--primary-600, #0d9488); border-color: var(--cmx-3eaf6fd92a); border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent); }
.rc-filters-btn-chevron { font-size: 10px; transition: transform 0.15s ease; }
.rc-filters-btn[aria-expanded="true"] .rc-filters-btn-chevron { transform: rotate(180deg); }
.rc-filters-btn .rc-filter-dot {
    position: absolute; top: 3px; right: 5px;
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--primary-500, #14b8a6);
    box-shadow: 0 0 0 2px var(--surface-primary, #fff);
}
/* ════════════════════════════════════════════════════════════════════════
   FILTERS POPOVER (rebuilt 2026-08-29, user: "complete the filters ui to be
   high quality and consistent with my ui")
   ────────────────────────────────────────────────────────────────────────
   What was wrong, measured on the real render:
     • The three category icons rendered BLACK. Their colour rules were all
       scoped to `.review-dashboard-header`, a wrapper this renderer stopped
       emitting — so every per-category treatment (work=blue, personal=pink,
       all=teal) was dead CSS the popover never inherited.
     • The active segment came out BLUE against a TEAL button and TEAL count
       badge: two accent families in one control.
     • "Sort: Urgency" sat under a section label reading "SORT".
     • The button promised "2 active" with no way to get back to none.
     • 220px min-width + 12px padding read as a utility menu, not as part of
       the same design language as the rest of the Review Center's surfaces.

   The panel now matches the app's other floating surfaces (.rdh-overflow-menu,
   .rc-dropdown-menu): same radius family, same elevation, same 1px slate
   hairline, and ONE accent — teal — for every "this is chosen" state.
   ════════════════════════════════════════════════════════════════════════ */
.rc-filters-panel {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 30;
    /* Wide enough that "This month" and "Personal" never wrap, which is what
       made the old panel feel cramped at 220px. */
    min-width: 268px;
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--surface-primary, #fff);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 14px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.14);
}
.rc-filters-panel[hidden] { display: none; }

/* Head: what this is, and the way out. */
.rc-filters-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 8px 8px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--slate-100, #f1f5f9);
}
.rc-filters-panel-title {
    font-size: 12px;
    font-weight: 700;
    color: var(--text-primary, #0f172a);
    letter-spacing: -0.01em;
}
/* Quiet until wanted: a text button, not a competing filled control. Teal on
   hover so it belongs to the same accent as everything else in here. */
.rc-filters-clear {
    border: none;
    background: none;
    padding: 3px 7px;
    margin: -3px -4px -3px 0;
    border-radius: 6px;
    font: inherit;
    font-size: 11px;
    font-weight: 600;
    color: var(--slate-500, #64748b);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.rc-filters-clear:hover {
    background: rgba(20, 184, 166, 0.1);
    color: var(--primary-600, #0d9488);
}
.rc-filters-clear:focus-visible {
    outline: 2px solid var(--primary-500, #14b8a6);
    outline-offset: 1px;
}

.rc-filters-section {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 6px 8px 8px;
}
.rc-filters-section + .rc-filters-section { padding-top: 2px; }
/* CONTRAST, MEASURED (2026-08-29). At --slate-400 on white this label came out
   at 2.56:1 — below 4.5:1, and it is 10px uppercase, i.e. the hardest text in
   the panel to read. It is a real label naming the control beneath it, not
   decoration, so it gets a legible tone. --slate-500 lands at 4.76:1. */
.rc-filters-section-label {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--slate-500, #64748b);
}

/* ── Calendar segmented control ─────────────────────────────────────────
   A real segmented track: one recessed rail, three equal segments, the
   chosen one lifting out as a white card. Same idiom as the visibility
   track on the privacy rows, so "pick one of N" looks the same everywhere. */
.rc-filters-panel .rdh-filters {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: var(--slate-100, #f1f5f9);
    border-radius: var(--radius-full, 9999px);
}
.rc-filters-panel .rdh-filters .filter-tab {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    gap: 5px;
    padding: 6px 8px;
    font-size: 12px;
    font-weight: 500;
    /* --slate-500 measured 4.34:1 against the slate-100 RAIL (not against the
       panel) — the recessed track darkens the background, so the unselected
       segments need a step darker than they would on white. --slate-600 clears
       4.5:1 on the rail. */
    color: var(--slate-600, #475569);
    background: transparent;
    border: 1px solid transparent;  /* reserve the active ring's 1px */
    border-radius: var(--radius-full, 9999px);
    white-space: nowrap;
}
.rc-filters-panel .rdh-filters .filter-tab i {
    font-size: 10.5px;
    /* Muted at rest so the row reads as one control, not three icons. */
    color: var(--slate-400, #94a3b8);
    transition: color 0.15s ease;
}
.rc-filters-panel .rdh-filters .filter-tab:hover:not(.active) {
    color: var(--slate-700, #334155);
    background: rgba(255, 255, 255, 0.72);
}
.rc-filters-panel .rdh-filters .filter-tab:hover:not(.active) i {
    color: var(--slate-500, #64748b);
}
/* ONE ACCENT. The chosen segment is a lifted white card with teal type — the
   same "selected" vocabulary as the staged visibility chip and the Clear all
   hover. The old per-category blue/pink actives are deliberately NOT revived
   here: three different active colours inside a control whose own button is
   teal is what made this popover feel unfinished. Category identity still
   lives in the ICON, which is the part that means "work" or "personal". */
.rc-filters-panel .rdh-filters .filter-tab.active {
    color: var(--primary-700, #0f766e);
    background: var(--surface-primary, #fff);
    border-color: rgba(20, 184, 166, 0.28);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
    font-weight: 600;
}
.rc-filters-panel .rdh-filters .filter-tab.active i {
    color: var(--primary-600, #0d9488);
}
.rc-filters-panel .rdh-filters .filter-tab:focus-visible {
    outline: 2px solid var(--primary-500, #14b8a6);
    outline-offset: 1px;
}

/* ── Sort / Date dropdowns ──────────────────────────────────────────────
   Full-width triggers so the two rows align into a column, and the open menu
   covers exactly the trigger it belongs to. */
.rc-filters-panel .rc-dropdown { display: block; }
.rc-filters-panel .rc-dropdown-trigger {
    width: 100%;
    justify-content: space-between;
    padding: 8px 10px;
    border-radius: 9px;
    font-size: 12px;
    font-weight: 500;
    background: var(--surface-primary, #fff);
    border-color: var(--slate-200, #e2e8f0);
}
.rc-filters-panel .rc-dropdown-trigger:hover {
    border-color: var(--primary-300, #5eead4);
    background: rgba(20, 184, 166, 0.04);
}
.rc-filters-panel .rc-dropdown-menu {
    left: 0;
    right: auto;
    width: 100%;
    border-radius: 10px;
}

body.dark-mode .rc-filters-panel {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
body.dark-mode .rc-filters-panel-head { border-bottom-color: var(--slate-700, #334155); }
body.dark-mode .rc-filters-panel-title { color: var(--slate-100, #f1f5f9); }
/* MEASURED on the dark panel (#1e293b): --slate-400 gives only 3.07:1 for the
   section labels and Clear all, and --slate-400 on the darkened rail gives a
   badly failing 1.93:1 for the unselected segments. Dark surfaces need LIGHTER
   type than the light-mode mirror, not the same step — stepping to --slate-300
   clears 4.5:1 in all three places. */
body.dark-mode .rc-filters-section-label { color: var(--slate-300, #cbd5e1); }
body.dark-mode .rc-filters-clear { color: var(--slate-300, #cbd5e1); }
body.dark-mode .rc-filters-clear:hover {
    background: rgba(45, 212, 191, 0.16);
    color: var(--primary-300, #5eead4);
}
body.dark-mode .rc-filters-panel .rdh-filters { background: rgba(15, 23, 42, 0.55); }
body.dark-mode .rc-filters-panel .rdh-filters .filter-tab { color: var(--slate-300, #cbd5e1); }
body.dark-mode .rc-filters-panel .rdh-filters .filter-tab i { color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-filters-panel .rdh-filters .filter-tab:hover:not(.active) {
    background: rgba(148, 163, 184, 0.12);
    color: var(--slate-200, #e2e8f0);
}
body.dark-mode .rc-filters-panel .rdh-filters .filter-tab.active {
    background: var(--slate-700, #334155);
    border-color: rgba(45, 212, 191, 0.35);
    color: var(--primary-300, #5eead4);
}
body.dark-mode .rc-filters-panel .rdh-filters .filter-tab.active i { color: var(--primary-300, #5eead4); }
body.dark-mode .rc-filters-panel .rc-dropdown-trigger {
    background: var(--slate-900, #0f172a);
    border-color: var(--slate-600, #475569);
    color: var(--slate-100, #f1f5f9);
}
body.dark-mode .rc-filters-panel .rc-dropdown-trigger:hover {
    border-color: var(--primary-500, #14b8a6);
    background: rgba(20, 184, 166, 0.1);
}

/* TOUCH TARGETS (2026-08-29). Measured at 390px the segments were 27.3px tall
   and the dropdown triggers 31.3px — both well under the 44px this codebase
   uses for coarse pointers elsewhere (see the .incoming-source-split note).
   Gate on the POINTER, not the width: a narrow desktop window still has a
   mouse and does not need the extra height, while a large tablet does. */
@media (pointer: coarse) {
    .rc-filters-panel { min-width: 288px; }
    .rc-filters-panel .rdh-filters .filter-tab {
        min-height: 40px;      /* + the rail's 3px padding top and bottom = 46 */
        font-size: 12.5px;
    }
    .rc-filters-panel .rc-dropdown-trigger {
        min-height: 44px;
        font-size: 13px;
    }
    .rc-filters-panel .rc-dropdown-option { min-height: 44px; }
    /* Clear all is a small text button; give it a real hit box without
       changing how it looks. */
    .rc-filters-clear {
        min-height: 32px;
        padding-left: 10px;
        padding-right: 10px;
    }
}

body.dark-mode .rc-search-toggle,
body.dark-mode .rc-filters-btn {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .rc-search-toggle:hover,
body.dark-mode .rc-filters-btn:hover {
    color: var(--slate-200, #e2e8f0);
    border-color: var(--slate-600, #475569);
}
body.dark-mode .rc-filters-panel {
    background: var(--surface-secondary, #1e293b);
    border-color: var(--slate-700, #334155);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
body.dark-mode .rc-filters-panel .rdh-filters { background: var(--slate-800, #1e293b); }

/* Narrow screens: cap the expanded search so it never pushes the tabs off-row;
   the tab row already wraps (flex-wrap) so controls drop below the tabs. */
@media (max-width: 560px) {
    .rc-inline-controls { margin-left: auto; }
    .rc-search-inline.rc-search-open .rc-search-input-wrap { max-width: 180px; }
    .rc-filters-panel { right: 0; left: auto; }
}

/* ============================================
   SCHEDULED TAB PROGRESS BAR
   ============================================ */
.rc-scheduled-progress {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    margin-bottom: 6px;
}
/* Phase D: the whole panel scrolls now (no pinned fixed-height frame), so the
   list no longer "jumps inside a frame" when the bar first appears — it just
   pushes content down like any normal flow insert. So the empty placeholder no
   longer needs to reserve the populated bar's full height; collapse it to zero
   to reclaim ~28px above the first card. The real bar (rendered once the user
   has actioned something) carries its own height. */
.rc-scheduled-progress--empty {
    height: 0;
    margin-bottom: 0;
    padding: 0;
}
.rc-progress-text {
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    white-space: nowrap;
}
.rc-progress-track {
    flex: 1;
    height: 6px;
    background: var(--bg-tertiary, #e2e8f0);
    border-radius: 3px;
    overflow: hidden;
}
.rc-progress-fill {
    height: 100%;
    background: linear-gradient(90deg, #22c55e, #16a34a);
    border-radius: 3px;
    transition: width 0.4s ease;
}

body.dark-mode .rc-progress-track {
    background: #1e293b;
}
body.dark-mode .rc-progress-text {
    color: #94a3b8;
}

/* ============================================
   FIRST-TIME GUIDANCE BANNER
   ============================================ */
/* Collapsed-by-default <details> banner. The summary is a single compact
   line; the body only renders when expanded, so first-visit guidance no
   longer steals ~110px of the card queue. */
.rc-guidance-banner {
    margin-bottom: 10px;
    background: linear-gradient(135deg, rgba(59, 130, 246, 0.08), rgba(139, 92, 246, 0.06));
    border: 1px solid rgba(59, 130, 246, 0.2);
    border-radius: 10px;
    animation: rc-slide-down 0.3s ease;
}
/* The single-line summary (always visible). list-style:none drops the native
   disclosure triangle — we draw our own chevron. */
.rc-guidance-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.rc-guidance-summary::-webkit-details-marker { display: none; }
.rc-guidance-icon {
    font-size: 15px;
    color: #3b82f6;
    flex-shrink: 0;
}
.rc-guidance-summary-text {
    font-weight: 600;
    font-size: 13px;
    color: var(--text-primary, #1e293b);
}
.rc-guidance-summary-hint {
    font-size: 11px;
    color: var(--text-tertiary, #94a3b8);
}
/* "press ? for shortcuts" is meaningless on touch (no keyboard) and it crowds
   the guidance banner's header on a phone. Hide it on hover-less pointers; the
   full shortcuts list stays reachable inside the expanded banner on desktop. */
@media (hover: none) and (pointer: coarse) {
    .rc-guidance-summary-hint { display: none; }
}
.rc-guidance-summary-hint kbd {
    display: inline-block;
    padding: 0 4px;
    background: var(--bg-tertiary, #e2e8f0);
    border-radius: 4px;
    font-family: monospace;
    font-size: 10px;
    border: 1px solid var(--border-color, #cbd5e1);
}
.rc-guidance-chevron {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-tertiary, #94a3b8);
    transition: transform 0.2s ease;
}
.rc-guidance-banner[open] .rc-guidance-chevron {
    transform: rotate(180deg);
}
.rc-guidance-body {
    padding: 0 12px 12px 34px;
}
.rc-guidance-text {
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    line-height: 1.5;
}
.rc-guidance-text strong {
    color: var(--text-primary, #1e293b);
}
.rc-guidance-shortcuts {
    margin-top: 8px;
    font-size: 11px;
    color: var(--text-tertiary, #94a3b8);
}
.rc-guidance-shortcuts kbd {
    display: inline-block;
    padding: 1px 5px;
    background: var(--bg-tertiary, #e2e8f0);
    border-radius: 4px;
    font-family: monospace;
    font-size: 11px;
    border: 1px solid var(--border-color, #cbd5e1);
}
.rc-guidance-dismiss {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Darker glyph + faint chip so this inline X stays visible on the light
       guidance row (2026-06-17 X-visibility pass; was slate-400 ≈2.7:1, gone on
       white). Kept lighter than the header chips since it's a small inline cue. */
    color: var(--slate-600, #475569);
    background: var(--slate-100, #f1f5f9);
    border: 1px solid var(--slate-200, #e2e8f0);
    cursor: pointer;
    padding: 2px 4px;
    margin-left: 2px;
    font-size: 13px;
    flex-shrink: 0;
    border-radius: 4px;
}
.rc-guidance-dismiss:hover {
    background: var(--slate-200, #e2e8f0);
    border-color: var(--slate-300, #cbd5e1);
    color: var(--slate-700, #334155);
}
body.dark-mode .rc-guidance-dismiss {
    background: var(--slate-700, #334155);
    border-color: var(--slate-600, #475569);
    color: var(--slate-200, #e2e8f0);
}
.rc-guidance-dismiss:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}

@keyframes rc-slide-down {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Reduced-motion: no slide-in, no chevron spin. */
@media (prefers-reduced-motion: reduce) {
    .rc-guidance-banner { animation: none; }
    .rc-guidance-chevron { transition: none; }
}

body.dark-mode .rc-guidance-banner {
    background: linear-gradient(135deg, rgba(59, 130, 246, 0.12), rgba(139, 92, 246, 0.08));
    border-color: rgba(59, 130, 246, 0.25);
}
body.dark-mode .rc-guidance-shortcuts kbd,
body.dark-mode .rc-guidance-summary-hint kbd {
    background: #334155;
    border-color: #475569;
}

/* ============================================
   BATCH SELECT BAR
   ============================================ */
.rc-batch-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    margin-bottom: 8px;
    border-radius: 8px;
    flex-wrap: wrap;
}
.rc-batch-bar-collapsed {
    justify-content: flex-end;
}
.rc-batch-bar-active {
    background: rgba(59, 130, 246, 0.06);
    border: 1px solid rgba(59, 130, 246, 0.2);
    justify-content: space-between;
    /* Stick to the top of the review modal scroll viewport so bulk
       actions stay reachable while scrolling through a long card list. */
    position: sticky;
    top: 0;
    z-index: 5;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
}

/* Inline tip surfaced once below the active batch bar. Dismissible. */
.rc-batch-tip {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    margin-bottom: 8px;
    font-size: 12px;
    color: var(--slate-600);
    background: rgba(59, 130, 246, 0.04);
    border: 1px dashed rgba(59, 130, 246, 0.25);
    border-radius: 6px;
}

.rc-batch-tip kbd {
    display: inline-block;
    padding: 0 5px;
    background: var(--slate-100, #f1f5f9);
    border: 1px solid var(--slate-300, #cbd5e1);
    border-bottom-width: 2px;
    border-radius: 3px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    line-height: 1.5;
    color: var(--slate-700);
}

.rc-batch-tip-close {
    margin-left: auto;
    background: transparent;
    border: none;
    cursor: pointer;
    color: var(--slate-500);
    width: 22px;
    height: 22px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 4px;
}

.rc-batch-tip-close:hover {
    background: rgba(0, 0, 0, 0.05);
    color: var(--slate-700);
}

body.dark-mode .rc-batch-tip {
    background: rgba(59, 130, 246, 0.08);
    border-color: rgba(59, 130, 246, 0.3);
    color: var(--slate-300);
}

body.dark-mode .rc-batch-tip kbd {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-600, #475569);
    color: var(--slate-200, #e2e8f0);
}

body.dark-mode .rc-batch-tip-close { color: var(--slate-400); }
body.dark-mode .rc-batch-tip-close:hover { background: rgba(255, 255, 255, 0.06); color: var(--slate-200); }
.rc-batch-info {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-secondary, #64748b);
}
.rc-batch-info input[type="checkbox"] {
    width: var(--checkbox-size, 18px);
    height: var(--checkbox-size, 18px);
    accent-color: #3b82f6;
}
.rc-batch-actions {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}
.rc-batch-actions .btn {
    font-size: 12px;
    padding: 5px 10px;
}
.rc-batch-cb {
    width: var(--checkbox-size, 18px);
    height: var(--checkbox-size, 18px);
    accent-color: #3b82f6;
    margin-right: 6px;
    flex-shrink: 0;
    cursor: pointer;
}
.rc-batch-selected {
    outline: 2px solid rgba(59, 130, 246, 0.4);
    outline-offset: -2px;
    background: rgba(59, 130, 246, 0.03) !important;
}

body.dark-mode .rc-batch-bar-active {
    background: rgba(59, 130, 246, 0.1);
    border-color: rgba(59, 130, 246, 0.3);
}

/* ============================================
   AUTO-ACCEPT TIMER URGENCY ESCALATION
   ============================================ */
.rc-auto-accept-timer {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 500;
    padding: 2px 8px;
    border-radius: 12px;
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background-color 0.3s ease, color 0.3s ease;
}
.rc-timer-calm {
    color: var(--rc-status-working);
    background: rgba(22, 163, 74, 0.08);
}
/* Dark mode: the calm green tint washed out on slate (the green-on-light-green
   fill was tuned for white). Lift the text + tint for contrast. */
body.dark-mode .rc-timer-calm {
    color: #4ade80;
    background: rgba(22, 163, 74, 0.18);
}
.rc-timer-amber {
    /* Amber-700 strong text on amber background — see urgency-tomorrow note. */
    color: #d97706;
    background: rgba(217, 119, 6, 0.1);
    animation: rc-pulse-amber 2s infinite;
}
.rc-timer-critical {
    color: var(--rc-status-busy);
    background: rgba(220, 38, 38, 0.1);
    animation: rc-pulse-critical 1.2s infinite;
    font-weight: 600;
}

@keyframes rc-pulse-amber {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}
@keyframes rc-pulse-critical {
    0%, 100% { opacity: 1; transform: scale(1); }
    50% { opacity: 0.8; transform: scale(1.02); }
}

/* Snooze button */
.rc-snooze-btn {
    font-size: 11px !important;
    padding: 2px 8px !important;
    border-radius: 10px !important;
    background: var(--bg-secondary, #f1f5f9) !important;
    border: 1px solid var(--border-color, #e2e8f0) !important;
    color: var(--text-secondary, #64748b) !important;
    cursor: pointer;
    transition: all 0.15s;
}
.rc-snooze-btn:hover {
    background: var(--bg-tertiary, #e2e8f0) !important;
    color: var(--text-primary, #1e293b) !important;
}

body.dark-mode .rc-snooze-btn {
    background: #1e293b !important;
    border-color: #334155 !important;
    color: #94a3b8 !important;
}

/* ============================================
   PROGRESSIVE DISCLOSURE (CARD EXPAND)
   ============================================ */
.rc-card-details {
    padding: 8px 0 0;
    animation: rc-slide-down 0.2s ease;
}
.rc-card-expand-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 4px;
    background: none;
    border: none;
    color: var(--text-tertiary, #94a3b8);
    cursor: pointer;
    font-size: 12px;
    transition: color 0.15s;
}
.rc-card-expand-btn:hover {
    color: var(--text-primary, #1e293b);
}

/* ============================================
   STICKY SUB-TABS
   ============================================ */
.review-sub-tabs {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--bg-primary, #fff);
    padding-bottom: 4px;
    margin: 0 -18px;
    padding-left: 18px;
    padding-right: 18px;
}
body.dark-mode .review-sub-tabs {
    background: var(--dark-bg, #0f172a);
}

/* ============================================
   TAB TRANSITION ANIMATION
   ============================================ */
/* Crossfade: the outgoing content fades toward 0 (it used to stop at 0.3,
   which read as a hard swap at 30% opacity), then the incoming content
   fades up from 0 via rc-fade-in. The exit is brief because the innerHTML
   swap happens on the next frame; a low target keeps it from looking stuck. */
.rc-tab-exit {
    opacity: 0.05;
    transition: opacity 0.12s ease;
}
.rc-tab-enter {
    animation: rc-fade-in 0.2s ease;
}
@keyframes rc-fade-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ============================================
   KEYBOARD FOCUS HIGHLIGHT
   ============================================ */
.rc-card-focused {
    outline: 2px solid var(--primary-color, #3b82f6) !important;
    outline-offset: -1px;
    box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.12) !important;
}

/* ============================================
   SCREEN READER ONLY
   ============================================ */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ============================================
   OUTGOING TAB: WITHDRAW BUTTON
   ============================================ */
.outgoing-actions {
    padding: 6px 0 2px;
    display: flex;
    gap: 6px;
    /* Right-aligned to match .my-proposal-actions (2026-08-06). Both card types
       render into the SAME scroll column under "Sent by me", and this one was
       left-aligned while its neighbour was right-aligned - so scanning down one
       list, the primary action jumped from one edge to the other. Right is the
       dominant convention across the panel. */
    justify-content: flex-end;
}
.rc-withdraw-btn {
    font-size: 11px !important;
    padding: 4px 10px !important;
    color: var(--text-secondary, #64748b) !important;
}
.rc-withdraw-btn:hover {
    color: var(--review-urgent, #ef4444) !important;
    border-color: var(--review-urgent, #ef4444) !important;
}

/* ============================================
   REVIEW ITEM: WARNING CLASS (amber urgency)
   ============================================ */
.review-item.warning {
    /* Darker amber for the accent border on light backgrounds — see
       urgency-tomorrow comment. */
    border-left: 3px solid #d97706;
}
.review-item.urgent {
    border-left: 3px solid var(--rc-status-busy);
}

body.dark-mode .review-item.warning {
    border-left-color: var(--rc-status-tentative);
}
body.dark-mode .review-item.urgent {
    border-left-color: #ef4444;
}

/* ============================================
   BATCH TOGGLE BUTTON
   ============================================ */
.rc-batch-toggle-btn {
    font-size: 12px;
    padding: 5px 12px;
    border-radius: 8px;
    opacity: 0.7;
    transition: opacity 0.15s;
}
.rc-batch-toggle-btn:hover {
    opacity: 1;
}

/* ============================================
   EMPTY STATES: MEANINGFUL MESSAGES
   ============================================ */
/* (The .empty-hint colour/size live once at :7711 — AA-passing slate-600.
   This block previously re-set them to a failing slate-tertiary; removed.) */
.review-empty .empty-hint i {
    margin-right: 4px;
}

/* ============================================
   (CONFLICT TIMELINE VISUALIZATION removed 2026-08-15.
   The bespoke two-lane chart, .rc-conflict-timeline / .rc-timeline-*,
   was replaced by the conflict mini-board, .rcx-*, defined next to the
   .conflict-item card styles earlier in this file. The mini-board speaks
   the board vocabulary: slate busy spans on an #e2e8f0 empty track, with
   the red overlap column as the only red element.)
   ============================================ */

/* ============================================
   DARK MODE: additional overrides (3rd pass)
   ============================================ */
body.dark-mode .rc-guidance-summary-text { color: #f1f5f9; }
body.dark-mode .rc-guidance-text { color: #94a3b8; }
body.dark-mode .rc-guidance-text strong { color: #f1f5f9; }
body.dark-mode .rc-guidance-icon { color: #60a5fa; }
body.dark-mode .rc-guidance-dismiss { color: #64748b; }
body.dark-mode .rc-guidance-dismiss:hover { color: #e2e8f0; }
body.dark-mode .rc-batch-info { color: #94a3b8; }
body.dark-mode .rc-card-expand-btn { color: #64748b; }
body.dark-mode .rc-card-expand-btn:hover { color: #e2e8f0; }
body.dark-mode .rc-search-clear { color: #64748b; }
body.dark-mode .rc-search-clear:hover { color: #e2e8f0; }
body.dark-mode .rc-scheduled-progress { border-color: #334155; }
body.dark-mode .rc-seen-badge { color: #4ade80; }
body.dark-mode .rc-unseen-badge { color: #64748b; }
body.dark-mode .confidence-pct { color: #94a3b8; }

/* ============================================
   MOBILE: responsive breakpoints (3rd pass)
   ============================================ */
@media (max-width: 640px) {
    /* Search on its own row; the two dropdowns (Sort + date) SHARE the next row
       rather than each taking a full-width row (user 2026-06-21: "more minimal
       with its space"). The bar already flex-wraps, so a full-basis search input
       breaks the line and the two flex:1 dropdowns then split the row below it —
       reclaiming ~50px vs the old flex-direction:column stack. */
    .rc-search-filter-bar { flex-direction: row; flex-wrap: wrap; row-gap: 8px; }
    .rc-search-input-wrap { flex: 1 1 auto; min-width: 0; }
    .rc-dropdown { flex: 1 1 0; min-width: 0; }
    .rc-dropdown-trigger { width: 100%; justify-content: space-between; }
    .rc-dropdown-menu { right: auto; left: 0; width: 100%; }

    /* Mobile: search stays on the row with a compact "Filters" toggle beside it;
       the Sort + Date dropdowns are tucked away until the toggle is tapped. This
       drops the at-rest filter chrome from two strips (search row + dropdown row)
       to one, so the user reaches their items sooner. */
    .rc-filters-toggle {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        position: relative;
        flex: 0 0 auto;
        min-height: 40px;
        padding: 8px 12px;
        border: 1px solid var(--slate-200, #e2e8f0);
        border-radius: var(--radius-md, 8px);
        background: var(--surface-primary, #fff);
        color: var(--slate-600, #475569);
        font-size: 13px;
        font-weight: 500;
        cursor: pointer;
    }
    .rc-filters-toggle.rc-filter-on {
        color: var(--primary-600, #0d9488);
        border-color: var(--cmx-4a9824eabb);
        border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent);
    }
    .rc-filters-toggle .rc-filter-dot {
        width: 7px; height: 7px; border-radius: 50%;
        background: var(--primary-500, #14b8a6);
    }
    /* Dropdowns hidden until the toggle opens them; then they take their own
       full-width row below the search+toggle line. */
    .rc-filter-dropdowns {
        display: none;
        flex: 1 1 100%;
        gap: 8px;
    }
    .rc-search-filter-bar.rc-filters-open .rc-filter-dropdowns {
        display: flex;
    }
    body.dark-mode .rc-filters-toggle {
        background: var(--slate-800, #1e293b);
        border-color: var(--slate-700, #334155);
        color: var(--slate-300, #cbd5e1);
    }
    .rc-guidance-banner { flex-direction: column; gap: 8px; }
    .rc-batch-bar-active { flex-direction: column; align-items: stretch; }
    .rc-batch-actions { justify-content: stretch; }
    .rc-batch-actions .btn { flex: 1; text-align: center; }
}

/* ============================================
   SEEN / UNSEEN badges (outgoing tab)
   ============================================ */
.rc-seen-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 500;
    color: #16a34a;
}
.rc-unseen-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 500;
    color: #94a3b8;
}
.confidence-pct {
    font-size: 11px;
    font-weight: 600;
    color: #64748b;
    margin-left: 4px;
}

/* ============================================
   DETAIL: location inside expandable details
   ============================================ */
.rc-detail-location {
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    padding: 4px 0;
}
.rc-detail-location i {
    margin-right: 4px;
    color: var(--text-tertiary, #94a3b8);
}
body.dark-mode .rc-detail-location {
    color: #94a3b8;
}
body.dark-mode .rc-detail-location i {
    color: #64748b;
}

/* ============================================
   CONFLICT: Keep Both action (shared, in card footer)
   ============================================ */
.conflict-action-btn.keep-both-btn {
    background: transparent;
    color: var(--slate-500);
    border-color: var(--slate-200);
}

.conflict-action-btn.keep-both-btn:hover {
    color: var(--rc-status-working);
    border-color: var(--rc-status-working);
    background: rgba(22, 163, 74, 0.08);
}

body.dark-mode .conflict-action-btn.keep-both-btn {
    background: transparent;
    color: var(--slate-400);
    border-color: var(--slate-700);
}

body.dark-mode .conflict-action-btn.keep-both-btn:hover {
    color: #4ade80;
    border-color: #4ade80;
    background: rgba(74, 222, 128, 0.1);
}

.incoming-empty {
    text-align: center;
    padding: 32px 20px;
}

.incoming-connect-btn {
    margin-top: 14px;
}

body.dark-mode .synced-sweep-bar {
    background: rgba(15, 23, 42, 0.4);
}

body.dark-mode .synced-sweep-btn:hover,
body.dark-mode .synced-vis-btn:hover {
    background: rgba(148, 163, 184, 0.1);
}

/* ============================================================
   Calendar privacy floor — first-connect prompt overlay
   ============================================================ */

/* ⭐2026-08-27 MAKEOVER — this dialog is a hand-rolled overlay (41 other
   surfaces go through Modal.open()), so it never inherited the app's modal
   chrome and drifted off-brand. Measured against the tokens before the
   rewrite:
     radius   12px / 8px literals   vs  --radius-2xl / --radius-md
     width    460px                 vs  560px (.modal-content)
     shadow   ad-hoc 20px 50px      vs  --shadow-xl
     accent   #0284c7 PRE-REBRAND   vs  --primary-500 teal  ⚠️
     focus    2px blue outline      vs  --focus-ring + halo
   The #0284c7 fallback is the same pre-rebrand blue the token comment in
   style.css records as having leaked onto ~93 sites. It only shows when the
   token is missing, but it is exactly how the drift spread — so it goes.

   Dark mode was VERIFIED WORKING before and after (body.dark-mode, not
   data-theme — the tokens carry it, including --border-color -> #334155).
   Nothing here hardcodes a hue that would defeat it. */
.calendar-floor-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    backdrop-filter: blur(2px);
    z-index: var(--z-system-top);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}

.calendar-floor-modal {
    background: var(--surface-primary, var(--bg-primary, #fff));
    border-radius: var(--radius-2xl, 20px);
    padding: 24px;
    max-width: 560px;
    width: 100%;
    box-shadow:
        0 24px 48px rgba(0, 0, 0, 0.2),
        0 0 0 1px rgba(255, 255, 255, 0.1),
        var(--shadow-xl, 0 20px 40px rgba(0, 0, 0, 0.12));
}

/* Beta badge: single definition lives in style.css (.beta-badge, with the
   dark-mode override). The copy that lived here drifted independently. */

.calendar-floor-modal h3 {
    margin: 0 0 8px;
    /* Matches .modal-header's title weight/size rather than a local guess. */
    font-size: 1.25rem;
    font-weight: 650;
    letter-spacing: -0.01em;
    line-height: 1.3;
    color: var(--text-primary, #0f172a);
}

.calendar-floor-modal .floor-subtitle {
    margin: 0 0 20px;
    font-size: 0.875rem;
    color: var(--text-secondary, #64748b);
    line-height: 1.55;
}

/* ⭐2026-08-27 THE CARDS ARE NOW THE SHARED COMPONENT. The choice buttons
   carry `.privacy-scale-option .visibility-option` and render a `.vis-preview`
   strip, exactly like the New Group class picker — so their card chrome,
   selected state, `.recommended-badge` and preview styling all come from
   board-modals.css / privacy-cards.css and are maintained in ONE place.
   Owner: "why not use something closer to this — the group creation example".

   The ~90 lines of bespoke `.floor-choice` card styling that used to live
   here are deliberately GONE. Re-adding local card rules would fork the two
   surfaces again, which is exactly what this change set out to fix. Only the
   list LAYOUT stays local. */
.calendar-floor-modal .floor-choices {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Only the roomier padding is local — `flex-wrap: wrap` already ships with the
   shared component (board-modals.css ~621, so the preview strip drops onto its
   own line), and re-declaring it here would be the start of forking it again. */
.calendar-floor-modal .floor-choice {
    padding: 14px 16px;
}


/* The skip now carries a consequence line like the three choices above it, so
   the row is a LEFT-ALIGNED STACK rather than a right-aligned single button.
   ⚠️`text-align: right` here used to push the (then wordless) button to the
   corner; keeping it would strand the new description away from the control
   it describes. See calendar-sync-ui.js for why the line exists. */
/* Sits below a divider, the way every other modal separates its footer from
   its body. Without it the skip floated against the last choice card and read
   as a fourth option rather than the way out of the set. */
.calendar-floor-modal .floor-skip-row {
    margin-top: 18px;
    padding-top: 16px;
    border-top: 1px solid var(--border-color, #e2e8f0);
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
}

.calendar-floor-modal .floor-skip {
    padding: 2px 0;
    background: transparent;
    border: none;
    color: var(--text-secondary, #64748b);
    cursor: pointer;
    font-size: 0.85rem;
    font-weight: 550;
    text-decoration: underline;
    text-underline-offset: 3px;
    transition: color 0.14s ease;
}

.calendar-floor-modal .floor-skip:hover {
    color: var(--text-primary, #0f172a);
}

.calendar-floor-modal .floor-skip-desc {
    font-size: 0.78rem;
    line-height: 1.45;
    color: var(--text-muted, #94a3b8);
}

.calendar-floor-modal .floor-skip:focus-visible {
    outline: 2px solid var(--accent, #0284c7);
    outline-offset: 2px;
    border-radius: 4px;
}

@media (max-width: 480px) {
    .calendar-floor-modal {
        padding: 18px;
    }
}

body.dark-mode .calendar-floor-modal {
    background: var(--bg-primary, #1e293b);
    color: var(--text-primary, #e2e8f0);
}

body.dark-mode .calendar-floor-modal .floor-choice:hover {
    background: rgba(148, 163, 184, 0.08);
}

/* ============================================================
   Integration card privacy row (settings → integrations modal)
   ============================================================ */

.integration-privacy-row {
    padding-top: 8px;
    border-top: 1px solid var(--border-color, #e2e8f0);
    margin-top: 8px;
}

.integration-privacy-row .privacy-row-title {
    font-size: 0.85rem;
    font-weight: 600;
    margin-bottom: 6px;
    color: var(--text-primary, #0f172a);
}

.integration-privacy-row .floor-selector {
    display: flex;
    gap: 6px;
}

.integration-privacy-row .btn-floor {
    flex: 1;
    padding: 6px 10px;
    border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: transparent;
    cursor: pointer;
    font-size: 0.8rem;
    color: var(--text-primary, #0f172a);
    transition: background 0.12s ease, border-color 0.12s ease;
}

.integration-privacy-row .btn-floor:hover {
    border-color: var(--accent, #0284c7);
}

.integration-privacy-row .btn-floor:focus-visible,
.integration-privacy-row .btn-rereview:focus-visible {
    outline: 2px solid var(--accent, #0284c7);
    outline-offset: 2px;
}

.integration-privacy-row .btn-floor.active {
    border-color: var(--accent, #0284c7);
    background: var(--accent-bg, rgba(2, 132, 199, 0.08));
    color: var(--accent, #0284c7);
}

.integration-privacy-row .btn-rereview {
    margin-top: 8px;
    padding: 6px 10px;
    border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: transparent;
    cursor: pointer;
    font-size: 0.8rem;
    width: 100%;
    color: var(--text-primary, #0f172a);
    transition: background 0.12s ease, border-color 0.12s ease;
}

.integration-privacy-row .btn-rereview:hover {
    border-color: var(--accent, #0284c7);
    background: var(--bg-secondary, #f1f5f9);
}

@media (max-width: 480px) {
    .integration-privacy-row .floor-selector {
        flex-direction: column;
    }
}

/* ------------------------------------------------------------
   Per-connection category pills ("This calendar contains:
   Work | Personal | Unset"). These share the .btn-floor class
   with the privacy-row pills but live in a different container
   (.label-category-selector), which previously had NO CSS — so
   the three buttons rendered as browser-default gray. Mirror the
   privacy-row pill exactly. NOTE the selected state keys off
   `.is-selected`: settings-modals.js wireLabelCategorySelector +
   the /api/calendar/status hydration both toggle `is-selected`
   (the privacy-row pills use `.active` — keep the two separate).
   ------------------------------------------------------------ */
.label-category-selector {
    display: flex;
    gap: 6px;
}

.label-category-selector .btn-floor {
    flex: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: transparent;
    cursor: pointer;
    font-size: 0.8rem;
    color: var(--text-primary, #0f172a);
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.label-category-selector .btn-floor:hover {
    border-color: var(--accent, #0284c7);
}

.label-category-selector .btn-floor:focus-visible {
    outline: 2px solid var(--accent, #0284c7);
    outline-offset: 2px;
}

.label-category-selector .btn-floor.is-selected {
    border-color: var(--accent, #0284c7);
    background: var(--accent-bg, rgba(2, 132, 199, 0.08));
    color: var(--accent, #0284c7);
}

/* ============================================
   Shift assignments link-out (Outgoing tab)
   Compact CTA replacing the old inline runs list — detail lives in the
   dedicated Quota & Assignment surface.
   ============================================ */
.rc-shift-link {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 18px;
    padding: 10px 14px;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 8px;
    background: var(--bg-secondary, #f8fafc);
    flex-wrap: wrap;
}

.rc-shift-link-text {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--slate-600, #475569);
    font-size: 13px;
}

.rc-shift-link-text i {
    color: var(--review-accent, var(--primary-500, #14b8a6));
}

.rc-shift-link-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    padding: 6px 12px;
    border: 1px solid var(--review-accent, var(--primary-500, #14b8a6));
    border-radius: 6px;
    background: transparent;
    color: var(--review-accent, var(--primary-500, #14b8a6));
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
}

.rc-shift-link-btn:hover {
    background: var(--review-accent-light, rgba(20, 184, 166, 0.1));
}

.rc-shift-link-btn:focus-visible {
    outline: 2px solid var(--review-accent);
    outline-offset: 2px;
}

body.dark-mode .rc-shift-link {
    border-color: var(--slate-700, #334155);
    background: rgba(255, 255, 255, 0.02);
}

body.dark-mode .rc-shift-link-text {
    color: var(--slate-300, #cbd5e1);
}

/* ============================================================
   Privacy tab — Option C unified queue
   ============================================================ */

/* Row wraps the filter-chip group (left) and the policy chip (right). The
   row-level chrome (background + bottom border) lives here now. */
.privacy-chip-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px 6px;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
    background: var(--bg-secondary, #f8fafc);
}
.privacy-filter-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    flex: 1; min-width: 0;
}

/* Publication-policy chip — deliberately quieter and visually distinct from
   the filter chips (it's a status indicator, not a selectable filter): no
   pill fill, a dotted-ish subtle border, auto-pushed to the right. */
.privacy-policy-chip {
    display: inline-flex; align-items: center; gap: 6px;
    flex-shrink: 0; margin-left: auto;
    padding: 4px 10px; border-radius: 6px;
    border: 1px dashed var(--border-color, #cbd5e1);
    background: transparent;
    color: var(--text-secondary, #64748b);
    font-size: 0.78rem; font-weight: 600;
}
/* Both variants are clickable now: admins jump to settings, read-only opens an
   info popover (2026-07-11 fix — the read-only chip used to be a dead button). */
.privacy-policy-chip.is-admin,
.privacy-policy-chip.is-readonly { cursor: pointer; }
.privacy-policy-chip.is-admin:hover,
.privacy-policy-chip.is-readonly:hover {
    border-color: var(--accent, #0284c7);
    color: var(--text-primary, #0f172a);
    background: var(--bg-primary, #fff);
}
.privacy-policy-chip:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}
.privacy-policy-chip .privacy-policy-chip-info {
    font-size: 0.72rem; opacity: 0.6;
}
/* "Policy:" qualifier so the chip self-identifies as the board publication
   policy rather than reading as a sixth queue-filter chip. Muted + lighter
   than the level word it precedes. */
.privacy-policy-chip .privacy-policy-chip-prefix {
    font-weight: 500; opacity: 0.7;
    margin-right: -2px;
}
/* Level tints on the leading icon so Strict reads heavier at a glance. */
.privacy-policy-chip[data-policy-level="strict"]   > i:first-child { color: var(--rc-status-busy, #dc2626); }
.privacy-policy-chip[data-policy-level="standard"] > i:first-child { color: var(--rc-status-tentative, #f59e0b); }
.privacy-policy-chip[data-policy-level="relaxed"]  > i:first-child { color: var(--rc-status-available, #22c55e); }
body.dark-mode .privacy-policy-chip { border-color: var(--slate-600, #475569); color: var(--slate-300, #cbd5e1); }
body.dark-mode .privacy-policy-chip.is-admin:hover,
body.dark-mode .privacy-policy-chip.is-readonly:hover { background: var(--slate-800, #1e293b); color: var(--slate-100, #f1f5f9); }

/* Read-only policy info popover — the on-screen explanation shown when a
   non-admin taps the chip (replaces the invisible-on-touch title tooltip). */
.privacy-policy-chip-wrap { position: relative; display: inline-flex; }
.privacy-policy-popover {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 30;
    width: 240px;
    padding: 12px 14px;
    background: var(--surface-primary, #fff);
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(15, 23, 42, 0.16);
    text-align: left;
}
.privacy-policy-popover[hidden] { display: none; }
.privacy-policy-popover-head {
    display: flex; align-items: center; gap: 8px;
    font-size: 0.86rem; font-weight: 700;
    color: var(--text-primary, #0f172a);
    margin-bottom: 6px;
}
.privacy-policy-popover-why {
    margin: 0 0 6px;
    font-size: 0.8rem; line-height: 1.45; font-weight: 400;
    color: var(--text-secondary, #475569);
}
.privacy-policy-popover-admin {
    margin: 0;
    font-size: 0.75rem; font-weight: 500;
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .privacy-policy-popover {
    background: var(--surface-secondary, #1e293b);
    border-color: var(--slate-700, #334155);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
body.dark-mode .privacy-policy-popover-head { color: var(--slate-100, #f1f5f9); }
body.dark-mode .privacy-policy-popover-why { color: var(--slate-300, #cbd5e1); }

/* Right-side action group: the quiet "Rules" chip + the policy chip live here
   together, pushed to the row's trailing edge. Grouping them means the policy
   chip no longer needs its own margin-left:auto (that would over-space the two
   inside the group), so neutralise it in this context. */
.privacy-chip-row-actions {
    display: inline-flex; align-items: center; gap: 6px;
    flex-shrink: 0; margin-left: auto;
}
/* ⚠️A WRAPPING SIBLING IS WHAT PUSHES THIS GROUP INTO THE LINE ABOVE
   (2026-09-23, owner: "Rules Still touching the top", with the dashed box
   overlapping the pill's bottom curve).
   `.privacy-chip-row` does NOT wrap, but its `.privacy-filter-chips` child
   DOES. When the filters take a second line that child grows taller, and the
   row's `align-items: center` re-centres this group against the TALLER box —
   so the dashed chip's top edge climbs level with the pill on the first line.
   `margin-left: auto` is a horizontal floor (the 2026-09-13 fix) and does
   nothing for this. Align this group to the row's LAST line instead of its
   centre, and keep a real vertical floor under it, so a second line of
   filters pushes the chip DOWN rather than into the pill above.
   ⛔Do not fix this by letting .privacy-chip-row wrap: the actions group is
   meant to stay on the trailing edge of the row, and wrapping would drop it
   onto a line of its own at every width. */
.privacy-chip-row .privacy-chip-row-actions {
    align-self: flex-end;
    margin-top: 4px;
}
.privacy-chip-row-actions .privacy-policy-chip { margin-left: 0; }

/* Automation-rules chip — the quietest thing in the row: it's a jump-off, not
   a status or a filter. Ghost styling matching the policy chip's weight so the
   two right-side chips read as a set, with a wand icon to signal "automation".
   Opens the AutomationRulesPanel (see click delegation in review-dashboard). */
/* ⭐2026-09-24 (owner, third report, measured on the LIVE build after the
   2026-09-23 fix shipped: Rules top == All bottom, a 0px gap). On the Review
   Center's Privacy tab Rules is NOT in .privacy-chip-row-actions: it is a
   block sibling UNDER the filter band inside .privacy-chip-row-wrap
   (review-dashboard.js renderPrivacyChips), so the align-self fix above never
   applied to it. Give it its own vertical floor there. Measured with this
   rule on the live page: 8px gap, still inside the sticky bar. */
.privacy-chip-row-wrap > .privacy-rules-chip { margin-top: 8px; }

.privacy-rules-chip {
    display: inline-flex; align-items: center; gap: 6px;
    flex-shrink: 0; cursor: pointer;
    padding: 4px 10px; border-radius: 6px;
    /* ⚠️THE OUTLINE IS THE QUIETEST MARK IN THE ROW (2026-09-23, owner:
       "Rules Still touching the top. Make outline smaller").
       Two separate things were wrong and only one of them was spacing.
       (a) A 1px dash on the shared --border-color is the SAME weight as the
       filter pills' own borders, so the row's quietest control — a jump-off,
       per the note above — carried the row's loudest chrome, and the dashes
       read as a drop-zone rather than a button. Thin the mark instead of
       shrinking the box: the hit area stays at the 4px/10px padding (the
       44px touch floor is met by the row, and a smaller box would fail it
       on its own), while a lighter dash at a smaller radius stops the
       outline competing. The HOVER border keeps full strength — that is
       where the control should announce itself.
       (b) the touching edge is handled on .privacy-chip-row above; it is a
       wrap artifact, not this box being too big.
       ⛔NOT color-mix() here: the owner's phone is iOS 15.8, whose Safari
       has no color-mix(), and a var() of such a value is invalid at
       computed-value time — the border would vanish entirely there (see
       6d3edd45). A plain rgba says the same thing everywhere. */
    border: 1px dashed rgba(148, 163, 184, 0.55);
    background: transparent;
    color: var(--text-secondary, #64748b);
    font-size: 0.78rem; font-weight: 600; line-height: 1;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.privacy-rules-chip > i:first-child {
    font-size: 0.75rem;
    color: var(--primary-500, #14b8a6);
}
.privacy-rules-chip:hover {
    border-color: var(--primary-400, #2dd4bf);
    color: var(--text-primary, #0f172a);
    background: var(--bg-primary, #fff);
}
.privacy-rules-chip:focus-visible {
    outline: 2px solid var(--primary-500, #14b8a6); outline-offset: 2px;
}
.privacy-rules-chip .privacy-rules-chip-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 9999px; line-height: 1;
    font-size: 0.66rem; font-weight: 700;
    color: #fff;
    background: linear-gradient(135deg, var(--primary-500, #14b8a6) 0%, var(--primary-600, #0d9488) 100%);
}
body.dark-mode .privacy-rules-chip { border-color: var(--slate-600, #475569); color: var(--slate-300, #cbd5e1); }
body.dark-mode .privacy-rules-chip:hover { background: var(--slate-800, #1e293b); color: var(--slate-100, #f1f5f9); }



/* Metrics aligned to .sub-tab (2026-08-06). Privacy is the only tab that
   filters with chips instead of sub-tabs — defensible, since `all` / `needs
   approval` / `done` do not map onto the awaiting/actioned spine the other tabs
   share, and deep links carry these filter values. But the chips also sat at a
   different size and padding from the sub-tabs one tab over, so the SAME job
   (choose which slice of this queue to see) looked like two different controls.
   Type scale, gap and padding now match; the 1px border is deliberately KEPT so
   a filter still reads as a filter rather than impersonating a tab. */
/* MINIMALIST, LIKE THE TABS ABOVE (2026-08-16, user: "Make the all needs
   approval and done minamalistoc like tje above tab design already").
   These carried a permanent 1px outline and an opaque white fill, which the
   .review-tab pills directly above them do not — so two rows of the same kind
   of control read as two different systems, and the heavier row was the less
   important one. Resting state is now bare like a tab; the border and fill
   arrive only on hover/active, where they mean something. */
.privacy-filter-chip {
    display: inline-flex; align-items: center; gap: 4px;
    /* Padding stays locked to .sub-tab (guard: review-empty-loading-pagination)
       — the two rows must measure the same. Only the resting border and fill
       are dropped. */
    padding: 5px 14px; border-radius: var(--radius-full, 9999px);
    border: 1px solid transparent;
    background: transparent;
    color: var(--text-secondary, #64748b);
    font-size: 0.75rem; font-weight: 500; cursor: pointer;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
/* Chips follow the tab's purple identity (2026-08-04) — the old cyan accent
   fill clashed with the purple Privacy tab sitting directly above them. */
.privacy-filter-chip:hover {
    border-color: var(--visibility-private, #8b5cf6);
    color: var(--text-primary, #0f172a);
}
.privacy-filter-chip:focus-visible {
    outline: 2px solid var(--visibility-private, #8b5cf6); outline-offset: 2px;
}
.privacy-filter-chip.active {
    background: var(--visibility-private, #8b5cf6);
    border-color: var(--visibility-private, #8b5cf6);
    color: #fff;
}
/* Count pill matched to .sub-tab .sub-tab-count (2026-08-06): same 16px box,
   same 0.625rem type, so a count reads identically whichever surface shows it.
   Colours stay chip-local (the active chip fills purple and needs a light
   count on it, where an active sub-tab does not). */
.privacy-filter-chip .privacy-chip-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 16px; height: 16px;
    background: rgba(148, 163, 184, 0.22); color: inherit;
    padding: 0 5px; border-radius: 8px;
    font-size: 0.625rem; font-weight: 600;
}
.privacy-filter-chip.active .privacy-chip-count {
    background: rgba(255, 255, 255, 0.25); color: #fff;
}

.synced-sweep-bar {
    display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
    background: var(--bg-secondary, #f8fafc);
}
.synced-sweep-label {
    font-size: 0.85rem; color: var(--text-secondary, #64748b);
}
/* ONE QUESTION, THREE ANSWERS (2026-08-16, user: "Clicking the options could
   be better designed").
   These were three detached, individually-outlined buttons separated by a gap,
   each carrying its own hue, its own hover fill and — when chosen — a heavy
   saturated fill plus a coloured drop-shadow. Three equal-weight boxes read as
   three independent toggles rather than a single choice, so nothing about the
   resting state said "pick one", and the selected state shouted.

   Make the group a SEGMENTED CONTROL: one bordered track, hairline dividers,
   and the selected segment lifted on a white card. That is the standard idiom
   for a mutually-exclusive choice, and it lets the selection read from shape
   and elevation rather than from decoding a colour. Each level keeps its hue
   as an ICON tint and a text colour, so the privacy grammar survives without
   three competing fills. */
.synced-row-actions,
.synced-sweep-actions {
    /* The track. */
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
    background: var(--slate-100, #f1f5f9);
    padding: 2px;
    gap: 0;
}

.synced-sweep-btn, .synced-vis-btn {
    padding: 5px 12px;
    border-radius: 6px;
    border: 1px solid transparent;
    background: transparent; cursor: pointer;
    font-size: 0.8125rem; line-height: 1.2;
    font-weight: 500;
    color: var(--text-secondary, #64748b);
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;
}
/* Hairline between segments — dropped on the selected one so the lifted card
   sits clean. */
.synced-vis-btn + .synced-vis-btn,
.synced-sweep-btn + .synced-sweep-btn {
    box-shadow: inset 1px 0 0 var(--slate-200, #e2e8f0);
}
.synced-vis-btn.active + .synced-vis-btn,
.synced-vis-btn:hover + .synced-vis-btn,
.synced-sweep-btn.active + .synced-sweep-btn,
.synced-sweep-btn:hover + .synced-sweep-btn {
    box-shadow: none;
}
.synced-sweep-btn:hover, .synced-vis-btn:hover {
    background: rgba(255, 255, 255, 0.6);
    color: var(--text-primary, #0f172a);
}
.synced-sweep-btn:focus-visible, .synced-vis-btn:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}

/* Visibility level colours — same grammar as the canonical .visibility-btn
   in the Scheduled settings panel: Private=purple, Busy=amber, Shared=green
   (the app-wide privacy scale from privacy-visual-config.js; red is never
   a privacy colour in this product). */
.synced-vis-btn i, .synced-sweep-btn i { font-size: 11px; }
.synced-vis-btn[data-visibility="hidden"] i,
.synced-sweep-btn[data-visibility="hidden"] i { color: var(--visibility-private, #8b5cf6); }
.synced-vis-btn[data-visibility="busy"] i,
.synced-sweep-btn[data-visibility="busy"] i { color: var(--rc-status-tentative); }
.synced-vis-btn[data-visibility="full"] i,
.synced-sweep-btn[data-visibility="full"] i { color: var(--rc-status-available); }

.synced-vis-btn[data-visibility="hidden"]:hover,
.synced-sweep-btn[data-visibility="hidden"]:hover {
    color: #7c3aed;
    background: rgba(139, 92, 246, 0.08);
    border-color: rgba(139, 92, 246, 0.3);
}
.synced-vis-btn[data-visibility="busy"]:hover,
.synced-sweep-btn[data-visibility="busy"]:hover {
    color: #d97706;
    background: rgba(245, 158, 11, 0.08);
    border-color: rgba(245, 158, 11, 0.3);
}
.synced-vis-btn[data-visibility="full"]:hover,
.synced-sweep-btn[data-visibility="full"]:hover {
    color: var(--rc-status-working);
    background: rgba(34, 197, 94, 0.08);
    border-color: rgba(34, 197, 94, 0.3);
}

/* SELECTED = A LIFTED WHITE CARD, not a saturated fill. The segment rises out
   of the track; the level's hue stays in the icon and the label, so the choice
   is legible by SHAPE first and colour second. This also stops the row from
   flashing a large block of colour per event on a list of twenty. */
.synced-vis-btn.active {
    background: var(--bg-primary, #fff);
    border-color: var(--border-color, #e2e8f0);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
    font-weight: 600;
}
/* Each level states its own committed background explicitly. It is the same
   raised white card in every case — the hue lives in the text and icon — but
   declaring it per level keeps the "committed owns a solid fill" contract
   legible (and checkable) per level, distinct from the dashed staged chip. */
.synced-vis-btn[data-visibility="hidden"].active {
    color: #7c3aed;
    background: var(--bg-primary, #fff);
}
.synced-vis-btn[data-visibility="busy"].active {
    color: #b45309;
    background: var(--bg-primary, #fff);
}
.synced-vis-btn[data-visibility="full"].active {
    color: var(--rc-status-working);
    background: var(--bg-primary, #fff);
}

/* ── Staged (chosen, not yet confirmed) visibility ──────────────────────────
   2026-08-06. Loosening privacy (→ Busy/Shared) is a one-way door: Undo can
   restore the stored value but cannot un-disclose what a teammate already saw.
   So that choice is TICKED first and committed from the confirm bar below.
   The staged chip reads as a selection — dashed ring, tinted, never the solid
   fill that means "saved" (.active above). Tightening → Private still commits
   on the first click, so it has no staged state. */
/* .visibility-btn is the EVENTS tab's copy of the same three-level control.
   It gained the identical loosening gate on 2026-08-06 (it previously had none
   at all, so one tap disclosed an event), so it must wear the same staged
   grammar — same dashed ring, same tint, same "not saved yet" reading. Listing
   both selectors here keeps the two surfaces from drifting into two looks. */
/* TEAL RING = "THIS IS YOUR SELECTION" (2026-08-29, user: "make the selection
   clearer perhaps a teal outline"). The dashed currentColor ring was the same
   hue as the button's own level (purple on Private, amber on Busy, green on
   Shared), so the ring carried NO information the icon didn't already carry —
   at a glance a staged Busy segment and a resting Busy segment differed only
   by a dash pattern. Selection is a state that belongs to the CONTROL, not to
   the level, so it gets one colour across all three: the app's teal accent,
   which appears nowhere in the privacy scale and therefore can only mean
   "chosen". Solid ring + halo, so it reads at arm's length on a phone.

   Still visibly NOT `.active`: committed keeps the raised white card with a
   soft neutral shadow; staged is flat, tinted teal, and ringed. Two different
   answers to "has this been saved?". */
.synced-vis-btn.pending-choice,
.visibility-btn.pending-choice {
    border-style: solid;
    border-color: var(--primary-500, #14b8a6);
    background: rgba(20, 184, 166, 0.1);
    box-shadow: 0 0 0 2px rgba(20, 184, 166, 0.28);
    font-weight: 700;
    /* Sits above its neighbours so the ring is never clipped by the next
       segment's border in the segmented track (gap:0 — they share edges). */
    position: relative;
    z-index: 1;
}
/* The tick that replaces the level icon when staged. Teal to match the ring,
   so icon and outline say the same thing. */
.synced-vis-btn.pending-choice i,
.visibility-btn.pending-choice i {
    color: var(--primary-600, #0d9488);
}
body.dark-mode .synced-vis-btn.pending-choice,
body.dark-mode .visibility-btn.pending-choice {
    border-color: var(--primary-400, #2dd4bf);
    background: rgba(45, 212, 191, 0.16);
    box-shadow: 0 0 0 2px rgba(45, 212, 191, 0.3);
}
body.dark-mode .synced-vis-btn.pending-choice i,
body.dark-mode .visibility-btn.pending-choice i {
    color: var(--primary-300, #5eead4);
}
.synced-row-confirm {
    /* The row is a flex line; take a full-width line of our own beneath it. */
    flex-basis: 100%;
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 8px 10px;
    border-radius: 8px;
    background: rgba(139, 92, 246, 0.08);
    border: 1px solid rgba(139, 92, 246, 0.35);
}
.synced-confirm-text {
    flex: 1;
    min-width: 0;
    font-size: 12px;
    line-height: 1.35;
    color: var(--text-primary, #0f172a);
}
.synced-confirm-cancel,
.synced-confirm-apply {
    flex-shrink: 0;
    padding: 5px 10px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid transparent;
}
.synced-confirm-cancel {
    background: transparent;
    border-color: var(--border-color, #e2e8f0);
    color: var(--text-secondary, #64748b);
}
.synced-confirm-cancel:hover { background: var(--slate-100, #f1f5f9); }
.synced-confirm-apply {
    background: var(--visibility-private, #8b5cf6);
    color: #fff;
}
.synced-confirm-apply:hover { filter: brightness(1.06); }
.synced-confirm-apply:focus-visible,
.synced-confirm-cancel:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--review-focus-ring, rgba(20, 184, 166, 0.4));
}
/* The row must be allowed to wrap for the confirm line to sit under the chips. */
.synced-row { flex-wrap: wrap; }
body.dark-mode .synced-row-confirm {
    background: rgba(139, 92, 246, 0.16);
    border-color: rgba(139, 92, 246, 0.45);
}
body.dark-mode .synced-confirm-text { color: var(--slate-100, #f1f5f9); }
body.dark-mode .synced-confirm-cancel {
    border-color: var(--slate-600, #475569);
    color: var(--slate-300, #cbd5e1);
}
body.dark-mode .synced-confirm-cancel:hover { background: var(--slate-700, #334155); }
/* (The grey dark-mode staged background that used to sit here was removed
   2026-08-29 with the teal selection ring. It came LATER in the file than the
   teal rule and shared its specificity, so it won the cascade and repainted
   the staged segment slate in dark mode — the exact "can't tell what's
   selected" the ring exists to fix. The dark variant now lives beside the
   light one at ~12569.) */
@media (max-width: 480px) {
    /* Stack on a phone so the message isn't squeezed to two words. */
    .synced-row-confirm { flex-wrap: wrap; }
    .synced-confirm-text { flex-basis: 100%; }
}

.synced-tab-content { display: flex; flex-direction: column; }
.synced-list { display: flex; flex-direction: column; }
.synced-row {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
}
.synced-row-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.synced-row-title {
    font-weight: 600; color: var(--text-primary, #0f172a);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Meta is a wrapping inline-flex row so its pills (recurring / approval /
   sensitive / suggested) reflow onto a second line on narrow widths instead
   of overflowing or shoving the action buttons off-row. */
.synced-row-meta {
    font-size: 0.8rem; color: var(--text-secondary, #64748b);
    display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px;
    line-height: 1.5;
}
/* gap:0 — this is a SEGMENTED TRACK, not three loose buttons. A 6px gap here
   was overriding the segmented rule above (this declaration comes later in the
   file and wins), so the segments floated apart inside their own track and the
   divider hairlines had nothing to divide. Verified on the live render at
   390px: real 6px gaps between segments. */
.synced-row-actions { display: flex; gap: 0; flex-shrink: 0; }

/* Suggested (calendar-default) pre-selection for a freshly-synced event.
   A calm amber hint — clearly "recommended, not decided" so the user still
   confirms. Sits in the meta row like the other pills. */
.synced-suggest-pill {
    display: inline-flex; align-items: center; gap: 3px;
    padding: 1px 7px; border-radius: 999px;
    background: rgba(217, 119, 6, 0.14);
    color: #b45309;
    font-size: 0.72rem; font-weight: 600;
}
.synced-suggest-pill i { font-size: 0.7rem; }
body.dark-mode .synced-suggest-pill { background: rgba(217, 119, 6, 0.24); color: #fbbf24; }

/* The vis button matching the suggestion gets a dashed amber ring (distinct
   from the solid-fill `.active` "decided" state) so the recommended tap is
   obvious without looking already-chosen. */
.synced-vis-btn.suggested:not(.active) {
    border-color: #d97706;
    border-style: dashed;
    box-shadow: 0 0 0 1px rgba(217, 119, 6, 0.25) inset;
}
body.dark-mode .synced-vis-btn.suggested:not(.active) {
    border-color: #fbbf24;
    box-shadow: 0 0 0 1px rgba(251, 191, 36, 0.3) inset;
}

.synced-row-newpip {
    display: inline-block; padding: 1px 6px;
    border-radius: 999px; background: var(--accent, #0284c7); color: #fff;
    font-size: 0.65rem; font-weight: 700; vertical-align: middle;
}
.synced-recurring-pill {
    display: inline-flex; align-items: center; gap: 3px;
    padding: 1px 6px; border-radius: 999px;
    background: rgba(148, 163, 184, 0.18);
    color: var(--text-secondary, #475569);
    font-size: 0.72rem; font-weight: 500;
}

.incoming-source-split { display: inline-flex; gap: 6px; align-items: center; }
/* Real filter buttons since 2026-07-27 (were inert <span> decoration that read
   as clickable pills and did nothing). They must LOOK and BEHAVE like controls:
   pointer cursor, hover/active affordance, focus ring, and a 44px touch target
   under coarse pointers. Unselected state is a muted outline so the ACTIVE
   provider is unambiguous — previously both were permanently filled, which is
   why they looked "already selected" and unresponsive. */
.incoming-source-chip {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 2px 8px; border-radius: 999px;
    font-size: 0.75rem; font-weight: 600;
    cursor: pointer;
    border: 1px solid transparent;
    background: rgba(148, 163, 184, 0.18);
    color: var(--text-secondary, #475569);
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.incoming-source-chip:hover { background: rgba(148, 163, 184, 0.3); }
.incoming-source-chip:focus-visible {
    outline: 2px solid var(--accent-color, #0284c7);
    outline-offset: 2px;
}
/* Selected: fill with the provider's brand colour. */
.incoming-source-chip.incoming-source-google.active    { background: #4285f4; color: #fff; border-color: #4285f4; }
.incoming-source-chip.incoming-source-microsoft.active { background: #00a4ef; color: #fff; border-color: #00a4ef; }
/* Unselected: brand-tinted text + border so the provider is still identifiable. */
.incoming-source-chip.incoming-source-google:not(.active)    { color: #1a56c4; border-color: rgba(66, 133, 244, 0.45); }
.incoming-source-chip.incoming-source-microsoft:not(.active) { color: #0369a1; border-color: rgba(0, 164, 239, 0.45); }

@media (hover: none) and (pointer: coarse) {
    .incoming-source-chip { min-height: 44px; padding: 2px 12px; }
}

/* Zero-count filter chip: dimmed but PRESENT and still clickable. "Recent" used
   to be omitted entirely at 0, so its absence read as a missing control rather
   than an empty bucket. Not `disabled` — clicking it should still switch to the
   empty view and explain itself. (2026-07-27) */
.privacy-filter-chip.is-empty:not(.active) { opacity: 0.55; }
.privacy-filter-chip.is-empty:not(.active):hover { opacity: 0.85; }

.incoming-onboard {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 12px 14px;
    background: var(--accent-bg, rgba(2, 132, 199, 0.06));
    border-bottom: 1px solid var(--border-color, #e2e8f0);
    color: var(--text-primary, #0f172a);
}
/* Collapsed-by-default <details> variant (2026-06-29): the privacy explainer
   shows as a one-line summary so it no longer fills the first phone screen.
   The root is a block; the summary carries the flex row. */
details.incoming-onboard-details {
    display: block;
    padding: 0;
}
.incoming-onboard-summary {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 14px;
    cursor: pointer;
    list-style: none;
    font-size: 0.85rem;
}
.incoming-onboard-summary::-webkit-details-marker { display: none; }
.incoming-onboard-summary strong { flex: 1 1 auto; font-weight: 600; }
.incoming-onboard-chev {
    flex-shrink: 0; font-size: 0.7rem; color: var(--text-secondary, #64748b);
    transition: transform 0.2s ease;
}
details.incoming-onboard-details[open] .incoming-onboard-chev { transform: rotate(180deg); }
.incoming-onboard-details .incoming-onboard-body {
    padding: 0 14px 12px 40px;
}

/* The review-first CONSEQUENCE line (2026-08-25): unlike the collapsible
   explainer above it, this is not dismissable — while rows sit pending the
   user's availability reads as open to the group, and that stays true no
   matter how many times they've seen the note. Quiet, one line, no box. */
.incoming-pending-consequence {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 2px 2px 8px;
    font-size: 0.78rem;
    color: var(--text-secondary, #64748b);
}
.incoming-pending-consequence i {
    font-size: 0.7rem;
    color: var(--slate-400, #94a3b8);
    flex-shrink: 0;
}
body.dark-mode .incoming-pending-consequence { color: var(--slate-400, #94a3b8); }
.incoming-onboard-icon {
    flex-shrink: 0; padding-top: 2px;
    color: var(--accent, #0284c7); font-size: 1rem;
}
.incoming-onboard-body {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-direction: column; gap: 2px;
    font-size: 0.85rem; line-height: 1.4;
}
.incoming-onboard-body strong { font-weight: 600; }
.incoming-onboard-body span { color: var(--text-secondary, #64748b); }
.incoming-onboard-dismiss {
    flex-shrink: 0; padding: 4px;
    background: transparent; border: none; cursor: pointer;
    color: var(--text-secondary, #64748b);
    border-radius: 4px;
}
.incoming-onboard-dismiss:hover {
    background: rgba(148, 163, 184, 0.15);
    color: var(--text-primary, #0f172a);
}
.incoming-onboard-dismiss:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}

.incoming-empty { text-align: center; padding: 32px 20px; }
.incoming-empty-locked .empty-icon { color: var(--accent, #0284c7); }
.incoming-connect-btn { margin-top: 14px; }

@media (max-width: 640px) {
    /* Stack the policy chip under the (scrollable) filter chips on narrow
       screens so it isn't squeezed against them. */
    /* ⭐2026-08-20 (owner: "the tabs should fit in the row not stack").
       This block flipped .privacy-chip-row to a COLUMN on the assumption that
       its chips live inside a .privacy-filter-chips wrapper (which the rule
       below then scrolls horizontally). The renderer emits NO such wrapper —
       review-dashboard.js puts .privacy-filter-chip buttons DIRECTLY in
       .privacy-chip-row — so on every phone the three chips stretched to the
       full 279px width and stacked into three rows (measured live: tops 141 /
       179 / 217). Only take the column when the wrapper is actually present;
       otherwise keep one horizontally-scrolling row, matching the wrapper
       treatment. :has() is safe here — the modal already relies on it and the
       no-:has fallback is the pre-existing stacked layout. */
    .privacy-chip-row:has(.privacy-filter-chips) {
        flex-direction: column;
        align-items: stretch;
        gap: 6px;
        padding: 8px 12px 4px;
    }
    .privacy-chip-row:not(:has(.privacy-filter-chips)) {
        flex-direction: row;
        align-items: center;
        gap: 6px;
        padding: 8px 12px 4px;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        /* Right-edge fade so it reads as "more chips off-screen", the same
           affordance the wrapper variant gets. */
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
        mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
    }
    .privacy-chip-row:not(:has(.privacy-filter-chips))::-webkit-scrollbar { display: none; }
    /* Chips size to their own label instead of stretching to the row. */
    .privacy-chip-row:not(:has(.privacy-filter-chips)) > .privacy-filter-chip {
        flex: 0 0 auto;
        width: auto;
        align-self: center;
    }
    .privacy-filter-chips {
        flex-wrap: nowrap; overflow-x: auto;
        padding: 0;
        /* Hide the scrollbar but keep a right-edge fade so it's clear there
           are more chips off the right than fit (e.g. All/Sync/Approval/
           Sensitive/Recent on a 390px row). */
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
        mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
    }
    .privacy-filter-chips::-webkit-scrollbar { display: none; }
    .privacy-policy-chip { margin-left: 0; align-self: flex-start; }
    .privacy-filter-chip { flex-shrink: 0; }
    .synced-sweep-bar { flex-direction: column; align-items: stretch; }
    /* TIGHTEN THE STACKED ROW (2026-08-16, user: "Extra row space").
       Flipping to a column turned the 12px row gap into ~24px of vertical
       dead space per row (checkbox band + info band + action band), on top of
       10px padding — so a list of three-line rows scrolled far longer than it
       had content for. The axis flip was the only thing this block changed;
       the spacing that suited a single horizontal row was never revisited. */
    .synced-row {
        flex-direction: column;
        align-items: stretch;
        gap: 6px;
        padding: 8px 12px;
    }
    /* The segmented track spans the stacked row, so the three segments divide
       one full-width control rather than floating as separate buttons. */
    .synced-row-actions { justify-content: stretch; width: 100%; }
    /* Both sweep buttons AND per-row vis buttons stretch full-width in the
       stacked column so the Hidden/Busy/Shared sweep row matches the rows
       below it instead of sitting left-aligned at natural width. */
    .synced-vis-btn,
    .synced-sweep-btn { flex: 1; text-align: center; justify-content: center; }
}

body.dark-mode .privacy-filter-chip { background: var(--bg-primary, #1e293b); }
body.dark-mode .privacy-chip-row,
body.dark-mode .synced-sweep-bar { background: rgba(15, 23, 42, 0.4); }
body.dark-mode .incoming-onboard { background: rgba(2, 132, 199, 0.12); }
body.dark-mode .synced-recurring-pill { background: rgba(148, 163, 184, 0.22); }
body.dark-mode .synced-sweep-btn:hover,
body.dark-mode .synced-vis-btn:hover { background: rgba(148, 163, 184, 0.1); }

/* ============================================================
   Privacy — Reviewed-mode rows (April §2.2 history view)
   ============================================================ */

.synced-row.synced-row-reviewed { opacity: 0.92; }

.synced-row-decided {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 4px 10px; border-radius: 999px;
    font-size: 0.78rem; font-weight: 600;
}
/* Decided pills — Private=purple, Busy=amber, Shared=green (the app-wide
   privacy scale). Hidden was grey, which read as "neutral/unset" and disagreed with the
   active vis buttons; now it matches the rest of the visibility palette. */
.synced-row-decided-hidden { background: rgba(139, 92, 246, 0.16); color: #6d28d9; }
.synced-row-decided-busy   { background: rgba(217, 119, 6, 0.22);   color: #b45309; }
.synced-row-decided-full   { background: rgba(16, 185, 129, 0.22);  color: #047857; }

.synced-rereview-btn {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 5px 10px; border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: transparent; cursor: pointer;
    font-size: 0.8rem;
    color: var(--text-secondary, #64748b);
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.synced-rereview-btn:hover {
    background: var(--bg-secondary, #f1f5f9);
    border-color: var(--accent, #0284c7);
    color: var(--accent, #0284c7);
}
.synced-rereview-btn:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}

/* April §2.3 — compliance export row */
.incoming-export-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 14px;
    border-top: 1px solid var(--border-color, #e2e8f0);
}
.incoming-export-btn {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 6px 12px; border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: transparent; cursor: pointer;
    font-size: 0.85rem;
    color: var(--text-primary, #0f172a);
    transition: background 0.12s ease, border-color 0.12s ease;
}
.incoming-export-btn:hover {
    background: var(--bg-secondary, #f1f5f9);
    border-color: var(--accent, #0284c7);
}
.incoming-export-btn:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}
.incoming-export-hint {
    font-size: 0.78rem;
    color: var(--text-secondary, #64748b);
}

body.dark-mode .synced-rereview-btn:hover { background: rgba(148, 163, 184, 0.1); }
body.dark-mode .incoming-export-btn:hover { background: rgba(148, 163, 184, 0.1); }
body.dark-mode .synced-row-decided-hidden { background: rgba(139, 92, 246, 0.24); color: #a78bfa; }
body.dark-mode .synced-row-decided-busy   { background: rgba(217, 119, 6, 0.28);   color: #fbbf24; }
body.dark-mode .synced-row-decided-full   { background: rgba(16, 185, 129, 0.28);  color: #34d399; }

/* ============================================
   KEYBOARD SHORTCUTS OVERLAY
   ============================================ */
.rc-shortcuts-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.rc-shortcuts-panel {
    background: var(--review-card-bg, #fff);
    border: 1px solid var(--review-card-border, rgba(203, 213, 225, 0.5));
    border-radius: var(--rc-radius-card, 8px);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
    padding: 18px 22px;
    min-width: 320px;
    max-width: 420px;
    width: 90%;
}

.rc-shortcuts-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.rc-shortcuts-header h3 {
    margin: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--slate-800);
}

.rc-shortcuts-close {
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-500, #64748b);
    border: none;
    border-radius: var(--radius-full, 9999px);
    width: 32px;
    height: 32px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
}

.rc-shortcuts-close:hover {
    background: var(--slate-200, #e2e8f0);
    color: var(--slate-700, #334155);
}

.rc-shortcuts-list {
    margin: 0;
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 8px 16px;
    font-size: 13px;
}

.rc-shortcuts-list dt {
    margin: 0;
    text-align: right;
    color: var(--slate-700);
}

.rc-shortcuts-list dd {
    margin: 0;
    color: var(--slate-600);
}

.rc-shortcuts-list kbd {
    display: inline-block;
    padding: 1px 6px;
    background: var(--slate-100, #f1f5f9);
    border: 1px solid var(--slate-300, #cbd5e1);
    border-bottom-width: 2px;
    border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--slate-700);
    line-height: 1.4;
}

body.dark-mode .rc-shortcuts-panel {
    background: var(--surface-secondary, #1e293b);
    border-color: var(--slate-700, #334155);
}

body.dark-mode .rc-shortcuts-header h3 { color: var(--slate-100, #f1f5f9); }
body.dark-mode .rc-shortcuts-list dt   { color: var(--slate-200, #e2e8f0); }
body.dark-mode .rc-shortcuts-list dd   { color: var(--slate-300, #cbd5e1); }
body.dark-mode .rc-shortcuts-list kbd {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-600, #475569);
    color: var(--slate-200, #e2e8f0);
}
body.dark-mode .rc-shortcuts-close {
    background: var(--slate-700, #334155);
    color: var(--slate-300, #cbd5e1);
}
body.dark-mode .rc-shortcuts-close:hover {
    background: var(--slate-600, #475569);
    color: var(--slate-100, #f1f5f9);
}

/* ============================================
   PROPOSAL DETAIL OVERLAY (View Details / see full details)
   ============================================ */
.rc-proposal-detail-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.45);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    padding: 16px;
}
.rc-pd-panel {
    background: var(--review-card-bg, #fff);
    border: 1px solid var(--review-card-border, rgba(203, 213, 225, 0.5));
    border-radius: var(--rc-radius-card, 8px);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25);
    width: 92%;
    max-width: 480px;
    max-height: 86vh;
    display: flex;
    flex-direction: column;
}
.rc-pd-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
    flex-shrink: 0;
}
.rc-pd-header h3 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--text-primary, #1e293b);
}
.rc-pd-close {
    background: var(--slate-100, #f1f5f9);
    color: var(--slate-500, #64748b);
    border: none;
    border-radius: var(--radius-full, 9999px);
    width: 32px;
    height: 32px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    flex-shrink: 0;
}
.rc-pd-close:hover { background: var(--slate-200, #e2e8f0); color: var(--slate-700, #334155); }
.rc-pd-body {
    padding: 16px 20px 20px;
    overflow-y: auto;
}
.rc-pd-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}
.rc-pd-status {
    font-size: 12px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 9999px;
}
.rc-pd-status-open   { background: rgba(34, 197, 94, 0.16);  color: #15803d; }
.rc-pd-status-closed { background: rgba(148, 163, 184, 0.22); color: #475569; }
.rc-pd-status-expired{ background: rgba(239, 68, 68, 0.16);  color: #b91c1c; }
.rc-pd-deadline {
    font-size: 12px;
    color: var(--text-secondary, #64748b);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}
.rc-pd-description {
    margin: 0 0 16px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-primary, #334155);
    white-space: pre-wrap;
}
.rc-pd-section-title {
    margin: 12px 0 8px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-secondary, #64748b);
}
.rc-pd-slots { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.rc-pd-slot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 6px 10px;
    padding: 8px 10px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 8px;
}
.rc-pd-slot.leading { border-color: rgba(34, 197, 94, 0.4); background: rgba(34, 197, 94, 0.05); }
.rc-pd-slot-when { font-size: 13px; font-weight: 500; color: var(--text-primary, #1e293b); display: inline-flex; align-items: center; gap: 6px; }
/* Standings medals (2026-08-06). Replaces the binary trophy: the leading time
   had one, and a close second looked identical to last place. Gold keeps the
   trophy so the winner still reads as the winner at a glance; 2nd/3rd take
   medals. Used on RESULTS rows only — the rank circles stay numbered, because
   they are an input and the number is what teaches "1 is your top choice".
   Metal tones are deliberately literal here; this is the one place in the app
   where gold/silver/bronze IS the meaning, so it does not borrow the status
   palette (green/amber/red never means "second place"). */
.rc-standing {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    flex-shrink: 0;
    line-height: 1;
}
.rc-standing--gold   { color: #d97706; }
.rc-standing--silver { color: #94a3b8; }
.rc-standing--bronze { color: #b45309; }
/* Bronze on a dark surface is nearly invisible at 11px, and silver goes muddy;
   lift both rather than leaving the runners-up unreadable in dark mode. */
body.dark-mode .rc-standing--gold   { color: #fbbf24; }
body.dark-mode .rc-standing--silver { color: #cbd5e1; }
body.dark-mode .rc-standing--bronze { color: #d97706; }
.rc-pd-trophy { color: #d97706; font-size: 11px; }
.rc-pd-slot-stats { display: inline-flex; align-items: center; gap: 10px; flex-shrink: 0; }
.rc-pd-empty { font-size: 13px; color: var(--text-secondary, #64748b); padding: 8px 2px; }
.rc-pd-roster { display: flex; flex-wrap: wrap; gap: 6px; }
.rc-pd-person {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 500;
    padding: 3px 10px;
    border-radius: 9999px;
}
.rc-pd-person.voted   { background: rgba(34, 197, 94, 0.14);  color: #15803d; }
.rc-pd-person.pending { background: rgba(148, 163, 184, 0.18); color: #64748b; }
.rc-pd-roster-hint { margin-top: 8px; font-size: 12px; color: var(--text-secondary, #64748b); }

body.dark-mode .rc-pd-panel { background: var(--surface-secondary, #1e293b); border-color: var(--slate-700, #334155); }
body.dark-mode .rc-pd-header { border-color: var(--slate-700, #334155); }
body.dark-mode .rc-pd-header h3 { color: var(--slate-100, #f1f5f9); }
body.dark-mode .rc-pd-description { color: var(--slate-200, #e2e8f0); }
body.dark-mode .rc-pd-slot { border-color: var(--slate-700, #334155); }
body.dark-mode .rc-pd-slot-when { color: var(--slate-100, #f1f5f9); }
body.dark-mode .rc-pd-close { background: var(--slate-700, #334155); color: var(--slate-300, #cbd5e1); }
body.dark-mode .rc-pd-close:hover { background: var(--slate-600, #475569); color: var(--slate-100, #f1f5f9); }


/* Touch target floor for the review tab icons. (The old <=768px "collapse the
   review cluster to icon-only + wrap the toolbar to 2 rows" rules were removed
   2026-06-17: mobile now renders the whole app at a desktop width and scrolls the
   page — see "MOBILE = A DESKTOP-WIDTH APP" in style.css — so the toolbar keeps
   its one-row desktop layout with full labels and the stacked "Review" cluster.
   There's room for it, and it reads clearer than the collapsed/wrapped variants.) */
@media (max-width: 768px) {
  .review-icon-btn {
    min-width: 32px;
    min-height: 32px;
  }
}

/* ============================================
   REQUESTS TAB — pending join requests (admin)
   ============================================ */
.review-requests {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 4px 2px;
}

.review-request-card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--border-color, #e2e8f0);
  border-radius: 12px;
  background: var(--card-bg, #fff);
}

.review-request-card .request-avatar {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--amber-100, #fef3c7);
  color: var(--amber-600, #d97706);
  font-size: 16px;
}

.review-request-card .request-body {
  flex: 1;
  min-width: 0;
}

.review-request-card .request-headline {
  margin: 0;
  font-size: 0.875rem;
  color: var(--text-primary, #334155);
  line-height: 1.4;
}

/* Identity lines on a join-request card (2026-08-14). The email IS the
   identity when display_name is null, so it must not read as throwaway
   metadata; and neither-name-nor-email must warn, because approving is an
   access-control decision. */
.review-request-card .request-email {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--slate-600, #475569);
  overflow-wrap: anywhere;
}
.review-request-card .request-unidentified {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 5px;
  padding: 5px 8px;
  border-radius: var(--rc-radius-btn, 6px);
  background: var(--rc-attention-surface, #fff7ed);
  color: var(--rc-attention-ink, #7c2d12);
  font-size: 0.72rem;
  line-height: 1.35;
}
body.dark-mode .review-request-card .request-email { color: var(--slate-300, #cbd5e1); }

.review-request-card .request-when {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font-size: 0.72rem;
  color: var(--slate-500, #64748b);
}

.review-request-card .request-actions {
  display: flex;
  gap: 8px;
  flex-shrink: 0;
}

.review-request-card .request-actions .btn {
  padding: 6px 12px;
  font-size: 0.8rem;
  white-space: nowrap;
}

body.dark-mode .review-request-card {
  background: var(--slate-800, #1e293b);
  border-color: var(--slate-700, #334155);
}

body.dark-mode .review-request-card .request-headline {
  color: var(--slate-100, #f1f5f9);
}

body.dark-mode .review-request-card .request-avatar {
  background: rgba(217, 119, 6, 0.2);
  color: var(--amber-300, #fcd34d);
}

/* On narrow screens the actions wrap below the headline. */
@media (max-width: 520px) {
  .review-request-card {
    flex-wrap: wrap;
  }
  .review-request-card .request-actions {
    width: 100%;
    justify-content: flex-end;
  }
}

/* ── Live-update banner ──────────────────────────────────────────────────
   Surfaces realtime changes from teammates WITHOUT repainting the list
   underneath a half-made decision. Re-rendering on every incoming event
   moves the row the user is reaching for, so the refresh is offered rather
   than forced. Sits at the top of the tab panel; removed on apply, dismiss,
   or any real re-render. (2026-07-29) */
.rc-live-update-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  margin-bottom: 12px;
  border: 1px solid var(--primary-500, #14b8a6);
  border-radius: var(--radius-md, 8px);
  background: rgba(20, 184, 166, 0.08);
  font-size: 0.875rem;
}

.rc-live-update-text {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  color: var(--slate-700, #334155);
}

.rc-live-update-banner .btn.rc-live-update-apply {
  flex-shrink: 0;
  padding: 4px 12px;
  font-size: 0.8125rem;
}

.rc-live-update-dismiss {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: var(--radius-sm, 4px);
  color: var(--slate-500, #64748b);
  line-height: 1;
}

.rc-live-update-dismiss:hover { color: var(--slate-700, #334155); }
.rc-live-update-dismiss:focus-visible,
.rc-live-update-banner .btn.rc-live-update-apply:focus-visible {
  outline: 2px solid var(--primary-500, #14b8a6);
  outline-offset: 2px;
}

body.dark-mode .rc-live-update-banner {
  background: rgba(20, 184, 166, 0.12);
  border-color: var(--primary-500, #14b8a6);
}

body.dark-mode .rc-live-update-text { color: var(--slate-100, #f1f5f9); }
body.dark-mode .rc-live-update-dismiss { color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-live-update-dismiss:hover { color: var(--slate-100, #f1f5f9); }

/* Narrow screens: let the text wrap above the actions rather than squashing
   the Refresh button to an unreadable width. */
@media (max-width: 520px) {
  .rc-live-update-banner { flex-wrap: wrap; }
  .rc-live-update-text { width: 100%; }
}

/* ── CONDENSED "My Proposals" CARD (2026-07-30) ─────────────────────────────
   User: "seems visually inconsistent to my typical well laid out minimalist
   vibes. perhaps too many colours and info. consider what can be condensed or
   hidden."

   The card was competing with itself — SIX colours and FOUR overlapping status
   signals that all said the same thing before anyone voted. These rules support
   the markup changes in renderMyProposalCard(): type as a glyph, times
   collapsed until there is something to compare, secondary actions in a ⋯ menu.
   Deliberately NO new colour: everything here is neutral so the turnout bar and
   an urgent deadline stay the only things that can shout. */

/* Event type: a muted glyph, not a coloured chip. */
.rc-type-glyph {
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-400);
    margin-right: 6px;
    flex: 0 0 auto;
}

/* Collapsed time list — one option plus a "+N more" affordance. */
.my-proposal-times.times-collapsed .times-list {
    gap: 4px;
}

.times-more-btn {
    align-self: flex-start;
    background: none;
    border: none;
    padding: 2px 0;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: var(--slate-500);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.times-more-btn:hover { color: var(--slate-700); }

/* Single action row: primary on the right, everything else behind ⋯ */
.rc-actions-condensed .my-proposal-actions-right {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

.rc-overflow-wrap { position: relative; display: inline-flex; }

.rc-overflow-btn {
    padding: 6px 10px;
    line-height: 1;
    color: var(--slate-500);
}

.rc-overflow-btn:hover { color: var(--slate-700); }

.rc-overflow-menu {
    position: absolute;
    right: 0;
    bottom: calc(100% + 6px);
    min-width: 168px;
    background: #fff;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
    padding: 4px;
    z-index: 30;
    display: flex;
    flex-direction: column;
}

.rc-overflow-menu[hidden] { display: none; }

.rc-overflow-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: none;
    border: none;
    border-radius: 7px;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--slate-700);
    text-align: left;
    cursor: pointer;
}

.rc-overflow-item:hover { background: var(--slate-100, #f1f5f9); }

.rc-overflow-item.rc-overflow-danger { color: var(--rc-status-busy, #dc2626); }

body.dark-mode .rc-overflow-menu {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}

body.dark-mode .rc-overflow-item { color: var(--slate-200, #e2e8f0); }
body.dark-mode .rc-overflow-item:hover { background: var(--slate-700, #334155); }

/* ═══════════════════════════════════════════════════════════════════════
   REVIEW CENTER — DENSITY PASS (2026-08-04)
   User: "redesign review center with a more logical and less space wasting
   layout, space should focus on useful info."

   Measured on a 430px viewport BEFORE this pass: a single proposal card was
   300px tall, of which ~180px carried no signal:
     .proposal-header          109px  (a "Deadline: No deadline" line, mostly)
     .proposal-closed-summary   62px  (one sentence, boxed + padded)
     .proposal-vote-summary     12px  (rendered EMPTY)
     .proposal-options-section   0px  (collapsed, contributing nothing)
   Four cards filled the screen and the user could not scan their queue.

   The rules below are DENSITY ONLY — no colour, no hierarchy, no behaviour
   changes. Every one targets measured dead space, and the strongest cuts are
   scoped to EXPIRED cards, which are pure backlog: they carry no decision, so
   they collapse to a scannable one-liner while live cards keep full detail.
   ═══════════════════════════════════════════════════════════════════════ */

/* 1. Kill the empty vote-summary box. It reserves height even with no content,
      once per card, purely as a spacer. */
.proposal-vote-summary:empty {
    display: none !important;
}

/* 2. "Deadline: No deadline" is an absence of information taking a full line.
      Suppress the label when there is no deadline; a real deadline still shows. */
.proposal-deadline-area:has(.proposal-deadline-label) {
    display: none !important;
}

/* 3. The closed/expired summary is one sentence in a padded, bordered card.
      Keep the sentence, drop the box. */
.proposal-closed-summary {
    padding: 6px 8px !important;
    margin: 4px 0 !important;
    gap: 6px !important;
}
.proposal-closed-summary .proposal-closed-sub,
.proposal-closed-summary p + p {
    font-size: 0.75rem !important;
}

/* 4. EXPIRED cards collapse to a scannable row: title, who, and Dismiss.
      An expired proposal has no decision left, so the explanatory sentence and
      the meta line are noise — the "Expired" tag already says everything. */
.proposal-item.proposal-expired .proposal-closed-summary,
.proposal-item.proposal-expired .proposal-meta-line,
.proposal-item.proposal-expired .proposal-status-line,
.proposal-item.proposal-expired .proposal-vote-summary {
    display: none !important;
}
.proposal-item.proposal-expired {
    padding: 8px 10px !important;
    margin-bottom: 6px !important;
}
.proposal-item.proposal-expired .proposal-header {
    margin-bottom: 0 !important;
    padding-bottom: 0 !important;
}
/* Actions sit inline on the collapsed row rather than owning their own band. */
.proposal-item.proposal-expired .proposal-actions {
    margin-top: 4px !important;
    padding-top: 0 !important;
    border-top: 0 !important;
}

/* 5. Tighten the shared card frame. 300px -> the content's real height. */
.proposal-item {
    padding: 10px 12px;
    margin-bottom: 8px;
}
.proposal-item .proposal-header {
    margin-bottom: 6px;
}
.proposal-item .proposal-title-row {
    gap: 6px;
    margin-bottom: 4px;
}
.proposal-item .proposal-meta-line {
    margin-bottom: 4px;
}

/* 6. The tab strip was 92px on a 430px screen — a quarter of the viewport
      above the first card. Bring it back to a single scannable row. */
.review-tabs,
.rc-tabrow {
    padding-top: 4px !important;
    padding-bottom: 4px !important;
    row-gap: 4px !important;
}

/* 7. The priority badge floated as a 24px square in its own gutter. Keep it
      legible but stop it reserving a column of whitespace. */
.proposal-priority-badge {
    width: 18px !important;
    height: 18px !important;
    font-size: 0.6875rem !important;
}


/* ============================================================
   TWO-ROW STRIP — PHONE (2026-08-04)
   ============================================================
   Appended last so these win over the older per-breakpoint patches without
   having to unpick each one.

   The goal on a phone is that NOTHING IS LOST, only compacted. The previous
   approach hid filters outright below 480px, trading function for width; with
   the two-row split there IS width to spend, so every control survives in a
   smaller form.

   Net at 390px: one 44px nav row + one 36px tool row, versus the old single row
   that wrapped to two or three depending on label lengths.
   ============================================================ */
@media (max-width: 480px) {
    .rc-navrow { gap: 6px; }

    /* ⭐THE TABS STOP SCROLLING (owner 2026-09-04: "I want icons that when
       clicked show the text, currently we have a scroll like mechanism").
       The icon-collapse design was already correct — inactive tabs are icons,
       the active one names itself — but the strip was overflowing anyway, so
       the designed overflow-x fallback fired and it read as a swipe UI.

       Cause: renderNavRowControls() migrates the search + filters cluster
       INTO the nav row on any tab whose tool row has no triage anchor
       (review-dashboard.js:4297). That cluster is `flex: 0 0 auto`, so it
       never yields, and the tabs — the row's only shrinkable item — absorb
       the whole squeeze. Measured at 390px: nav padding 32 + close 44 +
       gaps 24 + cluster 94 leaves 196px for tabs that need ~266. A 70px
       deficit, every time, on Events and Proposals.

       The two-row split already exists precisely so "where you are" and
       "what you're looking at" stop competing. Send the cluster back to the
       tool row it was designed for on phones; the tabs then fit with room to
       spare and the scroll never triggers. Desktop keeps the migration,
       where the row is wide enough to hold both.

       ⛔NOT `display: none` on the cluster: renderNavRowControls() only
       returns markup when the tool row has NO anchor, in which case the tool
       row is dropped entirely (renderToolRow returns '' and the caller
       removes it). Hiding the nav-row copy would therefore delete search and
       filters outright on exactly the tabs that have nowhere else to put
       them. The cluster stays; it just stops being unshrinkable. */
    .rc-navrow .rc-inline-controls {
        flex: 0 1 auto;
        min-width: 0;
    }
    /* The buttons already collapse to icons on a phone (review-dashboard.js
       :4118 "on a phone the button collapses to an icon"), so nothing else
       needs hiding — the cluster simply has to stop refusing to shrink. */

    /* Sync is a DESKTOP power action: the Review Center already refreshes on
       its own sync events, so a manual refresh button is redundant chrome on a
       phone. Hidden here and surfaced in the overflow menu instead (see
       #rdh-sync-item), which leaves exactly ONE right-side control on a phone —
       Close — the only one that is always needed. */
    .rc-navrow .rdh-actions #rdh-sync-btn { display: none; }

    /* ⋯ MUST NOT GET A ROW OF ITS OWN (2026-08-29, user: "we have a whole row
       for a 3 dot menu?").
       ------------------------------------------------------------------
       MEASURED at 390px before this rule: the nav row was 100px tall, not the
       44px this two-row design intends, and the overflow button sat at
       top:70px / left:18px — a full line containing nothing but "⋯" and ~340px
       of empty space, while Close stayed on line 1 at top:24px.

       Cause: the legacy single-row patches (~9695 and ~10003) set
       `.rc-tabrow .rdh-actions { display: contents }` to dissolve the action
       cluster so its children could be ORDERED individually across a wrapping
       row. .rc-navrow is also a .rc-tabrow and is `flex-wrap: wrap`, so those
       children became independent flex items that wrap SEPARATELY — the button
       group stopped being a group. Nothing pinned them back together, so the
       first one that did not fit took a new line alone.

       Restore the cluster as ONE unwrappable box on the phone strip. It is a
       button group; it should break as a unit or not at all. This also re-nests
       .rdh-overflow inside a positioned parent, which is where its dropdown
       expects to anchor from. */
    .rc-navrow .rdh-actions {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        flex: 0 0 auto;
        flex-wrap: nowrap;
        margin-left: auto;
    }
    /* The tab strip is the only elastic thing here, so it absorbs the width the
       cluster needs instead of pushing the cluster onto a second line. It
       already scrolls horizontally, so yielding width costs no tabs. */
    /* grow 1→0, basis 0%→auto (2026-09-01 compact ruling): the ELASTIC part
       this rule exists for is the SHRINK — the strip yields width to the
       control cluster and scrolls, so nowrap below stays a rule, not a hope.
       Growing it only stretched the grey pill into the reclaimed space. */
    .rc-navrow .review-tabs { flex: 0 1 auto; min-width: 0; }

    /* And the row itself does not wrap AT ALL on a phone. The comment block
       above this media query promises "one 44px nav row"; `flex-wrap: wrap`
       (inherited from the legacy single-row patches) made that a hope rather
       than a rule, and any group that did not fit silently bought a new line.
       With the tab strip elastic and scrollable, there is always a valid
       single-line solution, so forbid the wrap and let the tabs absorb the
       pressure. */
    .rc-navrow { flex-wrap: nowrap; }
    /* The brand shrinks before anything else — it is the only purely decorative
       item in the row, and its text already clips to an icon at narrow widths. */
    .rc-navrow .rdh-title { flex: 0 1 auto; min-width: 0; overflow: hidden; }

    /* NEUTRALISE THE LEGACY ORDER LADDER inside the restored cluster.
       ------------------------------------------------------------------
       The single-row design gave each dissolved child its own slot on one
       wrapping line: title 1, tabs 2, close 3, controls 4, overflow 6. Those
       numbers only ever made sense while the children WERE the flex items.
       Now that .rdh-actions is a box again they resolve against its internal
       axis instead, which sorted Close (3) ahead of ⋯ (6) inside the cluster
       and — because the cluster itself carries no order, defaulting to 0 — put
       the whole group BEFORE the tabs. Measured: ⋯ and Close rendered at the
       far LEFT of the strip, ahead of "Events".
       Reset to source order and let `margin-left: auto` (above) do the
       positioning, which is the two-row design's actual intent. */
    .rc-navrow .rdh-actions,
    .rc-navrow .rdh-title,
    .rc-navrow .review-tabs,
    .rc-navrow .rdh-actions > *,
    .rc-navrow #close-review-modal,
    .rc-navrow .rdh-overflow { order: 0; }

    /* SAME LEGACY LEAK, one control over (2026-09-01, owner screenshot: the
       Proposals tab crushed under its own outline). The single-row patches at
       ~9720/~10023 set `.rc-tabrow .rc-inline-controls { flex: 1 1 auto }` so
       row 2's controls would "own the slack" — but .rc-navrow IS a .rc-tabrow,
       those rules sit LATER in source than the navrow's own `flex: 0 0 auto`
       (~1825), and on the phone strip the cluster GREW to a measured 185px
       (its children total 82px) while the tab track starved at 83px. Re-assert
       content sizing + source order for the navrow ride, mirroring the
       rdh-actions reset above. */
    .rc-navrow .rc-inline-controls {
        flex: 0 0 auto;
        order: 0;
        margin-left: auto;
    }

    .rc-toolrow { gap: 6px; }

    /* Filters collapses to an ICON + active-count badge. The LABEL is what
       costs width, not the control, so the label goes and the affordance stays
       — and the count badge makes active filters more visible than the old
       single dot, not less.
       ⭐ALSO scoped to .rc-navrow (2026-09-01). This rule was written when the
       controls lived on the tool row; the 2026-08-04 follow-up MIGRATED them
       onto the nav row for tabs without triage (Proposals/Scheduled/Privacy),
       and the collapse silently stopped matching there — so a full "Filters ▾"
       label rode the phone nav row, the ~209px control cluster crushed the tab
       track to 83px, and the active tab broke (see .review-tab.active above).
       Same control, same width problem, same collapse — wherever it rides. */
    .rc-toolrow .rc-filters-btn-label,
    .rc-toolrow .rc-filters-btn-chevron,
    .rc-navrow .rc-filters-btn-label,
    .rc-navrow .rc-filters-btn-chevron { display: none; }
    .rc-toolrow .rc-filters-btn,
    .rc-navrow .rc-filters-btn {
        min-width: 40px;
        justify-content: center;
        padding-left: 10px;
        padding-right: 10px;
    }

    /* Triage segments KEEP THEIR WORDS (2026-09-21, owner: "unfamiliar icons
       like hourglass and clock"). Dropping the label left a bare ⌛ and 🕐
       beside a number — glyphs that name nothing on their own, and the two
       most easily confused with each other in this panel (both say "time").
       The accessible name still carried the phrase, so a screen reader was
       fine while the SIGHTED narrow-window user was the one left guessing.
       The label is what makes the segment legible, so it stays and the strip
       scrolls instead; only the padding tightens. */
    .rc-toolrow .rc-triage-seg {
        padding-left: 8px;
        padding-right: 8px;
    }
    .rc-toolrow .rc-triage-label {
        font-size: 0.68rem;
    }
    /* The strip may now exceed the row on a very narrow phone; let it scroll
       rather than clip a word in half. */
    .rc-toolrow .rc-triage-strip {
        overflow-x: auto;
        scrollbar-width: none;
        flex-wrap: nowrap;
    }
    .rc-toolrow .rc-triage-strip::-webkit-scrollbar { display: none; }
    .rc-toolrow .rc-triage-seg { flex: 0 0 auto; }
}

/* Very narrow (<=360px): tab labels give way to icons + badges. The tab row
   scrolls rather than wraps (see .rc-navrow .review-tabs), so this is a
   legibility choice, not a rescue — three icon tabs read cleanly at this width
   where three labelled pills do not. Each tab button carries its full name in
   aria-label, so assistive tech loses nothing. */
@media (max-width: 360px) {
    /* ⭐EXCEPT THE ACTIVE TAB (2026-09-07). This rule predates the phone
       block's `.review-tab.active .tab-icon { display: none }` — with BOTH
       live the active tab lost its label here AND its icon, and rendered as a
       measured 18px empty stub: the one tab that must say where you are
       became the only one saying nothing. The inactive tabs still collapse to
       icons as intended. */
    .rc-navrow .review-tab:not(.active) .tab-label { display: none; }
    .rc-navrow .review-tab {
        padding-left: 10px;
        padding-right: 10px;
    }
    /* 320-360px is where the labelled active tab is tightest, so let it
       shorten its own word rather than push a neighbour off the track. */
    .rc-navrow .review-tab.active .tab-label {
        overflow: hidden;
        text-overflow: ellipsis;
        max-width: 92px;
    }
    /* Measured at 320px: the four tabs total 39.7px more than the track. The
       track scrolls (overflow-x:auto) so nothing is lost, but a strip that
       needs scrolling to show four items is not "elegant" either. Trim at the
       TIGHTEST width only: the padding and the gap, and let the Events
       conflict badge drop its number to a bare dot — the count is repeated in
       the tab's aria-label and on the card list underneath. */
    .rc-navrow .review-tab {
        padding-left: 6px;
        padding-right: 6px;
    }
    .rc-navrow .review-tabs { gap: 1px; }
    .rc-navrow .review-tab .tab-conflict-badge .tab-conflict-count {
        display: none;
    }
}

/* ============================================================
   PHONE TAB STRIP — DE-BLOAT (2026-09-07, owner: "tab structure seems
   oversized and inelegant", screenshot at 390px)
   ============================================================
   MEASURED before this block (390px harness, real stylesheets):
     navrow            328 x 60      — 60px of chrome for a 4-item strip
     tab track         232 x 52      — its own grey pill, INSIDE the navrow
     active pill        75 x 44      radius 9999px  -> reads as a BLOB
     inactive tabs      35-37 x 44   radius 9999px  -> three loose circles
     sub-tab track     364 x 55      a SECOND full-width white pill
     "Needs Your Vote" 130 x 48      wrapped to TWO lines (white-space:normal)
     "Sent by me"      103 x 48      wrapped to TWO lines

   Two separate causes, both real:

   1. PILL-IN-PILL. The nav track is a rounded grey pill sitting inside the
      navrow, and the sub-tabs are a second rounded track directly beneath it.
      Two stacked capsule containers is what reads as "oversized" — the chrome
      is drawing itself twice before any content appears.

   2. THE LAUNCHER RHYME OVER-APPLIED. review-tab-identity.js deliberately
      gives the tab strip the same glyph+colour as the launcher icon row, which
      is right. But the strip also inherited the launcher's ROUND ICON BUTTON
      shape (radius 9999px at a 44px min-height), and a labelled 75px tab at
      that radius is a lozenge, not a tab. Keep the shared identity (icon +
      colour), drop the shared geometry.

   Fix: ONE track, tab-shaped pills, and sub-tabs that never wrap. The 44px
   touch target is PRESERVED throughout — this trims chrome, not tap area.
   ============================================================ */
@media (max-width: 480px) {
    /* 1. The nav track stops being a container of its own. The navrow already
          bounds this group; a second capsule inside it is pure duplication. */
    .rc-navrow .review-tabs {
        background: transparent;
        padding: 0;
        border-radius: 0;
        gap: 2px;
    }

    /* 2. Tab-shaped, not blob-shaped. radius 9999px -> 10px. The active pill
          measured 75x44 as a near-circle; at 10px it reads as a tab that
          happens to be rounded, which is what every other strip in the app
          does. Height is unchanged (44px target kept). */
    .rc-navrow .review-tab {
        border-radius: 10px;
        min-height: 40px;
        padding: 6px 9px;
    }
    /* The inactive icon tabs were 35-37px wide circles floating in the track.
       Square them up so the three of them scan as one group of tabs beside
       the labelled one, rather than as scattered launcher buttons. */
    .rc-navrow .review-tab:not(.active) {
        padding-left: 8px;
        padding-right: 8px;
    }

    /* 3. Reclaim the navrow's vertical padding. 60px for a 40px control row is
          the rest of the "oversized" read. */
    .rc-navrow {
        min-height: 0;
        padding-top: 2px;
        padding-bottom: 2px;
    }

    /* 4. SUB-TABS NEVER WRAP. "Needs Your Vote" broke to two lines and forced
          every sub-tab to 48px tall; "Sent by me" did the same. The track is
          a segmented control — it scrolls before it wraps.
          This block sits LAST in source deliberately: the ~11797 "sticky
          sub-tabs" rule repaints the track white with -18px side margins,
          which is what made it a full-bleed band rather than a control. */
    .review-sub-tabs {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        margin: 0 0 8px;
        padding: 3px;
        background: var(--slate-100);
        border-radius: 9999px;
        gap: 2px;
    }
    .review-sub-tabs::-webkit-scrollbar { display: none; }
    /* All three sub-tabs must FIT 390px, not merely stop wrapping. Measured
       with nowrap at padding 7px/12px: 152.4 + 87.1 + 119.1 = 358.6 in a 328
       track, so "Sent by me" fell off the end with no scroll affordance — a
       cut-off third option is worse than the wrap it replaced. Tightened
       padding + gap brings the row inside the track; overflow-x stays as the
       fallback for longer translations (de/fr run wider than en). */
    .review-sub-tabs .sub-tab {
        white-space: nowrap;
        flex: 0 0 auto;
        min-height: 36px;
        padding: 7px 10px;
        border-radius: 9999px;
        font-size: 0.72rem;
    }

    /* ⭐SAME RULE AS THE TAB STRIP ABOVE: only the ACTIVE one is labelled.
       Three labelled sub-tabs do not fit 390px — measured 358.6 of content in
       a 328 track. Shrinking them to fit produced "D…" for "Done", and a
       three-way choice where two options are ellipsised is unreadable: the
       word IS the whole affordance here (unlike the top strip, whose icons
       carry a learned launcher identity). So spend the width where it means
       something — the active sub-tab names itself, the other two collapse to
       icon + count, which is exactly the shape the row already uses when it
       is the tab strip one level up. The full name stays in aria-label
       (review-dashboard.js builds it), so assistive tech loses nothing. */
    .review-sub-tabs .sub-tab:not(.active) > span:not(.sub-tab-count) {
        display: none;
    }
    /* The count is the actionable part — never let it be the thing that
       ellipsises away. */
    .review-sub-tabs .sub-tab .sub-tab-count {
        flex: 0 0 auto;
    }
}


/* ============================================================
   FILTERS ACTIVE-COUNT BADGE (2026-08-04)
   ============================================================
   Replaces the bare `.rc-filter-dot` on the Filters button. The dot could only
   say "something is filtered"; once the button collapses to an icon on a phone
   that was not enough. Derives from the same --primary-* tokens as the dot it
   replaces, so it reads as the established "this control is active" signal
   rather than a new vocabulary.
   ============================================================ */
.rc-filters-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    margin-left: 2px;
    border-radius: var(--radius-full, 9999px);
    background: var(--primary-500, #14b8a6);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    flex-shrink: 0;
}
/* Collapsed (icon-only) state: the badge rides the button's top-right corner
   the way the dot did, so it never widens the 40px touch target. */
@media (max-width: 480px) {
    .rc-toolrow .rc-filters-btn { position: relative; }
    .rc-toolrow .rc-filters-count {
        position: absolute;
        top: -4px;
        right: -4px;
        margin-left: 0;
        box-shadow: 0 0 0 2px var(--surface-primary, #fff);
    }
    body.dark-mode .rc-toolrow .rc-filters-count {
        box-shadow: 0 0 0 2px var(--slate-800, #1e293b);
    }
}

/* ============================================================
   PRIVACY SWEEP — collapsed by default (2026-08-04 redesign)
   ============================================================
   The sweep is now a <details> (review-dashboard.js renderPrivacyTab). It was a
   permanent three-button band sitting between the filter chips and the first
   event, on a tab that already stacked five bands of chrome before any content
   — and it is the most destructive control here, rewriting the visibility of
   the whole queue in one tap. At rest it is a single summary line; the buttons
   appear on demand.

   The base `.synced-sweep-bar` rule sets display:flex, which on a <details>
   element lays the summary and the content out as siblings on one row and
   defeats the collapse entirely. Override back to block and let the summary own
   the row instead.
   ============================================================ */
.synced-sweep-details {
    display: block;
    padding: 0;
}
.synced-sweep-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    cursor: pointer;
    list-style: none;              /* hide the native triangle; we ship a chevron */
    -webkit-tap-highlight-color: transparent;
}
.synced-sweep-summary::-webkit-details-marker { display: none; }
.synced-sweep-summary:hover .synced-sweep-label {
    color: var(--text-primary, #0f172a);
}
.synced-sweep-summary:focus-visible {
    outline: 2px solid var(--focus-ring, #2dd4bf);
    outline-offset: -2px;
}
.synced-sweep-chev {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-secondary, #64748b);
    transition: transform 0.18s ease;
}
.synced-sweep-details[open] .synced-sweep-chev {
    transform: rotate(180deg);
}
/* The buttons only exist while open, so they carry the row padding themselves
   rather than inheriting a band that is visible at rest. */
.synced-sweep-details[open] {
    padding-bottom: 10px;
}
.synced-sweep-details > .synced-sweep-btn,
.synced-sweep-details > .synced-split-hint {
    margin-left: 14px;
    margin-right: 0;
}
@media (prefers-reduced-motion: reduce) {
    .synced-sweep-chev { transition: none; }
}

/* ============================================================
   RULES + POLICY as overflow-menu items (2026-08-04 redesign)
   ============================================================
   Both moved out of the chip row and into the ⋯ menu — they are board SETTINGS,
   not per-decision controls, and that band sat directly above the queue. They
   keep their original classes for behaviour and gain `.rdh-overflow-item` for
   layout, so these rules only need to neutralise the chip-shaped chrome (pill
   border, inline width) that would otherwise look wrong in a menu column.
   ============================================================ */
.rdh-overflow-item.privacy-rules-chip,
.rdh-overflow-item .privacy-policy-chip {
    width: 100%;
    justify-content: flex-start;
    border: none;
    border-radius: 0;
    background: transparent;
}
.rdh-overflow-item.privacy-policy-chip-wrap {
    display: block;
    width: 100%;
    position: relative;
}
/* The policy popover is anchored to its wrapper, which now lives inside the
   menu — pull it clear of the menu's own bounds so it is not clipped. */
.rdh-overflow-item .privacy-policy-popover {
    right: 0;
    left: auto;
    min-width: 240px;
    z-index: 10;
}

/* ============================================================================
   PRIVACY: CHIPS IN THE TOOL ROW  (2026-08-07)
   User: "couldn't the third tab row go in the space where search is then if you
   click search it appears in its place on Mobile".

   The chips used to be a third stacked band inside the tab body, under a
   control row that on Privacy rendered essentially empty (a collapsed search
   icon and the overflow menu). Three chips squeezed into that leftover width
   truncated "Recent" to "Rece". They now ride the control row itself, so the
   band earns its space and the chips get the full row width.

   The body-band chrome (panel background, bottom border, block padding) is
   dropped in this context: it is an inline control group now, not a section.
   ========================================================================== */
.rc-toolrow .privacy-chip-row {
    flex: 0 1 auto;
    min-width: 0;
    padding: 0;
    gap: 6px;
    background: none;
    border-bottom: 0;
    overflow: hidden;
}
.rc-toolrow-chips .rc-inline-controls {
    flex: 0 0 auto;
    margin-left: auto;
}

/* MOBILE: search takes the row, chips step aside.
   The chips are the row's content, so an expanding input has nowhere to go: it
   would either squash them into unreadable slivers or push them off-screen.
   Instead the input claims the full row and the chips collapse out. This is
   phone-only. On desktop there is width for both, and the swap would be motion
   without purpose. */
@media (max-width: 640px) {
    .rc-toolrow-chips {
        position: relative;
        flex-wrap: nowrap;
    }
    .rc-toolrow-chips .privacy-chip-row {
        flex: 1 1 auto;
        transition: opacity 0.15s ease, max-width 0.2s ease;
        max-width: 100%;
        opacity: 1;
        /* ⭐2026-08-20 second pass (owner screenshot: the Done chip half-hidden
           behind the search button). The row shares ONE nowrap line with the
           inline search, so `flex: 1 1 auto` shrinks it to whatever search
           leaves — measured 279px of box for 336px of chips, which CLIPPED
           "Done 18" at the right edge. min-width:0 is what actually lets a
           flex child shrink BELOW its content and hand the overflow to its own
           scroller (without it the box is pinned at content width and the
           excess is simply cut). Paired with the horizontal scroll + edge fade
           declared in the ≤640 block above, so every chip stays reachable by
           swipe instead of disappearing under the search circle. */
        min-width: 0;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }
    .rc-toolrow-chips .privacy-chip-row::-webkit-scrollbar { display: none; }
    /* The trailing chip needs breathing room so it is not visually tucked
       under the search control the instant it scrolls into view. */
    .rc-toolrow-chips .privacy-chip-row > .privacy-filter-chip:last-child {
        margin-right: 6px;
    }
    /* Collapse the chips only while the search input is actually open. */
    .rc-toolrow-chips:has(.rc-search-inline.rc-search-open) .privacy-chip-row {
        max-width: 0;
        opacity: 0;
        pointer-events: none;
    }
    .rc-toolrow-chips:has(.rc-search-inline.rc-search-open) .rc-inline-controls {
        flex: 1 1 auto;
        margin-left: 0;
    }
    .rc-toolrow-chips:has(.rc-search-inline.rc-search-open) .rc-search-inline {
        flex: 1 1 auto;
        width: 100%;
    }
    .rc-toolrow-chips .rc-search-inline.rc-search-open .rc-search-input-wrap {
        max-width: 100%;
        flex: 1 1 auto;
    }
    .rc-toolrow-chips .rc-search-inline.rc-search-open .rc-search-input {
        width: 100%;
        max-width: none;
    }
    /* :has() fallback: without it the chips simply stay put and the input opens
       to its normal 180px beside them. Degraded, not broken. */
}

/* The active-filter pill shown INSIDE the expanded search field.
   Hiding the chips hides which filter is narrowing the list, which would
   recreate exactly the "list is short and nothing says why" problem the filter
   count badge exists to prevent. So the active non-default filter rides along
   as a dismissible pill. */
.rc-search-active-filter {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    flex-shrink: 0;
    margin-left: 6px;
    padding: 3px 8px;
    border-radius: var(--radius-full, 9999px);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--visibility-private, #7c3aed);
    background: rgba(139, 92, 246, 0.12);
    border: 1px solid rgba(139, 92, 246, 0.3);
    cursor: pointer;
}
.rc-search-active-filter:hover { background: rgba(139, 92, 246, 0.2); }
.rc-search-active-filter i { font-size: 9px; }
body.dark-mode .rc-search-active-filter {
    color: #c4b5fd;
    background: rgba(139, 92, 246, 0.22);
    border-color: rgba(139, 92, 246, 0.45);
}

/* ============================================================================
   PRIVACY: BATCH ASSIGN  (2026-08-07)
   Replaces the "Mark all N as:" sweep. Tick, assign, then one confirm.
   ========================================================================== */
.privacy-source-row {
    display: flex; align-items: center; gap: 8px;
    padding: 8px 14px 0;
}
/* The bulk controls, now the FIRST band of the bottom confirmation step
   (2026-08-27). Was .privacy-batch-bar, a non-sticky strip above the list:
   on mobile it scrolled out of reach as soon as you began deciding rows, so
   the batch had controls you could not get back to and a commit you could not
   see. The step below the list owns them now — see .privacy-final-confirm. */
.privacy-final-controls {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    padding-bottom: 12px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--border-color, #e2e8f0);
}
.privacy-batch-select {
    display: flex; align-items: center; gap: 10px;
    flex: 1 1 auto; min-width: 0;
}
.privacy-batch-selectall {
    display: inline-flex; align-items: center; gap: 7px;
    font-size: 0.85rem; color: var(--text-secondary, #64748b);
    cursor: pointer; white-space: nowrap;
}
.privacy-batch-selectall input[type="checkbox"] {
    width: var(--checkbox-size, 18px); height: var(--checkbox-size, 18px); cursor: pointer;
    accent-color: var(--visibility-private, #8b5cf6);
}
.privacy-batch-selectall-matching {
    background: none; border: 0; padding: 0;
    font-size: 0.8rem; font-weight: 600;
    color: var(--accent, #0284c7);
    cursor: pointer; text-decoration: underline;
    white-space: nowrap;
}
/* THE BATCH TRIPLET MUST NOT READ AS A FOURTH ROW (2026-08-11).
 *
 * The bar already has its own tint and border, but its Private/Busy/Shared
 * buttons are the same size, border and type scale as the three per-row
 * buttons directly beneath them — so the column reads as FOUR identical
 * triplets, one of which silently acts on every ticked row. Measured: batch
 * stamp 85x30 with a 0.667px slate border and 13.6px text; per-row button
 * 85x30 with the same border and the same 13.6px text.
 *
 * For a control whose mistake is irreversible (loosening visibility cannot be
 * un-seen), "looks like the thing next to it" is the wrong default. Label the
 * group and bind it visually to the bar rather than to the rows: a leading
 * caption states the scope in words, and a hairline rule closes the group so
 * the eye reads it as one control, not three loose buttons in a column. */
.privacy-batch-actions {
    display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
    padding-left: 10px;
    border-left: 2px solid var(--visibility-private, #8b5cf6);
}
.privacy-batch-actions-label {
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--text-secondary, #64748b);
    white-space: nowrap; margin-right: 2px;
}
/* When nothing is ticked the whole group is inert; say so once, on the group,
   instead of relying on three separately-dimmed buttons. */
.privacy-batch-actions[data-armed="false"] { border-left-color: var(--border-color, #cbd5e1); }
.privacy-batch-actions[data-armed="false"] .privacy-batch-actions-label { opacity: 0.6; }
.privacy-batch-stamp {
    padding: 6px 12px; border-radius: 6px;
    border: 1px solid var(--border-color, #cbd5e1);
    background: var(--bg-primary, #fff); cursor: pointer;
    font-size: 0.85rem; line-height: 1.2;
    color: var(--text-primary, #0f172a);
    transition: background 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.privacy-batch-stamp i { font-size: 11px; }
.privacy-batch-stamp[data-batch-stamp="hidden"] i { color: var(--visibility-private, #8b5cf6); }
.privacy-batch-stamp[data-batch-stamp="busy"] i   { color: var(--rc-status-tentative); }
.privacy-batch-stamp[data-batch-stamp="full"] i   { color: var(--rc-status-available); }
.privacy-batch-stamp:hover:not(:disabled) {
    background: var(--bg-secondary, #f1f5f9);
    border-color: var(--accent, #0284c7);
}
.privacy-batch-stamp:disabled { opacity: 0.45; cursor: not-allowed; }
.privacy-batch-stamp:focus-visible {
    outline: 2px solid var(--accent, #0284c7); outline-offset: 2px;
}
/* (.privacy-batch-commit / -staged / -tally / -review / -discard were REMOVED
   2026-08-27 with the top bar. The per-level tally they styled is superseded
   by the grouped readback in the confirm card, which names the events as well
   as counting them; the commit itself is .privacy-final-apply below.) */

/* Row tick + staged verdict. The verdict shows ON the row because a mixed
   batch is unreadable if the only record of what you assigned is a total. */
.synced-row-check { display: inline-flex; align-items: center; flex-shrink: 0; }
.synced-row-check input[type="checkbox"] {
    width: var(--checkbox-size, 18px); height: var(--checkbox-size, 18px); cursor: pointer;
    accent-color: var(--visibility-private, #8b5cf6);
}
.synced-row-ticked { background: rgba(139, 92, 246, 0.05); }
.synced-row-has-staged { border-left: 3px solid var(--visibility-private, #8b5cf6); }
.synced-row-staged {
    display: inline-flex; align-items: center; gap: 4px;
    margin-left: 8px; padding: 2px 8px;
    border-radius: var(--radius-full, 9999px);
    font-size: 0.72rem; font-weight: 600;
    vertical-align: middle;
}
.synced-row-staged i { font-size: 9px; }
.synced-row-staged-hidden {
    color: #7c3aed; background: rgba(139, 92, 246, 0.12);
}
.synced-row-staged-busy {
    color: #b45309; background: rgba(245, 158, 11, 0.14);
}
.synced-row-staged-full {
    color: #047857; background: rgba(16, 185, 129, 0.14);
}

/* The commit confirm card. Same three-section structure as the send-proposal
   summary (.propose-confirm-summary) because it answers the same question. */
.privacy-confirm-summary { text-align: left; }
.privacy-confirm-scope {
    font-size: 0.9rem; color: var(--text-secondary, #64748b);
    margin-bottom: 10px;
}
/* SCROLL THE READBACK, NOT THE PAGE (2026-08-29, user: "Review selections
   perhaps needs a scroll for large selections").
   Each group is ~92px and all three can be populated at once, so a mixed batch
   pushed the Discard/Apply pair below the fold — the readback grew until the
   controls it exists to introduce were off-screen, and the user had to scroll
   past the summary to reach the commit. Cap the list and let IT scroll, so the
   card's own height is bounded and the actions stay put. 46vh keeps at least
   two groups fully visible on a phone before scrolling starts. */
.privacy-confirm-groups {
    list-style: none; margin: 0; padding: 0;
    max-height: 46vh;
    overflow-y: auto;
    /* A hairline of padding so a group's focus ring / border is not shaved by
       the scroll container's edge. */
    padding-right: 2px;
    overscroll-behavior: contain;
}
/* Only decorate the track when it is actually scrollable, so short batches
   (the common case) show no scrollbar chrome at all. */
.privacy-confirm-groups { scrollbar-width: thin; }
.privacy-confirm-groups::-webkit-scrollbar { width: 8px; }
.privacy-confirm-groups::-webkit-scrollbar-thumb {
    background: rgba(148, 163, 184, 0.5);
    border-radius: 999px;
}
.privacy-confirm-groups::-webkit-scrollbar-track { background: transparent; }
body.dark-mode .privacy-confirm-groups::-webkit-scrollbar-thumb {
    background: rgba(148, 163, 184, 0.35);
}
.privacy-confirm-group {
    padding: 10px 12px; border-radius: 8px;
    border: 1px solid var(--border-color, #e2e8f0);
    margin-bottom: 8px;
    background: var(--bg-secondary, #f8fafc);
}
/* Shared is the section that needs reading, so it carries the most weight:
   loosest first, and visibly so. */
.privacy-confirm-group-full {
    border-color: rgba(16, 185, 129, 0.4);
    background: rgba(16, 185, 129, 0.07);
}
.privacy-confirm-group-busy { border-color: rgba(245, 158, 11, 0.35); }
/* ONE REAL ICON COLUMN (2026-08-29, user: "Alignment of the visibility summary
   needs perfecting").
   The heading was a flex row (icon + gap:7px + label) while the two lines under
   it faked the same indent with a hardcoded `margin-left: 22px`. Those are two
   different measurements of the same column and they never agreed: the icons
   are Font Awesome glyphs of DIFFERENT natural widths (fa-eye is wider than
   fa-clock, which is wider than fa-eye-slash), so the heading text started at a
   different x per group while the description and names all started at a fixed
   22px. Result: in a three-group summary nothing shared a left edge, and within
   a group the title sat proud of its own body text.

   Replace both with a single grid whose first track is a FIXED icon column, so
   the icon is centred in it and every text line — heading, description, names —
   starts on the exact same edge in every group regardless of glyph. */
.privacy-confirm-group {
    display: grid;
    grid-template-columns: 15px 1fr;
    column-gap: 7px;
    align-items: start;
}
.privacy-confirm-group-head {
    display: contents;
}
.privacy-confirm-group-head i {
    grid-column: 1;
    /* Centre each glyph inside the fixed track and pin it to the cap-height of
       the label beside it, so wide and narrow icons sit identically. */
    justify-self: center;
    line-height: 1.35;
    font-size: 0.85rem;
}
.privacy-confirm-group-head strong {
    grid-column: 2;
    font-size: 0.9rem; color: var(--text-primary, #0f172a);
    line-height: 1.35;
}
.privacy-confirm-group-full .privacy-confirm-group-head i { color: var(--rc-status-available); }
.privacy-confirm-group-busy .privacy-confirm-group-head i { color: var(--rc-status-tentative); }
.privacy-confirm-group-hidden .privacy-confirm-group-head i { color: var(--visibility-private, #8b5cf6); }
/* Both body lines occupy the TEXT column, so the 22px guess is gone and the
   indent is whatever the grid says it is — one source of truth. */
.privacy-confirm-group-desc {
    grid-column: 2;
    font-size: 0.78rem; color: var(--text-secondary, #64748b);
    margin: 2px 0 6px;
}
.privacy-confirm-group-names {
    grid-column: 2;
    font-size: 0.82rem; color: var(--text-primary, #0f172a);
    line-height: 1.5;
    /* Long titles wrap inside the column instead of pushing it wider. */
    min-width: 0;
    overflow-wrap: anywhere;
}
.privacy-confirm-rec, .privacy-confirm-more {
    font-size: 0.72rem; font-weight: 600;
    color: var(--text-secondary, #64748b);
}
.privacy-confirm-rec {
    padding: 1px 6px; border-radius: var(--radius-full, 9999px);
    background: rgba(100, 116, 139, 0.14);
}
.privacy-confirm-next {
    display: flex; align-items: flex-start; gap: 8px;
    padding-top: 10px; margin-top: 4px;
    border-top: 1px solid var(--border-color, #e2e8f0);
    font-size: 0.82rem; color: var(--text-secondary, #64748b);
}
.privacy-confirm-warn { color: #b45309; }
.privacy-confirm-warn i { color: var(--rc-status-tentative); margin-top: 2px; }

body.dark-mode .privacy-final-controls { border-bottom-color: var(--slate-700, #334155); }
body.dark-mode .privacy-batch-stamp {
    background: var(--slate-900, #0f172a);
    border-color: var(--slate-600, #475569);
    color: var(--slate-100, #f1f5f9);
}
body.dark-mode .privacy-batch-stamp:hover:not(:disabled) { background: var(--slate-700, #334155); }
body.dark-mode .synced-row-ticked { background: rgba(139, 92, 246, 0.12); }
body.dark-mode .privacy-confirm-group {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
body.dark-mode .privacy-confirm-group-full {
    background: rgba(16, 185, 129, 0.12);
    border-color: rgba(16, 185, 129, 0.45);
}
body.dark-mode .privacy-confirm-group-names { color: var(--slate-100, #f1f5f9); }
body.dark-mode .privacy-confirm-next { border-top-color: var(--slate-700, #334155); }
body.dark-mode .privacy-confirm-warn { color: #fbbf24; }

/* PHONE: ONE LEFT EDGE (2026-08-25, user: "the alignment on mobile is wrong").
 *
 * The bar stayed `flex-wrap: wrap` and faked rows with `flex: 1 1 100%`, so the
 * stamp group kept the desktop grouping chrome that binds the triplet
 * HORIZONTALLY: a 2px left rule plus 10px padding-left. Stacked, that is a 12px
 * indent on the middle row only. Measured at 390px: the checkbox and the Review
 * button start at x=14.7, the "SET SELECTED TO" caption at x=26.7 - three rows
 * on two different left edges. The rule itself, which closes the group when the
 * buttons sit beside it, degrades to a 2px orphan tick in the margin once the
 * group is full width.
 *
 * Column + stretch is the idiom the Events batch bar already uses
 * (.rc-batch-bar-active at <=768px), so batch mode reads the same across tabs.
 *
 * The scope caption STAYS - it is load-bearing (see the .privacy-batch-actions
 * note: without it the triplet reads as a fourth per-row triplet while acting
 * on every ticked event) - but moves to its own line so the three buttons split
 * the full width evenly instead of sharing a row with a 99px caption. */
@media (max-width: 640px) {
    .privacy-final-controls {
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
    }
    .privacy-batch-select { flex: 0 0 auto; }
    /* Full-width group: the left rule would now be a stray tick, so the accent
       moves to the top edge where it still closes the group. */
    .privacy-batch-actions {
        flex: 0 0 auto;
        flex-wrap: wrap;
        gap: 6px;
        padding-left: 0;
        padding-top: 8px;
        border-left: 0;
        border-top: 2px solid var(--visibility-private, #8b5cf6);
    }
    .privacy-batch-actions[data-armed="false"] {
        border-left-color: transparent;
        border-top-color: var(--border-color, #cbd5e1);
    }
    .privacy-batch-actions-label { flex: 1 0 100%; margin-right: 0; }
    .privacy-batch-actions .privacy-batch-stamp { flex: 1; text-align: center; }
    .synced-row-staged { margin-left: 0; margin-top: 3px; }

    /* PRIVACY ROW = TWO BANDS, NOT THREE (2026-08-25, user: "there is spare
       space on mobile privacy can we bring it down to two rows so we dont have
       one row with just dots").
     *
     * The stacked row (~12826, "Extra row space") is a COLUMN: checkbox band,
     * info band, then the full-width segmented control. That segmented track is
     * ratified and stays. What earns its own band and should not is the
     * CHECKBOX: a 16px tick sitting alone on a line with ~340px of empty space
     * beside it, which is the row that reads as spare. Float it up beside the
     * title instead - same line, no extra height - so the row is:
     *
     *     [x] Title                     <- band 1
     *         source · date · time      <- (inside band 1's column)
     *     [ Private | Busy | Shared ]   <- band 2
     *
     * Measured at 390px: 144.2px -> see the tally below the rules. */
    .synced-row {
        display: grid;
        grid-template-columns: auto 1fr;
        grid-template-areas: "check info" "actions actions";
        align-items: start;
        column-gap: 10px;
        row-gap: 6px;
    }
    .synced-row-check { grid-area: check; align-self: center; }
    .synced-row-info  { grid-area: info; min-width: 0; }
    .synced-row-actions { grid-area: actions; }
}

/* Meta segments: the separator travels WITH the segment it precedes, so a "·"
   is never left to start a wrapped line. `nowrap` keeps each "· <segment>" pair
   atomic; the row still wraps BETWEEN segments, which is the only place a break
   reads correctly. */
.synced-meta-seg { display: inline-flex; align-items: center; white-space: nowrap; }
.synced-meta-sep { margin: 0 5px 0 0; }

/* ── Conflicts entry row (Events tab) ─────────────────────────────────────
   2026-08-16, user: "the ui to manage conflicts is missing, unlike before"
   and "conflicts manager could be a sub section of events".

   The panel was never removed — its entry points were. A count-gated triage
   strip vanishes on a quiet day, and the Scheduled-tab dot is display:none in
   every state by deliberate design (no coloured dots on that row). That left
   the manager reachable only by accident. This row is the standing route.

   It names its count in WORDS rather than leaning on a hue, and it renders at
   zero too: an empty state that says "nothing double-booked" teaches that the
   manager exists and is working, which a hidden control never can. */
/* SPEAK THE REVIEW CENTER'S OWN CARD LANGUAGE (2026-09-21, owner: "can we
   make the conflicting events tab less ugly").

   This row sat in the Events tab wearing none of the chrome its neighbours
   wear: a hard 1px slate box with --radius-md (10px) and hard-coded 10/12px
   padding, no glass, no shadow, no lift — the same mistake .conflict-item
   itself made before 2026-08-22 ("the conflict tab ... clashes with my app
   in style"), which was fixed by adopting the shared --rc-* card tokens.
   This is that fix, applied to the entry that OPENS the panel, so the door
   and the room finally match.

   The owner chose to keep this as an entry row rather than promote conflicts
   to a 6th tab, so it must read as a deliberate destination card — not a
   stray button someone forgot to style. */
.rc-conflicts-entry {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin: 0 0 12px;
    /* Same tokens as .review-item / .conflict-item. */
    padding: var(--rc-spacing-card);
    border-radius: var(--rc-radius-card);
    background: var(--review-card-bg);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid var(--review-card-border);
    /* The left accent is the Review Center's category-accent idiom. Slate at
       rest: "no overlaps" is a status, not a warning. */
    border-left: 3px solid var(--rc-status-busy);
    box-shadow: var(--review-card-shadow);
    text-align: left;
    cursor: pointer;
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background-color 0.15s ease, border-color 0.15s ease,
                box-shadow 0.15s ease, transform 0.15s ease;
}
/* The shared lift + teal ring every other Review card has on hover. */
.rc-conflicts-entry:hover {
    border-color: var(--primary-300);
    border-left-color: var(--rc-status-busy);
    box-shadow: var(--shadow-md), 0 0 0 2px rgba(20, 184, 166, 0.08);
    transform: translateY(-1px);
}
.rc-conflicts-entry.has-conflicts:hover {
    border-left-color: var(--rc-conflict-red, #dc2626);
}
/* THE GLYPH RIDES THE TRAILING RAIL (2026-09-22, owner: "the warning icon is
   under the text, make it to the right").

   ⚠️A LEADING TILE HAS NOTHING TO ALIGN TO WHEN THE TEXT IS TWO LINES. The
   28px square centred itself against the label+hint PAIR, so it floated
   between the two lines and read as sitting under the label. The row's other
   affordances (count, chevron) already share a right rail; the icon joins
   them, and `margin-left: auto` on the first of them is what pushes the whole
   group to the edge — see the rules below. */
.rc-conflicts-entry__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm, 6px);
    /* Slate at rest: "no overlaps" is not a warning. */
    color: var(--slate-500, #64748b);
    background: var(--slate-100, #f1f5f9);
    /* Takes the right edge when it is the first trailing element (count == 0).
       With a count chip present the chip's own auto margin wins, because this
       one resolves against a box that is no longer the first on the rail. */
    margin-left: auto;
}
.rc-conflicts-entry__text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    flex: 1 1 auto;
}
.rc-conflicts-entry__label {
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: var(--text-primary, #0f172a);
}
.rc-conflicts-entry__hint {
    font-size: 11px;
    color: var(--text-secondary, #64748b);
}
.rc-conflicts-entry__chev {
    flex: 0 0 auto;
    font-size: 11px;
    color: var(--slate-400, #94a3b8);
}
/* Only an ACTUAL double-booking earns the danger hue — this is the one place
   red is correct (conflicts/danger), per the busy-is-slate split. The accent
   moves to the LEFT BORDER (the Review Center's category-accent idiom) rather
   than washing the whole card, so it matches .conflict-item's own red rail. */
.rc-conflicts-entry.has-conflicts {
    border-left-color: var(--rc-conflict-red, #dc2626);
    background: var(--review-card-bg);
}
.rc-conflicts-entry.has-conflicts .rc-conflicts-entry__icon {
    color: #dc2626;
    background: rgba(220, 38, 38, 0.1);
}

/* The count as a chip on the right, so the row states its number twice: once
   in words ("2 overlapping events") and once as a figure the eye can catch
   without reading. Mirrors the tab badge it replaces at this scale.

   ⛔THE CHIP IS THE NUMBER ALONE (2026-09-22). It used to carry its own
   triangle-exclamation, which put a SECOND warning glyph a few pixels from the
   row's icon once both moved onto the same trailing rail. The icon is the
   row's warning; this is its magnitude. `gap` and `margin-left:auto` go with
   the glyph — the icon ahead of it now owns the rail's auto margin. */
.rc-conflicts-entry__count {
    display: inline-flex;
    align-items: center;
    flex: 0 0 auto;
    padding: 2px 9px;
    border-radius: var(--radius-full, 999px);
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--rc-conflict-red, #dc2626);
    background: rgba(220, 38, 38, 0.1);
}
body.dark-mode .rc-conflicts-entry__count {
    color: #fecaca;
    background: rgba(220, 38, 38, 0.22);
}
/* ⛔NO SECOND AUTO MARGIN ON THE RAIL (2026-09-22). This used to give the
   chevron `margin-left:auto` at rest, back when the icon led the row and the
   chevron was the only trailing element. Now the icon opens the trailing rail
   and carries that auto margin itself, so repeating it here would insert a
   second free-space gap and strand the icon in the middle of the row — the
   very "floating glyph" this change removed. The rail packs: icon, count,
   chevron. */

/* Dark mode rides the SAME --review-card-* tokens as light mode now, so these
   only need to restate what the tokens do not cover. Hard-coding slate-800
   here would defeat the glass the card just adopted. */
body.dark-mode .rc-conflicts-entry {
    background: var(--review-card-bg);
    border-color: var(--review-card-border);
    border-left-color: var(--rc-status-busy);
}
body.dark-mode .rc-conflicts-entry:hover {
    border-color: var(--primary-300);
    border-left-color: var(--rc-status-busy);
}
body.dark-mode .rc-conflicts-entry.has-conflicts:hover {
    border-left-color: #f87171;
}
body.dark-mode .rc-conflicts-entry__icon {
    color: var(--slate-300, #cbd5e1);
    background: var(--slate-700, #334155);
}
body.dark-mode .rc-conflicts-entry__label { color: var(--slate-100, #f1f5f9); }
body.dark-mode .rc-conflicts-entry__hint { color: var(--slate-400, #94a3b8); }
body.dark-mode .rc-conflicts-entry.has-conflicts {
    border-left-color: #f87171;
    background: var(--review-card-bg);
}
body.dark-mode .rc-conflicts-entry.has-conflicts .rc-conflicts-entry__icon {
    color: #f87171;
    background: rgba(248, 113, 113, 0.16);
}

/* Dark mode for the segmented visibility track (2026-08-16). The track must
   read as a recessed groove and the selected segment as a raised card in both
   themes — in dark mode that means the track goes DARKER than the surface and
   the selection LIGHTER, the inverse of the light-mode relationship. */
body.dark-mode .synced-row-actions,
body.dark-mode .synced-sweep-actions {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
body.dark-mode .synced-vis-btn,
body.dark-mode .synced-sweep-btn {
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .synced-vis-btn + .synced-vis-btn,
body.dark-mode .synced-sweep-btn + .synced-sweep-btn {
    box-shadow: inset 1px 0 0 var(--slate-700, #334155);
}
body.dark-mode .synced-vis-btn.active {
    background: var(--slate-700, #334155);
    border-color: var(--slate-600, #475569);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
body.dark-mode .synced-vis-btn[data-visibility="hidden"].active { color: #c4b5fd; }
body.dark-mode .synced-vis-btn[data-visibility="busy"].active { color: #fcd34d; }
body.dark-mode .synced-vis-btn[data-visibility="full"].active { color: #86efac; }
body.dark-mode .synced-vis-btn.active i { color: inherit; }

/* ── Final confirmation, at the end of the privacy list ───────────────────
   2026-08-16, user: "Then scroll for for a final confirmation button. This
   should show a summary of events grouped into their visibility selections
   then confirm."

   The grouped summary already existed but lived only inside a modal — and
   that modal is skipped whenever nothing loosens, so a tightening-only batch
   committed with no summary at all. This puts the same card in the page, at
   the bottom of the list, so scrolling to it is the natural last step and the
   review happens on the same surface as the choosing.

   Renders only while something is staged. */
.privacy-final-confirm {
    margin: 12px 0 4px;
    padding: 14px;
    border: 1px solid var(--border-color, #e2e8f0);
    border-radius: 12px;
    background: var(--bg-secondary, #f8fafc);
}
/* Armed = something is staged and committable. Carries the weight the old top
   bar's -armed state did, on the element that now owns the commit. */
.privacy-final-confirm-armed {
    background: rgba(139, 92, 246, 0.06);
    border-color: rgba(139, 92, 246, 0.3);
}
.privacy-final-confirm-title {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 10px;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    color: var(--text-primary, #0f172a);
}
/* Idle copy. The step is always present, so it has to say what it is waiting
   for rather than render as an empty card. */
.privacy-final-idle {
    margin: 0;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--text-secondary, #64748b);
}
/* The one-line readback that used to BE the button label ("Share 1, hide 2").
   2026-08-30: the button is now just "Apply", so this line carries the detail —
   and, when something loosens, it is the last statement of what gets shared
   before the commit. Right-aligned to sit directly over the button it describes. */
.privacy-final-cta-detail {
    margin: 12px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--border-color, #e2e8f0);
    text-align: right;
    font-size: var(--rc-font-secondary, 12px);
    color: var(--text-secondary, #64748b);
}
.privacy-final-confirm-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color, #e2e8f0);
}
/* When the detail line is present it owns the divider — two stacked rules
   would draw two hairlines 12px apart. */
.privacy-final-cta-detail + .privacy-final-confirm-actions {
    margin-top: 8px;
    padding-top: 0;
    border-top: 0;
}
.privacy-final-discard {
    padding: 8px 14px;
    border: 1px solid var(--border-color, #cbd5e1);
    border-radius: 8px;
    background: transparent;
    color: var(--text-secondary, #64748b);
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 500;
    cursor: pointer;
    /* Explicit props (not transition:all) per the visibility-trap guard. */
    transition: background-color 0.12s ease, border-color 0.12s ease;
}
.privacy-final-discard:hover {
    background: var(--bg-primary, #fff);
    border-color: var(--slate-300, #cbd5e1);
}
/* The commit. Purple = the Privacy tab's identity colour, so the button that
   applies a visibility change is coloured by the thing it changes. */
.privacy-final-apply {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--visibility-private, #8b5cf6);
    color: #fff;
    font-size: var(--rc-font-secondary, 12px);
    font-weight: 600;
    cursor: pointer;
    transition: background-color 0.12s ease, box-shadow 0.12s ease;
}
.privacy-final-apply:hover {
    background: #7c3aed;
    box-shadow: 0 2px 8px rgba(139, 92, 246, 0.3);
}
.privacy-final-apply:focus-visible,
.privacy-final-discard:focus-visible {
    outline: 2px solid var(--visibility-private, #8b5cf6);
    outline-offset: 2px;
}

body.dark-mode .privacy-final-confirm {
    background: var(--slate-800, #1e293b);
    border-color: var(--slate-700, #334155);
}
body.dark-mode .privacy-final-confirm-armed {
    background: rgba(139, 92, 246, 0.14);
    border-color: rgba(139, 92, 246, 0.45);
}
body.dark-mode .privacy-final-idle { color: var(--slate-400, #94a3b8); }
body.dark-mode .privacy-final-confirm-title { color: var(--slate-100, #f1f5f9); }
body.dark-mode .privacy-final-confirm-actions { border-top-color: var(--slate-700, #334155); }
body.dark-mode .privacy-final-discard {
    border-color: var(--slate-600, #475569);
    color: var(--slate-300, #cbd5e1);
}
body.dark-mode .privacy-final-discard:hover { background: var(--slate-700, #334155); }

@media (max-width: 640px) {
    /* Stack the actions so the commit is a full-width target, and put it
       FIRST in reading order under the summary it applies. */
    .privacy-final-confirm-actions { flex-direction: column-reverse; align-items: stretch; }
    .privacy-final-apply,
    .privacy-final-discard { width: 100%; justify-content: center; }
}

/* ============================================
   CONFLICTS — "Ignored" disclosure (2026-08-11)
   ============================================
   The record of overlaps the user accepted via "Keep both". It is the UNDO
   for a dismissal that now persists, so it must always be reachable — the
   conflicts panel deliberately stays mounted when the live queue empties
   rather than bouncing to the Scheduled list, or this section would become
   unreachable exactly when it is the only thing left to show.

   Collapsed by default and styled DOWN (muted ink, no card chrome): it is a
   record, not a queue, and must never compete with live conflicts above it. */
.rc-conflict-ignored {
    margin-top: 10px;
    border-top: 1px solid var(--slate-200, #e2e8f0);
    padding-top: 8px;
}

.rc-conflict-ignored-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    padding: 4px 2px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    color: var(--slate-500, #64748b);
    text-align: left;
}
.rc-conflict-ignored-toggle:hover {
    color: var(--slate-700, #334155);
}
.rc-conflict-ignored-toggle i {
    font-size: 10px;
    transition: transform 0.15s ease;
}
.rc-conflict-ignored-toggle[aria-expanded="true"] i {
    transform: rotate(90deg);
}

.rc-conflict-ignored-body {
    list-style: none;
    margin: 4px 0 0;
    padding: 0;
}

.rc-conflict-ignored-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 2px;
    border-top: 1px solid var(--slate-100, #f1f5f9);
}
.rc-conflict-ignored-row:first-child {
    border-top: none;
}

.rc-conflict-ignored-pair {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}
.rc-conflict-ignored-titles {
    font-size: 12px;
    color: var(--slate-600, #475569);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rc-conflict-ignored-amp {
    color: var(--slate-400, #94a3b8);
}
.rc-conflict-ignored-date {
    font-size: 10.5px;
    color: var(--slate-400, #94a3b8);
    font-variant-numeric: tabular-nums;
}

body.dark-mode .rc-conflict-ignored {
    border-top-color: var(--slate-700);
}
body.dark-mode .rc-conflict-ignored-row {
    border-top-color: var(--slate-800);
}
body.dark-mode .rc-conflict-ignored-toggle {
    color: var(--slate-400, #94a3b8);
}
body.dark-mode .rc-conflict-ignored-toggle:hover {
    color: var(--slate-200, #e2e8f0);
}
body.dark-mode .rc-conflict-ignored-titles {
    color: var(--slate-300, #cbd5e1);
}

/* The Outlook-beta dialog reuses .floor-skip-row for its footer, but that
   rule is authored for the choice list's WAY OUT: a left-aligned COLUMN
   carrying a skip button and its consequence line. A two-button
   Cancel / Continue footer needs the opposite, so it opts in explicitly
   rather than fighting the base rule with inline styles. (2026-09-03) */
.calendar-floor-modal .floor-skip-row--actions {
    flex-direction: row;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
}

/* ── Invitations to you (2026-09-06) ─────────────────────────────────────
   Owner: "looks weak. I was thinking it could look like a Facebook style or
   LinkedIn style Friend request". These rows rendered with classes that had
   NO CSS anywhere — a bare blue link beside a default <button>. They now reuse
   .review-request-card (the join-request card one section down) so the whole
   Requests tab is one design: initials avatar, bold headline, one hint line,
   a primary action and a quiet one. Anchors and buttons share the .btn look. */
.review-session-invites {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}
.review-invites-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 2px 0;
}
.review-invites-header .section-icon {
  color: var(--primary-600, #0d9488);
  font-size: 0.9rem;
}
.review-invites-header .section-count {
  background: var(--primary-600, #0d9488);
}
.review-request-card.review-invite-card {
  align-items: flex-start;
  padding: 14px;
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
}
.review-request-card .request-avatar--invite {
  width: 44px;
  height: 44px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: linear-gradient(135deg, var(--primary-100, #ccfbf1), var(--primary-200, #99f6e4));
  color: var(--primary-800, #115e59);
}
.review-request-card.review-invite-card .request-headline {
  font-size: 0.95rem;
}
.review-request-card .request-meta {
  display: block;
  margin-top: 3px;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--slate-500, #64748b);
}
.review-request-card.review-invite-card .request-actions {
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  min-width: 136px;
}
.review-request-card.review-invite-card .request-actions .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  font-size: 0.82rem;
  font-weight: 600;
  border-radius: var(--radius-full, 999px);
  text-decoration: none;
  white-space: nowrap;
}
body.dark-mode .review-request-card .request-avatar--invite {
  background: rgba(20, 184, 166, 0.22);
  color: var(--primary-200, #99f6e4);
}
body.dark-mode .review-request-card .request-meta {
  color: var(--slate-400, #94a3b8);
}
@media (max-width: 520px) {
  /* Thumb row: the two actions share the width under the text. */
  .review-request-card.review-invite-card .request-actions {
    flex-direction: row;
    width: 100%;
    min-width: 0;
  }
  .review-request-card.review-invite-card .request-actions .btn {
    flex: 1;
  }
}

/* ── Scheduled shifts: approved unless contested (2026-09-07) ──────────
   A shift the group admin scheduled arrives already on the calendar. The
   card carries a calm "Scheduled" verdict and ONE quiet text door to
   contest it. No Accept, no Undo, no amber: the default is "this stands". */
.review-item .rc-scheduled-line {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--primary-700, #0f766e);
  font-weight: 600;
}
.review-item .trust-indicator.trust-scheduled {
  color: var(--text-secondary, #64748b);
}
.review-item .rc-actioned-badge.rc-scheduled-badge {
  color: var(--primary-700, #0f766e);
  background: rgba(20, 184, 166, 0.10);
  border-color: rgba(20, 184, 166, 0.28);
}
.review-item .rc-scheduled-actions {
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
.review-item .rc-contest-link {
  appearance: none;
  background: none;
  border: 0;
  padding: 6px 4px;
  min-height: 32px;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--text-secondary, #64748b);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: rgba(100, 116, 139, 0.45);
  cursor: pointer;
  border-radius: 6px;
}
.review-item .rc-contest-link:hover,
.review-item .rc-contest-link:focus-visible {
  color: var(--text-primary, #1e293b);
  text-decoration-color: currentColor;
  outline: none;
}
.review-item .rc-contest-link:focus-visible {
  box-shadow: 0 0 0 2px rgba(20, 184, 166, 0.35);
}
.decline-panel .decline-shift-note {
  margin: 0 0 12px;
  padding: 10px 12px;
  border-radius: 8px;
  background: rgba(20, 184, 166, 0.08);
  color: var(--text-secondary, #475569);
  font-size: 0.8125rem;
  line-height: 1.45;
}
body.dark-mode .review-item .rc-actioned-badge.rc-scheduled-badge {
  color: #5eead4;
  background: rgba(20, 184, 166, 0.16);
  border-color: rgba(94, 234, 212, 0.35);
}
body.dark-mode .review-item .rc-scheduled-line { color: #5eead4; }
body.dark-mode .review-item .rc-contest-link { color: #94a3b8; }
body.dark-mode .review-item .rc-contest-link:hover,
body.dark-mode .review-item .rc-contest-link:focus-visible { color: #e2e8f0; }
body.dark-mode .decline-panel .decline-shift-note { background: rgba(20, 184, 166, 0.14); color: #cbd5e1; }


/* ==========================================================================
   OWNER 2026-09-13 — "Rules Burton overlaps slightly. Also add a border too
   the tab icons"
   --------------------------------------------------------------------------
   1) THE RULES CHIP OVERLAP. `.privacy-chip-row-actions` is pushed to the
      trailing edge with `margin-left: auto`, and inside `.rc-toolrow` the row
      carries `overflow: hidden` (an inline control group, not a scroller). A
      margin-auto offset is NOT a gap: when the filter chips grow to the full
      track the auto margin collapses to zero and the dashed Rules border ends
      up flush against the last filter pill, reading as an overlap. Give the
      group a real minimum separation that survives the collapse, and stop the
      dashed border sharing an edge with the pill next to it.
   2) THE TAB ICONS. A visible hairline around each icon so they read as
      controls rather than loose glyphs floating in the header.
   ========================================================================== */
.privacy-chip-row .privacy-chip-row-actions {
    /* margin-left:auto still does the right-alignment; this is the floor that
       auto cannot collapse below. */
    margin-left: auto;
    padding-left: 10px;
}
.rc-toolrow .privacy-chip-row .privacy-chip-row-actions {
    /* Inside the clipped tool row the auto margin is the first thing to go, so
       the floor has to be a real box property. */
    margin-left: 10px;
}

/* ⛔THE HAIRLINE AROUND EACH TAB ICON IS GONE (2026-09-23, owner: "I don't
   like the squares around the icons"). It was added on an earlier request to
   make the glyphs "read as controls rather than loose glyphs", and the boxes
   it drew are what the owner is now pointing at: a 6px-radius square around
   every icon in the strip, one per tab plus the launcher row.
   The icons keep their identity colour (.tab-teal/.tab-blue/... above), which
   is what actually distinguishes them; the box was never carrying meaning.
   Only `line-height` survives — it keeps the glyph optically centred on the
   label, and dropping it shifts the whole row a pixel.
   ⛔Do not reinstate a border, background or radius here without an owner
   ruling: this is the second time the treatment has changed. */
.review-tab .tab-icon {
    line-height: 1;
}

/* ============================================================
   PHONE HEADER = PICTURE 1 (owner 2026-09-14, photographed on the live beta:
   "I want the review center tab structure to look like the picture").
   ============================================================
   Row 1: ONE segmented capsule strip holding every tab — the ACTIVE tab is a
          labelled pill with an outline in its own colour, the inactive tabs
          are icon-only and share the rest of the track evenly — and Close is
          a separate round button OUTSIDE the strip on the right.
   Row 2: the toolbar — the round search button and the "Filters ▾" pill,
          left-aligned (renderToolRow builds .rc-toolrow-controls on a phone;
          the controls no longer ride the nav row there).
   SUPERSEDES the strip GEOMETRY of the 2026-09-07 de-bloat block above
   (transparent track, 10px radii): the owner chose the capsule track with
   round pills. Its other decisions stand: one track (no pill-in-pill from a
   second sub-tab capsule), sub-tabs never wrap, 44px+ touch targets.
   Sits LAST in source on purpose so it wins the phone cascade.
   ============================================================ */
@media (max-width: 480px) {
    .rc-navrow {
        gap: 10px;
        padding-top: 4px;
        padding-bottom: 4px;
    }
    /* The strip fills the row up to the close button and is a capsule again. */
    .rc-navrow .review-tabs {
        flex: 1 1 auto;
        display: flex;
        align-items: center;
        gap: 4px;
        padding: 4px;
        min-height: 52px;
        background: var(--slate-100, #f1f5f9);
        border-radius: var(--radius-full, 9999px);
        overflow-x: auto;
    }
    .rc-navrow .review-tab {
        border-radius: var(--radius-full, 9999px);
        min-height: 44px;
    }
    /* Inactive tabs: icon only, sharing the track evenly, centred. */
    .rc-navrow .review-tab:not(.active) {
        flex: 1 1 0;
        justify-content: center;
        padding: 6px 8px;
        min-width: 44px;
        background: transparent;
    }
    .rc-navrow .review-tab:not(.active) .tab-label { display: none; }
    .rc-navrow .review-tab:not(.active) .tab-icon { font-size: 1.05rem; }
    /* ⭐AN ICON-ONLY TAB HAS NO TRAILING ROOM TO HANG A BADGE IN
       (owner 2026-09-21, on the phone strip). .tab-can-badge /
       .tab-can-conflict reserve 28px / 62px of trailing padding so a badge
       can appear and disappear without resizing the tab — the STEADY TABS
       ruling, and correct wherever the tab is LABELLED. But this block
       collapses an inactive tab to a 44px icon circle with `padding: 6px 8px`,
       which overrides that reservation while the badges are still absolutely
       positioned at `right: 6px` / `right: 38px` — measured from an edge that
       is now ~54px further left. The conflict chip therefore floated OUTSIDE
       its own tab, into the gap before the next one, reading as a loose mark
       belonging to nothing.
       Re-anchor both badges to the icon circle they belong to. The tab is a
       fixed 44px square here and cannot be resized by a badge appearing, so
       the reservation has no work to do at this width — the ruling it serves
       is untouched. */
    .rc-navrow .review-tab:not(.active).tab-can-badge,
    .rc-navrow .review-tab:not(.active).tab-can-conflict {
        padding-right: 8px;
    }
    /* The badge must ride the ICON, not the tab box. An inactive tab is a wide
       flex cell (flex: 1 1 0) with a CENTRED glyph, so its right edge is dead
       space tens of px away from the icon — anchoring there left the chip
       reading as a loose mark in the gap between tabs.
       The badge is a sibling of .tab-icon (not a child), so the icon cannot
       become its containing block. Position it from the tab's CENTRE instead,
       which is where the glyph is: left:50% puts it on the icon, and the
       translate nudges it to the glyph's upper-trailing corner — the standard
       badge station, and stable because the icon is centred by construction. */
    .rc-navrow .review-tab:not(.active) .tab-badge,
    .rc-navrow .review-tab:not(.active) .tab-conflict-badge {
        left: 50%;
        right: auto;
        top: 4px;
        transform: translateX(2px);
    }
    /* Active tab: its word, on a white pill outlined in its own colour. */
    .rc-navrow .review-tab.active {
        flex: 0 0 auto;
        padding: 6px 20px;
        background: var(--surface-primary, #fff);
        box-shadow: inset 0 0 0 2px color-mix(in srgb, currentColor 55%, transparent);
    }
    /* ⭐THE BADGES LANDED ON THE WORD (2026-09-23, owner: "Events tab in review
       center still has overlap of the icon on text"). The block above collapses
       INACTIVE tabs and re-anchors their badges to the icon — but the ACTIVE tab
       shows its LABEL, and `padding: 6px 20px` on the line above overrides the
       28px/62px trailing reservation that `.tab-can-badge` / `.tab-can-conflict`
       set for exactly this purpose. The badges stayed absolutely positioned at
       `right: 6px` / `right: 38px`, so with only 20px of padding they sat ON the
       label: the amber triangle and the count printed over "Events".
       Reinstate the reservation for the ACTIVE tab only, at this width only.
       Inactive tabs keep their icon-anchored badges from the block above —
       they have no label to collide with. */
    .rc-navrow .review-tab.active.tab-can-badge {
        padding-right: 30px;
    }
    .rc-navrow .review-tab.active.tab-can-conflict {
        padding-right: 64px;
    }
    /* ⭐`display: inline` HERE UNSTACKED THE BOLD GHOST AND DOUBLED THE PILL
       (owner 2026-09-21: the phone strip "is still ugly and broken" — the
       selected tab rendered about as wide as both its siblings combined).

       .tab-label is an inline-grid whose real text and ::after ghost are
       pinned to the SAME cell (grid-row/grid-column 1, see the STEADY TABS
       block), so the box is sized by the wider of the two and switching
       weight repaints inside a box that does not move. Overriding display to
       `inline` destroys that grid: the ghost stops being a grid item, lays
       out INLINE AFTER the text, and the label becomes text + ghost wide.

       MEASURED at 412px, "Requests": label 135.06px for a 67.53px word —
       exactly 2x — making the pill 175.06px beside 81.5px siblings, 49.5% of
       the whole track. Restoring the grid takes the label to 67.53px and the
       pill to 107.53px. The ghost is invisible (height 0, visibility hidden),
       so the waste was pure geometry with nothing on screen to explain it.

       inline-grid keeps every property this rule actually wanted — the label
       still shows, still goes 15px/600, still refuses to truncate — while the
       ghost goes back to doing its job. ⛔Do not "simplify" this to `inline`
       or `block`: both unstack the ghost and the bug returns. */
    .rc-navrow .review-tab.active .tab-label {
        display: inline-grid;
        font-size: 15px;
        font-weight: 600;
        overflow: visible;
        text-overflow: clip;
        max-width: none;
    }
    /* Close: a separate round button, outside the strip, same height. */
    .rc-navrow .rdh-actions { flex: 0 0 auto; margin-left: 0; }
    .rc-navrow .rdh-close-btn {
        width: 48px;
        height: 48px;
        font-size: 16px;
    }

    /* Row 2, the toolbar: search round button + labelled Filters pill. */
    .rc-toolrow-controls {
        display: flex;
        align-items: center;
        justify-content: flex-start;
        gap: 8px;
        padding: 2px 0 4px;
        margin-bottom: 6px;
    }
    .rc-toolrow-controls .rc-inline-controls {
        flex: 1 1 auto;
        justify-content: flex-start;
        margin-left: 0;
        gap: 8px;
    }
    .rc-toolrow-controls .rc-search-inline .rc-search-toggle {
        width: 44px;
        min-width: 44px;
        height: 44px;
    }
    /* On its own row the label has room again: "Filters ▾" reads as a word. */
    .rc-toolrow-controls .rc-filters-btn {
        min-height: 44px;
        padding-left: 14px;
        padding-right: 14px;
        border-radius: var(--radius-full, 9999px);
        font-size: 14px;
    }
    .rc-toolrow-controls .rc-filters-btn-label,
    .rc-toolrow-controls .rc-filters-btn-chevron { display: inline; }
}

/* ============================================================
   FULL-SIZE PRIMARY ACTIONS ON TOUCH (owner 2026-09-14: "increase the apply
   button size" — image 2, the proposal vote card's ✓ Apply)
   ============================================================
   The Review Center button reset (top of this file) sizes buttons to their
   content, which on a phone left the card's ONE decision — Apply — at the
   size of a chip. Every card-level decision inside the Review Center shares
   one scale on a coarse pointer: 48px tall, a real label, comfortable
   padding. Micro-controls (rank circles, conflict-row icon buttons, chips)
   are not on this list on purpose.
   ============================================================ */
@media (hover: none) and (pointer: coarse) {
    .review-dashboard .proposal-actions .btn,
    .review-dashboard .my-proposal-actions .btn,
    .review-dashboard .review-actions-row .btn,
    .review-dashboard .timeoff-actions .btn,
    .review-dashboard .rc-vis-apply-btn,
    .review-dashboard .schedule-winner-btn,
    .review-dashboard .undo-accept-btn,
    .review-dashboard .change-vote-btn,
    .review-dashboard .submit-vote-btn {
        min-height: 48px;
        padding: 0 22px;
        font-size: 1rem;
        font-weight: 600;
        border-radius: 12px;
        gap: 8px;
    }
    .review-dashboard .proposal-actions .btn i,
    .review-dashboard .my-proposal-actions .btn i,
    .review-dashboard .review-actions-row .btn i {
        font-size: 1rem;
    }
    /* The primary decision takes the row's width when it stands alone. */
    .review-dashboard .proposal-actions .submit-vote-btn:only-child {
        flex: 1 1 auto;
        justify-content: center;
    }
}

/* ============================================================
   VOTE MEDALS: ONE THEME (owner, 2026-09-14)
   "the medal system needs to look more premium regarding voting, I'd like
   the consistent medal theme". Until now four surfaces disagreed: the rank
   circle beside a slot filled with the calm primary teal (the 2026-08
   hierarchy pass, now SUPERSEDED by this ruling), the Your-rankings disc
   wore a pastel gradient, the organiser's standings puck only gilded first
   place, and the "You voted" line carried a plain check. Every rank now
   draws from one set of medal tokens, defined ONCE below: metallic
   gradients with a rim highlight and a soft shadow for gold / silver /
   bronze, a quiet neutral for 4th+, a clean dashed ring when unranked, and
   a status-red X chip when unavailable. Ranking stays sequential-click and
   nobody else's preference is ever shown as a numeral; only the LOOK
   changed. Numerals stay on the discs because "1 is your top choice" is
   the instruction copy.
   ============================================================ */
.review-dashboard,
.review-modal {
    --medal-gold-a: #f9dc7a;
    --medal-gold-b: #d3920a;
    --medal-gold-ink: #4a2e00;
    --medal-gold-line: #d9a324;
    --medal-gold-tint: rgba(211, 146, 10, 0.10);
    --medal-silver-a: #f4f7fa;
    --medal-silver-b: #94a3b8;
    --medal-silver-ink: #1e293b;
    --medal-silver-line: #a9b6c5;
    --medal-silver-tint: rgba(148, 163, 184, 0.14);
    --medal-bronze-a: #e8aa72;
    --medal-bronze-b: #c47334;
    --medal-bronze-ink: #2a1200;
    --medal-bronze-line: #c97d43;
    --medal-bronze-tint: rgba(154, 74, 18, 0.09);
    --medal-other-a: #f1f5f9;
    --medal-other-b: #cbd5e1;
    --medal-other-ink: #334155;
    --medal-other-line: #cbd5e1;
    --medal-other-tint: rgba(148, 163, 184, 0.08);
    --medal-rim: inset 0 1px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(0, 0, 0, 0.14);
    --medal-shadow: 0 1px 2px rgba(15, 23, 42, 0.18), 0 3px 8px rgba(15, 23, 42, 0.12);
    --medal-unranked-line: var(--slate-300, #cbd5e1);
    --medal-unranked-ink: var(--slate-400, #94a3b8);
    --medal-unavailable-bg: rgba(239, 68, 68, 0.12);
    --medal-unavailable-line: rgba(239, 68, 68, 0.55);
    --medal-unavailable-ink: #b91c1c;
}
body.dark-mode .review-dashboard,
body.dark-mode .review-modal {
    --medal-gold-a: #fbe28a;
    --medal-gold-b: #c68400;
    --medal-gold-ink: #3a2300;
    --medal-gold-line: #e2b23a;
    --medal-gold-tint: rgba(251, 191, 36, 0.14);
    --medal-silver-a: #eef2f6;
    --medal-silver-b: #7f8da0;
    --medal-silver-ink: #0f172a;
    --medal-silver-line: #94a3b8;
    --medal-silver-tint: rgba(148, 163, 184, 0.14);
    --medal-bronze-a: #eaa66d;
    --medal-bronze-b: #c47334;
    --medal-bronze-ink: #2a1200;
    --medal-bronze-line: #d28a4e;
    --medal-bronze-tint: rgba(217, 119, 6, 0.12);
    --medal-other-a: #475569;
    --medal-other-b: #1e293b;
    --medal-other-ink: #e2e8f0;
    --medal-other-line: #475569;
    --medal-other-tint: rgba(148, 163, 184, 0.08);
    --medal-rim: inset 0 1px 0 rgba(255, 255, 255, 0.35), inset 0 -1px 0 rgba(0, 0, 0, 0.35);
    --medal-shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 3px 8px rgba(0, 0, 0, 0.35);
    --medal-unranked-line: var(--slate-500, #64748b);
    --medal-unranked-ink: var(--slate-400, #94a3b8);
    --medal-unavailable-bg: rgba(239, 68, 68, 0.18);
    --medal-unavailable-line: rgba(248, 113, 113, 0.6);
    --medal-unavailable-ink: #fca5a5;
}

/* --- The rank circle beside a slot (the INPUT) --- */
.review-dashboard .rank-circle,
.review-modal .rank-circle {
    width: 28px;
    height: 28px;
    min-width: 28px;
    border: 2px dashed var(--medal-unranked-line);
    background: transparent;
    color: var(--medal-unranked-ink);
    box-shadow: none;
}
.review-dashboard .rank-circle.rank-selected,
.review-modal .rank-circle.rank-selected,
.review-dashboard .rank-circle.rank-other,
.review-modal .rank-circle.rank-other {
    border: 1px solid var(--medal-other-line);
    background: linear-gradient(160deg, var(--medal-other-a) 0%, var(--medal-other-b) 100%);
    color: var(--medal-other-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.review-dashboard .rank-circle.rank-gold,
.review-modal .rank-circle.rank-gold {
    border: 1px solid var(--medal-gold-line);
    background: linear-gradient(160deg, var(--medal-gold-a) 0%, var(--medal-gold-b) 100%);
    color: var(--medal-gold-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.review-dashboard .rank-circle.rank-silver,
.review-modal .rank-circle.rank-silver {
    border: 1px solid var(--medal-silver-line);
    background: linear-gradient(160deg, var(--medal-silver-a) 0%, var(--medal-silver-b) 100%);
    color: var(--medal-silver-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.review-dashboard .rank-circle.rank-bronze,
.review-modal .rank-circle.rank-bronze {
    border: 1px solid var(--medal-bronze-line);
    background: linear-gradient(160deg, var(--medal-bronze-a) 0%, var(--medal-bronze-b) 100%);
    color: var(--medal-bronze-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.review-dashboard .rank-circle.rank-unavailable,
.review-modal .rank-circle.rank-unavailable {
    border: 2px solid var(--medal-unavailable-line);
    background: var(--medal-unavailable-bg);
    color: var(--medal-unavailable-ink);
    box-shadow: none;
}
.review-dashboard .rank-circle .rank-number,
.review-modal .rank-circle .rank-number {
    font-weight: 800;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
}

/* The slot row takes a faint tint of its medal. */
.review-dashboard .proposal-option.rank-gold,
.review-modal .proposal-option.rank-gold {
    background: var(--medal-gold-tint);
    border-color: var(--medal-gold-line);
}
.review-dashboard .proposal-option.rank-silver,
.review-modal .proposal-option.rank-silver {
    background: var(--medal-silver-tint);
    border-color: var(--medal-silver-line);
}
.review-dashboard .proposal-option.rank-bronze,
.review-modal .proposal-option.rank-bronze {
    background: var(--medal-bronze-tint);
    border-color: var(--medal-bronze-line);
}
.review-dashboard .proposal-option.rank-other,
.review-modal .proposal-option.rank-other {
    background: var(--medal-other-tint);
    border-color: var(--medal-other-line);
}

/* --- "Your rankings" (the confirmation) --- */
.review-dashboard .vote-rank-disc,
.review-modal .vote-rank-disc {
    width: 26px;
    height: 26px;
    border: 1px solid var(--medal-other-line);
    background: linear-gradient(160deg, var(--medal-other-a) 0%, var(--medal-other-b) 100%);
    color: var(--medal-other-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
    font-weight: 800;
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
}
.review-dashboard .vote-rank.rank-other .vote-rank-disc,
.review-modal .vote-rank.rank-other .vote-rank-disc {
    border-color: var(--medal-other-line);
    background: linear-gradient(160deg, var(--medal-other-a) 0%, var(--medal-other-b) 100%);
    color: var(--medal-other-ink);
}
.review-dashboard .vote-rank.rank-gold .vote-rank-disc,
.review-modal .vote-rank.rank-gold .vote-rank-disc {
    border-color: var(--medal-gold-line);
    background: linear-gradient(160deg, var(--medal-gold-a) 0%, var(--medal-gold-b) 100%);
    color: var(--medal-gold-ink);
}
.review-dashboard .vote-rank.rank-silver .vote-rank-disc,
.review-modal .vote-rank.rank-silver .vote-rank-disc {
    border-color: var(--medal-silver-line);
    background: linear-gradient(160deg, var(--medal-silver-a) 0%, var(--medal-silver-b) 100%);
    color: var(--medal-silver-ink);
}
.review-dashboard .vote-rank.rank-bronze .vote-rank-disc,
.review-modal .vote-rank.rank-bronze .vote-rank-disc {
    border-color: var(--medal-bronze-line);
    background: linear-gradient(160deg, var(--medal-bronze-a) 0%, var(--medal-bronze-b) 100%);
    color: var(--medal-bronze-ink);
}
.review-dashboard .vote-rank.rank-gold,
.review-modal .vote-rank.rank-gold {
    background: var(--medal-gold-tint);
    border-color: var(--medal-gold-line);
}
.review-dashboard .vote-rank.rank-silver,
.review-modal .vote-rank.rank-silver {
    background: var(--medal-silver-tint);
    border-color: var(--medal-silver-line);
}
.review-dashboard .vote-rank.rank-bronze,
.review-modal .vote-rank.rank-bronze {
    background: var(--medal-bronze-tint);
    border-color: var(--medal-bronze-line);
}
.review-dashboard .vote-rank.rank-other,
.review-modal .vote-rank.rank-other {
    background: var(--medal-other-tint);
    border-color: var(--medal-other-line);
}
.review-dashboard .vote-rank .vote-rank-ordinal,
.review-modal .vote-rank .vote-rank-ordinal {
    color: inherit;
    opacity: 0.85;
}

/* --- The organiser's standings puck (the OUTCOME): every place is a medal --- */
.review-dashboard .time-option .rc-standing,
.review-dashboard .my-proposal-time .rc-standing,
.review-modal .time-option .rc-standing,
.review-modal .my-proposal-time .rc-standing {
    width: 22px;
    height: 22px;
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.review-dashboard .rc-standing--gold,
.review-modal .rc-standing--gold {
    background: linear-gradient(160deg, var(--medal-gold-a) 0%, var(--medal-gold-b) 100%);
    color: var(--medal-gold-ink);
}
.review-dashboard .rc-standing--silver,
.review-modal .rc-standing--silver {
    background: linear-gradient(160deg, var(--medal-silver-a) 0%, var(--medal-silver-b) 100%);
    color: var(--medal-silver-ink);
}
.review-dashboard .rc-standing--bronze,
.review-modal .rc-standing--bronze {
    background: linear-gradient(160deg, var(--medal-bronze-a) 0%, var(--medal-bronze-b) 100%);
    color: var(--medal-bronze-ink);
}

/* --- "You voted" / "Vote submitted" carry the top-choice medal glyph --- */
.medal-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    font-size: 10px;
    line-height: 1;
    flex-shrink: 0;
    border: 1px solid var(--medal-other-line);
    background: linear-gradient(160deg, var(--medal-other-a) 0%, var(--medal-other-b) 100%);
    color: var(--medal-other-ink);
    box-shadow: var(--medal-rim), var(--medal-shadow);
}
.medal-glyph--gold {
    border-color: var(--medal-gold-line);
    background: linear-gradient(160deg, var(--medal-gold-a) 0%, var(--medal-gold-b) 100%);
    color: var(--medal-gold-ink);
}
.status-you-voted .medal-glyph i,
.voted-confirmation .medal-glyph i {
    font-size: 10px;
    margin: 0;
}
.review-dashboard .your-votes-label i,
.review-modal .your-votes-label i {
    color: var(--medal-gold-b);
}

/* ============================================================
   THE CARD'S DECISION IS LARGE ON ANY PHONE-WIDTH VIEWPORT (owner, 2026-09-14)
   67ec7ad0 enlarged Apply and its siblings under (hover: none) and
   (pointer: coarse) only. The owner's phone still showed the chip-sized
   Apply on v1278: a browser that reports a hover-capable pointer (an in-app
   webview, "desktop site", a phone with a mouse attached) never matched that
   query. Width is the second witness: at 480px or narrower the same scale
   applies whatever the pointer says. Same declarations as the coarse block
   so the two can never drift apart.
   ============================================================ */
@media (max-width: 480px) {
    .review-dashboard .proposal-actions .btn,
    .review-dashboard .my-proposal-actions .btn,
    .review-dashboard .review-actions-row .btn,
    .review-dashboard .timeoff-actions .btn,
    .review-dashboard .rc-vis-apply-btn,
    .review-dashboard .schedule-winner-btn,
    .review-dashboard .undo-accept-btn,
    .review-dashboard .change-vote-btn,
    .review-dashboard .submit-vote-btn {
        min-height: 48px;
        padding: 0 22px;
        font-size: 1rem;
        font-weight: 600;
        border-radius: 12px;
        gap: 8px;
    }
    .review-dashboard .proposal-actions .btn i,
    .review-dashboard .my-proposal-actions .btn i,
    .review-dashboard .review-actions-row .btn i {
        font-size: 1rem;
    }
    .review-dashboard .proposal-actions .submit-vote-btn:only-child {
        flex: 1 1 auto;
        justify-content: center;
    }
    /* The rank circle keeps a thumb-sized target on a narrow screen too. */
    .review-dashboard .rank-circle,
    .review-modal .rank-circle {
        width: 36px;
        height: 36px;
        min-width: 36px;
    }
}

/* ============================================
   THE OUTCOME'S SUPPORTING FACTS (2026-09-23)
   ============================================
   Owner: "show this was selected like this or this was the outcome, this is the
   winner" — then: "does it show the proposed time and deadline and closed time".

   The closed panel printed one bare count ("3 first-choice") with no scale and
   no losing row, and `scheduledAt` — written since scheduling shipped — was
   rendered NOWHERE, so a card could say WHAT won but never WHEN that was
   settled. These two lines carry the evidence WITHOUT competing with the
   winner: quieter and smaller than `.closed-summary-winner-line`, because a
   result should read first and its supporting numbers second. */
.closed-summary-runnerup {
    margin-top: 4px;
    font-size: 12px;
    line-height: 1.35;
    color: var(--slate-500, #64748b);
}
/* Quieter again than the runner-up: the decision's timestamp is provenance,
   not a comparison. */
.closed-summary-decided {
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.3;
    color: var(--slate-400, #94a3b8);
}

/* BOOKED reads as settled, not as a warning. The expired chip is amber and the
   closed chip is slate; a decided meeting is a GOOD outcome, so it takes the
   product's own primary tint rather than borrowing either. */
.rc-status-chip--booked {
    color: var(--primary-700, #0f766e);
    background: var(--cmx-1848fd55bd);
    background: color-mix(in srgb, var(--primary-500, #14b8a6) 12%, transparent);
    border-color: var(--cmx-d5873f1c02);
    border-color: color-mix(in srgb, var(--primary-500, #14b8a6) 35%, transparent);
}
/* ⚠️iOS 15.6 cannot compute color-mix() — a var() of such a token is invalid at
   computed-value time, so the chip would paint NOTHING. Plain leaders first
   (see the owner's phone note, 2026-09-22). */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
    .rc-status-chip--booked {
        background: rgba(20, 184, 166, 0.12);
        border-color: rgba(20, 184, 166, 0.35);
    }
}
body.dark-mode .closed-summary-runnerup { color: var(--slate-400, #94a3b8); }
body.dark-mode .closed-summary-decided { color: var(--slate-500, #64748b); }
body.dark-mode .rc-status-chip--booked { color: var(--primary-300, #5eead4); }

/* The organiser's view of a server-decided meeting (2026-09-29, ws2a):
   "Invites sent to 3 of 4 — 1 could not be delivered, retrying" and the two
   ways to undo the decision. Filled by js/services/meeting-decisions-client.js. */
.meeting-decision:empty { display: none; }
.meeting-decision { margin-top: 8px; }
.meeting-decision-status {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--slate-600, #475569);
}
.meeting-decision-status.has-trouble { color: var(--warning-700, #b45309); }
.meeting-decision-people {
    margin: 4px 0 0 20px;
    padding: 0;
    font-size: 12px;
    line-height: 1.4;
    color: var(--slate-600, #475569);
}
.meeting-decision-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
}
.meeting-decision-actions .btn { min-height: 44px; }
body.dark-mode .meeting-decision-status,
body.dark-mode .meeting-decision-people { color: var(--slate-300, #cbd5e1); }
body.dark-mode .meeting-decision-status.has-trouble { color: var(--warning-300, #fcd34d); }

/* What the conflicts list leaves out, and the way to see it (2026-10-07). */
.conflicts-scope-note {
    flex-basis: 100%;
    margin: 6px 0 0;
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-secondary, #64748b);
}
.conflicts-scope-toggle {
    appearance: none;
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    font-weight: 600;
    color: var(--accent-teal, #0d9488);
    text-decoration: underline;
    cursor: pointer;
}

/* ==========================================================================
   REVIEW CENTER REFIT · STEP 1: ONE SWITCH, ONE SUB-ROW, ONE COUNT STYLE
   (owner-approved 2026-10-07, canvas claude.ai/artifact/BsCtf2FBhQvs6teHxBoe6V)
   ==========================================================================
   The owner: the tabs "seem lower quality than the rest of the app". They had
   four looks for one job (outlined coloured pills, a white tab-shaped sub-tab
   box, filled purple filter chips, a dashed Rules chip). This block gives the
   whole header the app's own vocabulary and sits LAST so it wins on purpose:
     - top tabs = the app's 2-4 option switch: a grey track, the chosen tab
       raised in white. Each tab keeps its registry colour on its ICON only;
     - sub-tabs and the Privacy filters = one underline row (teal underline);
     - counts: a grey pill for what waits on you (teal on the chosen tab), a
       plain quiet number for history (.is-quiet), and no pill at zero. The
       Events conflict mark stays the amber triangle + count with no filled
       shape (owner ruling 2026-09-21, "just a yellow conflict icon").
   Geometry rulings that still hold and are NOT touched here: badges ride out
   of flow inside the reserved trailing padding (STEADY TABS), the bold-ghost
   label grid, Close outside the strip, one nav row that never wraps. */
#review-dashboard-modal .review-tabs {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: #eef2f6;
    border: 0;
    border-radius: 12px;
    box-shadow: none;
}
#review-dashboard-modal .review-tab {
    min-height: 34px;
    padding-top: 7px;
    padding-bottom: 7px;
    padding-left: 13px;
    gap: 7px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    box-shadow: none;
    color: var(--slate-500, #64748b);
    font-size: 13.5px;
    font-weight: 600;
    opacity: 1;
}
#review-dashboard-modal .review-tab:not(.tab-can-badge):not(.tab-can-conflict) { padding-right: 13px; }
#review-dashboard-modal .review-tab:hover:not(.active) {
    background: rgba(255, 255, 255, 0.55);
    color: var(--slate-700, #334155);
}
#review-dashboard-modal .review-tab.active {
    background: #fff;
    color: var(--slate-900, #0f172a);
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.10), 0 0 0 1px rgba(15, 23, 42, 0.05);
}
/* The icon keeps the tab's registry identity, chosen or not. */
#review-dashboard-modal .review-tab.tab-teal .tab-icon   { color: #14b8a6; }
#review-dashboard-modal .review-tab.tab-blue .tab-icon   { color: #3b82f6; }
#review-dashboard-modal .review-tab.tab-indigo .tab-icon { color: #6366f1; }
#review-dashboard-modal .review-tab.tab-purple .tab-icon { color: #8b5cf6; }
#review-dashboard-modal .review-tab.tab-emerald .tab-icon { color: #10b981; }
/* An empty queue is not a disabled tab. */
#review-dashboard-modal .review-tab.tab-empty,
#review-dashboard-modal .review-tabs.all-empty .review-tab { opacity: 1; filter: none; }
#review-dashboard-modal .review-tab .tab-badge {
    min-width: 19px;
    height: 18px;
    padding: 0 5px;
    border: 0;
    border-radius: 9px;
    background: #e2e8f0;
    color: #475569;
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    box-shadow: none;
    font-variant-numeric: tabular-nums;
}
#review-dashboard-modal .review-tab.active .tab-badge { background: #ccfbf1; color: #0f766e; }
/* STEADY TABS, re-measured for the new badge box: the trailing room each tab
   reserves = side padding + the badge(s) + a 7px gap to the label, so a count
   never touches its word ("Events19" at the old 28px). */
#review-dashboard-modal .review-tab.tab-can-badge { padding-right: 46px; }
#review-dashboard-modal .review-tab.tab-can-badge .tab-badge { right: 13px; }
#review-dashboard-modal .review-tab.tab-can-conflict { padding-right: 86px; }
#review-dashboard-modal .review-tab.tab-can-conflict .tab-badge { right: 50px; }
#review-dashboard-modal .review-tab.tab-can-conflict .tab-conflict-badge { right: 13px; }
/* With nothing to show, the reserved room is split evenly either side, so the
   tab keeps its width (STEADY TABS) but its word sits centred, not adrift. */
#review-dashboard-modal .review-tab.tab-can-badge:not(:has(.tab-badge)) { padding-left: 29.5px; padding-right: 29.5px; }
#review-dashboard-modal .review-tab.tab-can-conflict:not(:has(.tab-badge)):not(:has(.tab-conflict-badge)) { padding-left: 49.5px; padding-right: 49.5px; }

/* Sub-tabs: one underline row under the switch, on every tab. */
#review-dashboard-modal .review-sub-tabs {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: 24px;
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: 12px;
    background: var(--bg-primary, #fff);
    border-radius: 0;
    border-bottom: 1px solid var(--slate-200, #e2e8f0);
    overflow-x: auto;
    scrollbar-width: none;
}
#review-dashboard-modal .review-sub-tabs::-webkit-scrollbar { display: none; }
#review-dashboard-modal .review-sub-tabs .sub-tab,
#review-dashboard-modal .privacy-filter-chip {
    flex: 0 0 auto;
    gap: 7px;
    min-height: 0;
    padding: 9px 0 10px;
    margin-bottom: -1px;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: var(--slate-500, #64748b);
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
}
#review-dashboard-modal .review-sub-tabs .sub-tab:hover:not(.active),
#review-dashboard-modal .privacy-filter-chip:hover:not(.active) {
    background: transparent;
    color: var(--slate-700, #334155);
}
#review-dashboard-modal .review-sub-tabs .sub-tab.active,
#review-dashboard-modal .privacy-filter-chip.active {
    background: transparent;
    box-shadow: none;
    color: var(--slate-900, #0f172a);
    border-bottom-color: #14b8a6;
}
/* Words, not glyphs: the row is a list of named slices. */
#review-dashboard-modal .review-sub-tabs .sub-tab > i,
#review-dashboard-modal .privacy-filter-chip > i { display: none; }
#review-dashboard-modal .review-sub-tabs .sub-tab .sub-tab-count,
#review-dashboard-modal .privacy-filter-chip .privacy-chip-count {
    min-width: 19px;
    height: 18px;
    padding: 0 5px;
    border-radius: 9px;
    background: #e2e8f0;
    color: #475569;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
#review-dashboard-modal .review-sub-tabs .sub-tab.active .sub-tab-count,
#review-dashboard-modal .privacy-filter-chip.active .privacy-chip-count { background: #ccfbf1; color: #0f766e; }
#review-dashboard-modal .review-sub-tabs .sub-tab .sub-tab-count.is-quiet,
#review-dashboard-modal .privacy-filter-chip .privacy-chip-count.is-quiet {
    min-width: 0;
    padding: 0;
    background: none;
    color: var(--slate-400, #94a3b8);
}
#review-dashboard-modal .privacy-filter-chip.is-empty .privacy-chip-count { display: none; }

/* Privacy: its filters are the same underline row, across the full width. */
#review-dashboard-modal .rc-toolrow-chips .privacy-chip-row-wrap { flex: 1 1 auto; min-width: 0; }
#review-dashboard-modal .privacy-chip-row {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-end;
    gap: 24px;
    padding: 0;
    background: none;
    border-bottom: 1px solid var(--slate-200, #e2e8f0);
}

body.dark-mode #review-dashboard-modal .review-tabs { background: #1a2539; }
body.dark-mode #review-dashboard-modal .review-tab { color: #93a1b5; }
body.dark-mode #review-dashboard-modal .review-tab.active { background: #243149; color: #e7edf6; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4); }
body.dark-mode #review-dashboard-modal .review-tab .tab-badge,
body.dark-mode #review-dashboard-modal .review-sub-tabs .sub-tab .sub-tab-count,
body.dark-mode #review-dashboard-modal .privacy-filter-chip .privacy-chip-count { background: #2a3852; color: #c3cede; }
body.dark-mode #review-dashboard-modal .review-tab.active .tab-badge,
body.dark-mode #review-dashboard-modal .review-sub-tabs .sub-tab.active .sub-tab-count,
body.dark-mode #review-dashboard-modal .privacy-filter-chip.active .privacy-chip-count { background: #123b3a; color: #5eead4; }
body.dark-mode #review-dashboard-modal .review-sub-tabs { background: var(--dark-bg, #0f172a); border-bottom-color: #22304a; }
body.dark-mode #review-dashboard-modal .privacy-chip-row { border-bottom-color: #22304a; }
body.dark-mode #review-dashboard-modal .review-sub-tabs .sub-tab.active,
body.dark-mode #review-dashboard-modal .privacy-filter-chip.active { color: #e7edf6; }

/* PHONE: four equal segments, every tab named under its icon. The
   2026-09-14 capsule named only the chosen tab; the refit names all four
   (they fit 390px at 10.5px), so nothing is learned by position alone.
   Counts stay corner bubbles in the tab's own hue (the launcher's look),
   anchored to the icon; the conflict mark takes the left corner so the two
   never overlap. Close stays a separate 48px round button. */
@media (max-width: 480px) {
    #review-dashboard-modal .rc-navrow .review-tabs {
        flex: 1 1 auto;
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: minmax(0, 1fr);
        gap: 2px;
        padding: 3px;
        min-height: 0;
        border-radius: 14px;
        overflow: visible;
    }
    #review-dashboard-modal .rc-navrow .review-tab,
    #review-dashboard-modal .rc-navrow .review-tab.active,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active),
    #review-dashboard-modal .rc-navrow .review-tab.active.tab-can-badge,
    #review-dashboard-modal .rc-navrow .review-tab.active.tab-can-conflict,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active).tab-can-badge,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active).tab-can-conflict {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        min-width: 0;
        min-height: 52px;
        padding: 13px 2px 5px;
        border-radius: 11px;
    }
    #review-dashboard-modal .rc-navrow .review-tab .tab-icon,
    #review-dashboard-modal .rc-navrow .review-tab.active .tab-icon { display: inline-block; font-size: 1rem; }
    #review-dashboard-modal .rc-navrow .review-tab .tab-label,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active) .tab-label,
    #review-dashboard-modal .rc-navrow .review-tab.active .tab-label {
        display: inline-grid;
        max-width: 100%;
        font-size: 10.5px;
        font-weight: 600;
        line-height: 1.15;
        overflow: hidden;
    }
    #review-dashboard-modal .rc-navrow .review-tab .tab-badge,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active) .tab-badge,
    #review-dashboard-modal .rc-navrow .review-tab.tab-can-badge .tab-badge,
    #review-dashboard-modal .rc-navrow .review-tab.tab-can-conflict .tab-badge {
        top: 2px;
        left: auto;
        right: 3px;
        transform: none;
        min-width: 16px;
        height: 15px;
        padding: 0 4px;
        font-size: 9.5px;
        line-height: 15px;
        color: #fff;
    }
    #review-dashboard-modal .rc-navrow .review-tab.tab-teal .tab-badge   { background: #14b8a6; }
    #review-dashboard-modal .rc-navrow .review-tab.tab-blue .tab-badge   { background: #3b82f6; }
    #review-dashboard-modal .rc-navrow .review-tab.tab-indigo .tab-badge { background: #6366f1; }
    #review-dashboard-modal .rc-navrow .review-tab.tab-purple .tab-badge { background: #8b5cf6; }
    #review-dashboard-modal .rc-navrow .review-tab .tab-conflict-badge,
    #review-dashboard-modal .rc-navrow .review-tab:not(.active) .tab-conflict-badge,
    #review-dashboard-modal .rc-navrow .review-tab.tab-can-conflict .tab-conflict-badge {
        top: 2px;
        left: 3px;
        right: auto;
        transform: none;
        gap: 1px;
        padding: 0;
        font-size: 9px;
        line-height: 14px;
    }
    #review-dashboard-modal .rc-navrow .review-tab .tab-conflict-badge i { font-size: 8px; }
    #review-dashboard-modal .review-sub-tabs { gap: 18px; }
    #review-dashboard-modal .review-sub-tabs .sub-tab,
    #review-dashboard-modal .privacy-filter-chip { font-size: 13px; }
    /* Every sub-tab keeps its name (the 2026-09 phone rule hid the inactive
       ones; the underline row fits them, and scrolls if a translation runs long). */
    #review-dashboard-modal .review-sub-tabs .sub-tab:not(.active) > span:not(.sub-tab-count) { display: inline; }
    #review-dashboard-modal .privacy-chip-row { gap: 18px; }
}

/* ==========================================================================
   REVIEW CENTER REFIT · STEP 2: CONFLICTS, A FOCUS VIEW DRAWN LIKE THE BOARD
   (owner-approved v3, 2026-10-07)
   ==========================================================================
   One card per clash, one row per event (label | track | actions), sharing
   one hour window: the clash's own hours plus one either side, at least 4.
   The rows' cells touch, so the window reads as one block of the board's
   day-cycle cells (.ts-cell[data-hour] paints them from style.css), with the
   hours numbered on a band UNDER the rows so no bar ever covers a number,
   and EVERY hour in the window numbered (owner rule). The events are the
   board's own markers; one red "!" marks the clash's outer corner. */
/* ONE grid for the whole board: every row (and the numbered band) shares the
   same three columns, so the band's cells sit exactly under the rows' cells
   (measured: with a grid per row the empty actions cell of the band was 0px
   wide and its cells ran ~124px further right than the rows'). */
#review-dashboard-modal .rcx-focus {
    display: grid;
    grid-template-columns: minmax(120px, 190px) minmax(0, 1fr) max-content;
    column-gap: 12px;
    row-gap: 0;
    align-items: center;
    margin-top: 6px;
}
#review-dashboard-modal .rcx-focus .rcx-row { display: contents; }
#review-dashboard-modal .rcx-focus .rcx-row-label { min-height: 36px; justify-content: center; }
#review-dashboard-modal .rcx-focus .rcx-actions { justify-content: flex-end; }
#review-dashboard-modal .rcx-focus .rcx-track {
    height: 36px;
    border-radius: 0;
    background: none;
    overflow: visible;
}
#review-dashboard-modal .rcx-focus .rcx-hour-cells {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
}
#review-dashboard-modal .rcx-focus .rcx-row:first-child .rcx-track .rcx-hour-cells { border-radius: 8px 8px 0 0; }
#review-dashboard-modal .rcx-focus .rcx-axis-row .rcx-axis {
    position: relative;
    height: 18px;
    min-width: 0;
}
#review-dashboard-modal .rcx-focus .rcx-axis .rcx-hour-cells { border-radius: 0 0 8px 8px; }
#review-dashboard-modal .rcx-focus .rcx-hour-cell { border-right: 1px solid rgba(255, 255, 255, 0.45); }
/* Your work (blue) and sleep (purple) bracket edges, down the whole column. */
#review-dashboard-modal .rcx-focus .rcx-hour-cell.rcx-wk-in  { box-shadow: inset 3px 0 0 #3b82f6; }
#review-dashboard-modal .rcx-focus .rcx-hour-cell.rcx-wk-out { box-shadow: inset -3px 0 0 #3b82f6; }
#review-dashboard-modal .rcx-focus .rcx-hour-cell.rcx-sl-in  { box-shadow: inset 3px 0 0 #7c3aed; }
#review-dashboard-modal .rcx-focus .rcx-hour-cell.rcx-sl-out { box-shadow: inset -3px 0 0 #7c3aed; }
/* The hour numbers: every cell, on the band under the rows, in the board's
   style (every third hour on a light tag, the rest slate with a white halo). */
#review-dashboard-modal .rcx-focus .rcx-axis .rcx-hour-cell-label {
    position: absolute;
    left: 3px;
    bottom: 2px;
    font-size: 10px;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    color: #475569;
    opacity: 1;
    text-shadow: 0 0 2px #fff, 0 0 2px #fff;
}
#review-dashboard-modal .rcx-focus .rcx-axis .rcx-hour-cell-label.is-major {
    padding: 1px 2px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.78);
    color: #334155;
    font-weight: 700;
    text-shadow: none;
}
/* The event: the board's own marker, sized to this row. */
#review-dashboard-modal .rcx-focus .rcx-track .rcx-marker {
    position: absolute;
    top: 5px;
    bottom: auto;
    height: 26px;
    min-width: 3px;
    z-index: 1;
    pointer-events: none;
}
#review-dashboard-modal .rcx-focus .rcx-row-swatch {
    position: relative;
    width: 18px;
    height: 12px;
    border-radius: 3px;
    background: none;
    flex: 0 0 auto;
}
#review-dashboard-modal .rcx-focus .rcx-row-swatch .rcx-marker {
    inset: 0;
    width: 100%;
    height: 100%;
    min-width: 0;
    pointer-events: none;
}
/* One "!" per clash, at its outer corner. */
#review-dashboard-modal .rcx-clash-bang {
    position: absolute;
    top: -7px;
    z-index: 3;
    width: 17px;
    height: 17px;
    margin-left: -9px;
    border-radius: 50%;
    background: #ef4444;
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    line-height: 17px;
    text-align: center;
    box-shadow: 0 0 0 2px #fff;
    pointer-events: none;
}
/* Day headings over the cards. */
#review-dashboard-modal .conflicts-day-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin: 16px 2px 8px;
    font-size: 13px;
    font-weight: 700;
    color: var(--slate-900, #0f172a);
}
#review-dashboard-modal .conflicts-list > .conflicts-day-head:first-child { margin-top: 4px; }
#review-dashboard-modal .conflicts-day-count { font-size: 12px; font-weight: 600; color: var(--slate-500, #64748b); }
body.dark-mode #review-dashboard-modal .conflicts-day-head { color: #e7edf6; }
/* The Conflicts sub-tab count: the Events tab's own amber mark. */
#review-dashboard-modal .review-sub-tabs .sub-tab .sub-tab-count.is-conflict {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    min-width: 0;
    padding: 0;
    background: none;
    color: #b45309;
}
#review-dashboard-modal .review-sub-tabs .sub-tab .sub-tab-count.is-conflict i {
    font-size: 10px;
    color: var(--rc-status-tentative, #f59e0b);
}
body.dark-mode #review-dashboard-modal .rcx-clash-bang { box-shadow: 0 0 0 2px #0f172a; }
/* WHERE THE CLASH IS (2026-10-08, owner: "highlight where and how long each
   conflict is"): the overlapping stretch is one red column through every row
   and the numbered band, over the events, so it reads at a glance. */
#review-dashboard-modal .rcx-focus .rcx-clash-zone {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 2;
    background: rgba(239, 68, 68, 0.16);
    border-left: 2px solid #ef4444;
    border-right: 2px solid #ef4444;
    pointer-events: none;
}
#review-dashboard-modal .rcx-focus .rcx-clash-veil {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 2;
    background: rgba(255, 255, 255, 0.6);
    pointer-events: none;
}
body.dark-mode #review-dashboard-modal .rcx-focus .rcx-clash-veil { background: rgba(15, 23, 42, 0.6); }
/* Names are read whole: they wrap to a second line rather than clip
   ("Meet…" in the owner's screenshot). */
#review-dashboard-modal .rcx-focus .rcx-row-name {
    white-space: normal;
    overflow-wrap: break-word;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    flex: 1 1 auto;
    min-width: 0;
}
/* The "7 times" chip follows the time, and drops under it when short of room. */
#review-dashboard-modal .rcx-focus .rcx-row-when { flex-wrap: wrap; row-gap: 3px; overflow: visible; }
#review-dashboard-modal .rcx-focus .rcx-row-times {
    flex: 0 0 auto;
    padding: 1px 6px;
    border-radius: 999px;
    background: #f1f5f9;
    font-size: 11px;
    font-weight: 600;
    color: #475569;
}
/* A row taller than its track (a wrapped name) stretches the track, so the
   rows still touch as one block of the board. */
@media (min-width: 481px) {
    #review-dashboard-modal .rcx-focus .rcx-track { align-self: stretch; height: auto; min-height: 36px; }
}
body.dark-mode #review-dashboard-modal .rcx-focus .rcx-row-times { background: rgba(148, 163, 184, 0.18); color: #cbd5e1; }

/* PHONE: the window runs full width as one block (every track, then the
   numbered band), and each event's name, time and actions follow as its own
   line underneath, in the same order as the rows above. Works for any number
   of events (the 2-row nth-of-type layout above does not). */
@media (max-width: 480px) {
    #review-dashboard-modal .rcx-focus {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        column-gap: 8px;
        row-gap: 0;
    }
    /* The window spans both columns; each event's line puts its name and
       time on the left and its action on the right, like a list row. */
    #review-dashboard-modal .rcx-focus .rcx-track,
    #review-dashboard-modal .rcx-focus .rcx-axis-row .rcx-axis { grid-column: 1 / -1; }
    #review-dashboard-modal .rcx-focus .rcx-row-label {
        justify-self: stretch;
        /* the older phone rule makes the label a ROW, so the desktop
           vertical-centring would centre it horizontally: start it left */
        justify-content: flex-start;
        align-items: center;
        min-height: 0;
    }
    #review-dashboard-modal .rcx-focus .rcx-actions {
        align-self: center;
        justify-content: flex-end;
        padding-top: 8px;
    }
    /* The swatch is the event's own marker (the hatch already says
       "proposed"), so at this width the pill and the duration step aside and
       the NAME keeps its room (measured: "Meeting" clipped to "Me…"). */
    #review-dashboard-modal .rcx-focus .rcx-row-proposal-pill,
    #review-dashboard-modal .rcx-focus .rcx-row-duration { display: none; }
    #review-dashboard-modal .rcx-focus .rcx-row { display: contents; }
    #review-dashboard-modal .rcx-focus .rcx-track,
    #review-dashboard-modal .rcx-focus .rcx-row:nth-of-type(1) .rcx-track,
    #review-dashboard-modal .rcx-focus .rcx-row:nth-of-type(2) .rcx-track {
        order: 1;
        height: 32px;
        border-radius: 0;
        border-top: 0;
    }
    #review-dashboard-modal .rcx-focus .rcx-track .rcx-marker { top: 4px; height: 24px; }
    #review-dashboard-modal .rcx-focus .rcx-axis-row .rcx-axis { order: 2; margin: 0 0 8px; }
    #review-dashboard-modal .rcx-focus .rcx-axis .rcx-hour-cell-label { font-size: 9.5px; left: 2px; }
    #review-dashboard-modal .rcx-focus .rcx-row .rcx-row-label,
    #review-dashboard-modal .rcx-focus .rcx-row:nth-of-type(2) .rcx-row-label {
        order: calc(10 + var(--rcx-i, 0) * 2);
        padding-top: 8px;
    }
    #review-dashboard-modal .rcx-focus .rcx-row .rcx-actions,
    #review-dashboard-modal .rcx-focus .rcx-row:nth-of-type(2) .rcx-actions {
        order: calc(11 + var(--rcx-i, 0) * 2);
        padding-bottom: 4px;
    }
    /* The time already sits in the line above each action group. */
    #review-dashboard-modal .rcx-focus .rcx-actions[data-row-range]::before { content: none; }
    #review-dashboard-modal .rcx-focus .rcx-axis-row .rcx-row-label,
    #review-dashboard-modal .rcx-focus .rcx-axis-row .rcx-actions { display: none; }
}

/* ==========================================================================
   REVIEW CENTER REFIT · STEP 3: PRIVACY, WITH THE RULES CARD BESIDE THE QUEUE
   (owner-approved 2026-10-07: "rules can be included at the bottom right page")
   ==========================================================================
   The Rules were a dashed chip that wrapped onto its own line under the
   filters. They are the CAUSE of what lands in this queue, so they now sit
   beside it as a card: a right-hand column at 900px and wider (sticky, so it
   stays in view down a long list), and after the list on narrower screens. */
#review-dashboard-modal .privacy-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 272px;
    gap: 16px;
    align-items: start;
}
#review-dashboard-modal .privacy-layout-main { min-width: 0; }
#review-dashboard-modal .rc-rules-card {
    position: sticky;
    top: 12px;
    padding: 13px 14px 11px;
    border: 1px solid var(--slate-200, #e2e8f0);
    border-radius: 14px;
    background: var(--bg-primary, #fff);
    font-size: 12.5px;
}
#review-dashboard-modal .rc-rules-head { display: flex; align-items: center; gap: 8px; }
#review-dashboard-modal .rc-rules-head > i { color: #8b5cf6; font-size: 13px; }
#review-dashboard-modal .rc-rules-head h4 { margin: 0; font-size: 14px; font-weight: 700; color: var(--slate-900, #0f172a); }
#review-dashboard-modal .rc-rules-count {
    margin-left: auto;
    padding: 1px 7px;
    border-radius: 9px;
    background: #e2e8f0;
    color: #475569;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
#review-dashboard-modal .rc-rules-sub { margin: 4px 0 8px; font-size: 12px; line-height: 1.4; color: var(--slate-500, #64748b); }
#review-dashboard-modal .rc-rules-card.is-paused .rc-rules-sub { color: #b45309; }
#review-dashboard-modal .rc-rules-list { list-style: none; margin: 0; padding: 0; }
#review-dashboard-modal .rc-rule {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr) auto;
    align-items: center;
    gap: 9px;
    padding: 4px 0;
    border-top: 1px solid var(--slate-100, #f1f5f9);
}
#review-dashboard-modal .rc-rule-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: var(--slate-800, #1e293b);
}
#review-dashboard-modal .rc-rule.is-off .rc-rule-name { color: var(--slate-400, #94a3b8); font-weight: 500; }
/* The switch: a 28x16 track inside a taller label, so the tap target is the row height. */
#review-dashboard-modal .rc-rule-switch {
    position: relative;
    display: inline-flex;
    align-items: center;
    width: 30px;
    min-height: 32px;
    cursor: pointer;
}
#review-dashboard-modal .rc-rule-switch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
#review-dashboard-modal .rc-rule-switch-track {
    position: relative;
    width: 28px;
    height: 16px;
    border-radius: 8px;
    background: #cbd5e1;
    transition: background 0.15s ease;
}
#review-dashboard-modal .rc-rule-switch-track::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 2px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 2px rgba(15, 23, 42, 0.25);
    transition: left 0.15s ease;
}
#review-dashboard-modal .rc-rule-switch input:checked + .rc-rule-switch-track { background: #14b8a6; }
#review-dashboard-modal .rc-rule-switch input:checked + .rc-rule-switch-track::after { left: 14px; }
#review-dashboard-modal .rc-rule-switch input:focus-visible + .rc-rule-switch-track { outline: 2px solid #0284c7; outline-offset: 2px; }
#review-dashboard-modal .rc-rule-out { padding: 1px 6px; border-radius: 5px; font-size: 11px; font-weight: 700; white-space: nowrap; }
#review-dashboard-modal .rc-rule-out--hidden { background: #f5f3ff; color: #6d28d9; }
#review-dashboard-modal .rc-rule-out--busy   { background: #f1f5f9; color: #334155; }
#review-dashboard-modal .rc-rule-out--full   { background: #ecfdf5; color: #047857; }
#review-dashboard-modal .rc-rule-more { padding: 6px 0 0; border-top: 1px solid var(--slate-100, #f1f5f9); font-size: 12px; color: var(--slate-500, #64748b); }
#review-dashboard-modal .rc-rules-empty { margin: 2px 0 4px; font-size: 12.5px; line-height: 1.45; color: var(--slate-600, #475569); }
#review-dashboard-modal .rc-rules-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 6px;
    padding-top: 8px;
    border-top: 1px solid var(--slate-100, #f1f5f9);
}
#review-dashboard-modal .rc-rules-foot button {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-height: 32px;
    padding: 0 2px;
    border: 0;
    background: none;
    font: inherit;
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
}
#review-dashboard-modal .rc-rules-add { color: #0f766e; }
#review-dashboard-modal .rc-rules-manage { color: var(--slate-600, #475569); }
#review-dashboard-modal .rc-rules-foot button i { font-size: 10px; }
#review-dashboard-modal .rc-rules-foot button:hover { text-decoration: underline; }
body.dark-mode #review-dashboard-modal .rc-rules-card { background: #111a2c; border-color: #22304a; }
body.dark-mode #review-dashboard-modal .rc-rules-head h4,
body.dark-mode #review-dashboard-modal .rc-rule-name { color: #e7edf6; }
body.dark-mode #review-dashboard-modal .rc-rule,
body.dark-mode #review-dashboard-modal .rc-rules-foot,
body.dark-mode #review-dashboard-modal .rc-rule-more { border-color: #22304a; }
body.dark-mode #review-dashboard-modal .rc-rules-count { background: #2a3852; color: #c3cede; }
body.dark-mode #review-dashboard-modal .rc-rule-out--hidden { background: #2a1f4a; color: #c4b5fd; }
body.dark-mode #review-dashboard-modal .rc-rule-out--busy   { background: #1f2a3d; color: #cbd5e1; }
body.dark-mode #review-dashboard-modal .rc-rule-out--full   { background: #12372b; color: #86efac; }
body.dark-mode #review-dashboard-modal .rc-rules-add { color: #5eead4; }
@media (max-width: 899px) {
    #review-dashboard-modal .privacy-layout { grid-template-columns: minmax(0, 1fr); }
    #review-dashboard-modal .rc-rules-card { position: static; }
}

/* ==========================================================================
   REVIEW CENTER REFIT · STEP 4: SHORTER EVENTS CARDS, ONE ACTION BAR
   (owner-approved 2026-10-07)
   ==========================================================================
   Over the list, the quota reading, Select and Accept all safe share ONE row
   (they were three stacked bands). On each card, the Settings row and the
   note box move behind "More" in the action row, so a card is its name, who,
   time, impact, day, then Accept / Decline. */
#review-dashboard-modal .rc-action-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    margin: 0 0 6px;
}
#review-dashboard-modal .rc-action-bar .rc-quota-summary { margin: 0; justify-content: flex-start; }
#review-dashboard-modal .rc-action-bar .rc-batch-bar { margin: 0 0 0 auto; padding: 0; background: none; border: 0; }
#review-dashboard-modal .rc-action-bar .rc-batch-bar-collapsed .rc-batch-toggle-btn {
    min-height: 34px;
    padding: 0 12px;
    border-radius: 10px;
    font-size: 13px;
}
#review-dashboard-modal .rc-action-bar .rc-accept-safe-btn {
    width: auto;
    min-height: 34px;
    margin: 0;
    padding: 0 14px;
    border-radius: 10px;
    font-size: 13px;
}
/* With nothing on its left, Select still sits at the right of the row. */
#review-dashboard-modal .rc-action-bar .rc-batch-bar:first-child { margin-left: auto; }
/* While selecting, the batch toolbar needs the whole row. */
#review-dashboard-modal .rc-action-bar.is-selecting .rc-batch-bar {
    flex: 1 1 100%;
    order: 3;
    margin: 0;
    padding: 8px 12px;
}
#review-dashboard-modal .rc-accept-safe-hint { margin: 0 0 10px; text-align: right; font-size: 11.5px; }
/* "More" closes the action row: quiet, and right-aligned after the decisions. */
#review-dashboard-modal .rc-actions-bar .rc-more-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin-left: auto;
    padding: 6px 4px;
    border: 0;
    background: none;
    color: var(--slate-500, #64748b);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
}
#review-dashboard-modal .rc-actions-bar .rc-more-btn:hover { color: var(--slate-800, #1e293b); }
#review-dashboard-modal .rc-actions-bar .rc-more-btn .settings-chevron { font-size: 10px; transition: transform 0.15s ease; }
#review-dashboard-modal .rc-actions-bar .rc-more-btn .settings-chevron.expanded { transform: rotate(180deg); }
#review-dashboard-modal .rc-note-section .review-note-input { width: 100%; }
@media (max-width: 480px) {
    #review-dashboard-modal .rc-accept-safe-hint { text-align: left; }
}

/* The card's action row: the decisions, then More on the same line (wider
   than a phone). The generic .settings-toggle-btn is a full-width bar, which
   put More on a line of its own; here it is a quiet trailing link. */
#review-dashboard-modal .rc-actions-bar .rc-more-btn {
    width: auto;
    flex: 0 0 auto;
    margin-bottom: 0;
    border: 0;
    background: none;
}
@media (min-width: 481px) {
    #review-dashboard-modal .rc-actions-bar {
        flex-direction: row;
        flex-wrap: nowrap;
        align-items: center;
    }
    #review-dashboard-modal .rc-actions-bar > .review-actions-row {
        flex: 1 1 auto;
        margin-top: 0;
        justify-content: flex-start;
    }
}
