    /* Fonts (@font-face), the CSS reset, base typography (p), and link colours
       are all defined site-wide in style.css, which loads before this file.
       Only the homepage-specific html/body overrides below remain here:
       full-viewport width and overflow handling for the fixed gradient. */
    html, body {
      min-height: 100vh;
      /* 100% (not 100vw): 100vw includes the vertical scrollbar's width, which
         made the body a few px wider than the visible area — overflow-x:hidden
         then clipped that sliver off the right edge of everything (notably the
         full-bleed marquee's captions). 100% is the scrollbar-excluded width. */
      width: 100%;
      overflow-x: hidden;
    }
    /* Opt out of style.css's standard page scaffold: the homepage clears the
       fixed header via .stage padding, not body padding. `html body` outranks
       both the base scaffold and style.css's stacked-nav (≤720px) bump. */
    html body { padding-top: 0; padding-bottom: 0; }
    .stage {
      /* Cover Flow perspective lives on .coverflow itself (see below); the
         stage stacks the gallery, info, and about blocks in normal flow so it
         grows to contain them — the past-events marquee then always sits below
         the about instead of overlapping it on shorter viewports. */
      perspective-origin: 50% 50%;
      width: 100%;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      /* Constant top offset (matches the 11rem other pages clear the fixed
         header with) instead of a viewport-relative value, so the gallery
         doesn't drift down with extra blank space on taller screens. */
      padding-top: 11rem;
      position: relative;
    }
    /* Handwritten label pinned above a specific marquee card — scrolls with it. */
    .marquee-label {
      position: absolute;
      bottom: 100%;            /* sits above the card */
      left: 50%;
      transform: translateX(calc(-50% + var(--lx, 0px)));
      margin-bottom: 0.3rem;
      font-family: 'Caveat', 'Patrick Hand', cursive;
      font-weight: 400;
      font-size: 2rem;
      line-height: 1.1;
      text-align: center;
      color: var(--ink);
      pointer-events: none;
      white-space: nowrap;
      z-index: 5;
    }
    .marquee-label .label-arrow {
      display: block;
      margin: 0 auto 0.15rem;
      overflow: visible;
    }
    /* Filed away for later: the standalone above-section variant */
    .archive-wrap {
      position: relative;
      padding-top: 7rem;
    }
    .gallery-cap {
      position: absolute;
      top: var(--gy, 1rem);
      left: 50%;
      transform: translateX(calc(-50% + var(--gx, 0px)));
      font-family: 'Caveat', 'Patrick Hand', cursive;
      font-weight: 400;
      font-size: 2rem;
      line-height: 1.1;
      text-align: center;
      color: var(--ink);
      pointer-events: none;
      z-index: 10;
      white-space: nowrap;
    }
    .gallery-cap .cap-text { display: inline-block; }
    .gallery-cap .cap-arrow-down {
      display: block;
      margin: 0.15rem auto 0;
      overflow: visible;
    }

    .past-events {
      width: min(720px, 90vw);
      margin: 6rem auto 5rem;
    }
    .past-events-title {
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 2.4rem;
      text-transform: uppercase;
      letter-spacing: -0.01em;
      margin: 0 0 1.5rem;
      text-decoration: underline;
      text-decoration-thickness: 6px;
      text-underline-offset: 0.12em;
      color: var(--ink);
    }
    /* Year-grouped editorial layout for past events */
    .past-year { margin-bottom: 3.5rem; }
    .past-year:last-child { margin-bottom: 0; }
    .past-year-label {
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 3rem;
      line-height: 1;
      letter-spacing: -0.02em;
      text-transform: uppercase;
      margin: 0;
      padding: 0.6rem 0 0.4rem;
      border-top: 3px solid #000;
      color: var(--ink);
    }
    .past-year-list { display: block; }
    .past-row {
      display: grid;
      grid-template-columns: 140px 1fr auto;
      gap: 1.5rem;
      align-items: center;
      padding: 0.9rem 0;
      border-bottom: 1.5px solid #000;
      text-decoration: none;
      color: var(--ink);
    }
    .past-row-thumb {
      width: 140px;
      height: 140px;
      overflow: hidden;
      background: #eee;
    }
    .past-row-thumb img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
      transition: transform 220ms ease;
    }
    .past-row:hover .past-row-thumb img { transform: scale(1.08); }
    .past-row-title {
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 1.35rem;
      line-height: 1.2;
      letter-spacing: -0.01em;
      text-transform: uppercase;
      margin: 0;
    }
    .past-row:hover .past-row-title {
      text-decoration: underline;
      text-decoration-thickness: 3px;
      text-underline-offset: 0.15em;
    }
    .past-row-date {
      font-family: var(--font-mono);
      font-size: 0.85rem;
      letter-spacing: 0.04em;
      color: var(--ink-muted);
      text-transform: uppercase;
      text-align: right;
      white-space: nowrap;
    }
    @media (max-width: 600px) {
      .past-row { grid-template-columns: 100px 1fr; gap: 1rem; }
      .past-row-thumb { width: 100px; height: 100px; }
      .past-row-title { font-size: 1.1rem; grid-column: 2; }
      .past-row-date { grid-column: 2; text-align: left; font-size: 0.8rem; }
    }

    /* Year picker tabs above the list */
    .year-picker {
      display: flex;
      flex-wrap: wrap;
      gap: 0.5rem;
      margin: 0 0 1.5rem;
      border-bottom: 2px solid #000;
      padding-bottom: 0.75rem;
    }
    .year-tab {
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 1rem;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      padding: 0.5rem 0.9rem;
      background: #fff;
      color: var(--ink);
      border: 2px solid #000;
      cursor: pointer;
      transition: background 120ms ease, color 120ms ease;
    }
    .year-tab:hover { background: #eee; }
    .year-tab.is-active { background: #000; color: #fff; }
    .past-year[hidden] { display: none; }
    /* When filtering, hide the per-section year label (since the tab shows it) */
    .past-events.is-filtered .past-year-label { display: none; }
    .past-events.is-filtered .past-year { margin-bottom: 0; }

    /* Cute CSS UI: 3D-rotated auto-scrolling marquee of past events */
    .past-marquee {
      width: 100vw;
      margin: 5rem 0 4rem;
      margin-left: calc(50% - 50vw);
      overflow: hidden;
      position: relative;
      /* 8rem bottom: room for the shifted caps + handwritten descenders.
         perspective is set on each card via the transform function so the
         rotation pivot is always the card's own center — otherwise a single
         parent perspective makes the off-center cards look skewed. */
      padding: 1.5rem 0 8rem;
      cursor: grab;
      user-select: none;
    }
    .past-marquee-title {
      display: block;
      width: min(720px, 90vw);
      margin: 0.3rem auto 2rem;
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 2.8rem;
      line-height: 1;
      letter-spacing: -0.02em;
      text-transform: uppercase;
      color: var(--ink);
      text-align: left;
      text-decoration: none;
    }
    .past-marquee-title:hover { text-decoration: underline; text-decoration-thickness: 4px; text-underline-offset: 0.12em; }
    .past-marquee-divider {
      width: min(720px, 90vw);
      margin: 6rem auto 0;
      border: 0;
      border-top: 8px solid #000;
    }
    .past-marquee.is-dragging { cursor: grabbing; }
    .past-marquee.is-dragging .marquee-card img {
      transition: none;
    }
    .marquee-track {
      display: flex;
      gap: 0;
      width: max-content;
      padding: 10rem 0 2.5rem;
      transform-style: preserve-3d;
      will-change: transform;
    }
    .marquee-card {
      flex: 0 0 auto;
      width: 320px;
      height: 320px;
      margin-right: -176px;   /* matches the 200→320 scale-up (≈45% visible) */
      text-decoration: none;
      color: var(--ink);
      transform-style: preserve-3d;
      position: relative;
    }
    .marquee-card .cap {
      position: absolute;
      top: calc(100% + 40px + var(--cap-y, 1.5rem));
      left: 50%;
      transform: translateX(calc(-50% + var(--cap-x, 0px)));
      width: 320px;          /* same width as the image */
      text-align: center;
      font-family: 'Caveat', 'Patrick Hand', cursive;
      font-weight: 400;
      font-size: 2rem;
      line-height: 1.2;
      padding-bottom: 0.5rem; /* keep descenders within the .cap box */
      word-wrap: break-word;
      color: var(--ink);
      pointer-events: none;
    }
    .marquee-card .cap-arrow {
      position: absolute;
      bottom: 100%;          /* sits just above the cap text */
      left: 50%;
      transform: translateX(-50%);
      overflow: visible;
      pointer-events: none;
    }
    .marquee-card .cap-text {
      display: inline-block;
      transform: translateX(var(--shift, 0));
    }
    .marquee-card img {
      width: 320px;
      height: 320px;
      object-fit: cover;
      display: block;
      backface-visibility: hidden;
      transform-origin: center center;
      transform: perspective(2400px) rotateY(-55deg);
      transition: transform 0.45s cubic-bezier(0.22, 0.7, 0.2, 1);
      box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);
    }
    .marquee-card:hover {
      z-index: 5;
    }
    .marquee-card:hover img {
      transform: perspective(2400px) rotateY(-45deg) translateY(-14px) scale(1.04);
    }

    /* ===== Site footer =====
       Footer styles (incl. the stacked ≤700px layout) are defined site-wide in
       style.css, which loads before this file. Nothing homepage-specific. */

    /* ---- Cover Flow gallery -------------------------------------------
       The centered cover faces front; neighbours fan to both sides, angled
       toward the middle and receding in depth, with a floor reflection.
       Per-cover transforms are written as CSS vars by home.js. */
    .coverflow {
      position: relative;
      width: 100%;
      height: 340px;
      cursor: grab;
      user-select: none;
      -webkit-user-select: none;
      touch-action: pan-y;
    }
    .coverflow.is-dragging { cursor: grabbing; }
    .cf-track {
      position: absolute;
      left: 50%;
      top: 40px;
      width: 0;
      height: 0;
    }
    .cover {
      position: absolute;
      left: 0;
      top: 0;
      width: 300px;
      height: 300px;
      margin-left: -150px;          /* center the tile on the track origin */
      /* Perspective is baked into each cover's own transform rather than
         inherited from a `perspective` ancestor through `preserve-3d`. That
         inherited chain renders flat-squished intermittently on JS-inserted
         nodes (the old card stack hit the same bug and fixed it the same way).
         Depth ordering comes from z-index, not 3D, so no preserve-3d needed.
         Fallbacks keep the transform valid if a cover paints before JS runs. */
      transform: perspective(2200px)
                 translate3d(var(--x, 0px), 0, var(--z, 0px))
                 rotateY(var(--ry, 0deg)) scale(var(--s, 1));
      z-index: var(--zi, 1);
      cursor: pointer;
      will-change: transform;
    }
    /* Transition is enabled by JS only after the initial flat transforms have
       been applied to the freshly-inserted covers, so the first move animates
       cleanly instead of snapping from "none" on a fast refresh. */
    .coverflow.is-ready .cover {
      transition: transform 0.5s cubic-bezier(0.22, 0.72, 0.16, 1);
    }
    .cover img {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: cover;
      box-shadow: 0 16px 36px rgba(0, 0, 0, 0.26);
      pointer-events: none;
    }
    /* White wash on side covers so the centered one reads as focused —
       echoes the progressive tint of the old stack. */
    .cover::after {
      content: '';
      position: absolute;
      inset: 0;
      background: #fff;
      opacity: var(--tint, 0);
      transition: opacity 0.5s ease;
      pointer-events: none;
    }
    .cover.is-active { cursor: default; }
    /* Mirrored copy faded out beneath each cover — the Cover Flow floor. */
    .cover .reflection {
      position: absolute;
      left: 0;
      top: 100%;
      width: 100%;
      height: 100%;
      transform: scaleY(-1);
      transform-origin: top;
      opacity: 0.3;
      pointer-events: none;
      -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), transparent 42%);
              mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), transparent 42%);
    }
    .cover .reflection img { box-shadow: none; }
    .info {
      width: min(720px, 90vw);
      margin-top: 60px;        /* gap below the gallery (matches the old offset) */
      text-align: left;
      color: var(--ink);
      opacity: 0;
      transition: opacity 120ms ease;
    }
    /* nudged down from the old stack offset to clear the taller covers + reflection */
    .info.is-visible { opacity: 1; }

    .about {
      width: min(720px, 90vw);
      margin-top: 64px;        /* gap below the info block */
      text-align: left;
      color: var(--ink-soft);
    }
    .about hr {
      border: 0;
      border-top: 2px solid #000;
      margin: 0 0 1.25rem;
    }
    .about p { text-align: justify; }
    .about-body {
      display: grid;
      grid-template-columns: 1fr 2px 1fr;
      gap: 1.75rem;
    }
    .about-body .divider {
      background: #000;
    }
    /* The black CTA button itself (incl. hover invert) is style.css's shared
       .button-block rule, whose selector also covers .about a.signup-link. */
    .signup-link { margin-top: 0.5rem; }
    .socials {
      margin-top: 1rem;
      display: flex;
      flex-wrap: wrap;
      gap: 0.4rem 1.25rem;
      font-family: var(--font-mono);
      font-size: 0.95rem;
      letter-spacing: 0.02em;
    }
    .socials a {
      color: var(--ink);
      text-decoration: none;
      display: inline-flex;
      align-items: center;
      gap: 0.4rem;
    }
    /* Email row: label + obfuscated address anchor as one flex group, matching
       the label+handle spacing of the sibling <a> social links. */
    .socials .social-email {
      display: inline-flex;
      align-items: center;
      gap: 0.4rem;
    }
    .socials a:hover { text-decoration: underline; }
    .socials .label {
      font-weight: 700;
      text-transform: uppercase;
      font-size: 0.75rem;
      letter-spacing: 0.06em;
      color: var(--ink-muted);
    }
    /* .info .title is style.css's .page-title (see the div's classes);
       .desc uses the default p style. */
    /* Title + date are links to the event page (home.js); keep them looking
       like plain text — color already inherits via style.css's :where(a). */
    .info a { color: inherit; text-decoration: none; }
    .info .show-date {
      font-family: var(--font-mono);
      font-weight: 700;
      font-size: 1rem;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      color: var(--ink);
      margin-top: 0.9rem;
    }

    /* ===== Voxel logo =====
       The logo styles, hover letter-animation states, and header layout/dock
       behavior are all defined site-wide in style.css (loaded first). Nothing
       homepage-specific remains here. */

    /* ===== Mobile responsiveness ===== */
    @media (max-width: 800px) {
      /* Header layout (incl. mobile wrapping + dock) is defined site-wide in
         style.css; the body-padding opt-out at the top of this file already
         covers the stacked-nav bump. Only the stage/gallery is adjusted here. */

      /* Stage: flow vertically so info + about stack naturally after the gallery */
      .stage {
        flex-direction: column;
        align-items: center;
        padding-top: 14rem;
        height: auto;
      }

      /* Gallery: scale down so the spread fits narrow viewports */
      .coverflow {
        transform: scale(0.72);
        transform-origin: top center;
        height: 240px;
      }

      /* Info and about both flow normally below the stack — no more absolute overlap */
      .info, .about {
        position: static;
        transform: none;
        left: auto;
        top: auto;
        width: min(720px, 90vw);
        margin: 1.5rem auto 0;
      }
      /* More breathing room between the cover art and the selected show's title */
      .info { margin-top: 4rem; }
      .about { margin-top: 2.5rem; }
      .about-body {
        grid-template-columns: 1fr;
        gap: 1.5rem;
      }
      .about-body .divider {
        height: 2px;
        background: #000;
      }
      .info .title { font-size: 2.2rem; }
    }

    @media (max-width: 600px) {
      .past-marquee-title {
        font-size: 2rem;
        text-align: left;
      }
      /* Marquee: smaller cards + tighter overlap so labels fit */
      .marquee-card { width: 220px; height: 220px; margin-right: -120px; }
      .marquee-card img { width: 220px; height: 220px; }
      .marquee-card .cap { font-size: 1.5rem; width: 220px; }
      .marquee-label { font-size: 1.5rem; }
      /* Past Events heading shrinks slightly */
      .past-events { width: 90vw; }
    }

    /* ============================================================
       Background gradient square (ported from voxel.org.php)
       A blurred, slowly color-cycling square sits fixed behind all
       page content. Pure CSS — the palette / size / speed / end
       position arrive as custom properties on .gradient-container
       (see voxel_gradient_style() in the voxel-helpers plugin).
       ============================================================ */

    /* html keeps style.css's white base; body goes transparent so the fixed
       gradient (z-index:-1) shows through behind the content. */
    body { background: transparent; }

    /* Slow drift across the palette gradient; the color stops are laid out
       to ping-pong seamlessly (see voxel_gradient_css() in voxel-helpers). */
    @keyframes voxelBackgroundFade {
      from { background-position: 0%; }
      to   { background-position: var(--backgroundEndPosition); }
    }

    .gradient-container {
      /* Absolute (not fixed) so the glow scrolls with the page — staying put
         behind the show poster instead of pinned to the viewport. The square's
         top offset already matches the poster's position in the document. */
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      pointer-events: none;
      z-index: -1;
      /* Soft glow: the blur turns the hard-edged square into a halo. (filter
         also makes this the containing block for the absolute squares.) */
      filter: blur(50px);
    }
    /* Anchored behind the centered show poster. The active Cover Flow cover
       always lands at the same screen spot — horizontally centered, vertically
       at the stage's top padding + the track offset + the cover's half-height —
       so the glow stays put as you flip between shows. */
    .gradient-square {
      position: absolute;
      left: 50%;
      top: calc(11rem + 190px);   /* 11rem stage pad + 40px track top + 150px half-cover */
      transform: translate(-50%, -50%);
      width: 360px;
      height: 360px;
      background: var(--backgroundGradient);
      background-size: var(--backgroundSize);
      animation: voxelBackgroundFade var(--backgroundSpeed) linear infinite;
      /* Both layers sit stacked; the active one is shown and the incoming
         one fades in on top of it for a quick crossfade between shows. */
      opacity: 0;
      transition: opacity 400ms ease;
      will-change: opacity;
    }
    .gradient-square.is-active { opacity: 1; }
    @media (max-width: 800px) {
      /* Mobile: stage padding grows to 14rem and the coverflow scales to 0.72,
         so the poster center shifts and the glow shrinks to match. */
      .gradient-square {
        top: calc(14rem + 137px);
        width: 260px;
        height: 260px;
      }
    }

    /* Respect the OS "reduce motion" setting: freeze the continuously
       color-cycling background glow. The show-to-show crossfade (an opacity
       transition) and the hover transitions are brief and user-driven, so
       they're intentionally left intact. */
    @media (prefers-reduced-motion: reduce) {
      .gradient-square { animation: none; }
    }


