/* ==========================================================================
   DUST DUBS: record label site

   Structure is DS93's. The look comes off the Walk Among Duppy sleeve, and
   the important difference is which colour does the work. On DS93 the field
   is black and pink is a small accent. Here oxblood IS the field, and white
   and hard black are the graphic elements cut into it. Treating #642f34 as
   an accent on black would have produced a maroon DS93, not this.

   Palette sampled off the sleeve, not eyeballed:
     #642f34 oxblood (dominant), #37171b wine, #1a1919 near-black,
     #110000 red-black, #ffffff.

   Type follows the sleeve's own fonts. It is set in Helvetica Now Display
   Extra Black and DIN Bold, so the web stand-ins are Archivo Black (wide and
   very heavy, not condensed) and Archivo. Anton, which DS93 uses, is
   condensed and would read as a different label.
   ========================================================================== */

/* --------------------------------------------------------------------------
   TYPE
   Served from this domain rather than from Google. A webfont request to Google
   hands them every visitor's IP address and the page they were on, for no
   benefit to anyone here. Archivo and IBM Plex Mono are both SIL Open Font
   Licence, so hosting the files ourselves is what the licence is for.

   Latin and Latin Extended only. Cyrillic and Vietnamese are dropped, which is
   most of the weight.
   -------------------------------------------------------------------------- */
@font-face{
  font-family:'Archivo';
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url('../assets/fonts/archivo-400-700-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'Archivo';
  font-style:normal;
  font-weight:400 700;
  font-display:swap;
  src:url('../assets/fonts/archivo-400-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Archivo Black';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('../assets/fonts/archivo-black-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'Archivo Black';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('../assets/fonts/archivo-black-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('../assets/fonts/plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url('../assets/fonts/plex-mono-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:500;
  font-display:swap;
  src:url('../assets/fonts/plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'IBM Plex Mono';
  font-style:normal;
  font-weight:500;
  font-display:swap;
  src:url('../assets/fonts/plex-mono-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  /* colour, sampled from the sleeve */
  --oxblood:      #642f34;
  --oxblood-lift: #8a4149;   /* interactive states; the sampled oxblood is too dark to carry hover on a dark ground */
  --wine:         #37171b;
  --ink:          #140b0c;   /* red-black, not neutral: the sleeve has no true black field */
  --ink-raised:   #1a1212;
  --ink-card:     #1a1919;   /* the sleeve's label blocks */
  --line:         rgba(255,255,255,.10);
  --line-strong:  rgba(255,255,255,.20);
  --paper:        #ffffff;
  --paper-dim:    rgba(255,255,255,.66);
  --paper-faint:  rgba(255,255,255,.36);
  --concrete:     #b0a0a2;   /* warm grey, tinted toward the field */

  /* type */
  --f-display: 'Archivo Black', 'Helvetica Neue', Arial, sans-serif;
  --f-body: 'Archivo', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --f-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Type ramp. Twenty fixed sizes had accumulated between .55rem and 1.18rem,
     five of them inside a 1.1px band nobody could tell apart. Six deliberate
     steps instead, as tokens so a literal cannot drift back in.
     --t-micro is the floor for functional text: 11.2px was below a sensible
     legibility bar for form labels, and being on the ramp is not an excuse. */
  --t-micro: .72rem;   /* 11.5px: eyebrows, catalogue numbers, field labels */
  --t-small: .8rem;    /* 12.8px: footer links, help text, specs */
  --t-sub:   .88rem;   /* 14.1px: secondary copy, track names, errors */
  --t-body:  1rem;     /* 16px: body copy, primary buttons */
  --t-lead:  1.08rem;  /* 17.3px: page leads, standfirsts */
  --t-brand: 1.18rem;  /* 18.9px: the wordmark */

  /* layout */
  --edge: clamp(1.25rem, 4vw, 3.5rem);
  /* The bar is sized off the mark, not the other way round. Change --brand-h
     and the header, the hero padding and the page heads all follow.

     --header-h equals --brand-h with no slack: the +40px it used to carry put
     20px of dead space above the tonearm at the very top of the page, which
     read as a gap rather than as padding.

     The scrolled pair is separate because a 140px bar fixed over the page eats
     too much of the viewport once you are reading. --header-h itself must not
     change on scroll: the hero padding and the page heads are measured from it
     and the page would jump. */
  --brand-h:           140px;
  --header-h:          var(--brand-h);
  --brand-h-scrolled:   52px;
  --header-h-scrolled:  64px;
  --radius-s: 0;             /* the sleeve has no rounded corners anywhere */
  --chamfer: 14px;           /* the clipped corner on its label blocks */
  --ease: cubic-bezier(.16,.8,.28,1);
}

/* The sleeve's signature device: a block with one corner cut away. Used for
   the hero tag, the info dialog and the link buttons, so the furniture reads
   as the same system as the artwork. */
.chamfer-br{ clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--chamfer)), calc(100% - var(--chamfer)) 100%, 0 100%); }
.chamfer-bl{ clip-path: polygon(0 0, 100% 0, 100% 100%, var(--chamfer) 100%, 0 calc(100% - var(--chamfer))); }

/* ---------- reset ---------- */
*,*::before,*::after{ box-sizing:border-box; }
html{
  scroll-behavior:smooth;
  /* The header is fixed, so without this every anchor jump parks the target
     under it: #about landed with its first line hidden. It was wrong at 76px
     and obvious once the bar grew to hold the logo, so it is tied to
     --header-h rather than a number, and covers #top, #releases, #about and
     #signup at every breakpoint. */
  scroll-padding-top:calc(var(--header-h) + 1.25rem);
}
body{ margin:0; }
img{ display:block; max-width:100%; }
button{ font:inherit; color:inherit; background:none; border:0; cursor:pointer; }
a{ color:inherit; }
ul{ margin:0; padding:0; list-style:none; }
h1,h2,h3,p{ margin:0; }
svg{ display:block; }

/* The hidden attribute is only display:none from the browser's own stylesheet,
   so any class rule setting display beats it. .dz-chosen and .progress-wrap are
   both display:flex, so both ignored hidden and showed through: the dropzone
   had a remove button on it before a file was chosen. Make hidden win. */
[hidden]{ display:none !important; }

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

:focus-visible{ outline:2px solid var(--oxblood-lift); outline-offset:3px; }

body{
  background:var(--ink);
  color:var(--paper);
  font-family:var(--f-body);
  line-height:1.5;
  -webkit-font-smoothing:antialiased;
}

.container{ max-width:1360px; margin-inline:auto; padding-inline:var(--edge); }

/* The faint construction grid ruled across the sleeve. Fixed, very low
   contrast, and behind everything. */
body::before{
  content:''; position:fixed; inset:0; z-index:0; pointer-events:none;
  background-image:
    linear-gradient(rgba(255,255,255,.028) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.028) 1px, transparent 1px);
  background-size:96px 96px;
}
.site-header, main, .site-footer{ position:relative; z-index:1; }

.eyebrow{
  font-family:var(--f-mono);
  font-size:var(--t-micro);
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--concrete);
}
.eyebrow .dot{ color:var(--oxblood-lift); }

/* ==========================================================================
   Header
   ========================================================================== */
.site-header{
  position:fixed; inset:0 0 auto 0; height:var(--header-h);
  z-index:100;
  display:flex; align-items:center;
  background:linear-gradient(to bottom, rgba(20,11,12,.88), rgba(20,11,12,0));
  transition:background .35s var(--ease), height .3s var(--ease);
}
.site-header.is-scrolled{
  height:var(--header-h-scrolled);
  background:rgba(20,11,12,.92);
  backdrop-filter:blur(10px) saturate(140%);
  -webkit-backdrop-filter:blur(10px) saturate(140%);
  border-bottom:1px solid var(--line);
}
.site-header.is-scrolled .brand-mark{ height:var(--brand-h-scrolled); }
.site-header .container{
  width:100%;
  display:flex; align-items:center; justify-content:space-between;
  gap:1.5rem;
}

/* The label mark. Supplied as black ink with the wordmark in white, which
   disappears on this field: the file is inverted, white ink with the letters
   knocked back to --ink, so the splat and the tonearm read. Served at 3x for
   retina, height driven, width auto, so one file covers every breakpoint. */
.brand{ display:flex; align-items:center; text-decoration:none; flex:none; }
/* flex:none is load bearing. The mark sits in a flex row with the nav, and a
   flex item defaults to flex-shrink:1. Once the nav grew to seven icons the
   row no longer fit at 390px, so the browser shrank the image's width while
   the fixed height held: the logo rendered 66px wide where its ratio wants
   90px, squashed 27%. Nothing in the markup shows that, it only appears under
   measurement. The nav tightens its gap instead, below. */
.brand-mark{ height:var(--brand-h); width:auto; flex:none; display:block; transition:height .3s var(--ease); }

.nav{ display:flex; align-items:center; gap:1.25rem; }
.nav a{
  display:flex; align-items:center; justify-content:center;
  width:46px; height:46px;
  color:var(--paper);
  transition:background .2s var(--ease), color .2s var(--ease);
}
.nav a .nav-icon{ width:26px; height:26px; }
.nav a:hover, .nav a:focus-visible{ background:var(--oxblood); color:var(--paper); }
/* Where you are. Only the submit page sets this, since the others are anchors
   on one page and would all claim to be current at once. */
.nav a[aria-current="page"]{ background:var(--oxblood); }

/* ==========================================================================
   Hero: lead release, fills viewport
   ========================================================================== */
/* Capped, not a full screen.
   The content is bottom-aligned, which DS93 gets away with because a
   photograph fills the space above it. On a flat field that space is just
   empty, and it grew with the window: on a tall monitor the title sat right
   at the bottom under a third of a screen of nothing.
   min() rather than max-height, because when min-height and max-height
   disagree min-height wins and the cap is silently ignored. */
.hero{
  position:relative;
  min-height:min(100svh, 860px);
  width:100%;
  display:flex; flex-direction:column; justify-content:flex-end;
  overflow:hidden;
  isolation:isolate;
}
/* A flat field rather than a photograph.
   The sleeve works as a collage because it is one object at arm's length.
   Put it behind a page of type and every element in it competes with a word:
   the reel in the middle of the crop sat directly behind the tracklist and
   was the loudest thing on the screen. So the hero is oxblood falling to
   red-black, the sleeve's own colours, and the cover art carries the imagery. */
.hero-field{
  position:absolute; inset:0; z-index:-2;
  background:linear-gradient(to bottom, var(--wine) 0%, #2a1417 46%, var(--ink) 100%);
}
/* The sleeve's top band, blurred past recognition and dropped to 20%. At this
   strength there is no object left in it, just a warm swell behind the title
   that keeps the field from reading as a flat CSS gradient. The unblurred
   version put a bright reel directly behind the tracklist. */
.hero-field::after{
  content:''; position:absolute; inset:0;
  background-image:url('../assets/img/dd001-blur-v2.jpg');
  background-size:cover; background-position:center;
  opacity:.2;
}

/* The sleeve's radiating burst, rebuilt as repeating-conic so it costs
   nothing and scales. Kept faint: it is texture, not a picture. */
.hero-rays{
  position:absolute; z-index:-1;
  left:clamp(-14rem,-8vw,-4rem); top:-16%;
  width:min(54vw,720px); aspect-ratio:1;
  background:repeating-conic-gradient(from 210deg at 50% 50%,
    rgba(255,255,255,.04) 0deg 1.1deg, transparent 1.1deg 4.4deg);
  -webkit-mask-image:radial-gradient(circle at 50% 50%, #000 32%, transparent 68%);
          mask-image:radial-gradient(circle at 50% 50%, #000 32%, transparent 68%);
  opacity:.75;
}

.hero-content{
  position:relative;
  padding:calc(var(--header-h) + 2rem) var(--edge) clamp(2.5rem,6vw,4.5rem);
  width:100%;
}

/* The sleeve's black label block, chamfer and all. */
.hero-tag{
  display:inline-flex; align-items:center; gap:.55rem;
  background:var(--ink-card);
  color:var(--paper);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; font-weight:500;
  padding:.5rem .95rem .5rem .8rem;
  margin-bottom:1.25rem;
}
.hero-tag::before{ content:'●'; font-size:.55rem; color:var(--oxblood-lift); }
/* The tag is uppercased as a label. The catalogue number is a code, not a
   label, so it keeps the casing it is written in and matches the Releases
   heading further down. */
.hero-tag .cat{ text-transform:none; }

.hero-title{
  font-family:var(--f-display);
  font-weight:400;
  text-transform:uppercase;
  font-size:clamp(2.6rem, 7.6vw, 6.5rem);
  line-height:.92;
  letter-spacing:-.015em;   /* Archivo Black is wide; negative tracking keeps it as tight as the sleeve */
  max-width:15ch;
  color:var(--paper);
}
/* The record's own words, sitting between the artist and the tracklist.
   Narrow measure so it reads as a sleeve note rather than a paragraph of web
   copy, and the format line set in mono because it is a specification. */
.hero-blurb{ max-width:46ch; margin-top:clamp(1.1rem,2.4vw,1.6rem); }
.hero-blurb p{ color:var(--paper-dim); font-size:clamp(.95rem,1.7vw,1.05rem); line-height:1.55; }
.hero-blurb .hero-format{
  margin-top:.8rem;
  font-family:var(--f-mono); font-size:var(--t-small); line-height:1.6;
  letter-spacing:.02em; color:var(--concrete);
  border-left:2px solid var(--oxblood-lift); padding-left:.9rem;
}

.hero-artist{
  font-family:var(--f-mono);
  font-size:clamp(.85rem,1.6vw,1.05rem);
  color:var(--concrete);
  letter-spacing:.03em;
  margin-top:1rem;
  max-width:38ch;
}

.hero-lower{
  margin-top:clamp(1.75rem,4vw,2.75rem);
  display:flex; align-items:flex-start; gap:clamp(1.75rem,4vw,3rem);
}
.hero-tracklist{ flex:1 1 0%; min-width:0; }
.hero-side{
  flex:0 0 auto;
  width:clamp(188px,35vw,400px);
  display:flex; flex-direction:column; gap:1.25rem;
}
/* Solid white keyline, not a faint one. The sleeve is dark at its edges and
   sits on a dark field, so a 20%-white border let the corners dissolve into
   the background. White holds the square and matches the hard white shapes
   cut into the artwork itself. */
.hero-art{
  width:100%;
  aspect-ratio:1/1;
  overflow:hidden;
  background:var(--ink-raised);
  border:2px solid var(--paper);
  box-shadow:0 30px 70px -20px rgba(0,0,0,.7);
}
.hero-art img{ width:100%; height:100%; object-fit:cover; }

/* The sleeve turns over to show the back. Hover alone would hide it from
   touch and from the keyboard, so it is a button: click or Enter toggles,
   the state lives in aria-pressed, and the CSS reads that. The hint fades
   out once you are already looking at the back. */
.art-flip{
  display:block; position:relative; width:100%; height:100%;
  margin:0; padding:0; border:0; background:none; cursor:pointer;
  -webkit-appearance:none; appearance:none;
}
.art-flip:focus-visible{ outline:2px solid var(--oxblood-lift); outline-offset:3px; }
.art-face{ display:block; width:100%; height:100%; object-fit:cover; }
.art-back{
  position:absolute; inset:0; opacity:0;
  transition:opacity .35s var(--ease);
}
.art-flip:hover .art-back,
.art-flip:focus-visible .art-back,
.art-flip[aria-pressed="true"] .art-back{ opacity:1; }

.art-flip-hint{
  position:absolute; right:.6rem; bottom:.6rem;
  font-family:var(--f-mono); font-size:var(--t-micro);
  letter-spacing:.12em; text-transform:uppercase;
  color:var(--paper); background:rgba(20,11,12,.74);
  border:1px solid var(--line-strong);
  padding:.3rem .55rem; pointer-events:none;
  transition:opacity .25s var(--ease);
}
.art-flip:hover .art-flip-hint,
.art-flip:focus-visible .art-flip-hint,
.art-flip[aria-pressed="true"] .art-flip-hint{ opacity:0; }

@media (prefers-reduced-motion:reduce){
  .art-back,.art-flip-hint{ transition:none; }
}
.hero-side .link-row{ justify-content:flex-start; }

.scroll-cue{
  position:absolute; right:var(--edge); bottom:2rem;
  display:flex; flex-direction:column; align-items:center; gap:.6rem;
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.15em;
  color:var(--paper-faint); text-transform:uppercase;
}
.scroll-cue .line{ width:1px; height:34px; background:linear-gradient(var(--concrete),transparent); animation:cue 1.8s ease-in-out infinite; }
@keyframes cue{ 0%,100%{ opacity:.25; transform:scaleY(.6); transform-origin:top;} 50%{ opacity:1; transform:scaleY(1); transform-origin:top;} }

/* ==========================================================================
   Tracklist + transport (shared by hero + release cards)
   ========================================================================== */
.tracklist{ display:flex; flex-direction:column; }
.track{
  display:grid;
  grid-template-columns:auto 1fr auto;
  align-items:center;
  gap:.85rem;
  padding:.6rem .15rem;
  border-bottom:1px solid var(--line);
}
.track:first-child{ border-top:1px solid var(--line); }
.track-name{ font-size:var(--t-sub); font-weight:500; }
.track-time{ font-family:var(--f-mono); font-size:var(--t-micro); color:var(--paper-faint); }

/* Square, not round: the sleeve has no circles except the deliberate targets. */
.track-play,
.playall-btn{
  width:32px; height:32px;
  border:1px solid var(--line-strong);
  display:flex; align-items:center; justify-content:center; flex:none;
  transition:border-color .2s var(--ease), background .2s var(--ease), transform .15s var(--ease);
}
.track-play svg,
.playall-btn svg{ width:11px; height:11px; }
.track-play:hover,
.playall-btn:hover{ border-color:var(--oxblood-lift); }
.track-play:active,
.playall-btn:active{ transform:scale(.92); }
.track-play[aria-pressed="true"],
.playall-btn[aria-pressed="true"]{ background:var(--oxblood-lift); border-color:var(--oxblood-lift); }
.track-play .icon-play,
.playall-btn .icon-play{ display:block; margin-left:1px; }
.track-play .icon-pause,
.playall-btn .icon-pause{ display:none; }
.track-play[aria-pressed="true"] .icon-play,
.playall-btn[aria-pressed="true"] .icon-play{ display:none; }
.track-play[aria-pressed="true"] .icon-pause,
.playall-btn[aria-pressed="true"] .icon-pause{ display:block; }

.track.is-playing .track-name{ color:var(--oxblood-lift); }

/* No data-src means there is no file behind the row. main.js only binds to
   .track-play[data-src], so the button is inert either way: this stops it
   inviting a click it cannot answer. */
.track-play:not([data-src]){ opacity:.32; cursor:default; }
.track-play:not([data-src]):hover{ border-color:var(--line-strong); }
.track-play:not([data-src]):active{ transform:none; }
.track-play:not([data-src]) + .track-name{ color:var(--paper-faint); }

.track-playall{
  display:flex; align-items:center;
  padding-bottom:.85rem;
  margin-bottom:.15rem;
}
.track-playall .playall-text{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase;
}
.track-playall .playall-eyebrow{ color:var(--paper-faint); }
.track-playall .playall-dot{ color:var(--oxblood-lift); margin:0 .5em; }
.track-playall .playall-label{ color:var(--paper); font-weight:700; letter-spacing:.1em; }
.track-playall[data-active="true"] .playall-label{ color:var(--oxblood-lift); }

.playall-btn{ background:var(--oxblood); border-color:var(--oxblood); color:var(--paper); }
.playall-btn:hover{ background:var(--oxblood-lift); border-color:var(--oxblood-lift); }
.playall-btn[aria-pressed="true"]{ background:var(--oxblood-lift); border-color:var(--oxblood-lift); }

/* external link row */
.link-row{ display:flex; flex-wrap:wrap; gap:.6rem; }
.link-btn{
  display:flex; align-items:center; gap:.5rem;
  border:1px solid var(--line-strong);
  padding:.55rem .85rem;
  font-size:var(--t-small); font-weight:600; letter-spacing:.02em;
  text-decoration:none;
  transition:border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
  white-space:nowrap;
}
.link-btn svg{ width:15px; height:15px; flex:none; }
.link-btn:hover, .link-btn:focus-visible{ border-color:var(--oxblood-lift); background:var(--oxblood-lift); color:var(--paper); }
.link-row.is-compact .link-btn .lb-label{ display:none; }
.link-row.is-compact .link-btn{ padding:.6rem; }

/* ==========================================================================
   Releases
   ========================================================================== */
.section{ padding-block:clamp(4rem,9vw,7rem); }
.section-head{
  display:flex; align-items:flex-end; justify-content:space-between; gap:2rem;
  margin-bottom:clamp(2.25rem,5vw,3.5rem);
  border-bottom:1px solid var(--line);
  padding-bottom:1.5rem;
}
.section-head h2{
  font-family:var(--f-display); text-transform:uppercase; font-weight:400;
  font-size:clamp(1.9rem,4vw,2.75rem); letter-spacing:-.015em;
}
.section-count{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--paper-faint); }

.releases-list{ display:flex; flex-direction:column; gap:clamp(3rem,6vw,4.5rem); }

.release{
  display:grid;
  grid-template-columns:minmax(220px,340px) 1fr;
  gap:clamp(1.75rem,4vw,3.5rem);
  align-items:start;
  padding-top:clamp(2rem,4vw,3rem);
  border-top:1px solid var(--line);
}
.release:first-child{ border-top:0; padding-top:0; }

/* Same white keyline as the hero sleeve. It is the label's device for "this is
   a record", so it belongs on every sleeve on the page, not just the lead one. */
.release-art{
  position:relative;
  aspect-ratio:1/1;
  overflow:hidden;
  background:var(--ink-raised);
  border:2px solid var(--paper);
}
.release-art img{
  width:100%; height:100%; object-fit:cover;
  transition:transform .6s var(--ease);
}
.release:hover .release-art img{ transform:scale(1.035); }

.release-body{ display:flex; flex-direction:column; gap:1.5rem; min-width:0; }
.release-heading{ display:flex; flex-direction:column; gap:.5rem; }
.release-heading-top{ display:flex; align-items:center; justify-content:space-between; gap:1rem; }
.release-heading h3{
  font-family:var(--f-display); text-transform:uppercase; font-weight:400;
  font-size:clamp(1.5rem,3vw,2.1rem); line-height:1.02; letter-spacing:-.015em;
}
.release-artist{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--concrete); }

/* The description on a release-list block, the twin of .hero-blurb. 001 carries
   its copy in the hero; anything in the list needs somewhere to say the same
   thing or the words end up hidden behind the Info button, which is where the
   My Gazelles copy sat until 21 September 2026. Body size off the ramp rather
   than the hero's clamp: this is copy in a list, not a lead. */
.release-blurb{ max-width:46ch; }
.release-blurb p{ color:var(--paper-dim); font-size:var(--t-body); line-height:1.55; }
.release-blurb p + p{ margin-top:.8rem; }
.release-blurb .release-format{
  font-family:var(--f-mono); font-size:var(--t-small); line-height:1.6;
  letter-spacing:.02em; color:var(--concrete);
  border-left:2px solid var(--oxblood-lift); padding-left:.9rem;
}

.info-btn{
  display:flex; align-items:center; gap:.4rem; flex:none;
  border:1px solid var(--line-strong);
  padding:.35rem .7rem .35rem .55rem;
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.05em; text-transform:uppercase;
  color:var(--paper-dim);
  transition:border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.info-btn svg{ width:15px; height:15px; }
.info-btn:hover, .info-btn:focus-visible{ border-color:var(--oxblood-lift); background:var(--oxblood-lift); color:var(--paper); }

.release-links{ display:flex; flex-direction:column; gap:.4rem; }
.release-links-label{ font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase; color:var(--paper-faint); }

/* ==========================================================================
   Release info dialog
   ========================================================================== */
.promo-dialog{
  border:1px solid var(--line-strong);
  background:var(--ink-card);
  color:var(--paper);
  padding:0;
  width:min(560px, calc(100vw - 2.5rem));
  max-height:min(640px, calc(100vh - 4rem));
  box-shadow:0 40px 90px -20px rgba(0,0,0,.75);
}
.promo-dialog::backdrop{
  background:rgba(20,11,12,.78);
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
}
.promo-dialog[open]{ display:flex; flex-direction:column; animation:promo-in .22s var(--ease); }
@keyframes promo-in{ from{ opacity:0; transform:translateY(8px) scale(.98);} to{ opacity:1; transform:none; } }

.dialog-close{
  position:absolute; top:.85rem; right:.85rem;
  width:34px; height:34px;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--line-strong); color:var(--paper-dim);
  transition:border-color .2s var(--ease), color .2s var(--ease), background .2s var(--ease);
}
.dialog-close svg{ width:14px; height:14px; }
.dialog-close:hover, .dialog-close:focus-visible{ border-color:var(--oxblood-lift); background:var(--oxblood-lift); color:var(--paper); }

.dialog-body{
  padding:clamp(2rem,5vw,2.75rem); overflow-y:auto;
  scrollbar-width:thin; scrollbar-color:var(--line-strong) transparent;
}
.dialog-body::-webkit-scrollbar{ width:8px; }
.dialog-body::-webkit-scrollbar-track{ background:transparent; }
.dialog-body::-webkit-scrollbar-thumb{ background:var(--line-strong); border:2px solid var(--ink-card); }
.dialog-body::-webkit-scrollbar-thumb:hover{ background:var(--oxblood-lift); }
.dialog-body .dialog-eyebrow{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em; text-transform:uppercase; color:var(--oxblood-lift);
}
.dialog-body h3{
  font-family:var(--f-display); text-transform:uppercase; font-weight:400; letter-spacing:-.015em;
  font-size:clamp(1.4rem,4vw,1.9rem); margin-top:.6rem; margin-bottom:1.1rem;
}
.dialog-body p{ color:var(--paper-dim); font-size:var(--t-body); line-height:1.7; }
.dialog-body p + p{ margin-top:.9rem; }

/* ==========================================================================
   About: the one full-bleed oxblood field, so the site states the sleeve's
   own colour somewhere at full strength rather than only in accents.
   ========================================================================== */
.about{
  background:var(--oxblood);
  border-top:1px solid rgba(0,0,0,.35);
  border-bottom:1px solid rgba(0,0,0,.35);
  padding-block:clamp(5rem,11vw,9rem);
  position:relative; overflow:hidden;
}
.about::before{
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:
    linear-gradient(rgba(0,0,0,.10) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,.10) 1px, transparent 1px);
  background-size:96px 96px;
}
.about .container{ position:relative; display:grid; grid-template-columns:1fr; gap:3.5rem; }
/* The second label mark, the tangle, in place of the three concentric circles
   that stood here until 22 September 2026. White ink with the wordmark knocked
   back to the band colour: the band is --oxblood, a mid tone, so unlike
   the header the black has plenty of contrast and needs no inverting. Martin
   chose white over the black on 22 September 2026. 180px, because at 128 the
   tangle turns to mush and the wordmark inside it stops being readable. */
.about-mark{ width:180px; height:auto; flex:none; }
.about-mark img{ width:100%; height:auto; display:block; }
.about-text{ max-width:62ch; }
.about-text p{ font-size:clamp(1.05rem,2vw,1.4rem); line-height:1.5; color:var(--paper); }
.about-text p + p{ margin-top:1.1rem; color:rgba(255,255,255,.78); font-size:var(--t-body); }
.about-text .textlink{ color:var(--paper); text-decoration:underline; text-decoration-color:rgba(255,255,255,.45); text-underline-offset:3px; }
.about-text .textlink:hover{ text-decoration-color:var(--paper); }

@media (min-width:760px){
  .about .container{ grid-template-columns:auto 1fr; align-items:start; gap:clamp(3.5rem,9vw,7rem); }
}

/* ==========================================================================
   Footer
   ========================================================================== */
/* --------------------------------------------------------------------------
   NEWSLETTER
   A plain form posting to our own endpoint, which talks to MailerLite server
   side. Their embed script would put third party JavaScript and cookies on
   every page of a site that currently loads nothing from anyone else.
   -------------------------------------------------------------------------- */
.footer-signup{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:1.5rem 2rem;
  padding-bottom:2.25rem;
  border-bottom:1px solid var(--line);
}
.signup-title{
  display:block;
  font-family:var(--f-display); font-weight:400; text-transform:uppercase;
  font-size:clamp(1.05rem,2.6vw,1.5rem); letter-spacing:-.01em; line-height:1.1;
}
.signup-sub{ margin-top:.45rem; color:var(--paper-dim); font-size:var(--t-sub); max-width:40ch; }
.signup-form{ display:flex; align-items:stretch; gap:.7rem; flex-wrap:wrap; }
.signup-input{ min-width:min(300px,72vw); }
.signup-result{ flex-basis:100%; font-size:var(--t-sub); color:var(--paper-dim); }
.signup-result.is-ok{ color:var(--oxblood-lift); }
.signup-result.is-error{ color:#e6a3a3; }
.signup-form.is-done .signup-input{ opacity:.55; pointer-events:none; }

/* Read by a screen reader, seen by nobody. */
.visually-hidden{
  position:absolute; width:1px; height:1px; margin:-1px;
  padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}

.site-footer{ padding-block:2.5rem; }
.site-footer .container{
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:1.25rem;
  padding-top:2rem;
}
.footer-copy{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--paper-faint); }
.footer-links{ display:flex; flex-wrap:wrap; gap:.6rem 1.75rem; min-width:0; }
.footer-links a{ font-size:var(--t-small); color:var(--paper-dim); text-decoration:none; }
.footer-links a:hover{ color:var(--oxblood-lift); }

/* The four accounts as marks rather than words. Six names in the footer nav
   turned the site's own navigation into a list of platforms, and they were
   about to outnumber it. The keyline square is the same device as the play
   buttons, the buy buttons and the info button, so the marks read as controls
   rather than floating loose. */
/* min-width:0 because a flex item defaults to min-width:auto and refuses to
   shrink below its content. Without it this group stayed as wide as the icon
   row plus all four links and pushed "Privacy Policy" off the right edge of a
   phone instead of wrapping. */
.footer-right{
  display:flex; flex-wrap:wrap; align-items:center; gap:.9rem 1.75rem;
  min-width:0; max-width:100%;
}
.footer-social{ display:flex; align-items:center; gap:.5rem; }
.footer-social a{
  display:flex; align-items:center; justify-content:center;
  width:32px; height:32px; flex:none;
  border:1px solid var(--line-strong);
  color:var(--paper-dim);
  transition:background .2s var(--ease), color .2s var(--ease), border-color .2s var(--ease);
}
.footer-social svg{ width:15px; height:15px; flex:none; fill:currentColor; }
.footer-social a:hover, .footer-social a:focus-visible{
  border-color:var(--oxblood-lift); background:var(--oxblood-lift); color:var(--paper);
}
/* The marks carry very different amounts of ink. Matching their boxes makes
   the f look undersized and the butterfly look enormous, so they are sized by
   eye rather than by number. */
.footer-social a[aria-label="Facebook"] svg{ transform:scale(1.1); }
.footer-social a[aria-label="Bluesky"]  svg{ transform:scale(.92); }
.footer-social a[aria-label="X"]        svg{ transform:scale(.94); }
.footer-social a[aria-label="YouTube"]  svg{ transform:scale(.86); }
.footer-social a[aria-label="TikTok"]   svg{ transform:scale(1.02); }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width:900px){
  /* Between a desk and a phone the mark comes down in one step. Without this
     the 160px version holds all the way to 641px and crowds the nav. */
  :root{ --brand-h:112px; }
  .hero-lower{ flex-direction:column-reverse; align-items:flex-start; gap:1.75rem; }
  .hero-side{ width:min(400px,75vw); margin-inline:auto; }
  .hero-tracklist{ width:100%; }
  .release-links .link-row,
  .hero-side .link-row{ justify-content:center; }
  .release{ grid-template-columns:1fr; }
  .release-art{ width:min(400px,100%); margin-inline:auto; }
}

@media (max-width:640px){
  /* Six icons plus the wordmark do not fit across a phone. The wordmark is
     already a link home, so the house is a duplicate and goes first, then the
     targets tighten. */
  .nav{ gap:.4rem; }
  .nav a{ width:38px; height:38px; }
  .nav a .nav-icon{ width:21px; height:21px; }
  .nav a[aria-label="Home"]{ display:none; }
  :root{ --brand-h:76px; }
  .about-mark{ width:140px; }
  .scroll-cue{ display:none; }
  .section-head{ flex-direction:column; align-items:flex-start; gap:.5rem; }
  .hero-rays{ display:none; }
  /* Five links will not sit on one phone line. Let them wrap, and stop the
     longest one breaking mid-phrase into a three line column. */
  .footer-links{ gap:.55rem 1.1rem; }
  .footer-links a{ white-space:nowrap; }
  .footer-right{ gap:.8rem 1.1rem; }
}

/* The last of the small phones. Five icons and the wordmark still have to sit
   on one line, and the icon that gets clipped otherwise is Send us a track. */
@media (max-width:360px){
  .nav{ gap:.28rem; }
  .nav a{ width:34px; height:34px; }
  .nav a .nav-icon{ width:19px; height:19px; }
  :root{ --brand-h:64px; }
}

/* ==========================================================================
   Submission form
   ========================================================================== */
.page-head{
  padding-top:calc(var(--header-h) + 3.5rem);
  padding-bottom:clamp(2rem,5vw,3rem);
  border-bottom:1px solid var(--line);
}
.page-head h1{
  font-family:var(--f-display); font-weight:400; text-transform:uppercase;
  font-size:clamp(2.25rem,6vw,4rem); line-height:.98; letter-spacing:-.02em;
}
.page-lead{ margin-top:1.25rem; max-width:52ch; color:var(--paper-dim); font-size:var(--t-lead); }

/* Not a real field. Off screen and out of the tab order, so a person never
   meets it and a script that fills everything gives itself away. Kept out of
   display:none, which some bots check for. */
.honeypot{
  position:absolute; left:-9999px; top:auto;
  width:1px; height:1px; overflow:hidden;
}

/* ==========================================================================
   THANKS
   Where both forms land. The confirmation used to sit under the send button,
   below the fold on most screens, where it was easy to miss entirely.
   ========================================================================== */
.thanks{
  min-height:calc(100svh - var(--header-h));
  display:flex; align-items:center;
  padding-block:clamp(4rem,10vw,8rem);
  border-bottom:1px solid var(--line);
}
.thanks .container{ width:100%; }
.thanks-inner{ max-width:52ch; }
.thanks-tick{
  display:block; width:clamp(48px,7vw,64px); height:clamp(48px,7vw,64px);
  color:var(--oxblood-lift); margin-bottom:clamp(1.75rem,4vw,2.5rem);
}
.thanks-tick svg{ width:100%; height:100%; }
.thanks h1{
  font-family:var(--f-display); font-weight:400; text-transform:uppercase;
  font-size:clamp(2.75rem,9vw,6rem); line-height:.92; letter-spacing:-.025em;
}
.thanks-line{
  margin-top:clamp(1.5rem,3vw,2rem);
  font-size:clamp(1.1rem,2.2vw,1.45rem); line-height:1.45; color:var(--paper);
}
.thanks-sub{ margin-top:.9rem; color:var(--paper-dim); font-size:var(--t-body); }
.thanks-actions{
  display:flex; flex-wrap:wrap; gap:.9rem;
  margin-top:clamp(2.25rem,5vw,3.25rem);
}

/* ==========================================================================
   LEGAL PAGES
   Long-form text set narrow. Display face for the section headings, mono for
   the small print, body copy dimmed so the headings carry the structure.
   ========================================================================== */
.legal{ padding-block:clamp(2.5rem,6vw,4.5rem); }
.legal-body{ max-width:68ch; }
.legal-updated{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.14em;
  text-transform:uppercase; color:var(--paper-faint);
}
.legal-body h2{
  font-family:var(--f-display); font-weight:400; text-transform:uppercase;
  font-size:clamp(1.05rem,2.4vw,1.45rem); letter-spacing:-.01em; line-height:1.1;
  margin-top:clamp(2.5rem,5vw,3.5rem); padding-top:1.35rem;
  border-top:1px solid var(--line);
}
.legal-body p{ margin-top:1rem; color:var(--paper-dim); line-height:1.65; }
.legal-body p.lead{ color:var(--paper); font-size:var(--t-lead); }
.legal-body ul{ margin-top:1.25rem; padding-left:0; list-style:none; }
.legal-body li{
  position:relative; padding-left:1.6rem; margin-top:.8rem;
  color:var(--paper-dim); line-height:1.65;
}
/* A square bullet, to match the play buttons rather than fight them. */
.legal-body li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:6px; height:6px; background:var(--oxblood-lift);
}
.legal-body strong{ color:var(--paper); font-weight:600; }

/* A line that carries the whole argument, set like a pull quote. */
.legal-body .pull{
  margin-top:1.25rem;
  padding-left:1.25rem;
  border-left:2px solid var(--oxblood-lift);
  color:var(--paper);
  font-size:clamp(1.05rem,2.1vw,1.3rem); line-height:1.45;
}

/* Figures. Mono for the numbers so the columns line up and read as readings
   rather than prose. Scrolls sideways on a phone instead of squashing. */
.data-wrap{ margin-top:1.5rem; overflow-x:auto; }
.data{ width:100%; border-collapse:collapse; font-size:var(--t-sub); }
.data th, .data td{
  padding:.65rem .9rem;
  border-bottom:1px solid var(--line);
  text-align:left; white-space:nowrap;
}
.data thead th{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; color:var(--paper-faint); font-weight:400;
  border-bottom-color:var(--line-strong);
}
.data tbody th{ font-weight:500; color:var(--paper); }
.data td{ color:var(--paper-dim); }
.data .num{ text-align:right; font-family:var(--f-mono); }
.data tbody tr:hover th, .data tbody tr:hover td{ background:rgba(255,255,255,.03); }
.legal-body a{
  color:var(--paper); text-decoration:underline;
  text-decoration-color:rgba(255,255,255,.35); text-underline-offset:3px;
}
.legal-body a:hover{ text-decoration-color:var(--paper); }

.submit-section{ padding-block:clamp(2.5rem,6vw,4.5rem); }
.submit-form{ max-width:760px; display:flex; flex-direction:column; gap:clamp(1.5rem,3vw,2rem); }

.field{ display:flex; flex-direction:column; gap:.55rem; }
.field-row{ display:grid; grid-template-columns:1fr; gap:clamp(1.5rem,3vw,2rem); }
@media (min-width:640px){ .field-row{ grid-template-columns:1fr 1fr; } }

/* Six named link fields rather than one comma-soup box: people fill in what
   they have and we get labelled values instead of guessing what a URL is. */
.link-grid{ display:grid; grid-template-columns:1fr; gap:clamp(.9rem,2vw,1.15rem); margin-top:.15rem; }
@media (min-width:640px){ .link-grid{ grid-template-columns:1fr 1fr; } }
.link-item{ display:flex; flex-direction:column; gap:.4rem; cursor:text; }
.link-name{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; color:var(--paper-faint);
}
.link-item:focus-within .link-name{ color:var(--concrete); }

.field-label{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em; text-transform:uppercase;
  color:var(--concrete);
}
.field-optional{ color:var(--paper-faint); letter-spacing:.06em; }
.field-help{ font-size:var(--t-small); color:var(--paper-faint); }
.field-error{ font-size:var(--t-sub); color:#ff8a8a; }

.input{
  font:inherit; color:var(--paper);
  background:var(--ink-raised);
  border:1px solid var(--line-strong);
  padding:.8rem .9rem;
  width:100%;
  transition:border-color .2s var(--ease), background .2s var(--ease);
}
.input::placeholder{ color:var(--paper-faint); }
.input:hover{ border-color:rgba(255,255,255,.3); }
.input:focus{ outline:none; border-color:var(--oxblood-lift); background:#1f1416; }
.textarea{ resize:vertical; min-height:9rem; line-height:1.6; }

/* ---------- dropzone ---------- */
.dropzone{
  position:relative;
  display:flex; align-items:center; justify-content:center;
  min-height:190px;
  padding:1.5rem;
  border:2px dashed var(--line-strong);
  background:var(--ink-raised);
  cursor:pointer;
  transition:border-color .2s var(--ease), background .2s var(--ease);
}
.dropzone:hover{ border-color:rgba(255,255,255,.34); }
.dropzone.is-over{ border-color:var(--paper); background:#241618; border-style:solid; }
.dropzone.has-file{ border-style:solid; border-color:var(--oxblood-lift); background:#1f1416; }

.dz-idle{ display:flex; flex-direction:column; align-items:center; gap:.5rem; text-align:center; pointer-events:none; }
.dz-icon{ width:34px; height:34px; color:var(--concrete); margin-bottom:.25rem; }
.dz-title{ font-family:var(--f-display); text-transform:uppercase; font-size:var(--t-lead); letter-spacing:-.01em; }
.dz-sub{ font-size:var(--t-sub); color:var(--paper-faint); }
.dz-browse{ color:var(--paper); text-decoration:underline; text-underline-offset:3px; }

.dz-chosen{ display:flex; align-items:center; justify-content:space-between; gap:1rem; width:100%; }
.dz-file{ display:flex; flex-direction:column; gap:.3rem; min-width:0; }
.dz-name{ font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dz-size{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--paper-faint); }
.dz-clear{
  width:34px; height:34px; flex:none;
  display:flex; align-items:center; justify-content:center;
  border:1px solid var(--line-strong); color:var(--paper-dim);
  transition:border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.dz-clear svg{ width:14px; height:14px; }
.dz-clear:hover{ border-color:var(--oxblood-lift); background:var(--oxblood-lift); color:var(--paper); }

/* ---------- send ---------- */
.submit-actions{ display:flex; flex-wrap:wrap; align-items:center; gap:1.25rem; }
.btn-send{
  display:inline-flex; align-items:center; gap:.7rem;
  background:var(--oxblood); color:var(--paper);
  border:1px solid var(--oxblood);
  padding:.85rem 1.5rem;
  font-family:var(--f-display); text-transform:uppercase; letter-spacing:.01em;
  font-size:var(--t-body);
  transition:background .2s var(--ease), border-color .2s var(--ease), transform .15s var(--ease);
}
.btn-send .send-icon{ width:18px; height:18px; }
.btn-send:hover{ background:var(--oxblood-lift); border-color:var(--oxblood-lift); }
.btn-send:active{ transform:scale(.98); }
.btn-send:disabled{ opacity:.55; cursor:default; transform:none; }
.submit-note{ font-size:var(--t-sub); color:var(--paper-faint); }

/* ---------- progress ---------- */
.progress-wrap{ display:flex; flex-direction:column; gap:.6rem; }
.progress-head{ display:flex; align-items:baseline; justify-content:space-between; }
.progress-label{ font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em; text-transform:uppercase; color:var(--concrete); }
.progress-pct{ font-family:var(--f-mono); font-size:var(--t-sub); color:var(--paper); }
.progress-track{ height:4px; background:var(--ink-raised); border:1px solid var(--line); overflow:hidden; }
/* Scaled, not widened. Animating width relayouts the element on every progress
   event, which for a file upload is dozens of times a second. A transform is
   composited instead, so the bar costs nothing while the browser is busy
   pushing bytes. transform-origin has to be stated: the default is centre, and
   the bar would grow outwards from the middle.
   Still linear, so it tracks the upload honestly rather than easing past it. */
.progress-bar{
  height:100%; width:100%;
  transform:scaleX(0); transform-origin:left center;
  background:var(--oxblood-lift);
  transition:transform .18s linear;
}

/* ---------- result ---------- */
.form-result{
  padding:1rem 1.1rem;
  border:1px solid var(--line-strong);
  font-size:var(--t-body);
}
.form-result.is-ok{ border-color:var(--oxblood-lift); background:rgba(138,65,73,.18); }
.form-result.is-error{ border-color:#7a3437; background:rgba(122,52,55,.18); color:#ffd7d7; }

.submit-form.is-sending .dropzone{ cursor:default; opacity:.7; }
.submit-form.is-done .dropzone,
.submit-form.is-done .input{ opacity:.6; pointer-events:none; }

/* ---------------------------------------------------------------------------
   NEWS
   A post is a single column of text at a readable measure, with images allowed
   to break out wider than the text. The label's records are square, the
   artwork is square and the photographs came in square, so the grid is square
   and never crops to a different shape.
   --------------------------------------------------------------------------- */
.news-list{ padding-block:0 clamp(3rem,7vw,5.5rem); }

.post + .post{ margin-top:clamp(3.5rem,8vw,6rem); border-top:1px solid var(--line); padding-top:clamp(2.5rem,6vw,4rem); }

.post-meta{
  display:flex; align-items:center; gap:.75rem; flex-wrap:wrap;
  font-family:var(--f-mono); font-size:var(--t-micro);
  text-transform:uppercase; letter-spacing:.08em; color:var(--paper-faint);
}
.post-meta .dot{ color:var(--oxblood-lift); }

.post-title{
  margin:.75rem 0 0; font-family:var(--f-display);
  font-size:clamp(1.9rem,5vw,3.1rem); line-height:1.02; letter-spacing:-.01em;
}

/* 62ch, not the page width. The post body is the one place on this site with
   more than three lines of running text and it needs a measure. */
.post-body{ margin-top:1.5rem; max-width:62ch; }
.post-body p{ color:var(--paper-dim); font-size:var(--t-lead); line-height:1.65; }
.post-body p + p{ margin-top:1rem; }
.post-body strong{ color:var(--paper); font-weight:700; }

/* Inline links in running copy. The body sits at --paper-dim, so a link has to
   lift out of it rather than only be underlined; the underline is offset so it
   does not cut through descenders. */
.post-body a{
  color:var(--paper);
  text-decoration:underline;
  text-decoration-color:var(--oxblood-lift);
  text-underline-offset:3px;
  text-decoration-thickness:1.5px;
}
.post-body a:hover,
.post-body a:focus-visible{ color:var(--oxblood-lift); text-decoration-color:var(--oxblood-lift); }

/* .link-btn is display:flex, so inside a block it would stretch the full
   measure. A flex parent sizes it to its own content instead. */
.post-actions{ margin-top:1.5rem; display:flex; }

/* margin-inline:0 is not decoration. A <figure> carries a UA default of
   `margin: 1em 40px`, so with width:100% it sits 40px right of the text column
   and runs 40px past the container, which at 390px is a 20px horizontal
   scroll on the page. */
/* The lead is capped by height, not just width. The photographs are portrait,
   and a portrait picture set to the full container width is 1600px tall on a
   desktop: you scroll past a wall of it before reaching the galleries. Capping
   the height means the whole print is on screen at once, which is the point of
   a lead image. fit-content on the figure stops the border drawing a box wider
   than the picture inside it. */
.post-lead-img{
  margin:clamp(2rem,5vw,3rem) 0 0;
  width:fit-content; max-width:100%;
  border:1px solid var(--line);
}
.post-lead-img img{
  display:block; width:auto; height:auto;
  max-width:100%; max-height:min(72vh,720px);
}

.figure-note{
  margin-top:.75rem; font-family:var(--f-mono); font-size:var(--t-small);
  color:var(--paper-faint);
}

.gallery-head{
  margin-top:clamp(2.5rem,6vw,4rem);
  display:flex; align-items:baseline; justify-content:space-between; gap:1rem;
  border-bottom:1px solid var(--line); padding-bottom:.75rem;
}
.gallery-head h3{
  font-family:var(--f-display); font-size:1.05rem;
  text-transform:uppercase; letter-spacing:.06em;
}
.gallery-head .count{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--paper-faint); }

.gallery{
  margin-top:1.25rem; display:grid; gap:10px;
  grid-template-columns:repeat(4,1fr);
}
.shot{
  position:relative; display:block; aspect-ratio:1/1; overflow:hidden;
  background:var(--ink-card); border:1px solid var(--line);
  cursor:zoom-in;
}
.shot img{
  display:block; width:100%; height:100%; object-fit:cover;
  transition:transform .45s var(--ease), opacity .3s var(--ease);
}
.shot:hover img,
.shot:focus-visible img{ transform:scale(1.04); }
.shot:focus-visible{ outline:2px solid var(--oxblood-lift); outline-offset:2px; }

/* --- lightbox ------------------------------------------------------------ */
.lightbox{
  border:0; padding:0; max-width:none; max-height:none;
  width:100%; height:100%; background:transparent;
}
.lightbox::backdrop{ background:rgba(10,6,7,.93); }
.lb-inner{
  width:100%; height:100%; display:flex; align-items:center; justify-content:center;
  padding:clamp(1rem,5vw,3rem);
}
.lb-inner img{ max-width:100%; max-height:100%; object-fit:contain; display:block; }
.lb-close{
  position:fixed; top:1rem; right:1rem; width:44px; height:44px;
  display:grid; place-items:center; background:var(--ink-raised);
  border:1px solid var(--line-strong); color:var(--paper); cursor:pointer;
}
.lb-close svg{ width:20px; height:20px; }
.lb-cap{
  position:fixed; left:0; right:0; bottom:0; padding:1rem clamp(1rem,5vw,3rem);
  font-family:var(--f-mono); font-size:var(--t-small); color:var(--paper-dim);
  text-align:center; background:linear-gradient(transparent,rgba(10,6,7,.8));
}

@media (max-width:900px){ .gallery{ grid-template-columns:repeat(3,1fr); } }
@media (max-width:640px){ .gallery{ grid-template-columns:repeat(2,1fr); gap:8px; } }

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

/* --- carousel ------------------------------------------------------------
   A scroll-snap track, so the swipe belongs to the browser rather than to a
   drag handler. Three things fall out of that: there is no drag script to
   fight the lightbox click, it still works with JS off, and momentum feels
   native on a phone. Buttons, dots and the counter are enhancement on top.

   .carousel-slide overrides .shot deliberately: .shot is square and crops,
   which is right for the screen print grid and wrong for these, which are
   3:2 and have the sleeve running to both edges. */
.carousel{ margin:clamp(2rem,5vw,3rem) 0 0; }
/* The arrows anchor here, not to .carousel, or the footer below the track
   drags their 50% centre line down off the picture. */
.carousel-view{ position:relative; }
.carousel-track{
  display:flex; gap:10px;
  overflow-x:auto; overscroll-behavior-x:contain;
  scroll-snap-type:x mandatory;
  scrollbar-width:none; -ms-overflow-style:none;
}
.carousel-track::-webkit-scrollbar{ display:none; }
.carousel-track:focus-visible{ outline:2px solid var(--oxblood-lift); outline-offset:3px; }

.carousel-slide{
  flex:0 0 100%; scroll-snap-align:start;
  aspect-ratio:auto; cursor:zoom-in;
}
.carousel-slide img{ aspect-ratio:3/2; object-fit:cover; }


.carousel-btn{
  position:absolute; top:50%; transform:translateY(-50%);
  width:44px; height:44px; display:grid; place-items:center;
  background:rgba(20,11,12,.74); color:var(--paper);
  border:1px solid var(--line-strong); cursor:pointer;
  transition:background .2s var(--ease), opacity .2s var(--ease);
}
.carousel-btn:hover{ background:rgba(20,11,12,.94); }
.carousel-btn:focus-visible{ outline:2px solid var(--paper); outline-offset:2px; }
.carousel-btn:disabled{ opacity:0; pointer-events:none; }
.carousel-btn svg{ width:20px; height:20px; }
.carousel-prev{ left:.6rem; }
.carousel-next{ right:.6rem; }

.carousel-foot{
  display:flex; align-items:center; justify-content:space-between;
  gap:1rem; margin-top:.9rem;
}
.carousel-dots{ display:flex; gap:6px; flex-wrap:wrap; }
.carousel-dot{
  width:8px; height:8px; padding:0; border:0; cursor:pointer;
  background:rgba(255,255,255,.26);
  transition:background .2s var(--ease);
}
.carousel-dot:hover{ background:rgba(255,255,255,.5); }
.carousel-dot.is-on{ background:var(--paper); }
.carousel-dot:focus-visible{ outline:2px solid var(--oxblood-lift); outline-offset:3px; }
.carousel-now{
  font-family:var(--f-mono); font-size:var(--t-micro);
  letter-spacing:.12em; color:var(--paper-faint); white-space:nowrap;
}

/* --- lightbox paging ----------------------------------------------------- */
.lb-prev,.lb-next{
  position:fixed; top:50%; transform:translateY(-50%);
  width:44px; height:44px; display:grid; place-items:center;
  background:var(--ink-raised); border:1px solid var(--line-strong);
  color:var(--paper); cursor:pointer;
}
.lb-prev{ left:1rem; }
.lb-next{ right:1rem; }
.lb-prev svg,.lb-next svg{ width:20px; height:20px; }
.lb-prev[hidden],.lb-next[hidden]{ display:none; }

/* The caption sits where the arrows are on a phone, so move them up. */
@media (max-width:640px){
  .carousel-btn{ display:none; }        /* swipe is the interaction here */
  .lb-prev,.lb-next{ top:auto; bottom:4.5rem; transform:none; }
}

@media (prefers-reduced-motion:reduce){
  .carousel-track{ scroll-behavior:auto; }
  .carousel-btn,.carousel-dot{ transition:none; }
}
