/* ==========================================================================
   OFF2 Entertainment — Homepage stylesheet
   Ported from the OFF2 Homepage design (Claude Design project).
   Every color/spacing value below matches the source design 1:1.
   Content that will move into a CMS later is marked with a
   `data-cms="section.field"` attribute in index.html — see /content/site-content.json.
   ========================================================================== */

:root {
  --bg: #000205;
  --card-bg: #05080B;
  --text: #C9D6E0;
  --text-dim: #7C93A8;
  --text-dimmer: #6E8CA6;
  --text-faint: #46617A;
  --white: #FFFFFF;
  --accent: #7EC8F5;
  --accent-deep: #1E7FD4;
  --btn-text: #001322;
  --border: rgba(126, 200, 245, 0.08);
  --border-soft: rgba(126, 200, 245, 0.1);
  --border-strong: rgba(126, 200, 245, 0.22);

  --font-display: 'Saira Condensed', sans-serif;
  --font-body: 'Barlow', sans-serif;

  --container: 1440px;
  --gutter: 48px;
}

/* ---------- Reset ---------- */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); }
body {
  -webkit-font-smoothing: antialiased;
  font-family: var(--font-body);
  color: var(--text);
  overflow-x: hidden;
  position: relative;
}
img { max-width: 100%; display: block; }
a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--white); }
::selection { background: var(--accent-deep); color: #fff; }
button, input { font-family: inherit; }

.wrap { position: relative; background: var(--bg); color: var(--text); font-family: var(--font-body); }

/* ---------- Motion ---------- */
@keyframes off2Twinkle { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
@keyframes off2Float {
  0% { transform: translate3d(0, 0, 0); }
  50% { transform: translate3d(-1.2%, -2.2%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}
@keyframes off2Meteor {
  0% { opacity: 0; transform: translate3d(0, 0, 0) rotate(var(--tilt)) scaleX(.3); }
  7% { opacity: 1; }
  64% { opacity: 1; }
  100% { opacity: 0; transform: translate3d(var(--dx), var(--dy), 0) rotate(var(--tilt)) scaleX(1); }
}
@keyframes off2Marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; animation-iteration-count: 1 !important; }
}

/* Scroll reveal. Elements rise, sharpen and settle; main.js staggers
   siblings so a row of cards arrives one after another rather than as a
   single block. --reveal-delay is set per element by main.js. */
[data-reveal] {
  opacity: 0;
  transform: translate3d(0, 32px, 0) scale(.99);
  filter: blur(5px);
  transition:
    opacity .85s cubic-bezier(.22, .61, .36, 1),
    transform .85s cubic-bezier(.22, .61, .36, 1),
    filter .85s cubic-bezier(.22, .61, .36, 1);
  transition-delay: var(--reveal-delay, 0s);
}
[data-reveal].is-visible { opacity: 1; transform: none; filter: none; }

/* Motion is decoration here — with reduced motion everything is simply
   present, with no transition to interrupt. */
@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal].is-visible {
    opacity: 1; transform: none; filter: none; transition: none;
  }
}

/* ---------- Nav ---------- */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  padding: 22px var(--gutter);
  backdrop-filter: blur(18px);
  background: linear-gradient(180deg, rgba(0, 2, 5, .82), rgba(0, 2, 5, .18));
  border-bottom: 1px solid var(--border);
}
/* Logo is the chrome OFF2 wordmark (images/off2-logo.png, ~3.8:1). Sized by
   height so the nav bar's rhythm stays fixed regardless of the artwork's
   width; display:block kills the inline-baseline gap under the image. */
.nav__logo { display: flex; align-items: center; flex-shrink: 0; }
.nav__logo img { display: block; height: 30px; width: auto; }
@media (max-width: 980px) { .nav__logo img { height: 26px; } }
.nav__links {
  display: flex; align-items: center; gap: 26px;
  font-family: var(--font-display); font-size: 15px; letter-spacing: .2em; text-transform: uppercase;
}
.nav__links a { color: var(--text-dim); }
.nav__links a:hover { color: var(--white); }
.nav__links a[aria-current="page"] { color: var(--white); }
.nav__cta {
  font-family: var(--font-display); font-size: 14px; letter-spacing: .24em; text-transform: uppercase;
  color: var(--btn-text);
  background: linear-gradient(120deg, var(--accent), var(--white) 55%, #9FD5F8);
  padding: 11px 22px; border: 0; cursor: pointer;
}
.nav__cta:hover { background: var(--white); color: var(--btn-text); }
.nav__toggle {
  display: none; width: 40px; height: 40px; border: 1px solid var(--border-strong);
  background: transparent; color: var(--white); cursor: pointer; align-items: center; justify-content: center;
}

@media (max-width: 980px) {
  .nav__links { position: fixed; inset: 74px 0 auto 0; flex-direction: column; align-items: flex-start;
    gap: 0; background: rgba(0, 2, 5, .97); border-bottom: 1px solid var(--border);
    padding: 8px var(--gutter) 20px; transform: translateY(-8px); opacity: 0; pointer-events: none;
    transition: opacity .25s ease, transform .25s ease; }
  .nav__links a { padding: 12px 0; width: 100%; border-bottom: 1px solid rgba(126,200,245,.06); }
  .nav.is-open .nav__links { opacity: 1; transform: none; pointer-events: auto; }
  .nav__toggle { display: flex; }
  .nav__cta { display: none; }
}

/* ---------- Sky background ---------- */
/* z-index 0, not 1: content sections are position:relative with z-index auto,
   so at z-index 1 the starfield and meteors painted OVER the posters and
   cards. At 0 the sky shares the same layer as those sections and loses on
   tree order (it appears first in the DOM), which puts it behind all content
   without needing a z-index on every section. Negative values are wrong here
   — they'd drop it behind .wrap's background and hide it entirely. */
.sky {
  position: fixed; inset: -10%; z-index: 0; pointer-events: none; overflow: hidden;
}
/* The copy main.js clones into .page-flow. Sticky rather than fixed so it
   stays inside the flow and never paints over the hero at the top of the
   page; z-index -1 puts it above .page-flow's opaque background but behind
   every section, which are all position: relative. The negative margin keeps
   it out of the layout. */
.sky--flow {
  position: sticky; top: -10vh; left: 0; right: 0; bottom: auto;
  height: 120vh; margin-bottom: -120vh; z-index: -1;
}
/* 300% tall was ~30x the viewport per layer, and with eight layers animating
   that is a lot of pixels to repaint every frame. off2Float only travels
   ~2%, so 140% is all the headroom the drift needs. */
.sky__layer {
  position: absolute; left: 0; right: 0; top: -6%; height: 112%;
  animation: off2Twinkle 7s ease-in-out infinite, off2Float 46s ease-in-out infinite;
  will-change: transform;
}
.meteor-field { position: absolute; left: 0; right: 0; top: -20%; height: 140%; overflow: hidden; will-change: translate; }
.meteor {
  position: absolute; width: 160px; height: 1.5px; border-radius: 2px; transform-origin: 100% 50%;
  background: linear-gradient(270deg, rgba(255,255,255,.9) 0%, rgba(126,200,245,.5) 26%, rgba(126,200,245,0) 100%);
  box-shadow: 0 0 10px rgba(126,200,245,.8), 0 0 26px rgba(30,127,212,.45);
  animation: off2Meteor 18s cubic-bezier(.3,0,.5,1) infinite;
  opacity: 0; will-change: transform, opacity;
}

/* ---------- Section shell ---------- */
section { position: relative; }
.section { padding: 130px var(--gutter); border-bottom: 1px solid var(--border); }
.section-inner { max-width: var(--container); margin: 0 auto; }
.eyebrow {
  font-family: var(--font-display); font-size: 14px; letter-spacing: .36em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 20px;
}
.section-head {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 40px; margin-bottom: 56px;
}
.section-head h2 {
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 76px; line-height: .94;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white);
}
.section-head__note { max-width: 320px; margin: 14px 0 0; font-size: 15px; line-height: 1.7; color: var(--text-dim); }
.link-more {
  font-family: var(--font-display); font-size: 15px; letter-spacing: .26em; text-transform: uppercase;
  color: var(--text-dim); border-bottom: 1px solid var(--border-strong); padding-bottom: 6px; white-space: nowrap;
}
.link-more:hover { color: var(--white); border-color: var(--accent); }

/* ---------- Hero ---------- */
/* Height comes from the artwork's own aspect ratio (both hero banners are
   exactly 16:9) rather than the viewport. A 100vh box letterboxed the
   contained image — up to ~45px of dead space at 16:10 — which detached the
   beam divider below from the bottom of the artwork. Matching the ratio means
   the image fills the box edge to edge and the beam sits flush against it. */
/* ---------- Pinned hero (homepage) ----------
   .hero-stack sticks to the top of the viewport and .page-flow, which is
   opaque, scrolls up over it. The stack keeps the lower z-index so it is
   covered rather than overlapping. */
.hero-stack { position: sticky; top: 0; z-index: 1; }
.page-flow { position: relative; z-index: 2; background: var(--bg); }

.hero {
  position: relative; z-index: 2; aspect-ratio: 16 / 9; overflow: hidden; background: transparent;
}
.hero__slide {
  position: absolute; inset: 0; opacity: 0; transition: opacity 1.1s cubic-bezier(.4, 0, .2, 1);
}
.hero__slide.is-active { opacity: 1; }
.hero__media { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.hero__media img { width: 100%; height: 100%; object-fit: contain; }
/* Centre-composed banners (Dreamstate) run their line-up down the middle of
   the frame, leaving only ~500px of clear art on the left. At the standard
   116px the title reaches into the line-up, so it steps down to fit that
   gutter. These banners are also bright, so a fade behind the caption keeps
   white text legible; it runs out before the line-up starts. Desktop only —
   the caption is hidden on mobile anyway. */
@media (min-width: 721px) {
  .hero__slide--centred .hero__title { font-size: 92px; }
  .hero__slide--centred .hero__media::after {
    content: ''; position: absolute; top: 0; bottom: 0; left: 0; width: 38%;
    background: linear-gradient(90deg, rgba(0, 2, 5, .78), rgba(0, 2, 5, .42) 55%, rgba(0, 2, 5, 0));
    pointer-events: none;
  }
}
.hero__caption { position: absolute; left: var(--gutter); bottom: 156px; max-width: 640px; pointer-events: none; }
.hero__kicker {
  font-family: var(--font-display); font-size: 15px; letter-spacing: .34em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 18px;
}
.hero__title {
  font-family: var(--font-display); font-weight: 700; font-size: 116px; line-height: .88; letter-spacing: .02em;
  text-transform: uppercase; color: var(--white);
}
.hero__meta {
  font-family: var(--font-display); font-weight: 300; font-size: 22px; letter-spacing: .26em; text-transform: uppercase;
  color: var(--text-dim); margin-top: 20px;
}
.hero__controls {
  position: absolute; left: var(--gutter); right: var(--gutter); bottom: 34px;
  display: flex; align-items: center; justify-content: space-between; gap: 40px;
}
/* Fixed-width ticks rather than flex-stretched bars: two slides sharing a
   560px track rendered as two ~275px lines, which read as rules across the
   artwork instead of a progress indicator. */
.hero__dots { display: flex; gap: 8px; flex: 0 0 auto; }
.hero__dot { width: 40px; height: 2px; border: 0; padding: 0; background: rgba(255,255,255,.28); cursor: pointer; transition: background .3s ease; }
.hero__dot.is-active { background: rgba(255,255,255,.9); }
.hero__nav { display: flex; align-items: center; gap: 26px; }
.hero__counter { font-family: var(--font-display); font-size: 15px; letter-spacing: .26em; color: var(--text-dim); }
.hero__arrow {
  width: 46px; height: 46px; border: 1px solid var(--border-strong); background: transparent;
  color: var(--text); font-size: 15px; cursor: pointer;
}
.hero__arrow:hover { border-color: var(--accent); color: var(--white); }

@media (max-width: 720px) {
  .hero__title { font-size: 58px; }
  /* Below this width .hero__media swaps to the portrait poster (see the
     <picture><source media> in each .hero__media in index.html), which
     already has the title/date/lineup baked into the artwork — the text
     overlay below would just duplicate it and collide visually, so hide it. */
  .hero__caption { display: none; }
  /* Portrait posters, so the box switches ratio with them. The two posters
     differ slightly (0.732 vs 0.75), so `cover` absorbs the ~2% difference
     instead of letterboxing one of them and re-opening the gap under the
     beam; the crop is small enough not to touch the baked-in artwork. */
  .hero { aspect-ratio: 3 / 4; }
  /* Scoped through .img-slot on purpose: the generic `.img-slot img` rule
     further down sets object-fit:contain at the same specificity and would
     otherwise win on source order. */
  .hero__media .img-slot img { object-fit: cover; }
  /* The portrait poster carries its own venue/ticket lines at the bottom, and
     the carousel controls were sitting straight on top of them — two sets of
     text in the same place. Give the hero a little extra height and stop the
     artwork short of it, so the controls get their own strip underneath
     instead of competing with the poster. Ratio rather than a vw calc so the
     band scales with the screen. */
  .hero { aspect-ratio: 300 / 444; }
  .hero__media { bottom: 56px; }
  .hero__controls { left: var(--gutter); right: var(--gutter); bottom: 14px; gap: 16px; }
  .hero__dot { width: 32px; }
  .hero__arrow { width: 40px; height: 40px; }
  .hero__nav { gap: 16px; }
}

.hr-beam {
  position: relative; height: 1px;
  background: linear-gradient(90deg, rgba(126,200,245,0) 0%, var(--accent) 22%, var(--white) 50%, var(--accent) 78%, rgba(126,200,245,0) 100%);
  box-shadow: 0 0 14px rgba(126,200,245,.85), 0 0 46px rgba(30,127,212,.55);
}

/* ---------- Intro / mission ---------- */
.intro { padding: 150px var(--gutter) 130px; border-bottom: 1px solid var(--border); overflow: hidden; }
.intro::before {
  content: ''; position: absolute; width: 900px; height: 900px; left: 50%; top: -420px; transform: translateX(-50%);
  background: radial-gradient(circle, rgba(30,127,212,.18), transparent 62%); filter: blur(40px); pointer-events: none;
}
.intro-inner { position: relative; max-width: 1180px; margin: 0 auto; text-align: center; }
.intro-inner .eyebrow { margin-bottom: 34px; }
.intro-inner h1 {
  margin: 0; font-family: var(--font-display); font-weight: 500; font-size: 62px; line-height: 1.06;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white); text-wrap: balance;
}
.intro-inner p {
  margin: 40px auto 0; max-width: 620px; font-size: 17px; line-height: 1.75; color: var(--text-dim); text-wrap: pretty;
}
@media (max-width: 720px) { .intro-inner h1 { font-size: 38px; } }

/* ---------- Cards (festivals / events) ---------- */
.card-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px; }
.card {
  display: flex; flex-direction: column; gap: 22px; color: inherit;
  transition: transform .6s cubic-bezier(.2, 0, .2, 1);
}
.card:hover { transform: translateY(-8px); color: inherit; }
.card__media {
  position: relative; aspect-ratio: 3 / 4; background: var(--card-bg); border: 1px solid var(--border-soft);
  overflow: hidden;
}
.card__name { font-family: var(--font-display); font-weight: 700; font-size: 30px; letter-spacing: .06em; text-transform: uppercase; color: var(--white); }
.card__date { font-family: var(--font-display); font-size: 14px; letter-spacing: .26em; text-transform: uppercase; color: var(--accent); }
.card__desc { font-size: 15px; line-height: 1.6; color: var(--text-dim); }
.card__body { display: flex; flex-direction: column; gap: 10px; }
.card__row { display: flex; justify-content: space-between; gap: 14px; font-family: var(--font-display); font-size: 14px; letter-spacing: .2em; text-transform: uppercase; color: var(--text-dim); }
.card__tickets { margin-top: 4px; font-family: var(--font-display); font-size: 14px; letter-spacing: .22em; text-transform: uppercase; color: var(--white); }

@media (max-width: 1100px) { .card-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .card-grid { grid-template-columns: 1fr; } }

.rail {
  display: flex; gap: 24px; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 18px; scrollbar-width: thin;
}
.rail .card { flex: 0 0 316px; scroll-snap-align: start; }
/* Event posters are all 4:5 (not the 3:4 used by festival posters) — match the
   frame to that so `object-fit: contain` fills it edge-to-edge with no
   letterboxing. Every rail card shares the same fixed width above, so a
   shared aspect-ratio here keeps every frame identically sized too. */
.rail .card__media { aspect-ratio: 4 / 5; }

/* ---------- Gallery ---------- */
.gallery-grid {
  display: grid; grid-template-columns: 1.6fr 1fr 1fr; grid-template-rows: 260px 260px; gap: 18px;
}
.gallery-grid .g1 { grid-row: 1 / span 2; }
.gallery-grid .g4 { grid-column: 2 / span 2; }
.gallery-grid > div { position: relative; overflow: hidden; border: 1px solid var(--border-soft); }
/* The teaser tiles are fixed-height panels of varying ratio, so photos fill
   and crop rather than letterboxing the way .img-slot img does. */
.gallery-grid img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .7s cubic-bezier(.2, 0, .2, 1);
}
.gallery-grid > div:hover img { transform: scale(1.04); }
@media (max-width: 860px) {
  .gallery-grid { grid-template-columns: 1fr 1fr; grid-template-rows: repeat(3, 220px); }
  .gallery-grid .g1 { grid-row: auto; grid-column: 1 / span 2; }
  .gallery-grid .g4 { grid-column: 1 / span 2; }
}

/* ---------- About ---------- */
.about { padding: 140px var(--gutter); overflow: hidden; border-bottom: 1px solid var(--border); }
.about::before {
  content: ''; position: absolute; width: 1100px; height: 700px; right: -280px; top: 40px;
  background: radial-gradient(circle, rgba(18,58,95,.5), transparent 62%); filter: blur(50px); pointer-events: none;
}
.about-inner {
  position: relative; max-width: var(--container); margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr;
  gap: 90px; align-items: center;
}
.about h2 {
  margin: 0 0 28px; font-family: var(--font-display); font-weight: 700; font-size: 66px; line-height: .98;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white);
}
.about p { margin: 0 0 34px; max-width: 500px; font-size: 17px; line-height: 1.75; color: var(--text-dim); text-wrap: pretty; }
.about-media {
  position: relative; aspect-ratio: 4 / 5; border: 1px solid var(--border-strong); overflow: hidden;
}
@media (max-width: 900px) {
  .about-inner { grid-template-columns: 1fr; gap: 50px; }
  .about h2 { font-size: 44px; }
}

/* ---------- Newsletter ---------- */
.newsletter { padding: 120px var(--gutter); overflow: hidden; }
.newsletter-inner { max-width: 900px; margin: 0 auto; text-align: center; }
.newsletter h2 {
  margin: 0 0 20px; font-family: var(--font-display); font-weight: 500; font-size: 54px; line-height: 1.04;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white);
}
.newsletter p { margin: 0 0 40px; font-size: 16px; line-height: 1.7; color: var(--text-dim); }
.newsletter-form { display: flex; gap: 12px; max-width: 540px; margin: 0 auto; }
.newsletter-form input {
  flex: 1; background: rgba(126,200,245,.05); border: 1px solid var(--border-strong); color: var(--white);
  font-size: 16px; padding: 15px 18px; outline: none;
}
.newsletter-form input:focus { border-color: var(--accent); }
.newsletter-form button {
  font-family: var(--font-display); font-size: 15px; letter-spacing: .24em; text-transform: uppercase;
  color: var(--btn-text); background: linear-gradient(120deg, var(--accent), var(--white) 55%, #9FD5F8);
  border: 0; padding: 15px 30px; cursor: pointer;
}
.newsletter-form button:hover { background: var(--white); }
@media (max-width: 560px) {
  .newsletter h2 { font-size: 34px; }
  .newsletter-form { flex-direction: column; }
}

/* ---------- Footer ---------- */
.site-footer { padding: 80px var(--gutter) 44px; }
.footer-inner { max-width: var(--container); margin: 0 auto; }
.footer-grid {
  display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 50px; padding-bottom: 60px; border-bottom: 1px solid var(--border-soft);
}
.footer-brand img { display: block; height: 42px; width: auto; }
.footer-address { margin-top: 16px; font-size: 15px; line-height: 1.7; color: var(--text-dimmer); max-width: 300px; }
.footer-col { display: flex; flex-direction: column; gap: 14px; font-family: var(--font-display); font-size: 14px; letter-spacing: .24em; text-transform: uppercase; }
.footer-col__head { color: var(--accent); }
.footer-col a { color: var(--text-dim); }
.footer-col a:hover { color: var(--white); }
.footer-bottom {
  display: flex; justify-content: space-between; padding-top: 26px; font-family: var(--font-display); font-size: 13px;
  letter-spacing: .24em; text-transform: uppercase; color: var(--text-faint); flex-wrap: wrap; gap: 10px;
}
@media (max-width: 860px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 520px) { .footer-grid { grid-template-columns: 1fr; } }

/* ==========================================================================
   Image slot — replaces the design tool's <image-slot> web component.
   Empty state mirrors its placeholder look (dashed ring + caption) so the
   site reads as finished today; drop a real file in /images and point the
   <img> at it (or wire a CMS field) to fill any slot later.
   ========================================================================== */
.img-slot {
  position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; text-align: center; padding: 20px; color: inherit; background: rgba(127,127,127,.06);
}
.img-slot::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border: 1.5px dashed currentColor; opacity: .28;
}
.img-slot svg { opacity: .45; flex-shrink: 0; }
.img-slot span {
  max-width: 90%; font-size: 12px; line-height: 1.5; letter-spacing: .02em; color: var(--text-dim); opacity: .85;
}
.img-slot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
.img-slot.is-filled::after { display: none; }
.img-slot.is-filled svg, .img-slot.is-filled span { display: none; }

/* ==========================================================================
   Festivals index page (festivals.html) — overview grid linking out to each
   festival's own page (not built yet, per the brief — CTAs go to
   coming-soon.html?title=<name> like every other not-yet-built page on
   the site).
   ========================================================================== */
.page-header { padding: 200px var(--gutter) 90px; overflow: hidden; }
.page-header-inner { position: relative; max-width: 900px; margin: 0 auto; text-align: center; }
.page-header-inner h1 {
  margin: 0 0 22px; font-family: var(--font-display); font-weight: 500; font-size: 58px; line-height: 1.05;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white); text-wrap: balance;
}
.page-header-inner p { margin: 0; font-size: 17px; line-height: 1.75; color: var(--text-dim); text-wrap: pretty; }
@media (max-width: 720px) { .page-header { padding: 150px 24px 70px; } .page-header-inner h1 { font-size: 36px; } }

.festival-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 44px 32px; }
@media (max-width: 860px) { .festival-grid { grid-template-columns: 1fr; gap: 40px; } }

/* Staggered reveal — each entry fades in slightly after the last. */
.festival-grid [data-reveal]:nth-child(2) { transition-delay: .08s; }
.festival-grid [data-reveal]:nth-child(3) { transition-delay: .16s; }
.festival-grid [data-reveal]:nth-child(4) { transition-delay: .24s; }

.festival-entry {
  display: flex; flex-direction: column; gap: 24px; color: inherit;
  transition: transform .5s cubic-bezier(.2, 0, .2, 1);
}
.festival-entry:hover { transform: translateY(-6px); color: inherit; }
.festival-entry__media {
  position: relative; aspect-ratio: 3 / 4; background: var(--card-bg); overflow: hidden;
  border: 1px solid var(--border-soft); transition: border-color .4s ease, box-shadow .5s ease;
}
/* Each entry's --mood custom property (set inline per festival) tints its own
   card on hover — a restrained per-festival accent inside one shared grid,
   not a full re-theme. */
.festival-entry:hover .festival-entry__media {
  border-color: var(--mood, var(--accent));
  box-shadow: 0 0 0 1px var(--mood, var(--accent)), 0 30px 70px -30px var(--mood, var(--accent));
}
.festival-entry__media img { transition: transform .8s cubic-bezier(.2, 0, .2, 1); }
.festival-entry:hover .festival-entry__media img { transform: scale(1.035); }
.festival-entry__body { display: flex; flex-direction: column; gap: 10px; }
.festival-entry__mood {
  width: 30px; height: 3px; border-radius: 2px; background: var(--mood, var(--accent)); margin-bottom: 4px;
}
.festival-entry__date {
  font-family: var(--font-display); font-size: 14px; letter-spacing: .26em; text-transform: uppercase; color: var(--mood, var(--accent));
}
.festival-entry__name {
  margin: 0; font-family: var(--font-display); font-weight: 700; font-size: 40px; line-height: 1; letter-spacing: .04em;
  text-transform: uppercase; color: var(--white);
}
.festival-entry__tagline { margin: 0; max-width: 46ch; font-size: 16px; line-height: 1.6; color: var(--text-dim); }
.festival-entry__cta {
  margin-top: 6px; font-family: var(--font-display); font-size: 14px; letter-spacing: .24em; text-transform: uppercase; color: var(--white);
}
@media (max-width: 720px) { .festival-entry__name { font-size: 32px; } }

/* ==========================================================================
   Events page (events.html) — venue-filterable grid, expanded from the
   homepage's Harbour-only teaser rail to cover every OFF2 venue.
   ========================================================================== */
.venue-filter {
  display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 44px;
}
.venue-filter button {
  appearance: none; cursor: pointer; background: transparent; border: 1px solid var(--border-strong);
  color: var(--text-dim); font-family: var(--font-display); font-size: 13px; letter-spacing: .2em; text-transform: uppercase;
  padding: 10px 20px; transition: border-color .3s ease, color .3s ease, background .3s ease;
}
.venue-filter button:hover { color: var(--white); border-color: var(--accent); }
.venue-filter button.is-active { color: var(--btn-text); background: var(--accent); border-color: var(--accent); }

.event-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px 28px; }
@media (max-width: 1100px) { .event-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px) { .event-grid { grid-template-columns: 1fr; } }

.event-grid .card__media { aspect-ratio: 4 / 5; }
.event-item { transition: opacity .25s ease, transform .25s ease; }
.event-item.is-filtered-out { opacity: 0; transform: translateY(10px); pointer-events: none; }
.card__venue {
  font-family: var(--font-display); font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: var(--text-dimmer);
}
.card__desc.card__desc--event { font-size: 14px; }

.venue-empty {
  display: none; padding: 60px 0; text-align: center; color: var(--text-dim); font-size: 15px;
}
.venue-empty.is-visible { display: block; }

/* ==========================================================================
   Venues page (venues.html) — one stacked section per owned venue, each with
   its own image strip, copy, location and booking CTA. Stacked (not
   side-by-side) so each venue's gallery strip gets full width to breathe.
   ========================================================================== */
.venue-section { padding: 110px var(--gutter); border-bottom: 1px solid var(--border); }
.venue-section:last-of-type { border-bottom: 0; }
.venue-section-inner { max-width: var(--container); margin: 0 auto; }

.venue-head { max-width: 720px; margin-bottom: 42px; }
.venue-head h2 {
  margin: 0 0 20px; font-family: var(--font-display); font-weight: 700; font-size: 60px; line-height: .98;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white);
}
.venue-head p { margin: 0 0 18px; font-size: 17px; line-height: 1.75; color: var(--text-dim); text-wrap: pretty; }
.venue-head p:last-child { margin-bottom: 0; }
@media (max-width: 720px) { .venue-head h2 { font-size: 38px; } }

/* Image strip — a horizontal row of equal frames that becomes a swipeable
   scroller on narrow screens rather than shrinking to unreadable slivers. */
.venue-gallery {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 42px;
}
.venue-gallery__item {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden; border: 1px solid var(--border-soft);
  transition: border-color .4s ease, transform .5s cubic-bezier(.2, 0, .2, 1);
}
.venue-gallery__item:hover { border-color: var(--accent); transform: translateY(-4px); }
/* 4:3 tiles holding 3:2 photos — fill and crop. The generic .img-slot img
   rule uses contain, which would letterbox them. */
.venue-gallery__item img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .8s cubic-bezier(.2, 0, .2, 1);
}
.venue-gallery__item:hover img { transform: scale(1.04); }
@media (max-width: 860px) {
  .venue-gallery {
    display: flex; overflow-x: auto; scroll-snap-type: x mandatory; padding-bottom: 14px; scrollbar-width: thin;
  }
  .venue-gallery__item { flex: 0 0 72%; scroll-snap-align: start; }
}

.venue-meta {
  display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 28px;
}
.venue-location__label {
  font-family: var(--font-display); font-size: 13px; letter-spacing: .28em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 10px;
}
.venue-location__address { font-size: 16px; line-height: 1.7; color: var(--text-dim); }
.venue-location__map {
  display: inline-block; margin-top: 8px; font-family: var(--font-display); font-size: 13px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--text-dim); border-bottom: 1px solid var(--border-strong); padding-bottom: 4px;
}
.venue-location__map:hover { color: var(--white); border-color: var(--accent); }

.btn-book {
  display: inline-block; font-family: var(--font-display); font-size: 14px; letter-spacing: .24em;
  text-transform: uppercase; color: var(--btn-text);
  background: linear-gradient(120deg, var(--accent), var(--white) 55%, #9FD5F8);
  border: 0; padding: 16px 34px; cursor: pointer; white-space: nowrap;
  transition: transform .3s ease, box-shadow .4s ease, background .3s ease;
}
.btn-book:hover {
  background: var(--white); color: var(--btn-text);
  transform: translateY(-2px); box-shadow: 0 14px 40px -14px rgba(126, 200, 245, .8);
}

/* ==========================================================================
   Careers page (careers.html)
   ========================================================================== */
.culture-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px; max-width: var(--container); margin: 0 auto;
}
@media (max-width: 860px) { .culture-grid { grid-template-columns: 1fr; gap: 32px; } }
.culture-item__label {
  font-family: var(--font-display); font-size: 13px; letter-spacing: .28em; text-transform: uppercase;
  color: var(--accent); margin-bottom: 14px;
}
.culture-item p { margin: 0; font-size: 16px; line-height: 1.75; color: var(--text-dim); text-wrap: pretty; }

/* ---------- Demo submission form (label.html) ----------
   Stacked variant of the .newsletter-form treatment: same border, focus
   colour and button, laid out vertically with labels. */
.demo-form { max-width: 560px; margin: 0 auto; }
.demo-form__field { margin-bottom: 22px; }
.demo-form__field label {
  display: block; font-family: var(--font-display); font-size: 13px;
  letter-spacing: .24em; text-transform: uppercase; color: var(--accent);
  margin-bottom: 10px;
}
.demo-form__field label span {
  letter-spacing: .12em; color: var(--text-dimmer); text-transform: none;
}
.demo-form input,
.demo-form textarea {
  width: 100%; box-sizing: border-box; padding: 14px 16px;
  background: rgba(255, 255, 255, .03); border: 1px solid var(--border-strong);
  color: var(--white); font-family: var(--font-body); font-size: 15px;
  transition: border-color .3s ease;
}
.demo-form textarea { resize: vertical; min-height: 110px; }
.demo-form input:focus,
.demo-form textarea:focus { outline: none; border-color: var(--accent); }
.demo-form input:disabled,
.demo-form textarea:disabled { opacity: .45; cursor: not-allowed; }
.demo-form input[aria-invalid="true"],
.demo-form textarea[aria-invalid="true"] { border-color: #E0554A; }

.demo-form__help,
.demo-form__legal {
  font-size: 13px; line-height: 1.65; color: var(--text-dimmer); margin: 10px 0 0;
}
.demo-form__legal { margin-top: 26px; }

/* Shown while the endpoint is not configured. */
.demo-form__notice {
  margin: 0 0 30px; padding: 14px 16px; font-size: 14px; line-height: 1.6;
  color: var(--accent); border: 1px solid var(--border-strong);
  background: rgba(126, 200, 245, .06);
}

.demo-form__status { margin: 18px 0 0; font-size: 14px; line-height: 1.6; }
.demo-form__status.is-error { color: #E0554A; }
.demo-form__status.is-success { color: var(--accent); }

.demo-form .btn-book { width: 100%; text-align: center; }
.demo-form .btn-book:disabled { opacity: .45; cursor: not-allowed; }

/* Off-screen rather than display:none — bots skip hidden inputs. */
.demo-form__trap {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* Job listing rows. Kept as a flat list (not cards) so a long roster stays
   scannable; the whole row is the link target. */
.job-list { border-top: 1px solid var(--border-soft); }
.job-row {
  display: grid; grid-template-columns: 2fr 1fr 1fr auto; align-items: center; gap: 24px;
  padding: 30px 20px; border-bottom: 1px solid var(--border-soft); color: inherit;
  transition: background .35s ease, padding-left .35s ease;
}
.job-row:hover { background: rgba(126, 200, 245, .05); padding-left: 30px; color: inherit; }
.job-row__title {
  font-family: var(--font-display); font-weight: 700; font-size: 24px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--white);
}
.job-row__meta {
  font-family: var(--font-display); font-size: 13px; letter-spacing: .2em; text-transform: uppercase; color: var(--text-dim);
}
.job-row__cta {
  font-family: var(--font-display); font-size: 13px; letter-spacing: .22em; text-transform: uppercase;
  color: var(--accent); white-space: nowrap;
}
.job-row__cta span { display: inline-block; transition: transform .35s ease; }
.job-row:hover .job-row__cta span { transform: translateX(5px); }
@media (max-width: 720px) {
  .job-row { grid-template-columns: 1fr; gap: 8px; padding: 24px 0; }
  .job-row:hover { padding-left: 10px; }
  .job-row__title { font-size: 20px; }
}

/* Open-application block — shown when there are no live postings. Never
   fabricate roles: an invented listing sends real people through a real
   application flow for a job that does not exist. */
.open-application {
  max-width: 760px; margin: 0 auto; text-align: center;
  border: 1px solid var(--border-soft); padding: 70px 40px;
  background: linear-gradient(180deg, rgba(126, 200, 245, .04), transparent);
}
.open-application h2 {
  margin: 0 0 20px; font-family: var(--font-display); font-weight: 500; font-size: 42px; line-height: 1.06;
  letter-spacing: .02em; text-transform: uppercase; color: var(--white); text-wrap: balance;
}
.open-application p { margin: 0 auto 34px; max-width: 520px; font-size: 16px; line-height: 1.75; color: var(--text-dim); }
.open-application__contact {
  display: block; margin-top: 22px; font-family: var(--font-display); font-size: 13px; letter-spacing: .22em;
  text-transform: uppercase; color: var(--text-dim);
}
@media (max-width: 720px) {
  .open-application { padding: 50px 24px; }
  .open-application h2 { font-size: 30px; }
}

/* ==========================================================================
   Gallery page (gallery.html) — photos from past events, filterable by show.
   ========================================================================== */
.gallery-page-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; grid-auto-flow: dense;
}
@media (max-width: 1100px) { .gallery-page-grid { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 780px) { .gallery-page-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 480px) { .gallery-page-grid { grid-template-columns: 1fr; } }

.gallery-tile {
  position: relative; display: block; overflow: hidden; border: 1px solid var(--border-soft);
  background: var(--card-bg); aspect-ratio: 3 / 2; cursor: pointer; padding: 0; width: 100%;
  color: inherit; text-align: left;
  transition: opacity .25s ease, transform .25s ease, border-color .4s ease;
}
/* A couple of deliberately larger tiles break up the even grid without
   needing a masonry library. Only applied where there's room for them. */
@media (min-width: 781px) {
  .gallery-tile--wide { grid-column: span 2; aspect-ratio: 3 / 1.32; }
  .gallery-tile--tall { grid-row: span 2; aspect-ratio: 3 / 4.2; }
}
.gallery-tile:hover { border-color: var(--accent); }
.gallery-tile img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .8s cubic-bezier(.2, 0, .2, 1); }
.gallery-tile:hover img { transform: scale(1.05); }
.gallery-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.gallery-tile__caption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 14px 16px;
  background: linear-gradient(180deg, transparent, rgba(0, 2, 5, .9));
  font-family: var(--font-display); font-size: 12px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text); opacity: 0; transform: translateY(6px);
  transition: opacity .35s ease, transform .35s ease;
}
.gallery-tile:hover .gallery-tile__caption,
.gallery-tile:focus-visible .gallery-tile__caption { opacity: 1; transform: none; }

/* Placeholder tiles reuse .img-slot, which is absolutely positioned. */
.gallery-tile .img-slot { cursor: pointer; }

/* ---------- Lightbox ---------- */
.lightbox {
  position: fixed; inset: 0; z-index: 100; display: none; align-items: center; justify-content: center;
  padding: 40px; background: rgba(0, 2, 5, .94); backdrop-filter: blur(6px);
}
.lightbox.is-open { display: flex; animation: off2FadeIn .25s ease; }
@keyframes off2FadeIn { from { opacity: 0; } to { opacity: 1; } }
.lightbox__inner { position: relative; max-width: 1100px; width: 100%; }
.lightbox__figure {
  display: flex; align-items: center; justify-content: center; max-height: 78vh; overflow: hidden;
  border: 1px solid var(--border-soft);
}
.lightbox__figure img { max-width: 100%; max-height: 78vh; width: auto; height: auto; object-fit: contain; }
.lightbox__placeholder {
  display: flex; align-items: center; justify-content: center; width: 100%; min-height: 300px; padding: 60px;
  color: var(--text-dim); font-size: 14px; letter-spacing: .04em; background: rgba(127, 127, 127, .06);
}
.lightbox__caption {
  margin-top: 18px; text-align: center; font-family: var(--font-display); font-size: 13px;
  letter-spacing: .2em; text-transform: uppercase; color: var(--text-dim);
}
.lightbox__close {
  position: absolute; top: -46px; right: 0; width: 38px; height: 38px; cursor: pointer;
  background: transparent; border: 1px solid var(--border-strong); color: var(--text); font-size: 18px; line-height: 1;
  transition: border-color .3s ease, color .3s ease;
}
.lightbox__close:hover { border-color: var(--accent); color: var(--white); }
@media (max-width: 720px) {
  .lightbox { padding: 20px; }
  .lightbox__close { top: -44px; }
}

/* ==========================================================================
   Mobile pass
   The layout already collapses to one column at each component's own
   breakpoint; what was missing was the page frame. --gutter stayed 48px at
   every width, so a 375px phone spent 96px — a quarter of the screen — on
   side padding, and the desktop vertical rhythm (130px between sections)
   left long dead gaps between short blocks.
   ========================================================================== */
@media (max-width: 720px) {
  :root { --gutter: 24px; }

  /* Desktop section rhythm is built for wide columns; on a narrow screen the
     same values read as empty space rather than breathing room. */
  .section { padding: 84px var(--gutter); }
  .newsletter { padding: 84px var(--gutter); }
  .site-footer { padding: 56px var(--gutter) 36px; }
  .footer-grid { gap: 34px; padding-bottom: 40px; }

  /* Touch targets. These are 22-30px tall on desktop, which is fine for a
     cursor and too small for a thumb — roughly 40px is the practical floor.
     Scoped to mobile so the desktop footer's spacing is untouched. */
  .footer-col { gap: 4px; }
  .footer-col a { display: inline-block; padding: 9px 0; }
  .link-more { display: inline-block; padding: 6px 0 10px; }
  .footer-bottom { gap: 14px; }

  /* .section-head is a nowrap flex row, and .link-more carries
     white-space: nowrap, so on a narrow screen the link cannot shrink or
     wrap — it pushed straight through the right edge and dragged the layout
     wider than the viewport. body{overflow-x:hidden} hid the scrollbar but
     not the drift: the layout viewport still stretched, so the whole page
     slid sideways while scrolling. Let the row wrap instead. */
  .section-head { flex-wrap: wrap; align-items: flex-start; gap: 18px; margin-bottom: 34px; }
  .section-head h2 { font-size: 38px; }
  .section-head > div { min-width: 0; }
  .section-head__note { max-width: none; }

  .venue-location__map { padding: 12px 0 8px; }

  /* iOS Safari zooms the whole page when a focused input's text is under
     16px. The newsletter field is already 16px; match it here so tapping a
     demo field does not jerk the layout. */
  .demo-form input,
  .demo-form textarea { font-size: 16px; }

  /* Long uppercase headings with wide tracking are the most likely thing to
     force a horizontal scrollbar at 320px. */
  h1, h2 { overflow-wrap: break-word; }
}

/* Small phones (iPhone SE and similar). */
@media (max-width: 380px) {
  :root { --gutter: 18px; }
  .section { padding: 68px var(--gutter); }
  .newsletter { padding: 68px var(--gutter); }
  .eyebrow { font-size: 13px; letter-spacing: .28em; }
  .footer-col, .footer-bottom { letter-spacing: .16em; }
}
