@font-face{font-family:Inter;font-style:normal;font-weight:600;font-display:swap;src:url("/v2/assets/fonts/inter-600.woff2?v=20") format("woff2")}
@font-face{font-family:Inter;font-style:normal;font-weight:400;font-display:swap;src:url("/v2/assets/fonts/inter-400.woff2?v=20") format("woff2")}
@font-face{font-family:Unbounded;font-style:normal;font-weight:700;font-display:swap;src:url("/v2/assets/fonts/unbounded-700.woff2?v=20") format("woff2")}

/* Juice FM, Night Feed. One stylesheet, no framework, no CDN. */
:root{
  --bg:#0B0B0F;          /* the ground, nothing sits behind it */
  --panel:#13131A;       /* raised panel */
  --panel-2:#1A1A24;     /* nested panel and hover plane */
  --line:#20202B;        /* hairline, the only divider on the site */
  --ink:#FFFFFF;
  --muted:#8E8E9F;       /* 6.1:1 on the ground, 5.3:1 on a panel */
  --brand:#E5007E;       /* fills: buttons, pills, bars, the wave lines */
  --brand-ink:#FF3D9A;   /* magenta TEXT. #E5007E reads 4.3:1 on the ground,
                            under the 4.5 minimum, so small magenta type uses this */
  --bloom:0 0 16px rgba(229,0,126,.45), 0 0 2px rgba(229,0,126,.9);
  --wrap:1200px;
  --sec:64px;            /* one section padding, used by every section */
  --gap:20px;
  --player:72px;
  --hdr:64px;
}
@media (min-width:900px){ :root{ --sec:96px; --hdr:72px; } }

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font:400 17px/1.6 Inter,system-ui,-apple-system,"Segoe UI",Helvetica,Arial,sans-serif;
  padding-bottom:calc(var(--player) + env(safe-area-inset-bottom));
  overflow-x:hidden;
}
img,svg{max-width:100%}
img{height:auto}
a{color:var(--ink)}
h1,h2,h3{
  font-family:Unbounded,Inter,system-ui,sans-serif; font-weight:700;
  letter-spacing:-.02em; line-height:1.04; margin-block:0; text-wrap:balance;
}
h1{font-size:clamp(34px,7.4vw,64px)}
h2{font-size:clamp(26px,4.2vw,40px)}
h3{font-size:clamp(18px,2.2vw,24px); line-height:1.15}
p{margin-block:0 1em}
p:last-child{margin-bottom:0}
ul{margin-block:0 1em; padding-inline-start:1.1em}
.wrap{max-width:var(--wrap); margin-inline:auto; padding-inline:20px}
section{padding-block:var(--sec)}
section + section{border-top:1px solid var(--line)}
/* Anything with an id can be an anchor target, and the header is fixed, so every
   one of them has to stop clear of it or the heading lands underneath. */
[id]{scroll-margin-top:calc(var(--hdr) + 20px)}
/* A page that does not open on a hero still has to clear the fixed header, and by
   the same amount, or the first eyebrow sits on the header's own hairline. */
main > section:first-child:not(.hero){padding-top:calc(var(--hdr) + 40px)}
.eyebrow{
  font:600 12px/1.3 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  color:var(--brand-ink); margin-block:0 14px;
}
.lede{font-size:clamp(17px,2.2vw,19px); color:#E7E5EC; max-width:62ch; margin-inline:auto}
/* ONE gap between a heading and the line under it, on every page and in every
   section head, from one token. It was 20px in the hero and 0 everywhere else, and
   later a scoped --hgap knocked it back to 0 on every inner page hero. */
:root{--headgap:16px}
h1 + .lede, h2 + .lede, h1 + p, h2 + p{margin-block:var(--headgap) 0}
.muted{color:var(--muted)}
.tnum{font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1}
.centre{text-align:center}
.note{font-size:15px;margin-block:0}
.mt22{margin-block:22px 0}
.mt26{margin-block:26px 0}
.mt30{margin-block:30px 0}
.hero.tight{padding-bottom:0}
.head.tight{margin-bottom:0}
.hero-cta.centred{justify-content:center}
.hero.pbsec{padding-bottom:var(--sec)}
.mt14{margin-block:14px 0}
.mt26a{margin-block:26px auto;max-width:62ch}
.small{font-size:14px}
.head{text-align:center; margin-block:0 40px}
.head p{margin-inline:auto}
.skip{position:absolute;left:-9999px;top:0;display:inline-flex;align-items:center;
  min-height:44px;padding:0 16px}
.skip:focus-visible{left:12px;top:12px;z-index:90;background:var(--brand);color:#fff}

/* Soft navigation. A page change swaps <main> and leaves the audio engine alone,
   so the hairline is the only thing that says a fetch is running. It sits under
   the header, is 2px, and is the only motion the swap makes. */
.navbar{position:fixed; left:0; top:var(--hdr); height:2px; width:0; z-index:70;
  background:var(--brand); opacity:0; pointer-events:none;
  transition:width .18s linear, opacity .18s linear}
.navbar[data-on]{opacity:1}
@media (prefers-reduced-motion:reduce){ .navbar{transition:none} }
/* Read by a screen reader, never seen. clip-path over display:none because a
   hidden element is not announced. */
.vh{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden;
  clip-path:inset(50%); white-space:nowrap; border:0}
/* A ring is for somebody navigating by keyboard, so it is :focus-visible and never
   bare :focus, everywhere on this site.
   ⛔ AND NEVER ON THE SOFT NAVIGATION TARGET. A page change moves focus to the new
   h1 so a screen reader announces it, and Ashton tapped a nav link and got a magenta
   box drawn round the headline. The h1 is not a control: it is focused so it can be
   READ, and a ring on it is the site telling a visitor they pressed something they
   did not press. */
:focus-visible{outline:2px solid var(--brand-ink); outline-offset:3px}
#main > h1[tabindex="-1"]:focus,
#main h1[tabindex="-1"]:focus,
#main h1[tabindex="-1"]:focus-visible{outline:none; box-shadow:none}
/* iOS paints its own blue-grey flash over anything tappable, which reads as a bug on
   a near-black page. Every control below has a real :active state instead. */
a,button,[role="button"],summary,label,input,select,textarea{
  -webkit-tap-highlight-color:transparent}
.btn:active,.pbtn:active,.livepill:active,.mute:active,.map-go:active,
.card:active,.tlink:active,.socials a:active,.xclose:active,.close:active{
  filter:brightness(.88)}

/* ---------------------------------------------------------------- controls */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:10px;
  min-height:52px; padding:0 22px; border:1px solid var(--brand); border-radius:0;
  background:var(--brand); color:#fff; cursor:pointer;
  font:600 13px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.12em;
  text-decoration:none; transition:background .15s linear, box-shadow .15s linear;
}
.btn.ghost{background:var(--panel); border-color:var(--line); color:var(--ink)}
.tlink{
  display:inline-flex; align-items:center; gap:8px; min-height:44px; color:var(--brand-ink);
  font:600 14px/1 Inter,sans-serif; text-decoration:underline;
  text-decoration-color:rgba(255,61,154,.45); text-underline-offset:4px;
}

/* ------------------------------------------------------------------ header */
.hdr{
  position:fixed; inset:0 0 auto 0; z-index:60; height:var(--hdr);
  display:flex; align-items:center; background:transparent;
  border-bottom:1px solid transparent; transition:transform .24s ease, background .2s linear, border-color .2s linear;
}
.hdr.solid{background:var(--bg); border-bottom-color:var(--line)}
.hdr.up{transform:translateY(-100%)}
.hdr .wrap{display:flex; align-items:center; gap:16px; width:100%}
.brand{display:flex; align-items:center; min-height:44px; margin-inline-end:auto}
/* Width only. The height comes from the file's own ratio through the global
   img{height:auto} and the width/height attributes, so a new logo master cannot
   be squashed by a hard-coded height that nobody thought to update. */
.brand img{width:132px}
@media (min-width:900px){ .brand img{width:158px} }
.nav{display:none; gap:22px}
.nav a{
  display:inline-flex; align-items:center; min-height:44px;
  font:600 13px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.12em;
  text-decoration:none; color:var(--ink); opacity:.82;
}
@media (min-width:900px){ .nav{display:flex} }
.livepill{
  display:inline-flex; align-items:center; gap:8px; height:44px; padding:0 14px;
  border:1px solid var(--brand); background:rgba(229,0,126,.12); color:#fff;
  font:600 11px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  cursor:pointer; border-radius:0;
}
.dot{width:8px; height:8px; background:var(--brand); box-shadow:var(--bloom); flex:none}
.burger{
  width:44px; height:44px; display:grid; place-items:center; background:none;
  border:1px solid var(--line); color:#fff; cursor:pointer; border-radius:0;
}
@media (min-width:900px){ .burger{display:none} }
.menu{
  position:fixed; inset:var(--hdr) 0 auto 0; z-index:55; background:var(--bg);
  border-bottom:1px solid var(--line); padding:8px 0 18px; display:none;
}
.menu[data-open]{display:block}
.menu a{
  display:flex; align-items:center; min-height:44px; padding:0 20px;
  border-top:1px solid var(--line);
  text-decoration:none; font:600 14px/1 Inter,sans-serif; text-transform:uppercase;
  letter-spacing:.12em;
}

/* -------------------------------------------------------------------- hero */
.hero{position:relative; overflow:hidden; padding-block:calc(var(--hdr) + 40px) 0; border-top:0}
.waves{position:absolute; inset:0; width:100%; height:100%; pointer-events:none}
/* The symbol is sliced to cover, so on a narrow tall hero the curves land at their
   steepest exactly where a phone centres the copy, and every line of the headline
   and the lede had a magenta rule through it. On a wide hero the same lines fall in
   the empty half and read as the texture they are meant to be. So they come down on
   a phone rather than being cut: same atmosphere, off the type. */
@media (max-width:899px){ .waves{opacity:.5} }
.wavedefs{position:absolute; width:0; height:0; overflow:hidden}
.hero .wrap{position:relative}
/* ONE rhythm token down the hero: eyebrow, headline, lede, buttons, figure are all
   separated by the same distance, so the hero reads as one block rather than five
   things that happen to be stacked. */
.hero{--hgap:24px}
.hero-grid{display:grid; gap:var(--hgap); align-items:end}
.hero-copy{text-align:center; padding-block:0}
.hero h1 span{display:block}
.hero h1{margin-block:var(--hgap) 0}
.hero .lede{margin-block:var(--headgap) 0}
.hero-cta{display:flex; flex-wrap:wrap; gap:16px; justify-content:center; align-items:center;
  margin-block:var(--hgap) 0}
.hero-art{position:relative; justify-self:center; width:min(100%,420px)}
.hero-art::before{
  content:""; position:absolute; left:50%; top:6%; width:120%; aspect-ratio:1;
  transform:translateX(-50%);
  background:radial-gradient(circle, rgba(229,0,126,.42) 0%, rgba(229,0,126,.14) 42%, rgba(229,0,126,0) 68%);
}
.hero-art img{position:relative; display:block; width:100%; height:auto}
/* ⛔ THE FIGURE IS CAPPED ON A PHONE (Ashton, 08/09, on his own screen). At its own
   proportions it drew 467px at 390 and pushed the whole hero past a screen and a
   quarter, so the first thing a listener saw was a photograph and the second thing
   was more photograph. 44vh is a bit over a third of the screen: the copy, the
   Listen live button AND the figure fit above the fold, and the on air strip is
   reachable in one thumb. Cover from the top keeps the head, the headphones and the
   hands and takes the crop out of the bottom of the jumper, which carries nothing.
   The desktop hero is untouched. */
@media (max-width:899px){
  .hero-art{height:44vh; max-height:44vh; overflow:hidden}
  .hero-art img{height:100%; object-fit:cover; object-position:50% 0}
}
@media (min-width:900px){
  .hero{padding-block:calc(var(--hdr) + 64px) 0}
  .hero{--hgap:40px}
  .hero-grid{grid-template-columns:1.15fr .85fr}
  /* The two columns stay; the copy centres INSIDE its own column rather than
     ranging left against it (Ashton, 08/09). The phone layout is already centred,
     so desktop only takes back the three overrides that pushed it left. */
  .hero-copy{text-align:center; padding-block:0 72px}
  .hero .lede{margin-inline:auto}
  .hero-cta{justify-content:center}
  .hero-art{justify-self:end; width:min(100%,430px)}
}

/* --------------------------------------------------------------- on air bar */
.onair{
  position:relative; overflow:hidden; background:var(--panel);
  border-block:1px solid var(--line); padding-block:0;
}
.onair .waves{opacity:.5}
.onair-in{
  position:relative; display:flex; align-items:center; justify-content:center;
  gap:26px; min-height:96px; padding-block:18px; flex-wrap:wrap; text-align:center;
}
.onair-line{display:flex; align-items:center; gap:12px; min-width:0}
.oa-main{display:flex; align-items:center; gap:12px; min-width:0; flex-wrap:wrap;
  justify-content:center}
.oa-label{
  display:inline-flex; align-items:center; gap:10px;
  font:600 11px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  color:var(--brand-ink); white-space:nowrap; flex:none;
}
/* Unbounded is the display face, so it takes the display leading. Left to inherit
   the body's 1.6 a two-line show name opened up to 27px of leading and read as two
   separate lines rather than one title. Same for every other title below. */
.oa-show{font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:17px;
  line-height:1.2; letter-spacing:-.01em}
.oa-time{color:var(--muted); font:600 14px/1 Inter,sans-serif; white-space:nowrap}
.onair-sep{width:1px; height:34px; background:var(--line); flex:none}
/* A phone stacks it: label over value, twice, no separator. As one wrapping row
   the label and the time floated against the second line of a wrapped show name
   and the separator ended up on a line of its own, dividing nothing.
   767 is where the whole strip measurably stops fitting on one row: at 768 it is
   96px tall and one row, at 700 it wraps to 123 and leaves the separator trailing
   the end of the first row, separating nothing again. Measured, not picked. */
@media (max-width:767px){
  .onair-in{flex-direction:column; gap:16px; min-height:0; padding-block:22px}
  .onair-sep{display:none}
  .onair-line{flex-direction:column; gap:8px}
}
.np[hidden]{display:none !important}
/* Art left, words right, the whole thing centred as one block. Each line is ONE
   line on a phone and clipped rather than wrapped: a title that wraps drops the
   artist onto a third line and the strip changes height as the songs change. */
.npbox{display:flex; align-items:center; gap:12px; min-width:0;
  max-width:420px; margin-inline:auto; text-align:left}
.np-body{min-width:0; flex:1 1 auto}
.np-text{display:block; min-width:0}
.np-track{display:block; font-weight:600; color:var(--ink); font-size:16px;
  line-height:1.3; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.np-artist{display:block; color:var(--muted); font-size:14px; line-height:1.35;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
@media (min-width:900px){
  .npbox{max-width:none}
  .np-track,.np-artist{white-space:normal; overflow:visible}
}
/* ⛔ TWO FITS, and the difference matters. Cover on a square cover photo is right.
   Cover on the LOGO cropped it to a sliver of the J stroke, which read as a broken
   image. The fallback is a whole mark on a panel with air around it, so it is
   contained and padded; real art fills the tile edge to edge. */
.np-art{flex:0 0 auto; display:block; width:56px; height:56px; overflow:hidden;
  border:1px solid var(--line); background:var(--panel)}
.np-art img{display:block; width:100%; height:100%; object-fit:cover; padding:0;
  background:var(--panel)}
.np-art img[data-fit="mark"]{object-fit:contain; padding:6px; background:#15141A}
/* ⚠️ scaleY, not height. The old pair animated `height` on a flex child, which asks
   the browser to lay the row out again sixty times a second for four 3px bars, and
   on a phone it simply did not run. A transform is composited and cannot reflow
   anything. Four different durations rather than one with delays, so the bars drift
   out of step instead of marching, which is what a level meter looks like. */
.eq{display:inline-flex; align-items:flex-end; gap:3px; height:16px; flex:none}
.eq i{width:3px; height:16px; display:block; background:var(--brand);
  transform-origin:50% 100%; transform:scaleY(.25)}
/* ONE driver for both sets of bars: the strip is not inside the player, so the flag
   lives on the root element and the audio's own paused state sets it. */
.is-playing .eq i{animation:eq 1s ease-in-out infinite}
.is-playing .eq i:nth-child(1){animation-duration:.9s;  animation-delay:0s}
.is-playing .eq i:nth-child(2){animation-duration:1.3s; animation-delay:-.35s}
.is-playing .eq i:nth-child(3){animation-duration:1.05s;animation-delay:-.6s}
.is-playing .eq i:nth-child(4){animation-duration:1.15s;animation-delay:-.15s}
@keyframes eq{0%,100%{transform:scaleY(.22)} 50%{transform:scaleY(1)}}

/* -------------------------------------------------------------- the timeline */
.tl{list-style:none; margin:0; padding:0}
.tl li{position:relative; display:grid; grid-template-columns:22px 1fr; gap:14px; padding-block:16px}
.tl li + li{border-top:1px solid var(--line)}
.tl .tick{position:relative; padding-block:6px}
.tl .tick::before{content:""; position:absolute; left:9px; top:0; bottom:-17px; width:1px; background:var(--line)}
.tl li:last-child .tick::before{bottom:50%}
.tl .tick::after{
  content:""; position:absolute; left:5px; top:9px; width:9px; height:9px;
  background:var(--bg); border:1px solid var(--line);
}
.tl li.now .tick::after{background:var(--brand); border-color:var(--brand); box-shadow:var(--bloom)}
.tl .body{display:flex; align-items:center; gap:12px; min-width:0; text-align:left}
.tl .t{display:block; font:600 13px/1.2 Inter,sans-serif; color:var(--muted); letter-spacing:.02em}
.tl li.now .t{color:var(--brand-ink)}
/* The padding is what makes the show name a 44px tap target; the matching negative
   margin means the row looks exactly as it did before it was one. */
/* 13, not 12: the display leading below took the line box from 25.6px to 19.2px and
   24px of padding around it came to 43.2, a fifth of a pixel under the floor. The
   negative margin matches, so the row still looks exactly as it did. */
.tl .n{display:block; padding-block:13px; margin-block:-13px;
  font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:16px;
  line-height:1.2; letter-spacing:-.01em; text-decoration:none}
.now-pill{
  display:none; align-items:center; gap:7px; margin-inline-start:auto; flex:none;
  border:1px solid var(--brand); background:rgba(229,0,126,.13); color:#fff;
  font:600 10px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  padding:6px 9px;
}
.tl li.now .now-pill{display:inline-flex}
.nextup{margin-block:26px 0; text-align:center; color:var(--muted); font-size:15px}
.nextup b{color:var(--ink); font-weight:600}
@media (min-width:900px){
  .tl{display:grid; grid-template-columns:repeat(8,1fr); border-top:1px solid var(--line)}
  .tl li{display:block; padding:26px 12px 0; border-top:0}
  .tl li + li{border-top:0; border-left:1px solid var(--line)}
  .tl .tick{position:absolute; inset:-1px 0 auto 0; height:1px; padding:0}
  .tl .tick::before{display:none}
  .tl .tick::after{left:12px; top:-5px}
  .tl .body{display:block; text-align:left}
  .tl .n{font-size:15px}
  .now-pill{margin:10px 0 0}
}

@media (prefers-reduced-motion:reduce){
  .is-playing .eq i{animation:none; transform:scaleY(.6)}
  .hdr{transition:none}
}

/* ---------------------------------------------------------------- playlist */
.chart{list-style:none; margin:0; padding:0; border-top:1px solid var(--line)}
/* ⛔ CENTRED, not start-aligned (Ashton, 08/09, on his own screen). It used to align
   to the start on the argument that a rank column keeps an even rhythm, but against a
   two-line title the big numeral read as sitting LOW in its own row, which is the one
   thing a reader actually notices. Centring each row against its own text block is
   what looks deliberate; rows of different heights were never going to line up as a
   run anyway. Both optical nudges come out with it: they were corrections for the
   start-aligned layout and now push the number back off centre. */
.chart li{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center; gap:16px;
  padding-block:18px; border-bottom:1px solid var(--line); text-align:left;
}
.chart .r{
  font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:34px;
  color:var(--brand-ink); line-height:1; min-width:56px; letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;
}
.chart .t{font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:17px;
  line-height:1.2; letter-spacing:-.01em}
.chart .a{color:var(--muted); font-size:15px}

/* One hairline for the whole family. The up pill used to carry a magenta border and
   the down pill the ordinary one, so a single column held two frames; the direction
   is the arrow's job and the colour's, not the border's. */
.mv{
  display:inline-flex; align-items:center; gap:6px; font:600 13px/1 Inter,sans-serif;
  color:var(--muted); border:1px solid var(--line); padding:7px 10px;
  font-variant-numeric:tabular-nums; white-space:nowrap;
}
.mv.up{color:var(--brand-ink)}
@media (min-width:900px){ .chart .r{font-size:40px; min-width:70px} .chart .t{font-size:19px} }

/* ------------------------------------------------------------------- shows */
.grid{display:grid; gap:1px; background:var(--line); border:1px solid var(--line)}
@media (min-width:560px){ .grid{grid-template-columns:1fr 1fr} }
@media (min-width:1000px){ .grid{grid-template-columns:repeat(4,1fr)} }
/* ⛔ EVERY TIME IN A ROW ON ONE BASELINE. One-line names and two-line names were
   sharing a row, so "Non-Stop Juice At Night" pushed its own time a line lower than
   the card beside it and the row read as broken rather than as a set.
   The card is a column, the title block is TWO LINES TALL WHETHER OR NOT IT NEEDS
   THEM (a reserved height, not a min-height that only long names reach), and the
   time comes after it, so a short name leaves the space empty and the times line up.
   Grid items stretch by default, so the cards are already equal height; `height:100%`
   makes the anchor actually use it. */
.card{background:var(--panel); text-decoration:none; transition:background .15s linear;
  display:flex; flex-direction:column; height:100%}
.card img{display:block; width:100%; height:auto; aspect-ratio:16/9; object-fit:cover}
.card .cb{display:flex; flex-direction:column; flex:1 1 auto;
  padding:16px 16px 20px; text-align:center}
.card .ct{font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:16px;
  line-height:1.2; letter-spacing:-.01em; display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:2; overflow:hidden; text-wrap:balance;
  /* exactly two lines of 1.2 leading, reserved */
  height:calc(16px * 1.2 * 2)}
.card .cm{display:block; margin-block:6px 0; color:var(--muted); font:600 13px/1 Inter,sans-serif;
  font-variant-numeric:tabular-nums}

/* ----------------------------------------------------------------- request */
.box{
  border:1px solid var(--line); background:var(--panel); padding:34px 22px;
  max-width:760px; margin-inline:auto; text-align:center;
}
.box .btn{margin-block:8px 0}
/* ⛔ ONE CENTRED CARD, the same shape at every width (Ashton, 08/09, photographing
   the Code of Practice page). It used to split into two columns from 700px, label
   ranged right and value ranged left against the centre line: under a CENTRED
   heading that reads as two things drifting apart, not as a card. And every row
   carried its own panel with a 1px gap showing the ground through, so the block was
   a stack of tinted stripes rather than one card with rules in it.
   Now: one panel, one hairline between rows, the label an eyebrow ABOVE its value,
   everything on the column's own centre line. 560px because these are short values
   and a wide card leaves a label stranded from what it labels. */
.rows{display:flex; flex-direction:column; background:var(--panel);
  border:1px solid var(--line); max-width:560px; margin-inline:auto}
.rows a,.rows div{
  display:grid; justify-items:center; gap:8px;
  padding:22px 20px; text-decoration:none; text-align:center;
}
.rows > * + *{border-top:1px solid var(--line)}
.rows .k{font:600 11px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  color:var(--brand-ink)}
.rows .v{font-family:Unbounded,Inter,sans-serif; font-weight:700; font-size:17px;
  line-height:1.3; letter-spacing:-.01em; overflow-wrap:anywhere}
/* The postal address is one verbatim string broken onto two DELIBERATE lines, never
   reworded. ⚠️ At the shared 17px the second half measures 562px against 518px of
   card, so it took a third line and the "two lines" became three. It is set a step
   smaller so both declared halves actually fit the card they were split for. A phone
   is narrower than either half and wraps further, which is the honest limit of a
   47-character line, not a layout fault. */
.rows .v span{display:block}
.rows .v.addr{font-size:15px}

/* -------------------------------------------------------------------- form */
.form{max-width:560px; margin-inline:auto; text-align:left}
.form label{display:block; margin-block:16px 6px; font:600 13px/1 Inter,sans-serif}
.form input,.form textarea{
  width:100%; background:var(--panel); border:1px solid var(--line); color:var(--ink);
  font:400 16px/1.5 Inter,sans-serif; padding:12px 14px; border-radius:0;
}
/* A field shows its own focus on a click as well as on Tab, which is right, but the
   BLOOM is the ring and only a keyboard gets that. */
.form input:focus,.form textarea:focus{border-color:var(--brand); outline:none}
.form input:focus-visible,.form textarea:focus-visible{box-shadow:var(--bloom)}
.form .hp{position:absolute; left:-9999px}
.form .err{color:#FFB4AB; font-size:14px; margin-block:8px 0}

/* --------------------------------------------------------------------- map */
/* FULL WIDTH and a real map from the first paint. 4:3 on a phone, 16:9 from 700px,
   and the band is the SAME height before and after the tap, so making the map live
   cannot shift the page under a thumb. The static picture is one of this site's own
   images; nothing third party is fetched until the tap. */
.mapsec{padding-block:var(--sec)}
.map-band{position:relative; overflow:hidden; aspect-ratio:4/3;
  border-block:1px solid var(--line); background:var(--bg); margin-block:40px 0}
@media (min-width:700px){ .map-band{aspect-ratio:16/9} }
.map-band > img{display:block; width:100%; height:100%; object-fit:cover}
/* The wrap sits one pin-height above the point, so the pin TIP lands on it and the
   label hangs underneath.
   ⚠️ left:0;right:0 and NOT left:50%. An absolutely positioned box with no width
   shrink-to-fits inside the space from its own left offset to the container's right
   edge, so left:50% left it HALF the band to lay out in: at 390 the label and the
   pill were both exactly 195px and both wrapped. Spanning the band and centring the
   contents gives them the whole width. */
.map-pin{position:absolute; left:0; right:0; top:50%; z-index:2; transform:translateY(-34px);
  display:flex; flex-direction:column; align-items:center; pointer-events:none}
.map-pin svg{color:var(--brand-ink); filter:drop-shadow(0 3px 10px rgba(0,0,0,.8))}
.map-label{margin-block:6px 0; background:rgba(11,11,15,.94); border:1px solid var(--brand);
  padding:8px 12px; text-align:center; font-family:Unbounded,Inter,sans-serif;
  font-weight:700; font-size:13px; line-height:1.25; letter-spacing:-.01em; color:var(--ink);
  max-width:min(86vw,340px)}
.map-label span{display:block; margin-block:4px 0; font-family:Inter,sans-serif;
  font-weight:600; font-size:11px; letter-spacing:.06em; color:var(--muted)}
/* Outline, not a fill: the map is already there, this only makes it live. */
.map-go{
  position:absolute; left:0; right:0; bottom:22px; z-index:3;
  margin-inline:auto; width:fit-content; max-width:calc(100% - 32px); white-space:nowrap;
  min-height:44px; padding:0 20px; cursor:pointer; border-radius:0;
  background:rgba(11,11,15,.86); border:1px solid var(--brand); color:var(--ink);
  font:600 12px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.14em;
  transition:background .15s linear;
}
.map-credit{position:absolute; right:10px; bottom:8px; z-index:3; margin:0;
  font:400 11px/1.4 Inter,sans-serif; color:var(--muted);
  background:rgba(11,11,15,.7); padding:3px 7px}
/* Once the tap lands, Google's frame fills the same box and the poster, the pin,
   the label and the pill all come out. Google's map is light and stays light: a
   filter over it would recolour real roads and real water into something that is
   not what any other map of Cork shows. A hairline is what puts it on this page. */
.map-live{display:none; position:absolute; inset:0; z-index:4; background:#fff}
.map-live iframe{display:block; width:100%; height:100%; border:0}
.map-band[data-live] .map-live{display:block}
.map-band[data-live] > img,.map-band[data-live] .map-pin,
.map-band[data-live] .map-go,.map-band[data-live] .map-credit{display:none}
.map-band[data-live]{box-shadow:inset 0 0 0 1px var(--brand)}
.mapnote{margin-block:14px 0; text-align:center; font:400 13px/1.6 Inter,sans-serif;
  color:var(--muted)}
.maplinks{display:flex; flex-wrap:wrap; gap:0 26px; justify-content:center;
  margin-block:22px 0}
/* Three equal buttons: full width and stacked on a phone, one row from 700px. Equal
   columns rather than flex-wrap, so the three never break 2 + 1.
   ⛔ NOT three magenta slabs. The hero's Listen live is the site's one filled button
   and the bloom is meant to be rare; three solid fills in a row made requesting a
   song shout louder than listening, which is the wrong way round. Outlined, in the
   pattern the notice bar and the map pill already use.
   ⛔ AND NOT UPPERCASE. The label carries a PHONE NUMBER, and uppercase at .12em
   tracking pushed it to two lines and broke it as "086 047 / 7887". A number split
   across lines is not a number any more. Sentence case fits each label on one line
   at every width, and the number is nowrap so it can never break even if it did. */
.reqrow{display:grid; gap:10px; margin-block:22px 0}
@media (min-width:700px){ .reqrow{grid-template-columns:repeat(3,1fr); gap:12px} }
.reqrow .btn{width:100%; text-align:center; justify-content:center; min-height:48px;
  padding-inline:14px; text-transform:none; letter-spacing:0;
  font:600 15px/1.25 Inter,sans-serif}
/* ⚠️ ONE flex item, not three. `.btn` is inline-flex, so every text node and span
   inside it became its OWN flex item with the button's 10px gap between them: the
   label read "WhatsApp    086 047 7887" and the Instagram one broke into two
   columns. Wrapping the whole label in one span makes it ordinary text again. */
.reqrow .lbl{display:block; text-align:center}
.reqrow .nb{white-space:nowrap}
.reqalt{margin-block:16px 0; text-align:center}

/* ------------------------------------------------------------------ footer */
/* ONE rhythm token between footer groups, so nothing is spaced by accident. */
.foot{--fgap:26px;
  border-top:1px solid var(--line); padding-block:var(--sec) 34px; text-align:center}
/* Four links, one line, even at 320. The gap is the only thing that has to give, so
   it shrinks with the viewport rather than letting a link fall to a second row. */
.fnav{display:flex; flex-wrap:nowrap; justify-content:center; align-items:center;
  gap:0 clamp(12px,4.6vw,24px); margin-block:var(--fgap) 0}
.fnav a{white-space:nowrap}
.fnav a,.flegal a{
  display:inline-flex; align-items:center; min-height:44px;
  font-size:14px; text-decoration:underline; text-decoration-color:rgba(142,142,159,.5);
  text-decoration-thickness:1px; text-underline-offset:4px; color:var(--ink);
}
.fline{color:var(--muted); font-size:15px; margin-block:0 6px}
.fname{color:var(--ink)}
.faddr span{white-space:nowrap}
.fline:first-of-type{margin-block:var(--fgap) 6px}
.fcontact{margin-block:0}
.fcontact a{color:var(--ink); text-decoration:underline;
  text-decoration-color:rgba(142,142,159,.5); text-decoration-thickness:1px;
  text-underline-offset:4px}
.socials{display:flex; gap:10px; justify-content:center; margin-block:var(--fgap) 0}
.socials a{
  width:44px; height:44px; display:grid; place-items:center; border:1px solid var(--line);
  background:var(--panel); color:var(--ink);
}
/* Four logos of four different proportions. A GRID with equal cells is what makes
   them a row rather than four things of four widths: 2x2 on a phone, one row from
   560px. Each file is already trimmed to its own ink, so one height IS one cap
   height, and each sits centred in its own cell. */
.partners{display:grid; grid-template-columns:1fr 1fr; gap:18px 26px;
  align-items:center; justify-items:center;
  margin-block:var(--fgap); padding-block:22px; border-block:1px solid var(--line)}
@media (min-width:560px){ .partners{grid-template-columns:repeat(4,1fr)} }
.partners a{display:inline-flex; align-items:center; justify-content:center;
  min-height:44px; width:100%}
.partners img{opacity:.7; height:32px; width:auto; max-width:100%; object-fit:contain}
.flegal{display:flex; flex-wrap:wrap; gap:0 18px; justify-content:center;
  margin-block:var(--fgap) 0; color:var(--muted); font-size:14px}
.pwr{margin-block:16px 0; color:var(--muted); font-size:14px}
.pwr a{color:var(--muted); text-decoration:underline; text-decoration-color:rgba(142,142,159,.5);
  text-decoration-thickness:1px; text-underline-offset:4px}

/* ------------------------------------------------------------- sticky player */
.player{
  position:fixed; inset:auto 0 0 0; z-index:70; height:var(--player);
  background:var(--panel); border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
}
.player .wrap{display:flex; align-items:center; gap:14px; height:var(--player)}
.pbtn{
  width:48px; height:48px; flex:none; display:grid; place-items:center; cursor:pointer;
  background:var(--brand); border:1px solid var(--brand); color:#fff; border-radius:0;
  transition:box-shadow .15s linear, background .15s linear;
}
.pbtn .ic{display:grid; place-items:center; width:20px; height:20px}
.pbtn svg{display:none}
.pbtn[data-state=idle] .i-play,.pbtn[data-state=error] .i-play{display:block}
.pbtn[data-state=connecting] .i-wait{display:block; animation:spin 1s linear infinite}
.pbtn[data-state=playing] .i-stop{display:block}
@keyframes spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){ .pbtn[data-state=connecting] .i-wait{animation:none} }
.pmeta{min-width:0; flex:1; text-align:left}
.pmeta .l{display:flex; align-items:center; gap:8px; font:600 11px/1 Inter,sans-serif;
  text-transform:uppercase; letter-spacing:.14em; color:var(--brand-ink)}
.pmeta .s{display:block; margin-block:6px 0; font-size:14px; color:var(--muted);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.pmeta .s b{color:var(--ink); font-weight:600}
.plogo{display:none}
@media (min-width:900px){ .plogo{display:block; width:104px} }
/* 48, matching the play button beside it. At 44 the two square controls in the one
   bar were four pixels apart, which reads as a slip rather than a hierarchy; the
   hierarchy is the magenta fill against the outline, and that is enough. */
.mute{
  width:48px; height:48px; flex:none; display:grid; place-items:center; cursor:pointer;
  background:none; border:1px solid var(--line); color:var(--ink); border-radius:0;
}
.mute svg{display:none}
.mute[data-muted=off] .i-vol{display:block}
.mute[data-muted=on] .i-mute{display:block}
.perr{
  position:absolute; inset:auto 0 100% 0; background:var(--panel-2);
  border-top:1px solid var(--line); padding:10px 20px; font-size:14px; color:#FFB4AB;
  text-align:center;
}
.perr[hidden]{display:none}

/* ------------------------------------------------------------ notice, modal */
/* One short line, so the bar cannot cover the button a visitor came for on a
   320px screen. It sits directly on top of the player and never over it. */
.notice{
  position:fixed; inset:auto 0 var(--player) 0; z-index:65; background:var(--panel-2);
  border-top:1px solid var(--line); padding:10px 16px calc(10px + env(safe-area-inset-bottom));
  display:flex; gap:12px; align-items:center; justify-content:center;
  text-align:left; font-size:14px; color:var(--muted);
}
/* The text and the button stay on one row. Wrapping put the button on a line of
   its own and the bar grew tall enough to sit over a hero button at 320. */
.notice span{min-width:0}
.notice button{flex:none}
@media (min-width:700px){ .notice{justify-content:center; text-align:center} }
.notice[hidden]{display:none !important}
.notice a{color:var(--ink)}
/* Outlined, not filled. A solid magenta block for dismissing a notice was the
   loudest thing on the lower half of a phone screen, louder than the play button and
   louder than Listen live. Same treatment as the Live pill, so the filled magenta is
   left to the two controls that actually start the radio. */
.notice button{
  min-height:44px; padding:0 16px; background:rgba(229,0,126,.12); border:1px solid var(--brand);
  color:#fff; cursor:pointer; font:600 12px/1 Inter,sans-serif; text-transform:uppercase;
  letter-spacing:.12em; border-radius:0; transition:background .15s linear;
}
/* The sheet is a fixed head, a scrolling body and a fixed foot, so a long policy
   scrolls inside the card on a phone and the page behind never moves. The X and
   the Close button are both always in reach, whatever the body is doing. */
.modal{
  position:fixed; inset:0; z-index:80; display:flex; align-items:flex-end; justify-content:center;
  background:rgba(4,4,7,.72); padding:0;
}
.modal[hidden]{display:none !important}
.modal .sheet{
  background:var(--bg); border:1px solid var(--line); width:min(760px,100%);
  max-height:88vh; display:flex; flex-direction:column; text-align:left; overflow:hidden;
}
/* ⛔ ON A PHONE THE SHEET MUST NOT REACH THE TOP OF THE SCREEN. 88vh sounds like it
   leaves room, but vh is the LAYOUT viewport and ignores the browser's own chrome,
   so a long document ran the card's title and its X up under the address bar with
   no visible card edge at all. 100dvh is the height actually on screen, and the
   12px inset is what makes it read as a card lying over the page rather than a new
   page. The desktop dialog is untouched. */
@media (max-width:699px){
  .modal{align-items:flex-end;
    padding:calc(env(safe-area-inset-top, 0px) + 12px) 12px 0}
  .modal .sheet{width:100%; border-radius:6px;
    max-height:calc(88vh - 12px);
    max-height:calc(100dvh - env(safe-area-inset-top, 0px) - 12px)}
  .modal .shead{padding-top:20px}
}
@media (min-width:700px){ .modal{align-items:center; padding:24px} }
.modal .shead{position:relative; flex:none; padding:24px 68px 16px 22px;
  border-bottom:1px solid var(--line)}
.modal .sbody{flex:1 1 auto; overflow:auto; -webkit-overflow-scrolling:touch;
  padding:20px 22px 6px}
.modal .sfoot{flex:none; padding:14px 22px calc(18px + env(safe-area-inset-bottom));
  border-top:1px solid var(--line)}
.modal h2{font-size:26px; margin-block:0 8px}
.modal h3{font-size:17px; margin-block:22px 6px}
.modal .sbody > :first-child{margin-top:0}
.modal .sbody h3:first-child{margin-top:0}
.modal p,.modal li{font-size:15px; color:#DCDAE2}
.modal .xclose{
  position:absolute; top:12px; right:12px; width:44px; height:44px; display:grid;
  place-items:center; background:var(--panel); border:1px solid var(--line); color:#fff;
  cursor:pointer; border-radius:0;
}
.modal .close{
  display:block; width:100%; min-height:48px; background:var(--panel);
  border:1px solid var(--line); color:#fff; cursor:pointer; border-radius:0;
  font:600 13px/1 Inter,sans-serif; text-transform:uppercase; letter-spacing:.12em;
}
.draftline{border:1px solid var(--line); background:var(--panel); padding:12px 14px; font-size:14px}

/* --------------------------------------------------------------- long copy */
/* A bare URL in the verbatim Code of Practice is 33 characters and pushed the
   document 8px wide at 320. The words are not ours to shorten, so they wrap. */
.prose{max-width:74ch; margin-inline:auto; text-align:left; overflow-wrap:anywhere}
.prose h2{font-size:clamp(21px,2.6vw,26px); margin-block:38px 12px}
.prose h3{margin-block:28px 8px}
.prose ul{padding-inline-start:1.2em}
.prose li{margin-block:0 .5em}
.prose a{color:var(--brand-ink)}
.crumb{margin-block:0 18px; font:600 12px/1 Inter,sans-serif; text-transform:uppercase;
  letter-spacing:.14em; color:var(--muted)}
.crumb a{color:var(--muted); text-decoration:none}

/* The playful mark, on the two pages that carry it. A line of its own above the
   section label, never beside it: it is artwork, not a heading. */
.markline{margin-block:0 22px}
.markline img{display:block; margin-inline:auto; width:min(300px,72vw); height:auto}

/* ------------------------------------------------------------- show header */
.showhead{display:grid; gap:26px; align-items:center}
@media (min-width:900px){ .showhead{grid-template-columns:1fr 1fr; gap:44px} }
.showhead img{display:block; width:100%; height:auto; border:1px solid var(--line)}
.showhead .m{text-align:center}
/* The two columns stay; the left column's contents centre INSIDE it, the same call
   as the homepage hero (Ashton, 08/09). The crumb sits above the grid, so it is
   centred at the section level rather than in the column. */
.meta{display:flex; flex-wrap:wrap; gap:10px; justify-content:center; margin-block:22px 0}
.chip{
  display:inline-flex; align-items:center; gap:8px; border:1px solid var(--line);
  background:var(--bg); color:var(--muted); font:600 12px/1 Inter,sans-serif;
  letter-spacing:.06em; padding:9px 12px; font-variant-numeric:tabular-nums;
}

@media (hover:hover){

.btn:hover{background:#FF0F8F; box-shadow:0 0 20px rgba(229,0,126,.6)}
.btn.ghost:hover{background:var(--panel-2); border-color:#fff; box-shadow:none}
.tlink:hover{text-decoration-color:var(--brand-ink)}
.nav a:hover,.nav a[aria-current]{opacity:1; color:var(--brand-ink)}
.tl .n:hover{color:var(--brand-ink)}
.card:hover{background:var(--panel-2)}
.rows a:hover{background:var(--panel-2)}
.map-go:hover{background:rgba(229,0,126,.24)}
.fnav a:hover,.flegal a:hover{text-decoration-color:var(--ink)}
.fcontact a:hover{text-decoration-color:var(--ink)}
.socials a:hover{border-color:var(--brand); color:var(--brand-ink)}
.partners a:hover img{opacity:1}
.pwr a:hover{color:var(--ink)}
.pbtn:hover{background:#FF0F8F; box-shadow:0 0 20px rgba(229,0,126,.6)}
.mute:hover{border-color:#fff}
.notice button:hover{background:rgba(229,0,126,.24)}
.modal .xclose:hover{border-color:#fff; background:var(--panel-2)}
.modal .close:hover{border-color:#fff; background:var(--panel-2)}
.crumb a:hover{color:var(--brand-ink)}
}
