
@font-face{font-family:Poppins;src:url('/v1/assets/fonts/poppins-700.woff2?v=14') format('woff2');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:Inter;src:url('/v1/assets/fonts/inter-400.woff2?v=14') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:Inter;src:url('/v1/assets/fonts/inter-600.woff2?v=14') format('woff2');font-weight:600;font-style:normal;font-display:swap}

:root{
  --white:#FFFFFF;
  --panel:#FAF5F7;
  --panel-2:#F3ECF0;
  --line:#EBE0E6;
  --ink:#111111;
  --muted:#6B6470;
  --mag:#E5007E;
  --mag-ink:#B80064;
  --mag-hover:#CC0070;
  --sec:56px;
  --max:1080px;
  --pad:20px;
  /* the player's own measured height: 9 + 48 + 9 + a 1px border, the same at every width.
     It was 74/78, which overstated it, so the notice's "12px above the player" was really
     19px and the page's own bottom clearance was 7px. Every user of this token adds its own
     clearance now, so the number stays honest. */
  /* a NO-JS fallback only. site.js measures the real bar and overwrites this, because the
     bar's height moves with the safe-area inset and the visitor's text size, and the page's
     bottom padding has to be exactly that and not a guess. */
  --player:calc(67px + env(safe-area-inset-bottom));
  /* the notice bar's measured height, written by the JS when it shows the bar and used to
     clear the last links in the footer, which have no scroll room left underneath them */
  --notice-h:0px;
  /* the header's own measured height, and the only number the anchor offset uses */
  --hdr:65px;
  --r-card:16px;
}
@media(min-width:900px){:root{--sec:80px;--pad:24px}}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--white);color:var(--ink);
  font:400 17px/1.6 Inter,system-ui,-apple-system,"Segoe UI",sans-serif;
  /* EXACTLY the player's height: the fixed bar then covers this padding precisely and there
     is no pale strip left under the footer (Ashton, 08/09). --player already carries the
     safe-area inset, so adding env() here again would put it back. */
  padding-bottom:var(--player);
  text-align:center;
}
/* While the first-visit notice is up it floats above the player, and the last three links in
   the footer sit under it with no scroll room left to clear them. The JS measures the bar and
   sets --notice-h, so the page makes exactly as much room as the bar takes and no more. */
html.notice-on body{padding-bottom:calc(var(--player) + var(--notice-h) + 12px)}
img{max-width:100%;height:auto}
a{color:var(--mag-ink)}
/* text-wrap:balance so a three word heading cannot leave one word on its own line at 320.
   Chrome caps it at six lines, which every heading here is well inside. */
h1,h2,h3{font-family:Poppins,system-ui,sans-serif;font-weight:700;line-height:1.05;margin:0;text-wrap:balance}
h1{font-size:34px;letter-spacing:-.01em}
h2{font-size:27px;letter-spacing:-.01em}
h3{font-size:19px;line-height:1.2}
p{margin:0}
@media(min-width:900px){h1{font-size:52px}h2{font-size:38px}h3{font-size:21px}}

/* an in-page jump lands under the sticky header, not behind it */
[id]{scroll-margin-top:calc(var(--hdr) + 16px)}

.wrap{max-width:var(--max);margin-inline:auto;padding-inline:var(--pad)}
.sec{padding-block:var(--sec)}
main>section+section{border-top:1px solid var(--line)}
main>section.band+section,main>section+section.band{border-top:0}
.panel{background:var(--panel)}
.eyebrow{font-weight:600;font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--mag-ink);margin-block:0 12px}
.lede{color:var(--muted);max-width:56ch;margin-inline:auto;margin-block:14px 0;text-wrap:pretty}
.sec>.wrap>h2+.lede{margin-block:16px 0}
.small{font-size:14px;color:var(--muted)}

/* -------------------------------------------------------------- buttons */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  min-height:52px;padding:0 26px;border-radius:999px;border:1.5px solid transparent;
  font:600 16px/1 Inter,sans-serif;text-decoration:none;cursor:pointer;
  transition:background .15s,border-color .15s,color .15s;
}
.btn svg{width:20px;height:20px;flex:none}
.btn-mag{background:var(--mag);color:#fff}
@media(hover:hover){.btn-mag:hover{background:var(--mag-hover)}}
.btn-ghost{background:transparent;border-color:var(--line);color:var(--ink)}
@media(hover:hover){.btn-ghost:hover{background:var(--panel);border-color:var(--ink)}}
.btn-white{background:#fff;color:var(--mag-ink)}
@media(hover:hover){.btn-white:hover{background:var(--panel)}}
.btn-wo{background:transparent;border-color:rgba(255,255,255,.7);color:#fff}
@media(hover:hover){.btn-wo:hover{background:rgba(255,255,255,.14);border-color:#fff}}
.btn-lg{min-height:60px;font-size:17px;padding:0 32px}
:focus-visible{outline:3px solid var(--mag-ink);outline-offset:2px}
/* ⛔ NO TAP EVER LEAVES A RING. Every ring on this site is :focus-visible, never :focus, and
   every interactive thing kills the iOS blue flash and answers a press with :active instead. */
a,button,[data-play],[role=button],input,textarea,select,summary{-webkit-tap-highlight-color:transparent}
.btn:active,.play:active,.mute:active,.burger:active,.map-tap:active,.modal-x:active{transform:translateY(1px)}
.shows a:active,.socials a:active,.sched .n a:active{opacity:.86}
/* ⛔ THE SOFT-NAVIGATION FOCUS TARGET IS NOT A CONTROL. The new page's H1 takes focus so a
   screen reader announces it, and Chrome painted the 3px ring around it on every tap-through
   (Ashton's screenshot). It is programmatic focus on a heading: no ring, in any state. */
#main h1[tabindex="-1"],#main h1[tabindex="-1"]:focus,#main h1[tabindex="-1"]:focus-visible{
  outline:none;box-shadow:none;-webkit-tap-highlight-color:transparent}

/* --------------------------------------------------------------- header */
.hdr{position:sticky;top:0;z-index:60;background:#fff;border-bottom:1px solid var(--line);transition:transform .22s ease}
.hdr-hide{transform:translateY(-100%)}
/* the thin line under the header while a page is being fetched, so a tap on a slow
   connection is visibly doing something */
.navbar{position:fixed;top:var(--hdr);left:0;height:2px;width:0;background:var(--mag);z-index:60;
  opacity:0;transition:width .25s ease,opacity .2s}
.navbar.on{width:82%;opacity:1;transition:width 2.2s cubic-bezier(.1,.8,.2,1),opacity .1s}
@media(prefers-reduced-motion:reduce){.navbar{transition:opacity .1s}.navbar.on{width:100%}}
@media(prefers-reduced-motion:reduce){.hdr{transition:none}}
.hdr-in{max-width:var(--max);margin-inline:auto;padding:10px var(--pad);display:flex;align-items:center;gap:12px}
/* every control in the header is at least 44px in both directions, thumb sized */
.hdr-logo{display:inline-flex;align-items:center;flex:none;min-height:44px}
.hdr-logo img{width:118px;height:27px;display:block}
.nav{display:none}
/* the current-page rule is a pseudo-element, so the link can be a 44px tall box without
   the underline drifting away from the words */
.nav a{position:relative;display:inline-flex;align-items:center;min-height:44px;color:var(--ink);text-decoration:none;font-weight:600;font-size:15px}
.nav a::after{content:"";position:absolute;left:0;right:0;bottom:9px;height:2px;border-radius:2px;background:transparent}
.nav a[aria-current=page]::after{background:var(--mag)}
@media(hover:hover){.nav a:hover::after{background:var(--mag)}}
.hdr-cta{margin-left:auto;display:flex;align-items:center;gap:8px}
.hdr-cta .btn{min-height:44px;padding:0 16px;font-size:14px;flex:none}
.burger{width:44px;height:44px;flex:none;display:grid;place-items:center;background:none;border:1.5px solid var(--line);border-radius:999px;color:var(--ink);cursor:pointer;transition:border-color .15s,color .15s}
/* the same hover the other bordered round controls carry (.modal-x, .socials a, .mute) */
@media(hover:hover){.burger:hover{border-color:var(--mag);color:var(--mag-ink)}}
.burger svg{width:20px;height:20px}
/* under 360px the header runs out of room, so the play control keeps its icon and drops
   its label rather than squeezing the burger below 44px */
@media(max-width:359px){.hdr-cta .btn span{display:none}.hdr-cta .btn{width:44px;padding:0}}
.mobnav{display:none;border-top:1px solid var(--line);padding:8px var(--pad) 18px;background:#fff}
.mobnav.open{display:block}
.mobnav a{display:flex;align-items:center;min-height:48px;color:var(--ink);text-decoration:none;font-weight:600;border-bottom:1px solid var(--line)}
.mobnav a:last-child{border-bottom:0}
@media(min-width:900px){
  /* Three tracks, not three flex items with two competing auto margins: the logo and the
     call to action are different widths, so `margin-inline:auto` on the nav inside a flex row
     centred it between them and left it 44px short of the page's own centre line. The middle
     track is what centres, so the nav now sits on the same axis as every heading below it. */
  .hdr-in{display:grid;grid-template-columns:1fr auto 1fr;gap:12px}
  .nav{display:flex;gap:26px;justify-self:center}
  .hdr-cta{justify-self:end;margin-left:0}
  .burger{display:none}
  .mobnav{display:none !important}
  .hdr-logo img{width:148px;height:34px}
}

/* ----------------------------------------------------------------- hero */
.hero{padding-block:44px var(--sec)}
@media(min-width:900px){.hero{padding-block:64px var(--sec)}}
.hero h1{margin-block:0 0}
.hero .lede{font-size:18px}
.hero-btns{display:flex;flex-direction:column;gap:12px;align-items:center;margin-block:28px 0}
@media(min-width:560px){.hero-btns{flex-direction:row;justify-content:center}}
/* 20px, not 12: the line under the buttons is its own piece of information, not a caption
   bolted to them. Named hero-note, not hero-names: nothing on this site names a presenter,
   and a class called "names" is a trap for whoever edits this next. */
.hero-note{margin-block:20px 0}
/* the playful straw logo, the one with Cork under it, as the picture on a page hero */
.hero-mark{width:min(300px,72vw);height:auto;margin:0 auto 22px;display:block}
@media(min-width:900px){.hero-mark{width:380px;margin-bottom:26px}}

/* ------------------------------------------------------------ on air now */
.oncard{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-card);padding:20px;display:grid;gap:22px;text-align:center}
@media(min-width:900px){.oncard{grid-template-columns:1fr 1fr;padding:32px;gap:32px;align-items:center}
  .oncard>div+div{border-left:1px solid var(--line);padding-left:32px}}
.oncard img.art{border-radius:12px;width:100%;max-width:340px;margin-inline:auto;display:block}
.pill{display:inline-flex;align-items:center;gap:7px;background:var(--mag);color:#fff;border-radius:999px;padding:6px 13px;font:600 11px/1 Inter,sans-serif;letter-spacing:.08em;text-transform:uppercase}
.on-h{margin-block:14px 0}
.np-label{margin-block:22px 8px}
.on-meta{color:var(--muted);margin-block:6px 0;font-variant-numeric:tabular-nums}
.np-art{width:64px;height:64px;border-radius:12px;object-fit:cover;margin-inline:auto;display:block;background:var(--panel-2)}
.np-track{margin-block:12px 0;font-family:Poppins,sans-serif;font-weight:700;font-size:21px;line-height:1.2}
.np-artist{color:var(--muted);margin-block:4px 0}
/* At rest the four bars used to sit at one flat 6px each, which reads as an ellipsis rather
   than as an equaliser waiting for the stream. They are stepped at rest, so the shape says
   what it is before anything moves. */
.eq{display:flex;justify-content:center;gap:4px;height:20px;align-items:flex-end;margin-block:14px 0}
.eq i{width:4px;background:var(--mag);border-radius:2px;height:9px;transition:height .2s}
.eq i:nth-child(2){height:15px}.eq i:nth-child(3){height:7px}.eq i:nth-child(4){height:12px}
.eq.on i{animation:eqb .9s ease-in-out infinite}
.eq.on i:nth-child(2){animation-delay:.15s}.eq.on i:nth-child(3){animation-delay:.3s}.eq.on i:nth-child(4){animation-delay:.45s}
@keyframes eqb{0%,100%{height:5px}50%{height:20px}}
@media(prefers-reduced-motion:reduce){.eq i{transition:none}.eq.on i{animation:none;height:12px}}
.nextup{margin-block:20px 0;color:var(--muted)}

/* ------------------------------------------------------------- schedule */
.sched{list-style:none;margin:26px auto 0;padding:0;max-width:720px;text-align:left}
.sched li{display:grid;grid-template-columns:64px 1fr;gap:12px;align-items:center;padding:14px 0 14px 12px;border-top:1px solid var(--line);border-left:3px solid transparent}
.sched li:last-child{border-bottom:1px solid var(--line)}
.sched .t{font-weight:600;font-variant-numeric:tabular-nums;color:var(--muted);font-size:15px}
.sched .n{font-weight:600;line-height:1.3}
.sched .n a{color:var(--ink);text-decoration:none}
@media(hover:hover){.sched .n a:hover{text-decoration:underline}}
.sched li.now{border-left-color:var(--mag);background:var(--panel)}
.sched .now-pill{display:none}
.sched li.now .now-pill{display:inline-flex;margin-left:8px;vertical-align:middle;padding:3px 9px;font-size:10px}
.after{margin-block:22px 0}


/* --------------------------------------------------------------- chart */
/* 720px, the same as .sched: the two are the same kind of list one section apart, and at
   640 their left rules landed 40px inside the schedule's, which reads as a misalignment
   rather than as a narrower list. */
.chart{list-style:none;margin:26px auto 0;padding:0;max-width:720px;text-align:left}
.chart li{display:grid;grid-template-columns:42px 1fr auto;gap:14px;align-items:center;padding:16px 0;border-top:1px solid var(--line)}
.chart li:last-child{border-bottom:1px solid var(--line)}
.chart .no{font-family:Poppins,sans-serif;font-weight:700;font-size:28px;line-height:1;color:var(--ink);text-align:center}
.chart .tr{display:block;font-weight:600;line-height:1.3}
.chart .ar{display:block;color:var(--muted);font-size:15px;margin-block:2px 0}
/* The direction used to be a bare ▲ / ▼, which is a font glyph standing in for the icon set
   the rest of the site draws, and which a screen reader reads as "black up-pointing triangle".
   It is an authored SVG now, on currentColor, with the direction said in words for a reader. */
.chart .mv{display:inline-flex;align-items:center;gap:6px;font-weight:600;font-size:15px;color:var(--mag-ink);font-variant-numeric:tabular-nums;white-space:nowrap}
.chart .mv svg{width:11px;height:11px;flex:none;fill:currentColor}
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}

/* ---------------------------------------------------------- request band */
.band{background:var(--mag);color:#fff}
.band h2,.band .eyebrow{color:#fff}
/* full white, not 92%: at 92% this line measured 3.96:1 on the magenta and body copy
   needs 4.5. White on this magenta is 4.54:1 */
.band .lede{color:#fff}
/* Three EQUAL pills: full width stacked on a phone, one row of thirds from 700px. `1fr` on
   the grid, not flex, so the three stay the same width whatever their labels say. */
.band-btns{display:grid;gap:10px;margin-block:26px 0;max-width:340px;margin-inline:auto}
@media(min-width:700px){.band-btns{grid-template-columns:repeat(3,1fr);gap:12px;max-width:820px}}
.band-btns .btn{width:100%;gap:9px;font-size:15px;padding-inline:18px}
.band-btns .btn svg{width:19px;height:19px;flex:none;fill:currentColor}
.band-also{margin-block:14px 0;font-size:15px}
.band-also a{color:#fff;text-decoration:underline;text-decoration-color:rgba(255,255,255,.55);
  text-underline-offset:3px;display:inline-flex;align-items:center;min-height:44px}
@media(hover:hover){.band-also a:hover{text-decoration-color:#fff}}

/* ---------------------------------------------------------- shows grid */
.shows{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin-block:28px 0;align-items:stretch}
@media(min-width:900px){.shows{grid-template-columns:repeat(4,1fr);gap:18px}}
/* ⛔ EVERY TIME IN A ROW SITS ON ONE BASELINE (Ashton, 08/09, on his phone). "Non-Stop Juice
   At Night" wraps to two lines and used to push its own time down while the card beside it
   kept theirs high. Three things together do it, and all three are needed: the cards stretch
   to the row's height, the card is a column so the time can be pushed to the bottom of a
   fixed block, and the TITLE BLOCK IS ALWAYS EXACTLY TWO LINES TALL whether it uses one or
   two. Equalising the card height alone would not have worked: the time follows the title. */
.shows a{display:flex;flex-direction:column;height:100%;text-decoration:none;color:var(--ink);border:1px solid var(--line);border-radius:var(--r-card);overflow:hidden;background:#fff;transition:border-color .15s}
@media(hover:hover){.shows a:hover{border-color:var(--mag)}}
.shows img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover}
.shows .b{padding:13px 12px 16px;display:flex;flex-direction:column;flex:1 1 auto}
.shows .nm{font-family:Poppins,sans-serif;font-weight:700;font-size:15px;line-height:1.25;
  min-height:calc(2 * 1.25em);text-wrap:balance;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.shows .t{color:var(--muted);font-size:13px;margin-block:5px 0;font-variant-numeric:tabular-nums}

/* ------------------------------------------------------------ show page */
.showhead img{border-radius:var(--r-card);margin-block:26px 0;width:100%;max-width:720px;margin-inline:auto;display:block}
.showmeta{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin-block:20px 0}
.chip{background:var(--panel-2);border:1px solid var(--line);border-radius:999px;padding:8px 15px;font:600 14px/1 Inter,sans-serif;color:var(--muted);font-variant-numeric:tabular-nums}
.crumb{margin-block:0 18px;font-size:14px}
.crumb a{color:var(--muted)}
/* the Code of Practice prints full URLs, which are one unbreakable word at 320px */
.body{max-width:62ch;margin-inline:auto;overflow-wrap:break-word}
.body p+p{margin-block:16px 0}
.body h2{margin-block:34px 14px;font-size:24px}
.body h3{margin-block:26px 10px}
/* the first heading in a document already has the section's own padding above it */
.body>:first-child{margin-block-start:0}
/* The house rule centres everything except input and reading. The Code of Practice and the
   privacy policy are long-form reading, in the same class as FAQ answer copy, so their
   paragraphs and lists are left aligned. Headings still centre. One line to overrule. */
.body.doc{max-width:70ch}
.body.doc p,.body.doc li{text-align:left}
.body ul{text-align:left;padding-left:22px;margin-block:14px 0;color:var(--ink)}
.body li{margin-block:8px 0}
@media(min-width:900px){.body h2{font-size:28px}}

/* --------------------------------------------------------------- cards */
.cards{display:grid;gap:14px;margin-block:28px 0}
@media(min-width:760px){.cards{grid-template-columns:repeat(3,1fr);gap:18px}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-card);padding:22px 18px}
.card h3{margin-block:0 8px}
.card p{color:var(--muted)}
.card a{font-weight:600}

/* ----------------------------------------------------------------- map */
/* The map is a REAL MAP the moment the page paints: a picture of the streets around the
   station, baked at build time by build/make-map.py from OpenStreetMap tiles, so the page
   itself fetches nothing from anybody. Tapping it swaps in a Google Maps embed, poster and
   pin removed together. 4:3 on a phone, 16:9 from 760px up. */
.map-band{position:relative;margin-block:26px 0;border:1px solid var(--line);border-radius:var(--r-card);overflow:hidden;background:var(--panel-2)}
.map-band>img{display:block;width:100%;height:auto;aspect-ratio:4/3;object-fit:cover}
@media(min-width:760px){.map-band>img{aspect-ratio:16/9}}
/* ⚠️ -36px, COMPUTED not guessed: the pin path bottoms out at y=21.5 in a 0 0 24 24 viewBox,
   which is 35.83px down a 40px icon, so a 36px lift puts the TIP on the station, 0.17px out.
   The bake centres the coordinate in the picture and object-fit:cover centres the picture in
   the box, so the box centre IS 51.893486, -8.467494 at every width. */
.map-pin-wrap{position:absolute;top:50%;left:50%;transform:translate(-50%,-36px);display:flex;
  flex-direction:column;align-items:center;pointer-events:none}
.map-pin{width:40px;height:40px;flex:none;fill:var(--mag);stroke:#111;stroke-width:1;
  filter:drop-shadow(0 3px 8px rgba(17,17,17,.35))}
.map-label{margin-block:4px 0;background:#fff;border:1px solid var(--line);border-radius:10px;
  padding:7px 12px;font-size:13px;font-weight:700;line-height:1.4;text-align:center;color:var(--ink);
  white-space:nowrap;box-shadow:0 6px 18px rgba(17,17,17,.14)}
.map-label span{display:block;font-weight:500;font-size:12px;color:var(--muted)}
/* the tap target, a pill centred over the picture */
.map-tap{position:absolute;left:50%;bottom:14px;transform:translateX(-50%);z-index:3;
  background:var(--mag);color:#fff;border:0;border-radius:999px;padding:11px 20px;font:inherit;
  font-size:14px;font-weight:700;cursor:pointer;min-height:44px;
  box-shadow:0 6px 18px rgba(229,0,126,.32)}
@media(hover:hover){.map-tap:hover{background:var(--mag-ink)}}
.map-tap:focus-visible{outline:2px solid var(--ink);outline-offset:3px}
.map-credit{position:absolute;right:8px;bottom:6px;margin:0;font-size:11px;color:#4a4a4a;
  background:rgba(255,255,255,.72);padding:2px 6px;border-radius:5px}
/* ⚠️ The POSTER is what gives the band its height, so once the tap removes it the box would
   collapse to a hairline. `.live` puts the same ratio on the band itself. */
.map-band.live{aspect-ratio:4/3}
@media(min-width:760px){.map-band.live{aspect-ratio:16/9}}
/* the live map, hidden until the tap fills it with the Google embed */
.map-live{display:none;position:absolute;inset:0;z-index:2;background:var(--panel-2)}
.map-live iframe{display:block;width:100%;height:100%;border:0}
/* the two ways out, under the card, plain links because that is what they are */
.maplinks{display:flex;flex-wrap:wrap;gap:6px 22px;justify-content:center;margin-block:16px 0;font-size:15px}
.maplinks a{display:inline-flex;align-items:center;min-height:44px}

/* -------------------------------------------------------------- footer */
/* The footer is the last thing on the page, so its ground is what sits against the player.
   Nothing may add margin under it: the body's bottom padding is exactly the bar's height and
   the bar covers it. */
.foot{background:var(--panel);border-top:1px solid var(--line);padding-block:44px 34px;--fgap:20px;margin-bottom:0}
.foot>.wrap>:last-child{margin-bottom:0}
@media(min-width:760px){.foot{--fgap:24px}}
/* the FULL lockup, tagline and all, on this light version */
.foot img.logo{width:180px;height:43px;margin-inline:auto;display:block}
/* ⛔ Code of Practice is OUT of this nav (Ashton, 08/09): five items wrapped 4+1 on a phone
   and left it orphaned on its own line. It is in the header nav and in the legal row below,
   so nothing is lost. Four items hold ONE line at 320 on a 15px gap. */
.foot-nav{display:flex;gap:0 15px;justify-content:center;margin-block:var(--fgap) 0;white-space:nowrap}
@media(min-width:420px){.foot-nav{gap:0 22px}}
/* footer links carry a faint underline at every width, not on hover. --line is too pale
   against the footer's own panel to read, so the underline uses a mid tone. Every one is a
   44px tall box, because a 24px line of text is not a thumb target. */
.foot a{color:var(--ink);text-decoration:underline;text-decoration-color:#CBB9C3;text-decoration-thickness:1px;text-underline-offset:3px;font-size:15px}
@media(hover:hover){.foot a:hover{text-decoration-color:var(--mag)}}
.foot-nav a,.legal .row a,.partners a{display:inline-flex;align-items:center;min-height:44px}
/* ONE rhythm token for every gap between footer groups, so nothing is doubled and nothing
   is tighter than its neighbours. */
.foot .addr{color:var(--muted);margin-block:var(--fgap) 0;font-size:15px;line-height:1.55}
.foot .addr strong{color:var(--ink);font-weight:700}
.foot .addr-2{margin-block:6px 0}
.foot .addr a{color:var(--muted);display:inline-flex;align-items:center;min-height:44px}
.foot .addr-2 span{color:#B9A7B1;padding-inline:4px}
/* the eircode never breaks away from its town */
.foot .addr .nb{white-space:nowrap}
.socials{display:flex;gap:12px;justify-content:center;margin-block:var(--fgap) 0}
.socials a{width:44px;height:44px;border-radius:999px;border:1px solid var(--line);display:grid;place-items:center;background:#fff;transition:border-color .15s}
@media(hover:hover){.socials a:hover{border-color:var(--mag)}}
/* 20px, like every other icon on the site. 19 in a 44px box is an odd number either side. */
.socials svg{width:20px;height:20px;fill:var(--ink)}
.thanks{margin-block:var(--fgap) 0}
.thanks .eyebrow{color:var(--muted)}
/* Four lockups of four different widths. EQUAL CELLS with the logo contained inside is what
   makes them read as one row rather than four sizes: 2x2 on a phone, one row from 620px, the
   same cell height everywhere. Greyscale at 70% so a partner logo never shouts over the
   station's own, full colour on hover. */
.partners{display:grid;grid-template-columns:repeat(2,1fr);gap:6px 18px;align-items:center;
  justify-items:center;margin-block:12px 0}
@media(min-width:620px){.partners{grid-template-columns:repeat(4,1fr);gap:0 22px}}
.partners a{display:grid;place-items:center;width:100%;height:56px;min-height:0}
.partners img{max-height:30px;max-width:100%;width:auto;height:auto;
  filter:grayscale(1);opacity:.7;transition:filter .15s,opacity .15s}
@media(min-width:620px){.partners img{max-height:34px}}
/* ⚠️ EQUAL BOX HEIGHT IS NOT EQUAL VISUAL HEIGHT. Coimisiun na Mean is a two-line wordmark
   with no symbol, so at the shared height its letters read half again as big as the brand
   names beside it; ComReg's own type is the smallest of the four. These two nudges are what
   make the row look like one row. */
.partners .partner-cnam img{max-height:24px}
@media(min-width:620px){.partners .partner-cnam img{max-height:27px}}
.partners .partner-comreg img{max-height:33px}
@media(min-width:620px){.partners .partner-comreg img{max-height:38px}}
@media(hover:hover){.partners a:hover img{filter:none;opacity:1}}
.legal{margin-block:var(--fgap) 0;font-size:14px;color:var(--muted)}
.legal .row{display:flex;flex-wrap:wrap;gap:2px 16px;justify-content:center}
.pwr{margin-block:10px 0;font-size:14px;color:var(--muted)}
.pwr a{color:var(--muted)}

/* -------------------------------------------------------- sticky player */
/* A FLAT BAR: full width, one 1px hairline, no shadow. The glow used to spill a soft band
   above the hairline and read as a second edge (Ashton, 08/09). Its own bottom padding carries
   the safe-area inset, so the white runs under the home indicator. */
.player{
  position:fixed;left:0;right:0;bottom:0;z-index:70;background:#fff;
  border-top:1px solid var(--line);
  padding:9px var(--pad);padding-bottom:calc(9px + env(safe-area-inset-bottom));
}
.player-in{max-width:var(--max);margin-inline:auto;display:flex;align-items:center;gap:12px;text-align:left}
.play{
  width:48px;height:48px;flex:none;border-radius:999px;border:0;background:var(--mag);color:#fff;
  display:grid;place-items:center;cursor:pointer;transition:background .15s;
}
@media(hover:hover){.play:hover{background:var(--mag-hover)}}
.play svg{width:20px;height:20px;fill:currentColor}
/* the three icons live in every play control, round or pill; exactly one shows at a time */
[data-play] .ico-pause,[data-play] .ico-wait{display:none}
[data-play][data-state=playing] .ico-play{display:none}
[data-play][data-state=playing] .ico-pause{display:block}
[data-play][data-state=connecting] .ico-play{display:none}
[data-play][data-state=connecting] .ico-wait{display:block;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){[data-play][data-state=connecting] .ico-wait{animation:none}}
.btn[data-play] svg{fill:currentColor}
.player-txt{min-width:0;flex:1}
.player-live{font:600 11px/1.2 Inter,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:var(--mag-ink)}
/* ⛔ THE BAR CARRIES THE TRACK AND NOTHING ELSE (Ashton, 08/09): "Artist - Title", or the
   title alone where there is no artist, and "Juice FM Cork, 88.7" until a track is known.
   The show name stays on the on-air card, where it belongs; two competing lines of text in a
   44px bar is what made it read as clutter. One line, clipped at whatever width it gets. */
.player-now{display:flex;min-width:0;font-size:14px;color:var(--muted);margin-block:2px 0}
.player-now>span{min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.player-logo{display:none}
.mute{width:44px;height:44px;flex:none;border-radius:999px;border:1px solid var(--line);background:#fff;color:var(--ink);display:grid;place-items:center;cursor:pointer;transition:border-color .15s,color .15s}
@media(hover:hover){.mute:hover{border-color:var(--mag);color:var(--mag-ink)}}
.mute svg{width:20px;height:20px;fill:currentColor}
.mute .ico-off{display:none}
.mute[aria-pressed=true] .ico-on{display:none}
.mute[aria-pressed=true] .ico-off{display:block}
@media(min-width:900px){.player-logo{display:block;width:100px;height:23px;flex:none}}

/* -------------------------------------------------------- notice + modal */
/* One row, so the notice stays short enough that it never lands on top of a button.
   ⚠️ .notice sets display:flex, which beats the browser's own [hidden] rule, so the
   hidden state has to be spelled out or the bar shows before the JS has looked at it. */
.notice{position:fixed;left:12px;right:12px;bottom:calc(var(--player) + 12px);z-index:75;background:#fff;border:1px solid var(--line);border-radius:var(--r-card);box-shadow:0 8px 32px -6px rgba(17,17,17,.14);padding:12px 14px;max-width:560px;margin-inline:auto;display:flex;align-items:center;gap:12px;text-align:left}
.notice[hidden]{display:none}
.notice p{font-size:13px;line-height:1.45;color:var(--muted);flex:1;min-width:0}
.notice .btn{min-height:44px;font-size:14px;padding:0 18px;flex:none}

/* The card is the scroll container, not the overlay and never the page: on a phone a
   900 word document scrolls inside the dialog with its close controls always in reach. */
.modal{position:fixed;inset:0;z-index:90;background:rgba(17,17,17,.45);padding:16px;display:flex;align-items:center;justify-content:center;overscroll-behavior:contain}
.modal[hidden]{display:none}
.modal-in{background:#fff;border-radius:24px;max-width:640px;width:100%;max-height:calc(100vh - 32px);max-height:calc(100dvh - 32px);display:flex;flex-direction:column;overflow:hidden;text-align:left}
.modal-head{display:flex;align-items:center;gap:12px;padding:16px 18px;border-bottom:1px solid var(--line);flex:none}
.modal-head h2{flex:1;min-width:0;text-align:left;font-size:22px}
/* 18px in from a 24px corner radius, so the button sits clear of the curve */
.modal-x{width:44px;height:44px;flex:none;border-radius:999px;border:1px solid var(--line);background:#fff;color:var(--ink);display:grid;place-items:center;cursor:pointer}
@media(hover:hover){.modal-x:hover{border-color:var(--mag);color:var(--mag-ink)}}
.modal-x svg{width:18px;height:18px;fill:currentColor}
.modal-body{flex:1;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;padding:20px 18px}
.modal-foot{flex:none;padding:12px 18px calc(12px + env(safe-area-inset-bottom));border-top:1px solid var(--line)}
.modal-in .body{max-width:none}
.modal-close{display:block;margin-inline:auto;width:100%;max-width:260px}
@media(min-width:560px){.modal-head,.modal-body,.modal-foot{padding-inline:24px}.modal-head h2{font-size:24px}}
