/* ==========================================================================
   Black Cambry 2026 — Shared Component Stylesheet
   --------------------------------------------------------------------------
   Slice A: state primitives only (loading / error / empty).
   Card / input / floating-pill rules will be extracted into this file at
   the end of Slice C, once two views have adopted them (Req 15.1).

   Tokens consumed (defined in styles.css :root):
     --color-bg          slate background
     --color-surface     card chrome (one step lighter than bg)
     --color-accent      soft pink accent
     --color-text-muted  muted blue-grey body copy
     --color-on-accent   dark text for surfaces riding the pink accent
   Typography: Yantramanav, with rem-based sizes that mirror the scale
   already in use across the games / results / admin views.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Loading state
   --------------------------------------------------------------------------
   Visible inside 200ms of mount: the spinner has NO animation-delay so its
   first paint happens on the same frame the element is inserted; the
   rotation animation itself runs continuously thereafter.
   -------------------------------------------------------------------------- */
.cambry-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  padding: 3rem 1rem;
  min-height: 8rem;
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  color: var(--color-text-muted);
  text-align: center;
}

.cambry-loading__spinner {
  /* Default 'md' diameter — overridable via the size modifiers below.
     A border-based spinner reaches first paint immediately (no image
     fetch, no SVG parse) and the rotation runs from frame 0. */
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 3px solid var(--color-surface);
  border-top-color: var(--color-accent);
  animation-name: cambry-spinner-rotate;
  animation-duration: 0.9s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* Explicit zero animation-delay to satisfy the 200ms first-paint
     constraint even if a parent stylesheet later sets a non-zero
     default for animation-delay on bare descendants. */
  animation-delay: 0s;
}

.cambry-loading__spinner--sm {
  width: 24px;
  height: 24px;
  border-width: 2px;
}

.cambry-loading__spinner--lg {
  width: 56px;
  height: 56px;
  border-width: 4px;
}

.cambry-loading__label {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.95rem;
  font-weight: 400;
  color: var(--color-text-muted);
  letter-spacing: 0.01em;
  margin: 0;
}

@keyframes cambry-spinner-rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* Reduced-motion fallback. The rotation is decorative; users who have
   asked the system to minimise motion get a static ring instead. The
   200ms first-paint guarantee is unaffected. */
@media (prefers-reduced-motion: reduce) {
  .cambry-loading__spinner {
    animation: none;
    border-top-color: var(--color-accent);
  }
}

/* --------------------------------------------------------------------------
   Error state
   --------------------------------------------------------------------------
   Message + Retry button. Retry hit area is at least 44 × 44 CSS px to
   meet the cross-view tap-target minimum (Reqs 14.1 / 14.2).
   -------------------------------------------------------------------------- */
.cambry-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 3rem 1.5rem;
  min-height: 8rem;
  font-family: 'Yantramanav', sans-serif;
  text-align: center;
  color: var(--color-text-muted);
}

.cambry-error__message {
  /* Body copy uses muted text on the slate background; AAA contrast at
     this colour pairing per the palette commentary in styles.css. */
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-muted);
  max-width: 36rem;
  margin: 0;
}

.cambry-error__retry {
  /* Primary affordance riding the accent fill. min-width and min-height
     enforce the 44 × 44 tap target even when the label is short. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.6rem 1.4rem;
  border: none;
  border-radius: 999px;
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: background-color 0.2s ease, transform 0.2s ease;
}

.cambry-error__retry:hover {
  background-color: var(--color-accent);
  filter: brightness(1.08);
}

.cambry-error__retry:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.cambry-error__retry:active {
  transform: translateY(1px);
}

.cambry-error__retry:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   Empty state
   --------------------------------------------------------------------------
   Reserves an illustration slot above the message. When no illustration
   is rendered into the slot the :empty rule collapses it to zero box so
   the message sits flush against the top of the container without an
   orphan whitespace gap.
   -------------------------------------------------------------------------- */
.cambry-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 4rem 1.5rem;
  min-height: 8rem;
  font-family: 'Yantramanav', sans-serif;
  text-align: center;
  color: var(--color-text-muted);
}

.cambry-empty__illustration {
  /* Slot for an optional illustration. The factory only inserts an
     <img> child when callers pass an illustration path; an unused slot
     stays text-empty and collapses via the :empty rule below. */
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 240px;
}

.cambry-empty__illustration:empty {
  /* Collapse the slot when no illustration is provided — no padding,
     no margin, no gap remainder, so the message sits directly under
     the container edge. */
  display: none;
}

.cambry-empty__illustration > img,
.cambry-empty__illustration > svg {
  display: block;
  width: 100%;
  height: auto;
  max-height: 160px;
  opacity: 0.8;
}

.cambry-empty__message {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1.1rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-muted);
  max-width: 36rem;
  margin: 0;
}

/* ==========================================================================
   Canonical shared rules — extracted at end of Slice C (task 3.7)
   --------------------------------------------------------------------------
   The following rule sets were promoted from per-view stylesheets into
   this Components_Stylesheet once two views (Trips, Attendees) had both
   adopted them, satisfying Requirement 15.1's two-view threshold and
   Requirement 15.2's "single canonical home" rule. The duplicates were
   deleted from `trips.css` and `attendees.css` at the same time
   (Requirement 15.3 — they were not left as overrides).

   What lives here vs. in a per-view stylesheet:
     - `.cambry-card` surface + base slot containers (`__header`,
       `__title`, `__body`, `__meta`, `__feedback`, `__actions`) — shared
       by every view that mounts a card via the `renderCambryCard`
       factory in `components.js`.
     - `.cambry-input` chassis + checkbox variants — shared form-field
       primitive used by the Attendees view's search / add / edit forms
       and (post-extraction) by any future view that wants the same
       44 × 44 input treatment.
     - `.cambry-fab` floating-action pill (base + `--bottom-center` /
       `--bottom-right` anchors + `__icon` / `__label`) — emitted by
       `renderFloatingActionPill` in `components.js`. Today only the
       Trips view mounts a pill, but the markup originates in the
       shared module so the canonical home is here.

   What stays per-view:
     - `.cambry-card__status-badge` + `--active`: only Trips renders a
       status badge in the card's top-right corner; Attendees uses an
       `.attendee-card__admin-badge` instead. Trips_View specific.
     - `.cambry-card__action-btn` + variants: only Trips uses this
       selector for its View Details / Make Active controls. Attendees
       uses a different selector (`.attendees-btn`) for its Edit /
       Delete / Save / Cancel / Add buttons.
     - All `.trips-view*` / `.trip-create-form*` / `.trips-list` rules
       remain in `trips.css`.
     - All `.attendees-view*` / `.attendees-toolbar*` /
       `.attendees-add-form*` / `.attendee-card*` / `.attendee-edit-form*`
       rules remain in `attendees.css`.

   Tokens consumed (defined in styles.css :root):
     --color-bg          slate background
     --color-surface     card chrome
     --color-primary     chip/button base
     --color-accent      soft pink accent
     --color-accent-hover  hover variant of the accent
     --color-on-accent   dark text for accent fills
     --color-text        body copy on slate
     --color-text-muted  secondary text
     --color-error       inline-error tone
   Requirements covered: 15.1, 15.2, 15.3
   ========================================================================== */

/* --------------------------------------------------------------------------
   Cambry card surface
   --------------------------------------------------------------------------
   Resting treatment:
     - slate #1f2c38 background
     - 12px corner radius
     - 0 2px 8px rgba(0,0,0,0.25) resting shadow
     - Yantramanav for the trip / attendee name (sized on the header child)
   Hover treatment matches the Games view game-card:
     - translateY(-4px) over 200ms
     - shadow elevates to 0 6px 16px rgba(0,0,0,0.35)
   Active variant (Trips_View "Currently Active" trip):
     - 2px solid #f896ff accent border, painted via inset box-shadow ring
       so the card geometry matches non-active siblings exactly (no 2px
       nudge in the grid).
   -------------------------------------------------------------------------- */
.cambry-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  background-color: #1f2c38;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  /* The `transform` transition powers the hover lift; `box-shadow`
     transitions in lockstep so the elevation feels continuous. */
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  /* Callers can add their own border later without nudging the
     active-variant ring (which is painted via box-shadow, not border). */
  border: none;
}

@media (hover: hover) and (pointer: fine) {
  .cambry-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
  }
}

.cambry-card:focus-within {
  /* When any control inside the card receives keyboard focus, lift the
     card with the same shadow used for hover so keyboard users get the
     same affordance pointer users do. No transform here — keyboard
     navigation shouldn't shift the card under the focused control. */
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
}

.cambry-card--active {
  /* 2px solid soft-pink accent ring per Trips Req 1.7. Painted as an
     inset box-shadow so the card geometry matches non-active siblings
     exactly. */
  box-shadow:
    inset 0 0 0 2px var(--color-accent),
    0 2px 8px rgba(0, 0, 0, 0.25);
}

@media (hover: hover) and (pointer: fine) {
  .cambry-card--active:hover {
    box-shadow:
      inset 0 0 0 2px var(--color-accent),
      0 6px 16px rgba(0, 0, 0, 0.35);
  }
}

.cambry-card--disabled {
  opacity: 0.6;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Card slot containers — emitted by the renderCambryCard factory
   --------------------------------------------------------------------------
   These rules style the slot wrappers themselves, not their contents.
   Per-view stylesheets layer on top of these to position avatars,
   identity columns, status badges, etc. inside the slots.
   -------------------------------------------------------------------------- */
.cambry-card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.6rem;
  width: 100%;
}

/* Default title styling. Trips_View uses an <h3> with this class for
   the trip display name; Attendees_View overrides with its own
   .attendee-card__display-label rule when a different size is wanted. */
.cambry-card__title {
  font-family: 'Yantramanav', sans-serif;
  font-size: 18px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--color-text);
  margin: 0;
  /* Allow long titles to wrap rather than overflow the card. */
  overflow-wrap: anywhere;
  flex: 1 1 auto;
  min-width: 0;
}

.cambry-card__body {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.95rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-muted);
  margin: 0;
}

.cambry-card__meta {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--color-text-muted);
  letter-spacing: 0.01em;
  margin: 0;
}

.cambry-card__feedback {
  /* Reserved live region; collapses to zero height when empty so an
     idle card has no orphan whitespace. Per-view stylesheets layer on
     additional tone modifiers (e.g. .attendees-feedback--success). */
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
  min-height: 0;
}

.cambry-card__feedback:empty {
  display: none;
}

.cambry-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem; /* 8px — adjacent-control spacing per Req 14.3 */
  margin-top: auto; /* push actions to the bottom of the card */
}

/* Mobile spacing safety net — at < 900 CSS px both Trips and Attendees
   require 8 px minimum gaps between adjacent enabled controls (Req 14.1
   / 14.3). The base .cambry-card__actions rule above already sets
   gap: 0.5rem; this reinforces the row-gap when the actions wrap onto
   multiple lines on narrow screens. */
@media (max-width: 899px) {
  .cambry-card__actions {
    row-gap: 0.5rem;
    column-gap: 0.5rem;
  }
}

/* --------------------------------------------------------------------------
   .cambry-input — canonical Cambry form-field treatment
   --------------------------------------------------------------------------
   Tap-target: min-height 44 px (Req 14.1). The min-height applies on
   every viewport — the spec only requires it < 900 px but a single
   unconditional rule is simpler and a 44 px tap target is also a
   comfortable mouse-and-keyboard target.

   Width: ``width: 100%`` so a parent flex column controls the actual
   horizontal extent. Callers wanting auto-sized inputs (e.g. inline
   single-line edits) can override with a more specific selector.
   -------------------------------------------------------------------------- */
.cambry-input {
  min-height: 44px;
  width: 100%;
  padding: 0.55rem 0.75rem;
  border: 1px solid var(--color-primary);
  border-radius: 8px;
  background-color: var(--color-bg);
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.4;
  /* The default appearance on iOS Safari is a chunky beveled input;
     ``-webkit-appearance: none`` lets our padding rules dictate the
     box geometry so the 44 px min-height is actually visual height. */
  -webkit-appearance: none;
  appearance: none;
}

.cambry-input:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: 0 0 0 2px rgba(248, 150, 255, 0.25);
}

.cambry-input::placeholder {
  color: var(--color-text-muted);
  opacity: 0.7;
}

.cambry-input:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

/* Checkbox variant — native checkboxes ignore most of the input
   styling above, so we re-do the geometry to give the box a 22 × 22
   visual click area at desktop widths. On mobile (< 900 px) we bump
   the checkbox bounding rect to 44 × 44 so it independently meets the
   tap-target minimum (Reqs 14.1 / 14.2) — task 3.6 audit found the
   stand-alone checkbox rect was 22.4 × 22.4, below the threshold,
   even though the surrounding wrapper carried a 44 px min-height. */
.cambry-input--checkbox {
  width: 1.4rem;
  height: 1.4rem;
  min-height: 1.4rem;
  margin: 0;
  padding: 0;
  accent-color: var(--color-accent);
  flex-shrink: 0;
}

.cambry-input--checkbox-label {
  /* Sits next to the checkbox in the inline form. Clicks toggle the
     checkbox via the `for=` association, so the label is itself an
     interactive control and must meet the 44 × 44 tap-target minimum
     on mobile (task 3.6 audit). On desktop it stays compact. */
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--color-text);
  cursor: pointer;
  user-select: none;
}

/* Mobile tap-target enforcement (Reqs 14.1 / 14.2 — task 3.6 fix).
   At viewport widths < 900 CSS px, the checkbox and its label each
   get a bounding rectangle of at least 44 × 44 CSS px. The visible
   accent fill on the checkbox scales with the box, and the label
   adopts inline-flex with align-items: center so the visible text
   stays vertically centered inside its 44 px-tall hit area. */
@media (max-width: 899px) {
  .cambry-input--checkbox {
    min-width: 44px;
    min-height: 44px;
  }
  .cambry-input--checkbox-label {
    display: inline-flex;
    align-items: center;
    min-width: 44px;
    min-height: 44px;
  }
}

/* --------------------------------------------------------------------------
   Floating Action Pill — .cambry-fab
   --------------------------------------------------------------------------
   Mirrors the Games view's `voting-floating-submit` element so any
   shared primary-action affordance reads visually identical across
   views. Today only the Trips_View mounts a pill (Create Trip), but
   the markup originates from `renderFloatingActionPill` in
   `components.js`, so the canonical CSS lives alongside the other
   shared primitives. Centered horizontally, anchored to the bottom
   of the viewport with safe-area-inset awareness for devices with
   home-indicator notches.
   -------------------------------------------------------------------------- */
.cambry-fab {
  position: fixed;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* 44 × 44 minimum hit area per Req 14.1. min-* values are belt-and-
     suspenders alongside the label-driven width. */
  min-width: 44px;
  min-height: 44px;
  padding: 0.85rem 1.5rem;
  border: none;
  border-radius: 999px;
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: transform 0.2s ease-out, box-shadow 0.2s ease, opacity 0.2s ease;
  /* Soft pink halo so the pill feels lifted off the cards beneath it,
     matching the Games view treatment exactly. */
  box-shadow:
    0 6px 18px rgba(248, 150, 255, 0.25),
    0 2px 4px rgba(0, 0, 0, 0.4);
}

/* Bottom-center anchor (Games view parity, Trips Req 2.3). */
.cambry-fab--bottom-center {
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
}

@media (hover: hover) and (pointer: fine) {
  .cambry-fab--bottom-center:hover:not(:disabled) {
    transform: translateX(-50%) translateY(-2px);
    box-shadow:
      0 10px 24px rgba(248, 150, 255, 0.35),
      0 3px 6px rgba(0, 0, 0, 0.4);
  }
}

/* Bottom-right anchor — reserved for future callers; the second
   `position` value `renderFloatingActionPill` honors keeps a paired
   stylesheet here so any future caller doesn't need new CSS. */
.cambry-fab--bottom-right {
  bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
  right: 1rem;
}

@media (hover: hover) and (pointer: fine) {
  .cambry-fab--bottom-right:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow:
      0 10px 24px rgba(248, 150, 255, 0.35),
      0 3px 6px rgba(0, 0, 0, 0.4);
  }
}

.cambry-fab:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.cambry-fab:disabled {
  cursor: progress;
  opacity: 0.85;
}

.cambry-fab__icon {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
}

.cambry-fab__icon:empty {
  display: none;
}

.cambry-fab__label {
  font-weight: 600;
}
