/* The single italic (800) is for the sponsor pitch line. Azeret Mono has real
   italics, and a synthesised slant on a mono face looks broken — worth the one
   extra face, which is why the axis is listed rather than the whole range. */
@import url('https://fonts.googleapis.com/css2?family=Azeret+Mono:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,800&display=swap');

/* ==========================================================================
   Not So Daily Stand Up
   --------------------------------------------------------------------------
   Design tokens come straight from Assets/Brand Notes.gdoc in the NSDS Drive:

     Purple #2E1A42 · Yellow #FFFF82 · Pale Yellow #FFFFC0 · Pale Cyan #DDEFD1

   Two shape rules from the same doc drive most of the layout below:
     1. One line of text  -> round the ends to perfect circles (a pill).
     2. A block of text   -> radius matches the text size (24px text, 24px
        radius), and padding is that size x 1.75.
   Both are encoded as tokens rather than eyeballed per component, so a font
   size change carries its radius and padding along with it.
   ========================================================================== */

:root {
  --purple: #2e1a42;
  --yellow: #ffff82;
  --pale-yellow: #ffffc0;
  --pale-cyan: #ddefd1;

  /* Tints of the purple, lifted from the brand SVGs (Squig-S1 uses #533858)
     so surfaces stay inside the palette instead of drifting toward grey. */
  --purple-raised: #3a2252;
  --purple-line: #533858;
  --ink: #1b0f27;

  --font: 'Azeret Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Brand rule 2, as a scale. Each step carries its own radius + padding. */
  --t-xs: 12px;
  --t-sm: 14px;
  --t-md: 16px;
  --t-lg: 20px;
  --t-xl: 24px;
  --pad-ratio: 1.75;

  --r-pill: 999px;
  --r-sm: 14px;
  --r-md: 20px;
  --r-lg: 24px;
  --r-xl: 32px;

  --gutter: clamp(20px, 5vw, 56px);
  --measure: 1180px;

  --shadow: 0 18px 48px rgb(12 6 20 / 45%);
  --shadow-sm: 0 6px 18px rgb(12 6 20 / 30%);

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* --------------------------------------------------------------- reset -- */

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
  /* Anchored sections shouldn't land under the sticky header. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--purple);
  color: #fff;
  font-family: var(--font);
  font-size: var(--t-md);
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

img,
svg,
video,
iframe {
  display: block;
  max-width: 100%;
}

/* The width/height attributes on <img> exist to reserve space and stop layout
   shift, but they also land as presentational hints. Without this, a definite
   height wins over any aspect-ratio we set and the element renders at its
   literal pixel height. */
img {
  height: auto;
}

a {
  color: var(--yellow);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

ul {
  list-style: none;
}

/* The UA sheet's `[hidden] { display: none }` loses to any author rule that
   sets display — and both .pill and .stats do. Without this, setting the
   `hidden` property from JS silently does nothing, which left the "Show all
   shows" button on screen as a dead control after it had been used. */
[hidden] {
  display: none !important;
}

/* The focus ring has to follow the surface it lands on. Brand yellow is crisp
   against the purple, but sits at roughly 1:1 on the pale-yellow and white
   panels — where the ring would simply vanish. */
:root {
  --focus: var(--yellow);
}

.section--light,
.cta,
.card--accent,
.run--main,
.metric--hero {
  --focus: var(--purple);
}

:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 4px;
}

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

.skip-link {
  position: absolute;
  top: -100px;
  left: 16px;
  z-index: 999;
  padding: 12px 24px;
  background: var(--yellow);
  color: var(--purple);
  font-weight: 700;
  border-radius: var(--r-pill);
  transition: top 0.2s var(--ease);
}

.skip-link:focus {
  top: 16px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ------------------------------------------------------------ primitives -- */

.wrap {
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Brand rule 1: single-line text always gets fully round ends. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  border-radius: var(--r-pill);
  /* padding = text size x 1.75, halved vertically to keep the pill tight */
  padding: calc(var(--t-sm) * var(--pad-ratio) / 2.2)
    calc(var(--t-sm) * var(--pad-ratio));
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
  text-decoration: none;
  border: 2px solid transparent;
  transition: transform 0.2s var(--ease), background-color 0.2s var(--ease),
    color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.pill--solid {
  background: var(--yellow);
  color: var(--purple);
}

.pill--solid:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}

.pill--ghost {
  background: transparent;
  color: var(--yellow);
  border-color: var(--yellow);
}

.pill--ghost:hover {
  background: var(--yellow);
  color: var(--purple);
  transform: translateY(-2px);
}

/* A yellow ghost pill on the pale-yellow surface is ~1:1 — invisible. The
   "Show all shows" button lives there, so it needs the inverse treatment. */
.section--light .pill--ghost {
  color: var(--purple);
  border-color: var(--purple);
}

.section--light .pill--ghost:hover {
  background: var(--purple);
  color: var(--yellow);
}

/* Same problem for plain links dropped into a light section. */
.section--light a:not(.pill):not(.archive__item) {
  color: var(--purple-line);
}

.pill--lg {
  font-size: var(--t-md);
  padding: calc(var(--t-md) * var(--pad-ratio) / 2.2)
    calc(var(--t-md) * var(--pad-ratio));
}

.eyebrow {
  display: inline-block;
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--pale-cyan);
}

/* Inline code, the way Slack renders it: a tinted chip in the system code
   face. The joke only lands if the flag looks like something you'd actually
   type, and Azeret — being the body face here — reads as prose instead.
   Menlo/Monaco/Consolas is literally Slack's own stack, so it costs no
   webfont and looks native on every OS. */
.hero__flag {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
    'Liberation Mono', 'Courier New', monospace;
  font-style: normal;
  font-weight: 600;
  /* Code faces run large at a shared size, and this sits inside a 56px
     headline — em keeps the ratio wherever the clamp lands. */
  font-size: 0.82em;
  letter-spacing: -0.02em;
  color: var(--yellow);
  background: rgb(255 255 130 / 13%);
  border: 1px solid rgb(255 255 130 / 32%);
  border-radius: 0.22em;
  padding: 0.08em 0.28em;
  /* Without this the chip loses its border and padding on the second line
     when the headline wraps mid-flag. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}

/* ---------------------------------------------------------------- header -- */

/* Kept quiet on purpose: darker and less translucent than the old bar, a
   hairline instead of a yellow rule, and no CTA pill. The hero underneath is
   doing the shouting. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgb(27 15 39 / 88%);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid rgb(255 255 255 / 8%);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding-block: 10px;
}

/* Sizing by width and letting height follow keeps the wordmark's proportions.
   Pinning the height instead made flex shrink the width on narrow screens,
   squashing the logo horizontally rather than scaling it down. */
.site-header__logo {
  flex: 0 0 auto;
  line-height: 0;
}

.site-header__logo img {
  width: min(216px, 42vw);
  height: auto;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 2px;
  min-width: 0;
}

.site-nav a {
  padding: 6px 12px;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Dimmed until you look for it — full white made four links compete with
     the wordmark next to them. */
  color: rgb(255 255 255 / 72%);
  text-decoration: none;
  white-space: nowrap;
  border-radius: var(--r-pill);
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease);
}

.site-nav a:hover,
.site-nav a[aria-current='page'] {
  background: rgb(255 255 130 / 12%);
  color: var(--yellow);
}

.site-nav .pill {
  margin-left: 8px;
}

/* Nav collapses to the CTA only — the section links stay reachable by
   scrolling, so a burger menu would be more machinery than the page needs.
   Collapsing at 1020px rather than 860px because that's the width where the
   logo, four links and the CTA actually stop fitting on one row. */
@media (max-width: 1020px) {
  /* The links used to be hidden here, which was survivable only because a CTA
     pill stayed behind. With that pill gone, hiding them would leave the
     header with no navigation at all — so the row wraps instead and the links
     take a second line, scrolling sideways if even that is too narrow. */
  .site-header__inner {
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding-block: 8px;
  }

  .site-nav {
    width: 100%;
    justify-content: center;
    overflow-x: auto;
    /* Momentum scrolling without a visible bar under four short links. */
    scrollbar-width: none;
  }

  .site-nav::-webkit-scrollbar {
    display: none;
  }

  .site-nav__links {
    display: flex;
    gap: 2px;
  }
}

@media (max-width: 560px) {
  .site-header__logo img {
    width: min(230px, 44vw);
  }

  /* Large pills can't wrap either, so step them down to the base size rather
     than let a two-word CTA push the page sideways. */
  .pill--lg {
    font-size: var(--t-sm);
    padding: calc(var(--t-sm) * var(--pad-ratio) / 2.2)
      calc(var(--t-sm) * var(--pad-ratio));
  }

  /* A full email address is wider than a phone even at the smaller size. Once
     it wraps it stops being one line, so it takes the block radius instead of
     round ends — same rule the duo cards follow. */
  .cta__actions .pill {
    white-space: normal;
    overflow-wrap: anywhere;
    border-radius: var(--r-sm);
  }
}

/* ------------------------------------------------------------------ hero -- */

.hero {
  position: relative;
  min-height: min(92vh, 860px);
  display: flex;
  align-items: center;
  overflow: hidden;
  padding-block: clamp(56px, 10vh, 120px);
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

/* Photo layer. Frames cross-fade with a slow drift — this is the default
   background and it carries the hero on its own. The video layer below is
   an enhancement that sits on top only once it is genuinely playing. */
.hero__frame {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  opacity: 0;
  animation: heroCycle 24s infinite;
  will-change: opacity, transform;
}

.hero__frame:nth-child(1) {
  animation-delay: 0s;
}
.hero__frame:nth-child(2) {
  animation-delay: 8s;
}
.hero__frame:nth-child(3) {
  animation-delay: 16s;
}

@keyframes heroCycle {
  0% {
    opacity: 0;
    transform: scale(1.06);
  }
  6%,
  30% {
    opacity: 1;
  }
  36% {
    opacity: 0;
  }
  100% {
    opacity: 0;
    transform: scale(1.14);
  }
}

/* Stills instead of a slideshow when motion is unwelcome. */
@media (prefers-reduced-motion: reduce) {
  .hero__frame {
    animation: none;
  }
  .hero__frame:nth-child(1) {
    opacity: 1;
  }
}

/* Video wall — three columns of the same reel, seeked to three different
   points. Fades in as one unit so you never see it assemble column by column.
   The 1px inset on each column hides the seam that subpixel column widths can
   otherwise leave between them. */
.hero__wall {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  opacity: 0;
  transition: opacity 0.9s var(--ease);
}

/* Set by JS only once every column has actually decoded a frame, so a missing
   or blocked media/hero.mp4 leaves the photo slideshow untouched. */
.hero__wall.is-playing {
  opacity: 1;
}

.hero__col {
  position: relative;
  overflow: hidden;
  margin-inline: -1px;
}

.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* One column instead of three on phones. Three landscape panes on a 390px
   screen are slivers, and three simultaneous h264 decodes is real jank and
   battery cost for something purely decorative. main.js reads the computed
   display here and never gives the hidden columns a src, so this also saves
   the two extra requests rather than just hiding them. */
@media (max-width: 700px) {
  .hero__wall {
    grid-template-columns: 1fr;
  }

  .hero__col + .hero__col {
    display: none;
  }
}

/* ---- TEMPORARY: single-column preview -------------------------------------
   Collapses the wall to one full-bleed video at every width so the reel can be
   judged on its own. main.js reads the computed display and only loads the one
   visible column, so this is a real single-video hero, not three with two
   hidden. DELETE THIS BLOCK to get the three-column wall back.             */
.hero__wall {
  grid-template-columns: 1fr;
}

.hero__col + .hero__col {
  display: none;
}
/* ---- end temporary -------------------------------------------------------- */

/* Keeps type legible over any frame, and pushes the imagery toward the
   brand purple rather than letting raw photo colour set the mood. */
/* Dialled back so the reel is actually visible: the middle band used to
   composite to roughly 80% purple, which flattened the video to a texture. Now
   about half that. Previous values, if this turns out too transparent in the
   wild: 72/58/94 on the linear, 20/85 on the radial.

   Two layers, not one — the linear keeps the bottom edge solid so the hero
   melts into the section below it, and the radial darkens the corners so the
   header and the type block don't sit on raw footage. Legibility no longer
   depends on either: the hero type carries its own shadow below. */
.hero__scrim {
  position: absolute;
  inset: 0;
  /* Layered front-to-back. The first one is doing the real work: the hero type
     is left-aligned, so the contrast it needs is a left-hand wedge, not a veil
     over the entire frame. Tested against the brightest moment in the reel — a
     white lineup card next to a lit food shot — where an even scrim had to sit
     near 80% to keep the yellow legible and flattened the whole video to
     wallpaper. This keeps the right-hand two thirds nearly clear. */
  background: linear-gradient(
      90deg,
      rgb(46 26 66 / 84%) 0%,
      rgb(46 26 66 / 62%) 46%,
      rgb(46 26 66 / 0%) 72%
    ),
    linear-gradient(
      180deg,
      rgb(46 26 66 / 46%) 0%,
      rgb(46 26 66 / 22%) 45%,
      rgb(46 26 66 / 88%) 100%
    ),
    radial-gradient(120% 80% at 50% 0%, rgb(46 26 66 / 4%), rgb(46 26 66 / 44%));
}

/* Narrow screens have no spare width for a wedge — the type runs the full
   measure, so it needs an even wash behind it instead. */
@media (max-width: 700px) {
  .hero__scrim {
    background: linear-gradient(
        180deg,
        rgb(46 26 66 / 62%) 0%,
        rgb(46 26 66 / 52%) 45%,
        rgb(46 26 66 / 92%) 100%
      ),
      radial-gradient(120% 80% at 50% 0%, rgb(46 26 66 / 10%), rgb(46 26 66 / 50%));
  }
}

.hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: var(--measure);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.hero__logo {
  width: min(560px, 88%);
  margin-bottom: 28px;
  filter: drop-shadow(0 8px 24px rgb(12 6 20 / 55%));
}

.hero__tagline {
  max-width: 30ch;
  font-size: clamp(30px, 5.4vw, 56px);
  font-weight: 800;
  line-height: 1.12;
  letter-spacing: -0.02em;
  color: #fff;
  text-wrap: balance;
  /* This line is wider than the scrim's wedge, so on the reel's brightest frame
     its tail lands on a white lineup card. Rather than widen the veil across
     the whole hero to protect one word, the type carries its own edge — which
     is also what the wordmark does, so it reads as brand rather than as a
     patch. `paint-order` puts the stroke behind the fill; without it the stroke
     grows inward and thins the letterforms. */
  paint-order: stroke fill;
  -webkit-text-stroke: 4px rgb(27 15 39 / 78%);
  text-shadow: 0 2px 14px rgb(12 6 20 / 65%);
}

.hero__tagline em {
  font-style: normal;
  color: var(--yellow);
}

.hero__sub {
  max-width: 52ch;
  margin-top: 20px;
  font-size: clamp(15px, 1.7vw, 18px);
  color: rgb(255 255 255 / 88%);
  text-wrap: pretty;
  /* Small text over moving footage is the first thing to become unreadable, so
     this leans on a tighter, denser shadow than the headline's. */
  text-shadow: 0 1px 2px rgb(12 6 20 / 70%), 0 1px 8px rgb(12 6 20 / 85%);
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 32px;
}

/* Stats bar ---------------------------------------------------------------- */

.stats {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(24px, 6vw, 72px);
  margin-top: 48px;
  padding-top: 28px;
  border-top: 1px solid rgb(255 255 130 / 22%);
}

/* Markup is <dt> then <dd> because that's the order the spec requires;
   reversing the column paints the number above its label anyway. */
.stat {
  display: flex;
  flex-direction: column-reverse;
}

.stat__value {
  font-size: clamp(28px, 4vw, 40px);
  font-weight: 900;
  line-height: 1;
  color: var(--yellow);
  font-variant-numeric: tabular-nums;
}

.stat__label {
  margin-top: 6px;
  font-size: var(--t-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(221 239 209 / 85%);
}

/* -------------------------------------------------------------- sections -- */

.section {
  padding-block: clamp(64px, 9vw, 112px);
  position: relative;
}

.section--light {
  background: var(--pale-yellow);
  color: var(--purple);
}

.section--raised {
  background: var(--purple-raised);
}

.section__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: clamp(28px, 4vw, 48px);
}

.section__title {
  font-size: clamp(28px, 4.4vw, 44px);
  font-weight: 900;
  line-height: 1.1;
  letter-spacing: -0.02em;
}

.section__intro {
  max-width: 62ch;
  margin-top: 12px;
  font-size: var(--t-md);
  opacity: 0.88;
  text-wrap: pretty;
}

.section--light .eyebrow {
  color: var(--purple-line);
}

/* Squig decorations, straight from Assets/Web assets/SVG. Purely ornamental,
   so they're hidden from assistive tech and dropped on small screens. */
.squig {
  position: absolute;
  pointer-events: none;
  user-select: none;
  opacity: 0.9;
}

.squig--shows {
  top: -26px;
  right: 6%;
  width: 78px;
  transform: rotate(12deg);
}

.squig--watch {
  bottom: 4%;
  left: 3%;
  width: 96px;
  transform: rotate(-14deg);
  opacity: 0.5;
}

@media (max-width: 760px) {
  .squig {
    display: none;
  }
}

/* ----------------------------------------------------------------- shows -- */

.shows {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
  gap: 24px;
}

.show-embed {
  width: 100%;
  min-height: 450px;
  border: 0;
  border-radius: var(--r-md);
  background: var(--purple-raised);
  box-shadow: var(--shadow);
}

/* Shown when Luma reports nothing scheduled — a real state, not an error.
   Between runs of the show this is what most visitors will land on, so it
   gets a genuine call to action rather than an apology. */
.no-shows {
  /* 20px floor rather than 32px: on a 320px screen the wider inset squeezed
     the two buttons below their own width. */
  padding: clamp(20px, 5vw, 56px);
  border: 2px dashed rgb(255 255 130 / 45%);
  border-radius: var(--r-lg);
  background: rgb(255 255 130 / 6%);
  text-align: center;
}

.no-shows__title {
  font-size: clamp(20px, 3vw, 28px);
  font-weight: 800;
  color: var(--yellow);
  margin-bottom: 10px;
}

.no-shows__body {
  max-width: 46ch;
  margin: 0 auto 24px;
  opacity: 0.9;
}

/* ------------------------------------------------------------- instagram -- */

.posts {
  display: grid;
  /* Eight 9:16 reels. 230px lands on four across at desktop — two even rows of
     four — folding to three, then two on a phone. Any narrower and the columns
     become slivers; any wider and the last row is left half empty. */
  grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr));
  gap: 14px;
}

/* Forced to two on a phone. auto-fit drops to a single column below ~500px,
   which turns eight 9:16 reels into a ~5000px stack — a pair per row keeps the
   section scannable and the cards are still ~168px wide. */
@media (max-width: 600px) {
  .posts {
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
}

/* 9:16, because that is what a reel is. The poster fills it exactly, so there
   is no letterboxing and no layout shift when the video starts. */
.post {
  position: relative;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--ink);
  aspect-ratio: 9 / 16;
}

.post__video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: pointer;
  background: var(--ink);
}

/* The yellow button. Centred and large before the click — it is the single
   affordance on the card — then it gets out of the way once playing. */
.post__play {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--yellow);
  color: var(--purple);
  cursor: pointer;
  box-shadow: 0 6px 20px rgb(12 6 20 / 45%);
  transition: transform 0.2s var(--ease), opacity 0.25s var(--ease);
}

/* Nudged right so the triangle looks centred — its visual mass sits left of
   its bounding box. */
.post__play svg {
  margin-left: 3px;
}

.post:hover .post__play {
  transform: scale(1.08);
}

/* Hidden once it's running, but pointer-events:none rather than display:none
   so the video underneath keeps receiving the click that pauses it. */
.post--playing .post__play {
  opacity: 0;
  pointer-events: none;
}

/* "View on Instagram" — small, bottom-left, out of the way of the play
   button, and gone while the video is running. */
.post__source {
  position: absolute;
  left: 10px;
  bottom: 10px;
  padding: 5px 11px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--purple);
  background: rgb(255 255 255 / 88%);
  border-radius: var(--r-pill);
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.2s var(--ease);
}

.post:hover .post__source,
.post__source:focus-visible {
  opacity: 1;
}

.post--playing .post__source {
  opacity: 0;
  pointer-events: none;
}


/* ----------------------------------------------- secondary page furniture -- */

/* Shorter, calmer hero for sponsorship.html and perform.html — one still
   photo rather than the landing page's slideshow. */
.page-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: min(64vh, 560px);
  padding-block: clamp(64px, 12vh, 128px) clamp(48px, 8vh, 88px);
  overflow: hidden;
}

.page-hero--short {
  min-height: min(46vh, 420px);
}

.page-hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.page-hero__media picture,
.page-hero__media img {
  width: 100%;
  height: 100%;
}

.page-hero__media img {
  object-fit: cover;
}

/* The sponsorship shot is cropped to the left two-thirds of the frame so the
   crowd carries it rather than the back of a comedian's head. That leaves a
   near-square file in a wide band, so cover crops vertically — sit slightly
   below centre to keep the seats in view instead of the ceiling rig. */
.page-hero__media--crowd img {
  object-position: 50% 58%;
}

.page-hero__inner {
  position: relative;
  z-index: 2;
}

.page-hero__title {
  max-width: 26ch;
  margin-top: 10px;
  font-size: clamp(26px, 3.6vw, 40px);
  font-weight: 900;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.page-hero__title em {
  font-style: normal;
  color: var(--yellow);
}

.page-hero__sub {
  max-width: 62ch;
  margin-top: 18px;
  font-size: clamp(15px, 1.6vw, 17px);
  color: rgb(255 255 255 / 88%);
  text-wrap: pretty;
}

.page-hero__sub em {
  font-style: normal;
  color: var(--yellow);
  font-weight: 700;
}

.sub-title {
  font-size: clamp(20px, 2.6vw, 26px);
  font-weight: 800;
  letter-spacing: -0.01em;
  margin-bottom: 18px;
}

.muted {
  opacity: 0.6;
  font-weight: 600;
}

.fineprint {
  margin-top: 10px;
  font-size: var(--t-xs);
  opacity: 0.7;
}

/* Metrics --------------------------------------------------------------- */

.metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: 16px;
}

.metric {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: calc(var(--t-lg) * var(--pad-ratio) / 1.6);
  background: var(--purple);
  color: #fff;
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

/* The headline number carries its emphasis with colour rather than by
   spanning columns — spanning left the fifth metric orphaned on its own row. */
.metric--hero {
  background: var(--yellow);
  color: var(--purple);
}

.metric__value {
  font-size: clamp(30px, 4.4vw, 48px);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--yellow);
  font-variant-numeric: tabular-nums;
}

.metric--hero .metric__value {
  color: var(--purple);
}

.metric__label {
  font-size: var(--t-sm);
  font-weight: 700;
  letter-spacing: 0.04em;
}

.metric__note {
  font-size: var(--t-xs);
  opacity: 0.72;
  line-height: 1.45;
}

/* Ticked lists ----------------------------------------------------------- */

.ticks {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ticks--tight {
  gap: 8px;
}

.ticks li {
  position: relative;
  padding-left: 28px;
  font-size: var(--t-sm);
  line-height: 1.55;
}

.ticks li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--yellow);
  border: 2px solid var(--purple);
}

.section--raised .ticks li::before,
.section:not(.section--light) .ticks li::before {
  border-color: var(--yellow);
  background: transparent;
}

/* Investor name chips ---------------------------------------------------- */

.logos {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.logos li {
  padding: calc(var(--t-sm) * var(--pad-ratio) / 2.4) calc(var(--t-sm) * var(--pad-ratio) / 1.4);
  border-radius: var(--r-pill);
  border: 2px solid currentColor;
  font-size: var(--t-sm);
  font-weight: 700;
  white-space: nowrap;
}

/* Run of show ------------------------------------------------------------ */

.runofshow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 16px;
  counter-reset: run;
  list-style: none;
}

.run {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: calc(var(--t-lg) * var(--pad-ratio) / 1.6);
  border: 2px solid rgb(255 255 130 / 30%);
  border-radius: var(--r-lg);
}

/* The show itself is the middle of the sandwich — make it read that way. */
.run--main {
  background: var(--yellow);
  color: var(--purple);
  border-color: var(--yellow);
}

.run__time {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pale-cyan);
}

.run--main .run__time {
  color: var(--purple-line);
}

.run__name {
  font-size: var(--t-lg);
  font-weight: 800;
  line-height: 1.2;
}

.run__note {
  font-size: var(--t-sm);
  opacity: 0.85;
  line-height: 1.5;
}

/* Feature cards ---------------------------------------------------------- */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 20px;
}

.card {
  padding: calc(var(--t-lg) * var(--pad-ratio) / 1.5);
  background: var(--purple-raised);
  border: 1px solid rgb(255 255 130 / 18%);
  border-radius: var(--r-lg);
}

.card--accent {
  background: var(--yellow);
  color: var(--purple);
  border-color: var(--yellow);
}

.card__title {
  font-size: var(--t-lg);
  font-weight: 800;
  line-height: 1.25;
  margin-bottom: 14px;
}

/* Two big linked cards on the landing page ------------------------------- */

.duo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 20px;
}

.duo__card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  padding: clamp(28px, 4vw, 44px);
  background: var(--purple);
  border: 2px solid rgb(255 255 130 / 26%);
  border-radius: var(--r-xl);
  color: #fff;
  text-decoration: none;
  transition: transform 0.2s var(--ease), border-color 0.2s var(--ease),
    background-color 0.2s var(--ease);
}

.duo__card:hover {
  transform: translateY(-4px);
  border-color: var(--yellow);
}

.duo__title {
  font-size: clamp(22px, 2.8vw, 30px);
  font-weight: 900;
  line-height: 1.15;
  letter-spacing: -0.02em;
}

.duo__body {
  font-size: var(--t-sm);
  opacity: 0.85;
  margin-bottom: 8px;
  text-wrap: pretty;
}

.duo__body em {
  font-style: normal;
  color: var(--yellow);
  font-weight: 700;
}

/* These labels are too long to hold one line on a phone. The brand rule only
   asks for round ends when the text *is* one line — once it wraps it's a text
   block, so it takes the matching radius for its own size instead. */
.duo__card .pill {
  white-space: normal;
  border-radius: var(--r-sm);
  text-align: left;
}

/* Embedded Google Form --------------------------------------------------- */

.formframe {
  border-radius: var(--r-lg);
  overflow: hidden;
  background: #fff;
  box-shadow: var(--shadow);
}

.formframe iframe {
  display: block;
  width: 100%;
  border: 0;
}

.formframe__fallback {
  margin-top: 20px;
  text-align: center;
  font-size: var(--t-sm);
  opacity: 0.8;
}

/* --------------------------------------------------------------- archive -- */

.archive {
  display: grid;
  /* auto-fit, not auto-fill: a partly-filled last row stretches to fill the
     width instead of leaving phantom empty tracks beside it. 300px lands on
     three big posters at desktop and two on a tablet. */
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 20px;
}

/* Poster card: the artwork leads at full width and the details sit under it.
   These are designed 1080x1080 graphics we want people to actually look at, so
   they get the whole tile rather than a thumbnail beside the text. */
.archive__item {
  display: flex;
  flex-direction: column;
  background: rgb(46 26 66 / 6%);
  border: 1px solid rgb(46 26 66 / 14%);
  border-radius: var(--r-lg);
  /* Clips the poster to the card's rounded top corners. */
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform 0.2s var(--ease), background-color 0.2s var(--ease),
    border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

.archive__item:hover {
  transform: translateY(-4px);
  background: rgb(46 26 66 / 12%);
  border-color: rgb(46 26 66 / 30%);
  box-shadow: var(--shadow);
}

/* Square because the source art is square — a 1:1 box crops nothing. */
.archive__art {
  width: 100%;
  aspect-ratio: 1;
  overflow: hidden;
  /* Covers are a grab-bag of light and dark artwork; a purple bed keeps the
     ones with transparency inside the palette. */
  background: var(--purple);
}

.archive__art img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.45s var(--ease);
}

.archive__item:hover .archive__art img {
  transform: scale(1.04);
}

/* min-width:0 lets the long show names actually ellipsize instead of forcing
   the flex row wider than its column. */
.archive__body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  padding: calc(var(--t-md) * var(--pad-ratio) / 1.5);
}

/* Tighter tracking than the other eyebrows: this line carries a date *and* a
   city, and at 0.1em "Jun 17, 2026 · San Francisco" wrapped to two lines. */
.archive__date {
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--purple-line);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Show names run to 60+ characters. Two lines keeps every row the same height
   whatever the title, so the grid stays a grid. */
.archive__name {
  font-size: var(--t-sm);
  font-weight: 700;
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.archive__where {
  font-size: var(--t-xs);
  opacity: 0.75;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.archive__more {
  margin-top: 28px;
  text-align: center;
}

/* ----------------------------------------------------------- sponsor ticker -- */

/* The ticker breaks out of .wrap so logos can run to both screen edges. */
.ticker {
  margin-inline: calc(var(--gutter) * -1);
  overflow: hidden;
}

/* Without .is-ticking this is a centred, wrapping wall — see the note in
   index.html about why the animation is opt-in rather than opt-out. Both the
   track and the set have to wrap: the set holds the chips, and the track holds
   the set plus its clone. */
.ticker__track,
.ticker__set {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(8px, 1.4vw, 16px);
}

.ticker__track {
  padding-inline: var(--gutter);
}

.ticker__set {
  list-style: none;
}

.ticker.is-ticking .ticker__track,
.ticker.is-ticking .ticker__set {
  flex-wrap: nowrap;
  justify-content: flex-start;
}

/* main.js owns the transform — see the note there on why this isn't a keyframe
   animation. Grab affordance lives here; pan-y keeps a vertical swipe scrolling
   the page while a horizontal one scrubs the row. */
.ticker.is-ticking {
  cursor: grab;
  touch-action: pan-y;
}

.ticker.is-grabbed {
  cursor: grabbing;
}

.ticker.is-ticking .ticker__track {
  width: max-content;
  padding-inline: 0;
  user-select: none;
  -webkit-user-select: none;
  will-change: transform;
}

/* Otherwise a drag that crosses a logo selects the alt text, and the pointer
   turns into a text caret halfway through the gesture. */
.ticker.is-ticking .sponsor__link,
.ticker.is-ticking .sponsor__img {
  cursor: inherit;
  -webkit-user-drag: none;
}

/* Only meaningful once the row actually runs off both edges — on the static
   fallback it would just dim the outermost chips for no reason. */
.ticker.is-ticking {
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent,
    #000 7%,
    #000 93%,
    transparent
  );
  mask-image: linear-gradient(
    90deg,
    transparent,
    #000 7%,
    #000 93%,
    transparent
  );
}

/* Reduced motion is handled in main.js: it returns before setting .is-ticking,
   so the row never starts moving and stays the static wall above. Nothing to
   undo here. */

/* Logos are drawn for light backgrounds and each brand's colour is the point,
   so every mark sits on its own near-white chip and keeps its own palette. */
.sponsor__link {
  display: grid;
  place-items: center;
  width: 190px;
  height: 96px;
  padding: 0 24px;
  background: #fbfaf7;
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-sm);
  transition:
    transform 0.25s var(--ease),
    box-shadow 0.25s var(--ease);
}

.sponsor__link:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow);
}

.sponsor__img {
  /* Height is the constant and width follows each logo's own ratio — a row
     reads as level when the marks share a height, not a bounding box. */
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 32px;
}

/* A lockup spends much of its height on its symbol, so an equal height cap
   leaves it looking shrunken beside the pure wordmarks. */
.sponsor__img--lockup {
  max-height: 44px;
}

/* Fallback for a sponsor signed before their logo lands: set the name in the
   brand face at the weight of the marks around it, so the row reads as
   deliberate rather than half-loaded. Every current sponsor has a file, so
   nothing uses this today — it's kept because mid-season additions are normal.
   Usage is the same <a> minus the <img>. */
.sponsor__link--text {
  font-size: var(--t-lg);
  font-weight: 800;
  letter-spacing: -0.03em;
  color: var(--purple);
  text-align: center;
  text-decoration: none;
}

.squig--sponsors {
  top: -22px;
  left: 5%;
  width: 70px;
  transform: rotate(-8deg);
  opacity: 0.5;
}

/* The pitch under the row. The logos are the proof; this is the ask, so it runs
   in brand yellow at display size rather than as another body paragraph, with
   the CTA on the same line instead of stranded up in the section head. */
.sponsor-pitch {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px clamp(24px, 4vw, 48px);
  margin-top: clamp(30px, 4.5vw, 52px);
}

.sponsor-pitch__copy {
  flex: 1 1 30ch;
  max-width: 68ch;
  color: var(--yellow);
  font-size: clamp(19px, 2.1vw, 27px);
  font-weight: 500;
  line-height: 1.32;
  letter-spacing: -0.02em;
  text-wrap: pretty;
}

/* The turn into the joke. Real italics — the font request carries an 800
   italic so this isn't a synthesised slant, which a mono face wears badly. */
.sponsor-pitch__copy em {
  display: block;
  margin-top: 0.35em;
  font-style: italic;
  font-weight: 800;
}

.sponsor-pitch .pill {
  flex: 0 0 auto;
}

@media (max-width: 560px) {
  .sponsor__link {
    width: 148px;
    height: 78px;
    padding: 0 16px;
  }

  .sponsor__img {
    max-height: 26px;
  }

  .sponsor__img--lockup {
    max-height: 34px;
  }

  .sponsor__link--text {
    font-size: var(--t-md);
  }
}

/* ------------------------------------------------------------------- CTA -- */

.cta {
  padding: clamp(36px, 6vw, 72px);
  border-radius: var(--r-xl);
  background: var(--yellow);
  color: var(--purple);
  text-align: center;
}

.cta__title {
  font-size: clamp(26px, 4vw, 40px);
  font-weight: 900;
  line-height: 1.12;
  letter-spacing: -0.02em;
}

.cta__body {
  max-width: 54ch;
  margin: 14px auto 28px;
  text-wrap: pretty;
}

.cta .pill--solid {
  background: var(--purple);
  color: var(--yellow);
}

.cta .pill--ghost {
  border-color: var(--purple);
  color: var(--purple);
}

.cta .pill--ghost:hover {
  background: var(--purple);
  color: var(--yellow);
}

.cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
}

/* ---------------------------------------------------------------- footer -- */

.site-footer {
  padding-block: 56px 40px;
  background: var(--ink);
  border-top: 1px solid rgb(255 255 130 / 14%);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.site-footer__mark {
  display: flex;
  align-items: center;
  gap: 14px;
}

.site-footer__mark img {
  width: 46px;
}

.site-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
  font-size: var(--t-sm);
}

.site-footer__links a {
  color: var(--pale-cyan);
  text-decoration: none;
}

.site-footer__links a:hover {
  color: var(--yellow);
  text-decoration: underline;
}

.site-footer__legal {
  width: 100%;
  padding-top: 24px;
  margin-top: 8px;
  border-top: 1px solid rgb(255 255 255 / 10%);
  font-size: var(--t-xs);
  color: rgb(255 255 255 / 55%);
}

/* --------------------------------------------------------- reveal on scroll */

/* Content is visible by default and only hidden once main.js has confirmed it
   is running and will reveal it again. Gating it the other way round means any
   failure to execute the script — blocked, 404, file:// CORS — leaves half the
   page permanently invisible. */
.reveal-ready .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
}

.reveal-ready .reveal.is-visible {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .reveal-ready .reveal {
    opacity: 1;
    transform: none;
  }
}

