/* ============================================================
   ACB DRUMMER, acbdrummer.com
   ============================================================ */

@font-face{
  font-family:'Archivo';
  src:url('assets/fonts/archivo-var-latin.woff2') format('woff2');
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2122,U+2212,U+FEFF,U+FFFD;
}
@font-face{
  font-family:'Archivo';
  src:url('assets/fonts/archivo-var-latin-ext.woff2') format('woff2');
  font-weight:100 900; font-stretch:62% 125%; font-style:normal; font-display:swap;
  unicode-range:U+0100-02BA,U+02BD-02C5,U+1E00-1E9F,U+2020,U+20A0-20AB,U+2C60-2C7F,U+A720-A7FF;
}
@font-face{
  font-family:'Plex Mono';
  src:url('assets/fonts/ibm-plex-mono-400.woff2') format('woff2');
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:'Plex Mono';
  src:url('assets/fonts/ibm-plex-mono-500.woff2') format('woff2');
  font-weight:500; font-style:normal; font-display:swap;
}

:root{
  --ink:#0A0A0C;
  --ink-2:#121217;
  --bone:#F4F1EA;
  --bone-60:rgba(244,241,234,.60);
  --bone-38:rgba(244,241,234,.38);
  --rule:rgba(244,241,234,.13);
  --accent:#F0C03C;    /* warm stage gold, sampled from her own lighting */
  --beam:115,177,232;   /* #73B1E8, the stage beam */
  --wash:47,93,142;     /* #2F5D8E, the mid stage blue */
  --slate:150,156,146;  /* neutral, faintly green: the unlit venue grey, measured off the photo */

  --display:'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --mono:'Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  --gut:clamp(20px, 5vw, 76px);
  --bleed:calc((100vw - min(100vw, 1560px)) / 2 + var(--gut));
  --max:1560px;

  --t:cubic-bezier(.2,.7,.25,1);

  /* Tempo. Every duration on this page is a note value at her band's tempo,
     so the whole site moves in time rather than to arbitrary numbers.

     140 BPM is measured, not picked: an onset envelope and comb filter run
     over The Laurettes' "Mainstage", the title track of the album this page
     links to, which holds 140.0 to within 0.2 BPM across every 30 second
     window of its four minutes. Click tracked, so there is a real grid.

     Change --bpm and the entire site re-tempos. */
  --bpm:140;
  --beat:calc(60s / var(--bpm));   /* quarter, 428.6ms */
  --n16:calc(var(--beat) / 4);     /* 107.1ms */
  --n8:calc(var(--beat) / 2);      /* 214.3ms */
  --n8d:calc(var(--beat) * .75);   /* dotted eighth, 321.4ms */
  --n4d:calc(var(--beat) * 1.5);   /* dotted quarter, 642.9ms */
  --n2:calc(var(--beat) * 2);      /* half, 857.1ms */
  --bar:calc(var(--beat) * 4);     /* one bar of 4/4, 1714.3ms */
}

*,*::before,*::after{box-sizing:border-box}
/* clip, not hidden. The lighting layers are deliberately two viewports wide
   so their own edges are never on screen, and that overflow has to be cut off
   or the page scrolls sideways into the empty half. overflow-x:hidden on body
   alone does not do it while html is visible; the overflow propagates up and
   the viewport scrolls anyway. clip cuts it without making html a scroll
   container, which would break the fixed playhead and top bar. */
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; overflow-x:clip }
@media (prefers-reduced-motion:reduce){ html{scroll-behavior:auto} }

html{background:var(--ink)}

body{
  margin:0;
  background-color:transparent;
  background-repeat:no-repeat;
  background-size:100% 100%;
  background-image:
    /* carries the hero light down past the fold */
    radial-gradient(88% 14% at 90% 19%,
      rgba(var(--wash),.172), rgba(var(--wash),.126) 20%, rgba(var(--wash),.086) 36%,
      rgba(var(--wash),.053) 52%, rgba(var(--wash),.027) 68%, rgba(var(--wash),.009) 84%,
      rgba(var(--wash),0) 95%),
    /* intro and watch, from the right */
    radial-gradient(112% 15% at 88% 30%,
      rgba(var(--wash),.106), rgba(var(--wash),.077) 21%, rgba(var(--wash),.052) 38%,
      rgba(var(--wash),.031) 54%, rgba(var(--wash),.015) 70%, rgba(var(--wash),.005) 85%,
      rgba(var(--wash),0) 95%),
    /* the laurettes, from the left */
    radial-gradient(102% 14% at 13% 52%,
      rgba(var(--wash),.090), rgba(var(--wash),.065) 22%, rgba(var(--wash),.043) 39%,
      rgba(var(--wash),.026) 55%, rgba(var(--wash),.012) 71%, rgba(var(--wash),.004) 86%,
      rgba(var(--wash),0) 96%),
    /* stages, barely there so the photographs carry the colour */
    radial-gradient(96% 12% at 86% 72%,
      rgba(var(--wash),.056), rgba(var(--wash),.040) 22%, rgba(var(--wash),.026) 40%,
      rgba(var(--wash),.015) 56%, rgba(var(--wash),.007) 72%, rgba(var(--wash),.002) 86%,
      rgba(var(--wash),0) 96%),
    /* contact, swelling back up to bookend the hero */
    radial-gradient(128% 16% at 42% 96%,
      rgba(var(--wash),.152), rgba(var(--wash),.110) 21%, rgba(var(--wash),.074) 37%,
      rgba(var(--wash),.045) 53%, rgba(var(--wash),.022) 69%, rgba(var(--wash),.007) 85%,
      rgba(var(--wash),0) 95%);
  color:var(--bone);
  font-family:var(--display);
  font-size:clamp(15px,.55vw + 13px,17px);
  line-height:1.55;
  font-synthesis:none;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

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

/* A drumstick for the arrow, the normal hand for anything clickable.
   Gated on a real pointing device so touch screens and anyone using an
   enlarged system cursor for accessibility are left alone. */
@media (hover:hover) and (pointer:fine){
  body{
    cursor:url('assets/img/cursor-stick.png?v=485100') 13 13, auto;
    cursor:image-set(url('assets/img/cursor-stick.png?v=485100') 1x,
                     url('assets/img/cursor-stick@2x.png?v=485100') 2x) 13 13, auto;
  }
  /* On contact the stick pivots at the butt, where a hand would hold it, and
     the tip drops 5px. The butt stays exactly where it was. Pivoting at the
     bead instead pins the tip and waves the back end about, which does not
     read as hitting anything.
     app.js holds this for one sixteenth note; a real mouse click is over in
     less time than the eye can catch. Both states come out of
     tools/draw-cursor.py, so they cannot drift apart. */
  .striking body{
    /* Bump the ?v here and you must bump the preload in index.html to match,
       or the preload warms a URL nothing asks for and the first click goes
       back to showing the arrow. */
    cursor:url('assets/img/cursor-hit.png?v=485100') 13 13, auto;
    cursor:image-set(url('assets/img/cursor-hit.png?v=485100') 1x,
                     url('assets/img/cursor-hit@2x.png?v=485100') 2x) 13 13, auto;
  }
  a, button, summary, label, [role="button"], .tiles a, .player__btn,
  .lb__nav, .lb__close{cursor:pointer}
  input, textarea, select{cursor:auto}
  .tiles{cursor:grab}
  .tiles:active,.tiles.is-dragging{cursor:grabbing}
}

/* ---------- metronome playhead ---------- */
/* The grain that dithers every gradient on the page, and the reason there is
   no banding across the wash. It is fixed and blended, which is right on a
   desktop and wrong on a phone:

   Being fixed, it does not scroll with the photographs. On a desktop that is
   invisible. During momentum scrolling on a phone the blended layer composites
   separately and lags behind the content, so the picture appears to slide
   underneath a stationary texture, which reads as parallax on the images and
   as the light not covering them. A full viewport mix-blend-mode recomposited
   every frame is also about the most expensive thing you can ask mobile Safari
   to do while scrolling, which is the other half of the same problem.

   So it is gated on a real pointing device. The dither buys nothing on a phone
   anyway: banding is a function of how many screen pixels a gradient is spread
   across, and at three times the density on a small screen there is nothing
   left to band. */
@media (hover:hover) and (pointer:fine){
  body::after{
    content:''; position:fixed; inset:0; z-index:40; pointer-events:none;
    background-image:url('assets/img/grain.png');
    background-size:160px 160px;
    opacity:.05;
    mix-blend-mode:overlay;
  }
}

/* The rail is a count-in: a fine ruler of subdivisions with a heavier mark on
   each section, so the gold crosses a beat exactly as a section arrives.
   The section marks are irregular by nature, so app.js writes them as a stop
   list into --ticks and keeps them true through resize and reflow.

   The rail is drawn twice. The lower copy shows on the unplayed track; the
   upper copy is clipped by the bar, which is what carries the same marks
   across the gold in ink rather than letting the fill swallow them. Both read
   currentColor for the section marks, so one stop list serves both.

   Height and tick length are what make this legible rather than merely
   correct. At 3px tall with every tick full height, a section mark is a 2px
   dot among forty identical ones and announces nothing. The subdivisions are
   short and hang off the bottom edge; the section marks run the full height.
   Major against minor is the whole point, so do not level them up. */
.playhead{
  position:fixed; inset:0 0 auto 0; height:7px; z-index:60;
  background:var(--ink);
  overflow:hidden;
}
.playhead__rail{
  position:absolute; top:0; left:0; height:100%;
  width:var(--ph-w, 100vw);
  background-image:
    var(--ticks, none),
    repeating-linear-gradient(90deg, var(--ruler) 0 1px, transparent 1px 40px);
  background-repeat:no-repeat, repeat-x;
  background-size:100% 100%, 100% 3px;
  background-position:0 0, 0 100%;
}
.playhead__rail--unplayed{
  --ruler:var(--rule);
  color:rgba(244,241,234,.52);
}
.playhead__rail--played{
  --ruler:rgba(10,10,12,.26);
  color:rgba(10,10,12,.78);
}
.playhead__bar{
  position:absolute; top:0; left:0; height:100%; width:0%;
  background:var(--accent);
  overflow:hidden;
  transition:width var(--n16) linear;
}

/* ---------- top bar ---------- */
.topbar{
  position:fixed; top:7px; left:0; right:0; z-index:50;
  display:flex; align-items:center; justify-content:space-between;
  padding:12px var(--gut);
  font-family:var(--mono);
  font-size:11px; letter-spacing:.16em; text-transform:uppercase;
}
.topbar__mark{display:block; line-height:0; text-decoration:none}
/* Up about 30% from where it started. It carries three letters now, and at the
   old size they were technically legible and practically not worth reading. */
.crest{width:auto; height:clamp(44px,4vw,57px); display:block; color:var(--bone)}
/* Plain white, and no difference blend. The blend was there to guarantee
   contrast over the photograph, but checked against every backdrop these links
   actually pass over, from the hero through to the lit truss at the bottom,
   plain white reads better on all of them. All the blend was doing was
   stopping the links ever reaching white, worst exactly where the background
   brightened. Hover goes to the accent, since white can no longer brighten. */
.topbar__nav{display:flex; gap:clamp(14px,2.4vw,30px)}
.topbar__nav a{
  text-decoration:none; color:var(--bone);
  transition:color var(--n8) var(--t);
}
.topbar__nav a:hover{color:var(--accent)}

/* ---------- shared type ---------- */
.eyebrow{
  margin:0 0 clamp(18px,3vw,34px);
  display:flex; align-items:center; gap:14px;
  font-family:var(--mono); font-size:11px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--bone-60);
}
.eyebrow .dot{width:5px; height:5px; background:var(--accent); flex:none}

/* BIRTHDAY BANNER. Delete this block and the .bday markup in index.html to
   remove it.

   One line, at a size that reads as an occasion rather than as a label parked
   above the name. No date, no rule, no confetti, no bunting. She is reading it
   on the day, so the date was telling her something she already knew, and a
   date stamp makes a greeting look like a record. */
.bday{
  margin:0 0 clamp(16px,2.2vw,26px);
  color:var(--accent);
  font-weight:700; font-stretch:108%;
  font-size:clamp(1.15rem,2.05vw,1.85rem);
  letter-spacing:-.012em; line-height:1.1;
}

/* The small section labels, Intro, Watch, The Laurettes, Stages, Contact, were
   removed: the headline or the content under each one already said what it
   was, so they were a caption on a caption. The rule is kept because it costs
   nothing and putting one back is then a single line of markup. */
.rubric{
  margin:0 0 clamp(26px,4vw,52px);
  font-family:var(--mono); font-size:11px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--bone-38);
}

.statement{
  margin:0;
  font-weight:700; font-stretch:112%;
  font-size:clamp(2rem,4.6vw,4.1rem);
  line-height:1.02; letter-spacing:-.025em;
  text-wrap:balance;
}
.statement--sm{font-size:clamp(1.7rem,3.2vw,2.9rem)}

/* ---------- section shell ---------- */
.band{
  max-width:var(--max); margin-inline:auto;
  padding:clamp(64px,9vw,140px) var(--gut);
}
.band--rule{border-top:1px solid var(--rule)}

/* ============================================================
   HERO
   ============================================================ */
.hero{
  position:relative;
  max-width:var(--max); margin-inline:auto;
  padding:clamp(92px,12vh,132px) var(--gut) clamp(44px,6vw,76px);
  display:grid; grid-template-columns:minmax(0,1fr) clamp(220px,26vw,392px);
  gap:clamp(28px,4vw,56px); align-items:center;
  min-height:100svh;
  isolation:isolate;
}
.hero__type{container-type:inline-size}
.hero__figure{position:relative; z-index:1}
/* Above the photograph: once the figure is grown its left edge reaches further
   across, and the type has to stay on top of it. */
.hero__type{position:relative; z-index:2}

/* Our own spotlight. Struck from the same place as the one in the photograph,
   thrown down and to the left, and deliberately longer than the hero so the
   light crosses into the next section instead of stopping at its edge. */
.spot{
  position:absolute; z-index:0; pointer-events:none;
  top:0; left:50%; width:200vw; transform:translateX(-50%);
  height:206%;
  background:linear-gradient(137deg,
    rgba(var(--beam),0) 52.72%,
    rgba(var(--beam),.042) 55.43%,
    rgba(var(--beam),.128) 57.81%,
    rgba(var(--beam),.216) 59.85%,
    rgba(var(--beam),.238) 60.86%,
    rgba(var(--beam),.190) 62.22%,
    rgba(var(--beam),.112) 64.26%,
    rgba(var(--beam),.056) 66.98%,
    rgba(var(--beam),.022) 71.05%,
    rgba(var(--beam),.006) 75.81%,
    rgba(var(--beam),0) 79.87%);
  -webkit-mask-image:linear-gradient(47deg,
    rgba(0,0,0,0) 16.83%, rgba(0,0,0,.07) 24.13%, rgba(0,0,0,.21) 31.43%, rgba(0,0,0,.40) 38.73%,
    rgba(0,0,0,.60) 46.02%, rgba(0,0,0,.77) 53.32%, rgba(0,0,0,.90) 60.62%, rgba(0,0,0,.97) 67.92%,
    #000 74.56%),
    linear-gradient(to bottom, #000 58%, rgba(0,0,0,.70) 76%, rgba(0,0,0,.30) 90%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(47deg,
    rgba(0,0,0,0) 16.83%, rgba(0,0,0,.07) 24.13%, rgba(0,0,0,.21) 31.43%, rgba(0,0,0,.40) 38.73%,
    rgba(0,0,0,.60) 46.02%, rgba(0,0,0,.77) 53.32%, rgba(0,0,0,.90) 60.62%, rgba(0,0,0,.97) 67.92%,
    #000 74.56%),
    linear-gradient(to bottom, #000 58%, rgba(0,0,0,.70) 76%, rgba(0,0,0,.30) 90%, rgba(0,0,0,0) 100%);
  /* The second mask layer is what stops the light being cut off at the bottom
     of its own box. The 47deg layer reaches full opacity and stays there, so
     without this the element ended at full strength and drew a hard line
     straight across the page at exactly its own bottom edge. Rotating it for
     the rig turned that into a moving line, which is what made a long-standing
     flaw impossible to miss. Every other lighting layer here already fades out
     at its own bottom for this reason; this one never did.
     Both mask layers must intersect, or the fade does nothing. */
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* The photograph is lit from the upper right: a hard beam edge running down at
   43 degrees, unlit venue grey above it. Both are continued off the image so
   the page is lit by the same light and the photo has no findable edge. */
.hero::after{
  content:''; position:absolute; z-index:0; pointer-events:none;
  top:-16%; bottom:-14%; left:50%; width:100vw; transform:translateX(-50%);
  background:
   /* the bright shaft itself, thrown out of the photo across the page */
   linear-gradient(137deg,
    rgba(var(--beam),0) 55%,
    rgba(var(--beam),.048) 60%,
    rgba(var(--beam),.098) 64%,
    rgba(var(--beam),.112) 66%,
    rgba(var(--beam),.070) 70%,
    rgba(var(--beam),.030) 76%,
    rgba(var(--beam),.008) 84%,
    rgba(var(--beam),0) 91%),
   /* the division between lit and unlit, carried off the photograph */
   linear-gradient(137deg,
    rgba(var(--slate),0) 2%,
    rgba(var(--slate),.052) 20%,
    rgba(var(--slate),.118) 38%,
    rgba(var(--slate),.176) 51%,
    rgba(var(--slate),.205) 60.5%,
    rgba(var(--beam),.112) 64.5%,
    rgba(var(--beam),.082) 71%,
    rgba(var(--beam),.052) 79%,
    rgba(var(--beam),.024) 88%,
    rgba(var(--beam),0) 97%);
  -webkit-mask-image:linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 24%, rgba(0,0,0,.14) 36%, rgba(0,0,0,.32) 47%,
    rgba(0,0,0,.55) 57%, rgba(0,0,0,.76) 67%, rgba(0,0,0,.90) 77%, rgba(0,0,0,.97) 87%,
    #000 94%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 24%, rgba(0,0,0,.14) 36%, rgba(0,0,0,.32) 47%,
    rgba(0,0,0,.55) 57%, rgba(0,0,0,.76) 67%, rgba(0,0,0,.90) 77%, rgba(0,0,0,.97) 87%,
    #000 94%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* the beam spilling out of the photo and into the page */
.hero::before{
  content:''; position:absolute; z-index:0; pointer-events:none;
  top:-16%; bottom:-14%; left:50%; width:100vw; transform:translateX(-50%);
  background:
    radial-gradient(620px 520px at calc(100% - 230px) 25%,
      rgba(var(--beam),.175), rgba(var(--beam),.123) 24%, rgba(var(--beam),.078) 42%,
      rgba(var(--beam),.042) 59%, rgba(var(--beam),.016) 77%, rgba(var(--beam),0) 93%),
    radial-gradient(1150px 880px at calc(100% - 330px) 45%,
      rgba(var(--wash),.250), rgba(var(--wash),.178) 20%, rgba(var(--wash),.119) 36%,
      rgba(var(--wash),.072) 51%, rgba(var(--wash),.038) 66%, rgba(var(--wash),.013) 81%,
      rgba(var(--wash),0) 94%);
  -webkit-mask-image:linear-gradient(137deg,
    rgba(0,0,0,0) 44%, rgba(0,0,0,.06) 53%, rgba(0,0,0,.24) 59%, rgba(0,0,0,.54) 64%,
    rgba(0,0,0,.80) 70%, rgba(0,0,0,.95) 77%, #000 85%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(137deg,
    rgba(0,0,0,0) 44%, rgba(0,0,0,.06) 53%, rgba(0,0,0,.24) 59%, rgba(0,0,0,.54) 64%,
    rgba(0,0,0,.80) 70%, rgba(0,0,0,.95) 77%, #000 85%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}


.wordmark{
  margin:0;
  display:flex; flex-direction:column;
  font-weight:800; font-stretch:125%;
  font-size:clamp(2.8rem,15.4cqw,11rem);
  line-height:.84; letter-spacing:-.045em;
  text-transform:uppercase;
}
.wordmark span{width:-moz-fit-content; width:fit-content; white-space:nowrap}

/* ACB carries the cloth. The outline is a true outer edge, not a centred one:
   this element paints the stroke at double weight and ::before lays the tartan
   fill over it, so only the outer half survives. -webkit-text-stroke alone eats
   into the letterform and goes soft on curves, which is what made the previous
   lockup read as amateur. */
/* ACB: solid, no outline. It is the name, it should not need a device. */
/* ACB carries the cloth. The keyline is deliberately hairline-fine: the element
   paints a stroke at double weight and ::before lays the tartan over the inner
   half, so only a sliver of outer edge survives. That gives a true outer edge
   with the letterform intact. A plain -webkit-text-stroke centres on the glyph
   path, eating the counters and going soft on curves, which reads as amateur. */
.wordmark__acb{
  position:relative;
  color:transparent;
}
.wordmark__acb::before{
  content:attr(data-text);
  position:absolute; left:0; top:0;
  -webkit-text-stroke:0;
  color:transparent;
  background-image:url('assets/img/tartan.png');
  background-repeat:repeat;
  background-size:clamp(46px,5.4vw,86px) clamp(46px,5.4vw,86px);
  -webkit-background-clip:text; background-clip:text;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .wordmark__acb{color:var(--bone); -webkit-text-stroke:0}
  .wordmark__acb::before{content:none}
}

/* DRUMMER: solid, and letterspaced by JS to sit flush with the width of ACB. */
.wordmark__sub{
  color:var(--bone);
  font-size:.34em;         /* JS refines this so the ink width matches ACB exactly */
  font-weight:800; font-stretch:118%;
  line-height:1.28;
  padding-top:.06em;
}

/* One quiet line of facts, then one row of actions. The old label/value table
   read as a spec sheet, and putting buttons in it wrecked the row rhythm. */
.hero__name{
  margin:clamp(20px,2.6vw,30px) 0 0;
  display:flex; flex-wrap:wrap; align-items:center;
  gap:clamp(14px,1.9vw,26px);
  font-family:var(--mono); font-size:clamp(11px,.82vw,13px);
  letter-spacing:.22em; text-transform:uppercase; color:var(--bone-60);
}
.hero__name span:first-child{color:var(--bone)}
.hero__name span + span{display:flex; align-items:center; gap:clamp(14px,1.9vw,26px)}
.hero__name span + span::before{
  content:''; width:1px; height:1.05em; flex:none;
  background:rgba(244,241,234,.24);
}

.hero__actions{
  margin:clamp(26px,3.4vw,40px) 0 0;
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:clamp(18px,2.6vw,34px) clamp(22px,3vw,40px);
}
.hero__socials{display:flex; flex-wrap:wrap; gap:8px}

.hero__cta{
  display:inline-block;
  font-weight:700; font-stretch:112%;
  font-size:clamp(1.1rem,1.7vw,1.55rem);
  letter-spacing:-.012em; text-decoration:none;
}
.hero__cta span{
  background-image:linear-gradient(var(--accent),var(--accent));
  background-size:100% 2px; background-repeat:no-repeat; background-position:0 100%;
  padding-bottom:5px;
  transition:background-size var(--beat) var(--t), color var(--n8d) var(--t);
}
.hero__cta:hover span{background-size:100% 100%; color:var(--ink)}

.hero__figure{position:relative; z-index:1}

/* Our own spotlight. Struck from the same place as the one in the photograph,
   thrown down and to the left, and deliberately longer than the hero so the
   light crosses into the next section instead of stopping at its edge. */
.spot{
  position:absolute; z-index:0; pointer-events:none;
  top:0; left:50%; width:200vw; transform:translateX(-50%);
  height:206%;
  background:linear-gradient(137deg,
    rgba(var(--beam),0) 52.72%,
    rgba(var(--beam),.042) 55.43%,
    rgba(var(--beam),.128) 57.81%,
    rgba(var(--beam),.216) 59.85%,
    rgba(var(--beam),.238) 60.86%,
    rgba(var(--beam),.190) 62.22%,
    rgba(var(--beam),.112) 64.26%,
    rgba(var(--beam),.056) 66.98%,
    rgba(var(--beam),.022) 71.05%,
    rgba(var(--beam),.006) 75.81%,
    rgba(var(--beam),0) 79.87%);
  -webkit-mask-image:linear-gradient(47deg,
    rgba(0,0,0,0) 16.83%, rgba(0,0,0,.07) 24.13%, rgba(0,0,0,.21) 31.43%, rgba(0,0,0,.40) 38.73%,
    rgba(0,0,0,.60) 46.02%, rgba(0,0,0,.77) 53.32%, rgba(0,0,0,.90) 60.62%, rgba(0,0,0,.97) 67.92%,
    #000 74.56%),
    linear-gradient(to bottom, #000 58%, rgba(0,0,0,.70) 76%, rgba(0,0,0,.30) 90%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(47deg,
    rgba(0,0,0,0) 16.83%, rgba(0,0,0,.07) 24.13%, rgba(0,0,0,.21) 31.43%, rgba(0,0,0,.40) 38.73%,
    rgba(0,0,0,.60) 46.02%, rgba(0,0,0,.77) 53.32%, rgba(0,0,0,.90) 60.62%, rgba(0,0,0,.97) 67.92%,
    #000 74.56%),
    linear-gradient(to bottom, #000 58%, rgba(0,0,0,.70) 76%, rgba(0,0,0,.30) 90%, rgba(0,0,0,0) 100%);
  /* The second mask layer is what stops the light being cut off at the bottom
     of its own box. The 47deg layer reaches full opacity and stays there, so
     without this the element ended at full strength and drew a hard line
     straight across the page at exactly its own bottom edge. Rotating it for
     the rig turned that into a moving line, which is what made a long-standing
     flaw impossible to miss. Every other lighting layer here already fades out
     at its own bottom for this reason; this one never did.
     Both mask layers must intersect, or the fade does nothing. */
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* The photograph is lit from the upper right: a hard beam edge running down at
   43 degrees, unlit venue grey above it. Both are continued off the image so
   the page is lit by the same light and the photo has no findable edge. */
.hero::after{
  content:''; position:absolute; z-index:0; pointer-events:none;
  top:-16%; bottom:-14%; left:50%; width:100vw; transform:translateX(-50%);
  background:
   /* the bright shaft itself, thrown out of the photo across the page */
   linear-gradient(137deg,
    rgba(var(--beam),0) 55%,
    rgba(var(--beam),.048) 60%,
    rgba(var(--beam),.098) 64%,
    rgba(var(--beam),.112) 66%,
    rgba(var(--beam),.070) 70%,
    rgba(var(--beam),.030) 76%,
    rgba(var(--beam),.008) 84%,
    rgba(var(--beam),0) 91%),
   /* the division between lit and unlit, carried off the photograph */
   linear-gradient(137deg,
    rgba(var(--slate),0) 2%,
    rgba(var(--slate),.052) 20%,
    rgba(var(--slate),.118) 38%,
    rgba(var(--slate),.176) 51%,
    rgba(var(--slate),.205) 60.5%,
    rgba(var(--beam),.112) 64.5%,
    rgba(var(--beam),.082) 71%,
    rgba(var(--beam),.052) 79%,
    rgba(var(--beam),.024) 88%,
    rgba(var(--beam),0) 97%);
  -webkit-mask-image:linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 24%, rgba(0,0,0,.14) 36%, rgba(0,0,0,.32) 47%,
    rgba(0,0,0,.55) 57%, rgba(0,0,0,.76) 67%, rgba(0,0,0,.90) 77%, rgba(0,0,0,.97) 87%,
    #000 94%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.04) 24%, rgba(0,0,0,.14) 36%, rgba(0,0,0,.32) 47%,
    rgba(0,0,0,.55) 57%, rgba(0,0,0,.76) 67%, rgba(0,0,0,.90) 77%, rgba(0,0,0,.97) 87%,
    #000 94%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* the beam spilling out of the photo and into the page */
.hero::before{
  content:''; position:absolute; z-index:0; pointer-events:none;
  top:-16%; bottom:-14%; left:50%; width:100vw; transform:translateX(-50%);
  background:
    radial-gradient(620px 520px at calc(100% - 230px) 25%,
      rgba(var(--beam),.175), rgba(var(--beam),.123) 24%, rgba(var(--beam),.078) 42%,
      rgba(var(--beam),.042) 59%, rgba(var(--beam),.016) 77%, rgba(var(--beam),0) 93%),
    radial-gradient(1150px 880px at calc(100% - 330px) 45%,
      rgba(var(--wash),.250), rgba(var(--wash),.178) 20%, rgba(var(--wash),.119) 36%,
      rgba(var(--wash),.072) 51%, rgba(var(--wash),.038) 66%, rgba(var(--wash),.013) 81%,
      rgba(var(--wash),0) 94%);
  -webkit-mask-image:linear-gradient(137deg,
    rgba(0,0,0,0) 44%, rgba(0,0,0,.06) 53%, rgba(0,0,0,.24) 59%, rgba(0,0,0,.54) 64%,
    rgba(0,0,0,.80) 70%, rgba(0,0,0,.95) 77%, #000 85%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(137deg,
    rgba(0,0,0,0) 44%, rgba(0,0,0,.06) 53%, rgba(0,0,0,.24) 59%, rgba(0,0,0,.54) 64%,
    rgba(0,0,0,.80) 70%, rgba(0,0,0,.95) 77%, #000 85%), linear-gradient(to bottom,
    #000 54%, rgba(0,0,0,.86) 68%, rgba(0,0,0,.58) 80%, rgba(0,0,0,.30) 89%,
    rgba(0,0,0,.10) 96%, rgba(0,0,0,0) 100%);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}


.wordmark{
  margin:0;
  display:flex; flex-direction:column;
  font-weight:800; font-stretch:125%;
  font-size:clamp(2.8rem,15.4cqw,11rem);
  line-height:.84; letter-spacing:-.045em;
  text-transform:uppercase;
}
.wordmark span{width:-moz-fit-content; width:fit-content; white-space:nowrap}

/* ACB carries the cloth. The outline is a true outer edge, not a centred one:
   this element paints the stroke at double weight and ::before lays the tartan
   fill over it, so only the outer half survives. -webkit-text-stroke alone eats
   into the letterform and goes soft on curves, which is what made the previous
   lockup read as amateur. */
/* ACB: solid, no outline. It is the name, it should not need a device. */
/* ACB carries the cloth. The keyline is deliberately hairline-fine: the element
   paints a stroke at double weight and ::before lays the tartan over the inner
   half, so only a sliver of outer edge survives. That gives a true outer edge
   with the letterform intact. A plain -webkit-text-stroke centres on the glyph
   path, eating the counters and going soft on curves, which reads as amateur. */
.wordmark__acb{
  position:relative;
  color:transparent;
}
.wordmark__acb::before{
  content:attr(data-text);
  position:absolute; left:0; top:0;
  -webkit-text-stroke:0;
  color:transparent;
  background-image:url('assets/img/tartan.png');
  background-repeat:repeat;
  background-size:clamp(46px,5.4vw,86px) clamp(46px,5.4vw,86px);
  -webkit-background-clip:text; background-clip:text;
}
@supports not ((-webkit-background-clip:text) or (background-clip:text)){
  .wordmark__acb{color:var(--bone); -webkit-text-stroke:0}
  .wordmark__acb::before{content:none}
}

/* DRUMMER: solid, and letterspaced by JS to sit flush with the width of ACB. */
.wordmark__sub{
  color:var(--bone);
  font-size:.34em;         /* JS refines this so the ink width matches ACB exactly */
  font-weight:800; font-stretch:118%;
  line-height:1.28;
  padding-top:.06em;
}

.hero__name{
  margin:clamp(14px,1.9vw,20px) 0 0;
  font-family:var(--mono); font-size:clamp(12px,1vw,14px);
  letter-spacing:.24em; text-transform:uppercase; color:var(--bone-60);
}







.hero__cta{
  display:inline-block; margin-top:clamp(22px,2.8vw,32px);
  font-weight:700; font-stretch:112%;
  font-size:clamp(1.05rem,1.6vw,1.45rem);
  letter-spacing:-.01em; text-decoration:none;
}
.hero__cta span{
  background-image:linear-gradient(var(--accent),var(--accent));
  background-size:100% 2px; background-repeat:no-repeat; background-position:0 100%;
  padding-bottom:5px;
  transition:background-size var(--beat) var(--t), color var(--n8d) var(--t);
}
.hero__cta:hover span{background-size:100% 100%; color:var(--ink)}

/* Grown by pulling its top left corner out, bottom right pinned, so she gets
   bigger and rises until the top of her head sits on the cap line of ACB.

   A transform rather than negative margins, deliberately. The mask below lives
   in this element's own coordinate space, so it scales with the photograph and
   the fade stays exactly where it was in the picture. Grow the box instead and
   the fade keeps the old geometry, which puts a hard edge through her.

   app.js solves the real figure, because one fixed number only lands at one
   window size: ACB is a fixed height block centred in a row that grows with
   the viewport, so it drifts down as the window gets taller while this hangs
   from the row's top edge. 1.19 is the answer at 1644x830 and stands here as
   the no-JS fallback, which is the same deal the lockup above already has. */
.hero__figure{
  margin:0 calc(var(--bleed) * -1) 0 0; align-self:stretch;
  position:relative; overflow:hidden;
  min-height:min(74svh,620px);
  transform-origin:100% 100%;
  transform:scale(1.19);
  --edge:
    linear-gradient(to right,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.06) 7%, rgba(0,0,0,.17) 14%, rgba(0,0,0,.33) 22%,
      rgba(0,0,0,.52) 30%, rgba(0,0,0,.70) 38%, rgba(0,0,0,.85) 46%, rgba(0,0,0,.95) 54%,
      #000 62%),
    linear-gradient(to bottom,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.42) 6%, rgba(0,0,0,.84) 13%, #000 20%,
      #000 78%, rgba(0,0,0,.86) 88%, rgba(0,0,0,.40) 95%, rgba(0,0,0,0) 100%);
  -webkit-mask-image:var(--edge); mask-image:var(--edge);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}
.hero__figure img{
  position:absolute; inset:0;
  width:100%; height:100%; object-fit:cover; object-position:58% 28%;
}


/* ============================================================
   01 INTRO
   ============================================================ */
.intro{
  display:grid; grid-template-columns:minmax(0,1fr);
  align-items:center;
}
/* The photograph's left edge is already masked to nothing, so the copy can sit
   over it rather than beside it. That removes the dead column a short piece of
   text leaves next to a tall image, and lets the image run bigger. */
.intro__text{
  grid-area:1 / 1; position:relative; z-index:1;
  max-width:min(44ch, 50%);
}
.intro__text .rubric{margin-bottom:clamp(18px,2.4vw,28px)}
.intro .statement{max-width:12ch; font-size:clamp(2rem,5.2vw,4.7rem); line-height:.97; letter-spacing:-.03em}
.intro__body{
  max-width:44ch;
  margin-top:clamp(22px,2.6vw,32px);
  color:var(--bone-60);
}
.intro__body p{margin:0 0 1em}
.intro__figure{
  grid-area:1 / 1; justify-self:end; margin:0 calc(var(--bleed) * -1) 0 0;
  width:min(70%, 1040px);
}
.intro__figure{position:relative}
.intro__figure img{
  width:100%; aspect-ratio:5/4; object-fit:cover; object-position:50% 40%;
  filter:contrast(1.04) saturate(1.02);
  /* Five falloffs, intersected. The diagonal follows the beam; the four edges
     let the photograph sit down into the dark instead of ending on a line.
     Generous and asymmetric on purpose: a tight, even ring is a vignette. */
  --diag:linear-gradient(137deg,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.07) 9%, rgba(0,0,0,.19) 18%, rgba(0,0,0,.36) 27%,
    rgba(0,0,0,.55) 36%, rgba(0,0,0,.73) 45%, rgba(0,0,0,.87) 54%, rgba(0,0,0,.96) 63%,
    #000 72%);
  --left:linear-gradient(to right,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.20) 5%, rgba(0,0,0,.48) 10%, rgba(0,0,0,.74) 15%,
    rgba(0,0,0,.90) 20%, rgba(0,0,0,.98) 25%, #000 30%);
  --right:linear-gradient(to left,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.22) 4%, rgba(0,0,0,.52) 9%, rgba(0,0,0,.78) 14%,
    rgba(0,0,0,.92) 19%, rgba(0,0,0,.99) 24%, #000 29%);
  --bottom:linear-gradient(to top,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.16) 4%, rgba(0,0,0,.40) 9%, rgba(0,0,0,.66) 14%,
    rgba(0,0,0,.85) 19%, rgba(0,0,0,.95) 24%, #000 30%);
  --top:linear-gradient(to bottom,
    rgba(0,0,0,0) 0%, rgba(0,0,0,.30) 3%, rgba(0,0,0,.66) 7%, rgba(0,0,0,.88) 11%,
    rgba(0,0,0,.97) 15%, #000 19%);
  -webkit-mask-image:var(--diag), var(--left), var(--right), var(--bottom), var(--top);
  mask-image:var(--diag), var(--left), var(--right), var(--bottom), var(--top);
  -webkit-mask-composite:source-in; mask-composite:intersect;
}

/* ============================================================
   02 WATCH
   ============================================================ */
.watch{display:grid; grid-template-columns:minmax(0,1fr); gap:clamp(34px,4.6vw,58px)}
.watch__lead{max-width:none}

.stats{
  list-style:none; margin:clamp(28px,3.6vw,44px) 0 0; padding:0;
  display:grid; grid-template-columns:repeat(3,minmax(0,1fr));
  max-width:640px;
  border-top:1px solid var(--rule); border-bottom:1px solid var(--rule);
}
.stats li{
  padding:clamp(18px,2.6vw,26px) 14px clamp(18px,2.6vw,26px) 0;
  display:flex; flex-direction:column; gap:6px;
}
.stats li + li{border-left:1px solid var(--rule); padding-left:clamp(16px,2.4vw,28px)}
.stats b{
  font-weight:800; font-stretch:112%;
  font-size:clamp(1.55rem,3vw,2.6rem);
  line-height:.94; letter-spacing:-.035em;
  font-variant-numeric:tabular-nums;
}
.stats span{
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--bone-38);
}

.linkrow{display:flex; flex-wrap:wrap; gap:10px; margin-top:clamp(24px,3vw,34px)}
.linkbtn--sm{padding:8px 16px; font-size:10px; letter-spacing:.16em}
.linkbtn{
  display:inline-block; padding:11px 22px;
  border:1px solid var(--rule); text-decoration:none;
  font-family:var(--mono); font-size:11px;
  letter-spacing:.18em; text-transform:uppercase;
  transition:background-color var(--n8d) var(--t), color var(--n8d) var(--t), border-color var(--n8d) var(--t);
}
.linkbtn:hover{background:var(--bone); border-color:var(--bone); color:var(--ink)}

/* A feed, so it scrolls. It runs off the right edge of the page on purpose:
   a row that stops short of the edge looks finished, and nobody scrolls it. */
.feed{
  position:relative; min-width:0;
  margin-right:calc(var(--bleed) * -1);
}
.tiles{
  list-style:none; margin:0; padding:0 var(--bleed) 2px 0;
  display:flex; gap:clamp(8px,1vw,14px);
  overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;
  /* Dragging is a drag, not a text selection. */
  user-select:none; -webkit-user-select:none;
  scrollbar-width:thin;
  scrollbar-color:rgba(244,241,234,.20) transparent;
}
/* A thrown row has to be free to coast. Snapping during the glide yanks it to
   the nearest tile and kills the throw, so it is suspended until it settles. */
.tiles.is-dragging{scroll-snap-type:none; scroll-behavior:auto}
.tiles:focus-visible{outline:2px solid var(--accent); outline-offset:3px}
.tiles::-webkit-scrollbar{height:3px}
.tiles::-webkit-scrollbar-track{background:transparent}
.tiles::-webkit-scrollbar-thumb{background:rgba(244,241,234,.20)}
.tiles li{margin:0; flex:0 0 clamp(158px,16.5vw,232px); scroll-snap-align:start}
.tiles a{
  position:relative; display:block; overflow:hidden;
  aspect-ratio:3/4; background:var(--ink-2);
}
.tiles img{width:100%; height:100%; object-fit:cover; transition:transform var(--n2) var(--t), opacity var(--n8d) var(--t)}
.tiles a:hover img{transform:scale(1.05); opacity:.72}
.tile__play{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%) scale(.7);
  width:40px; height:40px; display:grid; place-items:center;
  border-radius:50%;
  background:var(--accent); opacity:0;
  transition:opacity var(--n8d) var(--t), transform var(--n8d) var(--t);
  pointer-events:none;
}
.tiles a:hover .tile__play{opacity:1; transform:translate(-50%,-50%) scale(1)}
.tile__play svg{width:18px; height:18px; fill:var(--ink)}

/* ============================================================
   03 THE LAURETTES
   ============================================================ */
.live{display:grid; gap:clamp(30px,4vw,48px)}
.live__lead{max-width:34ch}
.live__cols{
  display:grid; grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);
  gap:clamp(26px,3.4vw,52px); align-items:start;
}
.live__note{margin:clamp(16px,2.2vw,22px) 0 0; color:var(--bone-60)}

.player{position:relative; overflow:hidden; background:var(--ink-2)}
.player__poster{width:100%; aspect-ratio:16/9; object-fit:cover; transition:transform var(--n4d) var(--t), opacity var(--beat) var(--t)}
.player:hover .player__poster{transform:scale(1.02)}
.player__btn{
  position:absolute; inset:0; margin:auto;
  width:clamp(64px,7vw,92px); height:clamp(64px,7vw,92px);
  display:grid; place-items:center;
  border-radius:50%;
  background:var(--accent); border:0; color:var(--ink); cursor:pointer;
  transition:transform var(--n8d) var(--t);
}
.player__btn:hover{transform:scale(1.07)}
.player__btn svg{width:38%; height:38%; fill:currentColor}
.player__cap{
  position:absolute; left:0; bottom:0; margin:0;
  padding:10px 14px; background:var(--ink);
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.18em; text-transform:uppercase; color:var(--bone-60);
}
.player iframe{width:100%; aspect-ratio:16/9; border:0; display:block}

/* The two records are the content here; booking and the website are admin.
   As four identical label-and-value rows they read as a spec sheet and the
   music was no more prominent than a URL. So the releases became things you
   go to, each a full row you can click with the title carrying the weight,
   and the two utility links dropped to a quiet line underneath. */
.credits{display:grid; gap:clamp(18px,2.4vw,26px)}

.releases{list-style:none; margin:0; padding:0}
.releases a{
  display:grid; grid-template-columns:minmax(0,1fr) auto;
  grid-template-areas:"label go" "name go";
  gap:2px clamp(12px,1.6vw,20px); align-items:center;
  padding:clamp(13px,1.6vw,18px) 0;
  text-decoration:none; color:var(--bone);
  /* Two hairlines, not a border. A border runs the full width at a constant
     weight, which is what made this read as a table. The base line instead
     fades out towards the right like everything else lit on this page, and a
     gold one grows across it from the left on hover. That is the same device
     the email address in the hero uses, so it belongs here. */
  background-image:
    linear-gradient(var(--accent), var(--accent)),
    linear-gradient(90deg, var(--rule), rgba(244,241,234,0));
  background-repeat:no-repeat, no-repeat;
  background-position:0 100%, 0 100%;
  background-size:0 1px, 100% 1px;
  transition:color var(--n8) var(--t), background-size var(--beat) var(--t);
}
.releases__label{
  grid-area:label;
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.2em; text-transform:uppercase; color:var(--bone-38);
  transition:color var(--n8) var(--t);
}
.releases__name{
  grid-area:name;
  font-weight:600; font-stretch:108%;
  font-size:clamp(1.05rem,1.55vw,1.38rem);
  letter-spacing:-.014em; line-height:1.18;
  text-wrap:balance;
}
.releases__go{
  grid-area:go; width:20px; height:20px; flex:none;
  fill:none; stroke:currentColor; stroke-width:1.6;
  color:var(--bone-38);
  transition:transform var(--n8d) var(--t), color var(--n8) var(--t);
}
.releases a:hover{color:var(--accent); background-size:100% 1px, 100% 1px}
.releases a:hover .releases__label{color:var(--bone-60)}
.releases a:hover .releases__go{color:var(--accent); transform:translateX(5px)}
.releases a:focus-visible{outline:2px solid var(--accent); outline-offset:3px}

/* Admin, and it should look like it. */
.credits__more{
  margin:0; display:flex; flex-wrap:wrap; align-items:baseline;
  gap:6px clamp(10px,1.4vw,16px);
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.16em; text-transform:uppercase;
}
.credits__more span{color:var(--bone-38)}
.credits__more a{
  color:var(--bone-60); text-decoration:none;
  border-bottom:1px solid var(--rule);
  transition:color var(--n8) var(--t), border-color var(--n8) var(--t);
}
.credits__more a:hover{color:var(--accent); border-color:var(--accent)}

/* ============================================================
   04 STAGES
   ============================================================ */
.feature{margin:0 0 clamp(10px,1.4vw,18px); overflow:hidden; background:var(--ink-2)}
/* Cropped shorter than the file it comes from. The photograph's own 1314/898
   puts dead ceiling along the top and empty stage floor along the bottom, and
   at full page width that made a very tall block that pushed the four portraits
   below it off the screen. 16/9 takes the waste off both ends, keeps the whole
   band and the crowd, and lets the row underneath sit in the same view. */
.feature img{width:100%; aspect-ratio:16/9; object-fit:cover; object-position:50% 50%; transition:transform var(--n2) var(--t)}
.feature:hover img{transform:scale(1.025)}

.grid{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(10px,1.4vw,18px);
}
.grid__i{margin:0; overflow:hidden; background:var(--ink-2)}
.grid__i img{width:100%; aspect-ratio:4/5; object-fit:cover; transition:transform var(--n2) var(--t)}
.grid__i:hover img{transform:scale(1.04)}

/* ============================================================
   05 CONTACT
   ============================================================ */
.contact{display:grid; gap:clamp(26px,3.6vw,42px)}
.contact__mail{
  display:inline-block; justify-self:start;
  font-weight:800; font-stretch:118%;
  font-size:clamp(1.7rem,6.4vw,5.2rem);
  line-height:1; letter-spacing:-.042em;
  text-decoration:none; word-break:break-word;
  background-image:linear-gradient(var(--accent),var(--accent));
  background-size:100% 3px; background-repeat:no-repeat; background-position:0 100%;
  padding-bottom:.08em;
  transition:background-size var(--beat) var(--t), color var(--n8d) var(--t);
}
.contact__mail:hover{background-size:100% 100%; color:var(--ink)}
.contact__body{max-width:46ch; color:var(--bone-60)}
.contact__body p{margin:0 0 .6em}
.contact__body a{color:var(--bone); text-decoration:none; border-bottom:1px solid var(--accent)}

/* ---------- footer ----------
   Full strength, shield included. It sat at 38% opacity, which is the sort of
   value that reads as considered restraint on a light page and simply as
   unfinished on a dark one: the last thing on the site looked like it had been
   dimmed by accident. The mono face at 10.5px is already quiet enough to know
   its place without being faded as well.

   The credit link's rule is lifted with it. Left at 13% under white text it
   reads as a rendering fault rather than an underline. */
.foot .crest{height:48px; color:var(--bone)}
.foot__credit a{
  color:var(--bone); text-decoration:none;
  border-bottom:1px solid rgba(244,241,234,.32);
  transition:color var(--n8) var(--t), border-color var(--n8) var(--t);
}
.foot__credit a:hover{color:var(--accent); border-color:var(--accent)}
.foot{
  max-width:var(--max); margin-inline:auto;
  padding:26px var(--gut) 40px;
  border-top:1px solid var(--rule);
  display:flex; flex-wrap:wrap; gap:12px 20px; align-items:center; justify-content:space-between;
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.18em; text-transform:uppercase; color:var(--bone);
}

/* ---------- lightbox ---------- */
.lb{
  position:fixed; inset:0; z-index:80;
  display:flex; align-items:center; justify-content:center;
  gap:clamp(6px,2vw,28px);
  padding:clamp(12px,3vw,40px);
  background:rgba(6,6,8,.93);
  -webkit-backdrop-filter:blur(18px) saturate(.9); backdrop-filter:blur(18px) saturate(.9);
  opacity:0; transition:opacity var(--n8d) var(--t);
}
/* A class rule setting display:flex beats the UA's [hidden]{display:none},
   so without this the closed lightbox stays laid out: invisible, full screen,
   and swallowing every click on the page. */
.lb[hidden]{display:none}
.lb:not(.is-open){pointer-events:none}
.lb.is-open{opacity:1; pointer-events:auto}
.lb__stage{
  position:relative;
  height:min(86vh, 900px); aspect-ratio:9/16;
  max-width:100%;
  display:flex; flex-direction:column;
}
/* The Embed Player has no pink "Watch now" bar, so no cropping is needed here.
   The older /embed/v2 route did, which is why this used to be oversized. */
.lb__frame{
  position:relative; flex:1 1 auto; overflow:hidden;
  background:var(--ink-2); border:1px solid var(--rule);
}
.lb__frame iframe{
  position:absolute; inset:0; width:100%; height:100%; border:0;
}
.lb__count{
  margin:12px 0 0;
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  font-family:var(--mono); font-size:10.5px;
  letter-spacing:.2em; color:var(--bone-38);
}
.lb__out{
  color:var(--bone-60); text-decoration:none;
  border-bottom:1px solid var(--rule);
  transition:color var(--n8) var(--t), border-color var(--n8) var(--t);
}
.lb__out:hover{color:var(--bone); border-color:var(--accent)}
/* Round, to match the play buttons. Both of these are already square, so 50%
   gives a true circle rather than an oval. */
.lb__close{
  position:absolute; top:clamp(12px,2.4vw,28px); right:clamp(12px,2.4vw,28px);
  width:44px; height:44px; display:grid; place-items:center;
  border-radius:50%;
  background:none; border:1px solid var(--rule); color:var(--bone);
  cursor:pointer; transition:background-color var(--n8) var(--t), color var(--n8) var(--t);
}
.lb__close:hover{background:var(--bone); color:var(--ink)}
.lb__nav{
  flex:none; width:clamp(38px,4vw,52px); height:clamp(38px,4vw,52px);
  display:grid; place-items:center;
  border-radius:50%;
  background:none; border:1px solid var(--rule); color:var(--bone);
  cursor:pointer; transition:background-color var(--n8) var(--t), color var(--n8) var(--t), opacity var(--n8) var(--t);
}
.lb__nav:hover{background:var(--bone); color:var(--ink)}
.lb__close svg,.lb__nav svg{width:40%; height:40%; fill:none; stroke:currentColor; stroke-width:2}
@media (max-width:620px){
  .lb__nav{position:absolute; bottom:clamp(12px,3vw,24px)}
  .lb__nav--prev{left:clamp(12px,4vw,28px)}
  .lb__nav--next{right:clamp(12px,4vw,28px)}
  .lb__stage{height:min(74vh,760px)}
}
@media (prefers-reduced-motion:reduce){ .lb{transition:none} }

/* ---------- reveal ---------- */
/* An animation, not a transition, and deliberately so. `transition` is a single
   property: a .rv rule carrying one would replace whatever transition the
   element already declared for itself, and this rule is last in the file, so it
   won a same-specificity tie against every one of them. That is what silently
   killed the gold fill on .contact__mail, the biggest link on the page. It
   spared .hero__cta only because that one hangs its transition on a child span.
   Animating leaves each element's own transition alone. */
.rv{opacity:0; transform:translateY(22px)}
.rv.in{animation:reveal var(--n2) var(--t) both}
@keyframes reveal{ to{opacity:1; transform:none} }
@media (prefers-reduced-motion:reduce){
  .rv{opacity:1; transform:none; animation:none}
  .player__poster,.grid__i img{transition:none}
}

/* ---------- the rig ----------
   The stage light leans towards the cursor, the way a fixture on a yoke does.

   The rule this obeys: the light in the photograph is baked into the
   photograph and cannot move, so the join between the picture and the page
   must never shift. That is why the beam pivots at its own source, the top
   right corner, where a rotation moves nothing at all. The photograph sits
   close to that corner, so it stays lit exactly as before. The sweep only
   opens up further down the page, where there is no edge left to betray.

   .hero::after is the seam itself and is deliberately not listed here. It
   stays welded to the photograph's own edge whatever the rig does.

   The numbers are small on purpose: 1.1 degrees and nine pixels. This should
   register as the room being lit rather than as an effect. app.js drags the
   value towards the cursor slowly, so the fixture feels heavy; snapping to the
   pointer is what makes this sort of thing look cheap. Written as custom
   properties so it stays on the compositor and never lays the page out again.

   --rig-x and --rig-y are -1 to 1, and are 0 on touch, on keyboards, and for
   anyone who has asked for reduced motion. At 0 everything sits exactly where
   it was designed. */
:root{ --rig-x:0; --rig-y:0 }

/* A second fixture, thrown right across the hero.
   The light in the photograph is baked in and cannot move, so rather than try
   to shift it this adds another one. A beam that was never in the picture is
   free to move without contradicting anything in it.

   THE TAIL IS THE WHOLE PROBLEM AND THE WHOLE FIX. A band of light that
   travels drags its own edges with it, and on a page this dark the bottom edge
   of the band reads as a diagonal line sweeping across the photograph. The
   first version had a short falloff written by hand, four stops from the peak
   to nothing, and that was the line. These stops are generated from a gaussian
   instead, wide on the trailing side, so the band decays over more than twice
   the distance it takes to rise, with no step anywhere in it to catch the eye.
   If you edit this gradient, keep the tail long and keep it smooth.

   Outside .hero deliberately: the hero sets isolation:isolate, and a screen
   blend inside an isolated group cannot blend past it. Screen, because light
   adds; painting it on would grey the photograph rather than light it.
   Horizontal travel only, because travelling vertically slid the band's own
   falloff up and down the page. */
.rig{
  position:absolute; z-index:4; pointer-events:none;
  top:0; left:50%; width:200vw;
  height:210%;
  mix-blend-mode:screen;
  background:linear-gradient(137deg,
    rgba(var(--beam),0) 27.0%,
    rgba(var(--beam),0.0065) 30.0%,
    rgba(var(--beam),0.0217) 33.0%,
    rgba(var(--beam),0.0564) 36.0%,
    rgba(var(--beam),0.1142) 39.0%,
    rgba(var(--beam),0.1803) 42.0%,
    rgba(var(--beam),0.2219) 45.0%,
    rgba(var(--beam),0.2222) 48.0%,
    rgba(var(--beam),0.2083) 51.0%,
    rgba(var(--beam),0.1847) 54.0%,
    rgba(var(--beam),0.1549) 57.0%,
    rgba(var(--beam),0.1229) 60.0%,
    rgba(var(--beam),0.0922) 63.0%,
    rgba(var(--beam),0.0655) 66.0%,
    rgba(var(--beam),0.044) 69.0%,
    rgba(var(--beam),0.0279) 72.0%,
    rgba(var(--beam),0.0168) 75.0%,
    rgba(var(--beam),0.0095) 78.0%,
    rgba(var(--beam),0.0051) 81.0%,
    rgba(var(--beam),0.0026) 84.0%,
    rgba(var(--beam),0) 87.0%);
  /* and it has to die before its own bottom edge as well, or the box draws the
     line the gradient no longer does */
  -webkit-mask-image:linear-gradient(to bottom, #000 30%, rgba(0,0,0,.72) 58%, rgba(0,0,0,.34) 78%, rgba(0,0,0,.10) 91%, rgba(0,0,0,0) 100%);
  mask-image:linear-gradient(to bottom, #000 30%, rgba(0,0,0,.72) 58%, rgba(0,0,0,.34) 78%, rgba(0,0,0,.10) 91%, rgba(0,0,0,0) 100%);
  transform:translateX(-50%) translate3d(calc(var(--rig-x) * -92px), 0, 0);
}
@media (prefers-reduced-motion:reduce){ .rig{transform:translateX(-50%)} }
@media (max-width:860px){ .rig{display:none} }

/* .spot does not move, and that is deliberate.
   It is the page wide beam, and its gradient has a defined edge where the
   light begins. Rotating it swept that edge across the page as a hard
   diagonal line following the cursor, which is far more noticeable than the
   light it was supposed to be moving. A soft edge you never see; the same
   edge in motion you cannot stop seeing.
   The cursor still moves the light: .rig sweeps across the photograph and the
   page, and .hero__figure's glow shifts with it. Those two have no hard edge
   to drag. This one stays put. */
.hero::before{
  transform:translateX(-50%)
            translate3d(calc(var(--rig-x) * 11px), calc(var(--rig-y) * 6px), 0);
}
@media (prefers-reduced-motion:reduce){
  .spot{transform:translateX(-50%)}
  .hero::before{transform:translateX(-50%)}
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:1000px){
  .live__cols{grid-template-columns:minmax(0,1fr)}
}
@media (max-width:860px){
  .hero{
    grid-template-columns:minmax(0,1fr);
    min-height:auto; align-items:start;
    /* No top padding on a phone. The padding was clearance for the fixed top
       bar, but on a phone the photograph is the first thing in the hero, and
       clearing the bar left a band of empty dark above it. The photograph runs
       up underneath the bar instead: its own mask fades the top eighth to
       nothing, so it emerges from the dark rather than starting on an edge,
       and the crest and the links sit over that fade with nothing behind them
       to fight. */
    padding-top:0;
  }
  .hero__figure{
    /* Height comes from the picture, never from the viewport.

       It used to be 60svh. On iOS the address bar collapses as you scroll,
       which changes the viewport height, which changed this box's height,
       which changed what object-fit:cover cropped, so the photograph appeared
       to grow and reveal more of its own bottom halfway down a scroll.

       An aspect-ratio on this element does not work either, and that mistake
       shipped: on the desktop layout its only child is absolutely positioned,
       so the box has nothing in flow to take a height from and it collapsed to
       nothing. The image goes back in flow here instead and gives the box its
       own height, which no browser can get wrong and no scroll can change. */
    min-height:0; height:auto;
    order:-1;
    margin:0 calc(var(--gut) * -1) clamp(18px,4vw,30px);
    transform:none;   /* stacked here, so there is no cap line to meet */
    --edge:linear-gradient(to bottom,
      rgba(0,0,0,0) 0%, rgba(0,0,0,.34) 2%, rgba(0,0,0,.74) 5%, rgba(0,0,0,.94) 8%, #000 12%,
      #000 46%, rgba(0,0,0,.97) 57%, rgba(0,0,0,.90) 67%, rgba(0,0,0,.76) 76%,
      rgba(0,0,0,.56) 84%, rgba(0,0,0,.34) 91%, rgba(0,0,0,.14) 96%, rgba(0,0,0,0) 100%);
    -webkit-mask-composite:source-over; mask-composite:add;
  }
  .hero__figure img{
    position:static; width:100%; height:auto;
    aspect-ratio:4/5; max-height:620px; object-fit:cover;
  }
  .hero::before{
    top:-4%; bottom:34%; left:50%; width:100vw; transform:translateX(-50%);
    background:
      radial-gradient(70% 32% at 62% 24%,
        rgba(var(--beam),.165), rgba(var(--beam),.115) 26%, rgba(var(--beam),.070) 44%,
        rgba(var(--beam),.037) 61%, rgba(var(--beam),.013) 78%, rgba(var(--beam),0) 93%),
      radial-gradient(112% 52% at 55% 52%,
        rgba(var(--wash),.250), rgba(var(--wash),.178) 20%, rgba(var(--wash),.119) 36%,
        rgba(var(--wash),.072) 51%, rgba(var(--wash),.037) 67%, rgba(var(--wash),.013) 82%,
        rgba(var(--wash),0) 94%);
  }
  .intro{grid-template-columns:minmax(0,1fr)}
  .intro__text{grid-area:auto; max-width:none}
  .intro__figure{
    grid-area:auto; justify-self:stretch; width:auto; max-width:none;
    margin:clamp(28px,5vw,40px) calc(var(--gut) * -1) 0;
  }
  .grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .feature img{aspect-ratio:1314/898}
}
@media (max-width:560px){
  .linkbtn--sm{padding:6px 11px; font-size:9.5px; letter-spacing:.11em}
  .stats b{font-size:1.35rem; letter-spacing:-.03em}
  .stats span{font-size:9px; letter-spacing:.14em}
  .stats li{padding:14px 8px 14px 0}
  .stats li + li{padding-left:10px}
  .credits li{grid-template-columns:minmax(0,1fr); gap:4px}
  .topbar__nav{gap:14px}
}

/* ---------- the lockup arrives whole ----------
   ACB is transparent text filled by the tartan through background-clip, so it
   does not exist as anything visible until that image has loaded. DRUMMER is
   solid bone and paints the instant the font is there. On a cold load that
   left a real moment, most of a second, where the hero read as the word
   DRUMMER floating on its own with nothing above it. It was the first thing
   anyone saw and it looked broken.

   So the lockup is held until it is whole: fonts in, cloth in, and the JS fit
   run so the two lines are already the same width. Then both arrive together.

   Held only when JS is actually running, hence .js. Without that a browser
   with scripting off would hold it forever. app.js also has a timeout that
   shows it regardless, so no failure can leave it hidden. */
.js .wordmark{opacity:0}
.js .wordmark.is-ready{animation:lockup var(--n2) var(--t) both}
@keyframes lockup{
  from{opacity:0; transform:translateY(9px)}
  to{opacity:1; transform:none}
}
@media (prefers-reduced-motion:reduce){
  .js .wordmark{opacity:1}
  .js .wordmark.is-ready{animation:none}
}
