/* @cascade-order: 3 — do not reorder. Split 4/10 of the former
   monolithic style.css (see docs/ui-pattern-audit.md and ADR-0095-style-css-split-into-ten-contiguous-files).
   Originally a contiguous slice of style.css in @cascade-order. That
   ordering is still enforced (tests/unit/css-split-structure.test.js);
   byte-for-byte reconstruction is NOT enforced and must not be relied on
   — the split files have been edited independently since. Earlier headers
   cited tests/unit/css-split-equality.test.js, which does not exist.
   Contains: The modal system (createModal's CSS home), filter pills, shared utility classes, the class check-in page, entry-ban styling, and the start of the responsive/mobile overrides.
   Original line range: 6290-8481. */


/* SmartWaiver Log Modal */
.sw-log-year-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  width: 100%;
  padding: var(--space-2) 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text);
  text-align: left;
}

.sw-log-year-toggle:hover {
  color: var(--color-accent);
}

.sw-log-year + .sw-log-year {
  border-top: 1px solid var(--color-border);
}

.sw-log-months {
  padding: 0 0 var(--space-2) var(--space-4);
}

.sw-log-month {
  font-size: var(--text-sm);
  padding: 2px 0;
}

.sw-log-month-name {
  font-weight: var(--font-medium);
  display: inline-block;
  width: 32px;
}

/* Recommendation box (checkbox with description) */

/* Mobile responsiveness */
@media (max-width: 600px) {
  /* A modal's WIDTH and HEIGHT on a phone are not set here: it is a bottom sheet, full width and the height of its
     content, drawn by v3.css (MODAL AS A SHEET, ADR-0248), so below 600px the five-size vocabulary collapses to it. */

  .modal-split-body {
    flex-direction: column;
  }
}

/* Camp header's meta line lays out in a row, unlike the class header which
   stacks time over age group (the date-nav at the top already states the
   date, so camp's meta is just the note now — e.g. "4-day week: No July 1st"). */
.camp-checkin-header .class-header-meta {
  flex-direction: row;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-1);
}

/* The coach STAT BOX is gone — the V3 header's coach is a chip
   (`.class-header-coach`), and the loading placeholder that was still emitting
   `.stat-item.stat-item-coach` is now that chip too. `.stat-item-coach-split`
   survives, but as its own component in v3.css: the markup has never carried
   `.stat-item` alongside it, so the `.stat-item.stat-item-coach-split` rules
   here matched nothing either, and their `.stat-value` / `.stat-label` children
   do not exist in the split chip's markup. */

.stat-item.stat-item-coach-split .stat-value:hover,
.stat-item.stat-item-coach-split .stat-label:hover {
  background: var(--color-bg-muted);
}

/* (Old .class-header-actions and .stat-action removed ; replaced by overflow menu) */

/* Mobile styles for the class-checkin header family. The Settings, Camps, Online
   Orders and Quick Reference headers are single lines at every width (their height is
   fixed), so they do not stack. Only the direction is declared here: the stretch, padding and
   gap this rule used to carry lost to v3.css's `.class-checkin-header` (same
   specificity, later), so they never applied; and `:not()` raises this
   selector's specificity, which would have made them start applying. */
@media (max-width: 600px) {
  .class-checkin-header:not(.settings-header):not(.camps-header):not(.orders-header):not(.qr-head) {
    flex-direction: column;
  }

  .class-header-name {
    font-size: var(--text-lg);
  }

  /* Combine time + age on one row: "12:00 PM • Kids" */
  .class-header-meta {
    flex-direction: row;
    align-items: center;
    gap: 0;
  }

  .class-header-time::after {
    content: '•';
    margin: 0 var(--space-1);
  }

  /* Move overflow menu to top-right corner on mobile */
  .class-header-overflow.perm-visible {
    position: absolute;
    top: var(--space-2);
    right: var(--space-2);
    margin-left: 0;
    z-index: 2;
  }

  .stat-item {
    min-width: 50px;
    padding: var(--space-1) var(--space-2);
  }

  .stat-item .stat-value {
    font-size: var(--text-base);
  }

  .stat-item .stat-label {
    font-size: 0.7143rem;
  }
}

/* Two-column body */
/* Checked-in is the WIDE primary pane. Open Gym and classes derive their
   "roster" from history — it is a suggestion, never an expected list — so it
   is a toggleable companion rather than a permanent equal column. With the
   roster off, the record gets the full width. */
/* The crosswalk merged TWO distinct design components onto this one name:
   base.css:1361 `.cc-body` (single list, flush, padding:0) and base.css:1646
   `.class-checkin-body` (dual list, `display:grid 1fr 1fr`, 16px gutter,
   14px padding). The generated v3.css resolved the clash by keeping only the
   first — v3.css:1435 is a comment where the grid rule used to be — so the
   camp and event bodies, which are genuinely dual, lost their grid and stacked
   their two panes full-width with no gutter. Restored on the two ids that ARE
   dual, rather than re-broadening the name the class page shares.
   Report the collision to the designer: it wants scoping in base.css. */
/* align-items + align-content: start, not the grid defaults (effectively
   `stretch` for both) — two DIFFERENT stretches were both inflating the
   short left column when the right column (or, stacked at narrow widths,
   the card's own leftover height) was taller. `align-items` stretched the
   column's own BOX to match its row; `align-content` separately stretched
   the ROW TRACK itself to fill any leftover height in the grid container
   (e.g. a short guest list in a card whose height floors to the tab's full
   available space — see ADR-0079). Fixing only one left the other's empty
   space behind, just moved from "inside a stretched box" to "inside an
   oversized row track" — same dead void either way. */
/* 14px on every side, per the design — this is the DUAL-CARD layout (camp,
   event): two bordered panels with their own side padding/gutter from the
   surrounding card edge, a different shape from the single-column pages
   (class check-in, search), which run flush/full-bleed. Reverted a same-
   session attempt to zero the horizontal padding for closer text alignment
   with the header above — that broke the gutter itself, flattening both
   panels against the outer card's edges instead of leaving them inset as
   distinct cards. The gutter is the more load-bearing fact; a header/search-
   bar text offset of a few px is not worth losing it. */
#camp-checkin-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: start;
  align-content: start;
  gap: var(--space-4);
  padding: 14px;
}

@media (max-width: 1080px) {
  #camp-checkin-body {
    grid-template-columns: 1fr;
  }
}

/* Card depth follows LIST COUNT. One list and the rows sit flush in the main
   card — a border around the only thing in the card says nothing. Two adjacent
   lists need a boundary, so each takes its own pane card. Camp is permanently
   dual (Registered/Present); the class page's roster panel was removed, so its
   body is single-list, always flush. */
#camp-checkin-body .class-checkin-panel {
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* THE CROSSWALK MERGED TWO DIFFERENT ROWS ONTO ONE NAME, same shape as the
   `.class-checkin-body` collision above: base.css declared `.attendance-row`
   TWICE, as the F3 camp row (flex) and as `.cc-row`, the F1 record row (grid),
   crosswalked onto the same production class name. Both compiled into v3.css
   under the bare selector at equal specificity, so the later grid rule won
   everywhere — including on real F3 rows, where `.row-main{flex:1}` does
   nothing and the action buttons stopped reading as right-aligned.

   The fix here USED to be a flex rule restored on the two container ids that
   happened to be F3. That left the collision in place and covered only the two
   lists anyone had noticed — the group-visit participant list emits the same
   anatomy into `.gv-participant-section` and was still being laid out as a
   four-column grid holding two children. Resolved properly by ADR-0130: the
   layout is `.attendance-row.is-roster`, a class the ONE builder
   (`core/attendance-row.js`) writes, declared in v3.css beside the record grid.
   Nothing scoped is needed here any more. */

/* The [View][...] band is the members table's, reused verbatim — View opens a
   record, it does not change one, so it never wears the boxed icon-button
   language that check-in/check-out own (ADR-0093). The one thing that IS
   camp-local is height: on the members table the band stands alone at .btn-sm's
   22px, but here it sits beside a 28px icon button, and a 6px mismatch between
   two adjacent controls reads as sloppy. Match the sibling; nothing else about
   the band changes. */
#camp-roster-list .row-actions .btn-group .btn,
#camp-attendance-list .row-actions .btn-group .btn {
  height: 28px;
}

/* The dual card's last row keeps its own border-bottom right up against
   `.class-checkin-panel`'s rounded bottom corner — drop it, bordered
   dual-card case only (the flush single-list layout just below has no
   border-radius to protect). */
#camp-checkin-body .class-checkin-panel .attendance-list > :last-child {
  border-bottom: none;
}

/* Gap AFTER the dual card, not inside its list: `#camp-checkin-body` is
   simultaneously a grid container (this rule) and a flex item
   (`flex:1;min-height:0`, v3.css) of its scrolling ancestor (`#tab-camp-checkin`) —
   when its grid content (a 100+-row list) is taller than that flex-computed
   height, the grid's own `padding:14px` anchors to the phantom flex height
   instead of after the real, overflowing content, so it never renders.
   `.class-checkin-panel` is a plain grid ITEM sized by its own content, with
   none of that mismatch, so margin-bottom here is what actually shows up
   before the scroll area ends. Matches the grid's own 14px gutter.

   Scoped to `.attendance-column` (always the LAST column) rather than every panel: below the `1080px` breakpoint the two
   columns stack into one grid track, and margin-bottom on the search-column
   panel too would stack with the grid's own `gap` between them instead of
   sitting after the true last card. */
#camp-checkin-body .attendance-column .class-checkin-panel {
  margin-bottom: 14px;
}

/* Single list: the pane head is a band across the card and the rows run to the
   card's own edges — which is simply v3.css:1159's padding:0, so no rule here. */

/* Responsive - stack when space is limited */
@media (max-width: 1080px) {
  .class-checkin-body {
    grid-template-columns: 1fr;
  }

  .class-checkin-column.search-column {
    order: 1;
  }

  .class-checkin-column.attendance-column {
    order: 2;
  }
}

/* Search card - prominent styling */
.search-card {
  background: var(--color-bg-elevated);
}

.search-card .card-body {
  display: flex;
  flex-direction: column;
}

.checkin-search-row {
  display: flex;
  gap: var(--space-2);
}

/* `.search-wrapper` removed (ADR-0128). It was a relative box holding a
   `.form-input` and a solid accent-blue clear button absolutely positioned
   over the field's right end — the second of the two copies that fought the
   quiet `.search-clear` in v3.css, and the one the Camps overview wore. Its
   single host is `.attendance-search` now, where the clear button is a real
   flex child rather than a block floated on top of the text.
   `.checkin-search-row .search-wrapper` went with it: that container holds a
   copy-link row (a `<code>` and a Copy button), and has held no search field
   for as long as the selector has existed. */

/* Go to Today banner - shown above search when viewing historical dates */

/* Large search input */
.search-input-lg {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
}

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

/* Attendance list */

.suggestions-list {
  display: flex;
  flex-direction: column;
  max-height: 280px;
}

.suggestion-row {
  cursor: pointer;
}

/* Row addition animation - reverse of slideOutAndCollapse */
.attendance-row.adding {
  animation: slideInAndExpand 0.3s ease forwards;
  overflow: hidden;
}

/* 50%/100% used to set padding-top/bottom to `var(--space-2)` (8px) — a round
   token, not `.attendance-row`'s own real vertical padding (`padding: 5px
   14px`). The animation's `forwards` fill only holds through the last frame;
   `.adding` is removed at the SAME 300ms the animation ends
   (`list-reconciler.js`'s `setTimeout`), and the instant it is, the row
   drops the animation's 8px and reverts to the real 5px — a visible snap
   right after the row finishes sliding in. End frame now matches the row's
   own value exactly, the same "don't jump to a token" rule `slideOutAndCollapse`
   below already follows for its own end frame. */
@keyframes slideInAndExpand {
  0% {
    opacity: 0;
    transform: translateX(-20px);
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: 0;
    border-bottom-width: 0;
  }
  50% {
    opacity: 0;
    transform: translateX(-20px);
    max-height: 60px;
    padding-top: 5px;
    padding-bottom: 5px;
  }
  100% {
    opacity: 1;
    transform: translateX(0);
    max-height: 60px;
    padding-top: 5px;
    padding-bottom: 5px;
    margin-bottom: 0;
  }
}

/* Row removal animation with height collapse. */
.attendance-row.removing {
  overflow: hidden;
  animation: slideOutAndCollapse 0.3s ease forwards;
}

/* The row collapses from its OWN height, over the whole 300ms.
   `--row-h` is measured and set by `markRowLeaving` (list-reconciler.js) just
   before the class lands; a literal here would snap the row to that literal on
   frame 0 (rows are 40px on some surfaces, taller on others). `min-height` and
   `padding` appear only in the end frame on purpose: an omitted start value
   interpolates from the element's own computed value, so the collapse starts
   where the row actually is instead of jumping to a token first. Both floors
   have to go — v3.css floors `.attendance-row` at 40px, and a min-height
   always beats a max-height, so on its own `max-height: 0` collapses nothing
   and the list snaps up only once the node is gone. */
@keyframes slideOutAndCollapse {
  0% {
    opacity: 1;
    transform: translateX(0);
    max-height: var(--row-h, 60px);
    margin-bottom: 0;
  }
  50% {
    opacity: 0;
    transform: translateX(-20px);
  }
  100% {
    opacity: 0;
    transform: translateX(-20px);
    max-height: 0;
    min-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    /* A row in a list with a gap (separate cards) also takes the gap away, so
       nothing below it steps 12px on the first frame of an entry or the last of
       a removal. `--row-gap` is the list's row-gap, measured beside `--row-h`;
       a list with no gap leaves it unset (0). Both borders go for the same reason. */
    margin-bottom: calc(-1 * var(--row-gap, 0px));
    border-top-width: 0;
    border-bottom-width: 0;
  }
}

/* New member row slide-in animation (Recent Accounts) */
/* Desktop: animate cell padding + opacity for smooth row expansion */
.member-row.member-row-new td {
  animation: memberCellExpand 0.35s ease forwards;
  overflow: hidden;
}

/* padding + opacity only — NEVER font-size/line-height. Those used to animate
   from 0 to a hardcoded var(--text-sm), then hold it via fill-mode:forwards
   for the row's first 450ms; the row's real cells don't all rest at
   --text-sm (.member-name sits at --text-md, plain .member-cell text
   inherits --text-base from .data-table — ADR-0081 draws this line on
   purpose: "px box metrics stay put" while only rem text scales), so any
   content that doesn't pin its own font-size rendered at the animation's
   assumed size and then visibly snapped to its true size the moment
   .member-row-fadein was stripped. Padding alone already accounts for the
   full height delta (measured live: a 45px settled row is exactly 29px
   content + 2 * 8px padding), so dropping font-size/line-height loses
   nothing. */
@keyframes memberCellExpand {
  from {
    padding-top: 0;
    padding-bottom: 0;
    opacity: 0;
    border-bottom-color: transparent;
  }
  60% {
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
    opacity: 0;
  }
  100% {
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
    opacity: 1;
  }
}

/* Enrich pass (pass 2) growth: a newly-revealed group/family member row.
   `insertRowsBefore` (app.js) measures the row's own real, final height
   right after inserting it (full content already in place), collapses it
   to 0, then sets this transition toward that EXACT measured number —
   never a guessed/hardcoded one. A fixed keyframe animating padding
   toward a hardcoded var(--space-2) used to do this and measured clean in
   isolation, but produced a real, repeatedly-confirmed-live overshoot for
   family rows (the row landed taller than its settled height and visibly
   shrank moments later — the exact mismatch was never fully pinned down
   across many live traces: a mid-flight content swap, a second render
   landing while the first was still animating). Animating to a number
   read directly off the fully-rendered row removes the possibility
   outright: the target height IS the final height, always. */
.member-row-measuring {
  overflow: hidden;
  transition: height 0.35s ease, opacity 0.35s ease;
}

/* Same-height swap (skeleton → full row, or a header replacing itself):
   fade the real content in rather than growing the row (which would
   collapse-then-expand and flicker). */
.member-row-fadein td {
  animation: memberRowFadeIn 0.3s ease forwards;
}

@keyframes memberRowFadeIn {
  from { opacity: 0.45; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .member-row-measuring { transition: none; }
  .member-row-fadein td { animation: none; }
}

/* Skeleton placeholder for an inactive search match whose full doc is still
   loading (pass 1 of the non-blocking search render). A neutral, gradient-free
   pulsing bar that reserves the cell's space so the row height matches the
   final row — when the enrich pass swaps in the real row there's no layout
   jump. Name/age/buttons in the skeleton are real and stay put. */
.member-cell-loading {
  display: inline-block;
  width: 44px;
  height: 14px;
  border-radius: 4px;
  background: var(--color-bg-muted);
  vertical-align: middle;
  animation: memberSkeletonPulse 1.1s ease-in-out infinite;
}

@keyframes memberSkeletonPulse {
  0%, 100% { opacity: 0.55; }
  50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: reduce) {
  .member-cell-loading { animation: none; }
}

/* Mobile: rows are grid items in flex container, use max-height */
@media (max-width: 600px) {
  .member-row.member-row-new {
    overflow: hidden;
    animation: memberRowExpand 0.35s ease forwards;
  }

  .member-row.member-row-new td {
    animation: none;
  }

  @keyframes memberRowExpand {
    from {
      max-height: 0;
      padding: 0;
      margin: 0;
      border-width: 0;
      opacity: 0;
    }
    60% {
      max-height: 200px;
      opacity: 0;
    }
    100% {
      max-height: 200px;
      opacity: 1;
    }
  }
}

/* ============================================================================
   ENTRY BAN (v6) — banned account is unmistakable in search + on the profile.
   Whole-element treatment only: solid danger tint on the full row + a solid
   BANNED pill. No border-left strip, no gradient (design rules). The pill
   pulses to catch the eye; the pulse is disabled under prefers-reduced-motion
   but the solid tint + pill remain (already obvious without motion).
   ============================================================================ */
.member-row--banned,
.member-row--banned > .member-cell,
.member-row--banned > td {
  background: color-mix(in srgb, var(--color-danger) 10%, transparent);
}
.member-row--banned:hover,
.member-row--banned:hover > .member-cell,
.member-row--banned:hover > td {
  background: color-mix(in srgb, var(--color-danger) 16%, transparent);
}

.ban-pill {
  margin-left: var(--space-2);
  vertical-align: middle;
  white-space: nowrap;
  animation: banPulse 1.6s ease-in-out infinite;
}
@keyframes banPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-danger) 55%, transparent); }
  50%      { box-shadow: 0 0 0 5px transparent; }
}

/* Greyed, non-interactive check-in icons on a banned row. The row builders
   already strip the data-action; pointer-events:none is belt-and-suspenders
   so the icons can never start a check-in even if markup drifts. */
.member-icon-single.is-banned,
.member-schedule-btn.is-banned {
  opacity: 0.4;
  cursor: not-allowed;
  pointer-events: none;
}

/* Entry-ban banner — its OWN full-width row between the header bar and the
   subnav (NOT a flex child of .account-header-bar; that left it no room and
   broke the header). Single compact line, whole border + solid danger tint
   (design rules: no one-side accent, no gradient). */
.account-ban-banner {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
  padding: var(--space-2) var(--space-3);
  background: color-mix(in srgb, var(--color-danger) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-danger) 45%, transparent);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  line-height: 1.4;
}
.account-ban-banner-icon {
  color: var(--color-danger);
  flex-shrink: 0;
}
.account-ban-banner-text { color: var(--color-text); }
.account-ban-banner-text strong { color: var(--color-danger); }
.account-ban-banner-reason::before,
.account-ban-banner-meta::before { content: " · "; color: var(--color-text-muted); }
.account-ban-banner-meta { color: var(--color-text-muted); }

@media (prefers-reduced-motion: reduce) {
  .ban-pill { animation: none; }
}

.suggestion-name {
  flex: 1;
  font-weight: var(--font-medium);
  color: var(--color-text);
  font-size: var(--text-base);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Clickable name - for copy-to-clipboard functionality */

/* Attendance family grouping */
.attendance-family {
  margin-bottom: var(--space-1);
}

.attendance-family-header {
  margin-bottom: 0;
}

.attendance-family-header > i {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  background: var(--color-accent);
  border-radius: var(--radius-sm);
  color: white;
  font-size: var(--text-sm);
  flex-shrink: 0;
}

.attendance-family-name {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
  color: var(--color-text);
}

.attendance-family-count {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  background: var(--color-bg-muted);
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  font-weight: var(--font-medium);
}

/* The six `.attendance-family-header .family-action-btn.member-icon-single`
   rules that sat here were dead: `.attendance-family-header` appears in no
   JS at all, and the only `.family-action-btn` in the app is the camp
   family header's "Check in all", which is the check-in button now. */

/* Balance check modal (check-in with outstanding balance) */
.balance-check-person {
  margin-bottom: var(--space-3);
}

.balance-check-person:last-child {
  margin-bottom: 0;
}

.balance-check-name {
  font-weight: var(--font-semibold);
  font-size: var(--text-sm);
  margin-bottom: var(--space-2);
}

.balance-check-total {
  color: var(--color-danger);
  font-weight: var(--font-medium);
}

.balance-check-entries {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.balance-check-entry {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  background: var(--color-bg-subtle);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  cursor: pointer;
}

.balance-check-entry input[type="checkbox"] {
  flex-shrink: 0;
}

.balance-check-desc {
  flex: 1;
  color: var(--color-text-secondary);
}

.balance-check-amount {
  font-weight: var(--font-semibold);
  color: var(--color-danger);
  white-space: nowrap;
}

.balance-check-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-top: var(--space-3);
  margin-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-weight: var(--font-semibold);
}

.balance-check-grand-total {
  font-size: var(--text-lg);
  color: var(--color-danger);
}

/* The Camps overview's INSTANCE of the shared field (ADR-0128). It is the
   only one that sizes itself rather than filling its slot: it sits in the
   stats bar's right slot beside the age filters, and a full-width field there
   would push them off the row. It still grows on focus, which is the
   affordance the `.form-input` version had and the reason it can afford to
   rest narrow. */
#overview-search {
  margin-left: auto;
  flex: none;
  width: 180px;
  transition: width 0.2s ease, border-color 0.15s, box-shadow 0.15s;
}

#overview-search:focus-within {
  width: 240px;
}

#overview-search input {
  font-size: var(--text-md);
  padding: 6px 9px;
}

.attendance-family-member {
  background: color-mix(in srgb, var(--color-accent) 4%, var(--color-bg));
  padding-left: var(--space-3);
  border-bottom-color: color-mix(in srgb, var(--color-accent) 15%, transparent);
}

.attendance-family-member .attendance-name::before {
  content: '↳';
  color: var(--color-accent);
  opacity: 0.6;
  font-size: var(--text-lg);
  margin-right: var(--space-1);
}

.suggestion-meta {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  flex-shrink: 0;
  width: 55px;
  text-align: center;
}

.attendance-badge {
  font-size: var(--text-xs);
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}

.attendance-badge.first-timer {
  background: color-mix(in srgb, var(--color-warning) 20%, transparent);
  color: var(--color-warning);
}

.attendance-undo {
  padding: 2px 8px;
  font-size: var(--text-xs);
  background: transparent;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  cursor: pointer;
  flex-shrink: 0;
}

.attendance-undo:hover {
  background: color-mix(in srgb, var(--color-danger) 15%, transparent);
  border-color: var(--color-danger);
  color: var(--color-danger);
}

/* `.attendance-info` and `.attendance-time` are DELETED (ADR-0130). They were
   the event check-in row's private spelling of the name cell and the time
   column — a wrapper div and a 55px centred cell where every other record row
   has a right-aligned `.class-header-time`. The one builder emits the same four
   children for every record row now, so the event list stopped being a
   three-child row in a four-column grid. */
.attendance-badges {
  pointer-events: none;
}

/* Archived user search results in universal search */
/* The divider <td> contributes nothing; the header div inside it carries the
   band edge to edge, in the same register as the thead. The old `!important`
   padding beat v3's `padding:0` outright and border-top leaked past it, so the
   band sat inset with a spurious rule above it. */
/* base.css: `.member-table tr.is-archived td { opacity: .6 }` — the whole
   row dims, no background tint, no hover variant. Production's background-
   subtle tint was a different visual language for the same idea; matched
   the design exactly, nothing invented on top. */
.archived-result-row td {
  opacity: .6;
}

/* Admin: Checked-in today badge in search results */
.search-checked-in {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: var(--text-xs);
  color: var(--color-success);
  font-weight: var(--font-medium);
  margin-left: var(--space-2);
}

/* Attendance count badge */
.attendance-count {
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-success);
  color: #fff;
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: var(--font-semibold);
}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 600px) {
  .sidebar {
    display: none;
  }

  .schedule-sidebar {
    display: none;
  }

  .suggestions-grid {
    grid-template-columns: 1fr;
  }

  .filter-row {
    grid-template-columns: 1fr;
  }

  

  

  

  

  

  

  

  

  

  

  

  

  
  
  

  
  
  

  

  

  

  

  

  

  

  
  

  
  
  
  
  
  

  

  

  

  

  

  

  

  

  
  

  

  

  

  

  

  /* ==========================================================================
     ACCOUNT EDIT/ADD ; MOBILE LAYOUT
     Compact Cards + Bottom Tab Bar navigation
     ========================================================================== */

  #edit-account-contact {
    display: none;
  }


  /* --- Compact cards: tighter padding, smaller headers --- */
  .account-subtab .card {
    margin-bottom: var(--space-2);
  }

  .account-subtab .card-header {
    padding: var(--space-2) var(--space-3);
    height: auto;
    min-height: 40px;
  }

  .account-subtab .card-body {
    padding: var(--space-3);
  }

  /* --- Account type toggle: full-width --- */
  #account-type-toggle {
    width: 100%;
  }

  #account-type-toggle .btn-toggle {
    flex: 1;
  }

  .log-row {
    flex-wrap: wrap;
  }

  .log-date {
    display: none;
  }

  /* Narrow desktop: the band's own inset is already tight, so the field
     only needs its chip inset back. */
  .main-search-bar {
    padding-right: var(--space-2);
  }

}

/* ============================================================================
   SHARE A PASS (feature/pass-share-ui.js, ADR-0212)
   Three `.titled-section`s read left to right: Owner (its bundles, `.choice`), Users (a joined `.choice` tick list
   of the people in today without a pass, newest first), Overview (a `.record-card` of `.kv-row`s saying what sharing
   will do). The grid's gap spaces them, side by side or stacked on a phone.
   ========================================================================== */
.share { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr) minmax(0, 1.15fr); gap: var(--space-5); align-items: start; }
.share > .titled-section { min-width: 0; }
/* The grid spaces the sections: the stacking margin `.titled-section + .titled-section` adds (v3.css) would drop the
   second and third columns below the first. */
.share > .titled-section:not(.hidden) + .titled-section:not(.hidden) { margin-top: 0; }
@media (max-width: 760px) { .share { grid-template-columns: minmax(0, 1fr); } }
/* The result slots are always in the markup; an empty one takes no gap. */
.share > .titled-section > :empty { display: none; }
/* The three titles sit on one line whether or not a section has an action in its title. */
.share .section-legend { min-height: 28px; }
/* A suggested owner's passes, under its name. */
.share-badges { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: 2px; }
/* Who it is for: one person to a line, where they are under the name. */
.share-for { display: flex; flex-direction: column; gap: var(--space-2); }
.share-for > span { display: flex; flex-direction: column; }
.share-for > span > .text-muted { font-size: var(--text-sm); }
.share-note { display: flex; align-items: flex-start; gap: var(--space-2); font-size: var(--text-sm); color: var(--color-text-secondary); }
.share-note i { color: var(--color-text-muted); margin-top: 2px; }
/* The overview's keys are short (Pass, Owner, For): the card's own 110px key column would leave the badge no room. */
#share-preview .kv-row { grid-template-columns: 64px minmax(0, 1fr) auto; }
/* "Shared now" is the account card's own `.pass-uses` list (10-forms-account-settings.css); the section's gap is its
   spacing here, so its own top margin goes. */
.share > .titled-section > .pass-uses { margin-top: 0; }
