/* Generated by scripts/build-css.ts from public/styles/components/*.css. Don't edit. */
@layer base, components;

@layer components {

/* ---- admin.css ---- */
/* Admin Dashboard (DESIGN-SYSTEM.md › Admin section nav, Total card, Leading bids note; SPEC §8).
   One page per section; the section nav is plain links, the current one marked with
   aria-current (DECISIONS entry 59). */
.admin { display: grid; gap: var(--space-8); }

/* Section nav: links that wrap onto more lines when narrow; never tabs. */
.admin-nav__list { display: flex; flex-wrap: wrap; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.admin-nav__link {
  display: inline-flex; align-items: center; min-height: var(--size-target); padding: 0 var(--space-4);
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-pill);
  font: var(--type-label); color: var(--color-text); text-decoration: none; background: var(--color-surface);
}
.admin-nav__link:hover { background: var(--color-accent-soft); }
.admin-nav__link:focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.admin-nav__link[aria-current="page"] { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-on-accent); }

.admin-section { display: grid; gap: var(--space-5); }
.admin-section__title { margin: 0; font: var(--type-heading-2); color: var(--color-text); }
.admin-section__body { display: grid; gap: var(--space-5); }
.admin-glance { margin: 0; font: var(--type-body); color: var(--color-text-muted); }

/* Total cards: one per sale type and the grand total, each pledged then confirmed. */
.total-cards { display: grid; gap: var(--space-4); margin: 0; padding: 0; list-style: none; }
.total-card { display: grid; gap: var(--space-3); padding: var(--space-5); }
.total-card--grand { border-color: var(--color-border-strong); }
.total-card__title { margin: 0; font: var(--type-heading-3); color: var(--color-text); }
.total-card__figures { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin: 0; }
.total-card__figure { display: grid; gap: var(--space-1); }
.total-card__figure dt { font: var(--type-label); color: var(--color-text-muted); }
.total-card__figure dd { margin: 0; }
.total-card__amount { font: var(--type-amount); color: var(--color-text); font-variant-numeric: var(--figures-amount); }
.total-card__amount--confirmed { color: var(--color-success); }
/* $0 reads as "nothing yet", not as good news (owner, 6B review). */
.total-card__amount--zero { color: var(--color-text-muted); }
@media (min-width: 40rem) {
  .total-cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.admin-gap { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; font: var(--type-body); color: var(--color-text); }
.admin-gap svg { flex: none; margin-top: var(--space-1); color: var(--color-text-muted); }
.admin-key { display: grid; gap: var(--space-2); margin: 0; font: var(--type-caption); color: var(--color-text-muted); }
.admin-key div { display: flex; flex-wrap: wrap; gap: var(--space-1) var(--space-2); }
.admin-key dt { font-weight: var(--weight-semibold); color: var(--color-text); }
.admin-key dt::after { content: ":"; }
.admin-key dd { margin: 0; }

/* Leading bids: live only, and kept apart from pledged and confirmed (a dashed box, no amount color). */
.leading-note {
  display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-4) var(--space-5);
  border: var(--border-thin) dashed var(--color-border-strong); border-radius: var(--radius-lg); background: var(--color-surface-muted);
}
.leading-note > svg { flex: none; margin-top: var(--space-1); color: var(--color-text-muted); }
.leading-note__body { display: grid; gap: var(--space-1); }
.leading-note__title { margin: 0; font: var(--type-label); color: var(--color-text); }
.leading-note__amount { margin: 0; font: var(--type-heading-2); color: var(--color-text); font-variant-numeric: var(--figures-amount); }
.leading-note__text { margin: 0; font: var(--type-caption); color: var(--color-text-muted); }

/* People table cells. */
.admin-person { display: inline-flex; align-items: center; gap: var(--space-2); }
.admin-person .pill { margin-left: var(--space-1); }
.admin-proof { display: inline-flex; align-items: center; gap: var(--space-2); }
.admin-proof__detail { font-variant-numeric: var(--figures-amount); color: var(--color-text); }

/* Proof review (DESIGN-SYSTEM.md › Proof review card): one card per person's latest proof,
   waiting first. The thumbnail is always shown; View larger opens the full-width picture in
   place (a disclosure, not a dialog: there's no decision in looking). */
.proof-queue { display: grid; gap: var(--space-4); }
.proof-queue__title { margin: 0; font: var(--type-heading-3); color: var(--color-text); }
.proof-queue__title:focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.proof-queue__list { display: grid; gap: var(--space-4); margin: 0; padding: 0; list-style: none; }
.proof-review { display: grid; gap: var(--space-3); padding: var(--space-5); }
.proof-review--pending { border-color: var(--color-border-strong); }
.proof-review__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.proof-review__name { margin: 0; font: var(--type-heading-3); color: var(--color-text); }
.proof-review__uploaded { margin: 0; font: var(--type-caption); color: var(--color-text-muted); }
.proof-review__main { display: grid; gap: var(--space-4); }
.proof-review__thumb {
  display: block; width: calc(var(--size-target) * 3); aspect-ratio: 3 / 4; object-fit: cover; object-position: top;
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface-muted);
}
.proof-review__details { display: grid; gap: var(--space-3); align-content: start; justify-items: start; }
.proof-review__figures { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-8); margin: 0; }
.proof-review__figures dt { font: var(--type-label); color: var(--color-text-muted); }
.proof-review__figures dd { margin: 0; }
.proof-review__amount { font: var(--type-heading-2); color: var(--color-text); font-variant-numeric: var(--figures-amount); }
.proof-review__note { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; font: var(--type-body); }
.proof-review__note svg { flex: none; margin-top: var(--space-1); }
.proof-review__note--warning { padding: var(--space-2) var(--space-3); border-radius: var(--radius-md); background: var(--color-warning-soft); color: var(--color-text); font-weight: var(--weight-semibold); }
.proof-review__note--warning svg { color: var(--color-warning); }
.proof-review__note--info { color: var(--color-text); }
.proof-review__note--info svg { color: var(--color-text-muted); }
.proof-review__reviewed { margin: 0; font: var(--type-body); color: var(--color-text-muted); }
.proof-review__reason { margin: 0; font: var(--type-body); color: var(--color-text); }
.proof-review__large { margin: 0; }
.proof-review__large-img { display: block; max-width: 100%; height: auto; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); }
.proof-review__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); padding-top: var(--space-2); border-top: var(--border-thin) solid var(--color-border); }
.admin-alert:empty { display: none; }
@media (min-width: 40rem) {
  .proof-review__main { grid-template-columns: auto 1fr; }
}

/* Items (DESIGN-SYSTEM.md › Admin item row): one card per item, grouped by sale type. The bid
   history opens in place (a disclosure); Remove and Cancel item each ask in a danger dialog. */
.admin-items { display: grid; gap: var(--space-4); }
.admin-items__list { display: grid; gap: var(--space-3); margin: 0; padding: 0; list-style: none; }
.admin-item { display: grid; gap: var(--space-3); padding: var(--space-4); }
.admin-item__main { display: grid; grid-template-columns: auto 1fr; gap: var(--space-4); align-items: start; }
.admin-item__thumb {
  display: block; width: calc(var(--size-target) * 1.5); aspect-ratio: 1; object-fit: cover;
  border-radius: var(--radius-md); background: var(--color-surface-muted);
}
.admin-item__text { display: grid; gap: var(--space-1); min-width: 0; }
.admin-item__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-3); }
.admin-item__title { margin: 0; font: var(--type-heading-3); }
.admin-item__title a { color: var(--color-text); text-decoration-thickness: var(--border-thin); text-underline-offset: var(--space-1); }
.admin-item__title:focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.admin-item__figures { margin: 0; font: var(--type-body); font-weight: var(--weight-semibold); color: var(--color-text); font-variant-numeric: var(--figures-amount); }
.admin-item__donor { margin: 0; font: var(--type-caption); color: var(--color-text-muted); }
.admin-item__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.admin-item--cancelled .admin-item__thumb { opacity: 0.5; } /* like a muted item tile */
.admin-item--cancelled .admin-item__figures { color: var(--color-text-muted); }
.admin-bid__who { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-1) var(--space-2); }
.admin-bid__note { flex-basis: 100%; font: var(--type-caption); color: var(--color-text-muted); }
.admin-bid__when { white-space: nowrap; color: var(--color-text-muted); }
.admin-bid__action { text-align: right; }
.admin-bid--removed .admin-person__name, .admin-bid--removed .admin-bid__amount { color: var(--color-text-muted); }
.admin-bid--removed .admin-bid__amount { text-decoration: line-through; }
/* Phones: the Time column hides and the time is a caption line under the name (like Bid history). */
@media (min-width: 40rem) {
  .admin-bid__when-inline { display: none; }
}
@media (width < 40rem) {
  .admin-bids .admin-bid__when { display: none; }
  .admin-bids th[scope="row"] { white-space: normal; }
  .admin-bids th, .admin-bids td { padding-inline: var(--space-2); }
  .admin-bids tr > :first-child { padding-inline-start: var(--space-3); }
  .admin-bids tr > :last-child { padding-inline-end: var(--space-3); }
  .admin-purchases .admin-purchase__price { display: none; } /* the price joins the caption under the name */
  .admin-bids .avatar { display: none; } /* the Top bid pill names the leader; the avatar would push Remove off screen */
}

/* Event settings (DESIGN-SYSTEM.md › Settings form): one form in a card, the regular field
   pattern. The three dates sit in a row that wraps; Save is the only primary button. */
.admin-settings { display: grid; gap: var(--space-5); max-width: calc(var(--space-16) * 12); padding: var(--space-5); }
.admin-settings__group { gap: var(--space-3); }
.admin-settings__group > legend { margin-bottom: var(--space-2); }
.admin-settings__group .field-row > .field { flex-basis: calc(var(--space-16) * 3.5); }
.admin-settings__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.admin-settings .input-group .input--short { flex: 0 1 auto; }

/* Email templates (DESIGN-SYSTEM.md › Template editor): a card per template; Edit opens the
   editor in place, with the live preview beside the fields on wide screens, under them on phones. */
.admin-template { display: grid; gap: var(--space-3); padding: var(--space-4); }
.admin-template__subject { margin: 0; font: var(--type-body); color: var(--color-text); overflow-wrap: anywhere; }
.admin-template__subject-label { font-weight: var(--weight-semibold); }
.admin-template__editor { display: grid; gap: var(--space-4); padding-top: var(--space-4); border-top: var(--border-thin) solid var(--color-border); }
@media (min-width: 64rem) {
  .admin-template__editor { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: start; }
  .admin-template__actions { grid-column: 1 / -1; }
}
.admin-template__fields { display: grid; gap: var(--space-4); min-width: 0; }
.admin-template__body { min-height: calc(var(--size-target) * 6); font-variant-ligatures: none; }
.admin-template__insert { display: grid; justify-items: start; gap: var(--space-2); }
.admin-template__variables {
  display: grid; gap: var(--space-1); margin: 0; padding: var(--space-2); list-style: none;
  border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface);
}
.admin-template__variables .admin-template__variable { justify-content: flex-start; width: 100%; text-align: left; }
.admin-template__variable code { font: inherit; font-weight: var(--weight-semibold); }
.admin-template__variable-text { font-weight: var(--weight-regular); color: var(--color-text-muted); }
.admin-template__preview {
  display: grid; gap: var(--space-2); min-width: 0; padding: var(--space-4);
  border-radius: var(--radius-md); background: var(--color-surface-muted);
}
.admin-template__preview-title { margin: 0; font: var(--type-label); color: var(--color-text-muted); }
.admin-template__preview-subject { margin: 0; font: var(--type-body); font-weight: var(--weight-semibold); color: var(--color-text); overflow-wrap: anywhere; }
.admin-template__preview-body { margin: 0; font: var(--type-body); color: var(--color-text); white-space: pre-wrap; overflow-wrap: anywhere; }
.admin-template__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* Event settings: what a changed time zone does to the close (owner, 6E review). */
.admin-settings__effect { color: var(--color-text); }

/* Reset for next year (SPEC §7.8; DECISIONS entry 118): the lock, what goes and what stays,
   then the danger button once it unlocks. */
.admin-reset { display: grid; gap: var(--space-4); max-width: calc(var(--space-16) * 12); padding: var(--space-5); }
.admin-reset__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3); }
.admin-reset__title { margin: 0; font: var(--type-heading-3); color: var(--color-text); }
.admin-reset__status { display: flex; align-items: flex-start; gap: var(--space-2); margin: 0; font: var(--type-body); color: var(--color-text); }
.admin-reset__status svg { flex: none; margin-top: var(--space-1); color: var(--color-text-muted); }
.admin-reset__lists { display: grid; gap: var(--space-4); }
.admin-reset__list-title { margin: 0 0 var(--space-2); font: var(--type-label); color: var(--color-text); }
.admin-reset__list ul { display: grid; gap: var(--space-1); margin: 0; padding-left: var(--space-5); font: var(--type-body); color: var(--color-text); }
.admin-reset__note { margin: 0; font: var(--type-caption); color: var(--color-text-muted); }
.admin-reset__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }
@media (min-width: 40rem) {
  .admin-reset__lists { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Windows high contrast replaces the fill, so the current section takes the system highlight. */
@media (forced-colors: active) {
  .admin-nav__link[aria-current="page"] { forced-color-adjust: none; background: Highlight; border-color: Highlight; color: HighlightText; }
}

/* ---- avatar.css ---- */
/* Avatar (DESIGN-SYSTEM.md): initials on a token-colored circle; the current high bidder
   wears the crown (SPEC §5). Sizes: sm (bid history), md (header), lg (profile menu). */
.avatar {
  position: relative;
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--size-avatar-md);
  height: var(--size-avatar-md);
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font: var(--type-label);
  font-weight: var(--weight-bold);
}
.avatar--sm { width: var(--size-avatar-sm); height: var(--size-avatar-sm); font: var(--type-badge); }
.avatar--lg { width: var(--size-avatar-lg); height: var(--size-avatar-lg); font: var(--type-heading-3); }

/* The crown: small, slightly tilted, at the top-left edge. Its outline separates it from
   white and from the avatar circle; in dark mode its fill separates it from the page. */
.avatar__crown {
  position: absolute;
  width: 70%;
  left: -16%;
  top: -30%;
  rotate: -16deg;
  overflow: visible;
  pointer-events: none;
}
.avatar__crown-shape { fill: var(--color-crown); stroke: var(--color-crown-stroke); stroke-linejoin: round; stroke-linecap: round; }
.avatar__crown-band { fill: none; stroke: var(--color-crown-stroke); stroke-linecap: round; }

/* ---- banner.css ---- */
/* Banner (DESIGN-SYSTEM.md): a message about the whole page or event that stays until
   the situation changes. Phase (butter; a dark butter-tinted surface in dark mode, bright
   butter for --urgent) says where the event is; info (neutral, the
   default fill) gives context; success, warning and danger report outcomes. Icon, then a title and text, then an optional action. */
.banner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  border: var(--border-thin) solid transparent;
  background: var(--color-surface-muted);
  color: var(--color-text);
}
.banner > .icon { margin-block: var(--space-1); }
/* The action wraps under the text when there isn't room beside it (320px reflow). */
.banner__body { flex: 1 1 calc(var(--space-16) * 3); display: grid; gap: var(--space-1); min-width: 0; }
.banner__title { font: var(--type-label); }
.banner__text { font: var(--type-body); }
.banner__action { flex: none; align-self: center; }
/* The action is a full-size button: 44px (SPEC §6). Banners aren't dense rows, so never --sm. */
.banner__action .btn { min-height: var(--size-target); }

.banner--phase   { background: var(--color-banner-calm); border-color: var(--color-banner-calm-border); color: var(--color-on-banner-calm); }
/* Ending soon: the bright butter in both modes. */
.banner--phase.banner--urgent { background: var(--color-banner); border-color: var(--color-banner); color: var(--color-on-banner); }
.banner--success { background: var(--color-success-soft); }
.banner--success > .icon { color: var(--color-success); }
.banner--warning { background: var(--color-warning-soft); }
.banner--warning > .icon { color: var(--color-warning); }
.banner--danger  { background: var(--color-danger-soft); }
.banner--danger > .icon { color: var(--color-danger); }
.banner--info > .icon { color: var(--color-link); }

/* The "demo was reset" offer (DESIGN-SYSTEM.md › Reset offer): an info banner whose title is a
   heading, so it reads as its own region on Home and the Admin Dashboard. */
.reset-offer .banner__title { margin: 0; }
/* On Home it opens the page, right under the phase banner: keep the two apart. */
.page > .reset-offer:first-child { margin-top: var(--space-4); }

/* ---- button.css ---- */
/* Button (DESIGN-SYSTEM.md): one per action. Variants primary, secondary, quiet, danger, danger-outline;
   sizes default and sm; icon-only and block layouts. The .is-* classes force a state for
   the gallery and Figma only; the app relies on the real pseudo-classes. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--size-target);
  padding: var(--space-2) var(--space-5);
  border: var(--border-thin) solid transparent;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
  color: var(--color-on-accent);
  font: var(--type-button);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-standard), border-color var(--duration-fast) var(--ease-standard);
}
.btn:hover, .btn.is-hover { background: var(--color-accent-hover); }
.btn:active, .btn.is-active { background: var(--color-accent-active); }
.btn.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }

.btn--secondary { background: var(--color-surface); color: var(--color-link); border-color: var(--color-border-strong); }
.btn--secondary:hover, .btn--secondary.is-hover { background: var(--color-accent-soft); }
.btn--secondary:active, .btn--secondary.is-active { background: var(--color-accent-soft-active); }

.btn--quiet { background: transparent; color: var(--color-text); }
.btn--quiet:hover, .btn--quiet.is-hover { background: var(--color-surface-muted); }
.btn--quiet:active, .btn--quiet.is-active { background: var(--color-accent-soft-active); }

.btn--danger { background: var(--color-danger); color: var(--color-on-danger); }
.btn--danger:hover, .btn--danger.is-hover { background: var(--color-danger-hover); }
.btn--danger:active, .btn--danger.is-active { background: var(--color-danger-active); }

/* Danger outline: a destructive action that opens its own red confirmation (Restore demo data in
   the demo bar). Cautious without shouting; the dialog carries the filled red. */
.btn--danger-outline { background: transparent; color: var(--color-danger); border-color: currentColor; }
.btn--danger-outline:hover, .btn--danger-outline.is-hover { background: var(--color-danger-soft); }
.btn--danger-outline:active, .btn--danger-outline.is-active { background: var(--color-danger-soft); box-shadow: inset 0 0 0 var(--border-thin) currentColor; }

.btn--sm { min-height: var(--size-target-sm); padding: var(--space-1) var(--space-4); font: var(--type-label); }
.btn--icon { padding: 0; width: var(--size-target); }
.btn--icon.btn--sm { width: var(--size-target-sm); }
.btn--block { display: flex; width: 100%; }

/* Disabled: still readable, clearly inactive. Prefer aria-disabled so the button stays
   focusable and can explain why; the native disabled attribute is styled the same. */
.btn:disabled, .btn[aria-disabled="true"]:not([aria-busy="true"]) {
  background: var(--color-surface-muted);
  border-color: var(--color-border);
  color: var(--color-text-disabled);
  cursor: not-allowed;
}

/* Loading: aria-busy="true" plus aria-disabled="true" while the request runs. The label
   stays (it can change to say what's happening); a spinner appears before it. */
.btn[aria-busy="true"] { cursor: progress; }
.btn[aria-busy="true"]::before {
  content: "";
  flex: none;
  width: var(--size-icon-sm);
  height: var(--size-icon-sm);
  border: var(--border-thick) solid currentColor;
  border-right-color: transparent;
  border-radius: var(--radius-pill);
  animation: btn-spin var(--duration-spin) linear infinite;
}
@keyframes btn-spin { to { rotate: 1turn; } }

/* ---- card.css ---- */
/* Card (DESIGN-SYSTEM.md): a contained group of content (an item, an event, a summary).
   Optional media, header with title, body and footer. --flat drops the shadow; --link
   makes the whole card open the title's link, while the title stays the only tab stop. */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
/* overflow: hidden also zeroes the automatic min-height, so a tall photo can't stretch the 4:3 box. */
.card__media { aspect-ratio: 4 / 3; overflow: hidden; background: var(--color-surface-muted); }
.card__media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.card__header { display: grid; gap: var(--space-1); padding: var(--space-4) var(--space-4) 0; }
.card__title { font: var(--type-heading-3); }
.card__body { flex: 1; display: grid; align-content: start; gap: var(--space-2); padding: var(--space-3) var(--space-4) var(--space-4); }
.card__footer { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-4); border-top: var(--border-thin) solid var(--color-border); }
.card--flat { box-shadow: none; }

.card--link { transition: box-shadow var(--duration-fast) var(--ease-standard); }
.card--link .card__title a { color: inherit; text-decoration: none; }
.card--link .card__title a::after { content: ""; position: absolute; inset: 0; }
.card--link .card__title a:focus-visible { outline: none; }
.card--link:hover, .card--link.is-hover { box-shadow: var(--shadow-md); border-color: var(--color-border-strong); }
.card--link:hover .card__title a, .card--link.is-hover .card__title a { text-decoration: underline; }
.card--link:has(.card__title a:focus-visible), .card--link.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.card--link:active, .card--link.is-active { box-shadow: var(--shadow-sm); }

/* ---- carousel.css ---- */
/* Photo carousel (DESIGN-SYSTEM.md; SPEC §7.3): one photo, previous and next, thumbnails
   beneath. Behavior in src/components/carousel.ts. No autoplay. */
.carousel { display: grid; gap: var(--space-3); }
.carousel__stage { position: relative; overflow: hidden; border-radius: var(--radius-lg); background: var(--color-surface-muted); aspect-ratio: 4 / 3; }
.carousel__slide { position: absolute; inset: 0; margin: 0; }
.carousel__slide[hidden] { display: none; }
.carousel__slide img, .carousel__photo { display: block; width: 100%; height: 100%; object-fit: cover; }
.carousel__nav { position: absolute; top: 50%; translate: 0 -50%; box-shadow: var(--shadow-md); }
.carousel__nav--prev { left: var(--space-3); }
.carousel__nav--next { right: var(--space-3); }
.carousel__count { position: absolute; bottom: var(--space-3); right: var(--space-3); padding: 0 var(--space-2); border-radius: var(--radius-pill); background: var(--color-inverse); color: var(--color-on-inverse); font: var(--type-badge); font-variant-numeric: var(--figures-amount); }
/* Up to five photos (SPEC §6), so the row never wraps: on a phone the thumbnails narrow to fit,
   never below the touch target. */
.carousel__thumbs { list-style: none; margin: 0; padding: 0; display: flex; gap: var(--space-2); }
.carousel__thumbs > li { flex: 0 1 calc(var(--size-target) * 1.5); min-width: var(--size-target); }
.carousel__thumb {
  display: block;
  width: 100%;
  height: var(--size-target);
  padding: 0;
  overflow: hidden;
  border: var(--border-thick) solid transparent;
  border-radius: var(--radius-md);
  background: var(--color-surface-muted);
  cursor: pointer;
}
.carousel__thumb > * { display: block; width: 100%; height: 100%; object-fit: cover; }
.carousel__thumb:hover, .carousel__thumb.is-hover { border-color: var(--color-border-strong); }
.carousel__thumb[aria-current="true"] { border-color: var(--color-accent); }
.carousel__thumb.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.carousel--single .carousel__nav, .carousel--single .carousel__thumbs { display: none; }

/* Windows high contrast makes every border one color, so the current thumbnail takes the system highlight. */
@media (forced-colors: active) {
  .carousel__thumb[aria-current="true"] { forced-color-adjust: none; border-color: Highlight; outline: var(--border-thick) solid Highlight; outline-offset: var(--border-thick); }
}

/* ---- charity-card.css ---- */
/* Charity card (DESIGN-SYSTEM.md): a charity's logo, name and what it does (Home and giving).
   Built on .card--flat and stacked: the logo big on top, on the art's own white in both modes
   (the logos have dark lettering), then the name, then the description. */
.charity-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--size-tile-min), 100%), 1fr)); gap: var(--space-4); }
.charity-card { flex-direction: column; gap: var(--space-4); padding: var(--space-4); height: 100%; }
.charity-card__tile { display: grid; place-items: center; padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-mark-tile); }
.charity-card__logo { display: block; width: var(--size-charity-logo); max-width: 100%; height: auto; aspect-ratio: 1; object-fit: contain; }
.charity-card__text { display: grid; gap: var(--space-1); min-width: 0; }
.charity-card__name { font: var(--type-heading-3); }
.charity-card__description { color: var(--color-text-muted); }
/* Forced colors would swap the tile's white for the system background, and the logos' dark
   lettering would vanish on a dark one, so the tile keeps the art's own white. */
@media (forced-colors: active) {
  .charity-card__tile { forced-color-adjust: none; }
}

/* ---- demo-bar.css ---- */
/* Demo bar (DESIGN-SYSTEM.md; SPEC §3.1): the demo's own controls, first on every page and
   in the normal flow (it never covers content). Dark in both modes, darker than the dark page,
   with butter hazard stripes, so it's the most distinct thing on the page either way (owner,
   visible changes A1). A <details>, so it collapses to a one-line strip; src/components/demo-bar.ts
   remembers that, announces who you're viewing as, and hides it for ?demo=off. */
.demo-bar { position: relative; container: demo-bar / inline-size; background: var(--color-demo-bar); color: var(--color-on-demo); }
.demo-bar::before { content: ""; display: block; height: var(--space-2); background: repeating-linear-gradient(135deg, var(--color-demo-stripe) 0 var(--space-2), var(--color-demo-bar) var(--space-2) var(--space-4)); }
.demo-bar__details { max-width: var(--size-content); margin: 0 auto; padding-inline: var(--space-4); }

/* The strip: one line at every width (tag, who you're viewing as, the tour link where it fits,
   the chevron). The name gives way with an ellipsis; nothing wraps. */
.demo-bar__summary {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--size-target);
  font: var(--type-label);
  cursor: pointer;
  list-style: none;
}
.demo-bar__summary::-webkit-details-marker { display: none; }
.demo-bar__summary > .icon { flex: none; margin-left: auto; transition: rotate var(--duration-fast) var(--ease-standard); }
.demo-bar__details[open] > .demo-bar__summary > .icon { rotate: 180deg; }
.demo-bar__tag { flex: none; padding: 0 var(--space-2); border-radius: var(--radius-pill); background: var(--color-banner); color: var(--color-on-banner); font: var(--type-badge); text-transform: uppercase; letter-spacing: var(--tracking-label); }
.demo-bar__who { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.demo-bar :focus-visible { outline-color: var(--color-on-demo); }

/* The open bar: compact (owner, visible changes A1). Small controls at --size-target-sm (SPEC
   §6 v1.25), never under WCAG 2.2's minimum. Top-aligned, so the Viewing as and Event phase labels
   share a line, and so do their controls (owner, pre-push review). */
.demo-bar__controls { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-2) var(--space-4); padding-block: 0 var(--space-3); }
.demo-bar .field { gap: var(--space-1); }
.demo-bar .field__label, .demo-bar__group-label { color: var(--color-on-demo); font: var(--type-label); }
.demo-bar__viewing { flex: 0 1 calc(var(--space-16) * 4); min-width: calc(var(--space-16) * 3); }
.demo-bar .select > .input { min-height: var(--size-target-sm); padding-block: var(--space-1); font: var(--type-label); }
.demo-bar__jump { display: grid; min-width: 0; margin: 0; padding: 0; border: 0; }
/* A legend isn't a grid item, so the fieldset's gap doesn't reach it; the margin matches
   the field's label-to-control gap, so the buttons line up with the select. */
.demo-bar__group-label { padding: 0; margin-bottom: var(--space-1); }
.demo-bar__jump-buttons { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* The phase buttons and the select take the bar's own control colors in both modes (owner, A1
   review): white pills were the loudest thing on the bar in light mode. */
.demo-bar__jump-buttons .btn { background: var(--color-demo-control); border-color: var(--color-demo-control-border); color: var(--color-demo-control-text); }
.demo-bar__jump-buttons .btn:hover { background: var(--color-demo-control-hover); }
/* color-scheme: dark only on the select, so its native option list matches; its own colors are fixed tokens. */
.demo-bar .select > .input { color-scheme: dark; background: var(--color-demo-control); border-color: var(--color-demo-control-border); color: var(--color-demo-control-value); }
.demo-bar .select > .input:hover { border-color: var(--color-demo-control-icon); }
.demo-bar .select > .icon { color: var(--color-demo-control-icon); }
/* The current phase: filled teal, plus aria-pressed for screen readers. */
.demo-bar__jump-buttons .btn[aria-pressed="true"] { background: var(--color-demo-current); border-color: var(--color-on-demo); color: var(--color-on-demo-current); }

/* Restore demo data, and the line beside it: on the controls' bottom edge. Restore is the danger
   outline, in the bar's own red, so it matches its red confirmation without shouting. */
.demo-bar__end { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); align-self: flex-end; }
.demo-bar .btn--danger-outline { color: var(--color-danger-on-demo); }
.demo-bar .btn--danger-outline:hover, .demo-bar .btn--danger-outline:active { background: var(--color-danger-on-demo); color: var(--color-demo-bar); box-shadow: none; }
/* "Shared demo · resets daily" (SPEC §3.1, v1.11): in the open bar only (v1.25). */
.demo-bar__note { margin: 0; font: var(--type-caption); }

/* The tour link and About this demo (SPEC v1.21, v1.24 §3.1): text-styled, at least --space-6 tall
   (WCAG 2.2 target size). About reopens the welcome dialog, so it's a button. */
.demo-bar__tour, .demo-bar__about { color: var(--color-on-demo); font: var(--type-label); text-decoration: underline; text-decoration-thickness: var(--border-thin); text-underline-offset: var(--space-1); border-radius: var(--radius-sm); }
.demo-bar__tour:hover, .demo-bar__about:hover { text-decoration-thickness: var(--border-thick); }
.demo-bar__about { min-height: var(--space-6); padding: 0; border: 0; background: none; cursor: pointer; }

/* On a narrow bar (under --breakpoint-sm) the tour link is the last thing in the open bar; phones
   start collapsed, so the welcome dialog points the way. On a wider bar it sits in the strip,
   open or collapsed, in room the summary keeps for it before the chevron. It's outside the
   <summary> (a link inside the toggle would be nested controls), so it's placed over that room. */
.demo-bar__tour--open { display: inline-flex; align-items: center; min-height: var(--space-6); flex-basis: 100%; }
.demo-bar__tour-room, .demo-bar__strip-tour { display: none; }
@container demo-bar (min-width: 40rem) {
  .demo-bar__tour--open { display: none; }
  .demo-bar__tour-room { display: block; flex: none; width: calc(var(--space-16) * 2); }
  .demo-bar__strip-tour {
    position: absolute;
    top: var(--space-2);
    left: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    max-width: var(--size-content);
    height: var(--size-target);
    margin: 0 auto;
    padding-inline: var(--space-4) calc(var(--space-4) + var(--size-icon-sm) + var(--space-2));
    pointer-events: none;
  }
  .demo-bar__strip-tour > a { display: inline-flex; align-items: center; min-height: var(--space-6); pointer-events: auto; }
}

/* Windows high contrast replaces the fill, so the current phase takes the system highlight. */
@media (forced-colors: active) {
  .demo-bar__jump-buttons .btn[aria-pressed="true"] { forced-color-adjust: none; background: Highlight; border-color: Highlight; color: HighlightText; }
}

/* ---- dialog.css ---- */
/* Dialog (DESIGN-SYSTEM.md): a native modal <dialog> built by src/components/dialog.ts.
   Variants: confirm, danger, type-to-confirm, info. .dialog--static shows one in place for the
   gallery and Figma. */
.dialog {
  width: min(var(--size-dialog), calc(100% - var(--space-8)));
  max-height: calc(100% - var(--space-8));
  padding: 0;
  border: 0;
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-lg);
}
.dialog::backdrop { background: var(--color-overlay); }
.dialog[open] { animation: dialog-in var(--duration-slow) var(--ease-standard); }
.dialog[open]::backdrop { animation: dialog-fade var(--duration-slow) var(--ease-standard); }
@keyframes dialog-in { from { opacity: 0; translate: 0 var(--space-4); } }
@keyframes dialog-fade { from { opacity: 0; } }

.dialog__form { display: grid; gap: var(--space-4); padding: var(--space-6); }
/* "Buy the Hand-knit scarf for $45?" breaks evenly on phones, never inside "Hand-knit". */
.dialog__title { font: var(--type-heading-2); text-wrap: balance; }
.dialog__word { white-space: nowrap; }
.dialog__body { display: grid; gap: var(--space-3); color: var(--color-text); }
.dialog__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--space-3); padding-top: var(--space-2); }
/* Below --breakpoint-sm the actions fill the row: side by side at matching widths when they fit,
   each full width when they wrap, so a stack never right-aligns at two widths (owner, welcome
   Stage 2 review). The order stays the DOM order, so what's seen and Tab agree. */
@media (width < 40rem) {
  .dialog__actions > * { flex: 1 1 0; }
}
.dialog--danger .dialog__title, .dialog--type-to-confirm .dialog__title { color: var(--color-danger); }

/* The item being confirmed: thumbnail, title, the computed amount. */
.dialog__item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3); border-radius: var(--radius-md); background: var(--color-surface-muted); }
.dialog__item-thumb { flex: none; width: var(--size-target); height: var(--size-target); border-radius: var(--radius-sm); background: var(--color-border); }
img.dialog__item-thumb { display: block; object-fit: cover; } /* the item's cover photo */
.dialog__item-title { font: var(--type-label); }
.dialog__amount { font: var(--type-amount); color: var(--color-price); font-variant-numeric: var(--figures-amount); }

/* Info (the welcome; DECISIONS entry 139): something to read, then a way on. When it's taller
   than the screen (a phone held sideways), only the body scrolls, between the title and the
   actions, which stay in view; the dialog's own max-height caps it. */
.dialog--info[open] { display: flex; flex-direction: column; }
.dialog--info .dialog__form { flex: 0 1 auto; min-height: 0; grid-template-rows: auto minmax(0, 1fr) auto; }
.dialog--info .dialog__body { overflow-y: auto; overscroll-behavior: contain; }
/* While it scrolls, a divider above the actions says there's more than the cut-off line shows
   (dialog.ts sets the class only then). */
.dialog--info .dialog__body--scrolls { padding-bottom: var(--space-3); border-bottom: var(--border-thin) solid var(--color-border-strong); }
/* The welcome's steps: the How to participate circles (steps.css), smaller, beside each line. */
.dialog__steps { display: grid; gap: var(--space-2); margin: 0; padding: 0; list-style: none; counter-reset: step; }
.dialog__steps > li { counter-increment: step; display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: var(--space-3); }
/* The × at the top right (SPEC v1.25 §3.1); the title leaves room for it. */
.dialog__close { position: absolute; top: var(--space-3); right: var(--space-3); }
.dialog--info .dialog__title { padding-right: var(--space-8); }

.dialog--static { position: relative; display: block; margin: 0; width: 100%; animation: none; }

/* ---- empty.css ---- */
/* Empty state (DESIGN-SYSTEM.md): what a list or page shows when there's nothing in it
   yet. Says what's missing, why, and the next step. */
.empty {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-10) var(--space-6);
  border: var(--border-thin) dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
  text-align: center;
}
.empty__icon { color: var(--color-text-muted); }
.empty__title { font: var(--type-heading-3); }
.empty__text { max-width: var(--size-content-narrow); color: var(--color-text-muted); }
.empty__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3); padding-top: var(--space-2); }

/* Loading (DESIGN-SYSTEM.md › Loading): a visible line with a spinner where the content will
   go, in the HTML so it shows at first paint. The spinner is the button's, stopped under
   reduced motion like every animation (base.css). */
.loading { display: flex; align-items: center; justify-content: center; gap: var(--space-3); margin: 0; padding: var(--space-8) var(--space-4); font: var(--type-body); color: var(--color-text-muted); }
.loading[hidden] { display: none; }
.loading__spinner {
  flex: none; width: var(--size-icon-md); height: var(--size-icon-md);
  border: var(--border-thick) solid currentColor; border-right-color: transparent; border-radius: var(--radius-pill);
  animation: btn-spin var(--duration-spin) linear infinite;
}

/* ---- error-summary.css ---- */
/* Error summary (DESIGN-SYSTEM.md): a danger banner at the top of a form after a submit
   with problems. It takes focus and links to each field. Built by src/components/forms.ts. */
.error-summary:focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.error-summary__list { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-1); }

/* ---- field.css ---- */
/* Form field (DESIGN-SYSTEM.md): label, optional help, the control, then a message.
   Only optional fields are marked, with "(optional)"; there are no asterisks. Errors are
   added by src/components/forms.ts; a warning is a message that still lets you submit. */
.field { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-2); min-width: 0; margin: 0; padding: 0; border: 0; }
.field__label { padding: 0; font: var(--type-label); color: var(--color-text); }
.field__optional { font-weight: var(--weight-regular); color: var(--color-text-muted); }
.field__help { font: var(--type-caption); color: var(--color-text-muted); }
.field__message { display: flex; align-items: flex-start; gap: var(--space-1); font: var(--type-caption); font-weight: var(--weight-semibold); }
.field__message .icon { margin-block: var(--space-1); }
.field__message--error { color: var(--color-danger); }
.field__message--warning { color: var(--color-warning); }
/* Labels keep full contrast when their control is disabled; the control shows the state. */

/* A row of short fields (price and quantity) that wraps on small screens. */
.field-row { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.field-row > .field { flex: 1 1 calc(var(--space-16) * 2.5); }

/* ---- giving.css ---- */
/* Giving stub (DESIGN-SYSTEM.md › Giving stub): the viewer's total and what to do next, under the steps. */
.giving__you { display: grid; gap: var(--space-3); justify-items: start; }
.giving__you > [hidden] { display: none; }
.giving__you > p { margin: 0; }
.giving__total { font: var(--type-heading-3); color: var(--color-text); }
.giving__none { font: var(--type-body); color: var(--color-text-muted); }

/* ---- guide.css ---- */
/* Things to try, the guide (/guide; DESIGN-SYSTEM.md › Guide; SPEC v1.21 §8): a reading column of
   scenario cards, each with who and which phase, Set me up and what it changes, its result,
   numbered steps, and why it works this way. */

.guide { max-width: calc(var(--size-content-narrow) + var(--space-16) * 2); }
.guide-contents ol { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: 0; padding: 0; list-style: none; counter-reset: scenario; }
.guide-contents li { counter-increment: scenario; }
.guide-contents li::before { content: counter(scenario) ". "; color: var(--color-text-muted); }
.guide-contents a { color: var(--color-link); text-decoration: underline; text-decoration-thickness: var(--border-thin); text-underline-offset: var(--space-1); border-radius: var(--radius-sm); }

.guide-scenario { display: grid; gap: var(--space-4); padding: var(--space-5); scroll-margin-top: var(--space-4); }
.guide-scenario__head { display: grid; gap: var(--space-2); }
.guide-scenario__title { font: var(--type-heading-2); }
.guide-scenario__story { font: var(--type-badge); text-transform: uppercase; letter-spacing: var(--tracking-label); color: var(--color-text-muted); }
.guide-scenario__meta { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-6); margin: 0; }
.guide-scenario__meta div { display: grid; gap: 0; }
.guide-scenario__meta dt { font: var(--type-caption); color: var(--color-text-muted); }
.guide-scenario__meta dd { margin: 0; font: var(--type-label); }
.guide-scenario__setup { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }
.guide-scenario__setup[hidden] { display: none; }
.guide-scenario__effect { flex: 1 1 calc(var(--space-16) * 3); font: var(--type-caption); color: var(--color-text-muted); }
.guide-scenario__effect strong { color: var(--color-text); font-weight: inherit; }
.guide-scenario__status:empty { display: none; }
.guide-steps { display: grid; gap: var(--space-2); margin: 0; padding-left: var(--space-6); }
.guide-steps li::marker { color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.guide-scenario__why { padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); background: var(--color-surface-muted); }
.guide-story { padding-left: var(--space-4); border-left: var(--border-thick) solid var(--color-accent); }
.guide-also { display: grid; gap: var(--space-2); margin: 0; padding-left: var(--space-6); }

/* ---- hero.css ---- */
/* Hero (DESIGN-SYSTEM.md): the top of Home. The lockup inside the page's h1, then the
   event's name and description. On phones the lockup and overline are centered and the
   paragraphs left-aligned, like the headings and cards below; side by side once the
   page is wide enough. */
.hero { display: grid; justify-items: center; gap: var(--space-6); padding-block: var(--space-8) var(--space-4); text-align: center; }
.hero__title { margin: 0; }
.hero__text { display: grid; gap: var(--space-3); justify-self: stretch; max-width: var(--size-content-narrow); margin-inline: auto; text-align: start; }
.hero__event { text-align: center; font: var(--type-overline); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--color-text-muted); }
.hero__lead { font: var(--type-body-lg); }
.hero__more { color: var(--color-text-muted); }
@media (min-width: 64rem) {
  .hero { grid-template-columns: auto 1fr; justify-items: start; align-items: center; gap: var(--space-12); text-align: start; }
  .hero__text { margin-inline: 0; }
  .hero__event { text-align: start; }
}

/* ---- icon.css ---- */
/* Icon (DESIGN-SYSTEM.md): original line icons from src/components/icons.ts, drawn on a
   24-unit grid with one stroke width. They take the text color around them. */
.icon {
  flex: none;
  width: var(--size-icon-md);
  height: var(--size-icon-md);
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: middle;
}
.icon--sm { width: var(--size-icon-sm); height: var(--size-icon-sm); }
.icon--lg { width: var(--size-icon-lg); height: var(--size-icon-lg); }

/* ---- input.css ---- */
/* Text input, textarea, select and date-time input (DESIGN-SYSTEM.md). All share .input;
   prefixes ($) and suffixes (the time zone) sit in an .input-group. Money inputs are
   type="text" inputmode="numeric" with a digits-only pattern (SPEC §7.9). */
.input {
  display: block;
  width: 100%;
  min-width: 0;
  min-height: var(--size-target);
  padding: var(--space-2) var(--space-3);
  border: var(--border-thick) solid var(--color-border-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: var(--type-body);
  transition: border-color var(--duration-fast) var(--ease-standard);
}
.input::placeholder { color: var(--color-text-muted); opacity: 1; }
.input:hover, .input.is-hover { border-color: var(--color-text-muted); }
.input:focus-visible, .input.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); border-color: var(--color-accent); }
.input[aria-invalid="true"] { border-color: var(--color-danger); }
.field--warning .input { border-color: var(--color-warning); }
.input:disabled { background: var(--color-surface-muted); border-color: var(--color-border); color: var(--color-text-disabled); cursor: not-allowed; }
.input[readonly] { background: var(--color-surface-muted); border-color: var(--color-border); }
textarea.input { min-height: calc(var(--size-target) * 3); resize: vertical; }
.input--money { font-variant-numeric: var(--figures-amount); }
.input--short { max-width: calc(var(--space-16) * 3); }

/* Prefix and suffix: part of the box, outside the typed value. */
.input-group { display: flex; align-items: stretch; min-width: 0; }
.input-group > .input { flex: 1; }
.input-group__affix {
  display: inline-flex;
  align-items: center;
  padding-inline: var(--space-3);
  border: var(--border-thick) solid var(--color-border-strong);
  background: var(--color-surface-muted);
  color: var(--color-text-muted);
  font: var(--type-label);
}
.input-group__affix:first-child { border-right: 0; border-radius: var(--radius-md) 0 0 var(--radius-md); }
.input-group__affix:last-child { border-left: 0; border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.input-group > .input:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.input-group > .input:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* Select: the native control, with the chevron from the icon set drawn over it. */
.select { position: relative; display: grid; grid-template-columns: minmax(0, 1fr); min-width: 0; } /* shrinks below its longest option */
.select > .input { appearance: none; padding-right: calc(var(--space-3) * 2 + var(--size-icon-md)); cursor: pointer; }
.select > .icon { position: absolute; right: var(--space-3); top: 50%; translate: 0 -50%; pointer-events: none; color: var(--color-text-muted); }
.select > .input:disabled + .icon { color: var(--color-text-disabled); }

/* ---- item-form.css ---- */
/* Item form (DESIGN-SYSTEM.md › Item form; SPEC §7.3): Submit an Item and Edit item. One
   column at reading width on a raised surface, like the gallery's demo form. Nested fieldsets
   group fields without borders. The delete section sits below, apart from the form. */
.item-form { display: grid; gap: var(--space-6); max-width: var(--size-content-narrow); padding: var(--space-6); border-radius: var(--radius-lg); background: var(--color-surface); box-shadow: var(--shadow-sm); }
.item-form fieldset { display: grid; gap: var(--space-6); min-width: 0; margin: 0; padding: 0; border: 0; }
.item-form fieldset[hidden] { display: none; }
.item-form__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.item-form__danger { display: grid; gap: var(--space-3); justify-items: start; max-width: var(--size-content-narrow); margin-top: var(--space-8); padding-top: var(--space-6); border-top: var(--border-thin) solid var(--color-border); }
.item-form__danger-title { font: var(--type-heading-3); }
.item-form__danger-text { color: var(--color-text-muted); }
@media (max-width: 40rem) {
  .item-form { padding: var(--space-4); }
}

/* ---- item-page.css ---- */
/* Item page (DESIGN-SYSTEM.md › Item page; SPEC §7.3): photos beside the amount panel on wide
   screens, stacked on phones; then the description and the bid history. */
/* The page content is drawn into one wrapper; it spaces its sections like .page does. */
.item-page { display: grid; gap: var(--space-6); }
.item-layout { display: grid; gap: var(--space-6); align-items: start; }
@media (min-width: 64rem) {
  .item-layout { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
}
.item-panel { display: grid; gap: var(--space-2); justify-items: start; padding: var(--space-5); }
.item-panel__amount { font: var(--type-amount); color: var(--color-price); font-variant-numeric: var(--figures-amount); }
.item-panel__detail { font: var(--type-label); color: var(--color-text-muted); font-variant-numeric: var(--figures-amount); }
.item-panel__note { max-width: var(--size-content-narrow); }
.item-panel__actions:empty { display: none; }
.item-panel--muted .item-panel__amount { color: var(--color-text-muted); }
.item-section { display: grid; gap: var(--space-3); }
.item-section__title { font: var(--type-heading-2); }
.item-section__count { font: var(--type-body); color: var(--color-text-muted); font-variant-numeric: var(--figures-amount); }
.item-section__text { max-width: var(--size-content-narrow); white-space: pre-line; }
.item-panel__link { font: var(--type-label); }
.bid-history__bidder { display: inline-flex; align-items: center; gap: var(--space-3); }
.bid-history__who { display: grid; }
.bid-history th[scope="row"] { white-space: normal; }
/* Phones: two columns (bidder, amount); the time is a caption line under the name. */
.bid-history__when { font: var(--type-caption); color: var(--color-text-muted); }
@media (min-width: 40rem) {
  .bid-history__when { display: none; }
}
@media (width < 40rem) {
  .bid-history .bid-history__time { display: none; }
}
/* Stage 4D: the bid form and buy button sit at the bottom of the panel. The panel takes focus
   after a bid (the form is gone by then), so it needs no ring of its own. */
.item-panel:focus:not(:focus-visible) { outline: none; }
.item-panel__actions { justify-self: stretch; margin-top: var(--space-2); }
.bid-form { display: grid; gap: var(--space-4); justify-items: start; }
.bid-form .field { justify-self: stretch; }
.bid-form .input-group { max-width: calc(var(--space-16) * 3); }

/* ---- item-tile.css ---- */
/* Item tile (DESIGN-SYSTEM.md; SPEC §8): one item in a browse grid or Your Items. Built on
   .card.card--link; the amount line and status come from src/components/item-tile-rules.ts. */
.item-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, var(--size-tile-min)), 1fr)); gap: var(--space-4); }
/* Each li fills its grid row, and the card fills the li, so a row of tiles shares one height;
   content stays top-aligned (.card__body grows, its rows start at the top). */
.item-grid > li { display: grid; }
.item-tile .card__media { position: relative; }
.item-tile__status { position: absolute; top: var(--space-2); left: var(--space-2); box-shadow: var(--shadow-sm); }
.item-tile__amount { font: var(--type-amount); color: var(--color-price); font-variant-numeric: var(--figures-amount); }
.item-tile__detail { font: var(--type-caption); color: var(--color-text-muted); font-variant-numeric: var(--figures-amount); }
.item-tile--muted .card__media > :not(.item-tile__status) { opacity: 0.5; } /* the photo fades; the status pill keeps full contrast */
.item-tile--muted .item-tile__amount { color: var(--color-text-muted); }

/* ---- link.css ---- */
/* Link (DESIGN-SYSTEM.md): text that goes somewhere. Always underlined, so it never relies
   on color alone. Variants: default, quiet (inherits text color), external (new tab, with
   the external icon and hidden "opens in a new tab" text). */
.link { color: var(--color-link); text-decoration: underline; text-decoration-thickness: var(--border-thin); text-underline-offset: var(--space-1); border-radius: var(--radius-sm); }
.link:hover, .link.is-hover { text-decoration-thickness: var(--border-thick); }
.link:active, .link.is-active { color: var(--color-text); }
.link.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.link--quiet { color: inherit; }
.link--external { display: inline-flex; align-items: center; gap: var(--space-1); }

/* ---- lockup.css ---- */
/* Hero lockup (DESIGN-SYSTEM.md; DECISIONS #47): the event mark, then "Kestrel" (teal)
   over "gives" (coral), live text in the heading font with one accessible name. The mark's
   file is white, so the lockup sits on a light card in both modes (.lockup--card). */
.lockup { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: var(--space-2) var(--space-4); margin: 0; }
.lockup__mark { display: block; height: var(--size-lockup-mark); width: auto; }
.lockup__words { display: flex; flex-direction: column; font: var(--type-hero); letter-spacing: var(--tracking-lockup); }
.lockup__kestrel { color: var(--color-brand-teal); }
.lockup__gives { color: var(--color-brand-coral); }
.lockup--card { padding: var(--space-6); border-radius: var(--radius-xl); background: var(--color-mark-tile); box-shadow: var(--shadow-md); }

/* ---- page-header.css ---- */
/* Page header (DESIGN-SYSTEM.md): the top of each page. Optional back link, the h1, an
   optional description, and actions that wrap under the title on small screens. */
.page-header { display: grid; gap: var(--space-2); padding-block: var(--space-6) var(--space-4); }
.page-header__back { display: inline-flex; align-items: center; gap: var(--space-1); justify-self: start; min-height: var(--size-target); font: var(--type-label); }
.page-header__row { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--space-4); }
.page-header__title { font: var(--type-heading-1); letter-spacing: var(--tracking-tight); }
.page-header__text { display: grid; gap: var(--space-2); min-width: 0; flex: 1 1 calc(var(--space-16) * 5); }
.page-header__description { max-width: var(--size-content-narrow); color: var(--color-text-muted); font: var(--type-body-lg); }
.page-header__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* ---- page.css ---- */
/* Page layout (DESIGN-SYSTEM.md › Page layout and footer): the content column every page
   shares, the slot for the phase banner under the header, and the footer. */
.page { display: grid; gap: var(--space-6); align-content: start; max-width: var(--size-content); margin: 0 auto; padding: 0 var(--space-4) var(--space-12); }
.page:focus { outline: none; }
.phase-banner { max-width: var(--size-content); margin: var(--space-4) auto 0; padding-inline: var(--space-4); }
.site-footer { border-top: var(--border-thin) solid var(--color-border); color: var(--color-text-muted); font: var(--type-caption); }
.site-footer > p { max-width: var(--size-content); margin: 0 auto; padding: var(--space-6) var(--space-4); }
/* The footer's links (Things to try, the design system, How this was built) keep apart when they share a line. */
.site-footer a + a { margin-left: var(--space-3); }
/* A titled section of a page (Home's How to participate and Where donations go). */
.home-section { display: grid; gap: var(--space-4); padding-top: var(--space-4); }
/* Browse pages: the item count and the sort above the grid. The sort keeps its own width and
   drops under the count when the row runs out of room. */
.browse-toolbar { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: var(--space-3) var(--space-4); }
.browse-toolbar__count { font: var(--type-label); color: var(--color-text-muted); font-variant-numeric: var(--figures-amount); }
.browse-toolbar__sort { min-width: min(100%, var(--size-tile-min)); }

/* ---- pill.css ---- */
/* Pill (DESIGN-SYSTEM.md): a short, non-interactive status label ("Paid", "Ending soon").
   The words carry the meaning; color and the optional icon only support them. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-surface-muted);
  color: var(--color-text-muted);
  font: var(--type-badge);
  white-space: nowrap;
}
.pill .icon { width: var(--size-icon-sm); height: var(--size-icon-sm); }
.pill--success { background: var(--color-success-soft); color: var(--color-success); }
.pill--warning { background: var(--color-warning-soft); color: var(--color-warning); }
.pill--danger  { background: var(--color-danger-soft);  color: var(--color-danger); }

/* ---- profile-menu.css ---- */
/* Profile menu (DESIGN-SYSTEM.md; SPEC §8): for admins, the avatar and a chevron open a
   short list of links (a disclosure, not role="menu"). Employees see their avatar only,
   as .profile-menu__static. */
.profile-menu { position: relative; }
.profile-menu__button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--size-target);
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1);
  border: var(--border-thin) solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text);
  cursor: pointer;
}
.profile-menu__button:hover, .profile-menu__button.is-hover { background: var(--color-surface-muted); }
.profile-menu__button:active, .profile-menu__button.is-active { background: var(--color-accent-soft-active); }
.profile-menu__button.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.profile-menu__button > .icon { transition: rotate var(--duration-fast) var(--ease-standard); }
.profile-menu__button[aria-expanded="true"] { border-color: var(--color-border-strong); }
.profile-menu__button[aria-expanded="true"] > .icon { rotate: 180deg; }
/* Employees: an image of their initials, not a control. No pointer, hover or focus state. */
.profile-menu__static { display: inline-flex; align-items: center; min-height: var(--size-target); cursor: default; }

.profile-menu__panel {
  position: absolute;
  top: calc(100% + var(--space-2));
  right: 0;
  z-index: var(--layer-header);
  display: grid;
  gap: var(--space-2);
  min-width: calc(var(--space-16) * 3.5);
  padding: var(--space-3);
  border: var(--border-thin) solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
}
.profile-menu__panel[hidden] { display: none; }
.profile-menu__who { display: grid; padding: var(--space-1) var(--space-2) var(--space-2); border-bottom: var(--border-thin) solid var(--color-border); }
.profile-menu__name { font: var(--type-label); }
.profile-menu__role { font: var(--type-caption); color: var(--color-text-muted); }
.profile-menu__links { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }
.profile-menu__link { display: flex; align-items: center; gap: var(--space-2); min-height: var(--size-target); padding: var(--space-2); border-radius: var(--radius-md); color: var(--color-text); font: var(--type-label); text-decoration: none; }
.profile-menu__link:hover, .profile-menu__link.is-hover { background: var(--color-surface-muted); }
.profile-menu__link[aria-current="page"] { color: var(--color-link); }

/* Windows high contrast replaces the link color, so the current page is underlined instead. */
@media (forced-colors: active) {
  .profile-menu__link[aria-current="page"] { text-decoration: underline; text-decoration-thickness: var(--border-thick); }
}

/* ---- proof-page.css ---- */
/* Upload donation proof (DESIGN-SYSTEM.md › Proof page; SPEC §7.6): what happened to the last
   proof, then the form (an item form) with the total and the giving link at its top. */
.proof-page { display: grid; gap: var(--space-6); max-width: var(--size-content-narrow); }
.proof-page[hidden] { display: none; }
.proof-page__notes { display: grid; gap: var(--space-3); }
.proof-page__notes:empty { display: none; }
.proof-page__total { display: grid; gap: var(--space-2); justify-items: start; }
.proof-page__total-text { margin: 0; font: var(--type-heading-3); color: var(--color-text); }
/* Below the total is allowed (an admin decides), so it's said plainly, not as an error. */
.proof-page__warning { display: flex; gap: var(--space-2); color: var(--color-warning); font-weight: var(--weight-semibold); }
.proof-page__warning[hidden] { display: none; }

/* ---- radio-card.css ---- */
/* Radio card (DESIGN-SYSTEM.md): a choice between a few big options, each with a title
   and a line on when to choose it. A native radio inside a label, so arrows, Space and
   screen readers work as usual. Side by side when there's room, stacked when not. */
/* The legend sits outside the fieldset's grid, so the space above the cards is a margin,
   wider than the focus ring (width + offset): the ring never covers the question. */
.radio-cards { margin-top: calc(var(--focus-width) + var(--focus-offset) + var(--space-2)); display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, calc(var(--space-16) * 3.5)), 1fr)); gap: var(--space-3); }
.radio-card {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-3);
  row-gap: var(--space-1);
  align-items: start;
  align-content: start;
  padding: var(--space-4);
  border: var(--border-thick) solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-standard), background-color var(--duration-fast) var(--ease-standard);
}
.radio-card__input { grid-row: span 2; width: var(--size-icon-md); height: var(--size-icon-md); margin: 0; accent-color: var(--color-accent); cursor: inherit; }
.radio-card__title { font: var(--type-label); color: var(--color-text); }
.radio-card__text { font: var(--type-caption); color: var(--color-text-muted); }
.radio-card:hover, .radio-card.is-hover { border-color: var(--color-accent); }
.radio-card:has(:checked) { border-color: var(--color-accent); background: var(--color-accent-soft); }
.radio-card:active, .radio-card.is-active { background: var(--color-accent-soft-active); }
.radio-card:has(:focus-visible), .radio-card.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.radio-card__input:focus-visible { outline: none; }
.radio-card:has(:disabled) { border-color: var(--color-border); background: var(--color-surface-muted); cursor: not-allowed; }
.radio-card:has(:disabled) .radio-card__title, .radio-card:has(:disabled) .radio-card__text { color: var(--color-text-disabled); }
/* A locked choice (the edit page once an item has activity): the chosen card keeps its
   checked look; only the others are disabled. */
.radio-cards:has(:disabled) .radio-card:has(:checked) { cursor: default; }
.radio-card:has([aria-invalid="true"]) { border-color: var(--color-danger); }

/* ---- site-header.css ---- */
/* Site header and nav (DESIGN-SYSTEM.md; SPEC §8). Mark and name, the main nav, then the
   person (avatar or profile menu). The header is a size container: below --breakpoint-lg
   of its own width the nav collapses to a Menu button (src/components/disclosure.ts). */
.site-header { container: site-header / inline-size; position: relative; z-index: var(--layer-header); background: var(--color-surface); border-bottom: var(--border-thin) solid var(--color-border); }
.site-header__inner { display: flex; align-items: center; gap: var(--space-2); max-width: var(--size-content); margin: 0 auto; padding: var(--space-2) var(--space-4); }
.site-header__brand { display: inline-flex; flex: 0 1 auto; align-items: center; gap: var(--space-2); min-height: var(--size-target); min-width: 0; border-radius: var(--radius-md); color: var(--color-text); text-decoration: none; font: var(--type-heading-3); }
.site-header__mark { display: inline-grid; place-items: center; flex: none; border-radius: var(--radius-sm); background: var(--color-mark-backing); padding: var(--space-1); }
.site-header__mark img { display: block; width: var(--size-mark-header); height: var(--size-mark-header); }
.site-header__nav { margin-left: auto; flex: none; }
/* When space is tight the name wraps to two lines before anything else moves. */
.site-header__name { min-width: 0; }
.site-header__end { display: flex; align-items: center; flex: none; }

/* Collapsed (the default, phone first): the Menu button shows; the links open as a panel
   under the header. Without JavaScript the links simply show, wrapped. */
.site-header__links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-1); }
.site-header__nav.is-enhanced .site-header__links { display: none; }
.site-header__nav.is-enhanced.is-open .site-header__links {
  position: absolute;
  top: 100%;
  inset-inline: 0;
  display: grid;
  gap: var(--space-1);
  padding: var(--space-2) var(--space-4) var(--space-4);
  background: var(--color-surface);
  border-bottom: var(--border-thin) solid var(--color-border);
  box-shadow: var(--shadow-md);
}
.site-header__menu-button { display: none; }
.site-header__nav.is-enhanced .site-header__menu-button { display: inline-flex; }
/* On the narrowest headers the Menu button shows its icon only; "Menu" stays its name. */
.site-header__menu-button { padding-inline: var(--space-3); }
.site-header__menu-label { position: absolute; width: var(--size-px); height: var(--size-px); overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@container site-header (width >= 40rem) {
  .site-header__menu-label { position: static; width: auto; height: auto; overflow: visible; clip-path: none; }
  .site-header__inner { gap: var(--space-3); }
}

.site-header__link {
  display: flex;
  align-items: center;
  min-height: var(--size-target);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text);
  font: var(--type-label);
  text-decoration: none;
  white-space: nowrap;
}
.site-header__link:hover, .site-header__link.is-hover { background: var(--color-surface-muted); }
.site-header__link:active, .site-header__link.is-active { background: var(--color-accent-soft-active); }
.site-header__link.is-focus { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
/* Current page: accent text and a bar, so it isn't shown by color alone. */
.site-header__link[aria-current="page"] { color: var(--color-link); box-shadow: inset var(--border-thick) 0 0 var(--color-accent); border-radius: 0 var(--radius-md) var(--radius-md) 0; }

/* Expanded: everything fits on one line. */
@container site-header (width >= 64rem) {
  .site-header__inner { gap: var(--space-6); }
  .site-header__brand { white-space: nowrap; }
  .site-header__nav.is-enhanced .site-header__menu-button { display: none; }
  .site-header__nav.is-enhanced .site-header__links, .site-header__nav.is-enhanced.is-open .site-header__links { position: static; display: flex; flex-wrap: nowrap; padding: 0; background: none; border: 0; box-shadow: none; }
  .site-header__link[aria-current="page"] { box-shadow: inset 0 calc(var(--border-thick) * -1) 0 var(--color-accent); border-radius: var(--radius-md) var(--radius-md) 0 0; }
}

/* Windows high contrast drops box shadows, so the current page is underlined instead. */
@media (forced-colors: active) {
  .site-header__link[aria-current="page"] { text-decoration: underline; text-decoration-thickness: var(--border-thick); text-underline-offset: var(--space-1); }
}

/* ---- skip-link.css ---- */
/* Skip link (DESIGN-SYSTEM.md): the first thing Tab reaches on every page. Hidden until
   focused, then shown over the top-left corner. */
.skip-link {
  position: absolute;
  top: var(--space-2);
  left: var(--space-2);
  z-index: var(--layer-dialog);
  display: inline-flex;
  align-items: center;
  min-height: var(--size-target);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-inverse);
  color: var(--color-on-inverse);
  font: var(--type-label);
  text-decoration: underline;
  translate: 0 calc(-100% - var(--space-4));
}
.skip-link:focus-visible, .skip-link.is-focus { translate: 0 0; outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }

/* ---- steps.css ---- */
/* Steps (DESIGN-SYSTEM.md): a short numbered list of what to do, in order (How to
   participate on Home). An <ol>, so the order is real; the circles repeat its numbers. */
.steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(min(var(--size-tile-min), 100%), 1fr)); gap: var(--space-4); counter-reset: step; }
.steps__item { counter-increment: step; display: grid; align-content: start; gap: var(--space-2); padding: var(--space-4); border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); }
/* The numbered circle, shared with the welcome dialog's steps (owner, visible changes A2). */
.steps__item::before, .dialog__steps > li::before {
  content: counter(step);
  display: inline-grid;
  place-items: center;
  width: var(--size-step-number);
  height: var(--size-step-number);
  border-radius: var(--radius-pill);
  background: var(--color-accent-soft);
  color: var(--color-link);
  font: var(--type-heading-3);
}
.steps__title { font: var(--type-heading-3); }
.steps__text { color: var(--color-text-muted); }
/* In the welcome dialog the circle is smaller, beside a line of body text (dialog.css lays out the list).
   Here, after the shared rule, so it wins regardless of file order. */
.dialog__steps > li::before { width: var(--size-step-number-sm); height: var(--size-step-number-sm); font: var(--type-label); font-weight: var(--weight-bold); }

/* ---- table.css ---- */
/* Table (DESIGN-SYSTEM.md): rows of records for admins. A caption names it; numbers are
   right-aligned in tabular figures; row actions sit in the last column. On narrow screens
   it scrolls inside .table-wrap (src/components/table.ts makes that region focusable). The
   wrap is positioned so visually hidden label text in row actions stays inside its scroll box. */
.table-wrap { position: relative; max-width: 100%; overflow-x: auto; border: var(--border-thin) solid var(--color-border); border-radius: var(--radius-lg); background: var(--color-surface); }
.table-wrap:focus-visible { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
.table { width: 100%; border-collapse: collapse; font: var(--type-body); }
.table caption { padding: var(--space-3) var(--space-4); text-align: left; font: var(--type-heading-3); }
.table th, .table td { padding: var(--space-2) var(--space-4); border-top: var(--border-thin) solid var(--color-border); text-align: left; vertical-align: middle; white-space: nowrap; }
.table thead th { font: var(--type-label); color: var(--color-text-muted); background: var(--color-surface-muted); }
.table tbody th { font-weight: var(--weight-semibold); }
.table tbody tr:hover, .table tbody tr.is-hover { background: var(--color-accent-soft); }
.table .table__num { text-align: right; font-variant-numeric: var(--figures-amount); }
.table__actions { text-align: right; }
.table__actions .btn + .btn { margin-left: var(--space-2); }
.table .table__empty { padding: var(--space-8) var(--space-4); text-align: center; color: var(--color-text-muted); white-space: normal; }
/* An empty table has nothing to scroll to: let its headers wrap so the message stays in view. */
.table:has(.table__empty) th { white-space: normal; }

/* ---- toast.css ---- */
/* Toast (DESIGN-SYSTEM.md): a brief confirmation at the bottom of the screen, in a polite
   live stack (or a quiet one when the page announces the words itself). Created by src/components/toast.ts. */
.toast-region {
  position: fixed;
  inset-inline: 0;
  bottom: var(--space-4);
  z-index: var(--layer-toast);
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-4);
  pointer-events: none;
}
.toast-region__stack { display: grid; justify-items: center; gap: var(--space-2); width: 100%; }
.toast {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: min(var(--size-toast), 100%);
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-inverse);
  color: var(--color-on-inverse);
  box-shadow: var(--shadow-lg);
  pointer-events: auto;
  animation: toast-in var(--duration-base) var(--ease-standard);
}
.toast__message { flex: 1; font: var(--type-body); }
.toast .toast__dismiss { color: inherit; }
.toast .toast__dismiss:hover, .toast .toast__dismiss.is-hover { background: transparent; box-shadow: inset 0 0 0 var(--border-thin) currentColor; }
/* In the gallery a toast is shown in place, not fixed. */
.toast--static { animation: none; }
@keyframes toast-in { from { opacity: 0; translate: 0 var(--space-4); } }

/* ---- uploader.css ---- */
/* Photo uploader (DESIGN-SYSTEM.md): add up to five photos, reorder with arrow buttons,
   remove. Behavior in src/components/uploader.ts. */
.uploader { display: grid; gap: var(--space-3); }
.uploader__add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border: var(--border-thin) dashed var(--color-border-strong);
  border-radius: var(--radius-lg);
}
.uploader__count { font: var(--type-caption); color: var(--color-text-muted); font-variant-numeric: var(--figures-amount); }
/* The file input is visually hidden; its label is the visible button and shows its focus. */
.uploader__input:focus-visible + .btn { outline: var(--focus-width) solid var(--color-focus); outline-offset: var(--focus-offset); }
/* Full: the button stays focusable (pressing it explains why), so its text keeps 4.5:1. */
.uploader--full .uploader__add .btn { background: var(--color-surface-muted); border-color: var(--color-border); color: var(--color-text-muted); cursor: not-allowed; }
.field--error .uploader__add { border-color: var(--color-danger); }

.uploader__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
.uploader__list[hidden] { display: none; }
/* Each photo is as wide as its control row: three full-size (44px) buttons. */
.uploader__item { position: relative; display: grid; gap: var(--space-2); width: calc(var(--size-target) * 3 + var(--space-1) * 2); }
.uploader__thumb { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: var(--radius-md); background: var(--color-surface-muted); }
.uploader__cover { position: absolute; top: var(--space-1); left: var(--space-1); background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-sm); }
.uploader__actions { display: flex; justify-content: space-between; gap: var(--space-1); }
.uploader__problems { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-1); }

/* Uploading: a spinner over the thumbnail until storage confirms (wired in Milestone 5). */
.uploader__item--uploading .uploader__thumb { opacity: 0.4; }
.uploader__progress { position: absolute; inset: 0 0 auto; aspect-ratio: 1; display: grid; place-items: center; font: var(--type-caption); font-weight: var(--weight-semibold); color: var(--color-text); }

/* ---- your-items.css ---- */
/* Your Items (DESIGN-SYSTEM.md › Your Items; SPEC §8): sections of item tiles, with the proof
   summary at the top of Won & purchased. Rows are item tiles; nothing here restyles them. */
.your-items { display: grid; gap: var(--space-10); }
.your-items__section { display: grid; gap: var(--space-4); }
.your-items__title { margin: 0; font: var(--type-heading-2); color: var(--color-text); }
.your-items__empty { margin: 0; font: var(--type-body); color: var(--color-text-muted); }
/* Edit sits under its tile, outside the linked card (a linked card holds one control). */
.item-grid > li:has(.your-items__edit) { grid-template-rows: 1fr auto; gap: var(--space-2); }
.your-items__edit { justify-self: start; }

/* The proof summary: the total, then the status, what happens next, and the upload link. */
.proof-summary { display: grid; gap: var(--space-4); padding: var(--space-5); }
.proof-summary__label { margin: 0; font: var(--type-label); color: var(--color-text-muted); }
.proof-summary__amount { margin: 0; font: var(--type-amount); color: var(--color-price); font-variant-numeric: var(--figures-amount); }
.proof-summary__body { display: grid; gap: var(--space-2); align-content: start; }
.proof-summary__status { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.proof-summary__due { font: var(--type-label); color: var(--color-text); }
.proof-summary__note { margin: 0; font: var(--type-body); color: var(--color-text); }
.proof-summary__actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); margin-top: var(--space-2); }
@media (min-width: 40rem) {
  .proof-summary { grid-template-columns: minmax(max-content, 1fr) 3fr; gap: var(--space-6); }
}

}
