/* ============================================================
   The Lost Boyz — mobile-first stylesheet
   Palette lifted from the Two Lost Souls artwork.
   ============================================================ */

:root {
  --ink:        #07040c;
  --ink-2:      #0e0718;
  --panel:      #150c22;
  --panel-2:    #1d1030;
  --line:       #2e1c47;

  --violet:     #a855f7;
  --violet-hi:  #c890ff;
  --violet-dim: #6d3fa8;
  --lilac:      #e2d1f9;

  --bone:       #ece7dd;
  --text:       #cfc4dd;
  --muted:      #9184a5;

  --wrap:       1120px;
  --pad:        clamp(1.25rem, 5vw, 2.5rem);
  --radius:     14px;

  --f-display:  "Oswald", "Arial Narrow", system-ui, sans-serif;
  --f-body:     "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
}

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

html {
  scroll-behavior: smooth;
  /* keep anchored sections clear of the sticky header */
  scroll-padding-top: 4.5rem;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 1rem;
  line-height: 1.65;
  /*
   * "clip" rather than "hidden". Both stop the page sliding sideways, but
   * overflow-x: hidden turns the body into a scroll container, which quietly
   * breaks position: sticky for anything inside it — the album art on the
   * desktop layout relies on that. clip has no such side effect.
   */
  overflow-x: clip;
}

/*
 * The same on the root element. body's overflow does not clip anything whose
 * containing block is the document itself, so a stray absolutely-positioned
 * element could still widen the page; this catches those.
 */
html { overflow-x: clip; }

img { max-width: 100%; height: auto; display: block; }

a { color: var(--violet-hi); }

::selection { background: var(--violet); color: #fff; }

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

/* visible to screen readers and search engines, not on screen */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--violet);
  color: #fff;
  padding: .75rem 1rem;
  z-index: 200;
}
.skip:focus { left: 0; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--pad);
}
.wrap--narrow { max-width: 640px; }

/* ---------------- Header ---------------- */

.hdr {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .6rem var(--pad);
  padding-top: max(.6rem, env(safe-area-inset-top));
}

/*
 * The bar itself is painted by a pseudo-element, not by .hdr.
 *
 * backdrop-filter makes an element the containing block for any
 * position:fixed descendant, and the nav drawer is a descendant of the
 * header. With the blur on .hdr the drawer was being laid out inside the
 * header box instead of the viewport: it came out 120px tall, so its
 * background covered only the top two links and the rest of the menu sat
 * straight on top of the page with nothing behind it. A pseudo-element
 * carries the same blur without taking the drawer with it.
 */
.hdr::before {
  content: "";
  position: absolute;
  inset: 0;
  /*
   * Above the drawer, not below it. The menu is longer than a short phone,
   * so it scrolls — and its links have to disappear behind the bar rather
   * than slide up over the band name and the close button.
   */
  z-index: 1;
  background: rgba(7, 4, 12, .72);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: background .3s, border-color .3s;
}
.hdr.is-stuck::before {
  background: rgba(7, 4, 12, .93);
  border-bottom-color: var(--line);
}
/* solid while the menu is open: a link scrolling up behind a see-through
   bar is worse than no bar at all */
body.nav-open .hdr::before {
  background: var(--ink-2);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom-color: var(--line);
}

.hdr__brand {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-right: auto;
  color: var(--bone);
  text-decoration: none;
  font-family: var(--f-display);
  font-size: 1.05rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.hdr__mark {
  width: 42px;
  height: auto;
  flex: none;
}

.hdr__toggle {
  /* above the drawer, so the button that opened the menu is still there to
     close it — it turns into a cross while the menu is open */
  position: relative;
  z-index: 2;
  display: grid;
  place-content: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
}
.hdr__toggle span {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--bone);
  transition: transform .28s, opacity .2s;
}
.hdr__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hdr__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hdr__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* mobile drawer */
.nav {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 0;
  --nav-cap: max(5.5rem, calc(env(safe-area-inset-top) + 4.9rem));
  width: min(78vw, 320px);
  padding: var(--nav-cap) 1.75rem 2rem;
  padding-bottom: max(2rem, env(safe-area-inset-bottom));
  /*
   * Solid, and lifted off the page. The panel is opaque, the border down its
   * left edge is a full violet rather than the faint line used elsewhere,
   * and a deep shadow falls from it onto the page — so it reads as something
   * sitting in front of the site rather than printed on top of it.
   */
  background: var(--ink-2);
  box-shadow: -20px 0 44px rgba(0, 0, 0, .65);
  border-left: 2px solid var(--violet);
  /*
   * Nine links do not fit on a short phone, so the drawer scrolls on its own
   * — and keeps that scroll to itself rather than letting it run on into the
   * page behind once it reaches the end.
   */
  overflow-y: auto;
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  gap: .25rem;
  transform: translateX(100%);
  transition: transform .32s cubic-bezier(.4, 0, .2, 1);
  visibility: hidden;
}
.nav.is-open { transform: translateX(0); visibility: visible; }

.nav a {
  /* likewise: links must keep their full height rather than being squeezed
     to fit a short screen — the drawer scrolls instead */
  flex: 0 0 auto;
  padding: .85rem 0;
  color: var(--bone);
  text-decoration: none;
  font-family: var(--f-display);
  font-size: 1.25rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--line);
}
.nav a:hover, .nav a:focus-visible { color: var(--violet-hi); }

.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: rgba(0, 0, 0, .72);
  opacity: 0;
  transition: opacity .3s;
}
.nav-scrim.is-on { opacity: 1; }

body.nav-open { overflow: hidden; }

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

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: 6rem var(--pad) 5rem;
  overflow: hidden;
}
.hero__bg { position: absolute; inset: 0; }
.hero__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(1.15);
}
/* darken + violet-wash the photo so type stays readable */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 40%, rgba(88, 28, 135, .45), transparent 62%),
    linear-gradient(180deg, rgba(7, 4, 12, .82) 0%, rgba(7, 4, 12, .62) 45%, rgba(7, 4, 12, .97) 100%);
}

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

.hero__inner picture { display: block; }

.hero__logo {
  width: min(78vw, 380px);
  height: auto;
  margin: 0 auto;
  filter: drop-shadow(0 6px 26px rgba(168, 85, 247, .45));
}

.hero__motto {
  margin: .5rem 0 .1rem;
  font-family: var(--f-display);
  font-size: clamp(.85rem, 3.4vw, 1.05rem);
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--violet-hi);
}
.hero__trans {
  margin: 0 0 1.4rem;
  font-size: .85rem;
  letter-spacing: .04em;
  color: var(--muted);
  font-style: italic;
}

.hero__tag {
  max-width: 22ch;
  margin: 0 auto 2rem;
  padding-top: 1.2rem;
  border-top: 1px solid rgba(168, 85, 247, .3);
  font-size: .95rem;
  line-height: 1.5;
  color: var(--lilac);
}

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: center;
}

.hero__scroll {
  position: absolute;
  bottom: 1.75rem;
  left: 50%;
  translate: -50% 0;
  z-index: 2;
  width: 26px;
  height: 42px;
  border: 2px solid rgba(226, 209, 249, .45);
  border-radius: 14px;
}
.hero__scroll span {
  position: absolute;
  top: 8px;
  left: 50%;
  translate: -50% 0;
  width: 3px;
  height: 7px;
  border-radius: 2px;
  background: var(--violet-hi);
  animation: wheel 1.8s ease-in-out infinite;
}
@keyframes wheel {
  0%   { transform: translateY(0);    opacity: 1; }
  70%  { transform: translateY(14px); opacity: 0; }
  100% { transform: translateY(0);    opacity: 0; }
}

/* ---------------- Buttons ---------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 46px;
  padding: .7rem 1.4rem;
  /*
   * Set explicitly, because a <button> and an <a> do not agree on it by
   * default — the browser gives the button "normal" while the link inherits
   * the body's 1.65. Two buttons that should be identical came out 3px
   * different in height.
   */
  line-height: 1.2;
  border: 1px solid var(--violet-dim);
  border-radius: 999px;
  background: rgba(20, 10, 32, .55);
  color: var(--lilac);
  font-family: var(--f-display);
  font-size: .95rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  transition: background .22s, border-color .22s, color .22s, transform .12s;
}
.btn:hover, .btn:focus-visible {
  background: rgba(168, 85, 247, .18);
  border-color: var(--violet);
  color: #fff;
}
.btn:active { transform: translateY(1px); }

.btn--solid {
  background: linear-gradient(135deg, var(--violet) 0%, #7c2fd4 100%);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 6px 22px rgba(124, 47, 212, .35);
}
.btn--solid:hover, .btn--solid:focus-visible {
  background: linear-gradient(135deg, var(--violet-hi) 0%, var(--violet) 100%);
  color: #fff;
}

/* ---------------- Section furniture ---------------- */

.sec { padding: clamp(3.5rem, 11vw, 6.5rem) 0; }
.sec--music { background: linear-gradient(180deg, var(--ink) 0%, var(--ink-2) 100%); }
.sec--band  { background: var(--ink-2); }
.sec--live  { background: var(--ink); }
.sec--gallery { background: var(--ink-2); }
.sec--booking { background: linear-gradient(180deg, var(--ink-2) 0%, var(--ink) 100%); }

.eyebrow {
  margin: 0 0 .35rem;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--violet);
}

.h2 {
  margin: 0 0 1.75rem;
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(2rem, 8.5vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--bone);
}
.h2::after {
  content: "";
  display: block;
  width: 62px;
  height: 3px;
  margin-top: .9rem;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--violet), transparent);
}

.lead {
  font-size: 1.05rem;
  color: var(--lilac);
  margin: 0 0 1.5rem;
}

/* release date badge, sits under the Two Lost Souls heading */
.release-date {
  display: inline-block;
  margin: -.75rem 0 1.75rem;
  padding: .4rem 1rem;
  background: rgba(168, 85, 247, .12);
  border: 1px solid var(--violet-dim);
  border-radius: 999px;
  font-family: var(--f-display);
  font-size: .85rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

/* ---------------- Release ---------------- */

.release { display: grid; gap: 2rem; }

.release__art { display: block; }

/*
 * The disc is a transparent cut-out, so it gets a soft glow rather than a
 * card border. It spins in once when it first scrolls into view.
 */
.disc {
  /*
   * Capped against the height of the screen, not just its width. Jumping to
   * the single puts about 290px of header, eyebrow, title and date above the
   * disc; on a short phone — an iPhone SE in Safari has barely 550px to play
   * with — a disc sized only by width ran off the bottom of the screen and
   * you watched half of it spin. svh is the small viewport height, the
   * height with the browser's own bars showing, which is the worst case.
   */
  width: min(100%, calc(100svh - 250px));
  max-width: 420px;
  margin-inline: auto;
  transform-origin: 50% 50%;
  filter:
    drop-shadow(0 14px 34px rgba(0, 0, 0, .65))
    drop-shadow(0 0 26px rgba(168, 85, 247, .35));
}

.disc.is-spinning {
  animation: disc-spin 1.6s cubic-bezier(.12, .7, .2, 1) both;
  will-change: transform;
}

@keyframes disc-spin {
  0%   { transform: rotate(-900deg) scale(.94); opacity: .5; }
  55%  { opacity: 1; }
  100% { transform: rotate(0deg) scale(1); opacity: 1; }
}

/*
 * On a short screen, the space between the title and the disc is the first
 * thing to give — better a tighter heading than a disc with its bottom cut
 * off. Anything taller than this keeps the roomier spacing.
 */
@media (max-height: 740px) {
  #music { padding-block: 2.25rem; }
  #music .h2 { margin-bottom: .5rem; }
  #music .release-date { margin-bottom: 1rem; }
}

/* Never animate, and never hold the page still, if motion is unwelcome. */
@media (prefers-reduced-motion: reduce) {
  .disc.is-spinning { animation: none; }
}

.tracks {
  list-style: none;
  margin: 0 0 1.75rem;
  padding: 0;
  border-top: 1px solid var(--line);
}
.tracks li {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: .9rem .25rem;
  border-bottom: 1px solid var(--line);
}
.tracks__n {
  font-family: var(--f-display);
  color: var(--violet);
  font-size: .95rem;
  min-width: 1.25rem;
}
.tracks__t { color: var(--bone); font-weight: 500; }
.tracks__t em { color: var(--muted); font-weight: 400; font-size: .9em; }

.links {
  display: flex;
  flex-wrap: wrap;
  gap: .65rem;
  margin-bottom: 2rem;
}

/*
 * Where the streaming links will go once the single is out. Deliberately
 * not buttons: a button that looks live but goes nowhere is worse than
 * saying plainly that it is not out yet.
 */
.soon { margin-bottom: 2rem; }

.soon__label {
  margin: 0 0 .6rem;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--violet);
}

.soon__list {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* the services not linked yet, said plainly rather than as dead buttons */
.soon__after {
  margin: -1.25rem 0 0;
  font-size: .88rem;
  color: var(--muted);
}

.chip {
  padding: .5rem 1rem;
  border: 1px dashed var(--violet-dim);
  border-radius: 999px;
  background: rgba(168, 85, 247, .07);
  color: var(--muted);
  font-family: var(--f-display);
  font-size: .88rem;
  letter-spacing: .08em;
  text-transform: uppercase;
}

.credits { margin: 0; display: grid; gap: .6rem; }
.credits > div {
  display: grid;
  grid-template-columns: 1fr;
  gap: .1rem;
  padding-bottom: .6rem;
  border-bottom: 1px dashed rgba(46, 28, 71, .8);
}
.credits dt {
  font-family: var(--f-display);
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--muted);
}
.credits dd { margin: 0; color: var(--bone); }

/* ---------------- Members ---------------- */

.members { display: grid; gap: 1rem; margin-bottom: 2.5rem; }

.member {
  padding: 1.5rem;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.member h3 {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: 1.5rem;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--bone);
}
.member__role {
  margin: 0 0 .85rem;
  font-size: .82rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--violet-hi);
}
.member p:last-child { margin: 0; color: var(--text); }

.band-shot img {
  width: 100%;
  border-radius: var(--radius);
  border: 1px solid var(--line);
}
.band-shot { margin: 0; }

/* ---------------- Reelz ---------------- */

/* sub-page header, clears the fixed site header */
.sec--page { padding-top: calc(4.5rem + clamp(2rem, 8vw, 4rem)); background: var(--ink-2); }

.backlink { margin-top: 2.5rem; text-align: center; }

/* ---------------- Single event page ---------------- */

.event {
  margin: 0 0 1.75rem;
  padding: 1.25rem 1.4rem;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--violet-dim);
  border-radius: var(--radius);
}

.event__when {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: clamp(1.15rem, 5vw, 1.5rem);
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--bone);
}

.event__where {
  margin: 0 0 .5rem;
  font-size: 1rem;
  color: var(--lilac);
}

.event__free {
  margin: 0;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

/* the call-out button row is centred by default; on a page of prose it is not */
.cta-links--left { justify-content: flex-start; margin: 1.5rem 0 0; }

/*
 * The "Read our story" button on the home page.
 *
 * It used to drift slowly up and down, which was a mistake: on a phone,
 * where nothing hovers, a thing that moves on its own reads as decoration —
 * and the record above it is already turning, so the page just looked busy.
 *
 * The idle movement is now a shine travelling across the artwork instead.
 * A sweep of light across a surface is the language of a button top, not of
 * something floating about, and it happens *to* the object rather than
 * moving it. Under it sits a line of text with an arrow at each end pointing
 * back up at the artwork, which says the thing outright.
 *
 * Three layers, because the ground, the push and the artwork each need their
 * own transform:
 *   .story-btn        the ground, and the shadow on it
 *   .story-btn__lift  the push — down when pressed, up when pointed at
 *   .story-btn__art   the artwork, with the shine over it
 */
.story-btn {
  position: relative;
  display: inline-block;
  width: min(88vw, 440px);
  padding-bottom: 20px;
  line-height: 0;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* the shadow the button sits on. It does not move — the button moves on it */
.story-btn::before {
  content: "";
  position: absolute;
  left: 14%;
  right: 14%;
  top: calc(100% - 76px);
  height: 20px;
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(0, 0, 0, .78) 0%, rgba(0, 0, 0, .42) 45%, transparent 72%);
  filter: blur(5px);
  transition: transform .16s ease, opacity .16s ease;
}

.story-btn__lift {
  position: relative;
  display: block;
  transition: transform .16s cubic-bezier(.2, .8, .3, 1);
}

.story-btn__art {
  display: block;
  filter: drop-shadow(0 12px 20px rgba(0, 0, 0, .55))
          drop-shadow(0 0 22px rgba(168, 85, 247, .38));
}

.story-btn img { display: block; width: 100%; height: auto; }

/*
 * The shine. A narrow diagonal band of light slides across, clipped to the
 * artwork's own silhouette by using the artwork as a mask — without that it
 * would sweep across the empty corners too and give the cut-out away as a
 * rectangle. Hidden entirely where masks are not supported, for the same
 * reason.
 */
.story-btn__shine {
  display: none;
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(102deg,
    transparent 40%,
    rgba(255, 255, 255, .5) 47%,
    rgba(255, 255, 255, .88) 50%,
    rgba(255, 255, 255, .5) 53%,
    transparent 60%);
  background-size: 260% 100%;
  background-repeat: no-repeat;
  mix-blend-mode: screen;
  -webkit-mask-image: url(/images/story-button-680.webp);
          mask-image: url(/images/story-button-680.webp);
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  animation: story-btn-shine 4.2s ease-in-out infinite;
}

@supports ((-webkit-mask-image: url(#m)) or (mask-image: url(#m))) {
  .story-btn__shine { display: block; }
}

/* across, then wait off the far edge before coming round again */
@keyframes story-btn-shine {
  0%, 8%    { background-position: 150% 0; }
  38%, 100% { background-position: -70% 0; }
}

/* ---- the line underneath that says what to do ---- */

.story-btn__cue {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  margin-top: .9rem;
  font-family: var(--f-display);
  font-size: .82rem;
  line-height: 1.4;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

/* the arrows point back up at the artwork, and nudge towards it */
.story-btn__arrow {
  font-size: .7em;
  animation: story-btn-nudge 1.5s ease-in-out infinite;
}
.story-btn__arrow:last-child { animation-delay: .12s; }

@keyframes story-btn-nudge {
  0%, 100% { transform: translateY(2px); opacity: .55; }
  50%      { transform: translateY(-2px); opacity: 1; }
}

/*
 * Pressed. The class is put on by app.js from the pointer events, because
 * :active on a link is unreliable on touch — some mobile browsers never
 * apply it, others hold it long after the finger has gone. :active stays as
 * the fallback for anyone without JavaScript.
 *
 * The button drops nearly onto the ground and the shadow collapses tight
 * underneath it. That is what sells the push: a thing that has landed has a
 * small hard shadow, a thing in the air a big soft one.
 */
.story-btn.is-pressing .story-btn__lift,
.story-btn:active .story-btn__lift {
  transform: translateY(11px) scale(.965);
  transition-duration: .07s;
}

.story-btn.is-pressing .story-btn__art,
.story-btn:active .story-btn__art {
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .7))
          drop-shadow(0 0 26px rgba(168, 85, 247, .8));
}

.story-btn.is-pressing::before,
.story-btn:active::before {
  transform: scale(.7, .45);
  opacity: 1;
  transition-duration: .07s;
}

/* Pointing at it lifts it clear — but only on something with a real
   pointer, so a phone is never left holding a hover state after a tap. */
@media (hover: hover) and (pointer: fine) {
  .story-btn:hover .story-btn__lift { transform: translateY(-6px) scale(1.025); }
  .story-btn:hover .story-btn__art {
    filter: drop-shadow(0 20px 28px rgba(0, 0, 0, .6))
            drop-shadow(0 0 36px rgba(168, 85, 247, .65));
  }
  .story-btn:hover::before { transform: scale(1.08, .9); opacity: .5; }
}

.story-btn:focus-visible { outline: 3px solid var(--violet-hi); outline-offset: 8px; border-radius: 18px; }
.story-btn:focus-visible .story-btn__lift { transform: translateY(-6px) scale(1.025); }

/*
 * Less movement for anyone who has asked for it: no shine, no nudging
 * arrows. The words and the arrows stay, which is the part that actually
 * tells you it is a button — the movement was only ever there to catch the
 * eye of people who would not read it.
 */
@media (prefers-reduced-motion: reduce) {
  .story-btn__shine,
  .story-btn__arrow { animation: none; }
  .story-btn__arrow { opacity: 1; transform: none; }
  .story-btn .story-btn__lift,
  .story-btn:hover .story-btn__lift,
  .story-btn:focus-visible .story-btn__lift { transform: none; }
  .story-btn.is-pressing .story-btn__lift,
  .story-btn:active .story-btn__lift { transform: translateY(6px); }
}

/* the thumbnail on the home page that opens /reelz */
.teaser {
  position: relative;
  display: block;
  width: min(100%, 320px);
  margin-inline: auto;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border: 2px solid var(--violet);
  border-radius: var(--radius);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .25),
    0 10px 34px rgba(124, 47, 212, .32);
  text-decoration: none;
  transition: border-color .25s, box-shadow .25s, transform .25s;
}
.teaser:hover, .teaser:focus-visible {
  border-color: var(--violet-hi);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .45),
    0 14px 44px rgba(124, 47, 212, .5);
  transform: translateY(-3px);
}

.teaser picture, .teaser img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.teaser img { object-fit: cover; object-position: center 28%; }

/* darken the photo so the play button and label stay readable */
.teaser::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(7, 4, 12, .25) 0%, rgba(7, 4, 12, .3) 45%, rgba(7, 4, 12, .92) 100%);
}

.teaser__play {
  position: absolute;
  top: 44%;
  left: 50%;
  translate: -50% -50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 66px;
  height: 66px;
  border: 2px solid rgba(236, 231, 221, .9);
  border-radius: 50%;
  background: rgba(7, 4, 12, .35);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  transition: background .25s, scale .25s;
}
/* the triangle */
.teaser__play::before {
  content: "";
  width: 0;
  height: 0;
  margin-left: 5px;
  border-top: 12px solid transparent;
  border-bottom: 12px solid transparent;
  border-left: 20px solid var(--bone);
}
.teaser:hover .teaser__play { background: rgba(168, 85, 247, .5); scale: 1.08; }

.teaser__cta {
  position: absolute;
  left: 50%;
  bottom: 1.25rem;
  translate: -50% 0;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: .6rem 1.4rem;
  white-space: nowrap;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--violet) 0%, #7c2fd4 100%);
  color: #fff;
  font-family: var(--f-display);
  font-size: .9rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  box-shadow: 0 6px 22px rgba(124, 47, 212, .45);
}

.reels {
  display: grid;
  gap: 1.5rem;
  justify-items: center;
  /* clips are portrait and landscape both, so tiles differ in height */
  align-items: start;
}

/* removed the moment the sound is unlocked, so it never nags */
.reels__hint {
  margin: 0 0 1.25rem;
  padding: .6rem 1rem;
  text-align: center;
  font-size: .88rem;
  color: var(--lilac);
  background: rgba(168, 85, 247, .1);
  border: 1px dashed var(--violet-dim);
  border-radius: 999px;
}

.reels__msg {
  width: 100%;
  padding: 1.75rem;
  text-align: center;
  color: var(--muted);
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.reel {
  width: 100%;
  max-width: 480px;
  margin: 0;
}

/*
 * The purple frame around each clip. No aspect-ratio here — it is set on the
 * element from the video's real dimensions, because these were filmed both
 * ways up and one fixed shape would crop half of them to bits. Setting it
 * per clip also reserves the right space before the poster arrives, so the
 * page does not jump as they load.
 */
.reel__shell {
  position: relative;
  width: 100%;
  /*
   * Never taller than the screen. A portrait clip at the full width of a
   * phone is about 600px tall, which does not fit on a short one — and since
   * these start playing as you scroll onto them, you would be watching a clip
   * whose bottom you cannot see. Capping the width by the height available
   * keeps the clip's real shape and simply makes it a little smaller. On a
   * normal-sized phone the cap never bites.
   */
  max-width: calc((100svh - 130px) * var(--reel-ratio, .5625));
  margin-inline: auto;
  aspect-ratio: 9 / 16;   /* fallback only; overridden per clip */
  overflow: hidden;
  background: #000;
  border: 2px solid var(--violet);
  border-radius: var(--radius);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .25),
    0 10px 34px rgba(124, 47, 212, .32);
  transition: border-color .25s, box-shadow .25s;
}
.reel:hover .reel__shell,
.reel__shell:focus-within {
  border-color: var(--violet-hi);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .45),
    0 12px 40px rgba(124, 47, 212, .5);
}

.reel__video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* the tile already matches the clip, so this neither crops nor letterboxes */
  object-fit: contain;
  background: #000;
  border: 0;
}

/* seen only until the poster frame paints */
.reel__shell::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 45%, rgba(88, 28, 135, .45), transparent 70%),
    linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
}

.reel__cap {
  margin-top: .75rem;
  text-align: center;
  font-family: var(--f-display);
  font-size: .95rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--lilac);
}

/* ---------------- Sponsors ---------------- */

.h3 {
  margin: 3rem 0 1.25rem;
  font-family: var(--f-display);
  font-weight: 600;
  font-size: clamp(1.35rem, 5.5vw, 1.85rem);
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bone);
}

/* the first h3 on a page follows the intro, so it needs no big gap above */
.h3--first { margin-top: 1.5rem; }

.sponsor-cta { margin: 0 0 1rem; }

.plans { display: grid; gap: 1rem; }

.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 1.6rem 1.4rem;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.plan--featured {
  border-color: var(--violet);
  box-shadow: 0 0 0 1px rgba(168, 85, 247, .25), 0 12px 36px rgba(124, 47, 212, .28);
}

/*
 * The open-ended option. It sits below the grid rather than in it, and runs
 * the full width, because it is not a fourth price — it is the invitation to
 * ignore the prices and ask for something else.
 */
.plan--custom {
  margin-top: 1.75rem;
  border-style: dashed;
  border-color: var(--violet-hi);
  background:
    radial-gradient(ellipse at 50% -20%, rgba(124, 47, 212, .32), transparent 60%),
    linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  box-shadow: 0 0 0 1px rgba(200, 144, 255, .22), 0 14px 40px rgba(124, 47, 212, .3);
}
.plan__flag--custom {
  background: linear-gradient(135deg, var(--violet-hi) 0%, var(--violet) 100%);
  color: #200a33;
}
.plan__blurb {
  margin: 0 0 1.1rem;
  color: var(--text);
}

@media (min-width: 700px) {
  /* the copy and the list side by side, so it does not become a tall slab */
  .plan--custom { display: grid; grid-template-columns: 1fr 1fr; gap: 0 2rem; align-items: start; }
  .plan--custom .plan__name,
  .plan--custom .plan__price { grid-column: 1 / -1; }
  .plan--custom .plan__blurb { margin-bottom: 0; }
  .plan--custom .plan__btn { grid-column: 1 / -1; margin-top: 1.25rem; }
}

.plan__flag {
  position: absolute;
  top: -.7rem;
  left: 1.4rem;
  padding: .25rem .7rem;
  border-radius: 999px;
  background: linear-gradient(135deg, var(--violet) 0%, #7c2fd4 100%);
  color: #fff;
  font-family: var(--f-display);
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
}

.plan__name {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: 1.3rem;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--bone);
}

.plan__price {
  margin: 0 0 1rem;
  font-family: var(--f-display);
  font-size: 2.1rem;
  line-height: 1;
  color: var(--violet-hi);
}
.plan__price span {
  font-size: .95rem;
  letter-spacing: .08em;
  color: var(--muted);
}

.plan__list {
  flex: 1;
  margin: 0 0 1.4rem;
  padding: 0;
  list-style: none;
}
.plan__list li {
  position: relative;
  padding: .35rem 0 .35rem 1.4rem;
  font-size: .95rem;
  color: var(--text);
}
.plan__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: .95em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--violet);
}

.plan__btn { width: 100%; }

/* only added once a plan actually has a payment link behind it */
.plans__note {
  margin: 1.25rem 0 0;
  text-align: center;
  font-size: .85rem;
  color: var(--muted);
}

.sponsors { display: grid; gap: 1.35rem; }

.sponsors__msg {
  padding: 1.75rem;
  text-align: center;
  color: var(--muted);
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

/*
 * Sponsor and charity cards have to read as cards.
 *
 * They were a dark panel on a dark page inside a hairline border, which on a
 * phone — smaller, brighter, usually held at arm's length outdoors — melted
 * into the background and the page looked like one long column of text. They
 * now have a violet edge, a lit surface, a glow beneath them and a bright bar
 * across the top, so each one is plainly a separate thing on the page. These
 * are people who have given the band money, or causes the band is asking you
 * to give money to; they should be the most obvious things on their pages.
 */
.sponsor {
  position: relative;
  overflow: hidden;
  padding: 1.6rem 1.35rem 1.45rem;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(124, 47, 212, .3), transparent 62%),
    linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 2px solid var(--violet-dim);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45), 0 0 0 1px rgba(168, 85, 247, .12);
  text-align: center;
}

/* the bar across the top of the card */
.sponsor::before,
.charity::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  background: linear-gradient(90deg, var(--violet-dim), var(--violet-hi) 50%, var(--violet-dim));
}
/*
 * Logos get a white plate. Almost every business logo is drawn for white
 * paper — Inkspires' has dark navy lettering that would disappear straight
 * into the card's dark panel — so rather than hoping each one suits a dark
 * background, they all sit on their own.
 */
.sponsor__logo {
  display: block;
  width: auto;
  max-width: 170px;
  max-height: 112px;
  margin: 0 auto .9rem;
  padding: .55rem .7rem;
  background: #fff;
  border-radius: 10px;
  object-fit: contain;
}
.sponsor__name {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: 1.15rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bone);
}
.sponsor__name a { color: inherit; text-decoration: none; }
.sponsor__name a:hover { color: var(--violet-hi); }
.sponsor__tier {
  margin: 0 0 .5rem;
  font-family: var(--f-display);
  font-size: .7rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--violet);
}
.sponsor__blurb { margin: 0; font-size: .92rem; color: var(--text); }

/* ---------------- Our Story ---------------- */

/* the line on the home page that leads into the full story */
.story-teaser__lead { margin-top: 1.75rem; }

/*
 * One width for the whole page. The story used to sit in a 640px column
 * while the memorial below it ran the full 1120px, so the photographs came
 * out nearly twice as wide as the text above them and the page stepped in
 * and out as you scrolled. Everything shares this now, so every left edge
 * lines up top to bottom.
 */
.wrap--story { max-width: 1240px; }

.story { margin-bottom: 1rem; }

/*
 * The story runs the full width of the page — no shorter measure holding
 * the paragraphs in, so a line carries a long way before it wraps and the
 * block sits centred on the screen rather than hard against the left with
 * empty space beside it. Long lines are harder to track back to the start
 * of, so they are given extra leading further down to compensate.
 */
.story p,
.bio__text p { margin: 0 0 1.15rem; }

/* the closing line, given a little room to land */
.story__end {
  margin-top: 1.75rem;
  font-family: var(--f-display);
  font-size: 1.1rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

.bio { display: grid; gap: 1.5rem; margin-bottom: 2rem; }

.bio__photo img {
  width: 100%;
  height: auto;
  border-radius: 16px;
  filter: drop-shadow(0 12px 28px rgba(0, 0, 0, .5));
}

/* ---------------- In loving memory ---------------- */

.memory {
  margin-top: 3rem;
  padding: clamp(2rem, 7vw, 3.5rem) 0 1rem;
  border-top: 1px solid var(--line);
  text-align: center;
}

.memory__eyebrow { color: var(--violet); }
.memory__title { margin-bottom: 2.5rem; }
.memory__title::after { margin-inline: auto; }

.memory__pair { display: grid; gap: 3rem; text-align: left; }

.remember p { margin: 0 0 1.05rem; }
.remember__text { min-width: 0; }

.remember__photo {
  display: block;
  width: min(100%, 320px);
  margin: 0 auto 1.25rem;
}

/*
 * Softened edges. The corners are rounded and the very outside of the
 * photograph is faded away, so it sits in the page rather than being stuck
 * on it — which suits a memorial far better than a hard rectangle. The
 * shadow is a drop-shadow rather than a box-shadow so that it follows the
 * faded shape instead of tracing a crisp box behind it.
 */
.remember__photo img {
  width: 100%;
  height: auto;
  border-radius: 20px;
  -webkit-mask-image: radial-gradient(ellipse 100% 100% at 50% 50%, #000 86%, transparent 100%);
          mask-image: radial-gradient(ellipse 100% 100% at 50% 50%, #000 86%, transparent 100%);
  filter: drop-shadow(0 14px 32px rgba(0, 0, 0, .55));
}

.remember__name {
  margin: 0 0 .9rem;
  text-align: center;
  font-family: var(--f-display);
  font-size: 1.6rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--bone);
}

.remember__by {
  margin-top: 1.25rem;
  font-style: italic;
  color: var(--muted);
}

.memory__motto {
  margin: 2.5rem 0 .3rem;
  font-family: var(--f-display);
  font-size: .85rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--violet);
}
.memory__trans {
  margin: 0;
  font-size: .85rem;
  font-style: italic;
  color: var(--muted);
}

/* ---------------- Geeks Corner ---------------- */

.gear__msg {
  padding: 1.75rem;
  text-align: center;
  color: var(--muted);
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.kits {
  display: grid;
  gap: 1rem;
  margin-bottom: 1rem;
}

.kit {
  padding: 1.35rem 1.4rem;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}

.kit__name {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: 1.2rem;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--bone);
}

.kit__what {
  margin: 0 0 .7rem;
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

.kit__note { margin: 0; font-size: .95rem; color: var(--text); }

/* ---------------- Charities ---------------- */

.charities {
  display: grid;
  gap: 1.35rem;
  margin-bottom: 1rem;
}

.charities__msg {
  padding: 1.75rem;
  text-align: center;
  color: var(--muted);
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.charity {
  position: relative;
  overflow: hidden;
  padding: 1.75rem 1.4rem 1.5rem;
  background:
    radial-gradient(ellipse at 50% -10%, rgba(124, 47, 212, .3), transparent 62%),
    linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 2px solid var(--violet-dim);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .45), 0 0 0 1px rgba(168, 85, 247, .12);
}

/*
 * Somebody's fundraiser is not the same thing as a charity, and the page
 * lists them one above the other. A full violet edge and a warmer bar keeps
 * the two apart at a glance without needing a heading to explain it.
 */
.charity--fundraiser {
  border-color: var(--violet);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .3),
    0 14px 38px rgba(124, 47, 212, .32);
}
.charity--fundraiser::before {
  height: 5px;
  background: linear-gradient(90deg, #7c2fd4, #ff9d5c 45%, #ffd58a 60%, #c890ff);
}

.charity__name {
  margin: 0 0 .2rem;
  font-family: var(--f-display);
  font-size: 1.4rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bone);
}
.charity__name a { color: inherit; text-decoration: none; }
.charity__name a:hover { color: var(--violet-hi); }

.charity__sub {
  margin: 0 0 .8rem;
  font-size: .82rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

.charity__blurb { margin: 0 0 .9rem; color: var(--text); }

.charity__facts {
  margin: 0 0 1.2rem;
  font-size: .82rem;
  color: var(--muted);
}

.charity__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .65rem;
}

/* ---------------- Sponsor form dialog ---------------- */

.modal {
  width: min(92vw, 520px);
  max-height: 88vh;
  padding: 0;
  border: 1px solid var(--violet-dim);
  border-radius: var(--radius);
  background: var(--ink-2);
  color: var(--text);
  overflow: auto;
}
.modal::backdrop { background: rgba(4, 2, 10, .78); backdrop-filter: blur(3px); }

.modal__inner { position: relative; padding: 1.75rem 1.4rem 1.5rem; }

.modal__close {
  position: absolute;
  top: .5rem;
  right: .65rem;
  width: 40px;
  height: 40px;
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 1.9rem;
  line-height: 1;
  cursor: pointer;
}
.modal__close:hover { color: var(--bone); }

.modal__title {
  margin: 0 0 .3rem;
  font-family: var(--f-display);
  font-size: 1.6rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--bone);
}
.modal__intro { margin: 0 0 1.4rem; font-size: .95rem; color: var(--muted); }

.field { margin-bottom: 1rem; }
.field label {
  display: block;
  margin-bottom: .35rem;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--lilac);
}
.field__opt { color: var(--muted); letter-spacing: .06em; }

.field input,
.field select,
.field textarea {
  width: 100%;
  padding: .7rem .85rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--bone);
  font-family: var(--f-body);
  font-size: 16px; /* keeps iOS from zooming the page on focus */
}
.field textarea { resize: vertical; }
.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--violet);
  box-shadow: 0 0 0 3px rgba(168, 85, 247, .2);
}

.modal__err {
  margin: 0 0 1rem;
  padding: .7rem .85rem;
  border-radius: 10px;
  background: rgba(190, 40, 60, .14);
  border: 1px solid rgba(230, 90, 110, .5);
  color: #ffc9d2;
  font-size: .9rem;
}

.modal__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .65rem;
  margin-top: 1.25rem;
}
.modal__actions .btn { flex: 1; }

.modal__note {
  margin: 1rem 0 0;
  font-size: .8rem;
  color: var(--muted);
  text-align: center;
}

/* ---------------- Gigs ---------------- */

.gigs__msg {
  padding: 1.75rem;
  text-align: center;
  color: var(--muted);
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}

.gig {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: .15rem 1.15rem;
  align-items: center;
  padding: 1.15rem 1.25rem;
  margin-bottom: .75rem;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.gig__date {
  display: grid;
  place-items: center;
  width: 62px;
  padding: .5rem 0;
  background: rgba(168, 85, 247, .12);
  border: 1px solid var(--violet-dim);
  border-radius: 10px;
  font-family: var(--f-display);
  line-height: 1.1;
}
.gig__day { font-size: 1.5rem; color: var(--bone); }
.gig__mon {
  font-size: .68rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--violet-hi);
}
/* only appears on gigs that are not in the current year */
.gig__yr {
  font-size: .6rem;
  letter-spacing: .1em;
  color: var(--muted);
}
.gig__venue {
  font-family: var(--f-display);
  font-size: 1.15rem;
  letter-spacing: .02em;
  color: var(--bone);
}
.gig__meta { font-size: .88rem; color: var(--muted); }

/* venue, town and note as one block, so the card stays [date][details][button] */
.gig__body { display: grid; gap: .1rem; }

/* the optional line for a charity night, a support act, whatever it is */
.gig__note {
  margin-top: .45rem;
  font-size: .86rem;
  line-height: 1.45;
  color: var(--violet-hi);
}

.gig__cta { grid-column: 1 / -1; margin-top: .9rem; }

/* ---------------- Gallery ---------------- */

.grid { display: grid; gap: .75rem; }
.grid__item { margin: 0; overflow: hidden; border-radius: var(--radius); border: 1px solid var(--line); }
.grid__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  aspect-ratio: 4 / 3;
  transition: transform .5s ease;
}
.grid__item:hover img { transform: scale(1.04); }

/* ---------------- Gallery tiles ---------------- */

.shots__hint {
  margin: 0 0 1rem;
  font-size: .9rem;
  color: var(--muted);
}

/*
 * On a phone this is a swipeable row rather than a grid, so each photo gets
 * most of the screen instead of being one of a dozen thumbnails. It bleeds
 * to the screen edges on purpose — a tile cut off at the right is what tells
 * you there is more to swipe to.
 */
.shots {
  display: flex;
  gap: .75rem;
  overflow-x: auto;
  /*
   * Snapping is switched on by app.js only while a finger is actually on the
   * strip, and switched off again once it lets go.
   *
   * A scroll-snap container re-snaps whenever the viewport is resized, and on
   * a phone the address bar sliding away as you scroll down the page *is* a
   * resize — so the strip kept jumping sideways while you were scrolling
   * vertically past it, and the page never felt still. Proximity snapping
   * softened that but did not stop it. Off unless it is being touched does.
   */
  scroll-snap-type: none;
  /* and keep that sideways scroll inside the strip rather than letting it
     chain out to the page or the browser's back gesture */
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  margin-inline: calc(var(--pad) * -1);
  padding: .25rem var(--pad) 1rem;
}
.shots.is-swiping { scroll-snap-type: x proximity; }
.shots::-webkit-scrollbar { display: none; }

.shot {
  /* the third value keeps a whole photo on screen on a short phone, where
     jumping to the gallery otherwise landed the first one half cut off */
  flex: 0 0 min(74vw, 320px, calc(100svh - 310px));
  scroll-snap-align: center;
  aspect-ratio: 1;
  padding: 0;
  overflow: hidden;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .25s, box-shadow .25s;
}
.shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .4s ease;
}
.shot:hover, .shot:focus-visible {
  border-color: var(--violet);
  box-shadow: 0 0 0 1px rgba(200, 144, 255, .3), 0 10px 28px rgba(124, 47, 212, .3);
}
.shot:hover img { transform: scale(1.05); }

/* ---------------- Photo viewer ---------------- */

.lb {
  /* 100vw includes the scrollbar gutter and overflows by a few pixels;
     percentages of the dialog's own containing block do not */
  width: 100%;
  max-width: 100%;
  height: 100dvh;
  max-height: 100dvh;
  padding: 0;
  border: 0;
  background: rgba(4, 2, 10, .97);
  color: var(--text);
  overflow: hidden;
}
.lb::backdrop { background: rgba(0, 0, 0, .92); }

.lb[open] {
  display: grid;
  place-items: center;
}

/*
 * The arrows sit on top of the photo rather than beside it. Flanking it in
 * their own columns stole about 130px on a phone, which left the photo
 * opening smaller than the tile that had just been tapped.
 */
.lb__fig {
  width: 100%;
  margin: 0;
  display: grid;
  justify-items: center;
  gap: .85rem;
  padding: 3.5rem 1rem 1.5rem;
}

/*
 * Width is set inline by the viewer, from each photo's own natural size:
 * max-width alone never enlarges anything, so the small gig snaps opened
 * smaller than the tile that had just been tapped. This is only the
 * fallback for before that runs.
 */
.lb__img {
  width: min(92vw, 560px);
  height: auto;
  max-height: 72dvh;
  object-fit: contain;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: #000;
}

.lb__cap {
  max-width: 34ch;
  font-size: .85rem;
  line-height: 1.5;
  color: var(--muted);
  text-align: center;
}
.lb__count {
  display: block;
  margin-bottom: .3rem;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .18em;
  color: var(--violet-hi);
}

.lb__close {
  position: absolute;
  top: .6rem;
  right: .8rem;
  width: 46px;
  height: 46px;
  background: none;
  border: 0;
  color: var(--muted);
  font-size: 2.1rem;
  line-height: 1;
  cursor: pointer;
}
.lb__close:hover { color: var(--bone); }

.lb__nav {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  z-index: 2;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  background: rgba(7, 4, 12, .6);
  border: 1px solid var(--violet-dim);
  border-radius: 50%;
  cursor: pointer;
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  transition: background .2s, border-color .2s;
}
.lb__nav:hover { background: rgba(168, 85, 247, .5); border-color: var(--violet); }
.lb__nav::before {
  content: "";
  width: 11px;
  height: 11px;
  border-top: 2px solid var(--bone);
  border-right: 2px solid var(--bone);
}
.lb__nav--prev { left: .5rem; }
.lb__nav--prev::before { transform: rotate(-135deg); margin-left: 4px; }
.lb__nav--next { right: .5rem; }
.lb__nav--next::before { transform: rotate(45deg); margin-right: 4px; }

@media (prefers-reduced-motion: reduce) {
  .shot:hover img { transform: none; }
}

/* ---------------- Booking, forms and the thank you ---------------- */

/*
 * The two ways of getting hold of the band, as a matched pair.
 *
 * On a phone they stack full width and centred, both exactly the same size
 * and shape — one is the thing to do, the other is the other thing to do,
 * and neither should look like a different kind of control. Side by side on
 * a wider screen, still matched, because the grid gives both columns the
 * same width.
 */
.book-actions {
  display: grid;
  gap: .75rem;
  justify-items: center;
  margin: 0 0 1.5rem;
}

.book-actions > * { width: 100%; max-width: 22rem; }

@media (min-width: 560px) {
  .book-actions {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    justify-content: center;
    max-width: 34rem;
  }
  .book-actions > * { max-width: none; }
}

/* the size both of them share */
.btn--big {
  min-height: 58px;
  padding: 1rem 1.6rem;
  font-size: 1.05rem;
  letter-spacing: .1em;
  text-align: center;
}

/*
 * The honeypot. Off-screen rather than display:none — a bot reading the DOM
 * fills in anything it can find a label for, and some of them skip hidden
 * fields on purpose. aria-hidden and tabindex="-1" keep it away from anyone
 * using a screen reader or the keyboard.
 */
.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* business or individual */
.field--choice {
  margin: 0 0 1rem;
  padding: 0;
  border: 0;
}
.field--choice legend {
  padding: 0;
  margin-bottom: .5rem;
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--violet-hi);
}

.choice {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin-bottom: .45rem;
  padding: .7rem .9rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color .2s, background .2s;
}
.choice input { accent-color: var(--violet); width: 1.05rem; height: 1.05rem; }
.choice:hover { border-color: var(--violet-dim); }
.choice:has(input:checked) {
  border-color: var(--violet);
  background: rgba(168, 85, 247, .12);
}

/* two fields on one line where there is room for it */
.field--pair { display: grid; gap: 1rem; }
@media (min-width: 560px) {
  .field--pair { grid-template-columns: 1fr 1fr; }
}

/*
 * The thank you.
 *
 * Deliberately not a form: nothing to read, nothing to decide, one button.
 * Somebody who has just sent an enquiry wants to know it went and to get on
 * with their day.
 */
.modal--thanks { max-width: 30rem; text-align: center; }

.thanks { padding: 2.25rem 1.6rem 1.9rem; }

.thanks__mark {
  display: block;
  width: min(60%, 200px);
  height: auto;
  margin: 0 auto 1.25rem;
  filter: drop-shadow(0 8px 26px rgba(168, 85, 247, .45));
}

.thanks__sent {
  margin: 0 0 1rem;
  font-size: .95rem;
  color: var(--text);
}

.thanks__big {
  margin: 0 0 1.25rem;
  font-family: var(--f-display);
  font-size: clamp(1.6rem, 7vw, 2.35rem);
  line-height: 1.05;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--bone);
  text-shadow: 0 4px 24px rgba(168, 85, 247, .5);
}

.thanks__note {
  margin: 0 0 1.5rem;
  font-size: .88rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.thanks__go { width: 100%; }
@media (min-width: 480px) {
  .thanks__go { width: auto; min-width: 12rem; }
}

/* ---------------- Coming dreckly ---------------- */

/*
 * The holding notice on the Geekz page. Big, shiny and unmistakably
 * deliberate — a page that is obviously not finished yet reads far better
 * than a page that looks finished and turns out to be empty.
 */
.dreckly {
  margin: 2.5rem 0 3rem;
  padding: clamp(2rem, 8vw, 3.5rem) 1.25rem;
  text-align: center;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(124, 47, 212, .35), transparent 65%),
    linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 2px solid var(--violet);
  border-radius: var(--radius);
  box-shadow:
    0 0 0 1px rgba(200, 144, 255, .3),
    0 18px 50px rgba(124, 47, 212, .35);
}

.dreckly__word {
  margin: 0;
  font-family: var(--f-display);
  font-size: clamp(2.6rem, 13vw, 5.5rem);
  line-height: .95;
  letter-spacing: .04em;
  text-transform: uppercase;

  /*
   * The shine: a bright band travelling across the letters. The gradient is
   * clipped to the glyphs themselves, so the light runs through the word
   * rather than over a box behind it.
   */
  background: linear-gradient(100deg,
    #b98cf0 0%, #e9d8ff 38%, #ffffff 48%, #e9d8ff 58%, #a855f7 100%);
  background-size: 260% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 4px 18px rgba(168, 85, 247, .55));
  animation: dreckly-shine 5s ease-in-out infinite;
}

@keyframes dreckly-shine {
  0%, 10%   { background-position: 130% 0; }
  55%, 100% { background-position: -30% 0; }
}

.dreckly__sub {
  max-width: 32ch;
  margin: 1.4rem auto 0;
  color: var(--muted);
}

/* the shine is decoration; the word must still be readable without it */
@media (prefers-reduced-motion: reduce) {
  .dreckly__word { animation: none; background-position: 50% 0; }
}

/* ---------------- Where the gig is ---------------- */

/*
 * A fold-out map on each gig page. Closed to start with, so the page stays
 * quick and no map is fetched for someone who already knows the way.
 */
.venue {
  margin: 2rem 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}

.venue__head {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: 1rem 1.15rem;
  cursor: pointer;
  font-family: var(--f-display);
  font-size: 1rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bone);
  list-style: none;
  transition: background .2s, color .2s;
}
.venue__head::-webkit-details-marker { display: none; }
.venue__head:hover { background: rgba(168, 85, 247, .1); color: var(--violet-hi); }
.venue__head:focus-visible { outline: 2px solid var(--violet-hi); outline-offset: -2px; }

/* a chevron that turns over when the panel opens */
.venue__head::after {
  content: "";
  width: .55rem;
  height: .55rem;
  margin-left: auto;
  border-right: 2px solid var(--violet);
  border-bottom: 2px solid var(--violet);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .25s ease;
}
.venue[open] .venue__head::after { transform: rotate(-135deg) translate(-2px, -2px); }

.venue__body { padding: 0 1.15rem 1.15rem; }

/* photo of the place first, map second — you want to know what you are
   looking for before you know how to get there */
.venue__grid { display: grid; gap: 1rem; }

.venue__shot { margin: 0; }
.venue__shot img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
  border: 1px solid var(--line);
}

.venue__credit {
  margin-top: .55rem;
  font-size: .85rem;
  line-height: 1.5;
  color: var(--muted);
}
/* the licence line, quieter than the sentence it follows */
.venue__by { display: block; font-size: .78rem; opacity: .75; }
.venue__by a { color: inherit; }

.venue__map {
  aspect-ratio: 16 / 10;
  /* never taller than the screen it is being read on */
  max-height: calc(100svh - 220px);
  border-radius: 12px;
  overflow: hidden;
  background: #0d0716;
  border: 1px solid var(--line);
}
.venue__map { position: relative; }
.venue__frame { display: block; width: 100%; height: 100%; border: 0; }

/*
 * The map does not take a touch until it has been asked to. Scrolling a gig
 * page on a phone, the moment a thumb crosses an embedded map the map takes
 * the gesture and pans instead of the page — which reads as the page sliding
 * about. The cover intercepts that first touch; once tapped it goes for good.
 */
.venue__frame { pointer-events: none; }
.venue__map.is-live .venue__frame { pointer-events: auto; }

.venue__cover {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  padding: 0 0 .9rem;
  background: linear-gradient(180deg, transparent 55%, rgba(7, 4, 12, .55) 100%);
  border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.venue__cover span {
  padding: .45rem .9rem;
  background: rgba(7, 4, 12, .82);
  border: 1px solid var(--violet-dim);
  border-radius: 999px;
  color: var(--lilac);
  font-family: var(--f-display);
  font-size: .72rem;
  letter-spacing: .12em;
  text-transform: uppercase;
}
.venue__cover:hover span,
.venue__cover:focus-visible span { border-color: var(--violet); color: #fff; }

.venue__addr { margin: .9rem 0 1rem; color: var(--muted); font-size: .95rem; }

.venue__links {
  display: flex;
  flex-wrap: wrap;
  gap: .6rem;
  margin: 0;
}

@media (min-width: 720px) {
  .venue__grid { grid-template-columns: 1fr 1fr; gap: 1.25rem; }
  /*
   * The two columns stretch to the same height and the map fills whatever
   * that turns out to be, so the photo and the map are one pair of panels
   * rather than two things of different sizes sitting next to each other.
   */
  .venue__shot { display: flex; flex-direction: column; }
  .venue__credit { margin-top: auto; padding-top: .55rem; }
  .venue__map { aspect-ratio: auto; height: 100%; min-height: 220px; }
}

/* ---------------- Booking ---------------- */

.contact { display: grid; gap: .6rem; }
.contact__row {
  display: flex;
  flex-direction: column;
  gap: .1rem;
  padding: 1rem 1.25rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  transition: border-color .22s, background .22s;
}
.contact__row:hover, .contact__row:focus-visible {
  border-color: var(--violet);
  background: var(--panel-2);
}
.contact__k {
  font-family: var(--f-display);
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--muted);
}
.contact__v { color: var(--bone); word-break: break-word; }

/* ---------------- Call-outs at the foot of the home page ---------------- */

.sec--cta { text-align: center; }
/* the heading rule is left-aligned by default, so centre it in these */
.sec--cta .h2::after { margin-inline: auto; }

/* alternating grounds so the two call-outs read as separate bands */
.sec--sponsors { background: var(--ink); }
.sec--charities { background: var(--ink-2); }

.cta-links {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: center;
  margin: 0;
}

/* ---------------- Dedication ---------------- */

.dedication {
  padding: clamp(3rem, 10vw, 5rem) 0;
  text-align: center;
  background: var(--ink);
  border-top: 1px solid var(--line);
}
.dedication__text {
  margin: 0;
  font-family: var(--f-display);
  font-size: clamp(1.1rem, 4.5vw, 1.5rem);
  letter-spacing: .05em;
  color: var(--lilac);
}
.dedication__text strong { color: var(--bone); font-weight: 600; }
.dedication__sub {
  margin: .3rem 0 1.5rem;
  color: var(--muted);
  font-style: italic;
}
.dedication__motto {
  margin: 0;
  font-family: var(--f-display);
  font-size: .85rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--violet);
}

/* ---------------- Footer ---------------- */

.ftr {
  padding: 2.5rem 0 calc(2.5rem + env(safe-area-inset-bottom));
  background: #04020a;
  border-top: 1px solid var(--line);
  text-align: center;
}
.ftr__mark {
  width: 84px;
  height: auto;
  margin: 0 auto .85rem;
  opacity: .8;
}
.ftr__c { margin: 0; font-size: .82rem; color: var(--muted); }

/* ============================================================
   Larger screens
   ============================================================ */

@media (min-width: 700px) {
  .members { grid-template-columns: 1fr 1fr; gap: 1.25rem; }

  .grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
  .grid__item--wide { grid-column: span 2; }
  .grid__item--wide img { aspect-ratio: 16 / 9; }

  /* enough room for a grid, so the swipe row becomes one */
  .shots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 1rem;
    overflow: visible;
    margin-inline: 0;
    padding: 0;
  }
  .shot { flex: none; }

  .credits { grid-template-columns: 1fr 1fr; gap: .6rem 1.5rem; }

  /*
   * auto-fit rather than a fixed column count, so one reel sits centred
   * instead of stranded in the left-hand column, and more reels fill out
   * the row as they get added.
   */
  .reels {
    grid-template-columns: repeat(auto-fit, minmax(240px, 300px));
    justify-content: center;
    gap: 1.75rem;
  }

  .plans { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.25rem; }
  .sponsors { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1.25rem; }
  .charities { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.25rem; }
  .kits { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1.25rem; }

  /* a bio only becomes two columns once it actually has a photo in it */
  .bio:has(.bio__photo) {
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    align-items: start;
    gap: 2.5rem;
  }

  .gig { grid-template-columns: auto 1fr auto; }
  .gig__cta { grid-column: auto; margin-top: 0; }
}

/*
 * A long line needs more space beneath it: the wider the measure, the
 * further the eye has to travel back, and tight leading is where it lands
 * on the wrong line. This only applies once the page is actually wide.
 */
@media (min-width: 880px) {
  .story p,
  .story .lead,
  .bio__text p,
  .remember p { line-height: 1.8; }
}

/* ---- the memorial rows ---- */
@media (min-width: 880px) {
  /*
   * Kyle and Timmy each get a full-width row rather than a column apiece:
   * Kyle's photograph on the left with his words beside it, Timmy's on the
   * right with his words to its left. Two tall columns side by side left
   * both of them squeezed; a row lets each photograph be a decent size and
   * the writing sit next to the face it is about.
   */
  .memory__pair { gap: 4.5rem; }

  .remember {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    align-items: start;
    gap: clamp(2rem, 4vw, 3.5rem);
  }

  /*
   * Because the rows alternate, the two photographs no longer have to match
   * each other's height — so nothing is cropped. Both keep the shape they
   * were taken in.
   */
  .remember__photo { width: 100%; max-width: 360px; margin: 0; }

  /* Timmy's row runs the other way round */
  .remember--reverse { grid-template-columns: minmax(0, 1fr) minmax(0, 360px); }
  .remember--reverse .remember__photo { grid-area: 1 / 2; }
  .remember--reverse .remember__text  { grid-area: 1 / 1; }

  /* the name now heads the writing beside the photo, so it starts with it */
  .remember__name { text-align: left; font-size: 1.9rem; }

}

@media (min-width: 900px) {
  html { scroll-padding-top: 5.5rem; }

  .hdr__toggle { display: none; }

  .nav {
    position: static;
    width: auto;
    padding: 0;
    background: none;
    border: 0;
    flex-direction: row;
    gap: 1.1rem;
    transform: none;
    visibility: visible;
  }
  .nav a {
    padding: .35rem 0;
    font-size: .88rem;
    letter-spacing: .08em;
    white-space: nowrap;
    border-bottom: 2px solid transparent;
  }
  .nav a:hover { border-bottom-color: var(--violet); }

  .release {
    grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
    gap: 3rem;
    align-items: start;
  }
  .release__art { position: sticky; top: 6rem; }

  .band-shot img { max-height: 720px; object-fit: cover; object-position: center 30%; }
}

/* ============================================================
   Motion / contrast preferences
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .grid__item:hover img { transform: none; }
}
