/* =============================================================================
   /cookie-alliance/ — the route's own stylesheet.

   Canonical site breakpoints (desktop-first cascade; base = Desktop wide).
   Use ONLY these boundary values in @media queries:
     Desktop wide : >= 1400px            (base styles; content capped ~1600px)
     Desktop      : 1100-1399px          -> max-width: 1399px
     Tablet       : 700-1099px           -> max-width: 1099px  (min-width: 700px)
     Mobile       : 325-699px            -> max-width: 699px
     Mobile split : <= 324px             -> max-width: 324px
   Desktop-up enhancements use min-width: 700px / 1100px / 1400px.
   Prefer fluid CSS (clamp/%/vw/minmax) over adding new breakpoints.

   SCOPE. Every rule below is scoped to body[data-route="cookie-alliance"].
   Two shared files this page loads are deliberately NOT touched from here:

     assets/css/team-build-card.css — its UNSCOPED base rules ARE the 1920px
       PNG poster (build-graphic-export.js builds its own tree from the same
       sheet). An unscoped rule added for this page would move every exported
       graphic on the site. The one thing this file says about a .tb-* selector
       is a margin reset on the card inside its wrapper, and that is scoped.

     feature-page.css — 42 references site-wide, and editing it forces an
       events rebake. So `.accent-alliance`, which exists nowhere else yet,
       is declared HERE beside the page that uses it.
   ========================================================================== */

body[data-route="cookie-alliance"] {
  /* Cookie Alliance's established site colour, copied from
     .tb-cat-tone--cookie-alliance in team-builder/styles.css and
     .guide-category-tone--cookie-alliance in community-guides/styles.css.
     Retune one and you must retune the others — the three are hand-kept in
     step, exactly like the guides/team-builder category tones. */
  --ca-accent: #22b8e6;
  --ca-accent-deep: #0e7490;
  --ca-accent-fill: linear-gradient(135deg, #22b8e6, #0e7490);
  --ca-line: rgba(34, 184, 230, 0.4);
  --ca-line-soft: rgba(34, 184, 230, 0.22);
  --ca-line-strong: rgba(34, 184, 230, 0.72);
  --ca-glow: rgba(34, 184, 230, 0.26);

  --ca-surface: rgba(10, 30, 42, 0.5);
  --ca-surface-raised: rgba(13, 42, 58, 0.78);
  --ca-text: #eaf8ff;
  --ca-text-soft: rgba(203, 231, 245, 0.9);
  --ca-text-muted: rgba(179, 211, 228, 0.82);
}

/* -----------------------------------------------------------------------------
   Accent. feature-page.css declares one of these per accent and sets nothing but
   a border colour; this matches that shape rather than inventing a richer one,
   so an .accent-alliance panel sits beside an .accent-arena panel unchanged.
   -------------------------------------------------------------------------- */
body[data-route="cookie-alliance"] .premium-glass.accent-alliance {
  border-color: var(--ca-line);
}

/* =============================================================================
   HERO
   Frame, type scale and head-cookie geometry all come from the shared
   `tcb-hero` / `tcb-hero-text` ladder in shared-shell.css. Only the dials that
   differ for THIS cookie are set here; anything commented out tracks the shared
   value, which is measured off the Dark Cacao art on /resonant/.
   ========================================================================== */
body[data-route="cookie-alliance"] {
  /* Box. Top-anchored (shift-y 0) rather than the shared centred model — this
     hero is shorter than Resonant's, so centring drops the art onto the
     overview section below the frame. */
  --tcb-hero-art-width: 23rem;
  --tcb-hero-art-top: -1.75rem;
  --tcb-hero-art-right: -1rem;
  --tcb-hero-art-shift-y: 0%;
  --tcb-hero-art-opacity: 1;
  /* Stormbringer is a bright, high-contrast portrait, so the shared lilac rim
     glow is retuned to the page accent instead of left to fight it. */
  --tcb-hero-art-glow: drop-shadow(0 0 30px rgba(34, 184, 230, 0.42));

  /* Mobile (<=699px) */
  --tcb-hero-art-width-mobile: 15rem;
  --tcb-hero-art-top-mobile: -2.25rem;
  --tcb-hero-art-right-mobile: -2.5rem;
  --tcb-hero-art-opacity-mobile: 1;
}

@media (max-width: 1099px) {
  body[data-route="cookie-alliance"] {
    --tcb-hero-art-width: 20rem;
  }
}

@media (max-width: 480px) {
  body[data-route="cookie-alliance"] {
    --tcb-hero-art-width-mobile: 13rem;
    --tcb-hero-art-right-mobile: -3.25rem;
  }
}

body[data-route="cookie-alliance"] .ca-hero {
  min-height: 15rem;
  align-self: start;
}

/* Eyebrow + title are one unit, so the hero's own gap does not space them as
   far apart as it spaces the copy below them. */
body[data-route="cookie-alliance"] .ca-hero .hero-head {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-hero .hero-copy {
  color: #fff;
  /* Anchored to the bottom of the hero while the eyebrow and h1 stay at the
     top. The hero is a flex column, so an auto TOP margin eats all the free
     space above this element and pushes only it down — no change to the
     container, and nothing to keep in sync if a sibling is added. */
  margin: auto 0 0;
  max-width: 34rem;
}

/* =============================================================================
   MODE OVERVIEW
   The page's indexable body copy: static HTML that renders with no JS and no
   Supabase. Every class the pasted block uses is styled below.
   ========================================================================== */
body[data-route="cookie-alliance"] .ca-overview {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  /* LOAD-BEARING. This section is a GRID ITEM of main.route-main, and a grid
     item's automatic minimum size is its content's min-content width — so
     without this the section cannot shrink below the widest thing inside it.
     The widest thing is the map-effects table (min-width: 32rem, deliberately,
     so four columns stay readable), and opening the "Full rules" disclosure
     therefore stretched the whole section to 582px inside a 390px phone.
     .ca-table-wrap's overflow-x: auto could not save it: a scroll container
     only reports a zero min-content width to an ancestor that is ALLOWED to
     shrink, and this one was not.
     It read as the panel being cut off rather than as a scrollbar, because
     body carries overflow-x: clip site-wide — which is also why a naive
     documentElement.scrollWidth assertion sees nothing wrong. Measure the
     section, not the document. */
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-overview-head {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-overview-lead {
  margin: 0;
  max-width: 62rem;
  color: var(--ca-text-soft);
  font-size: 1.02rem;
  line-height: 1.55;
}

body[data-route="cookie-alliance"] .ca-overview-lead strong,
body[data-route="cookie-alliance"] .ca-fact strong,
body[data-route="cookie-alliance"] .ca-detail-block strong {
  color: var(--ca-text);
  font-weight: 800;
}

/* The four "what the mode is" cards. auto-fit rather than a fixed count: at
   four across on a wide screen and one on a phone, nothing in between needs a
   breakpoint of its own. */
/* ---- The walkthrough ----------------------------------------------------
   Five steps, in the order a player meets the screens, each with one or two
   annotated screenshots. Full-width rows rather than the four-across card grid
   this replaced: the shots are 2.17:1 landscape phone captures, and a 16rem
   grid column rendered them too small to read the UI they are pointing at,
   which is the only reason they exist. */
body[data-route="cookie-alliance"] .ca-facts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.9rem;
  margin: 0;
  padding: 0;
  list-style: none;
  /* Each card is its own height. Without this, a collapsed step sharing a row
     with an open one is stretched to match it — a title bar with a card's worth
     of empty space under it. */
  align-items: start;
}

body[data-route="cookie-alliance"] .ca-fact {
  min-width: 0;
  /* No padding: the disclosure inside owns it, so the summary is clickable
     edge to edge rather than only across the card's content box. */
  border: 1px solid var(--ca-line-soft);
  border-radius: 0.9rem;
  background: var(--ca-surface);
}

body[data-route="cookie-alliance"] .ca-fact-disclosure > summary {
  padding: 0.8rem 1rem;
}

/* The heading keeps its own layout inside the summary, and takes the space the
   caret does not — without `min-width: 0` a long step name would push the caret
   off the card rather than wrapping. */
body[data-route="cookie-alliance"] .ca-fact-disclosure > summary h3 {
  flex: 1 1 auto;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-fact-disclosure > .ca-shots {
  padding: 0 1rem 1rem;
}

body[data-route="cookie-alliance"] .ca-fact h3 {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin: 0;
  font-size: 1.02rem;
  line-height: 1.2;
  color: var(--ca-text);
}

/* The step number. In the markup rather than a ::before counter so it can be
   aria-hidden — the list is already an <ol>, so a screen reader announces the
   position itself and would otherwise hear it twice. */
body[data-route="cookie-alliance"] .ca-fact-step {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--ca-accent-soft, rgba(34, 184, 230, 0.18));
  color: var(--ca-accent);
  font-size: 0.8rem;
  font-weight: 800;
}

/* One shot fills the row; two share it. `1fr 1fr` rather than auto-fit, so a
   pair always splits evenly instead of one wide shot starving the other. */
body[data-route="cookie-alliance"] .ca-shots {
  display: grid;
  gap: 0.8rem;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-shots--pair {
  grid-template-columns: 1fr 1fr;
}

body[data-route="cookie-alliance"] .ca-shot {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin: 0;
  min-width: 0;
}

/* The captures carry their own purple frame, so the radius here just stops the
   corners sitting square against the card. width/height are on the element too,
   so the row reserves its space before the image loads and nothing jumps. */
body[data-route="cookie-alliance"] .ca-shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.55rem;
}

body[data-route="cookie-alliance"] .ca-shot figcaption {
  margin: 0;
  color: var(--ca-text-soft);
  font-size: 0.88rem;
  line-height: 1.5;
}

body[data-route="cookie-alliance"] .ca-shot figcaption strong {
  color: var(--ca-text);
}

/* ---- Wide desktop: the single-shot steps share a row --------------------
   Steps 2 and 3 carry ONE capture where the others carry two, so at full width
   their image is twice the size of every other image on the page — the section
   reads as though those two steps matter twice as much, when the reason is
   only that they had nothing to sit beside.

   Sharing a row makes every image on the page the same width: a paired step's
   two shots and a solo step's one shot all land at half a row minus the gap.

   Keyed off `--solo`, which the markup carries because the step has a single
   shot, rather than off :nth-child(2) and :nth-child(3). Position-based rules
   would silently attach to the wrong steps the first time one is reordered or
   inserted, and this list is a walkthrough that will grow as the mode does.

   Deliberately NOT grid-auto-flow: dense. Dense would backfill a gap with a
   later solo, which reorders the walkthrough visually against its DOM and its
   numbering — the one thing a numbered sequence cannot afford. An odd solo out
   simply sits at half width with space beside it, which is still smaller than
   the full-width version this replaces.

   1080px matches the breakpoint the Dex metrics panel already uses for its own
   "there is room for a second column now" decision, rather than inventing a
   number: below it, half a row is too narrow to read an annotated screenshot,
   which is the whole reason these images exist. */
@media (min-width: 1080px) {
  body[data-route="cookie-alliance"] .ca-facts {
    grid-template-columns: 1fr 1fr;
  }

  body[data-route="cookie-alliance"] .ca-fact {
    grid-column: 1 / -1;
  }

  body[data-route="cookie-alliance"] .ca-fact--solo {
    grid-column: span 1;
  }
}

/* ---- The full-rules disclosure ------------------------------------------
   Collapsed by default: this is reference detail, and a reader who came for
   the community teams should not have to scroll a rulebook to reach them. It
   is still in the DOM either way, so a crawler reads all of it. */
body[data-route="cookie-alliance"] .ca-details {
  border: 1px solid var(--ca-line-soft);
  border-radius: 0.9rem;
  background: var(--ca-surface);
}

/* The caret chrome is shared by BOTH disclosures on this page — the
   walkthrough steps and the full-rules panel — rather than written twice. Only
   the box around them differs: a step is already inside a .ca-fact card, so it
   brings no border or background of its own. */
body[data-route="cookie-alliance"] .ca-fact-disclosure > summary,
body[data-route="cookie-alliance"] .ca-details > summary {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.75rem 1rem;
  cursor: pointer;
  color: var(--ca-text);
  font-family: "CookieRun KR", "Outfit", sans-serif;
  font-size: 0.95rem;
  /* The UA marker is replaced by the caret below, which can be rotated with
     the open state. `list-style` covers Firefox, the pseudo-element covers
     WebKit — both are needed. */
  list-style: none;
}

body[data-route="cookie-alliance"] .ca-fact-disclosure > summary::-webkit-details-marker,
body[data-route="cookie-alliance"] .ca-details > summary::-webkit-details-marker {
  display: none;
}

body[data-route="cookie-alliance"] .ca-fact-disclosure > summary::after,
body[data-route="cookie-alliance"] .ca-details > summary::after {
  content: "";
  width: 0.5rem;
  height: 0.5rem;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-0.1rem, -0.1rem);
  transition: transform 160ms ease;
}

body[data-route="cookie-alliance"] .ca-fact-disclosure[open] > summary::after,
body[data-route="cookie-alliance"] .ca-details[open] > summary::after {
  transform: rotate(-135deg) translate(-0.1rem, -0.1rem);
}

body[data-route="cookie-alliance"] .ca-details > summary:hover,
body[data-route="cookie-alliance"] .ca-details > summary:focus-visible {
  color: #fff;
}

body[data-route="cookie-alliance"] .ca-details-label {
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-details-body {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: 0 1rem 1rem;
  border-top: 1px solid var(--ca-line-soft);
  padding-top: 1rem;
}

body[data-route="cookie-alliance"] .ca-detail-block {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-detail-block h3 {
  font-size: 0.98rem;
  line-height: 1.2;
  color: var(--ca-text);
}

body[data-route="cookie-alliance"] .ca-detail-block ul {
  margin: 0;
  padding-left: 1.15rem;
  color: var(--ca-text-soft);
  font-size: 0.92rem;
  line-height: 1.55;
}

body[data-route="cookie-alliance"] .ca-detail-block li + li {
  margin-top: 0.3rem;
}

body[data-route="cookie-alliance"] .ca-detail-block a {
  color: var(--ca-accent);
}

body[data-route="cookie-alliance"] .ca-detail-note {
  margin: 0;
  max-width: 62rem;
  color: var(--ca-text-muted);
  font-size: 0.88rem;
  line-height: 1.5;
}

/* ---- Map-effects table --------------------------------------------------
   The wrap scrolls, not the page: eight rows of long effect names plus three
   difficulty columns is wider than a phone, and letting it push <body> sideways
   makes every section on the page scrollable. */
body[data-route="cookie-alliance"] .ca-table-wrap {
  min-width: 0;
  overflow-x: auto;
  border: 1px solid var(--ca-line-soft);
  border-radius: 0.75rem;
  background: rgba(6, 20, 30, 0.45);
}

body[data-route="cookie-alliance"] .ca-table {
  width: 100%;
  min-width: 32rem;
  border-collapse: collapse;
  font-size: 0.88rem;
}

body[data-route="cookie-alliance"] .ca-table th,
body[data-route="cookie-alliance"] .ca-table td {
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--ca-line-soft);
  text-align: left;
  vertical-align: top;
}

body[data-route="cookie-alliance"] .ca-table thead th {
  color: var(--ca-text);
  font-family: "CookieRun KR", "Outfit", sans-serif;
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  background: rgba(14, 116, 144, 0.28);
}

body[data-route="cookie-alliance"] .ca-table tbody th {
  color: var(--ca-text-soft);
  font-weight: 600;
  line-height: 1.4;
}

body[data-route="cookie-alliance"] .ca-table tbody tr:last-child th,
body[data-route="cookie-alliance"] .ca-table tbody tr:last-child td {
  border-bottom: 0;
}

/* The three difficulty columns are marks, not measurements — centred, and
   sized so a row scans as a pattern rather than as text. */
body[data-route="cookie-alliance"] .ca-table td {
  width: 5.5rem;
  text-align: center;
  font-size: 1.05rem;
  line-height: 1.2;
}

body[data-route="cookie-alliance"] .ca-table td.is-on {
  color: var(--ca-accent);
}

body[data-route="cookie-alliance"] .ca-table td.is-off {
  color: rgba(160, 190, 205, 0.55);
}

/* Visually hidden, still read aloud. The site has no such utility of its own,
   and the table needs one twice over: a caption that names the table for a
   screen reader without repeating the heading above it, and a word behind every
   ●/— so the grid is not silent. */
body[data-route="cookie-alliance"] .ca-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* =============================================================================
   COMMUNITY TEAMS
   The live half of the community-build pipeline (app.js). The baked link list
   below it keeps the inline styles the bake writes.

   The team card itself is styled entirely by assets/css/team-build-card.css —
   nothing here may touch a .tb-* selector beyond the margin reset noted where
   it appears.
   ========================================================================== */
body[data-route="cookie-alliance"] .ca-teams {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

body[data-route="cookie-alliance"] .ca-teams-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.5rem;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-teams-lead {
  margin: 0;
  max-width: 62rem;
  color: var(--ca-text-soft);
  font-size: 0.95rem;
  line-height: 1.5;
}

body[data-route="cookie-alliance"] .ca-teams-cta {
  padding: 0.45rem 0.9rem;
  border: 1px solid var(--ca-line);
  border-radius: 999px;
  background: rgba(14, 116, 144, 0.28);
  color: var(--ca-text);
  font: 700 0.82rem "Outfit", sans-serif;
  text-decoration: none;
}

body[data-route="cookie-alliance"] .ca-teams-cta:hover,
body[data-route="cookie-alliance"] .ca-teams-cta:focus-visible {
  border-color: var(--ca-line-strong);
  background: var(--ca-accent-fill);
  color: #fff;
}

body[data-route="cookie-alliance"] .ca-team-list {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

body[data-route="cookie-alliance"] .ca-team-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Kept deliberately small. Every level of padding between the page and a
     .tb-team narrows the card, and the card picks its layout from @container
     queries on ITSELF — so each nesting level makes it flip to the next layout
     that much earlier than the builder does. That is expected (see the note in
     team-build-card.css) but it compounds, and this page nests a level deeper
     than the hubs because a build holds three cards.
     Measured: at a 700px viewport the card's own box is ~608px, just under the
     620px container breakpoint, so the tile takes its narrow layout from about
     700px down rather than from ~630px. That is the trade, and it is why the
     glass padding is cut at 699px rather than the card being padded further. */
  padding: 14px;
  border: 1px solid var(--ca-line-soft);
  border-radius: 16px;
  background: rgba(8, 24, 34, 0.46);
}

body[data-route="cookie-alliance"] .ca-team-card.is-expanded {
  border-color: var(--ca-line);
}

body[data-route="cookie-alliance"] .ca-team-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 14px;
}

body[data-route="cookie-alliance"] .ca-team-card-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

body[data-route="cookie-alliance"] .ca-team-card-title {
  margin: 0;
  font-size: 1.02rem;
  line-height: 1.25;
}

/* ---- Collapsed roster ---------------------------------------------------
   One strip PER TEAM, stacked. Three rows is what makes a fifteen-Cookie build
   read as three teams at a glance rather than one long run of portraits. */
body[data-route="cookie-alliance"] .ca-roster {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-roster-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-roster-label {
  /* A fixed measure so the three strips line up under each other whatever the
     author named their teams; a long name truncates rather than shunting one
     row's portraits out of step with the others. */
  flex: 0 0 auto;
  width: 6.5rem;
  overflow: hidden;
  color: var(--ca-text-muted);
  font: 700 0.72rem "Outfit", sans-serif;
  letter-spacing: 0.02em;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body[data-route="cookie-alliance"] .ca-roster-cookies {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-roster-cookie {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--ca-line-soft);
  background: rgba(13, 42, 58, 0.8);
}

body[data-route="cookie-alliance"] .ca-team-card-meta {
  display: flex;
  /* Wraps so the overflow menu can claim a line of its own (it is a
     `flex: 1 0 100%` child, via display: contents on its wrapper). Without
     this it stays on the row and squeezes the contributor's name onto two
     lines to make space. */
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-left: auto;
}

body[data-route="cookie-alliance"] .ca-toggle {
  padding: 0.35rem 0.7rem;
  border-radius: 0.6rem;
  border: 1px solid var(--ca-line-soft);
  background: var(--ca-surface-raised);
  color: var(--ca-text-soft);
  cursor: pointer;
  font: 700 0.76rem "Outfit", sans-serif;
  white-space: nowrap;
}

body[data-route="cookie-alliance"] .ca-toggle:hover,
body[data-route="cookie-alliance"] .ca-toggle:focus-visible {
  border-color: var(--ca-line-strong);
  color: #fff;
}

body[data-route="cookie-alliance"] .ca-toggle.is-active {
  border-color: var(--ca-line);
  color: #fff;
}

/* ---- Overflow menu ------------------------------------------------------
   Expands IN the card (see the note in app.js) — a floating panel would cover
   the next card's own toggle. */
body[data-route="cookie-alliance"] .ca-menu-wrap {
  display: contents;
}

body[data-route="cookie-alliance"] .ca-menu-toggle {
  min-width: 2rem;
  padding: 0.35rem 0.5rem;
  border-radius: 0.6rem;
  border: 1px solid var(--ca-line-soft);
  background: var(--ca-surface-raised);
  color: var(--ca-text-soft);
  cursor: pointer;
  font: 700 0.95rem "Outfit", sans-serif;
  line-height: 1;
}

body[data-route="cookie-alliance"] .ca-menu-toggle:hover,
body[data-route="cookie-alliance"] .ca-menu-toggle:focus-visible,
body[data-route="cookie-alliance"] .ca-menu-toggle.is-active {
  border-color: var(--ca-line-strong);
  color: #fff;
}

/* display: contents on the wrapper puts the toggle in the meta row and this
   panel on its own line beneath the head — one flow, no absolute positioning.
   It wraps, which matters here: a three-team build lists five items. */
body[data-route="cookie-alliance"] .ca-menu {
  flex: 1 0 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
  padding-top: 8px;
  border-top: 1px solid var(--ca-line-soft);
}

body[data-route="cookie-alliance"] .ca-menu[hidden] {
  display: none;
}

body[data-route="cookie-alliance"] .ca-menu-item {
  padding: 0.35rem 0.7rem;
  border-radius: 0.6rem;
  border: 1px solid var(--ca-line-soft);
  background: var(--ca-surface-raised);
  color: var(--ca-text-soft);
  cursor: pointer;
  font: 700 0.76rem "Outfit", sans-serif;
  white-space: nowrap;
}

body[data-route="cookie-alliance"] .ca-menu-item:hover:not(:disabled),
body[data-route="cookie-alliance"] .ca-menu-item:focus-visible {
  border-color: var(--ca-line-strong);
  color: #fff;
}

body[data-route="cookie-alliance"] .ca-menu-item:disabled {
  cursor: wait;
  opacity: 0.65;
}

/* ---- Expanded body ------------------------------------------------------ */
body[data-route="cookie-alliance"] .ca-team-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  padding-top: 10px;
  border-top: 1px solid var(--ca-line-soft);
}

body[data-route="cookie-alliance"] .ca-build-notes {
  margin: 0;
  color: var(--ca-text-soft);
  font-size: 0.9rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

/* THE THREE TEAMS ARE ALWAYS STACKED, AT EVERY WIDTH.
   Not a stylistic choice. The team card sizes itself from @container queries on
   .tb-team, which resolve against the card's own box, NOT the viewport. Three
   ~460px cards in a desktop row would put every one of them below the card's
   620px container breakpoint while @media (max-width: 699px) stays false — so
   each card would take its narrow-phone layout, including swapping in the
   Cookie-Dex "Comfy" card, at full desktop width. Unreadable, and unfixable
   from here without editing the shared sheet. A flex COLUMN cannot do that;
   do not make this a grid with more than one column. */
body[data-route="cookie-alliance"] .ca-subteams {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  min-width: 0;
  list-style: none;
}

body[data-route="cookie-alliance"] .ca-subteam {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

body[data-route="cookie-alliance"] .ca-subteam-toggle {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--ca-line-soft);
  border-radius: 0.7rem;
  background: var(--ca-surface-raised);
  color: var(--ca-text-soft);
  cursor: pointer;
  font: 700 0.82rem "Outfit", sans-serif;
  text-align: left;
}

body[data-route="cookie-alliance"] .ca-subteam-toggle:hover,
body[data-route="cookie-alliance"] .ca-subteam-toggle:focus-visible {
  border-color: var(--ca-line-strong);
  color: #fff;
}

body[data-route="cookie-alliance"] .ca-subteam-toggle.is-open {
  border-color: var(--ca-line);
  background: var(--ca-accent-fill);
  color: #fff;
}

/* "Team 2" is the position and always shows; the name beside it is what the
   author typed, and app.js omits that span entirely when they typed nothing —
   otherwise an unnamed team would read "Team 2  Team 2". */
body[data-route="cookie-alliance"] .ca-subteam-index {
  flex: 0 0 auto;
  padding: 0.1rem 0.4rem;
  border-radius: 0.4rem;
  background: rgba(6, 20, 30, 0.45);
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

body[data-route="cookie-alliance"] .ca-subteam-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body[data-route="cookie-alliance"] .ca-subteam-chevron {
  flex: 0 0 auto;
  width: 0.5rem;
  height: 0.5rem;
  margin-left: auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-0.1rem, -0.1rem);
  transition: transform 160ms ease;
}

body[data-route="cookie-alliance"] .ca-subteam-toggle.is-open .ca-subteam-chevron {
  transform: rotate(-135deg) translate(-0.1rem, -0.1rem);
}

/* No padding of its own, deliberately: the card inside is the @container host,
   and every pixel this wrapper takes flips its layout that much earlier. The
   overflow guard is belt and braces — --view handles its own narrow layout;
   this catches anything that layout does not, so an overflow scrolls inside the
   card rather than widening the page. */
body[data-route="cookie-alliance"] .ca-subteam-card {
  min-width: 0;
  padding: 0;
  overflow-x: auto;
}

/* The one .tb-* rule in this file, and it is scoped: the card ships with its
   own outer margin for the builder's list, which here would double the gap the
   flex column already sets. */
body[data-route="cookie-alliance"] .ca-subteam .tb-team {
  margin: 0;
}

/* ---- Contributor + status ---------------------------------------------- */
body[data-route="cookie-alliance"] .ca-contributor {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: inherit;
  font-size: 0.84rem;
  text-decoration: none;
}

body[data-route="cookie-alliance"] .ca-contributor:hover .ca-contributor-name,
body[data-route="cookie-alliance"] .ca-contributor:focus-visible .ca-contributor-name {
  text-decoration: underline;
}

body[data-route="cookie-alliance"] .ca-contributor-avatar {
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 999px;
  overflow: hidden;
  display: grid;
  place-items: center;
  border: 1px solid var(--ca-line);
  background: var(--ca-surface-raised);
  flex: 0 0 auto;
}

body[data-route="cookie-alliance"] .ca-contributor-avatar-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

body[data-route="cookie-alliance"] .ca-contributor-avatar-fallback {
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

body[data-route="cookie-alliance"] .ca-team-note {
  margin: 0;
  padding: 12px 0;
  color: var(--ca-text-soft);
  font-size: 0.94rem;
}

body[data-route="cookie-alliance"] .ca-team-note.is-error {
  color: #ffb4c0;
}

body[data-route="cookie-alliance"] .ca-team-status {
  padding: 0 0 4px;
  font-size: 0.86rem;
}

body[data-route="cookie-alliance"] .ca-team-status.is-success {
  color: #9ee7c0;
}

/* =============================================================================
   MAP-EFFECTS TABLE — RESTACKED
   ==========================================================================
   Below 480px the four columns cannot fit without a sideways scroll inside the
   table's own box, and a horizontal scrollbar buried in a collapsed rules panel
   is a thing readers simply never find. So the table becomes one CARD PER
   EFFECT: the effect name as a heading, then its three difficulties as labelled
   chips underneath.

   The chips get their label from `data-difficulty` on the cell rather than from
   :nth-child, so the label cannot drift out of step with the column if a
   difficulty is ever added or reordered — the markup carries the answer.

   ACCESSIBILITY. `display: block` on a table element strips its IMPLICIT table
   role in every engine, which is why the markup carries explicit role="table"
   / rowgroup / row / columnheader / rowheader / cell. The <thead> is dropped
   from the stacked view because each cell now states its own difficulty, so
   nothing is lost by hiding a header row that no longer sits above anything;
   it is `display: none` rather than visually-hidden precisely so it is not
   announced twice.

   599px, not the page's 699px breakpoint: the table is the only thing on this
   page that needs to restack, and it stops fitting well before the rest of the
   layout does.

   599 is MEASURED, not chosen. The table's 32rem min-width plus the section's
   and the column's padding needs a 600px viewport to fit: swept in 5px steps
   with the restack disabled, 565px overflows by 33px, the overflow shrinks to
   3px at 595, and 600 is the first width that fits. So the restack ends exactly
   where the table starts fitting and there is no width left at which anything
   scrolls sideways. Earlier values of 480 and then 560 both left a narrow band
   just above them still scrolling, which is the whole behaviour this exists to
   remove. If the table's min-width or the section padding changes, re-sweep. */
@media (max-width: 599px) {
  /* Nothing left to scroll — the whole point. */
  body[data-route="cookie-alliance"] .ca-table-wrap {
    overflow-x: visible;
    border: 0;
    border-radius: 0;
    background: none;
  }

  body[data-route="cookie-alliance"] .ca-table {
    display: block;
    min-width: 0;
    width: 100%;
  }

  body[data-route="cookie-alliance"] .ca-table thead {
    display: none;
  }

  body[data-route="cookie-alliance"] .ca-table tbody {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  body[data-route="cookie-alliance"] .ca-table tbody tr {
    display: block;
    min-width: 0;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--ca-line-soft);
    border-radius: 0.75rem;
    background: rgba(6, 20, 30, 0.45);
  }

  /* The gap under the name is the HEADING's margin, not a margin on the first
     chip. Putting it on the chip shifted that one item down while its two
     siblings stayed put, so Easy sat visibly lower than Hard and Master on
     every row. */
  body[data-route="cookie-alliance"] .ca-table tbody th {
    display: block;
    margin-bottom: 0.45rem;
    padding: 0 0 0.45rem;
    border-bottom: 1px solid var(--ca-line-soft);
    color: var(--ca-text);
    font-weight: 700;
  }

  body[data-route="cookie-alliance"] .ca-table tbody td {
    display: inline-flex;
    align-items: baseline;
    gap: 0.3rem;
    width: auto;
    margin-right: 0.9rem;
    padding: 0;
    border-bottom: 0;
    font-size: 0.95rem;
  }

  /* The column this cell came from, now that the header row is gone. */
  body[data-route="cookie-alliance"] .ca-table tbody td::before {
    content: attr(data-difficulty);
    color: var(--ca-text-soft);
    font-family: "CookieRun KR", "Outfit", sans-serif;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
  }

  /* The last-row border reset above is for the table view; a stacked row is a
     card and keeps its own outline. */
  body[data-route="cookie-alliance"] .ca-table tbody tr:last-child th {
    border-bottom: 1px solid var(--ca-line-soft);
  }
}

/* =============================================================================
   NARROW
   ========================================================================== */
@media (max-width: 699px) {
  /* A 2.17:1 capture at half a phone's width is about 170px wide — the UI it
     is annotating stops being legible well before the layout stops fitting, so
     a pair stacks rather than shrinking. */
  body[data-route="cookie-alliance"] .ca-shots--pair {
    grid-template-columns: 1fr;
  }

  /* Give the cards their width back. The section's glass padding is on the
     outside of every team card, so it is the cheapest ~1rem to reclaim before
     the card starts dropping to its phone layout. */
  body[data-route="cookie-alliance"] .premium-glass {
    padding: 1rem;
  }

  body[data-route="cookie-alliance"] .ca-team-card {
    padding: 10px;
  }

  /* ---- Phone: the head becomes four flex LINES -------------------------
     "View teams" holds the card's top-right corner, beside the title, instead
     of sitting in the credit row underneath three rows of portraits — on a
     phone that put the card's primary action below the fold of the card
     itself.

         title .............................. View teams
         roster strips  (its own full-width line)
         contributor ................. votes   "..."
         [menu tray]

     Both wrappers dissolve to `display: contents` so their children become the
     head's own flex items and can be ordered into those lines; the head is
     already `flex-wrap: wrap`, so each `flex-basis: 100%` child forces a line
     to itself. This is the same shape /team-builder/'s saved-build row takes on
     a phone, deliberately — two cards that look alike on a desktop should not
     rearrange differently on a phone.

     `flex: 1 1 0` on the title (basis 0, not auto) stops a long build name
     claiming its content width and pushing the button onto a line of its own. */
  body[data-route="cookie-alliance"] .ca-team-card-main,
  body[data-route="cookie-alliance"] .ca-team-card-meta {
    display: contents;
  }

  body[data-route="cookie-alliance"] .ca-team-card-title {
    order: 1;
    flex: 1 1 0;
    min-width: 0;
  }

  /* `align-self`, because the head centres its items: a title that wraps to two
     or three lines would otherwise drag the button to the middle of them, and
     it is meant to hold the corner. */
  body[data-route="cookie-alliance"] .ca-toggle {
    order: 2;
    flex: 0 0 auto;
    align-self: start;
  }

  body[data-route="cookie-alliance"] .ca-roster {
    order: 3;
    flex: 1 1 100%;
  }

  body[data-route="cookie-alliance"] .ca-contributor {
    order: 4;
    min-width: 0;
  }

  /* Pushed right so the credit reads from the left edge and the two controls
     sit together at the other end, rather than three items spreading evenly. */
  body[data-route="cookie-alliance"] .ca-vote-controls {
    order: 5;
    margin-left: auto;
  }

  body[data-route="cookie-alliance"] .ca-menu-toggle {
    order: 6;
  }

  /* Already `flex: 1 0 100%` at every width, so it only needs its place in the
     order — last, under everything it was opened from. */
  body[data-route="cookie-alliance"] .ca-menu {
    order: 7;
  }

  body[data-route="cookie-alliance"] .ca-roster-cookie {
    width: 1.9rem;
    height: 1.9rem;
  }

  /* The label moves above its portraits: a 6.5rem column plus five circles does
     not fit, and shrinking the circles to keep the row costs more than the
     alignment is worth. */
  body[data-route="cookie-alliance"] .ca-roster-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 3px;
  }

  body[data-route="cookie-alliance"] .ca-roster-label {
    width: auto;
    max-width: 100%;
  }

  body[data-route="cookie-alliance"] .ca-roster {
    gap: 9px;
  }
}

@media (max-width: 324px) {
  body[data-route="cookie-alliance"] .premium-glass {
    padding: 0.75rem;
  }

  body[data-route="cookie-alliance"] .ca-team-card {
    padding: 8px;
  }
}
