/*
 * Help Room — mobile-first layout layer.
 *
 * NET-NEW, FLAGGED FOR REVIEW. The platform's shared components are
 * desktop-content oriented and there is no existing mobile-first pattern to
 * reuse, so these are NEW LAYOUT classes (a phone-first screen container, a
 * compact status strip, an action bar). They do NOT override shared components
 * — forms (.form-group), buttons (.btn), badges (.status-badge), and cards
 * (.section-card) are reused as-is. All colors and design tokens come only
 * from theme-consolidated.css (no hardcodes, no fallbacks).
 *
 * Mobile-first: base rules target the phone; min-width queries enhance upward.
 */

/* Landing hero page (body.template-helproom-landing, set by landing.html):
   the full-bleed .home-hero sits flush under the site header (zero the content
   wrapper's top padding, like the home/courses landings) and its
   ~scrollbar-width `width: 100vw` overhang is clipped.

   The clip is on the CONTENT WRAPPER, not the body: clipping the body would
   propagate to the viewport and pull the full-width sticky header in. The
   wrapper contains the hero but not the header, so the header stays full-width.
   `overflow-x: clip` (not `hidden`) clips without creating a scroll container. */
body.template-helproom-landing .background-white.padding-medium-top {
    padding-top: 0;
    overflow-x: clip;
}

/* Phone-first screen: comfortable single column, centered + capped on larger
   screens so it reads like an app screen, not a stretched desktop form. */
.hr-screen {
    width: 100%;
    max-width: 40rem;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-4);
}

.hr-screen__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-3);
    flex-wrap: wrap;
}

/* Avatar chip + page title, grouped on the left of the dashboard header. */
.hr-screen__heading {
    display: flex;
    align-items: center;
    gap: var(--spacing-3);
}

/* The counterpart on the right: two or more header buttons kept together.
   .hr-screen__header is space-between, which reads correctly for a title and a
   single action but spreads three children evenly across the full width — the
   buttons end up marooned at opposite ends. Wrapping them restores the header
   to two children, so they sit as a group. */
.hr-screen__actions {
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
    flex-wrap: wrap;
}

.hr-screen__title {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    margin: 0;
}

.hr-screen__subtitle {
    color: var(--text-secondary);
    margin: 0;
}

/* Semester / context tag shown on the header row, beside the screen title. */
.hr-screen__meta {
    color: var(--text-secondary);
    font-size: var(--font-size-lg);
}

/* Groups the screen title with its context tag so they share one row (and wrap
   together) while a header action stays opposite them. */
.hr-screen__heading {
    display: flex;
    align-items: baseline;
    gap: var(--spacing-3);
    flex-wrap: wrap;
}

/* The room location line in the landing hero — stronger than the subtitle so
   "where do I go" reads at a glance. */
.hr-screen__location {
    margin: 0;
    font-weight: var(--font-weight-semibold);
}

/* A quiet one-line hint (login note, empty states) — lighter than an alert. */
.hr-screen__hint {
    margin: 0;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

/* ── Landing top zone ─────────────────────────────────────────────────────
   Mirrors the demos landing: identity + intro text on the left, the live
   status panel (a .section-card) on the right. Mobile-first: a single
   stacked column; the two-zone split arrives with width. Theme-aware —
   everything uses semantic colors on the normal page background. */
.hr-top {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-4);
}

.hr-top__intro {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
    min-width: 0;
}

/* Carries its own surface rather than composing .section-card. It sits beside
   the .hr-item cards further down the page and takes their treatment — a
   hairline border on the same background, no drop shadow — where .section-card
   is a raised panel. Declaring it here avoids inheriting the shadow and then
   having to out-specify the [data-theme="dark"] .section-card rule to remove
   it, which would only hold while this file happens to load last. */
.hr-top__status {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-3);
    margin-bottom: 0;
    background: var(--card-bg);
    border: var(--border-1) solid var(--border-color);
    border-radius: var(--radius-lg);
    /* Matches what .section-card actually rendered here: components.css asks
       for --spacing-8, but the touch-target rule in accessibility.css lands
       later and wins with 0.5rem 1rem. */
    padding: var(--spacing-2) var(--spacing-4);
}

.hr-top__badge {
    margin: 0;
}

@media (min-width: 768px) {
    .hr-top {
        flex-direction: row;
        align-items: flex-start;
        gap: var(--spacing-8);
    }
    .hr-top__intro {
        flex: 1 1 55%;
    }
    .hr-top__status {
        flex: 1 1 45%;
        max-width: 28rem;
    }
}

/* Compact horizontal status strip — three at-a-glance numbers in one row on
   phones (vs. tall stacked stat-cards). */
.hr-statbar {
    display: flex;
    gap: var(--spacing-2);
}

.hr-statbar__item {
    flex: 1 1 0;
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    padding: var(--spacing-3) var(--spacing-2);
    text-align: center;
}

.hr-statbar__num {
    display: block;
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    line-height: 1;
    color: var(--ucb-gold-text);
}

.hr-statbar__label {
    display: block;
    margin-top: var(--spacing-1);
    font-size: var(--font-size-xs);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Action bar: full-width, thumb-friendly primary action on phones. */
.hr-actions {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
}

.hr-actions .btn {
    width: 100%;
    padding-block: var(--spacing-3);
    font-size: var(--font-size-base);
}

/* A single big at-a-glance number (e.g. queue position) for the status screen. */
.hr-bignum {
    text-align: center;
    padding: var(--spacing-4) 0;
}

.hr-bignum__value {
    font-size: var(--font-size-6xl);
    font-weight: var(--font-weight-bold);
    line-height: 1;
    color: var(--ucb-gold-text);
}

.hr-bignum__label {
    margin-top: var(--spacing-2);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: var(--font-size-sm);
}

/* Enhance for tablet/desktop: a touch more breathing room, side-by-side
   action buttons when there's room. */
@media (min-width: 768px) {
    .hr-actions {
        flex-direction: row;
        flex-wrap: wrap;
        /* Keep buttons at their natural height — without this, a row that also
           holds a taller item (e.g. the header's semester dropdown) stretches
           every button to match it. */
        align-items: center;
    }
    .hr-actions .btn {
        width: auto;
        flex: 1 1 auto;
    }
}

/* Compact semester dropdown in a screen header: its label is visually hidden
   (the value + the page title already say the semester), and it stays a fixed
   compact measure rather than stretching the action row.

   The measure is explicit because combobox.js swaps the <select> for a text
   input, which has no intrinsic width the way a select sized to its longest
   option does. 12rem comfortably fits "Summer 2026" plus room to type. */
.hr-semester-picker {
    flex: 0 0 12rem;
}

.hr-semester-picker select {
    width: auto;
}

/* Inline secondary actions inside a card (e.g. "Update location", the faculty
   "Remove" helper button): compact and right-aligned at every width so they
   don't compete with the screen's primary action. justify-content right-aligns
   the buttons within a full-width container; margin-left:auto pushes the whole
   group to the far edge when it sits beside a sibling in a flex row (the helper
   name in .hr-item__top). Placed after the media query above so it wins at
   desktop widths too. */
.hr-actions--inline {
    flex-direction: row;
    justify-content: flex-end;
    margin-left: auto;
}

.hr-actions--inline .btn {
    width: auto;
    flex: 0 0 auto;
    padding-block: var(--spacing-2);
    font-size: var(--font-size-sm);
}

/* Nav-link cluster (the manage screen's "Edit periods / Room hours / …" header
   and its lifecycle transitions): several equal secondary links, none of them
   the screen's primary action. The phone-first .hr-actions rule gives each one
   a full-width thumb target, which turns five links into a wall you scroll
   past to reach the page. These wrap as compact chips at every width instead. */
.hr-actions--wrap {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
}

.hr-actions--wrap .btn {
    width: auto;
    flex: 0 0 auto;
    padding-block: var(--spacing-2);
    font-size: var(--font-size-sm);
}

/* Wider variant for working screens (helper dashboard, schedule builder) that
   benefit from two columns on desktop while still stacking phone-first. */
.hr-screen--wide {
    max-width: 64rem;
}

/* A small section heading inside a screen ("My active sessions", "Waiting").
   Lighter than .section-card h2, which is a heavy 3xl panel heading. */
.hr-section-title {
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-bold);
    margin: 0 0 var(--spacing-2);
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
    flex-wrap: wrap;
}

/* The View-analytics button rides in the course heading row (after the section
   meta); keep it from shrinking when space is tight. Layout only. */
.hr-section-title .btn {
    flex-shrink: 0;
}

/* Faculty analytics: horizontal bar lists (category / weekly distributions)
   and a day×hour demand heatmap table. Layout + token shading only; the count
   numbers carry the precise data, so the heat tint is a visual aid (and stays
   subtle enough to keep the cell text readable in both themes). */
.hr-bars {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
}

.hr-bar {
    display: grid;
    grid-template-columns: minmax(5rem, 9rem) 1fr auto;
    align-items: center;
    gap: var(--spacing-3);
}

.hr-bar__label {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.hr-bar__track {
    background: var(--bg-elevated);
    /* Outline the full extent: the track shares --bg-elevated with cards and the
       table's even zebra stripe, so without a border it disappears into those
       surfaces (the 100% reference vanished on alternating rows). */
    border: var(--border-1) solid var(--border-color);
    border-radius: var(--radius-sm);
    height: var(--spacing-3);
    overflow: hidden;
}

.hr-bar__fill {
    display: block;
    height: 100%;
    width: var(--bar);
    min-width: var(--spacing-1);
    background: var(--ucb-gold);
    border-radius: var(--radius-sm);
}

.hr-bar__value {
    font-weight: var(--font-weight-bold);
    font-variant-numeric: tabular-nums;
}

.hr-heat-wrap {
    overflow-x: auto;
}

.hr-heat {
    border-collapse: collapse;
    width: 100%;
    font-size: var(--font-size-sm);
}

.hr-heat th,
.hr-heat td {
    border: 1px solid var(--border-color);
    padding: var(--spacing-1);
    text-align: center;
}

.hr-heat th {
    /* The global th pairing (--table-header-bg/-text) is a heavy charcoal that
       fights the gold heat scale, so this grid uses a light, neutral surface
       that lets the gold cells read as the data. Works in both themes:
       --bg-tertiary/--text-primary invert together. */
    background: var(--bg-tertiary);
    color: var(--text-primary);
    font-weight: var(--font-weight-bold);
    white-space: nowrap;
}

.hr-heat__cell {
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    /* Cap the gold tint at 22%: above that the dark-mode cell text drops below
       the WCAG AA 4.5:1 ratio (the count, not the shade, carries the value). */
    background: color-mix(in srgb, var(--ucb-gold) calc(var(--heat) * 22%), var(--bg-elevated));
}

/* Faculty analytics: an expandable bar — a <details> whose <summary> IS the
   bar row. Clicking a week reveals the anonymous descriptions students typed
   that week. A custom marker replaces the native one (grid summaries suppress
   it); a leading marker column (--markable) keeps expandable and plain bars in
   alignment. Layout + token surfaces only. */
.hr-bar--markable {
    grid-template-columns: var(--spacing-4) minmax(5rem, 9rem) 1fr auto;
}

.hr-bar--expandable {
    cursor: pointer;
    list-style: none;
}

.hr-bar--expandable::-webkit-details-marker {
    display: none;
}

.hr-bar--expandable:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

/* Right-pointing triangle (colored left border, transparent top/bottom); turns
   to point down when its <details> is open. */
.hr-bar__marker {
    width: 0;
    height: 0;
    border-left: var(--spacing-2) solid var(--text-secondary);
    border-top: var(--spacing-1) solid transparent;
    border-bottom: var(--spacing-1) solid transparent;
    transition: transform var(--transition-fast);
}

.hr-bar-group[open] .hr-bar__marker {
    transform: rotate(90deg);
}

.hr-topic-list {
    margin: 0;
    padding: var(--spacing-1) var(--spacing-3) var(--spacing-2) var(--spacing-7);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-1);
}

.hr-topic-list__item {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

/* Coverage grid (drill-down): week × slot table, reusing .hr-heat layout. The
   cell tint encodes the coverage tier (success/warning/danger tokens, all WCAG
   AA on their paired text) while the % text carries the exact value. */
.hr-cov th[scope="row"] {
    text-align: left;
    white-space: nowrap;
}

.hr-cov-cell--full {
    background: var(--color-success-bg);
    color: var(--color-success-text);
}

.hr-cov-cell--partial {
    background: var(--color-warning-bg);
    color: var(--color-warning-text);
}

.hr-cov-cell--none {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.hr-cov-cell--empty {
    color: var(--text-secondary);
}

/* Faculty portal: separate the add-helper form from the helper list above it. */
.hr-add-helper {
    margin-top: var(--spacing-5);
}

/* Pending check-out / undo prompts on the helper dashboard. Layout only —
   colors come from the shared .alert variants, so light and dark both follow
   the theme. These are banners rather than a modal on purpose: a helper works
   the queue on their phone, and a modal shown by a poll never reaches a
   suspended tab. Rendered server-side into the page and refreshed by the poll.
   See the Decision 4 amendment in shift-lifecycle-plan. */
.hr-checkout-banner {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-3);
    margin-bottom: var(--spacing-4);
}

.hr-checkout-banner__lead {
    margin: 0;
}

/* Faculty portal: confirm-helper modal card. Layout only — colors come from
   the shared .modal / .btn components. */
.hr-confirm__lead {
    margin: 0 0 var(--spacing-3);
}

.hr-confirm__details {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--spacing-1) var(--spacing-3);
    margin: 0 0 var(--spacing-3);
}

.hr-confirm__details dt {
    font-weight: var(--font-weight-semibold);
}

.hr-confirm__details dd {
    margin: 0;
}

.hr-confirm__note {
    margin: 0 0 var(--spacing-3);
    font-size: var(--font-size-sm);
}

/* Pick-list shown when a name matches more than one person in the directory.
   A bare fieldset: the legend is for screen readers, the visible prompt is the
   lead paragraph above it. */
.hr-confirm__choices {
    display: grid;
    gap: var(--spacing-2);
    margin: 0 0 var(--spacing-3);
    padding: 0;
    border: 0;
}

.hr-confirm__choice {
    display: flex;
    align-items: baseline;
    gap: var(--spacing-2);
}

.hr-confirm__choice label {
    display: grid;
}

.hr-confirm__choice-name {
    font-weight: var(--font-weight-semibold);
}

.hr-confirm__choice-meta {
    font-size: var(--font-size-sm);
    color: var(--text-secondary);
}

/* Top bar: breadcrumb on the left, the Available toggle on the right — using the
   otherwise-empty breadcrumb space so it doesn't take its own row beside the
   title on mobile. The check-in/out button stays by the title
   (.hr-screen__header), so the two presence controls split across the two rows
   that each have room. */
.hr-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-3);
    flex-wrap: wrap;
    margin-bottom: var(--spacing-3);
}

.hr-topbar__action {
    margin: 0;
    margin-left: auto;  /* stay right-aligned even if the breadcrumb wraps */
}

/* Availability toggle + its live label, sitting at the right of the title row. */
.hr-presence__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-2);
}

.hr-presence__status {
    font-weight: var(--font-weight-semibold);
    /* Tighten the line box to ~the toggle height so the switch and label center
       on each other (the relaxed body line-height made the toggle read low). */
    line-height: var(--line-height-tight);
}

.hr-presence__hint {
    margin: 0;
}

/* Claim-button group in the queue header: the course-scoped primaries +
   "any course" overflow, wrapping onto their own row on narrow screens. */
.hr-claim-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--spacing-2);
}

/* Shared "Help next:" label that prefixes the compact scope buttons, so the
   bare course-code / Any Course / Next in line buttons read as "help next in …"
   without each repeating the phrase. */
.hr-claim-actions__label {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
}

/* Compact claim buttons — tighter than .btn so the scope row stays on one line.
   Rounded rectangles (not the coverage chips' full pills) so the actions read
   as distinct from the Covering toggles. Course scopes are gold-filled
   (primary); the catch-alls are the lighter ghost overflow. */
.hr-claim__btn {
    padding: var(--spacing-1) var(--spacing-3);
    border: var(--border-2) solid var(--border-color);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

.hr-claim__btn:hover {
    background: var(--bg-secondary);
}

.hr-claim__btn--primary {
    background: var(--ucb-gold);
    border-color: var(--ucb-gold);
    color: var(--ucb-black);
}

.hr-claim__btn--primary:hover {
    background: var(--ucb-gold-dark);
    border-color: var(--ucb-gold-dark);
    color: var(--ucb-black);
}

.hr-claim__btn:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

.hr-claim__btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* Coverage toggles (helper dashboard): real <button aria-pressed> pills for the
   courses + Any Course the helper is covering right now. Off reads as an outlined
   pill, on as a filled gold pill — a fill/outline difference (not color alone),
   with aria-pressed carrying the state for assistive tech. Mirrors the
   lightweight On-break affordance. */
.hr-coverage {
    margin-block: var(--spacing-2);
}

.hr-coverage__toggles {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-2);
}

.hr-coverage__form {
    margin: 0;
}

.hr-coverage__toggle {
    padding: var(--spacing-1) var(--spacing-3);
    border: var(--border-2) solid var(--ucb-gold);
    border-radius: var(--radius-full);
    background: transparent;
    color: var(--text-primary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.hr-coverage__toggle:hover {
    background: var(--bg-secondary);
}

/* On: a filled gold pill (distinct from the outlined off state, not hue-only). */
.hr-coverage__toggle[aria-pressed="true"] {
    background: var(--ucb-gold);
    color: var(--ucb-black);
}

.hr-coverage__toggle[aria-pressed="true"]:hover {
    background: var(--ucb-gold-dark);
    border-color: var(--ucb-gold-dark);
}

.hr-coverage__toggle:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

.hr-coverage__toggle:disabled {
    opacity: 0.6;
    cursor: progress;
}

/* Phone-first columns: stacked on phones, side by side on desktop. The first
   child stays narrower (controls / forms), the second grows (the live list). */
.hr-cols {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-3);
}

/* Each column is its own vertical stack, so a gap separates the section
   heading (and the "Claim next" buttons) from the card list beneath it. */
.hr-cols__aside,
.hr-cols__main {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
    min-width: 0;
}

@media (min-width: 768px) {
    .hr-cols {
        flex-direction: row;
        align-items: flex-start;
    }
    /* The aside (coverage + active sessions) stays compact so the live queue
       gets the room — it's mostly empty when no one's being helped. */
    .hr-cols__aside {
        flex: 1 1 30%;
    }
    .hr-cols__main {
        flex: 1 1 70%;
    }
}

/* Landing manager tooling: a labeled cluster of admin links below the shared
   page list. The "Manager tools" heading + extra top space (no divider rule)
   separate manager-only tools from the public navigation; each task group
   stacks its own compact card list so a manager scans by group instead of
   reading one flat list. Layout/token only. */
.hr-manager-tools {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-4);
    padding-top: var(--spacing-4);
}

.hr-tool-group {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
}

/* Group label heading each cluster of links. Base size (matches the card
   titles, so it never reads smaller than the cards) and the secondary color +
   lighter weight keep it clearly subordinate to the bold "Manager tools" h2. */
.hr-tool-group__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-semibold);
    color: var(--text-secondary);
    margin: 0;
}

/* Compact card list (queue entries, active sessions, schedule slots). Tighter
   than .section-card, which is a heavy full-width panel — this mirrors the
   scheduling app's per-item card pattern. Token-only; no component overrides. */
.hr-list {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-1);
}

.hr-item {
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--spacing-2) var(--spacing-3);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-1);
}

/* Accent a card that needs attention (e.g. a student who requested this helper). */
.hr-item--accent {
    border-color: var(--color-success);
    border-width: 2px;
}

/* Location card with a ready-to-post join QR: details on the left, the
   scannable code on the right. Stacks on narrow screens. */
.hr-item--location {
    flex-direction: row;
    align-items: flex-start;
    flex-wrap: wrap;
    gap: var(--spacing-3);
}

.hr-item__body {
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--spacing-2);
}

/* Only the row-layout location card needs the body to claim the remaining
   width beside the QR code. On the default column-layout cards a flex-basis
   here would instead force a tall vertical height regardless of content. */
.hr-item--location .hr-item__body {
    flex: 1 1 16rem;
}

.hr-location-qr {
    flex: none;
    background: var(--ucb-white);
    border-radius: var(--radius-md);
    padding: var(--spacing-2);
    line-height: 0;
}

.hr-location-qr img {
    display: block;
    width: 7rem;
    height: auto;
    image-rendering: pixelated;
}

/* A card that is itself a link (landing navigation): the whole card is the
   tap target, so suppress the global anchor underline/color and signal
   clickability with a chevron + hover/focus affordance instead. */
a.hr-item--link {
    color: var(--text-primary);
    text-decoration: none;
    position: relative;
    padding-right: var(--spacing-8);
    transition: var(--transition-fast);
    transform: translateZ(0);
    backface-visibility: hidden;
}

a.hr-item--link::after {
    content: "›";
    position: absolute;
    right: var(--spacing-3);
    top: 50%;
    transform: translateY(-50%);
    font-size: var(--font-size-xl);
    color: var(--text-secondary);
}

a.hr-item--link:hover,
a.hr-item--link:focus-visible {
    color: var(--text-primary);
    text-decoration: none;
    border-color: var(--ucb-gold);
    box-shadow: var(--shadow-sm);
}

a.hr-item--link:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

.hr-item__top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--spacing-2);
}

.hr-item__name {
    font-weight: var(--font-weight-semibold);
}

/* Wait time / time range sits at the far right of the row. */
.hr-item__meta {
    margin-left: auto;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

/* The card's metadata row: course pill, status badges, and — on the shifts
   list — an inline action pushed to the far edge. align-items keeps the pills
   at their natural height; the default `stretch` would pull them to match a
   taller sibling like a button. */
.hr-item__badges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--spacing-1);
}

/* Per-display font-size tuner (manager displays page). */
.hr-scale-form {
    margin-block: var(--spacing-3);
}

.hr-scale-form__row {
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
    margin-block: var(--spacing-1);
}

.hr-scale-form__row .form-control {
    width: auto;
    max-width: 6rem;
    flex: 0 0 auto;
}

.hr-scale-form__row .btn {
    flex: 0 0 auto;
}

.hr-item__topic {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    margin: 0;
}

/* Student table/location — wayfinding for the helper, so it reads as primary
 * text (not muted like topic/meta). */
.hr-item__loc {
    display: flex;
    align-items: center;
    gap: var(--spacing-1);
    font-weight: var(--font-weight-semibold);
    color: var(--text-primary);
    margin: 0;
}

/* Waiting-queue card body: student info on the left; the claim button stacked
   top-right with the wait time directly beneath it (right-aligned). Keeps each
   queue card compact — the button no longer spans its own full-width row. */
.hr-queue {
    display: flex;
    align-items: flex-start;
    gap: var(--spacing-3);
}

.hr-queue__main {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
}

.hr-queue__aside {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--spacing-1);
}

/* Weekly schedule grid: one day per row on phones, widening to a multi-day
   grid on larger screens. */
.hr-week {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--spacing-3);
}

@media (min-width: 600px) {
    .hr-week {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1100px) {
    .hr-week {
        grid-template-columns: repeat(5, 1fr);
    }
}

/* A single day's coverage list inside the week grid. */
.hr-day__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
    margin: 0 0 var(--spacing-2);
    padding-bottom: var(--spacing-1);
    border-bottom: 1px solid var(--border-color);
}

.hr-slot {
    margin-bottom: var(--spacing-2);
}

.hr-slot:last-child {
    margin-bottom: 0;
}

.hr-slot__time {
    font-weight: var(--font-weight-semibold);
}

.hr-slot__notes {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

/* ── Calendar schedule (Tier D) ───────────────────────────────────────────
   A help-room calendar: weekday columns (Mon–Fri) × time rows on desktop, a
   stacked per-day agenda on phones. Adapts the scheduling app's cal-grid
   technique (CSS-grid time×day, grid-row positioning) but Mon–Fri, non-HTMX,
   and mobile-first — the agenda replaces the desktop grid below md instead of
   horizontally scrolling it. Layout + the established calendar color tokens
   only (mirrors .cal-event in scheduling.css). */

/* Mobile-first: the agenda is the default view; the grid is hidden until md. */
.hr-cal {
    display: none;
}

.hr-agenda {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-4);
}

/* Mobile day switcher (built by schedule_day_switcher.js): a segmented Mon–Fri
   control above the agenda. One day's coverage shows at a time, so a densely
   covered day stays scannable without a long scroll. It lives inside .hr-agenda,
   which is hidden at md+, so it's inherently mobile-only. */
.hr-day-switcher {
    display: flex;
    gap: var(--spacing-1);
}

.hr-day-tab {
    flex: 1 1 0;
    min-width: 0;
    padding: var(--spacing-2) var(--spacing-1);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-secondary);
    color: var(--text-secondary);
    font-family: inherit;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

/* Selected day reuses the primary (gold/black) pairing — the same active cue as
   the site nav, and contrast-checked in both themes via the button tokens. */
.hr-day-tab[aria-selected="true"] {
    background: var(--btn-primary-bg);
    color: var(--btn-primary-text);
    border-color: var(--btn-primary-border);
}

.hr-day-tab:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

@media (min-width: 768px) {
    .hr-agenda {
        display: none;
    }

    .hr-cal {
        --hr-cal-slot-height: var(--spacing-3);
        display: grid;
        grid-template-columns: auto repeat(5, 1fr);
        border: 1px solid var(--border-color);
        border-radius: var(--radius-lg);
        overflow: hidden;
    }
}

/* Header row: the time-gutter corner + sticky day names. */
.hr-cal__corner,
.hr-cal__day-header {
    grid-row: 1;
    background: var(--bg-secondary);
    padding: var(--spacing-2);
    text-align: center;
    font-weight: var(--font-weight-bold);
    border-bottom: 1px solid var(--border-color);
}

.hr-cal__corner {
    grid-column: 1;
}

/* Hour labels down the gutter. Top-aligned just below their hour line (no
   upward nudge — that pushed the first label into the day-header row); with
   no drawn gridlines this reads the same and matches slot tops exactly. */
.hr-cal__time-label {
    grid-column: 1;
    padding: var(--spacing-1) var(--spacing-2) 0;
    font-size: var(--font-size-xs);
    color: var(--text-secondary);
    text-align: right;
}

/* A day column: its own lane grid spanning the time rows, so overlapping
   coverage lays out side-by-side (lane positions come from the service).
   Inline styles supply the row/lane templates; rows reuse the same
   slot-height var as the outer grid so times stay aligned with the gutter. */
.hr-cal__day {
    display: grid;
    grid-row: 2 / -1;
}

/* A positioned coverage block. grid-row/grid-column come from
   row_start/row_span and lane_start/lane_span. */
.hr-cal__slot {
    margin: var(--border-1) var(--spacing-1);
    padding: var(--spacing-1) var(--spacing-2);
    border-left: var(--border-3) solid var(--color-info-border);
    border-radius: var(--radius-sm);
    background-color: color-mix(in srgb, var(--color-info-border) 10%, var(--color-bg-secondary));
    font-size: var(--font-size-xs);
    line-height: 1.25;
    overflow: hidden;
}

/* Closure / blackout block: danger accent so it reads as "room unavailable". */
.hr-cal__slot--closure {
    border-left-color: var(--color-danger-border);
    background-color: color-mix(in srgb, var(--color-danger-border) 12%, var(--color-bg-secondary));
}

.hr-cal__slot-name {
    font-weight: var(--font-weight-semibold);
}

.hr-cal__slot-time {
    color: var(--text-secondary);
}

.hr-cal__slot-link {
    display: inline-block;
    margin-top: var(--spacing-1);
}

/* ── Block schedule grid (sheet-style) ─────────────────────────────────────
   A period × weekday table with helpers stacked in each cell — the readable
   replacement for the lane grid when many helpers cover one block (mirrors the
   legacy Google Sheet). Desktop table; the .hr-agenda stack takes over below
   md. Course colors cycle a small palette of existing hue tokens, mixed into
   the card background so each chip reads in both themes. */

.hr-block-wrap {
    display: none;
}

@media (min-width: 768px) {
    .hr-block-wrap {
        display: block;
        overflow-x: auto;
    }
}

.hr-block {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

.hr-block th,
.hr-block td {
    border: 1px solid var(--border-color);
    vertical-align: top;
    padding: var(--spacing-1);
}

/* Header cells set their own color: the global `th { color: table-header-text }`
   in accessibility.css pairs with table-header-bg, but this grid uses a
   bg-secondary header, so the text must be re-paired or it vanishes in light
   mode. */
.hr-block thead th {
    background: var(--bg-secondary);
    color: var(--text-primary);
    text-align: center;
    padding: var(--spacing-2);
}

.hr-block__time-head {
    width: 9%;
}

.hr-block__period {
    background: var(--bg-secondary);
    white-space: nowrap;
    text-align: center;
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    color: var(--text-secondary);
}

/* This grid is laid out as a table but isn't a scannable data table, so the
   global zebra striping + full-row hover (set on `tr` in accessibility.css and
   re-applied for dark mode in dark-mode-components.css) fight the course
   colors. Rather than out-specify those theme-specific `tr` rules, give the
   data cells an opaque page-background so any row-level background never shows
   through — then cue the hovered cell instead, identically in both themes. */
.hr-block tbody td {
    background: var(--bg-primary);
}

/* A stacked helper chip inside a cell. The hover cues the individual helper
   session (the chip), not the whole cell/row. */
.hr-chip {
    margin-bottom: var(--spacing-1);
    padding: var(--spacing-1) var(--spacing-2);
    border-left: var(--border-3) solid var(--border-color);
    border-radius: var(--radius-sm);
    background-color: var(--bg-secondary);
    font-size: var(--font-size-xs);
    line-height: 1.3;
    text-align: center;
    overflow-wrap: anywhere;
    transition: transform var(--transition-fast), box-shadow var(--transition-fast);
}

.hr-chip:last-child {
    margin-bottom: 0;
}

/* Lift the hovered session above its dense neighbors so the cue is obvious. */
.hr-chip:hover {
    transform: translateY(-2px) scale(1.02);
    box-shadow: var(--shadow-lg);
    position: relative;
    z-index: 1;
}

.hr-chip__name {
    display: block;
    font-weight: var(--font-weight-semibold);
}

.hr-chip__course {
    color: var(--text-secondary);
}

.hr-chip__time {
    display: block;
    color: var(--text-secondary);
}

.hr-chip__link {
    display: inline-block;
    margin-top: var(--spacing-1);
}

/* When a helper signed up for a block, on a staff schedule grid. Quiet by
   design — bookkeeping a faculty member goes looking for when a capped block
   is contested, not something the grid should shout at everyone reading it. */
.hr-chip__claim {
    display: block;
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
}

/* An edit is a caveat on the sign-up date, not a second date of equal weight:
   updated_at moves for a note or a coordinator's lock as readily as for a
   change of hours. */
.hr-chip__claim-edit {
    font-style: italic;
}

/* The hours a helper has claimed, under their name on the faculty portal.
   Each line is the slot and when it was claimed — faculty cap how many
   helpers may take a block and run it first-come-first-served, so the
   timestamp is the point, not decoration. */
.hr-slot-claims {
    list-style: none;
    margin: var(--spacing-2) 0 0;
    padding: 0;
}

.hr-slot-claims li {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-1) var(--spacing-3);
    justify-content: space-between;
    font-size: var(--font-size-sm);
}

.hr-slot-claims__when {
    font-weight: var(--font-weight-semibold);
}

.hr-slot-claims__stamp {
    color: var(--text-secondary);
}

/* A rare action folded away on a row that is mostly read: the coverage page's
   per-occurrence cancel. Its note field on every row turned a day a
   coordinator only wants to scan into a page of stacked form controls.

   Sits at the end of the badge row, so a card that needs nothing is two lines.
   Opening it hands the card a full-width row, since the form inside would
   otherwise be squeezed into a flex item beside the badges. */
.hr-item__disclosure {
    margin-left: auto;
}

.hr-item__disclosure[open] {
    flex: 1 0 100%;
    margin-left: 0;
}

.hr-item__disclosure > summary {
    cursor: pointer;
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

.hr-item__disclosure[open] > summary {
    margin-bottom: var(--spacing-2);
}

/* Section-level disclosure: a <details> that IS a section card, with its own
   heading as the summary. Distinct from .hr-item__disclosure, which is scoped
   to one row of a list and carries that row's flex behavior. Follows the
   .hr-bar--expandable pattern — native marker suppressed, custom triangle that
   turns when open — so the two read the same way on different scales. */
.hr-disclosure > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
}

.hr-disclosure > summary::-webkit-details-marker {
    display: none;
}

.hr-disclosure > summary:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

/* The heading is the summary's own text, so it must not add block spacing that
   pushes the marker out of line with it. */
.hr-disclosure > summary > .hr-section-title {
    margin: 0;
}

.hr-disclosure__marker {
    flex: 0 0 auto;
    width: 0;
    height: 0;
    border-left: var(--spacing-2) solid var(--text-secondary);
    border-top: var(--spacing-1) solid transparent;
    border-bottom: var(--spacing-1) solid transparent;
    transition: transform var(--transition-fast);
}

.hr-disclosure[open] .hr-disclosure__marker {
    transform: rotate(90deg);
}

.hr-disclosure__body {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-3);
    padding-top: var(--spacing-3);
}

/* Day stepper on the coverage page: the day you are looking at reads as the
   heading, with the steps either side. Three same-weight buttons made the
   current date compete with the two dates you are not looking at. */
.hr-daynav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-2);
}

.hr-daynav__date {
    margin: 0;
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-semibold);
    text-align: center;
}

.hr-daynav__center {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-1);
}

.hr-daynav__jump {
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
}

.hr-daynav__jump input[type="date"] {
    width: auto;
    font-size: var(--font-size-sm);
}

.hr-daynav__today {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-normal);
}

/* Course color — one stable hue per course (ScheduleService.course_hue),
   supplied inline as --course-hue; saturation/lightness come from theme tokens
   so it adapts to light/dark. Both the schedule chip accent and the course
   badge key off the same hue, so a course reads the same color at every
   help-room touchpoint. */
.hr-course-accent {
    border-left-color: hsl(var(--course-hue) var(--course-sat) var(--course-border-l));
    background-color: hsl(var(--course-hue) var(--course-sat) var(--course-tint-l));
}

/* A shift longer than one period repeats in every row it covers. The repeats
   are marked by border STYLE only — never by color. Dimming the fill or
   dropping opacity would shift the rendered hue, and a course has to read as
   the same color at every help-room touchpoint, so --course-hue and the
   tint/border lightness are left exactly as the anchor row's. The full time
   range on each chip does the explaining; the dash just says "same shift". */
.hr-chip--continues {
    border-left-style: dashed;
}

/* Your own slot on the sign-up grid.
   A ring on all four edges is the one visual channel left: the left border and
   the background tint already carry the course hue, dashed already means "same
   shift continued", and red already means closure. Drawn inside the chip
   (negative offset) so it never shifts the dense grid's layout.
   Not a color-only cue -- the ring is present/absent, and --text-primary
   guarantees luminance contrast in both themes, so it survives greyscale
   (WCAG 1.4.1). The template pairs it with sr-only "Your slot." text.
   :focus-visible is (0,2,0) to this rule's (0,1,0), so keyboard focus still
   wins and paints the gold outline over it. */
.hr-chip--own {
    outline: var(--border-2) solid var(--text-primary);
    outline-offset: calc(-1 * var(--border-2));
}

.hr-chip--own .hr-chip__name {
    font-weight: var(--font-weight-bold);
}

/* Mobile agenda equivalent. The agenda card has no course-hue border to avoid,
   so the same ring reads cleanly there too. */
.hr-item--own {
    outline: var(--border-2) solid var(--text-primary);
    outline-offset: calc(-1 * var(--border-2));
}

/* A course pill (helper dashboard, status page, mobile agenda) — sized like
   .status-badge but colored by --course-hue. */
.hr-course-badge {
    display: inline-block;
    padding: var(--spacing-1) var(--spacing-2_5);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--text-primary);
    background-color: hsl(var(--course-hue) var(--course-sat) var(--course-tint-l));
    border: 1px solid hsl(var(--course-hue) var(--course-sat) var(--course-border-l));
}

/* Closure / blackout: danger accent, "room unavailable". */
.hr-chip--closure {
    border-left-color: var(--color-danger-border);
    background-color: color-mix(in srgb, var(--color-danger-border) 12%, var(--bg-secondary));
}

/* "Upcoming closures" below the grid: the shared .section-card panel (heading +
   compact rows) gives the section a clear boundary and a 3xl heading that leads
   the rows. The rows inside are flat one-liners — NOT item-cards — so there's no
   double-boxing (a card-in-a-card was the earlier mistake). Just needs top
   separation from the grid (.section-card supplies its own bottom margin). */
.hr-upcoming-closures {
    margin-top: var(--spacing-6);
}

/* In light mode .section-card's bg (--card-bg = --bg-elevated = white) matches
   the white page, so only the shadow separates it — no top edge. The subtle
   gray surface token gives a real boundary. In dark mode --bg-secondary is the
   same gray-950 the card already uses, so dark is unchanged. */
.hr-upcoming-closures.section-card {
    background: var(--bg-secondary);
}

/* section-card's default h2 is 3xl — larger than this page's 2xl H1. Bring the
   panel heading down to the help-room section scale so it sits below the page
   title. Two classes out-specify .section-card h2 (no load-order reliance); the
   panel's gold underline stays as the section delimiter. */
.hr-upcoming-closures.section-card h2 {
    font-size: var(--font-size-xl);
    margin-bottom: var(--spacing-4);
}

/* One compact row per closure (date · time — reason), separated by spacing only
   (no dividers). Mobile-first: stacked on two lines; collapses to a single
   inline line at desktop width. The em-dash separator only appears inline (it
   would dangle if the reason wrapped). */
.hr-closures {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-2);
}

.hr-closure-row {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-1);
}

.hr-closure-row__when {
    color: var(--text-primary);
    white-space: nowrap;
}

.hr-closure-row__reason {
    color: var(--text-secondary);
}

@media (min-width: 768px) {
    .hr-closure-row {
        flex-direction: row;
        align-items: baseline;
        gap: var(--spacing-2);
    }

    .hr-closure-row__reason::before {
        content: "—";
        margin-right: var(--spacing-2);
    }
}

/* Editable chip (manager builder): the read-only chip rendered as a <button>
   that opens the slot editor modal. Reset the native button chrome (borders,
   font) so it still reads as a chip; the lift on hover comes from the base
   .hr-chip:hover rule, and we add the keyboard-focus mirror + outline here. */
.hr-chip--editable {
    display: block;
    width: 100%;
    border-top: 0;
    border-right: 0;
    border-bottom: 0;
    font-family: inherit;
    color: var(--text-primary);
    cursor: pointer;
}

.hr-chip--editable:focus-visible {
    transform: translateY(-2px) scale(1.02);
    box-shadow: var(--shadow-lg);
    position: relative;
    z-index: 1;
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

/* Editable agenda item: the coverage card rendered as a full-width <button>
   that opens the slot editor.
   align-items is not cosmetic here. .hr-item is a column flexbox, and the UA
   stylesheet sets align-items: center on <button> — so the card's inner rows
   shrink to their content and center, while the identical card rendered as a
   <div> stretches them full width. On the manager builder every agenda card is
   a button, so they were uniformly centered and the difference never showed. The
   sign-up grid puts editable and read-only cards in one list (your slots vs
   everyone else's), which is where it became two layouts in one column. */
.hr-item--editable {
    width: 100%;
    align-items: stretch;
    font-family: inherit;
    font-size: inherit;
    text-align: left;
    color: var(--text-primary);
    cursor: pointer;
}

.hr-item--editable:hover,
.hr-item--editable:focus-visible {
    border-color: var(--ucb-gold);
}

.hr-item--editable:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

/* Per-cell "+ Add" affordance in the editable grid — subtle until hovered or
   focused so the filled chips stay the visual focus. */
.hr-cell-add {
    display: block;
    width: 100%;
    margin-top: var(--spacing-1);
    padding: var(--spacing-1);
    border: var(--border-1) dashed var(--border-color);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.hr-cell-add:hover {
    background-color: var(--bg-secondary);
    color: var(--text-primary);
}

.hr-cell-add:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}

/* Course legend above the grid — a row of course badges. */
.hr-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-2);
    margin: 0 0 var(--spacing-3);
    padding: 0;
    list-style: none;
}

/* Course-color picker (manager portal): a hue slider + live preview swatch and
   named preset swatches. The hue is unconstrained (0–359), so there's no cap on
   how many courses can have a distinct color. */
.hr-hue-row {
    display: flex;
    align-items: center;
    gap: var(--spacing-3);
}

.hr-hue-slider {
    flex: 1 1 auto;
    min-width: 0;
}

.hr-hue-presets {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-2);
    margin-top: var(--spacing-2);
}

/* A preset swatch is a mini course badge — the same tint fill + accent border
   the real badge uses, so the preview matches in both light and dark mode. */
.hr-hue-swatch {
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border: 1px solid hsl(var(--course-hue) var(--course-sat) var(--course-border-l));
    border-radius: var(--radius-sm);
    cursor: pointer;
    background-color: hsl(var(--course-hue) var(--course-sat) var(--course-tint-l));
}

/* Public schedule filter bar — ONE row holding every filter: the course chips
   lead, the helper picker and Clear trail. The field label is visually hidden
   (the default option text names the field), so the controls sit on a single
   tight line instead of a taller label-above-select grid.

   The margin-bottom is deliberately absent: .hr-screen's flex gap already
   spaces this from the grid, and the old margin stacked on top of it. Between
   that, the chip row's own margin, and a second band for the dropdown, two
   filters were costing three rows of vertical space above the grid. */
.hr-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-2) var(--spacing-3);
    align-items: center;
}

.hr-filters .form-group {
    margin-bottom: 0;
}

.hr-filters select {
    width: auto;
}

/* Helper picker. Fixed measure because combobox.js swaps the <select> for a
   text input, which — unlike a select sized to its longest option — has no
   intrinsic width and would otherwise collapse in the flex row. Wide enough
   for a full name, and free to shrink before the chips wrap. */
.hr-filters__picker {
    flex: 0 1 14rem;
}

/* Everything trailing the chips: picker, Clear, the no-JS Apply button. On a
   phone this wraps to its own line under the chips and stays left-aligned with
   them — an auto margin would still push it right on the wrapped line, leaving
   the two rows ragged against opposite edges. */
.hr-filters__end {
    display: flex;
    gap: var(--spacing-2);
    align-items: center;
}

/* Once both fit on one line, push this to the far end so the toolbar reads as
   chips-left / controls-right. */
@media (min-width: 768px) {
    .hr-filters__end {
        margin-left: auto;
    }
}

/* A button in the toolbar keeps its natural width. Left to shrink alongside
   the picker, "Clear filters" wrapped to two lines and made the whole row half
   again as tall — so the picker (flex: 0 1 14rem above) absorbs the shrink and
   the labels stay on one line. */
.hr-filters__action {
    flex: 0 0 auto;
}

/* "Add a profile photo" helper nudge (dashboard). Layout only — the alert
 * component supplies the colors. */
.hr-photo-nudge {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-3);
}

.hr-photo-nudge__text {
    margin: 0;
}

.hr-photo-nudge__actions {
    display: flex;
    align-items: center;
    gap: var(--spacing-2);
}

/* Small-screen sort control, on both faculty tables. Below 992px .data-table
   stacks into labeled cards and hides its thead (components.css), which takes
   the column-header sort with it — so the same orderings are offered here as a
   plain select instead.

   Hidden from 993px, not 992px, precisely because the card layout it stands in
   for ends AT 992px inclusive: pairing this with min-width: 992px would leave a
   one-pixel viewport where the headers and this control were both hidden and
   the table could not be sorted at all.

   display: none rather than a visually-hidden pattern: it takes the duplicate
   control out of the tab order and the accessibility tree on desktop, and a
   display: none select still submits its value with the form, so it carries
   the active ordering across a filter change. */
.hr-sort-mobile {
    flex: 1 1 100%;
}

@media (min-width: 993px) {
    .hr-sort-mobile {
        display: none;
    }
}

/* Consolidated attendance cell (helper-activity table): the met/scheduled count
   and reliability %, a slim reliability bar, and any ad-hoc coverage, stacked.
   The bar reuses the hr-bar track/fill so the gold sits in its own track and
   never behind text — contrast holds in both themes (the % lives in the text). */
.hr-attend {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-1);
    align-items: flex-start;
}

.hr-attend__counts {
    font-variant-numeric: tabular-nums;
}

.hr-attend__bar {
    display: block;
    width: 100%;
    max-width: 9rem;
}

/* Sortable column header: a plain text button so the sort is keyboard- and
   screen-reader-operable. The ↕/▲/▼ glyph changes shape (not just color) with
   aria-sort, so sort state isn't conveyed by color alone. */
.hr-sort {
    background: none;
    border: 0;
    padding: 0;
    margin: 0;
    font: inherit;
    font-weight: var(--font-weight-bold);
    color: inherit;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-1);
}

.hr-sort::after {
    content: "↕";
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
}

th[aria-sort="ascending"] .hr-sort::after {
    content: "▲";
    color: var(--text-primary);
}

th[aria-sort="descending"] .hr-sort::after {
    content: "▼";
    color: var(--text-primary);
}

.hr-sort:focus-visible {
    outline: var(--focus-outline-width) var(--focus-outline-style) var(--focus-outline-color);
    outline-offset: var(--focus-outline-offset);
}
