/* ============================================================
   campaigns.css — Campaigns + VIEW-ONLY sharing UI.

   Scopes: .campaigns (section root), .campaign-detail, the shared board, the
   share-picker / form modals, and the role/status badges. The READ-ONLY viewer
   reuses the .admin-view-overlay chrome from admin.css (banner with the accent
   rail), so this file does NOT re-declare the overlay — only a thin .camp-view-
   overlay marker hook is added there for clarity.

   All colors via tokens (base.css). No raw hex. OSR dark theme. WCAG AA: text on
   surface, focus-visible rings, never color-only state (badges carry a word).
   ============================================================ */

/* ----- page head ----- */
.campaigns__page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-lg);
}

.campaigns__head-actions {
  display: flex;
  gap: var(--space-sm);
  flex: 0 0 auto;
}

.campaigns__panel-title {
  margin: 0 0 var(--space-sm);
  font-family: var(--font-heading-ui);
  font-size: var(--text-h4);
  color: var(--color-heading);
}

/* ----- pending invites tray ----- */
.campaigns__invites-panel {
  margin-bottom: var(--space-lg);
  border-left: 3px solid var(--color-accent);
}

.campaigns__invites {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Detail-view rows follow the CORE .list-row standard (components.css §B):
   rest = Mortar Line border on surface; hover / focus-within = accent border +
   surface lift. These rows are not selectable (no .is-selected), so the key
   behavior is the hover/focus highlight. Layout stays local; the visual accent
   tokens match .list-row exactly so there is one row look app-wide. */
.campaign-invite {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast);
}

.campaign-invite:hover,
.campaign-invite:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

.campaign-invite__info { flex: 1 1 14rem; min-width: 0; }

.campaign-invite__name {
  margin: 0;
  font-weight: 600;
  color: var(--color-text);
  overflow-wrap: anywhere;
}

.campaign-invite__desc {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

.campaign-invite__actions {
  display: flex;
  gap: var(--space-sm);
  flex: 0 0 auto;
}

/* ----- campaigns grid -----
   The roster now uses the shared .char-card family (H-2, 2026-06-27): the grid is
   .char-card-grid and each card is .char-card.campaign-card. Layout, hover/focus,
   medallion, name, meta and (absent) corner-delete all come from the shared card
   rules in components.css. .campaigns__grid is kept only as a harmless co-class on
   the grid node for back-compat selectors; no own layout. Campaigns have a glyph
   medallion (no portrait) and NO card-corner delete by design — delete/leave live
   in the detail view. */
.campaign-card .char-card__meta.campaign-card__meta {
  /* Let the role badge sit on the meta line without the default meta type styles
     overriding the badge chrome. */
  display: flex;
  align-items: center;
}

/* Campaign default image as the card media (batch-13, 2026-06-28) inside the shared
   5rem .char-card__portrait medallion. The default is an illustrative banner-style
   tile, so object-fit:contain shows the WHOLE image (cover would crop it into the
   circle); a hair of padding keeps it off the circular border. Until Ken uploads
   /images/campaigns/default.webp via the Image Coverage swap, this <img> 404s and
   img-fallback.js replaces it with the banner glyph (sized by the shared
   .char-card__portrait-fallback rules) — so the card looks unchanged today. */
.campaign-card .char-card__portrait .campaign-card__default-img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  padding: 0.35rem;
}

/* ----- detail sections ----- */
.campaign-section { margin-bottom: var(--space-lg); }

.campaign-section__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-sm);
}

.campaign-section__head .campaigns__panel-title { margin-bottom: 0; }

.campaign-section__hint {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-style: italic;
}

.campaign-section__actions {
  display: flex;
  gap: var(--space-sm);
  flex-wrap: wrap;
}

.campaign-section--danger {
  border-left: 3px solid var(--color-danger);
}

/* ----- members roster ----- */
.campaign-members__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Member roster row — CORE .list-row hover/focus standard (see invite row note). */
.campaign-member {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
  padding: var(--space-sm) var(--space-md);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast);
}

.campaign-member:hover,
.campaign-member:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

.campaign-member__who {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  min-width: 0;
  flex-wrap: wrap;
}

.campaign-member__name {
  font-weight: 600;
  color: var(--color-text);
  overflow-wrap: anywhere;
}

/* ----- role/status badge (never color-only — carries a word) ----- */
.camp-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--color-border);
  font-size: var(--text-sm);
  font-family: var(--font-heading-ui);
  letter-spacing: var(--ls-cinzel-caps);
  text-transform: uppercase;
  white-space: nowrap;
}

.camp-badge__dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background-color: var(--color-text-muted);
}

.camp-badge.is-owner { border-color: var(--color-accent); color: var(--color-accent); }
.camp-badge.is-owner .camp-badge__dot { background-color: var(--color-accent); }

.camp-badge.is-member .camp-badge__word { color: var(--color-text); }
.camp-badge.is-member .camp-badge__dot { background-color: var(--color-heal); }

.camp-badge.is-invited { color: var(--color-text-muted); font-style: italic; }
.camp-badge.is-invited .camp-badge__dot { background-color: var(--color-luck); }

/* ----- per-type share actions (always present, one Add per shareable type) ----- */
.campaign-share-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  margin-bottom: var(--space-md);
}

/* ----- campaign detail: tabbed panels (Agatha 2026-06-30, #4) -----
   Detail now uses the shared .loc-tabs / .loc-tab / .loc-tabs__panel scaffold
   (loc_tabs.js + locations.css) so it reads like the location sheets and dashboards.
   Give the tab body a little breathing room above the first panel; the panels contain
   the existing .campaign-section .panel blocks, which own their own chrome. */
.campaign-detail .loc-tabs { margin-bottom: var(--space-lg); }
.campaign-detail .loc-tabs__panel:not([hidden]) {
  display: flex;
  flex-direction: column;
  gap: var(--space-md);
}

/* TAB ICON FIX (Ken, 2026-07-01): the shared .loc-tab icons rendered BLACK on the
   dark ground — invisible. The location sheets get their etched look from a broad
   `.char-dash svg { stroke: currentColor; fill: none }` baseline that only applies
   inside a .char-dash wrapper; the campaign detail mounts .loc-tabs with NO such
   ancestor, so the LOC_ICONS paths (open, stroke-shaped) fell back to the default
   black FILL. Give the campaign tab glyphs the same etched treatment the other
   detail screens use — thin single-stroke in currentColor, no fill — so they read
   light-on-dark and pick up the muted->text->accent color the .loc-tab states set. */
.campaign-detail .loc-tab svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

/* ----- Overview dashboard (Alex's spec 2026-07-01) -----
   The Overview tab LANDS on the campaign's contents: a section label, a responsive
   grid of eight per-type cards, a manuscript double-rule divider, then the About
   block. No new tokens/colors/fonts; radius stays 0; Cornflower is the only
   interactive signal; glyphs are muted etched marks, not controls. */
.campaign-dash { margin: 0; }

.campaign-dash__title {
  margin: 0 0 var(--space-md);
  font-family: var(--font-heading-ui);
  font-size: var(--text-h5);
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps);
  color: var(--color-heading);
}

/* Responsive card grid — the dashboard grid math (4-col wide -> 3 -> 2 -> 1), honoring
   the recent large-viewport work. Fixed at eight cards; align-items:start lets a card
   with a longer peek grow without dragging its row-mates. */
.campaign-dash__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-lg);
  align-items: start;
}
@media (min-width: 760px) {
  .campaign-dash__grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1080px) {
  .campaign-dash__grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1400px) {
  .campaign-dash__grid { grid-template-columns: repeat(4, 1fr); }
}

/* The manuscript double-rule divider between the cards and About (existing <hr>
   idiom; hr in this app is a single mortar line, so we draw the second rule with a
   ::after so it reads as a deliberate section break, not a field separator). */
.campaign-dash__divider {
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: var(--space-lg) 0 0;
  position: relative;
}
.campaign-dash__divider::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 3px;
  border-top: 1px solid var(--color-border);
}

/* ----- one per-type card -----
   A titled container (NOT a whole-card open button — that's .char-card): the card
   surface matches .panel / .char-card (--color-surface, 1px --color-border, radius 0,
   no shadow) so the dashboard reads as one material. Contents flex-column so the
   "+ Share" footer sinks to the bottom on equal-height cards. */
.campaign-type-card {
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  padding: var(--space-md);
}

/* Header: muted etched glyph + Cinzel-caps title + mono count. */
.campaign-type-card__head {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--color-border);
}

.campaign-type-card__glyph {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.6rem;
  height: 1.6rem;
  color: var(--color-text-muted); /* etched mark, NOT accent — a mark, not a control */
}
.campaign-type-card__glyph svg {
  width: 100%;
  height: 100%;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.2;
  stroke-linecap: square;
  stroke-linejoin: miter;
}

.campaign-type-card__title {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-family: var(--font-heading-ui);
  font-size: var(--text-h5);
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps);
  color: var(--color-heading);
  overflow-wrap: anywhere;
}

/* Count — mono (it's a number/stat). Muted at 0, --color-text when > 0. */
.campaign-type-card__count {
  flex: 0 0 auto;
  font-family: var(--font-mono);
  font-size: var(--text-h5);
  color: var(--color-text-muted);
}
.campaign-type-card__count.has-any { color: var(--color-text); }

/* Body grows to fill so the footer aligns across a row of unequal-length cards. */
.campaign-type-card__body {
  flex: 1 1 auto;
  padding: var(--space-sm) 0 0;
}

.campaign-type-card__empty {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-style: italic;
}

.campaign-type-card__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
}

/* Entry row — reuses the shared-board row hover/focus standard (.list-row idiom):
   surface lift + accent border on hover/focus-within. The inner .campaign-shared__open
   (reused verbatim) owns the two-column media-chip layout + focus ring. */
.campaign-type-card__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast);
}
.campaign-type-card__item:hover,
.campaign-type-card__item:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

/* "+ K more" / "Show fewer" expander — a muted text button on its own row; it is the
   only in-card affordance besides the entries + share, so it stays quiet (accent only
   on hover/focus) to keep Cornflower the single active signal. */
.campaign-type-card__more-row { list-style: none; }
.campaign-type-card__more {
  background: none;
  border: none;
  padding: var(--space-xs) 0;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--transition-fast);
}
.campaign-type-card__more:hover { color: var(--color-accent); }
.campaign-type-card__more:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Footer: the per-card "+ Share an X" control — the last, terminal action, set off
   from the list by a top hairline + spacing. */
.campaign-type-card__foot {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
}
.campaign-type-card__share { width: 100%; }

/* ----- owner-editable Description / Notes fields (#4) ----- */
.campaign-field { margin-bottom: var(--space-lg); }
.campaign-field:last-child { margin-bottom: 0; }

.campaign-field__label {
  display: block;
  margin-bottom: var(--space-xs);
}

.campaign-field__input {
  width: 100%;
  resize: vertical;
  font-family: var(--font-body);
}

/* The Notes field is the long one — give it a comfortable minimum height so the
   8000-char field reads as a proper notebook, not a single-line box. */
.campaign-field--notes .campaign-field__input {
  min-height: 12rem;
}

.campaign-field__hint {
  margin-top: var(--space-xs);
}

.campaign-field__actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

.campaign-field__status {
  margin: var(--space-xs) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.campaign-field__status.is-error { color: var(--color-danger, #c0392b); }
.campaign-field__status.is-ok { color: var(--color-text-muted); }

/* Non-owner read-only rendering of Description / Notes: the value as flowing text
   (preserve author line breaks), with a muted italic placeholder when empty. */
.campaign-field__readonly {
  margin: 0;
  color: var(--color-text);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.campaign-field__readonly--empty {
  color: var(--color-text-muted);
  font-style: italic;
}

/* ----- shared board ----- */
.campaign-shared__group { margin-bottom: var(--space-md); }
.campaign-shared__group:last-child { margin-bottom: 0; }

/* Each group's heading row carries its OWN "+ Share" control (#3) — the label on the
   left, the add button hugging the right. Wraps on narrow widths. */
.campaign-shared__group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  flex-wrap: wrap;
  margin-bottom: var(--space-xs);
}
.campaign-shared__group-head .campaign-shared__heading { margin-bottom: 0; }
.campaign-shared__add { flex: 0 0 auto; }

.campaign-shared__empty {
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-style: italic;
}

.campaign-shared__heading {
  margin: 0 0 var(--space-xs);
  font-family: var(--font-heading-ui);
  font-size: var(--text-h5);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps);
}

.campaign-shared__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

/* Shared-board item row — CORE .list-row hover/focus standard (see invite row
   note). The nested .campaign-shared__open keeps its own inner hover as a finer
   text-target affordance; the row owns the accent border + surface lift. */
.campaign-shared__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  transition:
    background-color var(--transition-fast),
    border-color var(--transition-fast);
}

.campaign-shared__item:hover,
.campaign-shared__item:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

.campaign-shared__open {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  text-align: left;
  background: none;
  border: none;
  padding: var(--space-xs) var(--space-sm);
  color: var(--color-text);
  cursor: pointer;
  border-radius: 3px;
  min-width: 0;
}

.campaign-shared__open:hover { background-color: var(--color-surface-hover); }
.campaign-shared__open:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 1px;
}

.campaign-shared__name {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.campaign-shared__owner {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  overflow-wrap: anywhere;
}

/* ----- shared/linked board media chip (contract #7, 2026-06-28) -----
   A small image-or-glyph thumbnail leading each shared/linked entry. The --media
   open button switches to a two-column grid: the chip in column 1 (row-spanning),
   name + owner stacked in column 2. Without a chip (degraded), the default column
   layout above still applies, so this is purely additive. */
.campaign-shared__open--media {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  column-gap: var(--space-sm);
}

.campaign-shared__open--media > .campaign-shared__chip {
  grid-column: 1;
  grid-row: 1 / span 2;
}

/* When the entry has no owner line, the chip still aligns centered against the
   single name row (grid auto-places the name into column 2 row 1). */
.campaign-shared__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 auto;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  overflow: hidden;
}

.campaign-shared__chip-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.campaign-shared__chip-fallback {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--color-text-muted);
}

.campaign-shared__chip-fallback svg {
  width: 58%;
  height: 58%;
}

.campaign-shared__unshare { flex: 0 0 auto; }

/* ----- share picker (inside the panel modal) ----- */
.share-picker__group { margin-bottom: var(--space-md); }
.share-picker__group:last-child { margin-bottom: 0; }

.share-picker__heading {
  margin: 0 0 var(--space-xs);
  font-family: var(--font-heading-ui);
  font-size: var(--text-h5);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps);
}

.share-picker__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}

.share-picker__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: 4px;
}

.share-picker__name {
  flex: 1 1 auto;
  overflow-wrap: anywhere;
  min-width: 0;
}

.share-picker__item .btn.is-done {
  opacity: 0.7;
  cursor: default;
}

/* A row may carry TWO share actions (To campaign / With a friend) — cluster them. */
.share-picker__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  flex: 0 0 auto;
}

/* ----- friend-share modal (Feature A) ----- */
.friend-share__pick { margin: var(--space-sm) 0; }
.friend-share__submit { margin-top: var(--space-sm); }
.friend-share__empty {
  margin: var(--space-sm) 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.friend-share__or {
  margin-top: var(--space-md);
}
.friend-share__invite {
  margin-top: var(--space-xs);
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
}
.friend-share__invite-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  align-items: center;
}
.friend-share__invite-row .field__input { flex: 1 1 14rem; min-width: 0; }
.friend-share__status { margin: 0; font-size: var(--text-sm); }
.friend-share__status.is-error { color: var(--color-danger, #c0392b); }
.friend-share__status.is-ok { color: var(--color-text-muted); }

/* ----- entity<->campaign cross-link panel (locations_stock_maps_sharing_batch #9b)
   The "Campaigns" block injected into a Shop/Tavern/House/Person detail view. It
   reuses the campaign-shared row idiom; this only handles its placement INSIDE a
   .saved-view / .loc-view (a divider above, no double panel chrome) and the heading
   margin reset (it uses .loc-view__section-title in the section head). */
.saved-view .entity-campaigns,
.loc-view .entity-campaigns {
  margin: var(--space-md) 0 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
  /* The view already sits inside a card/modal; don't stack a second panel surface. */
  background: none;
}

.entity-campaigns .campaign-section__head .loc-view__section-title { margin-bottom: 0; }

/* Add-to-campaign picker reuses the share-picker list inside the action-modal. */
.entity-campaigns-modal .share-picker__item .btn.is-done {
  opacity: 0.7;
  cursor: default;
}

/* ----- form modal (create / invite) ----- */
.camp-form-modal__intro {
  margin: 0 0 var(--space-md);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.camp-form-modal__field { margin-bottom: var(--space-md); }
.camp-form-modal__field:last-child { margin-bottom: 0; }

/* Name field + inline "Roll" addon sit on one row; the input flexes, the button
   hugs its content. The button keeps a comfortable >=44px tap target height. */
.camp-form-modal__input-row {
  display: flex;
  align-items: stretch;
  gap: var(--space-xs);
}

.camp-form-modal__input--with-addon {
  flex: 1 1 auto;
  min-width: 0;
}

.camp-form-modal__addon {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* ----- friends picker (invite modal) ----- */
.camp-friends {
  margin-top: var(--space-md);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
}

.camp-friends__heading {
  margin: 0 0 0.15rem;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}

.camp-friends__hint {
  margin: 0 0 var(--space-sm);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.camp-friends__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  max-height: 9rem;
  overflow-y: auto;
}

.camp-friends__item { margin: 0; }

/* The pick buttons are .btn--secondary (black-backed, accent-outlined) — a list
   of lesser pick affordances, not a primary CTA. The selected pick fills with the
   accent-hover and gains a focus-style ring so the chosen friend reads as pressed
   against its black-backed siblings (selection is never color-only — aria-pressed
   carries the state for AT). */
.camp-friends__pick.is-selected {
  background-color: var(--color-accent-hover);
  border-color: var(--color-accent-hover);
  box-shadow: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--color-accent);
}

.camp-form-modal__error {
  margin: 0 0 var(--space-md);
  padding: var(--space-xs) var(--space-sm);
  border-left: 3px solid var(--color-danger);
  background-color: var(--color-danger-bg);
  color: var(--color-text);
  font-size: var(--text-sm);
}

/* ----- panel modal (share picker) — taller, scrollable body ----- */
.camp-panel-modal__card {
  max-width: 32rem;
  width: 100%;
}

.camp-panel-modal__body {
  max-height: 60vh;
  overflow-y: auto;
}

/* ----- danger button variant (delete/leave) ----- */
.btn--danger {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.btn--danger:hover {
  background-color: var(--color-danger-bg);
  color: var(--color-text);
}

.btn--danger:focus-visible {
  outline: 2px solid var(--color-danger);
  outline-offset: 2px;
}

/* The shared read-only viewer reuses .admin-view-overlay (admin.css). This marker
   exists only so the campaign viewer can be targeted distinctly if ever needed. */
.camp-view-overlay { /* inherits .admin-view-overlay */ }

/* ============================================================
   Responsive — phone tier (Batch A, KDL 2026-06-27)
   --bp-tab = 700px (the phase-wide single value; see mobile_optimization_audit
   §3 + Phase 1 record). The page-head + section action clusters are otherwise
   flex:0 0 auto / nowrap and, at ~390px, the two head buttons (Join with code +
   New Campaign) eat the whole row beside the wrapped title and crowd the right
   edge. Below the breakpoint, let the action clusters take a full row and let
   their buttons grow so they read as a comfortable stacked pair rather than a
   pinched, near-overflowing strip. Desktop density is untouched.
   ============================================================ */
@media (max-width: 700px) {
  .campaigns__head-actions,
  .campaign-section__actions,
  .campaign-invite__actions {
    flex: 1 1 100%;
    flex-wrap: wrap;
  }

  /* Buttons in those rows share the width evenly instead of hugging content,
     giving each a comfortable tap target without forcing a hard single column. */
  .campaigns__head-actions > .btn,
  .campaign-section__actions > .btn {
    flex: 1 1 auto;
  }

  /* The per-type share cluster already wraps; just let each Add button grow so a
     four-button row breaks into balanced pairs rather than ragged widths. */
  .campaign-share-actions > .btn {
    flex: 1 1 auto;
  }

  /* Per-group "+ Share" (#3): on phones let the button take the full row under its
     heading rather than crowding the label. */
  .campaign-shared__group-head .campaign-shared__add {
    flex: 1 1 100%;
  }
}
