/* ==========================================================================
   Black Cambry 2026 — Trip Detail View (Slice D)
   --------------------------------------------------------------------------
   Per-view stylesheet for the admin Trip Detail page (`#trip-detail?id=…`).

   Loaded AFTER components.css and AFTER attendees.css. The canonical
   .cambry-card surface, slot wrappers, .cambry-input form-field
   treatment, and .cambry-fab floating-pill rules live in components.css
   (extracted in task 3.7 per Requirement 15.1). What lives here is
   Trip_Detail_View specific:
     - The view shell (`.trip-detail`, `.trip-detail__layout`).
     - The Lifecycle_Stepper (`.trip-detail__stepper*`).
     - The two-column layout media query (≥900 px).
     - The Mini_Game_Card wrapper (`.trip-detail__mini-game-card`) that
       neutralizes the Games view's interactive affordances when its
       card markup is reused here.
     - The Lifecycle_Transition_Card surface and its disabled treatment.
     - The Add Expense primary CTA.
     - The inline nickname-edit form (preserves the legacy markup so
       no JS-side change is required for the visual flip).

   The Trip Roster panel ships in Slice E; its CSS will land here in
   that slice. Expenses rewiring is Slice F.

   Tokens consumed (defined in styles.css :root):
     --color-bg          #1f2c38   slate background
     --color-surface     #2a3a4a   card chrome, one step lighter than bg
     --color-primary     #3a526a   chip/button base
     --color-accent      #f896ff   soft pink accent
     --color-accent-hover #ffb1ff  lighter pink for :hover
     --color-on-accent   #1f2c38   dark text for accent fills
     --color-text        #e8edf0   body copy on slate
     --color-text-muted  #a6b7bf   secondary text
     --color-error       #ff8aa1   inline-error tone
     --color-success     #7fd197   inline-success tone

   Typography: Yantramanav (loaded in index.html).
   Requirements covered: 3.1, 3.2, 3.3, 3.5, 4.1, 4.2, 4.3, 5.1,
                         6.1, 6.4, 14.1
   ========================================================================== */

/* --------------------------------------------------------------------------
   View shell
   --------------------------------------------------------------------------
   The Trip Detail view stacks a back link, the Lifecycle_Stepper, and a
   responsive two-column body region (lifecycle controls left, metadata
   right at ≥900 px; single-column below). Bottom padding leaves room
   so any Floating Action Pill mounted from a sibling view (e.g. Trips)
   does not visually collide while the user is briefly on this view.
   -------------------------------------------------------------------------- */
.trip-detail {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  padding: 1.25rem 1rem 4rem;
  max-width: 1200px;
  margin: 0 auto;
  width: 100%;
}

.trip-detail__back {
  /* Inline-flex with explicit min-height and horizontal padding so the
     "Back to Trips" link is an interactive control with a bounding
     rectangle of at least 44 × 44 CSS px (Reqs 14.1 / 14.2 — task 4.7
     audit). The visible glyph (the text "← Back to Trips") is
     unchanged; the hit area is extended via padding and min-height
     so screen-reader / keyboard / touch users all get the same
     comfortable target. */
  display: inline-flex;
  align-items: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  margin-left: -0.75rem; /* counter the padding so the visible text
                            stays flush with the column edge */
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-decoration: none;
  align-self: flex-start;
  border-radius: 4px;
}

.trip-detail__back:hover,
.trip-detail__back:focus-visible {
  color: var(--color-text);
  outline: none;
}

.trip-detail__back:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* --------------------------------------------------------------------------
   Lifecycle_Stepper
   --------------------------------------------------------------------------
   Horizontal flex container of step nodes connected by 1 px lines (the
   connectors are ::after pseudo-elements on each step except the last).
   Steps are READ-ONLY (Requirement 3.4): no buttons, no links, no
   onclick handlers attached. Steps render their text as real text so
   screen readers can read the lifecycle.

   Visual states (driven by the modifier class the JS emits):
     - --completed: muted text, slate-fill dot
     - --current:   accent fill + on-accent text, bold
     - --future:    surface fill, muted text
   The cancelled branch (Req 3.3) renders as a separate flex item below
   the main row, anchored to the row by a vertical 1 px connector. When
   status === 'cancelled' the JS emits ONLY the cancelled node and skips
   highlighting any of the six main-sequence steps.
   -------------------------------------------------------------------------- */
.trip-detail__stepper {
  /* <ol> reset — strip the default left margin and bullet so the
     stepper reads as a row of pills, not a numbered list. The ordinal
     semantics survive in the DOM tree for assistive tech. */
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0;
  /* Row separation reserved by margin-bottom; the row that holds the
     cancelled branch piggy-backs on this same gap. */
  row-gap: 0.5rem;
}

.trip-detail__stepper-step {
  /* Base treatment shared by every step. Modifier classes override
     background/color. The step is a flex item that grows to share the
     available width evenly so the row stays balanced on wide screens
     and wraps gracefully on narrow ones. */
  position: relative;
  flex: 1 1 0;
  min-width: 7rem;
  padding: 0.55rem 0.85rem;
  background-color: var(--color-surface);
  color: var(--color-text-muted);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  text-align: center;
  text-transform: capitalize;
  border-radius: 999px;
  /* Stepper steps are read-only (Req 3.4). cursor: default + no hover
     transform makes that visually unmistakable. The OS-native focus
     ring is preserved if a keyboard-accessible tooltip is wired by
     callers (today: title attributes only — no focus target). */
  cursor: default;
  user-select: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 1 px connector line between consecutive steps. The pseudo element
   sits in the gap between the current step and its sibling so the
   visual line tracks both ends precisely. The :last-child rule below
   removes the connector from the final main-sequence step so the row
   ends cleanly. */
.trip-detail__stepper-step::after {
  content: '';
  position: absolute;
  top: 50%;
  /* Anchor the connector to the right edge of the step pill so it
     bridges the 0-gap layout above into the next pill's left edge. */
  right: -1rem;
  width: 1rem;
  height: 1px;
  background-color: var(--color-primary);
  transform: translateY(-50%);
  pointer-events: none;
}

.trip-detail__stepper-step:last-child::after {
  display: none;
}

/* Force a small horizontal gap between pills so the connector has
   room to live. The negative right offset on the ::after places the
   line in this margin so the math stays simple. */
.trip-detail__stepper-step + .trip-detail__stepper-step {
  margin-left: 1rem;
}

/* Step state modifiers --------------------------------------------------- */

.trip-detail__stepper-step--completed {
  /* Completed steps look settled: muted text on slate. No accent. */
  background-color: var(--color-surface);
  color: var(--color-text-muted);
  font-weight: 500;
}

.trip-detail__stepper-step--current {
  /* The single highlighted step. Accent fill + on-accent text gives
     AAA contrast (per the palette commentary in styles.css). aria-
     current="step" is set in the JS, not here. */
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  font-weight: 700;
  /* Halo so the current step lifts off the row even on the busiest
     viewports. Matches the Floating_Action_Pill halo intensity. */
  box-shadow: 0 4px 12px rgba(248, 150, 255, 0.25);
}

.trip-detail__stepper-step--future {
  /* Future steps render in the same surface fill as completed steps
     but with slightly lower visual weight so the row reads as
     "behind / current / ahead" without three visually distinct
     fills competing for attention. */
  background-color: var(--color-surface);
  color: var(--color-text-muted);
  opacity: 0.8;
  font-weight: 500;
}

/* Cancelled branch — rendered ONLY when status === 'cancelled'. Anchored
   to the main row by a vertical 1 px connector dropping from the row.
   The branch lives on its own flex line so it doesn't disrupt the
   six-step main row layout. */
.trip-detail__stepper-cancelled {
  position: relative;
  flex: 0 0 auto;
  /* Push the cancelled node onto its own line. flex-basis 100% +
     flex-wrap: wrap on the parent makes this a clean break. */
  flex-basis: 100%;
  display: flex;
  justify-content: center;
  /* Top padding leaves room for the vertical connector. */
  padding: 1.5rem 0 0;
  margin-top: 0.25rem;
}

.trip-detail__stepper-cancelled::before {
  /* Vertical 1 px connector dropping from the main row down into the
     cancelled pill. */
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 1px;
  height: 1.25rem;
  background-color: var(--color-primary);
  transform: translateX(-50%);
}

.trip-detail__stepper-cancelled-pill {
  /* The cancelled chip itself — same chassis as a stepper step but
     emphasized with the error tone so the branch reads as terminal. */
  display: inline-flex;
  align-items: center;
  padding: 0.55rem 1rem;
  background-color: var(--color-surface);
  color: var(--color-error);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: capitalize;
  border-radius: 999px;
  /* 1 px outline in the error tone so the chip reads as branched
     terminal content rather than a regular muted step. */
  border: 1px solid var(--color-error);
}

/* --------------------------------------------------------------------------
   Two-column layout (Req 4.1 / 4.2 / 4.3)
   --------------------------------------------------------------------------
   At ≥900 px: lifecycle controls left (40 %), inline-editable metadata
   right (60 %). Below 900 px: single column with the lifecycle controls
   above the metadata. The layout flip is non-animated (no `transition`
   on the grid template) so an in-progress inline edit is not jiggled
   around when the viewport crosses the breakpoint.

   The single-column layout is the default (outside the @media block)
   so when window.innerWidth is undefined / 0 the safe default falls
   through naturally without a JS check (Req 4.5).
   -------------------------------------------------------------------------- */
.trip-detail__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.25rem;
  /* No `transition` on `grid-template-columns` — the layout flip is
     non-animated by design (Req 4.3). */
}

@media (min-width: 900px) {
  .trip-detail__layout {
    /* 40 / 60 split. minmax(0, …) so long children can't stretch a
       column beyond its share. */
    grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
    align-items: start;
  }
}

.trip-detail__layout-left,
.trip-detail__layout-right {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  /* min-width: 0 lets long content (e.g. URLs) ellipsis-truncate via
     overflow on children rather than blowing out the grid column. */
  min-width: 0;
}

/* --------------------------------------------------------------------------
   Inline-editable nickname (preserved from the legacy implementation)
   --------------------------------------------------------------------------
   The current trip_detail.js toggles `[data-mode]` visibility — read mode
   shows the heading + Edit button, edit mode shows the input + Save /
   Cancel. Both modes are mounted at render time; the JS flips the
   `hidden` attribute. We restyle the chassis to the Cambry palette
   without rewiring the markup.
   -------------------------------------------------------------------------- */
.trip-detail__name-block {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.trip-detail__name-read {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.trip-detail__name {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--color-text);
  letter-spacing: 0.01em;
  margin: 0;
  /* Allow long nicknames to wrap rather than blow out the column. */
  overflow-wrap: anywhere;
  flex: 1 1 auto;
  min-width: 0;
}

.trip-detail__name-edit-btn {
  /* Outline-style button — secondary affordance. 44 × 44 minimum hit
     area per Req 14.1. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem 1rem;
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease,
              transform 0.15s ease;
}

.trip-detail__name-edit-btn:hover:not(:disabled) {
  background-color: rgba(248, 150, 255, 0.08);
  border-color: var(--color-accent);
}

.trip-detail__name-edit-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.trip-detail__name-edit-btn:disabled,
.trip-detail__name-edit-btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
}

.trip-detail__name-edit {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.trip-detail__name-edit[hidden] {
  display: none;
}

.trip-detail__name-label {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0;
}

.trip-detail__name-input {
  /* Mirrors .cambry-input geometry without consuming that class to
     avoid coupling: the inline edit is preserved as a legacy DOM
     shape per Req 4.4. */
  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;
  -webkit-appearance: none;
  appearance: none;
}

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

.trip-detail__name-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem; /* 8 px between adjacent controls (Req 14.3) */
}

.trip-detail__name-save-btn,
.trip-detail__name-cancel-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.55rem 1rem;
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, color 0.15s ease,
              border-color 0.15s ease;
}

.trip-detail__name-save-btn {
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  border-color: var(--color-accent);
}

.trip-detail__name-save-btn:hover:not(:disabled) {
  background-color: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

.trip-detail__name-save-btn:disabled {
  background-color: var(--color-primary);
  color: var(--color-text-muted);
  border-color: var(--color-primary);
  cursor: not-allowed;
}

.trip-detail__name-cancel-btn:hover:not(:disabled) {
  background-color: rgba(248, 150, 255, 0.08);
  border-color: var(--color-accent);
}

.trip-detail__name-error {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
}

.trip-detail__name-error[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   Trip metadata block (status badge, created-at, spreadsheet link)
   -------------------------------------------------------------------------- */
.trip-detail__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  color: var(--color-text-muted);
}

/* Generic status badge shared with .cambry-card__status-badge in
   trips.css. Re-declared here at .status-badge selector level so the
   legacy markup the trip-detail view emits today (`<span class="status-
   badge status-{status}">`) keeps working. */
.trip-detail__meta .status-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background-color: var(--color-primary);
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

.trip-detail__created {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-text-muted);
}

.trip-detail__spreadsheet {
  margin: 0;
}

.trip-detail__spreadsheet-link {
  display: inline-flex;
  align-items: center;
  /* 44 × 44 minimum hit area (Req 14.1 — task 4.7 audit). The label
     "Open Spreadsheet" naturally exceeds 44 px in width; the
     min-width is belt-and-suspenders against future label changes. */
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem 1rem;
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.trip-detail__spreadsheet-link:hover,
.trip-detail__spreadsheet-link:focus-visible {
  background-color: rgba(248, 150, 255, 0.08);
  border-color: var(--color-accent);
  outline: none;
}

.trip-detail__spreadsheet-link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   Mini_Game_Card wrapper
   --------------------------------------------------------------------------
   Wraps the Games view's `.game-card` markup (re-exported from games.js
   via `renderGameCard`) so the chosen game renders with the same city
   image, team logos, date, and time as the Games view (Req 5.1). The
   wrapper neutralizes the Games view's interactive affordances so the
   mini card is not a focusable activator and does not lift on hover.
   -------------------------------------------------------------------------- */
.trip-detail__mini-game-card {
  /* The wrapper is a neutral container — its child .game-card carries
     the visual surface treatment from games.css. The wrapper just
     constrains width and stops pointer affordances. */
  display: block;
  width: 100%;
  /* Cap the mini card so it doesn't dominate the metadata column on
     very wide screens. The Games view card scales fluidly under a
     grid, but in this single-card context we want a sensible ceiling. */
  max-width: 28rem;
}

.trip-detail__mini-game-card .game-card {
  /* Stepper-style cursor + no-lift hover so the mini card reads as
     informational, not interactive. */
  cursor: default;
}

@media (hover: hover) and (pointer: fine) {
  .trip-detail__mini-game-card .game-card:hover {
    /* Suppress the games-view hover transform / shadow elevation. */
    transform: none;
    box-shadow: var(--shadow);
  }
}

.trip-detail__mini-game-card .game-card[role="button"] {
  /* Defensively neutralize pointer events on a button-styled card so
     a stray click doesn't trigger any hidden voting flow leaking in
     from games.js (the JS wrapper also strips role/tabindex/aria-
     pressed at insertion; this is belt-and-suspenders). */
  pointer-events: none;
}

/* If a `--selected` modifier ever leaks in (e.g. from a future games.js
   change that ships before this stylesheet is updated), suppress its
   pink-ring treatment so the mini card never reads as "selected". */
.trip-detail__mini-game-card .game-card--selected {
  box-shadow: var(--shadow);
}

.trip-detail__mini-game-card .game-card--selected::after {
  /* The selected variant in games.css uses a pseudo-element overlay
     for the pink ring; nuke it in the mini context. */
  display: none !important;
}

/* Loading placeholder rendered while GET /api/games is in flight and
   chosen_game_index is non-null (Req 5.5). The wrapper keeps the
   mini-card slot reserved so the layout doesn't reflow when the card
   eventually paints. */
.trip-detail__mini-game-loading {
  display: block;
  width: 100%;
  max-width: 28rem;
}

/* Textual fallback when GET /api/games resolved but the chosen index
   is missing from the response (Req 5.4). Renders in a calm slate
   surface so it reads as a notice rather than an error. */
.trip-detail__chosen-fallback {
  margin: 0;
  padding: 0.75rem 1rem;
  background-color: var(--color-surface);
  border-radius: 8px;
  border-left: 3px solid var(--color-text-muted);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  color: var(--color-text-muted);
  line-height: 1.4;
}

/* --------------------------------------------------------------------------
   Game picker (radio list) — preserved for `voting` status
   --------------------------------------------------------------------------
   When status === 'voting' and chosen_game_index is null, the existing
   radio-list picker renders. When status === 'voting' AND chosen_game_
   index is non-null, BOTH the Mini_Game_Card and the radio-list picker
   render so the admin can switch the pinned selection (Req 5.3).
   -------------------------------------------------------------------------- */
.trip-detail__game-pick {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 1rem;
  background-color: var(--color-surface);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

.trip-detail__game-pick-title {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  margin: 0;
}

.trip-detail__game-pick-current {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0;
}

.trip-detail__game-pick-current--unset {
  color: var(--color-error);
}

.trip-detail__game-pick-form {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.trip-detail__game-pick-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  /* 0.5 rem = 8 px minimum spacing between adjacent radio-list items
     (Req 14.3 — task 4.7 audit; was 0.4 rem = 6.4 px previously,
     below the threshold). */
  gap: 0.5rem;
}

.trip-detail__game-pick-item {
  margin: 0;
  padding: 0;
}

.trip-detail__game-pick-label {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  padding: 0.4rem 0.6rem;
  border-radius: 8px;
  cursor: pointer;
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  color: var(--color-text);
  transition: background-color 0.15s ease;
}

.trip-detail__game-pick-label:hover {
  background-color: rgba(255, 255, 255, 0.04);
}

.trip-detail__game-pick-label input[type="radio"] {
  width: 1.2rem;
  height: 1.2rem;
  accent-color: var(--color-accent);
  flex-shrink: 0;
  margin: 0;
}

/* Mobile tap-target enforcement (Reqs 14.1 / 14.2 — task 4.7 audit).
   At viewport widths < 900 CSS px, the chosen-game radio input gets
   a bounding rectangle of at least 44 × 44 CSS px in addition to the
   wrapping label's 44 px min-height. The visible accent fill on the
   radio glyph is unchanged (still 1.2 rem); only the hit rectangle
   extends. Mirrors the .cambry-input--checkbox pattern in
   components.css extracted in task 3.6. */
@media (max-width: 899px) {
  .trip-detail__game-pick-label input[type="radio"] {
    min-width: 44px;
    min-height: 44px;
  }
}

.trip-detail__game-pick-text {
  flex: 1 1 auto;
  min-width: 0;
}

.trip-detail__game-pick-empty {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0;
}

.trip-detail__game-pick-controls {
  display: flex;
  gap: 0.5rem;
}

.trip-detail__game-pick-save-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.55rem 1rem;
  border: 1px solid var(--color-accent);
  border-radius: 999px;
  background-color: var(--color-accent);
  color: var(--color-on-accent);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.trip-detail__game-pick-save-btn:hover:not(:disabled) {
  background-color: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
}

.trip-detail__game-pick-save-btn:disabled {
  background-color: var(--color-primary);
  color: var(--color-text-muted);
  border-color: var(--color-primary);
  cursor: not-allowed;
}

.trip-detail__game-pick-error {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
}

.trip-detail__game-pick-error[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   Lifecycle Transition Cards (Req 6.4)
   --------------------------------------------------------------------------
   Each valid next-state transition renders as a Lifecycle_Transition_
   Card: bold .cambry-card-style chrome (slate background, 12 px radius,
   soft shadow) with a destination-state primary label and a rule sub-
   label describing the policy the transition enforces. The card itself
   IS the activation target (a <button>); the 44 × 44 minimum hit area
   is enforced by min-* values on the surface.

   Disabled treatment: the gated `voting → committed` card when chosen_
   game_index is null. opacity 0.55, cursor not-allowed, no hover
   transform.

   Destructive variants (`cancelled`, `archived`) get a red-tinged
   border so the destination reads as terminal at a glance, but the
   confirmation gate happens in JS via window.confirm (Req 6.5).
   -------------------------------------------------------------------------- */
.trip-detail__lifecycle {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.trip-detail__lifecycle-title {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  margin: 0;
}

.trip-detail__lifecycle-empty {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  color: var(--color-text-muted);
  margin: 0;
}

.trip-detail__lifecycle-cards {
  display: flex;
  flex-direction: column;
  gap: 0.6rem; /* 8 px+ between adjacent enabled cards (Req 14.3) */
  margin: 0;
  padding: 0;
  list-style: none;
}

.trip-detail__transition-card {
  /* Surface — matches the Cambry card chassis from components.css
     (slate background, 12 px radius, soft shadow). The whole card is
     the activator so the bounding rectangle is the hit area. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.25rem;
  width: 100%;
  /* 44 × 44 minimum hit area (Req 6.4 / 14.1) — though the natural
     padding plus the two-line label already overshoots this, the
     min-* values are belt-and-suspenders. */
  min-width: 44px;
  min-height: 44px;
  padding: 0.85rem 1rem;
  background-color: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-primary);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  font-family: 'Yantramanav', sans-serif;
  text-align: left;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease,
              transform 0.15s ease, box-shadow 0.2s ease;
}

@media (hover: hover) and (pointer: fine) {
  .trip-detail__transition-card:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
    border-color: var(--color-accent);
  }
}

.trip-detail__transition-card:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.trip-detail__transition-card:active:not(:disabled) {
  transform: translateY(1px);
}

/* Disabled treatment (Req 6.4 / 6.8). Used by the gated voting →
   committed card when chosen_game_index is null. The hover lift is
   suppressed and the card reads as inert without becoming
   keyboard-unreachable (so the rule sub-label is still readable). */
.trip-detail__transition-card:disabled,
.trip-detail__transition-card[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  border-color: var(--color-primary);
  /* Suppress the hover transform even when the cursor is hovered
     over a disabled card. */
  transform: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

/* Destructive variant — `cancelled` and `archived` destinations. The
   border tints toward the error tone so the destination reads as
   terminal at a glance. The hover treatment swaps the accent border
   for the error tone so the affordance still reads as a bold action,
   not a soft one. */
.trip-detail__transition-card--destructive {
  border-color: rgba(255, 138, 161, 0.4);
}

@media (hover: hover) and (pointer: fine) {
  .trip-detail__transition-card--destructive:hover:not(:disabled) {
    border-color: var(--color-error);
  }
}

.trip-detail__transition-card-label {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 700;
  color: var(--color-text);
  letter-spacing: 0.01em;
}

.trip-detail__transition-card--destructive
.trip-detail__transition-card-label {
  color: var(--color-error);
}

.trip-detail__transition-card-sub-label {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  font-weight: 400;
  line-height: 1.4;
  color: var(--color-text-muted);
}

.trip-detail__transition-card-error {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0.4rem 0 0;
}

.trip-detail__transition-card-error[hidden] {
  display: none;
}

/* Legacy lifecycle error slot for the section-level error rendered by
   the legacy code path. Kept for backwards compatibility — the new
   per-card error slot above is preferred. */
.trip-detail__lifecycle-error {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
}

.trip-detail__lifecycle-error[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   Add Expense primary CTA (Req 6.1 / 6.2 / 6.3)
   --------------------------------------------------------------------------
   Rendered ONLY when status === 'committed'. Cambry primary-button
   treatment (accent fill, on-accent text, 44 × 44 minimum hit area).
   The native anchor's hashchange navigation triggers the expenses
   view's hashchange handler, so the markup stays an <a href="#expenses">.
   -------------------------------------------------------------------------- */
.trip-detail__pre-expense {
  margin: 0;
}

.trip-detail__add-expense-btn {
  /* Primary affordance — accent fill, on-accent text. The anchor
     element is laid out as an inline-flex pill so the 44 × 44 hit
     area is inclusive of padding. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.85rem 1.5rem;
  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;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, transform 0.15s ease,
              box-shadow 0.2s ease;
  box-shadow:
    0 6px 18px rgba(248, 150, 255, 0.25),
    0 2px 4px rgba(0, 0, 0, 0.4);
}

.trip-detail__add-expense-btn:hover {
  background-color: var(--color-accent-hover);
  transform: translateY(-2px);
  box-shadow:
    0 10px 24px rgba(248, 150, 255, 0.35),
    0 3px 6px rgba(0, 0, 0, 0.4);
}

.trip-detail__add-expense-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.trip-detail__add-expense-btn:active {
  transform: translateY(1px);
}

/* --------------------------------------------------------------------------
   Mobile spacing safety net (Reqs 14.1 / 14.3)
   --------------------------------------------------------------------------
   At < 900 px every adjacent enabled control needs an 8 px minimum
   horizontal and vertical gap. The button rows above already use
   `gap: 0.5rem` (= 8 px); this block reinforces row-gap when controls
   wrap onto multiple lines on narrow screens.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  .trip-detail__name-controls,
  .trip-detail__game-pick-controls,
  .trip-detail__lifecycle-cards {
    row-gap: 0.5rem;
    column-gap: 0.5rem;
  }
}

/* ==========================================================================
   Slice E — Trip Roster panel
   --------------------------------------------------------------------------
   The Trip Roster panel mounts in the right column of the two-column
   layout (after the game picker), or below the metadata in the single-
   column layout below 900 px. The panel chassis is a `<section>` with
   a stable heading + a content slot the JS rewrites; the slot
   progresses through Loading_State → Empty_State or rendered list,
   plus Error_State on failure.

   Tap-target enforcement (Slice E task 5.5 audit):
     - Roster_Picker `<input>` inherits 44 px min-height from
       `.cambry-input` in components.css.
     - Each `<li role="option">` listbox option gets min-height 44 px
       so the click target fills the row even when the visible label
       is short.
     - Each Remove button on each roster card is 44 × 44 minimum.

   8 px minimum spacing is enforced via the same `gap: 0.5rem` value
   used elsewhere in this stylesheet for adjacent-control rows. The
   panel itself is separated from the rest of the right column by the
   parent's existing `gap: 1rem` (= 16 px), comfortably above the 8 px
   minimum.

   Requirements covered: 7.1, 7.2, 7.3, 7.7, 7.8, 13.1, 13.2, 13.3,
                          13.4, 14.1, 14.2, 14.3
   ========================================================================== */

.trip-detail__roster {
  /* The panel chassis — a vertical flex column matching the layout-
     right child gap. The heading + content slot stack with comfortable
     internal spacing. The panel is a `<section>` so it lands as a
     landmark for assistive tech. */
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  padding: 1rem;
  background-color: var(--color-surface);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}

.trip-detail__roster-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.trip-detail__roster-title {
  font-family: 'Yantramanav', sans-serif;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  margin: 0;
}

/* The visually-hidden announcer span — it's never visible but carries
   `aria-live="polite"` so the locked-state tooltip text it receives on
   focus is announced to assistive tech. The `clip-path` pattern is the
   modern accessible-hide recipe; it preserves the element in the DOM
   so the live region still works. */
.trip-detail__roster-locked-announcer {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.trip-detail__roster-content {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

/* --------------------------------------------------------------------------
   Roster_Picker — combobox + popover listbox
   --------------------------------------------------------------------------
   The picker is rendered above the rendered roster list. Its layout is
   a labelled <input> followed by a popover <ul role="listbox">. The
   listbox is `position: absolute` so it overlays content beneath the
   input rather than pushing the list down on every focus.
   -------------------------------------------------------------------------- */
.trip-detail__roster-picker {
  /* Position relative so the listbox can absolutely-position itself
     relative to the picker rather than the viewport. */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.trip-detail__roster-picker-label {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0;
}

.trip-detail__roster-picker-input {
  /* `.cambry-input` already supplies the 44 × 44 chassis. The local
     selector exists so per-view tweaks (focus ring tone, padding) can
     be added without touching the canonical rule. */
}

.trip-detail__roster-picker-input:disabled {
  /* Locked-state visual treatment — slightly dimmed but still readable
     so the placeholder ("Add someone…") doesn't disappear. */
  opacity: 0.6;
  cursor: not-allowed;
}

/* Inline-error row used when the master attendees fetch fails: a
   disabled input plus a Retry button on the same line. */
.trip-detail__roster-picker-error-row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: 0.5rem; /* 8 px between input and Retry button (Req 14.3) */
}

.trip-detail__roster-picker-error-row .trip-detail__roster-picker-input {
  flex: 1 1 auto;
  min-width: 0;
}

.trip-detail__roster-picker-retry-btn {
  /* 44 × 44 minimum hit area (Req 14.1). The label "Retry" naturally
     exceeds 44 px in width; the min-* values are belt-and-suspenders. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem 1rem;
  border: 1px solid var(--color-primary);
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-text);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.trip-detail__roster-picker-retry-btn:hover,
.trip-detail__roster-picker-retry-btn:focus-visible {
  background-color: rgba(248, 150, 255, 0.08);
  border-color: var(--color-accent);
  outline: none;
}

.trip-detail__roster-picker-retry-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.trip-detail__roster-picker-error {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
}

/* Popover listbox — overlay below the input. Visible only when the
   `[hidden]` attribute is removed by the JS. We use `position:
   absolute` so the popover doesn't push the rendered roster list
   downward when it opens. */
.trip-detail__roster-picker-listbox {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 10;
  margin: 0.25rem 0 0;
  padding: 0.25rem 0;
  list-style: none;
  background-color: var(--color-bg);
  border: 1px solid var(--color-primary);
  border-radius: 8px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
  /* Cap the popover height so the listbox doesn't overflow the
     viewport on tiny screens. The listbox scrolls internally. */
  max-height: 16rem;
  overflow-y: auto;
}

.trip-detail__roster-picker-listbox[hidden] {
  display: none;
}

.trip-detail__roster-picker-option {
  /* Each option is a clickable row. min-height 44 px (Req 5.5 / 14.1
     audit) makes the click target fill the row even when the visible
     label is short. The padding gives the cursor breathing room and
     keeps the option text away from the popover edges. */
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-height: 44px;
  padding: 0.55rem 0.85rem;
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.9rem;
  color: var(--color-text);
  cursor: pointer;
  /* Avoid a hover transition; the highlight class drives the visual
     feedback so keyboard and pointer users get the same treatment. */
}

.trip-detail__roster-picker-option:hover,
.trip-detail__roster-picker-option--highlighted {
  background-color: rgba(248, 150, 255, 0.08);
  color: var(--color-text);
}

.trip-detail__roster-picker-option--inert {
  /* Used for the "Loading attendees…" and "No matches." placeholder
     rows. Reads as informational, not as a clickable option. */
  cursor: default;
  color: var(--color-text-muted);
  font-style: italic;
}

.trip-detail__roster-picker-option--inert:hover {
  background-color: transparent;
}

.trip-detail__roster-picker-option-name {
  font-weight: 600;
  letter-spacing: 0.01em;
}

.trip-detail__roster-picker-option-meta {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  /* Allow long emails / city strings to wrap rather than overflow the
     popover horizontally. */
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
   Roster panel feedback area
   --------------------------------------------------------------------------
   Panel-level feedback: POST errors render here. Per-card feedback
   (DELETE errors) renders into the per-card `.cambry-card__feedback`
   slot via `data-feedback-for="<email>"`.
   -------------------------------------------------------------------------- */
.trip-detail__roster-feedback {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
  margin: 0;
  min-height: 0;
}

.trip-detail__roster-feedback:empty {
  display: none;
}

/* --------------------------------------------------------------------------
   Roster list — one .cambry-card per entry
   -------------------------------------------------------------------------- */
.trip-detail__roster-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem; /* 8 px between adjacent cards (Req 14.3) */
}

.trip-detail__roster-list-item {
  margin: 0;
}

.trip-detail__roster-card {
  /* The card chassis is supplied by `.cambry-card` in components.css.
     Per-view tweaks: tighter padding so the card reads as a roster
     entry rather than a page-level affordance, and a subdued shadow
     (the panel surface itself already carries one). */
  padding: 0.75rem 0.85rem;
}

.trip-detail__roster-card-identity {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  min-width: 0;
}

.trip-detail__roster-card-name {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text);
  margin: 0;
  overflow-wrap: anywhere;
}

.trip-detail__roster-card-email {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin: 0;
  overflow-wrap: anywhere;
}

.trip-detail__roster-card-city {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0;
  overflow-wrap: anywhere;
}

.trip-detail__roster-remove-btn {
  /* Outline-style danger button. 44 × 44 minimum hit area (Req 14.1
     / Slice E task 5.5 audit). The error tone reads as destructive
     without screaming for attention on the panel. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0.5rem 1rem;
  border: 1px solid rgba(255, 138, 161, 0.4);
  border-radius: 999px;
  background-color: transparent;
  color: var(--color-error);
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

.trip-detail__roster-remove-btn:hover:not(:disabled),
.trip-detail__roster-remove-btn:focus-visible:not(:disabled) {
  background-color: rgba(255, 138, 161, 0.08);
  border-color: var(--color-error);
  outline: none;
}

.trip-detail__roster-remove-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

.trip-detail__roster-remove-btn:disabled,
.trip-detail__roster-remove-btn[aria-disabled="true"] {
  /* Locked-state treatment (Req 7.7): dimmed and inert. The tooltip
     is exposed via `title=` (pointer hover) and the focus announcer
     (keyboard / screen reader). */
  opacity: 0.55;
  cursor: not-allowed;
  border-color: var(--color-primary);
  color: var(--color-text-muted);
}

/* Per-card feedback line (DELETE errors). The shared
   `.cambry-card__feedback` rule in components.css already collapses
   the slot when empty; this declaration just adds the trip-detail
   text styling for the inner span. */
.trip-detail__roster-card-feedback-text {
  font-family: 'Yantramanav', sans-serif;
  font-size: 0.85rem;
  color: var(--color-error);
}

.trip-detail__roster-card-feedback-text:empty {
  display: none;
}

/* --------------------------------------------------------------------------
   Empty state placeholder slot — shared Empty_State mounts in here
   --------------------------------------------------------------------------
   The list renderer emits `<div data-roster-empty="true">` when the
   roster has zero entries; the JS then mounts the shared Empty_State
   into the slot via `mountRosterEmptyStates`. The slot has no extra
   styling — the shared `.cambry-empty` rules in components.css carry
   the visual.
   -------------------------------------------------------------------------- */
.trip-detail__roster-empty {
  display: block;
}
