@font-face{
  font-family:"Anton";
  src:url("/fonts/anton-latin.woff2") format("woff2");
  font-weight:400; font-style:normal; font-display:swap;
}
@font-face{
  font-family:"Oswald";
  src:url("/fonts/oswald-latin.woff2") format("woff2");
  font-weight:200 700; font-style:normal; font-display:swap;
}

:root{
  --head-img:url("/img/logo-head.png");
  --word-img:url("/img/logo-word.png");
  --void:#000;
  --bone:#EDEBE7;
  --ash:#7A776F;
  --faint:#3A3833;
  --grid:#2F2D2A;   /* board hairlines. Lifted to match the reference contrast */
  --rust:#C2481B;
  --rust-deep:#8A2F10;
  /* --ink is the cover ink for this release. One flat colour, per house style.
     Set it to the BDd044 artwork colour when the cover lands. */
  --ink:#EDEBE7;
  --ease:cubic-bezier(.2,.8,.3,1);
}
@media (prefers-color-scheme:light){
  :root{--void:#000;--bone:#EDEBE7;--ash:#7A776F;--faint:#3A3833;--grid:#2F2D2A;--rust:#C2481B;--rust-deep:#8A2F10;}
}
:root[data-theme="light"],:root[data-theme="dark"]{
  --void:#000;--bone:#EDEBE7;--ash:#7A776F;--faint:#3A3833;--rust:#C2481B;--rust-deep:#8A2F10;
}

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

body{
  margin:0;
  background:var(--void);
  color:var(--bone);
  font-family:"Oswald","Oswald","Helvetica Neue",Arial,sans-serif;
  font-weight:200;
  font-size:1rem;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

#grain{
  position:fixed;inset:-50%;width:200%;height:200%;
  pointer-events:none;z-index:9;opacity:.05;
  animation:drift 700ms steps(1,end) infinite;
}
@keyframes drift{
  0%{transform:translate(0,0)}25%{transform:translate(-2%,1%)}
  50%{transform:translate(1%,-2%)}75%{transform:translate(-1%,-1%)}100%{transform:translate(0,0)}
}

.anton{
  font-family:"Anton","Anton",Impact,sans-serif;
  font-weight:400;text-transform:uppercase;
  letter-spacing:-.005em;line-height:.86;margin:0;
}
.tag{
  font-weight:500;font-size:.68rem;letter-spacing:.24em;
  text-transform:uppercase;color:var(--ash);
}

.wrap{
  position:relative;z-index:10;
  width:min(58rem,100%);
  margin-inline:auto;
  padding:clamp(2rem,5vw,4rem) clamp(1.25rem,5vw,3rem) 6rem;
}

/* ---------------- gate ---------------- */
/* ---- the board -------------------------------------------------------
   Full-bleed isometric cell grid behind everything, per the reference:
   an oversized plane skewed on both axes and scaled back, clipped to the
   viewport and faded out at the edges by the radial mask.

   Hover colours a cell instantly and it fades back over 2s — the same
   timing as the reference's whileHover / animate pair. Colour is assigned
   in JS so each square gets a different one.

   z-1 keeps it under all page content while staying hoverable wherever
   content isn't covering it. */
#board{
  position:fixed;inset:0;z-index:1;overflow:hidden;
  -webkit-mask-image:radial-gradient(ellipse 82% 76% at 50% 45%,#000 38%,transparent 88%);
          mask-image:radial-gradient(ellipse 82% 76% at 50% 45%,#000 38%,transparent 88%);
}
#boardPlane{
  /* 2:1 is not arbitrary — it is what makes the skewed cell land as a near
     equilateral rhombus rather than a sliver. Bigger than before (72 vs 60) so
     a sleeve is actually readable at this scale. */
  --cw:72px; --ch:36px;
  position:absolute;left:50%;top:50%;
  display:grid;
  grid-template-columns:repeat(var(--cols,80),var(--cw));
  grid-auto-rows:var(--ch);
  transform:translate(-50%,-50%) skewX(-48deg) skewY(14deg) scale(.675) translateZ(0);
  transform-origin:center center;
  will-change:transform;
}
#boardPlane i{
  position:relative;
  border-right:1px solid var(--grid);
  border-bottom:1px solid var(--grid);
}
/* The cover lives on a pseudo-element rather than the cell itself, so it can
   fade on opacity while the grid hairline stays put — fading the cell would
   blink the whole lattice off and on. --c is set per cell from JS. */
#boardPlane i::after{
  content:"";position:absolute;inset:0;
  /* Stretched to fill, not cropped: the cover is squashed 2:1 here and the
     plane's shear stretches it back, so the sleeve lands square-ish on the
     isometric floor. `cover` would crop it to a horizontal slice instead. */
  background:var(--c) center/100% 100% no-repeat;
  opacity:0;transition:opacity 750ms ease-out;   /* fade back */
}
#boardPlane i.lit::after{ opacity:1; transition-duration:0ms; }  /* lands instantly on enter */

/* ---- let the board be hovered through the page --------------------------
   Inverted on purpose. #gate, the display h1 and even an empty #err are
   full-width blocks, so allow-listing was whack-a-mole: everything inside
   .wrap is transparent to the pointer, and only real controls plus the text
   actually worth copying get it back. Keeps the board hoverable nearly
   edge to edge without breaking selection or any interaction. */
.wrap, .wrap *{ pointer-events:none; }

.enter,                             /* the door */
#fb, .dl, .pl,                      /* feedback, download, play buttons */
.press p, .meta dd, .tracks .ttl,   /* text a DJ might want to copy */
a, button, input, textarea{ pointer-events:auto; }

/* ...but not once the release is open. Behind a paragraph the board turns
   reading into work: sleeves flicker in the gaps between lines and beside the
   measure as the eye tracks across. So the whole content column absorbs the
   pointer here, and the board keeps its full run of the door — where it is the
   point — plus the margins either side of the column. */
#rel, #rel *{ pointer-events:auto; }

/* The door is one button, so the whole screen centres on it — nothing else
   competes for the eye and there is no left edge to scan. */
#gate{min-height:calc(100svh - 8rem);display:flex;flex-direction:column;
  align-items:center;justify-content:center;text-align:center;
  gap:2rem;padding-block:.5rem 4rem;}
/* ---- logo, with a chromatic glitch -------------------------------------
   Adapted from the GlitchText component: a primary layer plus `before` and
   `after` layers offset on opposing translate/skew paths. That component
   duplicates *text*; the logo is a transparent PNG, so each glitch layer is
   a solid fill masked by the artwork instead. No markup change needed —
   ::before and ::after do the work.

   The split uses two inks the label actually prints (rust and the Against
   Faith teal) rather than the usual cyan/magenta, so it reads as chromatic
   aberration while staying on palette.

   Head mark only. The hand-drawn wordmark is already irregular by nature and
   a chromatic split on it just read as blur, so it stays clean.

   --gt / --gs scale the displacement per placement, so the 31px header mark
   glitches proportionally to the 150px one on the gate. */
.logo-head{ position:relative; --gt:3px; --gs:1deg; }
.logo-head{background:var(--head-img) center/contain no-repeat;}
.logo-word{background:var(--word-img) center/contain no-repeat;}

.logo-head::before,.logo-head::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  -webkit-mask-repeat:no-repeat; mask-repeat:no-repeat;
  -webkit-mask-position:center;  mask-position:center;
  -webkit-mask-size:contain;     mask-size:contain;
  mix-blend-mode:screen;         /* lets the two splits build rather than hide */
}
.logo-head::before,.logo-head::after{
  -webkit-mask-image:var(--head-img); mask-image:var(--head-img);
}

.logo-head::before{
  background:var(--rust);
  filter:drop-shadow(0 0 4px rgba(194,72,27,.45));
  animation:bd-glitch-a 2.4s cubic-bezier(.25,.46,.45,.94) infinite;
}
.logo-head::after{
  background:#2A8A7C;
  filter:drop-shadow(0 0 4px rgba(42,138,124,.4));
  animation:bd-glitch-b 2.4s cubic-bezier(.25,.46,.45,.94) infinite;
}

/* gate lockup is large, header mark is small — scale the displacement */
#gate .logo-head{ --gt:3px; --gs:1deg; }
.top .logo-head{ --gt:1px;  --gs:.5deg; }

@keyframes bd-glitch-a{
  0%,100%{ transform:translate(0); opacity:.85; }
  20%{ transform:translate(var(--gt),calc(var(--gt) * -1)) skew(var(--gs));            opacity:.70; }
  40%{ transform:translate(calc(var(--gt) * -1),var(--gt)) skew(calc(var(--gs) * -1)); opacity:.85; }
  60%{ transform:translate(calc(var(--gt) * -1),calc(var(--gt) * -1)) skew(var(--gs)); opacity:.70; }
  80%{ transform:translate(var(--gt),var(--gt)) skew(calc(var(--gs) * -1));            opacity:.85; }
}
/* the opposing path, as the component's `after` variant does */
@keyframes bd-glitch-b{
  0%,100%{ transform:translate(0); opacity:.85; }
  20%{ transform:translate(calc(var(--gt) * -1),var(--gt)) skew(calc(var(--gs) * -1)); opacity:.70; }
  40%{ transform:translate(var(--gt),calc(var(--gt) * -1)) skew(var(--gs));            opacity:.85; }
  60%{ transform:translate(var(--gt),var(--gt)) skew(calc(var(--gs) * -1));            opacity:.70; }
  80%{ transform:translate(calc(var(--gt) * -1),calc(var(--gt) * -1)) skew(var(--gs)); opacity:.85; }
}
#gate .logo-head{width:clamp(108px,17vw,152px);aspect-ratio:420/503;}
#gate .logo-word{width:clamp(196px,32vw,286px);aspect-ratio:734/49;margin-top:1.1rem;}
.lockup{display:flex;flex-direction:column;align-items:center;}
.top .logo-head{width:31px;aspect-ratio:420/503;flex:none;}
.top .logo-word{width:168px;aspect-ratio:734/49;flex:none;}
#gate .anton{font-size:clamp(2.5rem,7vw,4.25rem);}
button{font-family:inherit;border-radius:0;cursor:pointer;}
/* Reversed: the door opens orange and inverts to white on hover, so the two
   brand colours swap places rather than one giving way to the other. */
.enter{
  background:var(--rust);color:#fff;border:0;
  font-weight:600;font-size:.8rem;letter-spacing:.28em;text-transform:uppercase;
  padding:.95rem 3.2rem;
  transition:background 160ms var(--ease),color 160ms var(--ease);
}
.enter:disabled{opacity:.55;cursor:progress;}
.enter:hover{background:#fff;color:var(--rust);}
.enter:focus-visible{outline:2px solid var(--rust);outline-offset:3px;}
#err{min-height:1.3rem;color:var(--rust);font-weight:400;font-size:.85rem;letter-spacing:.02em;margin:0;}
.demo{color:var(--ash);font-size:.8rem;font-weight:200;margin:0;}
.demo b{color:var(--bone);font-weight:500;letter-spacing:.1em;}

/* ---------------- release ---------------- */
#rel{display:none;}
#rel.on{display:block;}

.top{display:flex;align-items:center;gap:1rem;margin-bottom:clamp(3rem,8vw,5.5rem);}


.top .spacer{margin-left:auto;}

h1.anton{font-size:clamp(3rem,13vw,8.5rem);}
h1.anton .dim{color:var(--ash);display:block;font-size:.42em;letter-spacing:.01em;margin-bottom:.28em;}

/* meta — inline pairs, no box, no rules */
.meta{
  display:flex;flex-wrap:wrap;gap:.35rem 3rem;
  margin:clamp(2rem,5vw,3rem) 0 0;
}
.meta div{display:flex;flex-direction:column;gap:.1rem;}
.meta dt{font-weight:500;font-size:.63rem;letter-spacing:.22em;text-transform:uppercase;color:var(--ash);margin:0;}
.meta dd{margin:0;font-weight:400;font-size:1rem;letter-spacing:.02em;}

.press{margin:clamp(2.5rem,6vw,4rem) 0 0;max-width:44ch;}
.press p{margin:0 0 1rem;color:#B9B5AD;font-weight:200;font-size:1rem;line-height:1.75;}
.press p:last-child{margin-bottom:0;}

/* ---------------- tracks ---------------- */
.sect{margin-top:clamp(3rem,8vw,5rem);}
.sect > .tag{display:block;margin-bottom:1.25rem;}

ol.tracks{list-style:none;margin:0;padding:0;}
.tr{
  position:relative;
  display:grid;
  grid-template-columns:2.1rem 1.75rem minmax(0,1fr) auto 2.9rem;
  align-items:center;gap:1rem;
  padding:.72rem 0;
}
.tr .n{font-weight:600;font-size:.95rem;color:var(--bone);font-variant-numeric:tabular-nums;}
.tr .ttl{font-weight:400;font-size:clamp(1rem,2.6vw,1.35rem);letter-spacing:.01em;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;transition:color 160ms var(--ease);}
.tr .dur{font-weight:200;font-size:.8rem;color:var(--ash);font-variant-numeric:tabular-nums;}
.tr:hover .ttl{color:#fff;}
.tr.on .ttl{color:#fff;font-weight:500;}

.pl{
  width:1.75rem;height:1.75rem;padding:0;border:0;background:transparent;
  color:var(--ash);display:grid;place-items:center;
  transition:color 160ms var(--ease),transform 160ms var(--ease);
}
.pl:hover{color:var(--bone);transform:scale(1.12);}
.pl:focus-visible{outline:2px solid var(--rust);outline-offset:2px;}
.tr.on .pl{color:var(--bone);}
.pl svg{width:13px;height:13px;display:block;}

/* progress appears only while playing — no permanent rules */
.prog{
  position:absolute;left:0;bottom:0;height:1px;width:0;
  background:var(--bone);opacity:0;transition:opacity 200ms var(--ease);
}
.tr.on .prog{opacity:.55;}

/* ---------------- feedback ---------------- */
.fbwrap{max-width:44rem;}
textarea{
  display:block;width:100%;min-height:8rem;resize:vertical;
  background:transparent;color:var(--bone);
  border:0;border-bottom:1px solid var(--faint);border-radius:0;
  font-family:inherit;font-weight:200;font-size:1rem;line-height:1.7;
  padding:0 0 .8rem;
  transition:border-color 200ms var(--ease);
}
textarea::placeholder{color:var(--faint);}
textarea:focus-visible{outline:none;border-bottom-color:var(--ash);}

/* Feedback is the one thing the page actually asks for, so it gets the only
   framed box on it. Rust rather than the usual hairline: it reads as a field to
   fill in rather than another block of text. Transparent inside, so the board
   keeps moving behind it. */
.fbbox{
  border:1px solid var(--rust);
  background:transparent;
  padding:1.35rem 1.45rem 1.1rem;
  transition:box-shadow 200ms var(--ease);
}
/* Doubles the frame on focus via shadow rather than border-width, so nothing
   reflows by a pixel when you click into it. */
.fbbox:focus-within{box-shadow:0 0 0 1px var(--rust);}

/* the box supplies the edge now — the underline would read as a second one */
.fbbox textarea{border-bottom:0;padding:0;min-height:9.5rem;}
.fbbox textarea:focus-visible{border-bottom-color:transparent;}

.act{display:flex;flex-wrap:wrap;align-items:center;gap:1.25rem;margin-top:1.75rem;}
.fbbox .act{
  margin-top:1.1rem;padding-top:.9rem;
  border-top:1px solid color-mix(in srgb,var(--rust) 32%,transparent);
}
/* per-track download. Locked until feedback exists; the server re-checks
   anyway, so this is an affordance rather than the gate. --rust stays the
   colour of "you may now have the file". */
.trdl{
  width:2.9rem;height:2.4rem;padding:0;border:0;background:transparent;
  color:var(--ash);display:grid;place-items:center;cursor:not-allowed;
  transition:color 160ms var(--ease),transform 160ms var(--ease);
}
.trdl svg{width:23px;height:23px;display:block;}
/* Locked sits at ash — dim, but never invisible; the icon has to read as a
   download before anyone works out how to earn it. Unlocked goes pure white:
   it is the one control on this page a tired DJ must find at a glance. */
.trdl.go{color:#fff;cursor:pointer;}
.trdl.go:hover{color:var(--rust);transform:translateY(1px);}
.trdl:focus-visible{outline:2px solid var(--rust);outline-offset:2px;}
.trdl.busy{color:var(--rust);animation:trdl-busy 700ms ease-in-out infinite;}
@keyframes trdl-busy{0%,100%{opacity:1}50%{opacity:.35}}

/* Send. Bone-filled like the door button, so the two things we ask a recipient
   to press look like the same kind of thing. Disabled it drops to an outline —
   present, clearly not ready yet. */
.send{
  background:var(--bone);color:var(--void);border:1px solid var(--bone);
  font-weight:600;font-size:.68rem;letter-spacing:.2em;text-transform:uppercase;
  padding:.6rem 1.5rem;flex:none;
  transition:background 160ms var(--ease),color 160ms var(--ease),
             border-color 160ms var(--ease),opacity 160ms var(--ease);
}
.send:hover:not(:disabled){background:var(--rust);border-color:var(--rust);color:var(--bone);}
.send:focus-visible{outline:2px solid var(--rust);outline-offset:3px;}
.send:disabled{background:transparent;color:var(--ash);border-color:var(--faint);cursor:not-allowed;}

.hint{font-weight:200;font-size:.82rem;color:var(--ash);margin:0;}

.foot{margin-top:clamp(3.5rem,9vw,6rem);color:var(--faint);font-size:.75rem;font-weight:200;line-height:1.8;}

#toast{
  position:fixed;left:50%;bottom:1.5rem;transform:translate(-50%,160%);
  opacity:0;visibility:hidden;pointer-events:none;
  z-index:30;background:var(--bone);color:var(--void);
  font-weight:500;font-size:.75rem;letter-spacing:.08em;
  padding:.8rem 1.2rem;max-width:min(34rem,calc(100vw - 2.5rem));
  transition:transform 340ms var(--ease),opacity 340ms var(--ease),visibility 340ms;
}
#toast.on{transform:translate(-50%,0);opacity:1;visibility:visible;}

.ribbon{
  position:fixed;top:0;left:0;right:0;z-index:20;background:var(--rust-deep);
  color:var(--bone);text-align:center;padding:.3rem 1rem;
  font-weight:500;font-size:.62rem;letter-spacing:.22em;text-transform:uppercase;
}
body{padding-top:1.6rem;}

@media (max-width:60rem){
  #boardPlane{--cw:46px;--ch:23px;}
  #gate{min-height:0;}
}
@media (max-width:34rem){
  .tr{grid-template-columns:1.5rem 1.6rem minmax(0,1fr) auto 1.7rem;gap:.7rem;}
  .meta{gap:.35rem 1.75rem;}
}
@media (prefers-reduced-motion:reduce){
  .logo-head::before,.logo-head::after{ animation:none; opacity:0; }
  #grain{animation:none;}
  *{transition-duration:1ms !important;}
}
