/* The update/patch TIMELINE skin, shared by four surfaces.
   Was www/events/styles.css until 2026-09-02, when the one tabbed Events page
   split into two routes. Both of them, and both kinds of page they generate,
   render the SAME update -> block cards out of the same renderer
   (assets/js/events-render.mjs), so the skin has one definition here rather
   than a near-copy per route:

     /events/                In-Game Events — the hosting hub (active spotlight,
                             event history, the community-team feed)
     /events/{event-id}/     one event: its breakdown and its posted teams
     /updates/               Updates & Patch Notes — the patch-note timeline
     /updates/{update-slug}/ one update: its events expanded + its full notes

   IT IS THE ROUTE STYLESHEET FOR BOTH ROUTES — neither has one of its own, so a
   rule added here for one of them must be scoped, not left global. The scopes
   already in use are the body classes `.events-hub-page`, `.updates-page`,
   `.events-event-page` and `.events-update-page`, plus `[data-route]`.

   Selectors keep the historical `events-` prefix throughout. The split renamed
   no classes on purpose: renaming several hundred selectors would have touched
   the renderer, both bake scripts and 31 generated pages to change nothing that
   is visible.

   Its four url() references are ROOT-RELATIVE, not "../assets/…". A CSS url()
   resolves against the STYLESHEET, so when this file moved out of www/events/
   every one of them silently became /assets/assets/… and the hero art 404'd on
   four surfaces at once. Root-relative is also the only form that can be right
   here, since the file is loaded from three different directory depths.

   Builds on the shared design tokens from feature-page.css (--brand-*, --text-soft, --line,
   --radius-*). Per-event-type accent colors:
     battle   -> cool (cyan)     story    -> violet
     mini-game-> gold            gacha    -> warm (pink) */

.events-main {
  display: grid;
  gap: 0rem;
}

/* ----------------------------------------------------------------- Hero */

.events-status-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.events-hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 1.1rem;
}

.events-update-block {
  display: flex;
  flex-direction: row;
  line-height: 1.2;
  gap: 0.2rem;
}

.events-update-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-soft);
  opacity: 0.8;
  align-self: end;
}

.events-update-value {
  font-weight: 700;
  font-size: 1.05rem;
  color: #fff;
}

.events-hero-jump {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.events-secondary {
  font-size: 0.92rem;
  padding: 0.5rem 1rem;
}

/* ----------------------------------------------------------------- Section shells */

.events-section {
  display: grid;
  gap: 1.1rem;
}

/* An author `display` beats the UA stylesheet's [hidden] { display: none },
   so anything given one here needs `hidden` restored explicitly — otherwise
   the Next Update band would still show once nothing is staged, and the
   active grid would still show with no active events. */
.events-section[hidden],
.events-active-grid[hidden],
.events-next-list[hidden] {
  display: none;
}

.events-section-head h2 {
  margin-top: 0.35rem;
}

.events-section-sub {
  margin: 0.4rem 0 0;
  color: var(--text-soft);
  max-width: 760px;
  font-size: 1rem;
}

.events-pill-live {
  background: linear-gradient(120deg, rgba(0, 234, 255, 0.22), rgba(214, 26, 255, 0.22));
  border-color: rgba(0, 234, 255, 0.45);
}

.events-loading,
.events-empty,
.events-error {
  color: var(--text-soft);
  padding: 1.25rem 1.5rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius-md);
  background: rgba(16, 6, 14, 0.5);
}

.events-empty h3,
.events-error h3 {
  margin-bottom: 0.35rem;
}

/* ----------------------------------------------------------------- Active spotlight grid */

/* The spotlight holds one or two TIERS (playable / information-only), and the
   cards are gridded inside each — so this element is a column of tiers and the
   card grid itself lives on .events-active-grid-items one level down. */
.events-active-grid {
  display: grid;
  gap: 1.6rem;
}

.events-active-grid-items {
  display: grid;
  /* Max 3 equal (1fr) columns: each column is at least a third of the row
     (minus the two gaps), so never more than 3 fit; drops to 2 then 1 as the
     row narrows. */
  grid-template-columns: repeat(auto-fit, minmax(max(280px, (100% - 2.2rem) / 3), 1fr));
  gap: 1.1rem;
}

/* ---- Playable vs information-only tiers ----
   Playable events are the ones the team builder supports; everything else is a
   breakdown to read. Only the playable tier is LABELLED — the label marks where
   the interactive half of the site begins, and heading the remainder would give
   equal billing to the tier the reader did not come for. The unlabelled group
   is still a group (its own box, and an aria-label for screen readers), just a
   quieter one. */
.events-tier {
  display: grid;
  gap: 0.7rem;
}

.events-tier-label {
  margin: 0;
  font-family: 'CookieRun KR', 'Outfit', sans-serif;
  font-size: 0.82rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-soft);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

/* A rule that runs to the end of the row, so the label reads as a divider
   rather than as another card title. */
.events-tier-label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.25;
}

/* The information-only tier carries a hairline rule instead of a heading.
   Whitespace alone was not enough: the playable grid usually ends on a ragged
   row, so the gap before the next group is indistinguishable from the hole
   beside the last card, and it was ambiguous which group a card belonged to. A
   rule marks the boundary without naming the group — which is the whole point
   of leaving this tier unlabelled. It mirrors the rule trailing the playable
   label above, so the two groups are bracketed by the same line. */
.events-tier--info {
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}

/* Except when it is the ONLY tier — a drop with no playable events would
   otherwise open on a rule with nothing above it. */
.events-tier--info:first-child {
  padding-top: 0;
  border-top: 0;
}

.event-spotlight {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 1.25rem;
  position: relative;
  overflow: hidden;
  transition: transform 0.18s ease, border-color 0.18s ease;
}

.event-spotlight::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--type-accent, var(--brand-warm));
  opacity: 0.9;
}

.event-spotlight:hover {
  transform: translateY(-3px);
}

/* flow-root contains the floated badge so the name text wraps around it
   (first line beside the badge, later lines flow underneath) and the float
   never bleeds onto the tagline below. */
.event-spotlight-top {
  display: flow-root;
}

.event-spotlight-top .event-type-badge {
  float: left;
  margin: 0.15rem 0.5rem 0.25rem 0;
}

.event-spotlight-name {
  font-size: 1.2rem;
  margin: 0;
}

.event-spotlight-tagline {
  margin: 0;
  color: var(--text-soft);
  font-size: 0.95rem;
}

.event-spotlight-image {
  width: 100%;
  /* Grow to absorb the extra height when the grid stretches this card to match
     a taller sibling, instead of leaving an empty gap. Floors at 242px. */
  flex: 1 1 242px;
  min-height: 132px;
  object-fit: cover;
  border-radius: 0.6rem;
  border: 1px solid var(--line);
  display: block;
}

.event-spotlight-from {
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-soft);
  opacity: 0.85;
}

.event-spotlight-from strong {
  color: #fff;
}

.event-spotlight-link {
  padding-top: 0.4rem;
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--brand-cool);
  text-decoration: none;
}

.event-spotlight-link:hover {
  text-decoration: underline;
}

/* ----------------------------------------------------------------- Type badges */

.event-type-badge {
  display: inline-block;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  border: 1px solid var(--type-accent, var(--brand-warm));
  color: var(--type-accent, var(--brand-warm));
  background: rgba(255, 255, 255, 0.04);
  white-space: nowrap;
}

.type-battle { --type-accent: #00eaff; }
.type-story { --type-accent: #d61aff; }
.type-mini-game { --type-accent: #ffcc00; }
.type-gacha { --type-accent: #ff5c93; }
/* Missions & Unlocks: green, the one hue not already spoken for by the four
   types above. Every type in data/events.json needs a pair of rules — here and
   in the light-background block below — or its badge falls back to the brand
   warm and reads as an unstyled Battle. */
.type-progression { --type-accent: #3ddc84; }

.event-dates {
  font-size: 0.8rem;
  color: var(--text-soft);
  white-space: nowrap;
}

.event-live-badge,
.event-upcoming-badge,
.event-ended-badge {
  font-size: 0.7rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.18rem 0.5rem;
  border-radius: 999px;
}

.event-live-badge {
  color: #0a0712;
  background: linear-gradient(120deg, #00eaff, #38ffb0);
}

/* Staged content: gold reads as "pending", and stays clear of both the
   live indicator and the muted "concluded" treatment. */
.event-upcoming-badge {
  color: #2a1c00;
  background: linear-gradient(120deg, var(--brand-gold), #ffe680);
}

.event-ended-badge {
  color: var(--text-soft);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
}

/* ----------------------------------------------------------------- Capability chips */

.event-capabilities {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.event-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.28rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-soft);
  cursor: default;
}

.event-chip em {
  font-style: normal;
  font-weight: 800;
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brand-gold);
  opacity: 0.9;
}

.event-chip-builds { border-color: rgba(0, 234, 255, 0.35); }

a.event-chip.is-link {
  text-decoration: none;
  cursor: pointer;
  color: #fff;
  transition: border-color 0.15s ease, background 0.15s ease;
}

a.event-chip.is-link:hover {
  border-color: var(--brand-cool);
  background: rgba(0, 234, 255, 0.12);
}

/* Locked chips on staged content. They stay in the DOM so a card reads the
   same before and after release, but are visibly inert — clicking one asks
   app.js to explain why (a <button>, so it is keyboard reachable too). */
/* Applies to both variants: the <button> on the hub (explains on click) and
   the plain <span> baked into the JS-less per-update pages. */
.event-chip.is-locked {
  opacity: 0.62;
  border-style: dashed;
  filter: grayscale(0.35);
}

.event-chip.is-locked em {
  color: var(--brand-gold);
}

button.event-chip.is-locked {
  font-family: inherit;
  /* `help`, not `not-allowed`: the chip does respond — it explains itself. */
  cursor: help;
  transition: opacity 0.15s ease;
}

button.event-chip.is-locked:hover,
button.event-chip.is-locked:focus-visible {
  opacity: 0.85;
}

/* The explanation app.js reveals under the chip row when a locked chip is
   clicked. Hidden until then so no-JS visitors just see the inert chips. */
.event-locked-note {
  flex-basis: 100%;
  margin: 0.15rem 0 0;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--brand-gold);
}

/* ----------------------------------------------------------------- Next Update

   Staged drops: curated from a public update notice, but not in the game yet.
   Deliberately styled apart from both the live spotlight and the archive so a
   visitor can never mistake it for playable content — gold framing, an explicit
   "Next Update" badge, and the go-live stamp in the header. */

.events-next-list {
  display: grid;
  gap: 1.25rem;
}

.next-update {
  padding: 1.4rem 1.6rem 1.6rem;
  border-color: rgba(255, 204, 0, 0.42);
  background-image: linear-gradient(180deg, rgba(255, 204, 0, 0.08), transparent 260px);
  /* Deep-link target (#next-<block-id>) — clear the sticky header, matching
     the offset used for .event-card. */
  scroll-margin-top: 90px;
}

.next-update-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.next-update-heading {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.next-update-badge {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
  color: #2a1c00;
  background: linear-gradient(120deg, var(--brand-gold), #ffe680);
}

.next-update-title {
  font-size: clamp(1.15rem, 2vw, 1.6rem);
  margin: 0;
}

.next-update-cycle {
  font-size: 0.85rem;
  color: var(--text-soft);
}

.next-update-live {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  margin: 0;
  text-align: right;
}

.next-update-live-label {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text-soft);
  opacity: 0.85;
}

.next-update-live time {
  font-weight: 700;
  font-size: 1rem;
  color: var(--brand-gold);
}

.next-update-note {
  margin: 0.85rem 0 0;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--text-soft);
}

/* ----------------------------------------------------------------- Updates timeline */

.events-updates-list {
  display: grid;
  gap: 1.25rem;
}

.update-card {
  padding: 0;
  overflow: hidden;
}

.update-card.status-active {
  border-color: rgba(0, 234, 255, 0.4);
}

details > summary {
  list-style: none;
  cursor: pointer;
}

details > summary::-webkit-details-marker {
  display: none;
}

.update-summary {
  display: grid;
  gap: 0.55rem;
  padding: 1.4rem 1.6rem;
  position: relative;
}

.update-summary-main {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  /* Reserve room on the right so the date never collides with the absolutely
     positioned expand chevron (top-right of .update-summary). */
  padding-right: 1.75rem;
}

.update-summary-heading {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
}

.update-status-badge {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.22rem 0.6rem;
  border-radius: 999px;
}

.update-status-badge.status-active {
  color: #0a0712;
  background: linear-gradient(120deg, #00eaff, #38ffb0);
}

.update-status-badge.status-previous {
  color: var(--text-soft);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
}

.update-title {
  font-size: clamp(1.4rem, 2.4vw, 2rem);
}

.update-subtitle {
  font-size: 1rem;
  color: var(--brand-cool);
  font-weight: 600;
}

.update-released {
  font-size: 0.9rem;
  color: var(--text-soft);
  white-space: nowrap;
}

.update-headline {
  margin: 0;
  color: var(--text-soft);
  font-size: 1.02rem;
  max-width: 900px;
}

.update-chevron,
.event-card-chevron,
.update-patch-chevron {
  position: absolute;
  width: 12px;
  height: 12px;
  border-right: 2px solid var(--text-soft);
  border-bottom: 2px solid var(--text-soft);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

.update-chevron {
  top: 1.7rem;
  right: 1.6rem;
}

details[open] > .update-summary .update-chevron {
  transform: rotate(225deg);
}

.update-body {
  padding-bottom: 1.0rem;
  display: grid;
  gap: 1.4rem;
  border-top: 1px solid var(--line);
  margin-top: 0.2rem;
}

.update-overview {
  margin: 0;
  color: var(--text-soft);
  max-width: 960px;
}

.update-block-title {
  font-family: 'CookieRun KR', 'Outfit', sans-serif;
  font-size: 1.1rem;
  margin: 0 0 0.8rem;
  color: #fff;
}

/* ----------------------------------------------------------------- Cycle blocks (2-week drops) */

.update-blocks {
  display: grid;
  gap: 0;
}

/* Cycle-page permalink at the bottom of each update card. justify-self keeps
   the click target on the text, not the full grid row. */
.update-permalink {
  justify-self: start;
  margin: 0 0.9rem 0.5rem;
  font-weight: 700;
  font-size: 0.92rem;
  color: var(--brand-cool);
  text-decoration: none;
}

.update-permalink:hover {
  text-decoration: underline;
}

.cycle-block {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(14, 6, 12, 0.5);
  overflow: hidden;
}

.cycle-block.status-active {
  border-color: rgba(0, 234, 255, 0.35);
}

.cycle-block-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.95rem 1.2rem;
}

.cycle-block-heading {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.cycle-block-label {
  font-family: 'CookieRun KR', 'Outfit', sans-serif;
  font-size: 0.95rem;
  font-weight: 800;
  color: #fff;
}

.cycle-block-title {
  font-size: 0.9rem;
  color: var(--text-soft);
}

.cycle-block-status {
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
}

.cycle-block-status.status-active {
  color: #0a0712;
  background: linear-gradient(120deg, #00eaff, #38ffb0);
}

.cycle-block-status.status-previous {
  color: var(--text-soft);
  border: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.03);
}

.cycle-block-meta {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.cycle-block-chevron {
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--text-soft);
  border-bottom: 2px solid var(--text-soft);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}

details[open] > .cycle-block-summary .cycle-block-chevron {
  transform: rotate(225deg);
}

.cycle-block-body {
  padding: 0 1.2rem 1.2rem;
  display: grid;
  gap: 1.1rem;
  border-top: 1px solid var(--line);
  padding-top: 1rem;
}

.cycle-block-subtitle {
  margin: 0;
  color: var(--brand-cool);
  font-weight: 600;
}

.cycle-block-overview {
  margin: 0;
  color: var(--text-soft);
  max-width: 960px;
}

.event-availability {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  border: 1px solid var(--line);
  color: var(--text-soft);
  white-space: nowrap;
}

.event-availability.availability-full-cycle {
  color: #38ffb0;
  border-color: rgba(56, 255, 176, 0.4);
}

.event-availability.availability-limited {
  color: var(--brand-gold);
  border-color: rgba(255, 204, 0, 0.4);
}

/* ----------------------------------------------------------------- Event cards inside updates */

.update-events-grid {
  display: grid;
  /* Max 3 equal (1fr) columns (see .events-active-grid). */
  grid-template-columns: repeat(auto-fit, minmax(max(320px, (100% - 2rem) / 3), 1fr));
  gap: 1rem;
}

/* The event cards act as a tab strip: the collapsed frames stay in the grid
   and the clicked card's breakdown renders in .update-events-panels below all
   of them (see app.js). .event-tab is the clickable frame (a <button>), so it
   needs the usual button resets on top of the .event-card styling. */
.event-tab {
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  padding: 0;
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.event-tab[aria-selected="true"] .event-card-chevron {
  transform: rotate(225deg);
}

/* Breakdown panel: opened as an EXPANDING ROW — app.js moves it into the grid
   right after the clicked card's row, where it spans the full grid width (on
   mobile's single column it sits directly beneath the tapped card). A category
   accent on the top edge links it back to the active card's hue. */
.event-panel {
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
  padding: 1.3rem 1.4rem 1.4rem;
  border: 1px solid var(--line);
  border-top: 3px solid var(--type-accent, var(--brand-warm));
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.82);
}
.event-panel[hidden] {
  display: none;
}

/* Seated inside the grid: span the whole row; the grid gap provides the
   spacing, so drop the standalone margin. */
.update-events-grid > .event-panel {
  grid-column: 1 / -1;
  margin-top: 0;
}

.event-card {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(20, 8, 17, 0.6);
  border-left: 3px solid var(--type-accent, var(--brand-warm));
  scroll-margin-top: 90px;
  transition: box-shadow 0.3s ease, border-color 0.3s ease;
}

.event-card-summary {
  display: grid;
  gap: 0.45rem;
  padding: 1rem 1.2rem;
  position: relative;
}

.event-card-top {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.event-card-name {
  font-family: 'CookieRun KR', 'Outfit', sans-serif;
  font-size: 1.1rem;
  margin: 0;
  color: #fff;
  padding-right: 1.5rem;
}

.event-card-tagline {
  margin: 0;
  color: var(--text-soft);
  font-size: 0.92rem;
}

.event-card-chevron {
  top: 1.2rem;
  right: 1.1rem;
  width: 10px;
  height: 10px;
}


.event-card-body {
  padding: 0 1.2rem 1.2rem;
  display: grid;
  gap: 1rem;
}

.event-card-overview {
  margin: 0;
  color: var(--text-soft);
  font-size: 0.96rem;
}

.event-sections {
  display: grid;
  gap: 0.9rem;
}

.event-section-heading {
  font-size: 0.95rem;
  margin: 0 0 0.4rem;
  color: var(--brand-cool);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
}

.event-section-list {
  margin: 0;
  padding-left: 1.1rem;
  display: grid;
  gap: 0.35rem;
  color: var(--text-soft);
  font-size: 0.92rem;
}

.event-section-list li::marker {
  color: var(--type-accent, var(--brand-warm));
}

.event-card-footer {
  border-top: 1px solid var(--line);
  padding-top: 0.9rem;
}

.event-flash {
  border-color: var(--brand-cool);
  box-shadow: 0 0 0 2px rgba(0, 234, 255, 0.45), 0 0 30px rgba(0, 234, 255, 0.25);
}

/* ----------------------------------------------------------------- Media galleries */

.event-media-gallery {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 0.7rem 0 0.2rem;
}

.event-media-item {
  position: relative;
  display: inline-block;
  border-radius: 0.6rem;
  overflow: hidden;
  border: 1px solid var(--line);
  background: rgba(0, 0, 0, 0.3);
  line-height: 0;
  transition: border-color 0.15s ease, transform 0.15s ease;
}

.event-media-item:hover {
  border-color: var(--brand-cool);
  transform: translateY(-2px);
}

.event-media-item img {
  display: block;
  max-width: 100%;
  max-height: 360px;
  width: auto;
  height: auto;
}

.event-media-item.is-animated {
  border-color: rgba(0, 234, 255, 0.4);
}

.event-media-badge {
  position: absolute;
  top: 0.4rem;
  right: 0.4rem;
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  padding: 0.1rem 0.35rem;
  border-radius: 999px;
  color: #0a0712;
  background: linear-gradient(120deg, #00eaff, #38ffb0);
}

.patch-entry .event-media-gallery {
  margin-top: 0.6rem;
}

/* Multi-image galleries: each item grows in proportion to its image's aspect
   ratio (--ar, set by app.js once the image's natural size is known), so two
   images side by side land at the same height with no cropping and fill the
   row. Single-image galleries keep their natural size (class not applied). */
.event-media-gallery.is-balanced {
  flex-wrap: nowrap;
  align-items: flex-start;
}
.event-media-gallery.is-balanced .event-media-item {
  flex-grow: var(--ar, 1);
  flex-shrink: 1;
  flex-basis: 0;
  min-width: 0;
}
.event-media-gallery.is-balanced .event-media-item img {
  width: 100%;
  height: auto;
  max-height: none;
}

/* ----------------------------------------------------------------- Patch notes */

.update-patchnotes {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: rgba(12, 5, 11, 0.55);
}

.update-patch-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.2rem;
  margin: 0;
  position: relative;
}

.update-patch-chevron {
  position: static;
  margin-right: 0.2rem;
}

details[open] > .update-patch-summary .update-patch-chevron {
  transform: rotate(225deg);
}

.update-patch-body {
  padding: 0 1.2rem 1.2rem;
  display: grid;
  gap: 1.3rem;
  border-top: 1px solid var(--line);
  padding-top: 1.1rem;
}

.patch-section-heading {
  font-family: 'CookieRun KR', 'Outfit', sans-serif;
  font-size: 1rem;
  margin: 0 0 0.7rem;
  color: var(--brand-gold);
}

.patch-entries {
  display: grid;
  gap: 0.85rem;
}

.patch-entry {
  border-left: 2px solid var(--line);
  padding-left: 0.9rem;
}

.patch-entry-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.patch-entry-title {
  font-size: 0.98rem;
  margin: 0;
  color: #fff;
  font-weight: 700;
}

.patch-entry-tag {
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--brand-violet);
  border: 1px solid rgba(214, 26, 255, 0.4);
  border-radius: 999px;
  padding: 0.12rem 0.45rem;
}

.patch-entry-body {
  margin: 0.35rem 0 0;
  color: var(--text-soft);
  font-size: 0.92rem;
  line-height: 1.5;
}

/* ----------------------------------------------------------------- Responsive */

@media (max-width: 640px) {
  .events-active-grid-items,
  .update-events-grid {
    grid-template-columns: 1fr;
  }

  .update-summary,
  .update-body {
    padding-left: 1.1rem;
    padding-right: 1.1rem;
  }

  .update-chevron {
    right: 1.1rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .event-spotlight,
  .update-chevron,
  .event-card-chevron,
  .update-patch-chevron,
  .event-card {
    transition: none;
  }
}

/* =====================================================================
   WARM LIGHT THEME (experimental)
   Scoped entirely to body.events-page so no other route is affected.
   Preserves the per-category color system (battle/story/mini-game/gacha)
   but deepens the hues and fills the badges so they read on a light bg.
   To revert the whole experiment, delete this block.
   ===================================================================== */

body.events-page {
  /* Re-pointed design tokens (inherited by the whole subtree). V-45b: these were
     the warm cream palette; they now read the shared --tcb-* theme. The
     "live / active" green stays a literal because it signals state, not theme. */
  --text: var(--tcb-ink);
  --text-soft: var(--tcb-ink-soft);
  --line: var(--tcb-line);
  --brand-cool: var(--tcb-brand-strong);
  --brand-gold: var(--tcb-brand-strong);
  --brand-violet: var(--tcb-accent);
  --events-live: #34d399;  /* "live / active" indicator, lifted for a dark base */
  --events-ink: var(--tcb-ink);

  background: var(--tcb-page-backdrop);
  color: var(--text);
}

/* `:not(.cookie-name)` on the h3, and only there. Below 700px the team card
   swaps each portrait for the Cookie-Dex "Comfy" card, whose cookie name is
   the one heading assets/js/team-build-card.js emits (`<h3 class="cookie-name">`).
   These rules are (0,2,1) and dex-cookie-card.css's `.tb-dexcard .cookie-name`
   is (0,2,0), so on an event page — and nowhere else, since no hub stylesheet
   declares h1-h6 — the name lost its colour and both drop shadows and took a
   1.2 line-height. Excluded rather than restated, so the values stay in
   dex-cookie-card.css where they belong. */
body.events-page h1,
body.events-page h2,
body.events-page h3:not(.cookie-name),
body.events-page h4,
body.events-page h5,
body.events-page h6 {
  color: var(--events-ink);
  /* The white 1px lift belonged to the cream background; on dark it haloes. */
  text-shadow: none;
}

/* The shared footer's contact-card title is an <h2>, so the warm-ink heading
   rule above catches it and darkens it against the dark footer. Restore the
   footer's intended light title colour. */
body.events-page .site-contact-card-title {
  color: #f3fbff;
  text-shadow: none;
}

body.events-page h3:not(.cookie-name) {
  line-height: 1.2;
}

body.events-page .event-spotlight-name {
  line-height: 1.5;
}

/* Chaos Dragon Raid: top-align the spotlight image so the dragon's head stays
   in frame instead of being centre-cropped. */
body.events-page .event-spotlight[data-event-id="chaos-dragon-raid"] .event-spotlight-image {
  object-position: top;
}

/* ---------------------------------------------------------------------------
   Shadows
   ---------------------------------------------------------------------------
   One tint token drives every drop shadow on this page. The default is a pale
   purple that sits with the dark theme; the old value was rgba(120, 70, 50, …),
   a warm brown left over from when this page had a cream background, which read
   as a muddy halo once the theme went dark.

   Frames that carry a --type-accent (story purple, battle blue, mini-game
   amber, gacha pink …) re-derive the tint from their OWN accent just below, so
   a card's shadow supports its colour instead of fighting it. Everything else
   keeps the shared purple.

   Intensity is deliberately lower than before — 8px/18px at ~0.22 alpha
   against the previous 12px/26px at 0.52. Change the two tokens to restyle the
   whole page.

   GEOMETRY AND TINT ARE SEPARATE TOKENS ON PURPOSE, composed at each use site:
     box-shadow: var(--events-shadow-geometry) var(--events-shadow-tint);
   Folding them into one `--events-shadow: 0 8px 18px var(--tint)` on <body>
   looks tidier and silently breaks the per-type colour — a var() inside a
   custom property resolves on the element the property is DECLARED on, so the
   composed value would capture body's tint and every card would inherit the
   same purple no matter what it re-declared. Same trap the mobile hero-art
   dials call out in shared-shell.css.
   -------------------------------------------------------------------------- */
body.events-page {
  --events-shadow-geometry: 0 4px 18px;
  --events-shadow-tint: rgba(178, 150, 255, 0.22);
}

/* Typed frames tint their own shadow. The var() fallback inside color-mix
   matters: without it, an element with no --type-accent would make the whole
   color-mix invalid and drop the tint entirely. */
body.events-page .event-spotlight,
body.events-page .event-card,
body.events-page .event-panel,
body.events-page .event-static {
  --events-shadow-tint: color-mix(in srgb, var(--type-accent, #b296ff) 20%, transparent);
}

/* Glass surfaces become warm, near-white panels */
body.events-page .premium-glass {
  background: linear-gradient(150deg, rgba(255, 253, 250, 0.94), rgba(255, 246, 237, 0.9));
  border-color: var(--line);
  box-shadow: var(--events-shadow-geometry) var(--events-shadow-tint), inset 0 1px 0 rgba(255, 255, 255, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

body.events-page .feature-hero {
  border-radius: 0;
}

/* Full-bleed hero: same break-out as the section bands, with side padding
   re-indenting the content onto the 1600px line (1.5rem min gutter). */
body.events-page .events-hero {
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding-left: max(1.5rem, calc(50vw - 800px));
  padding-right: max(1.5rem, calc(50vw - 800px));
  border: none;
  /* Drop premium-glass's inset top highlight (a 1px white bevel meant for the
     opaque panels): over the hero artwork it renders above the background as a
     bright line the gradient can't cover. Keep the outer drop shadow. */
  box-shadow: var(--events-shadow-geometry) var(--events-shadow-tint);
}

/* Hero background art: the kingdom plaza (castle right-of-center) anchored to
   the right, with a warm wash fading left-to-right so the hero copy on the
   left half stays readable over the busy artwork. --events-hero-bg-y tunes
   the image's vertical anchor (0% = top of the art, 100% = bottom). */
body.events-page .events-hero {
  --events-hero-bg-y: 75%;
  background:
    linear-gradient(
      90deg,
      rgba(255, 250, 243, 0.97) 0%,
      rgba(255, 250, 243, 0.73) 34%,
      rgba(255, 248, 240, 0.1) 58%,
      rgba(255, 246, 237, 0) 88%
    ),
    url("/assets/events-page/events-hero-bg-v1.webp") right var(--events-hero-bg-y) / cover no-repeat;
}

/* Hardcoded white text -> warm ink */
body.events-page .events-update-value,
body.events-page .event-spotlight-from strong,
body.events-page .update-block-title,
body.events-page .cycle-block-label,
body.events-page .event-card-name,
body.events-page .patch-entry-title,
body.events-page a.event-chip.is-link {
  color: var(--events-ink);
}

/* Eyebrow takes the shared .eyebrow treatment (purple + glow). The old
   #c2410c was chosen when this hero had a cream wash; the hero is dark since
   the theme consolidation, where warm orange reads as low contrast. */

/* Hero jump buttons: .btn-glow is white-on-translucent (built for the dark
   theme) and was invisible here — give them a visible warm outline style. */
body.events-page .events-secondary {
  color: #b1441f;
  background: rgba(210, 85, 47, 0.1);
  border: 1.5px solid rgba(210, 85, 47, 0.55);
  box-shadow: none;
  text-shadow: none;
  margin-top: 0;
}
body.events-page .events-secondary:hover {
  background: rgba(210, 85, 47, 0.18);
  border-color: #d2552f;
  box-shadow: var(--events-shadow-geometry) var(--events-shadow-tint);
}

/* Section labels (.alpha-page-status): the page already has plenty of pill/
   badge shapes, so these read as a flat "kicker" instead — a short accent
   line followed by spaced uppercase text, no pill background. */
body.events-page .alpha-page-status {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
  color: #b1441f;
  font-family: 'Outfit', sans-serif;
  font-weight: 800;
  font-size: 0.74rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
body.events-page .alpha-page-status::before {
  content: "";
  width: 1.6rem;
  height: 3px;
  border-radius: 99px;
  background: currentColor;
  flex: none;
}

/* "Live Now": green, with a pulsing dot instead of the line */
body.events-page .events-pill-live {
  color: #0f7a37;
}
body.events-page .events-pill-live::before {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  animation: events-live-pulse 1.8s ease-in-out infinite;
}
@keyframes events-live-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.72); }
}
@media (prefers-reduced-motion: reduce) {
  body.events-page .events-pill-live::before { animation: none; }
}

/* ---- Per-category colors: deepened for light bg, badges filled ----
   --type-accent stays a flat color for borders / list markers / top bars;
   --type-gradient is the same hue run as a linear gradient (accent -> deeper)
   used for filled surfaces like the badges and the spotlight top accent bar. */
body.events-page .type-battle {
  --type-accent: #0e87b0;
  --type-gradient: linear-gradient(130deg, #1cc6ec 0%, #0e87b0 48%, #053b4d 100%);
  --type-border-gradient: radial-gradient(circle at top left, #1cc6ec 0%, #0e87b0 34%, transparent 76%);
}
body.events-page .type-story {
  --type-accent: #9333ea;
  --type-gradient: linear-gradient(130deg, #c074ff 0%, #9333ea 48%, #4d1488 100%);
  --type-border-gradient: radial-gradient(circle at top left, #c074ff 0%, #9333ea 34%, transparent 76%);
}
body.events-page .type-mini-game {
  --type-accent: #ab6800;
  --type-gradient: linear-gradient(130deg, #f5a623 0%, #c47d08 48%, #5e3700 100%);
  --type-border-gradient: radial-gradient(circle at top left, #f5a623 0%, #c47d08 34%, transparent 76%);
}
body.events-page .type-gacha {
  --type-accent: #db2777;
  --type-gradient: linear-gradient(130deg, #ff5fa2 0%, #db2777 48%, #8f1149 100%);
  --type-border-gradient: radial-gradient(circle at top left, #ff5fa2 0%, #db2777 34%, transparent 76%);
}
body.events-page .type-progression {
  --type-accent: #15803d;
  --type-gradient: linear-gradient(130deg, #4ade80 0%, #15803d 48%, #08361a 100%);
  --type-border-gradient: radial-gradient(circle at top left, #4ade80 0%, #15803d 34%, transparent 76%);
}

body.events-page .event-type-badge {
  color: #fff;
  border-color: transparent;
  background: var(--type-gradient, var(--type-accent, var(--brand-warm)));
  /* keeps white text legible over the bright end of the gradient */
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Spotlight category accent border: a single rounded gradient RING drawn with
   the mask technique, so it follows the card's corner radius and the boldest
   top-left corner is never clipped (the old straight strips were cut by the
   rounded overflow:hidden corner). The corner-anchored radial fades the
   bottom/right edges to transparent, leaving a glowing top+left border. */
body.events-page .event-spotlight {
  overflow: visible;
}
body.events-page .event-spotlight::before {
  inset: 0;
  width: auto;
  height: auto;
  border-radius: inherit;
  padding: 3px;
  background: var(--type-border-gradient, var(--type-gradient, var(--type-accent)));
  opacity: 1;
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* Event cards get the same treatment as the active-grid spotlight cards: a
   corner-anchored category-hue RING (boldest top-left, fading toward the
   bottom/right, drawn with the mask technique so it follows the radius) plus
   the warm premium-glass drop shadow. */
body.events-page .event-card {
  position: relative;
  overflow: visible;
  border-color: transparent;
  border-left-width: 1px;
  background: rgba(255, 255, 255, 0.82);
  box-shadow: var(--events-shadow-geometry) var(--events-shadow-tint), inset 0 1px 0 rgba(255, 255, 255, 0.6);
}
body.events-page .event-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 3px;
  background: var(--type-border-gradient, var(--type-gradient, var(--type-accent)));
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

/* ---- "Live / active" indicators -> green (was cyan/mint) ---- */
body.events-page .event-live-badge,
body.events-page .update-status-badge.status-active,
body.events-page .cycle-block-status.status-active {
  color: #fff;
  background: linear-gradient(120deg, #2bbf6a, var(--events-live));
}
body.events-page .update-card.status-active,
body.events-page .cycle-block.status-active {
  border-color: rgba(21, 163, 74, 0.45);
}
body.events-page .event-availability.availability-full-cycle {
  color: #0f7a37;
  border-color: rgba(21, 163, 74, 0.45);
}
body.events-page .event-media-item.is-animated {
  border-color: rgba(21, 163, 74, 0.45);
}
body.events-page .event-media-badge {
  color: #fff;
  background: linear-gradient(120deg, #2bbf6a, var(--events-live));
}
body.events-page .event-flash {
  border-color: var(--events-live);
  box-shadow: 0 0 0 2px rgba(21, 163, 74, 0.4), 0 0 26px rgba(21, 163, 74, 0.22);
}

/* "ended / previous" chips on light */
body.events-page .event-ended-badge,
body.events-page .update-status-badge.status-previous,
body.events-page .cycle-block-status.status-previous,
body.events-page .event-availability {
  background: rgba(150, 95, 72, 0.1);
  color: #7d6358;
  border-color: var(--line);
}

/* ---- "upcoming / staged" indicators -> amber on the warm light palette ---- */
body.events-page .event-upcoming-badge,
body.events-page .update-status-badge.status-upcoming,
body.events-page .cycle-block-status.status-upcoming,
body.events-page .next-update-badge {
  color: #fff;
  background: linear-gradient(120deg, #d98a06, var(--brand-gold));
}
body.events-page .update-card.status-upcoming,
body.events-page .cycle-block.status-upcoming,
body.events-page .next-update {
  border-color: rgba(184, 120, 12, 0.45);
}
body.events-page .next-update {
  background-color: rgba(255, 248, 239, 0.66);
  background-image: linear-gradient(180deg, rgba(184, 120, 12, 0.09), transparent 260px);
}
body.events-page .next-update-live time,
body.events-page .event-locked-note,
body.events-page .event-chip.is-locked em {
  color: #9a6408;
}

/* ---- Nested surfaces: light warm fills (override dark rgba) ---- */
body.events-page .cycle-block {
  background: transparent;
  border: 0;
  border-radius: 0rem;
}
body.events-page .cycle-block-summary {
  background: linear-gradient(180deg, rgb(226 157 255 / 41%), rgb(101 28 129 / 70%));
}
body.events-page .update-patchnotes {
  background: rgba(255, 248, 239, 0.66);
}
body.events-page .events-loading,
body.events-page .events-empty,
body.events-page .events-error {
  background: rgba(255, 248, 240, 0.6);
}
body.events-page .event-media-item {
  background: rgba(150, 95, 72, 0.08);
}
body.events-page .event-chip {
  background: rgba(255, 255, 255, 0.55);
}

/* ---- Section bands: full-bleed fill, content locked to the 1600px column ----
   The bands break out of the 1600px .route-main column to the viewport edges
   (negative side margins, no fixed width so no scrollbar overflow), then the
   side padding re-indents the content back onto the 1600px line. */
body.events-page .events-active-section,
body.events-page .events-next-section,
body.events-page .events-updates-section {
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding: 1rem max(0.68rem, calc(50vw - 800px));
  border: 0;
  border-radius: 0rem;
}
body.events-page .events-active-section {
  background: linear-gradient(180deg, rgba(255, 240, 226, 0.85), rgba(255, 233, 214, 0.7));
}
/* Warm amber band so the staged section is legible as its own zone between
   the live spotlight above it and the archive below. */
body.events-page .events-next-section {
  background: linear-gradient(180deg, rgba(255, 244, 219, 0.9), rgba(255, 236, 205, 0.72));
}
body.events-page .events-updates-section {
  background: linear-gradient(180deg, rgba(253, 235, 238, 0.85), rgba(252, 238, 230, 0.7));
}

/* Reuse the active-section warm fill on the update patch body frame */
body.events-page .update-body {
  background: linear-gradient(180deg, rgba(255, 240, 226, 0.85), rgba(255, 233, 214, 0.7));
}

/* Desktop: trim the main padding down to just a bottom gap */
body.events-page .route-main {
  margin-top: 0;
  padding: 0 0 1rem;
}

/* ---- Cross-link between the two halves of the old combined page ----
   /events/ and /updates/ were one tabbed page until 2026-09-02. A reader who
   lands on the wrong one of the two should not have to go via the nav to find
   the other, so each hero carries a quiet link to its sibling. Deliberately not
   a `btn-glow`: the CTA beside it is the page's actual action, and two buttons
   of equal weight would make "go to the other page" look like the main one. */
.events-hero-crosslink {
  font: 700 0.86rem "Outfit", sans-serif;
  color: var(--text-soft);
  text-decoration: none;
  border-bottom: 1px solid currentColor;
  white-space: nowrap;
}

.events-hero-crosslink:hover,
.events-hero-crosslink:focus-visible {
  color: var(--tcb-brand, #d2552f);
}

/* Patch-view patch notes render expanded (no collapsible wrapper): the block
   title takes the summary row's place above the bordered body. */
body.events-page .update-patchnotes.is-view-body .update-patch-title {
  margin: 0;
  padding: 1rem 1.2rem;
}

/* ---- Mobile-only tightening (kept last so it overrides the theme block) ----
   Compact pass: smaller type, tighter paddings/gaps on every nested surface
   (hero, spotlight cards, update cards, cycle blocks, event tabs/panels,
   patch notes) so far more content fits per screen. */
@media (max-width: 699px) {
  /* Full-bleed section bands: keep the warm fill, drop the frame + padding */
  body.events-page .events-active-section,
  body.events-page .events-next-section,
  body.events-page .events-updates-section {
    padding: 0.5rem 0.4rem;
    border: 0;
    border-radius: 0;
  }

  body.events-page .next-update {
    padding: 0.85rem 0.75rem 1rem;
  }
  body.events-page .next-update-head {
    gap: 0.5rem;
  }
  body.events-page .next-update-live {
    text-align: left;
  }

  body.events-page .premium-glass {
    padding: 0.85rem 0.75rem;
  }

  /* The update cards are .premium-glass too — keep them at zero padding
     (their .update-summary / .update-body children own the spacing). */
  body.events-page .update-card {
    padding: 0;
  }

  body.events-page .events-main {
    gap: 0;
  }

  body.events-page .route-main {
    margin-top: 0rem;
    padding-bottom: 0.75rem;
  }

  /* ---------------- Hero ---------------- */
  /* Full-width copy on mobile: swap the left-to-right fade for a vertical
     wash — opaque behind the text, art showing through toward the bottom.
     The art is height-sized (auto width) so it fills the frame at scale 100%
     just like cover did; the three vars tune it independently:
       --events-hero-bg-scale-mobile  zoom (100% = fill; raise to zoom in,
                                       which also opens up vertical pan room)
       --events-hero-bg-x-mobile      horizontal anchor
       --events-hero-bg-y-mobile      vertical anchor (only bites once zoomed) */
  body.events-page .events-hero {
    position: relative;
    --events-hero-bg-scale-mobile: 130%;
    --events-hero-bg-x-mobile: 74%;
    --events-hero-bg-y-mobile: 80%;
    background:
      linear-gradient(
        180deg,
        rgba(255, 250, 243, 0.96) 0%,
        rgba(255, 250, 243, 0.8) 68%,
        rgba(255, 248, 240, 0) 100%
      ),
      url("/assets/events-page/events-hero-bg-v1.webp")
        var(--events-hero-bg-x-mobile) var(--events-hero-bg-y-mobile)
        / auto var(--events-hero-bg-scale-mobile) no-repeat;
  }

  body.events-page #eventsTitle {
    font-size: 1.6rem;
    margin: 0.15rem 0 0;
  }

  body.events-page .eyebrow {
    font-size: 0.72rem;
  }

  body.events-page .hero-copy {
    font-size: 0.92rem;
    line-height: 1.2;
    margin-top: 1.5rem;
    margin-bottom: 0.5rem;
  }

  /* "Last updated" used to pin to the hero's top-right corner here (the hero is
     position: relative on mobile), because the hub's actions row held EXACTLY
     ONE meta block and taking it out of the flow cost the phone hero nothing.
     That precondition died with the 2026-09-02 split: both hubs now carry a
     cross-link to the other route, and /events/ a "Build a team" CTA as well,
     so the pinned block printed straight through them. The rule the pin was
     avoiding (two blocks on one pair of coordinates) is the same failure.
     Everything stays in the FLOW on a phone now, stacked full-width and
     left-aligned, which is what the per-update and per-event heroes below have
     always done with their own pair.

     The :not() stays: the sub-page heroes have their own sizing further down,
     and this row is the hub's. */
  body.events-page:not(.events-update-page):not(.events-event-page) .events-hero-actions {
    margin-top: 0.85rem;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.7rem;
  }

  body.events-page .events-update-label {
    font-size: 0.68rem;
  }

  body.events-page .events-update-value {
    font-size: 0.92rem;
  }

  /* The two sub-page heroes stay in the flow, which means their pair of meta
     blocks has to actually fit on one line. Two things were working against
     that: the base `justify-content: space-between` flung the second block to
     the far edge (so "Runs …" and "Update …" read as unrelated items, the
     second one sitting over unscrimmed art), and at the hub's meta sizes the
     pair measured ~335px against 336px of content box at 360px — one pixel
     from wrapping, and past it on anything narrower. Grouping them left and
     stepping the type down to 0.62/0.85rem brings the pair to ~309px, so it
     holds one line down to 360px and only stacks below that, where stacking is
     the right answer anyway.

     These match `body.events-page …` above on specificity, so they only win by
     sitting after it. Keep them here. */
  body.events-update-page .events-hero-actions,
  body.events-event-page .events-hero-actions {
    justify-content: flex-start;
    gap: 0.5rem 1.4rem;
  }

  body.events-update-page .events-update-label,
  body.events-event-page .events-update-label {
    font-size: 0.62rem;
    letter-spacing: 0.1em;
  }

  body.events-update-page .events-update-value,
  body.events-event-page .events-update-value {
    font-size: 0.85rem;
  }

  body.events-page .events-hero-jump {
    gap: 0.45rem;
  }

  body.events-page .events-secondary {
    font-size: 0.82rem;
    padding: 0.38rem 0.75rem;
  }

  /* ---------------- Section heads ---------------- */
  body.events-page .events-section {
    gap: 0.7rem;
  }

  body.events-page .events-section-head h2 {
    font-size: 1.45rem;
    margin-top: 0.2rem;
  }

  body.events-page .events-section-sub {
    font-size: 0.88rem;
    margin-top: 0.25rem;
  }

  body.events-page .alpha-page-status {
    font-size: 0.66rem;
  }

  body.events-page .events-loading,
  body.events-page .events-empty,
  body.events-page .events-error {
    padding: 0.85rem 1rem;
  }

  /* ---------------- Active spotlight cards ---------------- */
  body.events-page .events-active-grid {
    gap: 1.1rem;
  }

  body.events-page .events-active-grid-items {
    gap: 0.7rem;
  }

  body.events-page .event-spotlight {
    padding: 0.85rem;
    gap: 0.45rem;
  }

  body.events-page .event-spotlight-name {
    font-size: 1.02rem;
    line-height: 1.35;
  }

  body.events-page .event-spotlight-tagline {
    font-size: 0.86rem;
  }

  body.events-page .event-spotlight-image {
    flex-basis: 170px;
    min-height: 110px;
  }

  body.events-page .event-spotlight-from {
    font-size: 0.78rem;
  }

  body.events-page .event-spotlight-link {
    font-size: 0.85rem;
    padding-top: 0.2rem;
  }

  /* ---------------- Badges, chips, dates ---------------- */
  body.events-page .event-type-badge {
    font-size: 0.6rem;
    padding: 0.14rem 0.42rem;
  }

  body.events-page .event-live-badge,
  body.events-page .event-ended-badge {
    font-size: 0.62rem;
    padding: 0.14rem 0.42rem;
  }

  body.events-page .event-dates {
    font-size: 0.72rem;
  }

  body.events-page .event-availability {
    font-size: 0.6rem;
  }

  body.events-page .event-capabilities {
    gap: 0.3rem;
  }

  body.events-page .event-chip {
    font-size: 0.72rem;
    padding: 0.2rem 0.5rem;
  }

  /* ---------------- Updates timeline ---------------- */
  body.events-page .events-updates-list {
    gap: 0.8rem;
  }

  body.events-page .update-summary {
    padding: 0.85rem 0.65rem;
    gap: 0.4rem;
  }

  body.events-page .update-summary-main {
    gap: 0.5rem;
    padding-right: 1.4rem;
  }

  body.events-page .update-summary-heading {
    gap: 0.5rem;
  }

  body.events-page .update-status-badge {
    font-size: 0.62rem;
    padding: 0.16rem 0.45rem;
  }

  body.events-page .update-title {
    font-size: 1.25rem;
  }

  body.events-page .update-subtitle {
    font-size: 0.9rem;
  }

  body.events-page .update-released {
    font-size: 0.78rem;
  }

  body.events-page .update-headline {
    font-size: 0.88rem;
  }

  body.events-page .update-chevron {
    top: 1.05rem;
    right: 0.65rem;
  }

  /* Chevrons run 2px smaller across the board on mobile (update/patch 12->10,
     event-card/cycle-block 10->8). */
  body.events-page .update-chevron,
  body.events-page .update-patch-chevron {
    width: 10px;
    height: 10px;
  }

  body.events-page .event-card-chevron,
  body.events-page .cycle-block-chevron {
    width: 8px;
    height: 8px;
  }

  /* Collapse the nested side gutters: the update body runs edge to edge so the
     inner cards sit only their own (section band + cycle block) padding from
     the screen edge. */
  body.events-page .update-body {
    padding-left: 0rem;
    padding-right: 0rem;
    padding-bottom: 0.8rem;
    gap: 0.9rem;
  }

  /* Bare text sits closer to the card edge now — give it a touch of indent
     while the nested boxes (cycle blocks, patch notes) run full width. */
  body.events-page .update-overview {
    font-size: 0.9rem;
    padding: 0 0.25rem;
  }

  body.events-page .update-block-title {
    font-size: 1rem;
    margin-bottom: 0.5rem;
  }

  /* ---------------- Cycle blocks ---------------- */
  body.events-page .cycle-block-summary {
    padding: 0.7rem 0.6rem;
    gap: 0.5rem;
  }

  body.events-page .cycle-block-heading {
    gap: 0.45rem;
  }

  body.events-page .cycle-block-label {
    font-size: 0.88rem;
  }

  body.events-page .cycle-block-title {
    font-size: 0.8rem;
  }

  body.events-page .cycle-block-status {
    font-size: 0.6rem;
  }

  body.events-page .cycle-block-meta {
    gap: 0.5rem;
  }

  body.events-page .cycle-block-body {
    padding: 0.75rem 0.4rem 0.8rem;
    gap: 0.75rem;
  }

  body.events-page .cycle-block-overview,
  body.events-page .cycle-block-subtitle,
  body.events-page .update-block-title {
    padding: 0 0.25rem;
  }

  body.events-page .cycle-block-overview {
    font-size: 0.9rem;
  }

  /* ---------------- Event cards (tabs) + shared panel ---------------- */
  body.events-page .update-events-grid {
    gap: 0.6rem;
  }

  body.events-page .event-card-summary {
    padding: 0.75rem 0.7rem;
    gap: 0.35rem;
  }

  /* Keep the badge row clear of the absolutely-positioned chevron */
  body.events-page .event-card-top {
    padding-right: 1.5rem;
    gap: 0.4rem;
  }

  body.events-page .event-card-name {
    font-size: 1rem;
  }

  body.events-page .event-card-tagline {
    font-size: 0.85rem;
  }

  body.events-page .event-card-chevron {
    top: 0.85rem;
    right: 0.7rem;
  }

  body.events-page .event-panel {
    margin-top: 0.7rem;
    padding: 0.8rem 0.6rem;
    gap: 0.7rem;
  }

  body.events-page .event-card-body {
    padding: 0 0.7rem 0.75rem;
    gap: 0.7rem;
  }

  body.events-page .event-card-overview {
    font-size: 0.88rem;
  }

  body.events-page .event-sections {
    gap: 0.6rem;
  }

  body.events-page .event-section-heading {
    font-size: 0.85rem;
    margin-bottom: 0.3rem;
  }

  body.events-page .event-section-list {
    font-size: 0.86rem;
    gap: 0.25rem;
    padding-left: 1rem;
  }

  body.events-page .event-card-footer {
    padding-top: 0.6rem;
  }

  /* ---------------- Media galleries ---------------- */
  body.events-page .event-media-gallery {
    gap: 0.5rem;
    margin-top: 0.5rem;
  }

  /* Side-by-side media (balanced or not) stacks vertically on mobile,
     every image filling the available frame width. */
  body.events-page .event-media-gallery.is-balanced {
    flex-wrap: wrap;
  }

  body.events-page .event-media-item,
  body.events-page .event-media-gallery.is-balanced .event-media-item {
    flex: none;
    width: 100%;
  }

  body.events-page .event-media-item img {
    width: 100%;
    height: auto;
    max-height: none;
  }

  /* ---------------- Patch notes ---------------- */
  body.events-page .update-patch-summary {
    padding: 0.75rem 0.6rem;
  }

  body.events-page .update-patchnotes.is-view-body .update-patch-title {
    padding: 0.75rem 0.6rem 0.4rem;
  }

  body.events-page .update-patch-body {
    padding: 0.8rem 0.45rem 0.8rem;
    gap: 0.85rem;
  }

  body.events-page .patch-section-heading {
    font-size: 0.92rem;
    margin-bottom: 0.5rem;
  }

  body.events-page .patch-entries {
    gap: 0.6rem;
  }

  body.events-page .patch-entry {
    padding-left: 0.7rem;
  }

  body.events-page .patch-entry-title {
    font-size: 0.9rem;
  }

  body.events-page .patch-entry-tag {
    font-size: 0.62rem;
  }

  body.events-page .patch-entry-body {
    font-size: 0.86rem;
    margin-top: 0.25rem;
  }
}

/* ---- Per-update pages (/events/{slug}/) --------------------------------- */
/* Static, fully-expanded view of one update cycle (baked by bake-events.mjs). */
.events-update-main {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.events-update-main .breadcrumbs {
  font-size: 0.82rem;
  color: var(--text-soft, #c9b8d6);
  opacity: 0.9;
}

.events-update-main .breadcrumbs a {
  color: inherit;
  text-decoration: none;
}

.events-update-main .breadcrumbs a:hover {
  text-decoration: underline;
}

/* .feature-hero carries align-self: center (from feature-page.css), which is
   inert in the hub's grid main but — because .events-update-main is a COLUMN
   flex container — turns the hero into a shrink-to-fit centered card here,
   dropping the full-bleed banner treatment. Stretch it back so the per-update
   hero matches the hub hero exactly (same bleed, scrim, and art rules). */
.events-update-main .events-hero {
  align-self: stretch;
}

/* Keep the hero copy on the scrimmed left half of the banner (the hub's intro
   paragraph is short enough to sit there naturally; cycle summaries are not). */
.events-update-page .events-hero .hero-copy {
  max-width: 44rem;
}

.events-update-page .events-hero .events-status-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* Group the meta blocks on the scrimmed left edge (the hub pattern);
   space-between pushed the second one onto the unscrimmed art on the right.
   Per-event heroes have the same pair and the same problem, so they share it —
   they were left on space-between when this was written for update pages. */
.events-update-page .events-hero-actions,
.events-event-page .events-hero-actions {
  justify-content: flex-start;
  gap: 1rem 2.75rem;
}

@media (max-width: 640px) {
  /* The mobile gap and the pin are both handled in the 699px block now — a
     640px override was what left 641–699px on the old, broken values. */

  body.events-update-page .hero-copy {
    margin-bottom: 0.75rem;
  }

  /* Cycle heroes carry much more copy than the hub hero: hold the wash
     further down the banner so the summary and meta stay readable, warming
     into a peach glow at the bottom edge. */
  body.events-update-page .events-hero {
    background:
      linear-gradient(
        180deg,
        rgb(255 242 223) 0%,
        rgb(255 255 255 / 96%) 72%,
        rgb(255 192 119 / 62%) 100%
      ),
      url("/assets/events-page/events-hero-bg-v1.webp")
        var(--events-hero-bg-x-mobile) var(--events-hero-bg-y-mobile)
        / auto var(--events-hero-bg-scale-mobile) no-repeat;
  }
}

.update-block-static {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}

.update-block-static + .update-block-static {
  margin-top: 1.4rem;
}

.update-block-static-head {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.update-events-static {
  display: grid;
  gap: 1rem;
}

.event-static {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 1.1rem 1.25rem;
  scroll-margin-top: 5rem;
}

.event-static-top {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.event-static-name {
  margin: 0.1rem 0 0;
  font-size: 1.16rem;
}

.events-update-backlink {
  margin: 0.4rem 0 0.5rem;
}

.events-update-backlink a {
  color: var(--text-soft, #c9b8d6);
  text-decoration: none;
  font-weight: 600;
}

.events-update-backlink a:hover {
  color: #fff;
  text-shadow: 0 0 10px rgba(0, 234, 255, 0.6);
}

/* Nav dropdown link color for this route. Overrides the shared --text-soft the
   resting ".bento-nav a, .feature-nav-dropdown-toggle" rule reads, so hover /
   active links keep their own #fff highlight. */
body.events-page .bento-nav {
  --text-soft: #c6bbb6;
}


/* =============================================================================
   Site theme (V-45b) — Events & Updates.
   The page's own design tokens above now read the shared --tcb-* palette, which
   covers most of the page by inheritance. This block handles the surfaces that
   painted their cream directly.

   Left alone on purpose: the per-event-type accents (battle cyan, story violet,
   mini-game gold, gacha pink) and the live-now green. Those encode what a thing
   IS, not what the site looks like.
   ========================================================================== */
body.events-page .premium-glass {
  background: linear-gradient(150deg, var(--tcb-surface), var(--tcb-surface-strong));
  border-color: var(--tcb-line);
}

/* Hero. The cream scrim over the kingdom-plaza art is replaced by a dark one so
   the artwork reads through without washing the page out, and the head cookie
   sits above it. */
body.events-page .events-hero,
body.events-page .events-hero.premium-glass {
  background:
    linear-gradient(
      180deg,
      rgba(9, 6, 16, 0.9) 0%,
      rgba(9, 6, 16, 0.72) 62%,
      rgba(9, 6, 16, 0.94) 100%
    ),
    url("/assets/events-page/events-hero-bg-v1.webp") center / cover no-repeat;
}

body.events-page .events-hero > :not(.hero-bg-art) {
  position: relative;
  z-index: 1;
}


/* ---- Section bands, cards and chips ----
   These painted their cream directly rather than through the design tokens, so
   the token re-point above could not reach them. Each becomes a translucent dark
   band over the page backdrop, keeping the original layering (bands sit under
   cards, cards under chips). */
body.events-page .events-active-section {
  background: linear-gradient(180deg, rgba(23, 20, 37, 0.72), rgba(16, 13, 26, 0.6));
}

/* The staged band was missed by this sweep and kept painting the cream from the
   light palette, so it read as a pale slab across an otherwise dark page — and
   its own near-white heading sat on that cream at almost no contrast. Same
   translucent dark treatment as its siblings, warmed toward the amber this page
   already spends on "upcoming", so it still reads as its own zone between the
   live spotlight and the archive. Covers BOTH tabs: the events band and the
   patch-notes one are the same class. */
body.events-page .events-next-section {
  background: linear-gradient(180deg, rgba(41, 29, 14, 0.72), rgba(16, 13, 26, 0.6));
}

body.events-page .events-updates-section {
  background: linear-gradient(180deg, rgba(26, 18, 40, 0.72), rgba(16, 13, 26, 0.6));
}

body.events-page .update-body,
body.events-page .update-patchnotes {
  background: linear-gradient(180deg, rgba(23, 20, 37, 0.8), rgba(16, 13, 26, 0.68));
}

body.events-page .event-card,
body.events-page .event-panel {
  background: var(--tcb-surface);
}

body.events-page .event-chip {
  background: var(--tcb-surface-strong);
  color: var(--tcb-ink-soft);
}

body.events-page .events-error {
  background: var(--tcb-surface-strong);
}

/* Two literals from the cream palette that the token re-point could not reach,
   because they were written directly rather than through --text-soft / a token.
   Both were around 3.3:1 on the dark surfaces. The "full cycle" green keeps its
   meaning, just lifted to a shade that reads on a dark base. */
body.events-page .events-pill-live,
body.events-page .event-availability.availability-full-cycle {
  color: #34d399;
}

body.events-page .event-availability {
  color: var(--tcb-ink-soft);
}

/* Same cream literal, on the "Concluded" badge. */
body.events-page .event-ended-badge {
  color: var(--tcb-ink-soft);
}

/* =============================================================================
   Head cookie dials — Stormbringer Cookie on /events/.
   These override the shared defaults in shared-shell.css, which are measured
   off the Dark Cacao art on /resonant/. Anything commented out below tracks the
   shared value; edit an active line to move the cookie on THIS page only.
   ========================================================================== */
body.events-page {
  /* Box. Top-anchored (shift-y 0) rather than the shared centred model — these
     heroes are shorter than Resonant's, so centring drops the art onto the
     content below the frame. */
  --tcb-hero-art-width: 24rem;
  --tcb-hero-art-top: -2.5rem;
  --tcb-hero-art-right: 1rem;
  --tcb-hero-art-shift-y: 0%;
  --tcb-hero-art-opacity: 0.94;
  /* --tcb-hero-art-shift-x: 0%; */
  /* --tcb-hero-art-scale: 1; */

  /* Image grade — inherited (saturate 1.18, contrast 1.12, brightness 1.06). */
  /* --tcb-hero-art-saturate: 1.18; */
  /* --tcb-hero-art-contrast: 1.12; */
  /* --tcb-hero-art-brightness: 1.06; */

  /* Mobile (<=699px) */
  --tcb-hero-art-width-mobile: 11rem;
  --tcb-hero-art-top-mobile: -1rem;
  --tcb-hero-art-right-mobile: -2rem;
  /* --tcb-hero-art-opacity-mobile: 0.85; */  /* inherits the shared 0.85 */
  /* --tcb-hero-art-scale-mobile: 1; */
}

/* =============================================================================
   HERO TUNING DIALS — /events/ (hub only)
   =============================================================================
   Same shape as the blocks at the bottom of guild-boss/ and team-builder/:
   one block per breakpoint, each with the same seven controls.

   SCOPED TO THE HUB ON PURPOSE. This stylesheet is also loaded by the 21
   generated pages under /events/{slug}/, and they carry the same
   data-route="events" and .events-hero. They are told apart by the extra
   .events-event-page / .events-update-page body class, which the :not() pair
   below excludes — otherwise tuning this hero would silently retune all of
   them. Those pages also have no .hero-bg-art at all, so the art dials would
   have been dead weight there.

   Direct properties on page-scoped selectors, NOT --tcb-hero-* dials: the
   shared ladder in shared-shell.css switches from var-driven to literal values
   below 639px, so a var-based scaffold works at the top three breakpoints and
   silently does nothing at the bottom two. This also collapses the art split
   (--tcb-hero-art-* above 699px, --tcb-hero-art-*-mobile below), so the same
   three lines work in every block.

   These sit ABOVE the shared ladder, so this hero stops tracking future
   changes to it. Delete or comment out any single line to hand that one dial
   back to the shared value.

   Values are what the page already computed at each width, so the block is a
   no-op as written. Edit freely.
   ========================================================================== */

@media (max-width: 1200px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 22rem;
    top: -2.5rem;
    right: -1.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 2rem;
    max-width: none;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 1.1rem;
    max-width: 46rem;
  }
}

@media (max-width: 1000px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 24rem;
    top: -2.5rem;
    right: -1.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 2rem;
    max-width: none;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 1.1rem;
    max-width: 36rem;
  }
}

@media (max-width: 860px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 22rem;
    top: -1.0rem;
    right: -1.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 1.8rem;
    max-width: none;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 1.1rem;
    max-width: 32rem;
  }
}

@media (max-width: 699px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 16rem;
    top: -1.0rem;
    right: -1.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 1.6rem;
    max-width: none;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 0.9rem;
    max-width: 28rem;
  }
}

@media (max-width: 530px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 16rem;
    top: -3.0rem;
    right: -0.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 1.6rem;
    max-width: 16rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 0.92rem;
    max-width: 30rem;
  }
}

@media (max-width: 390px) {
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-bg-art {
    width: 16rem;
    top: -2.0rem;
    right: -0.5rem;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero h1 {
    font-size: 1.6rem;
    /* 70% of the hero, so the title keeps clear of the art rather than
       wrapping against a fixed rem measure at the narrowest width. */
    max-width: 50%;
  }
  body.events-page:not(.events-event-page):not(.events-update-page) .events-hero .hero-copy {
    font-size: 0.92rem;
    max-width: 24rem;
  }
}

