/* ==========================================================================
   Game Domain
   Path: MITD/v5/sites/gd-home/assets/gd/css/gd.css

   One sheet while there is one page. Page families get their own through
   'extra_css' when they need components nothing else uses, rather than growing
   this file until a change for one page can break the front door.

   GD is the loudest palette in MITD and it is the only one that gets away with
   it: a games portal is allowed to be neon in a way a museum and a storefront
   are not. Magenta owns actions, cyan owns structure and secondary actions, and
   the near-black underneath keeps both readable.
   ========================================================================== */

:root {
  --void:     #06030F;
  --panel:    #0D0820;
  --panel-2:  #150C2E;
  --ice:      #F0EAFF;
  --ash:      #9A8FC0;
  --ash-dim:  #6A5F92;

  --magenta:  #FF2BD1;
  --magenta-2:#FF7AE3;
  --cyan:     #22D3FF;
  --cyan-2:   #8BEBFF;
  --violet:   #8B3BFF;
  --ember:    #FF3B5C;

  --hairline:    rgba(180, 140, 255, .24);
  --hairline-lo: rgba(180, 140, 255, .12);

  --gut:  clamp(16px, 3.2vw, 52px);
  --gap:  clamp(12px, 1.4vw, 20px);

  /* The accessibility button is position:fixed bottom-left, so it sits ON the
     last line rather than after it. Every page ends with this much extra room,
     which also gives a little scroll past the footer. env() adds the iOS home
     indicator strip and resolves to 0 everywhere else. */
  --a11y-clear: calc(clamp(58px, 7vh, 88px) + env(safe-area-inset-bottom, 0px));
  --ease: cubic-bezier(.22, .61, .36, 1);

  /* Text on artwork: a stack of wide, low-opacity shadows rather than one hard
     drop. Darkens what is immediately behind each letter without reading as an
     outline. */
  --lift:
    0 0 6px   rgba(0, 0, 0, .68),
    0 0 16px  rgba(0, 0, 0, .58),
    0 0 34px  rgba(0, 0, 0, .50),
    0 0 62px  rgba(0, 0, 0, .40),
    0 0 100px rgba(0, 0, 0, .30);
}

/* --------------------------------------------------------------------------
   Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body.gd {
  margin: 0;
  background: var(--void);
  color: var(--ice);
  font-family: Archivo, "Helvetica Neue", Arial, sans-serif;
  font-optical-sizing: auto;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, p, figure { margin: 0; }
img { display: block; max-width: 100%; }
svg { display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
iframe { border: 0; }
[hidden] { display: none !important; }

:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; border-radius: 2px; }
[aria-disabled="true"] { pointer-events: none; }

.sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 99;
  padding: 12px 18px; background: var(--magenta); color: #190021; font-weight: 700;
}
.skip:focus { left: 12px; top: 12px; }

.i { width: 20px; height: 20px; fill: none; stroke: currentColor;
     stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
/* The social marks are filled shapes: a one-pixel outline of a logo at 16px
   reads as mush, so these invert the rule rather than each getting a rule. */
.i--solid { fill: currentColor; stroke: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important; transition-duration: .001ms !important;
  }
}

/* ==========================================================================
   Masthead
   ========================================================================== */
.head {
  position: sticky; top: 0; z-index: 40;
  /* flex-wrap here rather than only inside the narrow media query. At normal
     text size nothing wraps, because everything fits -- but the accessibility
     panel's larger-text settings make the nav and the tools wider without
     making the window wider, and without this they simply run off the side.
     A wrapped masthead is taller; an overflowing one is unusable. */
  display: flex; flex-wrap: wrap; align-items: center; gap: clamp(12px, 2vw, 34px);
  padding: 10px var(--gut);
  background: rgba(6, 3, 15, .84);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--hairline-lo);
}

.brand { flex: 0 0 auto; display: block; }
.brand__mark { width: clamp(104px, 10vw, 142px); height: auto; }
/* Only seen when gd-logo.png is not on the server: wrong typeface, right words,
   still a link home, which beats a masthead with a hole in it. */
.brand__fallback {
  display: block; font-size: clamp(16px, 1.8vw, 21px);
  font-weight: 900; font-stretch: 112%; letter-spacing: -.02em; line-height: 1;
  text-transform: uppercase;
}
.brand__fallback .is-accent { color: var(--cyan); }
.brand__fallback span + span { margin-left: .28em; }

.nav { margin-inline: auto; }
.nav__list { display: flex; flex-wrap: wrap; align-items: center; gap: clamp(10px, 1.4vw, 24px); }
.nav__link {
  position: relative; display: block; padding: 10px 0;
  font-size: 10.5px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
  color: rgba(240, 234, 255, .76); transition: color .25s var(--ease);
  white-space: nowrap;
}
.nav__link::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: 4px;
  height: 2px; background: var(--cyan); transition: right .3s var(--ease);
}
.nav__link:hover { color: var(--ice); }
.nav__link:hover::after { right: 0; }
.nav__link.is-current { color: var(--ice); }
.nav__link.is-current::after { right: 0; }

.head__tools { display: flex; align-items: center; gap: clamp(6px, .8vw, 12px); }
.tool { display: grid; place-items: center; width: 34px; height: 34px;
        border-radius: 50%; transition: background-color .25s var(--ease); }
.tool:hover { background: rgba(34, 211, 255, .14); }
/* Nothing emits this today -- site-chrome's 'cta' is empty, so the masthead
   button is not rendered at all. Kept because restoring it is then one array
   entry rather than an array entry plus remembering this line. */
.head__cta { padding: 10px 18px; }

.burger { display: none; place-items: center; width: 34px; height: 34px; }
.burger__bar { position: relative; width: 20px; height: 1.5px; background: currentColor; }
.burger__bar::before, .burger__bar::after {
  content: ""; position: absolute; left: 0; width: 20px; height: 1.5px;
  background: currentColor; transition: transform .3s var(--ease);
}
.burger__bar::before { top: -6px; }
.burger__bar::after  { top: 6px; }
.burger[aria-expanded="true"] .burger__bar { background: transparent; }
.burger[aria-expanded="true"] .burger__bar::before { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__bar::after  { transform: translateY(-6px) rotate(-45deg); }

/* ==========================================================================
   Buttons

   Two, and the split is by JOB rather than by prominence: magenta is "go into
   the thing", cyan outline is everything else. A page with two filled buttons
   side by side has asked the visitor to choose between two equally loud things,
   which is the same as not having asked.
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; gap: 11px;
  padding: 13px 24px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .17em;
  text-transform: uppercase;
  /* NOT white-space: nowrap, which is what this was. A pill button whose label
     cannot wrap is a pill button wider than the screen the moment the label is
     long or the text is scaled up -- and it was the last thing still running
     off the side at twice the normal text size. Wrapping only ever happens when
     the alternative is overflowing, because a label shorter than its container
     has nothing to wrap onto. */
  white-space: normal; max-width: 100%; text-align: center;
  border: 1px solid transparent;
  transition: border-color .3s var(--ease), background-color .3s var(--ease),
              box-shadow .3s var(--ease), color .3s var(--ease);
}
.btn .i { width: 15px; height: 15px; transition: transform .3s var(--ease); }
.btn:hover .i { transform: translateX(4px); }

.btn--primary {
  background-image: linear-gradient(100deg, var(--violet), var(--magenta));
  color: #190021; font-weight: 800;
  box-shadow: 0 0 0 rgba(255, 43, 209, 0);
}
.btn--primary:hover { box-shadow: 0 0 28px rgba(255, 43, 209, .5); }

.btn--ghost {
  border-color: var(--cyan); color: var(--ice);
  background: rgba(6, 10, 26, .5); backdrop-filter: blur(6px);
}
.btn--ghost:hover { background: rgba(34, 211, 255, .15); box-shadow: 0 0 24px rgba(34, 211, 255, .3); }
.btn--ghost .i { color: var(--cyan); }
/* The play glyph is a circle, not an arrow, so it should not slide. */
.btn--ghost:hover .i { transform: none; }

/* Inert should read as not-yet, not as broken. */
.btn[aria-disabled="true"] {
  background-image: none; background: rgba(21, 12, 46, .6);
  border-color: var(--hairline); color: rgba(240, 234, 255, .55); box-shadow: none;
}
.btn[aria-disabled="true"] .i { color: var(--ash-dim); }

.chip {
  display: grid; place-items: center; flex: 0 0 auto;
  width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--cyan);
  transition: background-color .3s var(--ease), box-shadow .3s var(--ease);
}
.chip .i { width: 15px; height: 15px; color: var(--cyan); }
.chip:hover { background: rgba(34, 211, 255, .16); box-shadow: 0 0 20px rgba(34, 211, 255, .32); }
.chip[aria-disabled="true"] { border-color: var(--hairline); }
.chip[aria-disabled="true"] .i { color: var(--ash-dim); }

/* ==========================================================================
   Hero
   ========================================================================== */
.hero {
  position: relative; isolation: isolate;
  display: flex; align-items: center;
  min-height: min(78svh, 720px);
  padding: clamp(32px, 5.5vw, 78px) var(--gut);
  overflow: hidden;
}
.hero__art {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: 62% 48%;
}
.hero.is-empty {
  background:
    radial-gradient(56% 62% at 66% 40%, rgba(139, 59, 255, .38), transparent 70%),
    radial-gradient(62% 68% at 22% 74%, rgba(255, 43, 209, .24), transparent 72%),
    linear-gradient(162deg, #120634, var(--void));
}
/* Copy sits left, so the veil is directional: heavy at the left edge, gone by
   mid-frame, with a floor so the CTAs stay legible over bright ground. */
.hero__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to right, rgba(6, 3, 15, .94) 0%, rgba(6, 3, 15, .72) 26%,
                              rgba(6, 3, 15, .2) 52%, rgba(6, 3, 15, 0) 70%),
    linear-gradient(to top,   rgba(6, 3, 15, .8) 0%, rgba(6, 3, 15, 0) 34%);
}

.hero__copy { position: relative; z-index: 2; max-width: 560px; }

/* ==========================================================================
   The hero shell

   One box, and it owns the alignment of everything inside it. align-items on a
   flex column is the single property that centres ALL of them -- a block <img>
   that text-align cannot move, two nested flex rows that ignore text-align, and
   the paragraphs that do not. The previous version needed four rules for that,
   and four rules is four things to forget.

   justify-content on the two rows is not duplication: a flex item under
   align-items:center is shrink-to-fit, so it is already centred as a box --
   until its contents WRAP, at which point the box becomes full width and packs
   its children left again. These two lines are for the wrapped case only.
   ========================================================================== */
.hero__shell {
  display: flex; flex-direction: column;
  align-items: center;
  text-align: center;
}
.hero__shell .verbs,
.hero__shell .hero__ctas { justify-content: center; }
.hero__shell .hero__lede { max-width: 100%; }

.hero__logo {
  width: clamp(230px, 30vw, 420px); height: auto;
  filter: drop-shadow(0 0 12px rgba(0, 0, 0, .6))
          drop-shadow(0 0 40px rgba(0, 0, 0, .45))
          drop-shadow(0 0 90px rgba(139, 59, 255, .3));
}
/* Only used when the mark is missing -- otherwise the <h1> is screen-reader
   only and this never paints. */
.hero__title:not(.sr) {
  font-size: clamp(40px, 6vw, 84px); font-weight: 900; font-stretch: 108%;
  line-height: .94; letter-spacing: -.035em; text-transform: uppercase;
  text-shadow: var(--lift);
}

/* The four verbs. Separators are drawn, not typed: a bullet character between
   list items is punctuation a screen reader reads out, and this is a list. */
.verbs {
  display: flex; flex-wrap: wrap; align-items: center;
  margin-top: clamp(14px, 1.8vw, 22px);
}
.verbs li {
  display: flex; align-items: center;
  font-size: clamp(13px, 1.5vw, 20px); font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--cyan);
  text-shadow: var(--lift);
}
.verbs li + li::before {
  content: ""; flex: 0 0 auto;
  width: 5px; height: 5px; margin: 0 clamp(8px, 1vw, 14px);
  border-radius: 50%; background: var(--magenta);
}

.hero__lede {
  margin-top: clamp(8px, 1vw, 14px);
  font-size: clamp(12px, 1.15vw, 16px); font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase;
  color: rgba(240, 234, 255, .9);
  text-shadow: var(--lift);
}
.hero__ctas { display: flex; flex-wrap: wrap; gap: 12px; margin-top: clamp(20px, 2.6vw, 32px); }

/* ==========================================================================
   Band frame

   Top padding only: each band opens its own gap, so bands can be added,
   removed or reordered in a content file without anything below them needing
   to know.
   ========================================================================== */
.band { padding: clamp(26px, 3.6vw, 56px) var(--gut) 0; }

/* ==========================================================================
   Rail (section heading line)
   ========================================================================== */
.rail { display: flex; align-items: center; gap: clamp(12px, 2vw, 26px); }
.rail__label {
  font-size: clamp(13px, 1.3vw, 17px); font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase;
}
.rail__line { flex: 1 1 auto; height: 1px; background: var(--hairline-lo); }
.rail__more {
  display: inline-flex; align-items: center; gap: 9px; flex: 0 0 auto;
  font-size: 9.5px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
  color: var(--cyan); transition: color .25s var(--ease);
}
.rail__more .i { width: 13px; height: 13px; transition: transform .3s var(--ease); }
.rail__more:hover .i { transform: translateX(4px); }
.rail__more[aria-disabled="true"] { color: var(--ash-dim); }

/* ==========================================================================
   Scroller

   Native overflow with snap points, not a JS carousel: trackpad, touch and
   wheel all work before any script loads, the track is a real list so Tab
   reaches every card, and if gd.js fails the only loss is two chevrons.
   ========================================================================== */
.scroller__track {
  display: flex; gap: var(--gap);

  /* position:relative is load-bearing. A static-position scroll container clips
     its own painting but its layout overflow still propagates to its ancestors,
     which makes the whole DOCUMENT wider than a phone -- the page looks right
     until you scroll sideways or pinch out. */
  position: relative;

  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* Room for the focus ring, which the scroll container would otherwise clip on
     the first and last card. */
  padding: 4px 2px 10px;
  scrollbar-width: none;
}
.scroller__track::-webkit-scrollbar { display: none; }
.scroller__track > * { scroll-snap-align: start; }

.srail { position: relative; }
.srail__nav { position: absolute; inset: 0; pointer-events: none; }
.srail__nav .scroller__btn {
  position: absolute; top: 50%; transform: translateY(-50%); pointer-events: auto;
}
.scroller__btn {
  display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 50%; color: var(--cyan);
  background: rgba(6, 3, 15, .72); backdrop-filter: blur(8px);
  border: 1px solid var(--hairline-lo);
  transition: background-color .25s var(--ease), opacity .25s var(--ease);
}
.scroller__btn .i { width: 19px; height: 19px; stroke-width: 1.6; }
.scroller__btn:hover { background: rgba(34, 211, 255, .2); }
/* Set by gd.js at an exhausted end. Dimmed rather than removed, so the control
   does not move while someone is clicking it. */
.scroller__btn.is-spent { opacity: .2; pointer-events: none; }
.scroller__btn--prev { left: clamp(-30px, -1.8vw, -8px); }
.scroller__btn--next { right: clamp(-30px, -1.8vw, -8px); }

/* ==========================================================================
   Live band

   Present in the DOM only when Twitch credentials exist, and visible only while
   the channel is actually broadcasting. See inc/gd/bands/live.php.
   ========================================================================== */

/* The band's own backdrop, full-bleed behind the panel. It needs no show/hide
   logic: the art is INSIDE the section, so it arrives and leaves with the
   stream by construction rather than by a second switch that could fall out of
   step with the first.

   This is also what the narrower panel is for. A 78% panel floating on a
   full-width backdrop reads as a stage; the same panel on flat page background
   just reads as a band that forgot to fill its row. */
.band--live {
  position: relative; isolation: isolate;
  /* Bands carry top padding only, so each opens its own gap. This one needs a
     bottom as well, or the artwork would stop dead at the panel's lower edge. */
  padding-bottom: clamp(26px, 3.6vw, 56px);
}
.live__art {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover; object-position: 50% 45%;
}
.band--live.is-empty {
  background:
    radial-gradient(60% 80% at 50% 36%, rgba(139, 59, 255, .34), transparent 72%),
    radial-gradient(70% 70% at 16% 80%, rgba(255, 43, 209, .2), transparent 72%),
    linear-gradient(170deg, #0F0629, var(--void));
}
/* Dark at the top and bottom edges so the band joins the sections above and
   below it instead of starting and stopping on a hard seam, and dimmed through
   the middle so the panel is plainly the thing in focus. */
.live__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to bottom,
      rgba(6, 3, 15, .96) 0%,  rgba(6, 3, 15, .62) 18%,
      rgba(6, 3, 15, .55) 50%, rgba(6, 3, 15, .72) 82%,
      rgba(6, 3, 15, .96) 100%);
}
/* NARROWER THAN EVERYTHING ELSE ON THE PAGE, deliberately. The concept art is
   explicit about it: the stream panel spans about 76% of that canvas where the
   pathway cards below span 95%. Run full width and a 1900px monitor gets a
   1700px player -- bigger than the thing it is advertising and louder than the
   hero above it.

   A PERCENTAGE, not a fixed pixel cap. A cap that looked right at 1440 would
   stop binding at all on a 1340px-wide content column -- so the layout would be
   correct on a big screen and unchanged on a laptop, which is the wrong way
   round. 78% holds the art's proportion at every width.

   The ceiling is about HEIGHT, not width. 78% of a 1870px screen is a 1377px
   panel, and a 16:9 player that wide is 751px tall -- with the bar above it,
   essentially one full screen of video. 1180px caps the player at about 640px,
   which still reads as the main event without being the entire first scroll.
   On anything 1440 and under the percentage binds first and this never applies.

   One number to tune. Raise it for a bigger player, lower it for a smaller one;
   nothing else in this block has to change. */
.stream {
  position: relative; z-index: 2;
  max-width: min(78%, 1180px);
  margin-inline: auto;
  /* The width changes when the chat opens. Animating it stops the band from
     snapping to a new size under the cursor; the global reduced-motion rule
     switches this off for anyone who has asked for that. */
  transition: max-width .35s var(--ease);
  border: 1px solid rgba(255, 43, 209, .4); border-radius: 16px;
  background: linear-gradient(165deg, rgba(32, 12, 58, .9), rgba(8, 4, 20, .96));
  box-shadow: 0 0 60px rgba(255, 43, 209, .16);
  overflow: hidden;
}

.stream__bar {
  display: flex; align-items: center; gap: clamp(12px, 1.6vw, 22px);
  padding: clamp(12px, 1.4vw, 18px) clamp(14px, 1.8vw, 24px);
  border-bottom: 1px solid var(--hairline-lo);
}

.onair {
  display: inline-flex; align-items: center; gap: 9px; flex: 0 0 auto;
  padding: 7px 14px; border-radius: 6px;
  background: var(--ember);
  font-size: 10px; font-weight: 800; letter-spacing: .18em; text-transform: uppercase;
  color: #1A0208;
}
.onair__dot { width: 8px; height: 8px; border-radius: 50%; background: #1A0208; }
/* The pulse is the one piece of motion on the page, and it is doing a job:
   "live" is a state, and a static badge saying LIVE NOW looks identical whether
   it is true or left over. prefers-reduced-motion switches it off globally. */
@media (prefers-reduced-motion: no-preference) {
  .onair__dot { animation: gd-pulse 1.8s var(--ease) infinite; }
}
@keyframes gd-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

.stream__id { min-width: 0; margin-right: auto; }
.stream__title {
  font-size: clamp(13px, 1.3vw, 17px); font-weight: 800;
  letter-spacing: .06em; text-transform: uppercase;
}
.stream__now {
  margin-top: 3px;
  font-size: 11px; color: var(--ash);
  /* The stream title comes from Twitch and can be any length at all. One line,
     clipped, rather than a bar that changes height when the broadcaster edits
     their title mid-stream. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.stream__count {
  display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto;
  font-size: 11px; font-weight: 600; color: var(--ash);
}
.stream__count .i { width: 15px; height: 15px; }
.stream__chat { flex: 0 0 auto; padding: 10px 18px; }

/* A grid with one column until the chat is opened, then two. The player keeps
   its aspect-ratio box and so defines the row height; the chat stretches to
   match it rather than being given a height of its own to drift out of sync. */
.stream__stage {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: clamp(10px, 1.2vw, 16px);
  padding: clamp(12px, 1.6vw, 20px);
}
.stream.is-chatting .stream__stage {
  grid-template-columns: minmax(0, 1fr) clamp(270px, 28%, 345px);
}

/* The panel GROWS when the chat opens, rather than the video shrinking to make
   room. Splitting the closed width three ways took the player from 1009px to
   731px -- a third of it gone to open a sidebar, which is the wrong trade right
   after deciding the player was too big. There is simply more in the panel now,
   so it gets more room, and the player ends up near the size it was. */
.stream.is-chatting { max-width: min(90%, 1480px); }
/* 16:9, reserved before the iframe exists. Without a reserved box the page
   jumps by the height of a video the moment gd.js inserts the player. */
.stream__player {
  position: relative; aspect-ratio: 16 / 9;
  border-radius: 10px; overflow: hidden;
  background:
    radial-gradient(60% 60% at 50% 45%, rgba(139, 59, 255, .3), transparent 70%),
    linear-gradient(150deg, #150C2E, #06030F);
}
.stream__player iframe { position: absolute; inset: 0; width: 100%; height: 100%; }

/* NO backdrop-filter on anything sitting next to the video. .btn--ghost carries
   one for use over artwork, and inside this panel there is no artwork behind it
   to blur -- just the panel's own gradient -- so it was buying nothing and
   putting another compositing layer beside a playing iframe. */
.stream .btn--ghost { backdrop-filter: none; }

/* ----- chat --------------------------------------------------------------
   Opened on this page instead of sending the visitor to twitch.tv, which is a
   one-way door. Empty until first opened: see gd.js. */
.stream__chat-panel {
  display: flex; flex-direction: column; min-height: 0;
  border: 1px solid var(--hairline-lo); border-radius: 10px; overflow: hidden;
  background: var(--panel);
}
/* The frame fills the panel now that the footer is gone. The panel's own
   overflow:hidden and radius clip it, so the chat ends on a rounded corner
   rather than against a strip of nothing. */
.stream__chat-frame { flex: 1 1 auto; min-height: 0; }
.stream__chat-frame iframe { display: block; width: 100%; height: 100%; }
/* Pressed state for the toggle, so "Hide chat" does not look like a button
   that has simply changed its mind about what it says. */
.stream__chat[aria-expanded="true"] {
  background: rgba(34, 211, 255, .16); border-color: var(--cyan-2);
}


/* ==========================================================================
   Pathway cards
   ========================================================================== */
.paths { align-items: stretch; }
.path {
  position: relative;
  display: flex; flex-direction: column;
  flex: 0 0 clamp(228px, 24vw, 300px);
  border: 1px solid var(--hairline-lo); border-radius: 12px;
  background: linear-gradient(168deg, rgba(34, 18, 64, .55), rgba(8, 4, 20, .85));
  overflow: hidden;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.path:hover { border-color: var(--cyan); box-shadow: 0 0 26px rgba(34, 211, 255, .2); }

.path__media { display: block; }
.path__media img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }
.path.is-empty .path__media {
  aspect-ratio: 16 / 9;
  background:
    radial-gradient(70% 72% at 38% 32%, rgba(139, 59, 255, .36), transparent 70%),
    linear-gradient(145deg, #1A0E3C, #07040F);
}

.path__body {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 14px 15px 16px;
}
/* NOT margin-top: auto, and not align-items: flex-end.
   Both of those pushed this block to the BOTTOM of the card, which is fine
   while every card's description is the same number of lines and wrong the
   moment one is not: PLAY and EXPLORE wrap to two lines, CREATE and COMPETE
   fit on one, so their shorter blocks were pushed further down and their
   titles sat below the other two.
   Anchored under the picture instead, so the four titles are on one line
   whatever the descriptions do. The spare space now falls at the bottom of
   the short cards, where nobody reads it. */
.path__title { min-height: 1lh; }
.path__text { display: block; min-width: 0; }
.path__title {
  display: block;
  font-size: 13px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
}
.path__line {
  display: block; margin-top: 7px;
  font-size: 11.5px; line-height: 1.55; color: var(--ash);
}
/* The whole card is the target: the chip keeps the short accessible name and
   its ::after covers the card, because a 34px circle is a small ask on a touch
   screen. */
.path__go::after, .gcard__go::after { content: ""; position: absolute; inset: 0; }

/* ==========================================================================
   Game cards
   ========================================================================== */
.gcards { align-items: stretch; }
.gcard {
  position: relative;
  display: flex; flex-direction: column;
  flex: 0 0 clamp(196px, 20vw, 262px);
  border: 1px solid var(--hairline-lo); border-radius: 12px;
  background: linear-gradient(168deg, rgba(34, 18, 64, .5), rgba(8, 4, 20, .85));
  overflow: hidden;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.gcard:hover { border-color: var(--magenta); box-shadow: 0 0 26px rgba(255, 43, 209, .22); }

.gcard__media { display: block; }
.gcard__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.gcard.is-empty .gcard__media {
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(72% 70% at 42% 30%, rgba(255, 43, 209, .3), transparent 70%),
    linear-gradient(145deg, #2A0E3C, #07040F);
}

.gcard__body {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 10px;
  padding: 12px 14px 14px; margin-top: auto;
}
.gcard__text { display: block; min-width: 0; }
.gcard__title {
  display: block;
  font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
}
.gcard__line { display: block; margin-top: 5px; font-size: 10.5px; color: var(--ash); }

/* ==========================================================================
   Agenda — events and news, read together
   ========================================================================== */
.band--agenda {
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: clamp(20px, 2.6vw, 44px);
}
/* ONE COLUMN WHEN THERE IS ONLY ONE COLUMN. The streams page drops the news
   half, and without this the schedule would keep sitting in 1.25fr of a
   two-column grid with the other 1fr empty -- a page about broadcasts showing
   them in half its width beside nothing. :has() rather than a modifier class
   so the band keeps deciding this from its own content. */
.band--agenda:not(:has(.agenda__col--news)) { grid-template-columns: minmax(0, 1fr); }

.events { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
          gap: var(--gap); margin-top: clamp(14px, 1.8vw, 22px); }
.event {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--hairline-lo); border-radius: 12px;
  background: linear-gradient(168deg, rgba(34, 18, 64, .5), rgba(8, 4, 20, .85));
  overflow: hidden;
  transition: border-color .3s var(--ease);
}
.event:hover { border-color: var(--cyan); }
.event__media { position: relative; display: block; }
.event__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.event.is-empty .event__media {
  aspect-ratio: 16 / 10;
  background:
    radial-gradient(70% 70% at 44% 34%, rgba(34, 211, 255, .28), transparent 70%),
    linear-gradient(145deg, #11143C, #07040F);
}
/* The date is the thing people scan for, so it sits on the image rather than in
   the body text where it would be the third line down. */
.event__date {
  position: absolute; top: 10px; right: 10px;
  display: grid; justify-items: center; gap: 1px;
  padding: 6px 10px; border-radius: 8px;
  background: rgba(6, 3, 15, .82); backdrop-filter: blur(6px);
  border: 1px solid var(--hairline);
}
.event__mon {
  font-size: 8px; font-weight: 700; letter-spacing: .18em; color: var(--cyan);
}
.event__day { font-size: 15px; font-weight: 800; line-height: 1; }

.event__body { display: block; padding: 12px 14px 14px; }
.event__title {
  display: block;
  font-size: 11.5px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase;
}
.event__text { display: block; margin-top: 5px; font-size: 11px; color: var(--ash); }
.event__go::after, .nitem__go::after { content: ""; position: absolute; inset: 0; }

.news { display: grid; gap: 10px; margin-top: clamp(14px, 1.8vw, 22px); }
.nitem {
  position: relative;
  display: grid; grid-template-columns: 78px minmax(0, 1fr) auto;
  align-items: center; gap: 14px;
  padding: 8px; border: 1px solid var(--hairline-lo); border-radius: 10px;
  background: linear-gradient(140deg, rgba(34, 18, 64, .4), rgba(8, 4, 20, .8));
  transition: border-color .3s var(--ease);
}
.nitem:hover { border-color: var(--cyan); }
.nitem__media { display: block; border-radius: 7px; overflow: hidden; }
.nitem__media img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.nitem.is-empty .nitem__media {
  aspect-ratio: 3 / 2;
  background:
    radial-gradient(70% 70% at 40% 34%, rgba(139, 59, 255, .34), transparent 70%),
    linear-gradient(145deg, #1A0E3C, #07040F);
}
.nitem__body { display: block; min-width: 0; }
.nitem__title { display: block; font-size: 12px; font-weight: 700; }
.nitem__text { display: block; margin-top: 3px; font-size: 11px; color: var(--ash); }
.nitem__date {
  flex: 0 0 auto; font-size: 10px; color: var(--ash-dim); white-space: nowrap;
}

/* ==========================================================================
   Closing banner

   Full-bleed rather than inset like the other bands: it is the bottom of the
   document, and an inset panel there leaves a strip of background under it that
   reads as a mistake.
   ========================================================================== */
.band--banner {
  position: relative; isolation: isolate;
  display: flex; align-items: center; justify-content: flex-end;
  min-height: clamp(260px, 26vw, 400px);
  margin-top: clamp(30px, 4vw, 60px);
  padding: clamp(28px, 4vw, 56px) var(--gut);
  overflow: hidden;
}
.banner__art { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%;
                object-fit: cover; object-position: 44% 54%; }
.band--banner.is-empty {
  background:
    radial-gradient(60% 80% at 66% 46%, rgba(139, 59, 255, .36), transparent 72%),
    radial-gradient(60% 70% at 24% 70%, rgba(255, 43, 209, .22), transparent 72%),
    linear-gradient(160deg, #130735, #06030F);
}
/* Copy sits right here, mirroring the hero, so the veil runs the other way. */
.banner__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to left, rgba(6, 3, 15, .9) 0%, rgba(6, 3, 15, .6) 30%,
                             rgba(6, 3, 15, .12) 62%, rgba(6, 3, 15, 0) 100%),
    linear-gradient(to top,  rgba(6, 3, 15, .6) 0%, rgba(6, 3, 15, 0) 44%);
}
.banner__copy {
  position: relative; z-index: 2; text-align: right;
  /* NO ch CAP. It was max-width: 30ch, and that was wrong from the day this
     band was written, in a way that hid itself.

     ch resolves against the element's OWN font-size, and this element has
     none -- it inherits the body's 16px. The headline inside it is up to 48px.
     So "30 characters" was computed in 16px characters and produced a 275px
     box around a line that needs about 430px. The headline has been wrapping
     inside a box a third too small all along; it just happened to spill
     leftward out of a right-aligned box, where it looked deliberate.

     The heading's line breaks are authored -- each line is its own <span> in
     the content file -- so there is nothing for a cap to decide here. The
     author chooses where the lines fall; this only has to not be narrower
     than them. */
  max-width: 100%;
}
.banner__title {
  font-size: clamp(26px, 3.4vw, 48px); font-weight: 900; font-stretch: 106%;
  line-height: .98; letter-spacing: -.03em; text-transform: uppercase;
  text-shadow: var(--lift);
}
.banner__title span { display: block; }
/* Last resort only, for a genuinely unbreakable line on a genuinely tiny
   screen. With the box no longer three times too small, the headline reaches
   for this far less often -- and when it does, a broken word beats a clipped
   one. hyphens:auto first, so the break lands where English would put it
   rather than mid-syllable. */
.banner__title { hyphens: auto; overflow-wrap: break-word; }
.banner__copy .btn { margin-top: clamp(18px, 2.2vw, 28px); }

/* ==========================================================================
   Footer
   ========================================================================== */
.foot {
  padding: clamp(20px, 2.4vw, 32px) var(--gut) var(--a11y-clear);
  border-top: 1px solid var(--hairline-lo);
}
.foot__row {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: clamp(14px, 2.6vw, 36px);
}
.brand--foot .brand__mark { width: clamp(100px, 9vw, 130px); }

.foot__links { display: flex; flex-wrap: wrap; gap: clamp(12px, 1.8vw, 26px); }
.foot__links a {
  font-size: 10px; font-weight: 600; letter-spacing: .15em; text-transform: uppercase;
  color: rgba(240, 234, 255, .74); transition: color .25s var(--ease);
}
.foot__links a:hover { color: var(--cyan); }
.foot__links a[aria-disabled="true"] { color: var(--ash-dim); }

.foot__social { display: flex; align-items: center; gap: 8px; }
.social {
  display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;
  color: rgba(240, 234, 255, .78);
  transition: background-color .25s var(--ease), color .25s var(--ease);
}
.social .i { width: 17px; height: 17px; }
.social:hover { background: rgba(34, 211, 255, .16); color: var(--cyan); }
.social[aria-disabled="true"] { color: var(--ash-dim); }

.foot__legal {
  margin-top: clamp(14px, 1.8vw, 24px); padding-top: 14px;
  border-top: 1px solid var(--hairline-lo);
  font-size: 9.5px; letter-spacing: .15em; text-transform: uppercase; color: var(--ash-dim);
}

/* ==========================================================================
   Responsive

   Eight nav items is a lot to keep on one line, so this masthead collapses
   earlier than the other MITD sites -- 1080 rather than 860.
   ========================================================================== */
@media (max-width: 1180px) {
  .band--agenda { grid-template-columns: minmax(0, 1fr); }
  .events { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 1080px) {
  .head { flex-wrap: wrap; gap: 10px; }
  .burger { display: grid; }

  .nav {
    order: 3; flex-basis: 100%; margin-inline: 0;
    display: none; padding-top: 6px; border-top: 1px solid var(--hairline-lo);
  }
  .nav.is-open { display: block; }
  .nav__list { flex-direction: column; align-items: stretch; gap: 0; }
  .nav__list li { border-bottom: 1px solid var(--hairline-lo); }
  .nav__list li:last-child { border-bottom: 0; }
  .nav__link { padding: 13px 0; }
  .nav__link::after { display: none; }
  .nav__link.is-current { color: var(--cyan); }

  .head__tools { margin-left: auto; }

  /* The chevrons flanked the rails while there was gutter to flank them into.
     At this width they would hang off the edge, so they come inside the track. */
  .scroller__btn--prev { left: 2px; }
  .scroller__btn--next { right: 2px; }
}

@media (max-width: 860px) {
  .hero { min-height: 0; align-items: flex-start; }
  .hero__art { object-position: 68% 42%; }

  /* The SHELL is already centring its contents at every width -- this centres
     the shell itself in the band, which is a different question.

     Split out on purpose, and this is the line to move if the answer changes.
     The concept art puts this block on the LEFT of a wide hero, against artwork
     whose subject sits right of centre, and that only works while there is a
     right side for the art to own. Stacked, there is no left and right, just a
     column. Take this rule out of the media query and the hero is centred at
     every width instead. */
  .hero__copy { margin-inline: auto; }
  .hero__veil {
    background:
      linear-gradient(to right, rgba(6, 3, 15, .92) 0%, rgba(6, 3, 15, .66) 58%,
                                rgba(6, 3, 15, .42) 100%),
      linear-gradient(to top,   rgba(6, 3, 15, .86) 0%, rgba(6, 3, 15, 0) 46%);
  }

  /* Full width from here down. Below about 900px there is no spare room to
     inset into: 78% of a phone is a player nobody can see, and the inset was
     always about stopping the band dominating a big screen. */
  /* Both selectors, because .stream.is-chatting is a class more specific than
     .stream and would otherwise keep its 90% cap down here -- which made the
     phone player SHRINK when the chat opened, the opposite of what that rule
     exists to do. */
  .stream,
  .stream.is-chatting { max-width: 100%; }

  /* The chat stops sitting beside the player and goes under it. A 250px column
     next to a 400px video is two things neither of which can be read; stacked,
     both get the full width. The explicit height is needed because the chat no
     longer shares a grid row with the player, so it has nothing to stretch to. */
  .stream.is-chatting .stream__stage { grid-template-columns: minmax(0, 1fr); }
  .stream__chat-panel { height: clamp(280px, 48svh, 420px); }

  /* The bar stops being one line. The chat button and the viewer count cannot
     share a row with a stream title at this width, and a title squeezed to
     40px is not a title. */
  .stream__bar { flex-wrap: wrap; row-gap: 10px; }
  .stream__id { order: -1; flex: 1 1 100%; margin-right: 0; }
  .stream__chat { margin-left: auto; }

  .events { grid-template-columns: minmax(0, 1fr); }

  .band--banner { justify-content: flex-start; }
  .banner__copy { text-align: left; max-width: 100%; }
  .banner__veil {
    background:
      linear-gradient(to right, rgba(6, 3, 15, .92) 0%, rgba(6, 3, 15, .7) 58%,
                                rgba(6, 3, 15, .45) 100%),
      linear-gradient(to top,   rgba(6, 3, 15, .8) 0%, rgba(6, 3, 15, 0) 50%);
  }

  /* The footer row used to pack left here and push the social icons to the
     right edge with margin-left:auto. With the social row gone there are two
     children, so the space-between it already has is the whole job -- and
     flex-start plus a stale auto-margin would have pinned the nav links to the
     brand instead of spreading them. */
}

@media (max-width: 620px) {
  .path  { flex: 0 0 min(76vw, 290px); }
  .gcard { flex: 0 0 min(62vw, 240px); }
  .nitem { grid-template-columns: 64px minmax(0, 1fr); }
  /* The date drops under the headline rather than being squeezed into a third
     column that has nowhere left to go. */
  .nitem__date { grid-column: 2; margin-top: 2px; }
  .verbs li { font-size: 12px; }
  .stream__chat { width: 100%; justify-content: center; }
}

/* ==========================================================================
   Account menu

   APPENDED AFTER THE RESPONSIVE BLOCKS ABOVE, which is safe here and would not
   always be: none of these selectors appears anywhere earlier in the file, so
   there is nothing above for these rules to override and nothing above that
   can override them. Their own narrow-screen rules are at the bottom of this
   block rather than folded into the media queries higher up, for the same
   reason -- one component, one place.
   ========================================================================== */
.acct { position: relative; display: flex; }

/* The button IS a .tool, so it inherits the circle, the size and the hover
   wash. This only adds what a <button> does not get for free. */
.acct__btn { border: 0; background: none; color: inherit; cursor: pointer; padding: 0; }
.acct.is-open .acct__btn { background: rgba(34, 211, 255, .18); }

.acct__menu {
  position: absolute; top: calc(100% + 10px); right: 0; z-index: 60;
  min-width: 212px; padding: 8px;
  background: rgba(13, 8, 32, .97);
  border: 1px solid var(--hairline); border-radius: 14px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .55);
  backdrop-filter: blur(10px);
}
/* hidden is an attribute, and a display rule anywhere can beat it by
   specificity. Stated explicitly so the menu cannot be left visible by a
   future rule that happens to set display on a descendant of .head__tools. */
.acct__menu[hidden] { display: none; }

.acct__who {
  margin: 2px 8px 8px; padding-bottom: 8px;
  border-bottom: 1px solid var(--hairline-lo);
  font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ash-dim);
}
.acct__list { margin: 0; padding: 0; list-style: none; }
.acct__link {
  display: block; padding: 9px 10px; border-radius: 9px;
  font-size: 12.5px; font-weight: 600; color: var(--ice);
  transition: background-color .2s var(--ease), color .2s var(--ease);
}
.acct__link:hover { background: rgba(34, 211, 255, .14); color: var(--cyan-2); }
/* An inert entry has no href, so it is not a link and must not look like one. */
.acct__link[aria-disabled="true"] { color: var(--ash-dim); cursor: default; }
.acct__link[aria-disabled="true"]:hover { background: none; color: var(--ash-dim); }

/* ==========================================================================
   How-to steps  —  the streams page

   The numbers come from a CSS counter rather than from the markup, because they
   are a consequence of the order rather than part of the text: inserting a step
   must not mean renumbering the ones after it, and "1." typed into a heading
   would be read out as part of the sentence.
   ========================================================================== */
.band--howto { padding: clamp(28px, 4vw, 54px) var(--gut) clamp(36px, 5vw, 70px); }

.howto {
  display: grid; gap: clamp(20px, 2.6vw, 40px);
  grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
  /* align-items governs an item inside its row; align-content governs the ROWS
     themselves, and it is the one that matters here. The steps column is taller
     than the lede and the notes put together, and the default (stretch) spreads
     that surplus into both row tracks -- so the notes floated halfway down the
     left column with a hole above them. start keeps the rows at their content
     height and lets the steps run past the bottom, which is what the shape of
     the content actually is. */
  align-items: start; align-content: start;
}
.howto__lede { grid-column: 1; }
.howto__heading {
  margin: 0 0 12px;
  font-size: clamp(24px, 3.1vw, 40px); font-weight: 900; font-stretch: 112%;
  letter-spacing: -.02em; line-height: 1.04;
}
.howto__strap { margin: 0; max-width: 46ch; color: var(--ash); font-size: 14.5px; }
.howto__go { margin: clamp(18px, 2vw, 28px) 0 0; }

.steps {
  /* SPANS BOTH ROWS, so the left column's two blocks stack against each other
     instead of against the steps. Pinned to row 1 only, the first row grew to
     the full height of the steps and the asides were pushed to the bottom of
     it -- which looked deliberate with three notes and looked like a mistake
     with one. */
  /* span 2, NOT 1 / -1. With no grid-template-rows there is only one explicit
     row line, so -1 resolves back to line 1 and the whole declaration quietly
     becomes a single-row placement -- no error, no warning, no change on
     screen. An explicit span does not depend on an explicit grid existing. */
  grid-column: 2; grid-row: 1 / span 2;
  margin: 0; padding: 0; list-style: none; counter-reset: step;
  display: grid; gap: 14px; align-content: start;
}
.step {
  counter-increment: step;
  position: relative; padding: 16px 18px 16px 58px;
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--hairline-lo); border-radius: 14px;
}
.step::before {
  content: counter(step);
  position: absolute; left: 16px; top: 16px;
  display: grid; place-items: center; width: 28px; height: 28px;
  border-radius: 50%;
  background: linear-gradient(100deg, var(--violet), var(--magenta));
  color: #190021; font-size: 13px; font-weight: 800;
}
.step__title {
  margin: 2px 0 5px;
  font-size: 11px; font-weight: 700; letter-spacing: .15em; text-transform: uppercase;
  color: var(--cyan-2);
}
.step__text { margin: 0; color: var(--ash); font-size: 14px; }

.asides {
  grid-column: 1; grid-row: 2;
  margin: 0; padding: 0; list-style: none;
  display: grid; gap: 12px; align-content: start;
}
.aside {
  padding: 14px 16px; border-left: 2px solid var(--cyan);
  background: rgba(34, 211, 255, .05); border-radius: 0 10px 10px 0;
}
.aside__title { margin: 0 0 4px; font-size: 13.5px; font-weight: 800; }
.aside__text  { margin: 0; color: var(--ash); font-size: 13.5px; }

@media (max-width: 860px) {
  /* One column, and the order becomes the reading order: what this is, how to
     do it, then the asides -- which is why .asides is last rather than tucked
     under the lede as it is on a wide screen. */
  .howto { grid-template-columns: minmax(0, 1fr); }
  .howto__lede, .steps, .asides { grid-column: 1; }
  /* Both row placements released, or the explicit rows above would hold the
     asides at row 2 and the steps across both -- putting the notes level with
     the steps in a single-column layout. Source order is the reading order
     here. */
  .steps, .asides { grid-row: auto; }
  .howto__strap { max-width: none; }
}

/* ==========================================================================
   News stories

   The card is a button when the item has a story behind it. A <button> brings
   a user-agent stylesheet with it -- border, background, font, padding -- none
   of which belongs on an invisible overlay, so all of it is cleared. The
   ::after that stretches it across the whole card is already shared with
   .event__go higher up; this only has to undo the chrome.
   ========================================================================== */
button.nitem__go {
  /* POSITIONED ITSELF, rather than leaning on the shared ::after that stretches
     .event__go and the anchor version across their card.

     That trick works for an <a> and does not work here: a <button> renders its
     children inside an anonymous box, and its ::after does not end up as a hit
     target over the card the way an anchor's does. The symptom was a card that
     opened on Enter -- the button had focus, so the keyboard was fine -- and did
     nothing at all when clicked. Giving the button a real box makes the whole
     card one target for both, and makes it a 78px-tall touch target instead of
     the 1px box its screen-reader label occupies. */
  position: absolute; inset: 0;
  padding: 0; border: 0; background: none; color: inherit; font: inherit;
  cursor: pointer; appearance: none;
}

/* ==========================================================================
   The story panel
   ========================================================================== */
.story {
  width: min(760px, calc(100vw - 2 * var(--gut)));
  max-height: min(86vh, 900px);
  padding: 0; border: 1px solid var(--hairline); border-radius: 18px;
  background: var(--panel);
  color: var(--ice);
  overflow: hidden;
  box-shadow: 0 30px 90px rgba(0, 0, 0, .66);
}
/* A dialog is display:none until opened, so nothing here needs a hidden state
   of its own -- and the flex layout must only apply while it is open, or the
   closed dialog would start taking part in layout again. */
.story[open] { display: flex; }
.story::backdrop { background: rgba(3, 1, 10, .72); backdrop-filter: blur(3px); }

.story__inner {
  position: relative;
  display: flex; flex-direction: column;
  width: 100%; max-height: inherit; overflow-y: auto;
  overscroll-behavior: contain;   /* scrolling the story must not scroll the page behind it */
}

.story__art {
  /* THE WHOLE PICTURE, not a band cut out of the middle of it.

     This was a fixed height with object-fit:cover, which crops whatever does
     not fit -- on a 1672x776 render in a 760px panel that is 353px of image
     squeezed into 280px, so roughly 36px disappeared off the top and the same
     off the bottom. Fine for a decorative strip, wrong for artwork someone made
     on purpose and wants seen.

     height:auto lets the image keep its own proportions at whatever width the
     panel is. max-height is the one guard left, for a portrait render that would
     otherwise fill the screen before the headline appeared -- and object-fit
     switches to contain rather than cover so that even then nothing is cut,
     it just sits on the panel colour. */
  display: block; width: 100%; height: auto;
  max-height: min(52vh, 560px);
  object-fit: contain; object-position: center;
  background: var(--void);
  flex: none;
}
.story__copy { padding: clamp(18px, 2.4vw, 30px); }

.story__date {
  display: block; margin-bottom: 8px;
  font-size: 10px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--cyan-2);
}
.story__title {
  margin: 0 0 8px;
  font-size: clamp(21px, 2.7vw, 31px); font-weight: 900; font-stretch: 108%;
  letter-spacing: -.02em; line-height: 1.08;
}
.story__strap { margin: 0 0 16px; color: var(--ash); font-size: 14.5px; }
.story__text  { color: rgba(240, 234, 255, .88); font-size: 15px; }
.story__text p { margin: 0 0 14px; }
.story__text p:last-child { margin-bottom: 0; }
.story__more { margin: 22px 0 0; }

/* Pinned over the artwork and kept there while a long story scrolls under it.
   height:0 so it takes no space in the column and the image still starts at the
   very top of the panel; sticky rather than fixed, because fixed would escape
   the dialog and sit over the page behind it. */
.story__shut {
  position: sticky; top: 0; z-index: 2;
  height: 0; flex: none;
  display: flex; justify-content: flex-end;
}
.story__x {
  display: grid; place-items: center;
  width: 36px; height: 36px; margin: 12px 12px 0;
  border: 1px solid var(--hairline); border-radius: 50%;
  background: rgba(6, 3, 15, .72); color: var(--ice);
  font-size: 21px; line-height: 1; cursor: pointer;
  backdrop-filter: blur(6px);
  transition: background-color .25s var(--ease), border-color .25s var(--ease);
}
.story__x:hover { background: rgba(34, 211, 255, .2); border-color: var(--cyan); }

@media (max-width: 620px) {
  .story { width: 100vw; max-width: 100vw; max-height: 92vh; border-radius: 18px 18px 0 0; }
  /* Sits at the bottom of the screen on a phone, where a thumb is. The default
     dialog centring is fought here rather than inherited. */
  .story[open] { margin: auto auto 0; }
  /* Room for the accessibility button, which is fixed at the bottom-left and is
     now INSIDE this dialog while it is open -- so on a narrow screen it sits on
     the last line of the story unless the story ends above it. --a11y-clear is
     the same token every page already ends with, for the same reason. */
  .story__copy { padding-bottom: calc(var(--a11y-clear) + 12px); }
}

/* ==========================================================================
   Contact form

   Two columns on a laptop, one on a phone, with the form SECOND in the source
   so that reading order and tab order both run: what this is, then how to say
   it. The panel is visually to the right on a wide screen purely by grid
   placement, which costs nothing when the columns collapse.
   ========================================================================== */
.band--contact { padding: clamp(28px, 4vw, 54px) var(--gut) clamp(36px, 5vw, 70px); }

.cform {
  display: grid; gap: clamp(20px, 2.6vw, 44px);
  grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr);
  align-items: start; align-content: start;
}
.cform__heading {
  margin: 0 0 12px;
  font-size: clamp(24px, 3.1vw, 40px); font-weight: 900; font-stretch: 112%;
  letter-spacing: -.02em; line-height: 1.04;
}
.cform__strap { margin: 0; max-width: 42ch; color: var(--ash); font-size: 14.5px; }

.cform__notes { margin: clamp(20px, 2.4vw, 32px) 0 0; padding: 0; list-style: none;
                display: grid; gap: 12px; }
.cform__notes li {
  padding: 14px 16px; border-left: 2px solid var(--cyan);
  background: rgba(34, 211, 255, .05); border-radius: 0 10px 10px 0;
}
.cform__notes h3 { margin: 0 0 4px; font-size: 13.5px; font-weight: 800; }
.cform__notes p  { margin: 0; color: var(--ash); font-size: 13.5px; }
.cform__go { color: var(--cyan-2); white-space: nowrap; }
.cform__go:hover { text-decoration: underline; }

.cform__panel {
  padding: clamp(18px, 2.4vw, 30px);
  background: linear-gradient(180deg, var(--panel-2), var(--panel));
  border: 1px solid var(--hairline-lo); border-radius: 18px;
}

/* --------------------------------------------------------------------------
   Fields. Named the same as the admin sheet's but defined here independently:
   admin.css is not loaded on a public page, and a public page must never
   depend on a stylesheet that only exists behind a sign-in.
   -------------------------------------------------------------------------- */
.cform__form { display: grid; gap: 16px; }
.cform__form .fld { display: grid; gap: 5px; }
.cform__form .fld__label {
  font-size: 11px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--ash);
}
.cform__form .fld__in {
  width: 100%; padding: 12px 14px;
  background: rgba(6, 3, 15, .6); color: var(--ice);
  border: 1px solid var(--hairline); border-radius: 10px;
  font: inherit; font-size: 15px;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.cform__form .fld__in:focus {
  outline: none; border-color: var(--cyan);
  box-shadow: 0 0 0 3px rgba(34, 211, 255, .18);
}
/* Vertical only: a horizontal drag would push the textarea out of the panel. */
.cform__form .fld__in--area { resize: vertical; min-height: 150px; line-height: 1.6; }
.cform__form .fld__note { margin: 1px 0 0; color: var(--ash-dim); font-size: 12.5px; }

/* THE HONEYPOT. Off-screen rather than display:none or visibility:hidden --
   both of those are the first thing a spam script checks for, and a field it
   can see is hidden is a field it knows to leave alone. This is also why it
   keeps a real label and a real id. */
.cform__hp {
  position: absolute !important;
  left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden;
}

.cform__turnstile { margin: 4px 0 2px; min-height: 65px; }
.cform__go { margin: 4px 0 0; }
.cform__again { margin: 18px 0 0; }
.lnkish { color: var(--cyan-2); font-size: 13.5px; }
.lnkish:hover { text-decoration: underline; }

.cnote {
  margin: 0 0 18px; padding: 14px 16px; border-radius: 11px;
  border-left: 3px solid var(--cyan); background: rgba(34, 211, 255, .07);
  font-size: 14px;
}
.cnote p:first-child { margin-top: 0; }
.cnote p:last-child, .cnote ul:last-child { margin-bottom: 0; }
.cnote ul { margin: 8px 0 0; padding-left: 20px; }
.cnote--ok   { border-color: var(--cyan);   background: rgba(34, 211, 255, .07); }
.cnote--warn { border-color: var(--ember);  background: rgba(255, 59, 92, .09); }
.cnote code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .9em; padding: 1px 5px; border-radius: 5px;
  background: rgba(34, 211, 255, .12); color: var(--cyan-2);
}

@media (max-width: 860px) {
  .cform { grid-template-columns: minmax(0, 1fr); }
  .cform__strap { max-width: none; }
}

/* The event card's time / date range. Cyan like every other small meta label
   on this site, and above the description rather than below it: "when" is what
   someone scanning a row of events is actually looking for. */
.event__when {
  display: block; margin-top: 4px;
  font-size: 10px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
  color: var(--cyan-2);
}
.event__when + .event__text { margin-top: 4px; }

/* The admin form's checkbox: box and words in one hit target. */
.fld__check { display: flex; align-items: center; gap: 10px; cursor: pointer; }
.fld__check input { width: 18px; height: 18px; accent-color: var(--magenta); flex: none; }
.fld__check span { font-size: 14px; }

/* ==========================================================================
   Holding together when text is scaled up

   The accessibility panel can enlarge every font on the page without making
   the window any wider, and a layout that only works at one text size is a
   layout that fails the people the panel exists for.

   These are all the same bug: a grid or flex child defaults to a minimum size
   of its own content, so it refuses to shrink and pushes out of its container
   instead of wrapping inside it. min-width:0 lets it shrink; overflow-wrap
   lets a single long word break rather than stick out. Neither changes
   anything at normal text size, because nothing is being squeezed.
   ========================================================================== */
.step, .aside, .hero__copy, .cform__lede, .cform__panel,
.howto__lede, .event__body, .nitem__body, .gcard__body {
  min-width: 0;
}
.hero__copy, .howto__lede, .cform__lede { max-width: 100%; }
/* The hero shell centres its children on the cross axis, which sizes each one
   to its own content -- so a headline wider than the screen stays wider than
   the screen, and overflow-wrap never gets a chance to act because nothing has
   told the box to be narrower. This is the constraint that makes the wrapping
   above work. */
.hero__shell > * { max-width: 100%; }
.step__text, .aside__text, .howto__strap, .cform__strap,
.event__text, .nitem__text, .hero__lede,
/* The page headline too: "Live streams" at twice the normal text size is wider
   than a 320px phone, and a headline is the one thing that must never be the
   part running off the screen. */
.hero__title, .banner__title, .story__title { overflow-wrap: break-word; }

/* The rail's "view all" was free to stay on the heading line at any size.
   Let it drop under the heading instead of off the side. */
.rail { flex-wrap: wrap; }
.rail__label { min-width: 0; }

/* ==========================================================================
   The events and news pages

   Both reuse the homepage's own cards -- .event and .nitem, unchanged -- and
   only change how many sit in a row and what groups them. A visitor who clicked
   "View all" should land somewhere recognisably MORE of what they were looking
   at, not a second design of the same information, so the cards are the same
   cards and these rules do the grouping around them.
   ========================================================================== */
.band--eventlist,
.band--newslist { padding-bottom: clamp(30px, 4.4vw, 64px); }

/* --------------------------------------------------------------------------
   Events — one flowing grid

   Grouped under a day heading to begin with, and that was wrong: almost every
   day holds one event, so each heading introduced a single card followed by
   three quarters of an empty row. Four cards came to four screens of page. The
   cards flow now, and the date chip on each picture carries what the headings
   were there to say.

   auto-fill, NOT auto-fit. With auto-fit the empty tracks collapse, so a last
   row holding one card stretches it across the whole page -- a card that is
   300px wide in row one and 1400px wide in row two reads as a mistake.
   minmax(min(100%, 300px), 1fr) keeps every card the same size and lets them
   drop to full width on a phone.
   -------------------------------------------------------------------------- */
.events--wide {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
  margin-top: clamp(12px, 1.5vw, 18px);
}

.elist__count {
  margin-top: clamp(10px, 1.2vw, 16px);
  color: var(--ash); font-size: 12.5px;
}
.elist__count strong { color: var(--ice); font-weight: 700; }

/* --------------------------------------------------------------------------
   News, grouped by month
   -------------------------------------------------------------------------- */
.nmonth + .nmonth { margin-top: clamp(22px, 3vw, 40px); }

.nmonth__head {
  padding-bottom: 8px;
  border-bottom: 1px solid var(--hairline-lo);
  font-size: 13px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ice);
}

/* Two columns of rows on a wide screen. The homepage stacks them because its
   news column is a third of the page; here there is room for two, and a single
   column of 78px thumbnails down a 1400px page is mostly empty. */
.news--wide {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 420px), 1fr));
  margin-top: clamp(12px, 1.5vw, 18px);
}
.news--wide .nitem { padding: 10px; }
.news--wide .nitem__title { font-size: 13px; }
.news--wide .nitem__text  { font-size: 11.5px; }

/* --------------------------------------------------------------------------
   Nothing to show

   A real state on both pages, said in words. An empty grid looks like something
   failed to load; a sentence saying the calendar is clear is simply true.
   -------------------------------------------------------------------------- */
.elist__none {
  display: grid; gap: 8px; justify-items: center; text-align: center;
  margin-top: clamp(14px, 1.8vw, 22px);
  padding: clamp(28px, 5vw, 60px) clamp(18px, 3vw, 40px);
  border: 1px dashed var(--hairline);
  border-radius: 16px;
  background: linear-gradient(168deg, rgba(34, 18, 64, .4), rgba(8, 4, 20, .8));
}
.elist__none-title { font-size: clamp(16px, 1.6vw, 20px); font-weight: 800; }
.elist__none-text  { max-width: 54ch; color: var(--ash); font-size: 13.5px; line-height: 1.6; }
.elist__none-go    { margin-top: 6px; }

/* ==========================================================================
   Retiring a scheduled item while someone is looking at it
   --------------------------------------------------------------------------
   A card carries its own drop-off time (data-gd-ends) and gd.js retires it in
   two steps: .is-ending for a few seconds so the reader sees WHY the card is
   about to go, then removal. Something vanishing silently under the eye reads
   as a glitch; the same thing saying "Ended" first reads as the schedule
   keeping up.
   ========================================================================== */
.event.is-ending,
.ewhen__row.is-ending { position: relative; }

.event.is-ending { opacity: .55; }
.ewhen__row.is-ending { opacity: .55; }

/* The word itself, over the card rather than inside its text, so nothing
   reflows while it is showing -- a card that changes height on its way out
   drags every card below it. */
.event.is-ending::after {
  content: "Ended";
  position: absolute; top: 10px; left: 10px; z-index: 2;
  padding: 4px 10px; border-radius: 999px;
  background: rgba(5, 6, 14, .88); border: 1px solid var(--hairline);
  color: var(--ice); font-size: 10px; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
}
.ewhen__row.is-ending::after {
  content: "Ended";
  margin-left: 10px; padding: 3px 8px; border-radius: 999px;
  background: rgba(5, 6, 14, .88); border: 1px solid var(--hairline);
  font-size: 9.5px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
}

/* The fade is the last frame before removal, applied by script. */
.event.is-gone,
.ewhen__row.is-gone {
  opacity: 0;
  transition: opacity .45s var(--ease);
}

/* [hidden] LOSES TO ANY RULE THAT SETS display, and .elist__none sets
   display:grid a few lines above. Without this the "nothing scheduled" block
   renders on every page that HAS events, which is the opposite of its job.
   Same trap the artist page hit; same one-line answer. */
.elist__none[hidden] { display: none; }

.agenda__none {
  margin-top: clamp(12px, 1.6vw, 18px);
  padding: clamp(16px, 2.4vw, 26px);
  border: 1px dashed var(--hairline); border-radius: 14px;
  color: var(--ash); font-size: 13px; line-height: 1.6; text-align: center;
}
.agenda__none[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
  .event.is-gone,
  .ewhen__row.is-gone { transition: none; }
}

/* ==========================================================================
   Paging

   Drawn only when there is more than one page -- see inc/gd/pager.php. The
   "off" state is a <span> rather than a disabled link, so on page one the
   Previous control is not a tab stop at all.
   ========================================================================== */
.pager {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 8px clamp(10px, 1.4vw, 18px);
  margin-top: clamp(22px, 3vw, 40px);
  padding-top: clamp(16px, 2vw, 26px);
  border-top: 1px solid var(--hairline-lo);
}

.pager__step {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 16px; border-radius: 999px;
  border: 1px solid var(--hairline);
  font-size: 11px; font-weight: 800; letter-spacing: .13em; text-transform: uppercase;
  transition: border-color .25s var(--ease), background .25s var(--ease);
}
.pager__step .i { width: 13px; height: 13px; }
.pager__step:hover { border-color: var(--cyan); background: rgba(34, 211, 255, .12); }
.pager__step.is-off { color: var(--ash-dim); border-color: var(--hairline-lo); }

.pager__list { display: flex; align-items: center; gap: 4px; list-style: none; margin: 0; padding: 0; }
.pager__n {
  display: grid; place-items: center;
  min-width: 34px; height: 34px; padding: 0 9px;
  border-radius: 8px;
  font-size: 13px; font-variant-numeric: tabular-nums;
  transition: background .2s var(--ease), color .2s var(--ease);
}
.pager__n:hover { background: rgba(34, 211, 255, .14); color: var(--cyan-2); }
/* The page you are on, which is not a link. Solid rather than outlined so it
   reads as "here" at a glance rather than as one more button. */
.pager__n.is-here {
  background-image: linear-gradient(100deg, var(--violet), var(--magenta));
  color: #04121A; font-weight: 800;
}
.pager__gap { padding: 0 2px; color: var(--ash-dim); }

@media (max-width: 560px) {
  /* The numbers are the first thing to go: Previous and Next do the same job
     with two controls instead of nine, and a wrapped row of page numbers is
     taller than the content it pages through. */
  .pager__list { display: none; }
  .pager { justify-content: space-between; }
}

/* ==========================================================================
   One event's page — /event.php?e=<slug>
   ========================================================================== */
.ehead {
  position: relative; isolation: isolate;
  display: flex; align-items: flex-end;
  min-height: clamp(260px, 36vw, 440px);
  border-radius: 16px; overflow: hidden;
  background: linear-gradient(168deg, rgba(34, 18, 64, .7), rgba(8, 4, 20, .92));
}
.ehead__art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -2; }
/* The veil, not a filter on the image: a filter would also dim the text that
   sits over it, and this has to darken only what is behind. */
.ehead__veil {
  position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to top, rgba(4, 2, 12, .94) 12%, rgba(4, 2, 12, .55) 55%, rgba(4, 2, 12, .25));
}
.ehead.is-empty .ehead__veil {
  background:
    radial-gradient(72% 70% at 32% 28%, rgba(139, 59, 255, .4), transparent 70%),
    linear-gradient(145deg, #1A0E3C, #07040F);
}
.ehead__copy { padding: clamp(20px, 3vw, 40px); max-width: 68ch; }
.ehead__kind {
  display: inline-block; margin: 0 0 10px; padding: 3px 11px;
  border: 1px solid var(--cyan); border-radius: 999px;
  color: var(--cyan);
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
}
.ehead__title {
  margin: 0; font-size: clamp(30px, 5.4vw, 62px); line-height: 1.04;
  letter-spacing: -.02em; font-weight: 800;
}
.ehead__line { margin: 12px 0 0; color: var(--ash); font-size: clamp(14px, 1.4vw, 17px); }

.ewhen { margin-top: clamp(18px, 2.4vw, 30px); }
.ewhen__label {
  margin: 0 0 12px; font-size: 11px; font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ash);
}
.ewhen__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.ewhen__row {
  padding: 11px 15px;
  border: 1px solid var(--hairline-lo); border-radius: 10px;
  background: rgba(10, 5, 24, .5);
}
/* The next one, marked once. Every row claiming to be next is every row
   claiming nothing. */
.ewhen__row.is-next { border-color: var(--cyan); background: rgba(34, 211, 255, .08); }
.ewhen__when { font-size: 14px; font-weight: 600; }
.ewhen__row.is-next .ewhen__when { color: var(--ice); }
.ewhen__none { margin: 0; color: var(--ash); }
.ewhen__go { margin: clamp(16px, 2vw, 24px) 0 0; }

@media (max-width: 720px) {
  .ehead { min-height: 0; }
  /* Below the picture rather than over it: at this width the veil has to be
     almost opaque for the title to stay legible, which hides the picture the
     page is partly there to show. */
  .ehead { display: block; }
  .ehead__art { position: relative; height: auto; aspect-ratio: 16 / 10; z-index: 0; }
  .ehead__veil { display: none; }
  .ehead__copy { padding: 16px 0 0; }
}
/* The "through <date>" tail on a multi-day event. Its own element so it can
   stay on the same line as the date at full width and drop below it when the
   two together no longer fit. */
.ewhen__to { color: var(--ash); font-weight: 400; }

/* ==========================================================================
   An event, in the story panel

   The panel chrome is .story__* — shared with news so the close button, the
   picture and the scroll behaviour are one thing, not two. Only what is
   genuinely different about an event lives here.
   ========================================================================== */
.evdlg__kind {
  display: inline-block; margin: 0 0 8px; padding: 3px 11px;
  border: 1px solid var(--cyan); border-radius: 999px;
  color: var(--cyan);
  font-size: 10.5px; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
}
.evdlg__when {
  margin: 10px 0 0;
  font-size: 15px; font-weight: 700; color: var(--ice);
}
/* The "through <date>" tail on a multi-day event: lighter, because it is the
   second half of a fact whose first half is the date beside it. */
.evdlg__to { color: var(--ash); font-weight: 400; }
.evdlg__repeat {
  margin: 4px 0 0;
  font-size: 12.5px; color: var(--ash);
}
.evdlg .story__strap { margin-top: 14px; }

/* ── the event page's full write-up and extra pictures ───────────────────── */
/* The panel is the summary; this is what "More details" leads to, so it is
   allowed to be long. Measured rather than full-bleed: a line of body text
   running the whole width of a desktop is the one thing that makes a page of
   writing unreadable. */
.ebody {
  margin-top: clamp(18px, 2.4vw, 28px);
  max-width: 68ch;
  color: var(--ash);
  font-size: clamp(14px, 1.35vw, 16px); line-height: 1.7;
}
.ebody p { margin: 0 0 1em; }
.ebody p:last-child { margin-bottom: 0; }

.eshots {
  list-style: none; margin: clamp(18px, 2.4vw, 28px) 0 0; padding: 0;
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
}
.eshot {
  border: 1px solid var(--hairline-lo); border-radius: 10px; overflow: hidden;
  background: rgba(10, 5, 24, .5);
}
/* A fixed ratio so a folder of mixed shapes still reads as a grid rather than
   a ransom note. The full-size view is the file itself; this is the contact
   sheet. */
.eshot img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
