/* Calendar and Agenda List — shared components.
   Extracted from components.css (CSS split, CLAUDE.md §12).
   Consumed by frontend/js/components/calendar.js and agenda-list.js,
   which back the month-grid / day-by-day views across My Schedule,
   Wish Submission, Planning Calendar, Current Schedule, and Published
   Roster (CLAUDE.md §13), and the schedule badges drawn into those cells. Deliberately designed to be
   source-order-independent (specificity/compound-selector tricks,
   translucent overlay layers) — see the individual rule comments
   below — with the one exception noted where .cal-cell.cal-past is
   appended at the end of this same file.
   Dark-theme overrides are colocated at the bottom of this file
   instead of a separate far-away tail block. */
@layer components {

/* ── Schedule badges (components/schedule-calendar.js, features/wishes/presentation.js) ──
   One shift or wish badge in a month cell: My Schedule, the personal
   dashboard legend and Wish Submission. Mobile compaction further down. */
.sch-shift-badge {
  border-radius: var(--radius-sm);
  padding: 2px var(--space-2);
  margin-bottom: 2px;
  font-size: 0.7rem;
  font-weight: 600;
  display: flex;
  flex-direction: column;
  gap: 1px;
  border-left: 5px solid transparent;
}
/* Location colour (identity) — color-engine.js, the same axis the hero
   panel and the day strip use, so a shift's colour means the same
   thing everywhere it appears. A thick left border alone reads weakly at
   this badge's compact size, so .sch-shift-name also carries a .chip-dot
   (same dot markup roster-grid.js/day-detail.js use) — it inherits the
   badge's own `color`, so no separate per-slot dot rules are needed here,
   unlike .shift-chip's below (which doesn't tint its own text). A hollow
   ring (chip-oncall) marks on-call/off-hours instead of the solid dot —
   the fill-style channel §13.1 defines, stronger than the previous
   dashed-vs-solid border alone. */
.sch-shift-badge:is(.loc-1, .loc-2, .loc-3, .loc-4, .loc-5, .loc-6, .loc-7, .loc-8) { background: color-mix(in srgb, var(--loc) 14%, var(--surface)); color: var(--loc); border-left-color: var(--loc); }
/* Overflow slot — also hit by wish-submission.js's shift-type wishes that
   have no location preference set at all (location_id is optional, §3.6). */
.sch-shift-badge.loc-other { background: var(--surface-sunk); color: var(--muted); border-left-color: var(--muted); }
.sch-shift-badge.chip-oncall { border-left-style: dashed; }
.sch-shift-badge .chip-dot { background: currentColor; }
.sch-shift-badge.chip-oncall .chip-dot { background: transparent; border: 1.5px solid currentColor; }
.sch-shift-name { font-weight: 700; display: flex; align-items: center; gap: 4px; }
/* Approval-status tone (my-schedule.js's wish badges/legend — a fixed
   approved/not_approved/undecided set, unrelated to the location-hue loc-N
   modifiers above; a badge is only ever one or the other). */
.sch-shift-badge.tone-success { background: var(--success-bg); color: var(--success); border-left-color: var(--success); }
.sch-shift-badge.tone-danger  { background: var(--danger-bg);  color: var(--danger);  border-left-color: var(--danger); }
.sch-shift-badge.tone-neutral { background: var(--surface-sunk); color: var(--muted); border-left-color: var(--muted); }
.sch-loc-name   { font-weight: 400; opacity: 0.8; }
/* Future shifts render as an <a> (shiftBadgeHTML, my-schedule.js) — a direct
   "click a date to request a swap" affordance; reset link chrome and add a
   focus/hover cue so it still reads as clickable. */
a.sch-shift-badge { text-decoration: none; color: inherit; cursor: pointer; }
a.sch-shift-badge:hover, a.sch-shift-badge:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }

/* A planned compensation day off in a month cell (My Schedule, Wish
   Submission): the shared COMP mark (base-ui.css), on its own line. */
.sch-comp-day { margin-bottom: 2px; line-height: 1; }

/* ── Shift times in calendar badges (formerly "A1") ────────────────────────── */
.sch-shift-times {
  display: block;
  font-size: 0.68rem;
  color: var(--muted);
  margin-top: 1px;
}

/* ── Calendar ─────────────────────────────────────────────────────────────── */
/* A 7-column grid, not a <table> — components/calendar.js renders header
   cells and day cells as plain sibling divs and lets grid auto-placement
   wrap every 7 into a row. Two things this buys over the table markup it
   replaced:
   - `grid-template-columns: repeat(7, 1fr)` keeps all 7 day columns equal-
     width regardless of cell content, the same guarantee `table-layout:
     fixed` used to give.
   - Grid's default `align-items: stretch` makes every cell in a row match
     its tallest sibling for free. A table cell's child can't reliably do
     this — percentage heights don't resolve against a table cell whose own
     height is auto — so a lightly-loaded day used to visibly stop short of
     a heavier one in the same week (more wish/shift badges, more avatars).
   `gap` replaces the old per-cell `<td>` padding as the gutter between
   cells, now that cells are direct grid children with no wrapper. */
.calendar {
  width: 100%;
  display: grid;
  /* minmax(0, …): equal columns even when a cell holds unbreakable text. */
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 4px;
  user-select: none;
}

/* A consumer's row above a week (Calendar `renderWeek`): across every column,
   its items on the calendar's own columns (My Schedule's leave spans). */
.cal-week-row { grid-column: 1 / -1; display: grid; grid-template-columns: subgrid; }
.cal-th {
  padding: var(--space-2) var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--muted);
  text-align: center;
}

.cal-cell {
  min-height: 80px;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  cursor: default;
  transition: background var(--transition);
  position: relative;
}

/* Only cells wired up with onDayClick get role="button" (components/calendar.js)
   — a display-only grid (my-schedule.js) stays non-interactive-
   looking: no pointer cursor, no hover/focus tint. */
.cal-cell[role="button"] { cursor: pointer; }
.cal-cell[role="button"]:hover { background: var(--hover-tint); }
.cal-cell[role="button"]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
  background: var(--hover-tint);
}
.cal-cell.other-month { background: var(--surface-sunk); opacity: 0.5; cursor: default; }
.cal-cell.today { border-color: var(--primary); }
.cal-cell.locked { cursor: not-allowed; background: var(--surface-sunk); }
.cal-cell.selected { border-color: var(--primary); box-shadow: 0 0 0 2px var(--p-200); }
/* Mobile agenda counterpart: an AgendaList row carrying the same "selected
   day" class. */
.agenda-row.selected { box-shadow: inset 0 0 0 2px var(--p-200); }

/* Weekend shading — a translucent overlay layer rather than a `background`
   rule, so it composites on top of whatever background a cell already has
   (heatmap colour, .sc-holiday, plain surface) instead of fighting those
   rules for specificity. Matches the neutral-gray weekend wash roster-grid's
   .rgv2-row-weekend already established. */
.cal-cell.weekend::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, .035);
  pointer-events: none;
  border-radius: inherit;
}

/* Holiday marker (wish-submission.js) — same amber "informational, not a
   hard violation" treatment as .conflict-soft. */
.cal-cell.sc-holiday { border-color: var(--warning); }
/* Opt-in birthday marker shared by the wish calendar and roster editor. */
.birthday-badge {
  display: flex;
  align-items: center;
  gap: 4px;
  width: fit-content;
  max-width: 100%;
  margin-top: 3px;
  padding: 2px 6px;
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--warning) 14%, var(--surface));
  color: var(--text);
  font-size: var(--text-2xs);
  line-height: 1.25;
}
.birthday-badge span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.birthday-badge svg { flex: 0 0 auto; color: var(--warning); }

/* Standardized month/period navigation bar (components/calendar-nav.js) — the
   single prev/next + jump-to-period control every calendar/period-scoped view
   mounts, replacing what used to be a different hand-rolled <select> or
   button pair per view (some period-based, some raw month arithmetic — see
   that file's header comment). The jump control is a real <select>
   (.calendar-nav-select) styled to read as a clickable label rather than a
   boxed dropdown, so views with a plain month label and views with a full
   period dropdown end up visually consistent. */
.calendar-nav {
  max-width: 100%;
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
.calendar-nav-today { min-height: 44px; }
.calendar-nav-period { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--space-2); min-width: 0; max-width: 100%; }
.calendar-nav-period .calendar-nav-select { min-width: 0; flex: 1; }
.calendar-nav-btn {
  min-height: 44px;
  min-width: 44px;
  justify-content: center;
}
.calendar-nav-label {
  font-weight: 600;
  min-width: 10ch;
  text-align: center;
}
.calendar-nav-select {
  min-height: 44px;
  appearance: auto;
  border: none;
  background: transparent;
  color: var(--text);
  font-weight: 600;
  font-size: var(--text-base);
  text-align: center;
  text-align-last: center;
  cursor: pointer;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  max-width: 220px;
}
.calendar-nav-select:hover { background: var(--surface-sunk); }
.calendar-nav-select:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

#pc-calendar .cal-cell { min-height: 104px; }

/* inline-flex (not block): shrink-to-fit + a row layout for whatever
   decorateDayInformation() (features/day-information) appends inside it — the
   day-info badge then sits right next to the date number, on its own line
   above whatever the consumer view renders below (current-schedule.js's
   assigned-staff avatars),
   instead of falling to a separate line of its own. Views that never call
   decorateDayInformation (wish-submission.js, my-schedule.js, ...) render
   identically to before — inline-flex with a single text-node child looks
   the same as block. */
.cal-day {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--muted);
  margin-bottom: var(--space-1);
}

.cal-cell.today .cal-day { color: var(--primary); }

/* ── Agenda list (components/agenda-list.js) ─────────────────────────────────
   The mobile-first flat-list counterpart to .calendar's month grid — only
   ever instantiated inside a mobile branch (utils.js's isMobileViewport()),
   so this whole block never applies on desktop simply because the component
   is never rendered there; no ≤600px media guard needed for that reason
   alone. Rows reuse .cal-day for the date element (not a new class) so
   features/day-information's decorateDayInformation() keeps working unchanged, and
   reuse .cal-past/.sch-shift-badge for the same reason — one visual
   language, whether the day comes from a grid cell or an agenda row. */
.agenda-list {
  display: flex;
  flex-direction: column;
}

.agenda-row {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-1);
  border-bottom: 1px solid var(--border);
  min-height: 44px;
  position: relative;
}
.agenda-row:last-child { border-bottom: none; }

/* Only rows wired up with onDayClick get role="button" — a display-only
   list (my-schedule.js) stays non-interactive-looking, same
   precedent .cal-cell[role="button"] already sets. */
.agenda-row[role="button"] { cursor: pointer; }
.agenda-row[role="button"]:hover { background: var(--hover-tint); }
.agenda-row[role="button"]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
  background: var(--hover-tint);
}
.agenda-row.locked { cursor: not-allowed; }

.agenda-row.today { border-left: 3px solid var(--primary); padding-left: calc(var(--space-1) - 2px); }
.agenda-row.today .agenda-daynum { color: var(--primary); }

/* Same translucent-overlay technique as .cal-cell.weekend::after (and its
   dark-theme override below) rather than a `background` property, so it
   composites on top of .today's border/padding instead of fighting it for
   which rule "wins" the background. */
.agenda-row.weekend::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, .035);
  pointer-events: none;
}

.agenda-row.cal-past { opacity: 0.6; }
/* A folded run of empty days (AgendaList collapseEmptyRuns) and the label
   written into a single empty day. */
.agenda-row.agenda-run { min-height: 36px; padding-block: var(--space-2); }
.agenda-run-label { padding-left: var(--space-1); }
.agenda-empty { align-self: center; }

.agenda-row .cal-day {
  flex: 0 0 60px;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
  margin-bottom: 0;
}
.agenda-weekday { font-size: var(--text-2xs); text-transform: uppercase; letter-spacing: 0.03em; }
.agenda-daynum { font-size: var(--text-lg); font-weight: 700; color: var(--text); }

.agenda-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* An agenda row is full-width, unlike a cramped grid cell — badges show
   their full label instead of collapsing to a dot (the ≤600px .cal-cell
   .sch-shift-badge compaction rule above is deliberately scoped to .cal-cell
   only, so it never reaches badges rendered inside .agenda-content). */
.agenda-content .sch-shift-badge { margin-bottom: 0; }
.agenda-content:empty::before {
  content: '—';
  color: var(--muted);
  font-size: var(--text-sm);
}

/* Published Roster's mobile agenda rows (published-roster.js) — one group
   per location that has any slot that day, chips wrapped underneath. Unlike
   the desktop grid (one column per location), a phone-width row has no
   room for a real column layout, so this flattens to stacked groups instead
   — the day-detail modal a row-tap opens is still the single source of full
   detail; this is a scannable "who, roughly, is on and where" glance. */
.pr-agenda-location {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
.pr-agenda-location + .pr-agenda-location { margin-top: var(--space-2); }
.pr-agenda-location-name {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--muted);
  min-height: 44px;
  margin: 0;
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}
.pr-agenda-location-name > span:nth-child(2) { color: var(--text); }
.pr-agenda-location-count { margin-left: auto; font-weight: 400; }
.pr-agenda-open-sick { color: var(--warning); }
.pr-agenda-location-name::after {
  content: '›';
  color: var(--muted);
  font-size: var(--text-lg);
  line-height: 1;
  transition: transform var(--transition);
}
.pr-agenda-location[open] > .pr-agenda-location-name::after { transform: rotate(90deg); }
.pr-agenda-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 0 var(--space-3) var(--space-3);
}
.pr-agenda-chips .shift-chip { margin-bottom: 0; }

/* Published Team Roster's lens (views/published-roster/lens.js). Phone:
   day-first by default, with a deliberate month mode and search-driven
   result mode instead of one 30-day document. Desktop: Week or Month over
   the grid, with a week stepper. */
.pr-lens { margin-bottom: var(--space-3); }
.pr-desktop-lens { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.pr-week-nav { display: inline-flex; gap: var(--space-1); }
.pr-week-nav[hidden] { display: none; }
.pr-week-nav > .btn { min-width: 32px; justify-content: center; padding-inline: var(--space-1); }
.pr-lens-label { font-variant-numeric: tabular-nums; }
.pr-lens.is-searching .segmented { opacity: .55; pointer-events: none; }
.pr-mine-line {
  margin: var(--space-2) 0 0;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--p-50);
  color: var(--p-900);
  font-weight: 600;
}
.pr-mobile-day-nav { display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
.pr-mobile-day-nav > .btn { width: 44px; min-width: 44px; padding: 0; }
.pr-mobile-date-strip { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-1); }
.pr-mobile-date {
  position: relative;
  min-width: 0;
  min-height: 52px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-2);
  cursor: pointer;
}
.pr-mobile-date span, .pr-mobile-date strong { display: block; }
.pr-mobile-date span { font-size: var(--text-xs); }
.pr-mobile-date strong { font-size: var(--text-base); color: var(--text); }
.pr-mobile-date.is-mine { box-shadow: inset 0 3px 0 var(--p-600); }
.pr-mobile-date.is-active.is-mine { box-shadow: inset 0 3px 0 var(--on-primary); }
.pr-mobile-date.has-assignments::after { content: ''; position: absolute; bottom: 4px; left: 50%; width: 4px; height: 4px; border-radius: 50%; background: var(--p-500); transform: translateX(-50%); }
.pr-mobile-date.is-active { border-color: var(--p-600); background: var(--p-600); color: var(--on-primary); }
.pr-mobile-date.is-active strong { color: var(--on-primary); }
.pr-mobile-date.is-active::after { background: var(--on-primary); }
.pr-mobile-result { display: block; margin-top: var(--space-3); font-size: var(--text-lg); text-transform: capitalize; }
.pr-mobile-empty { margin: var(--space-2) 0 0; }
.pr-mobile-lens.is-searching .pr-mobile-day-nav { display: none; }
#pr-grid-area .agenda-row[hidden] { display: none; }
#pr-grid-area .agenda-row { display: block; margin-bottom: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
#pr-grid-area .agenda-row > .cal-day { width: auto; flex: none; flex-direction: row; gap: var(--space-2); margin-bottom: var(--space-3); }
#pr-grid-area .agenda-row > .agenda-content { width: 100%; }
.pr-agenda-open-day { margin-left: auto; color: var(--p-600); font-size: var(--text-sm); font-weight: 600; }

/* Aggregate-only "N colleagues also want this off" hint — deliberately more
   muted than the .sch-shift-badge wish chips above it since it's density
   context, not the user's own commitment. */
.cal-wish-density {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.68rem;
  color: var(--muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* Wish submission dims past dates. Its date decorator also removes the
   button role and tab stop; schedule readers can still inspect past days. */
.cal-cell.cal-past {
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}


/* ── Compact month (Calendar `variant: 'compact'`) ─────────────────────────
   Several months on one screen (features/leave/availability-calendar.js):
   square cells holding only the day number. The owner decorates a cell by
   setting custom properties rather than competing with the hover/focus
   rules above: --cal-cell-bg, --cal-cell-fg, --cal-cell-border,
   --cal-cell-shadow and --cal-cell-weight. Hover shows a primary border, keyboard focus a
   text-colour ring and `.selected` a primary ring, so all three stay
   distinguishable on any decorated background. */
.calendar-compact { gap: 3px; }
.calendar-compact .cal-th {
  padding: 0 0 2px;
  font-size: var(--text-2xs);
}
.calendar-compact .cal-th abbr { text-decoration: none; }
.calendar-compact .cal-cell {
  min-height: 0;
  aspect-ratio: 1;
  padding: 0;
  display: grid;
  place-items: center;
  border-color: var(--cal-cell-border, var(--border));
  background: var(--cal-cell-bg, var(--surface));
  color: var(--cal-cell-fg, var(--text));
  box-shadow: var(--cal-cell-shadow, none);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
.calendar-compact .cal-day { margin: 0; color: inherit; font-size: inherit; font-weight: var(--cal-cell-weight, 500); }
.calendar-compact .cal-cell.today .cal-day {
  color: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 2px;
}
.calendar-compact .cal-cell[role="button"]:hover {
  border-color: var(--primary);
  background: var(--cal-cell-bg, var(--hover-tint));
}
.calendar-compact .cal-cell.selected {
  box-shadow: var(--cal-cell-shadow, none);
  outline: 2px solid var(--p-600);
  outline-offset: 1px;
  z-index: 1;
}
.calendar-compact .cal-cell[role="button"]:focus-visible {
  background: var(--cal-cell-bg, var(--hover-tint));
  outline: 2px solid var(--text);
  outline-offset: 1px;
  z-index: 2;
}
.calendar-compact .cal-cell.cal-past { background: transparent; border-color: transparent; }
.calendar-compact .cal-cell.other-month { opacity: 1; background: transparent; border-color: transparent; }


/* ── Dark theme ────────────────────────────────────────────────────────── */
/* ── Calendar cells ──────────────────────────────────────────────────────── */
/* .cal-cell[role="button"]'s hover/focus,
   .cal-cell.sc-holiday, .notif-item (incl. its :hover/.notif-read),
   every
   .rgv2-row-weekend/.rgv2-norule cell, .chip-unfilled/.chip-add/
   .chip-clickable/.shift-chip.drop-target/.shift-chip.chip-selected,
   .conflict-chip(.conflict-soft), .inline-warning, and every
   .violation-summary-* surface need no dark overrides here any more — all
   now read colour through --hover-tint/--warning-bg/--danger-bg/
   --success-bg/--surface-sunk/--p-100, which already flip under
   [data-theme="dark"] in base.css.

   One narrow exception stays literal, since it has no light-mode token
   counterpart to flip from: the weekend-shading overlay deliberately
   lightens instead of darkening against a dark ground (the opposite
   direction from light mode's rgba(15,23,42,.035) darken, so it can't
   share that value either). */
[data-theme="dark"] .cal-cell.weekend::after { background: rgba(255, 255, 255, .045); }
[data-theme="dark"] .agenda-row.weekend::after { background: rgba(255, 255, 255, .045); }


/* ════════════════════════════════════════════════════════════════════════════
   Shared Calendar mobile compaction — components/calendar.js's .cal-th/
   .cal-cell/.cal-day markup, and the .sch-shift-badge shape shared by
   components/schedule-calendar.js and wish-submission.js's own badge
   renderers. Originally Wish Submission-only (ID-scoped to its own
   #calendar-container so current-schedule/my-schedule were unaffected);
   generalized to plain class selectors so every view built on the shared
   Calendar component benefits without duplicating this block per view —
   currently My Schedule and Wish Submission (current-schedule.js
   and planning-calendar.js render their own denser cell content and already
   carry their own higher-specificity min-height overrides, e.g.
   `.curr-sched-card .cal-cell`/`#pc-calendar .cal-cell`, so they correctly
   keep their existing desktop-dense sizing here — min-height is a floor,
   not a cap, so shrinking the unscoped default never clips a view that
   needs more room; moot for planning-calendar.js specifically below this
   same ≤600px breakpoint anyway, since it swaps to AgendaList there and
   never renders a .cal-cell at all). Badge rules are scoped to `.cal-cell .sch-shift-badge`
   (not bare `.sch-shift-badge`) specifically to out-specificity the
   `.sch-shift-badge.loc-N`/`.tone-*` colour modifiers (also (0,2,0), later
   in file — a tie `.sch-shift-badge` alone would lose outright).
   ════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 600px) {
  /* Seven columns at phone width leaves almost no room per cell — shrink
     padding/min-height and collapse each wish/shift badge into a small
     coloured dot (font-size:0 hides the label text visually only; screen
     readers still read it) instead of truncating a label mid-word. Tapping
     the day still opens the full-detail modal/panel where the view has one. */
  .cal-th {
    padding: 3px 1px;
    font-size: 0.68rem;
  }

  .cal-cell {
    min-height: 44px;
    padding: 3px;
  }

  .cal-day {
    font-size: 0.75rem;
    margin-bottom: 2px;
  }

  /* Badges render through .sch-shift-badge (schedule-calendar.js's
     shiftBadgeHTML, wish-submission.js's shiftWishBadgeHTML/offWishBadgeHTML)
     — collapse to the text/secondary lines hidden (font-size:0/display:none,
     screen readers still get the full label via the badge's title attribute)
     and the chip's own box chrome stripped, leaving only its .chip-dot
     (location hue + on-call ring, CLAUDE.md §13.1) visible as a small
     coloured dot. This is the mobile (max-width:600px) breakpoint, not
     print, despite the visual similarity to the print-mode rules elsewhere
     in this file — don't confuse the two. */
  .cal-cell .sch-shift-badge {
    display: inline-flex;
    padding: 0;
    margin: 0 3px 3px 0;
    background: none;
    border-left: none;
  }
  .cal-cell .sch-shift-badge .sch-shift-name {
    font-size: 0;
    gap: 0;
  }
  .cal-cell .sch-shift-badge .sch-loc-name,
  .cal-cell .sch-shift-badge .sch-shift-times {
    display: none;
  }
  .cal-cell .sch-shift-badge .chip-dot {
    width: 8px;
    height: 8px;
  }

  /* One compact month per row: touch-sized days. */
  .calendar-compact .cal-cell {
    min-height: 44px;
    padding: 0;
    font-size: var(--text-sm);
  }
  .calendar-compact .cal-th { font-size: var(--text-xs); }
}

} /* @layer components */
