/* Correspondance — consolidated production stylesheet (v95) */
:root { color-scheme: dark; --bg:#101010; --panel:#181818; --border:#333; --text:#f1f1f1; --muted:#999; }
* { box-sizing:border-box; }
button,input,select,textarea{font-family:inherit;}
body { font-family:"STM Montreal","STM Montréal","Montreal","Arial",sans-serif; max-width:860px; margin:40px auto; padding:0 20px 60px; background:var(--bg); color:var(--text); }
h1 { margin:0 0 5px; font-size:clamp(2.2rem,7vw,4.5rem); letter-spacing:-.06em; }
.sub { color:var(--muted); margin:0 0 30px; }
.panel { background:var(--panel); border:1px solid var(--border); border-radius:16px; padding:20px; margin-bottom:20px; }
.row { display:flex; gap:12px; align-items:end; flex-wrap:wrap; }
.field { display:flex; flex-direction:column; gap:6px; }
label,.label { color:var(--muted); font-size:.8rem; text-transform:uppercase; letter-spacing:.08em; }
input[type=date],button { font:inherit; min-height:44px; padding:9px 13px; border-radius:9px; border:1px solid #444; background:#222; color:var(--text); }
button { cursor:pointer; font-weight:650; }
button:disabled { opacity:.4; cursor:not-allowed; }
button.active { background:#eee; color:#111; }
.time { font-size:clamp(2.7rem,10vw,6rem); line-height:1; font-weight:750; letter-spacing:-.06em; margin:24px 0 5px; font-variant-numeric:tabular-nums; }
.dateText,.status { color:var(--muted); }
input[type=range] { width:100%; margin:20px 0 5px; accent-color:#ddd; }
.ends { display:flex; justify-content:space-between; color:#777; font-size:.78rem; }
.transport { display:flex; gap:10px; margin-top:22px; }
.status { margin-top:15px; min-height:22px; }
.voice { display:grid; grid-template-columns:120px 55px 1fr 70px; gap:12px; align-items:center; margin:15px 0; }
.name { font-weight:700; } .note { color:var(--muted); }
.meter { height:12px; background:#303030; border-radius:99px; overflow:hidden; }
.fill { height:100%; width:0; background:#e6e6e6; transition:width 2s linear; }
.value { text-align:right; font-variant-numeric:tabular-nums; }
@media(max-width:600px){ .voice{grid-template-columns:1fr auto auto}.meter{grid-column:1/-1;grid-row:2}.field{width:100%}input[type=date]{width:100%} }

#loadingOverlay{
  position:fixed; inset:0; z-index:9999;
  display:flex; align-items:center; justify-content:center;
  background:#090a0d;
  transition:opacity .8s ease, visibility .8s ease;
}
#loadingOverlay.hidden{opacity:0;visibility:hidden;pointer-events:none}
.loaderCard{text-align:center;max-width:520px;padding:42px}
.loaderTitle{font-size:clamp(32px,6vw,72px);letter-spacing:.08em;margin:0 0 24px}
.loaderStatus{opacity:.7;margin:12px 0 26px}
.spinner{
  width:36px;height:36px;margin:18px auto;border:2px solid rgba(255,255,255,.18);
  border-top-color:currentColor;border-radius:50%;animation:spin 1s linear infinite
}
@keyframes spin{to{transform:rotate(360deg)}}
#enterButton{display:none;font:inherit;padding:13px 24px;cursor:pointer}

/* ============================================================
   STM AMBIENT — DAY/NIGHT + TRANSIT IDENTITY
   ============================================================ */
:root{
  --sky-top:#07101f;
  --sky-bottom:#17132c;
  --glow:rgba(82,105,180,.18);
  --surface:rgba(16,18,28,.76);
  --surface-border:rgba(255,255,255,.15);
  --ui-text:#f4f6ff;
  --ui-muted:rgba(244,246,255,.66);
  --meter-track:rgba(255,255,255,.14);
}
html{min-height:100%;background:var(--sky-bottom)}
body{
  position:relative;
  min-height:100vh;
  max-width:none;
  margin:0;
  padding:40px max(20px,calc((100vw - 860px)/2)) 60px;
  color:var(--ui-text);
  background:
    radial-gradient(circle at 78% 14%,var(--glow),transparent 34%),
    linear-gradient(180deg,var(--sky-top) 0%,var(--sky-bottom) 100%);
  background-attachment:fixed;
  transition:background .8s linear,color .8s linear;
}
body::before{
  content:"";
  position:fixed;inset:0;pointer-events:none;z-index:-1;
  background:linear-gradient(180deg,rgba(255,255,255,.025),transparent 35%);
}
.panel{
  background:var(--surface);
  border-color:var(--surface-border);
  box-shadow:0 18px 50px rgba(0,0,0,.10);
  backdrop-filter:blur(12px);
  transition:background .8s linear,border-color .8s linear;
}
.sub,.dateText,.status,label,.label,.note{color:var(--ui-muted)}
.meter{background:var(--meter-track)}
.fill{background:var(--ui-text)}
input[type=range]{accent-color:var(--ui-text)}
input[type=date],select,button{
  border-color:var(--surface-border);
  background:rgba(12,14,22,.42);
  color:var(--ui-text);
}
button.active{background:var(--ui-text);color:#111}

.brandRow{
  display:flex;align-items:center;gap:18px;margin-bottom:4px;
}
.brandRow h1{margin:0}
.metroMark{
  width:58px;height:58px;flex:0 0 58px;
  border-radius:14px;
  display:grid;place-items:center;
  background:#005ca9;
  color:white;
  box-shadow:0 8px 24px rgba(0,0,0,.18);
}
.metroMark svg{width:39px;height:39px;display:block}
.dayPhase{
  margin-left:auto;
  align-self:flex-end;
  padding-bottom:8px;
  color:var(--ui-muted);
  font-size:.72rem;
  font-weight:750;
  letter-spacing:.12em;
  text-transform:uppercase;
}
.voice{
  grid-template-columns:120px 55px 1fr 70px 105px;
}
.densityWord{
  color:var(--ui-muted);
  font-size:.72rem;
  font-weight:750;
  letter-spacing:.08em;
  text-transform:uppercase;
  white-space:nowrap;
}
.voiceDetail{
  grid-column:1/-1;
  margin-top:-7px;
  color:var(--ui-muted);
  font-size:.74rem;
}
@media(max-width:700px){
  .brandRow{align-items:flex-start}
  .metroMark{width:48px;height:48px;flex-basis:48px;border-radius:12px}
  .metroMark svg{width:32px;height:32px}
  .dayPhase{display:none}
  .voice{grid-template-columns:1fr auto auto}
  .meter{grid-column:1/-1;grid-row:2}
  .densityWord{grid-column:1/-1;margin-top:2px}
  .voiceDetail{grid-column:1/-1;margin-top:0}
}

/* Readability safeguard across bright sunrise/daytime backgrounds. */
.brandRow h1,.dayPhase,.sub{
  text-shadow:0 2px 14px rgba(0,0,0,.45);
}

/* ============================================================
   HERO IDENTITY + MASTER VOLUME
   ============================================================ */
.hero{
  display:grid;
  grid-template-columns:minmax(0,2fr) minmax(220px,1fr);
  gap:42px;
  align-items:center;
  min-height:250px;
  margin:0 0 26px;
}
.heroCopy{
  min-width:0;
  display:flex;
  flex-direction:column;
  align-items:flex-start;
}
.hero h1{
  margin:0 0 8px;
  font-size:clamp(3.4rem,8vw,6.6rem);
  line-height:.88;
  letter-spacing:-.065em;
  text-shadow:0 3px 22px rgba(0,0,0,.34);
}
.hero .sub{
  margin:10px 0 10px;
  max-width:520px;
}
.hero .dayPhase{
  margin:0 0 16px;
  padding:0;
  align-self:auto;
}
.byline{
  color:var(--ui-muted);
  font-size:.82rem;
  letter-spacing:.04em;
}
.byline span{
  color:var(--ui-text);
  font-weight:750;
}
.metroMarkHero{
  width:min(29vw,260px);
  height:min(29vw,260px);
  min-width:220px;
  min-height:220px;
  justify-self:end;
  border-radius:34px;
  box-shadow:0 24px 70px rgba(0,0,0,.24);
}
.metroMarkHero svg{
  width:72%;
  height:72%;
}
.audioControls{
  align-items:center;
  flex-wrap:wrap;
}
.volumeControl{
  display:grid;
  grid-template-columns:auto minmax(120px,220px) 44px;
  gap:10px;
  align-items:center;
  margin-left:auto;
  min-width:min(100%,330px);
}
.volumeControl label{
  margin:0;
}
.volumeControl input[type=range]{
  width:100%;
  margin:0;
}
#volumeValue{
  color:var(--ui-muted);
  font-size:.78rem;
  font-variant-numeric:tabular-nums;
  text-align:right;
}
@media(max-width:700px){
  .hero{
    grid-template-columns:minmax(0,1.45fr) minmax(120px,.75fr);
    gap:18px;
    min-height:180px;
  }
  .hero h1{font-size:clamp(2.7rem,12vw,4.5rem)}
  .metroMarkHero{
    width:min(32vw,170px);
    height:min(32vw,170px);
    min-width:120px;
    min-height:120px;
    border-radius:22px;
  }
  .hero .dayPhase{display:block}
  .volumeControl{
    width:100%;
    margin-left:0;
    grid-template-columns:auto 1fr 44px;
  }
}

/* ============================================================
   WIDER LAYOUT + PROVIDED BRAND ASSETS
   ============================================================ */
body{
  /* Wider canvas: substantially less empty side margin on desktop. */
  padding-left:max(28px,calc((100vw - 1180px)/2));
  padding-right:max(28px,calc((100vw - 1180px)/2));
}
.hero{
  grid-template-columns:minmax(0,2fr) minmax(300px,1fr);
  gap:54px;
  min-height:300px;
}
.stmLogoLink{
  display:flex;
  width:100%;
  max-width:360px;
  justify-self:end;
  align-items:center;
  justify-content:center;
  border-radius:26px;
  transition:transform .2s ease,opacity .2s ease;
}
.stmLogoLink:hover,
.stmLogoLink:focus-visible{
  transform:scale(1.025);
  opacity:.92;
}
.stmLogoImage{
  display:block;
  width:100%;
  height:auto;
  max-height:290px;
  object-fit:contain;
}
.byline{
  display:flex;
  align-items:center;
  gap:10px;
  margin-top:4px;
}
.byText{
  color:var(--ui-muted);
  font-size:.78rem;
  text-transform:uppercase;
  letter-spacing:.12em;
}
.wmsLogoLink{
  display:inline-flex;
  align-items:center;
  width:190px;
  max-width:38vw;
  border-radius:8px;
  transition:transform .2s ease,opacity .2s ease;
}
.wmsLogoLink:hover,
.wmsLogoLink:focus-visible{
  transform:translateY(-1px);
  opacity:.9;
}
.wmsLogoImage{
  display:block;
  width:100%;
  height:auto;
  object-fit:contain;
}
@media(max-width:800px){
  body{
    padding-left:18px;
    padding-right:18px;
  }
  .hero{
    grid-template-columns:minmax(0,1.4fr) minmax(130px,.6fr);
    gap:20px;
    min-height:190px;
  }
  .stmLogoLink{
    max-width:210px;
  }
  .stmLogoImage{
    max-height:180px;
  }
  .wmsLogoLink{
    width:150px;
    max-width:46vw;
  }
}
@media(max-width:520px){
  .hero{
    grid-template-columns:1fr;
    gap:22px;
  }
  .stmLogoLink{
    justify-self:start;
    width:180px;
  }
}

/* ============================================================
   TWO-COLUMN TRANSIT DASHBOARD + STM LINE COLOURS
   ============================================================ */
.dashboardGrid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:22px;
  align-items:stretch;
}
.dashboardGrid > .panel{
  min-width:0;
  margin-bottom:20px;
}
.timePanel,
.activityPanel{
  height:100%;
}

/* The activity card is compact enough to sit beside the clock card. */
.activityPanel .voice{
  grid-template-columns:72px 38px minmax(70px,1fr) 54px minmax(118px,1.2fr);
  gap:8px;
  margin:12px 0;
  min-width:0;
}
.activityPanel .name,
.activityPanel .note,
.activityPanel .value{
  min-width:0;
}
.activityPanel .densityWord{
  min-width:0;
  max-width:100%;
  white-space:normal;
  overflow-wrap:anywhere;
  line-height:1.12;
  text-align:right;
  font-size:.66rem;
  letter-spacing:.045em;
}
.activityPanel .voiceDetail{
  grid-column:1/-1;
  margin-top:-3px;
  line-height:1.25;
  font-size:.69rem;
}

/* Montréal Métro line identities + STM turquoise buses. */
.line1Voice{ --route-colour:#00A651; }
.line2Voice{ --route-colour:#F58220; }
.line4Voice{ --route-colour:#FFD200; }
.line5Voice{ --route-colour:#0072BC; }
.busVoice  { --route-colour:#00A7CE; }

.activityPanel .voice .meter{
  background:rgba(255,255,255,.14);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.04);
}
.activityPanel .voice .fill{
  background:var(--route-colour);
  box-shadow:0 0 14px color-mix(in srgb,var(--route-colour) 42%,transparent);
}

/* Tiny colour cue beside each service name as well as the meter itself. */
.activityPanel .name{
  display:flex;
  align-items:center;
  gap:7px;
}
.activityPanel .name::before{
  content:"";
  width:8px;
  height:8px;
  flex:0 0 8px;
  border-radius:50%;
  background:var(--route-colour);
  box-shadow:0 0 10px color-mix(in srgb,var(--route-colour) 40%,transparent);
}

/* Keep the clock/transport card visually balanced beside activity. */
.timePanel .time{
  font-size:clamp(3.3rem,6.2vw,5.7rem);
}
.timePanel .transport{
  margin-top:18px;
}

/* Existing generic mobile .voice rule is overridden here deliberately. */
@media(max-width:960px){
  .dashboardGrid{
    grid-template-columns:1fr;
  }
  .activityPanel .voice{
    grid-template-columns:105px 48px minmax(100px,1fr) 62px minmax(145px,1fr);
  }
}
@media(max-width:650px){
  .activityPanel .voice{
    grid-template-columns:1fr auto;
    gap:7px 10px;
    padding:4px 0 8px;
  }
  .activityPanel .note{
    text-align:right;
  }
  .activityPanel .meter{
    grid-column:1/-1;
    grid-row:auto;
  }
  .activityPanel .value{
    text-align:left;
  }
  .activityPanel .densityWord{
    text-align:right;
    white-space:normal;
  }
  .activityPanel .voiceDetail{
    grid-column:1/-1;
    margin-top:0;
  }
}

/* ============================================================
   CORRESPONDANCE IDENTITY + DATA ATTRIBUTION
   ============================================================ */
.hero{
  display:block;
  min-height:0;
  margin-bottom:30px;
}
.heroCopy{
  max-width:900px;
}
.hero h1{
  font-size:clamp(4rem,9vw,7.6rem);
}
.hero .sub{
  max-width:680px;
}
.byline{
  margin-top:14px;
}
.wmsLogoLink{
  width:310px;
  max-width:min(52vw,310px);
}
.clockPhase{
  display:inline-flex;
  align-items:center;
  width:max-content;
  margin:8px 0 10px;
  padding:6px 10px;
  border:1px solid rgba(255,255,255,.18);
  border-radius:999px;
  background:rgba(255,255,255,.07);
  color:var(--ui-text);
  font-size:.69rem;
  font-weight:800;
  letter-spacing:.13em;
  text-transform:uppercase;
}
.dataFooter{
  display:grid;
  grid-template-columns:minmax(180px,260px) minmax(0,1fr);
  gap:34px;
  align-items:center;
  margin-top:10px;
  padding:28px 30px;
  border:1px solid var(--surface-border);
  border-radius:16px;
  background:var(--surface);
  backdrop-filter:blur(12px);
}
.stmFooterLink{
  display:block;
  width:100%;
  transition:transform .2s ease,opacity .2s ease;
}
.stmFooterLink:hover,
.stmFooterLink:focus-visible{
  transform:scale(1.02);
  opacity:.92;
}
.stmFooterLogo{
  display:block;
  width:100%;
  height:auto;
  max-height:130px;
  object-fit:contain;
}
.dataCredit{
  display:flex;
  flex-direction:column;
  gap:7px;
  color:var(--ui-muted);
  font-size:.76rem;
  line-height:1.45;
}
.dataCredit strong{
  color:var(--ui-text);
  font-size:.82rem;
}
.dataCredit a{
  width:max-content;
  color:var(--ui-text);
  text-underline-offset:3px;
}
@media(max-width:700px){
  .hero h1{
    font-size:clamp(3.2rem,15vw,5.2rem);
  }
  .wmsLogoLink{
    width:230px;
    max-width:62vw;
  }
  .dataFooter{
    grid-template-columns:1fr;
    gap:18px;
  }
  .stmFooterLink{
    width:190px;
  }
}

/* ============================================================
   CORRESPONDANCE — OPENING SCREEN
   ============================================================ */
#loadingOverlay{
  background:
    radial-gradient(circle at 72% 28%,rgba(0,167,206,.18),transparent 30%),
    radial-gradient(circle at 28% 72%,rgba(88,68,150,.18),transparent 34%),
    linear-gradient(145deg,#07101f 0%,#17132c 58%,#101827 100%);
}
.correspondanceLoader{
  width:min(760px,92vw);
  text-align:center;
  max-width:none;
  padding:52px 34px 42px;
}
.loaderEyebrow{
  margin-bottom:16px;
  color:rgba(248,249,255,.58);
  font-size:.68rem;
  font-weight:800;
  letter-spacing:.19em;
}
.correspondanceLoader .loaderTitle{
  width:100%;
  margin:0 auto;
  text-align:center;
  font-size:clamp(3.2rem,9vw,7.2rem);
  line-height:.9;
  letter-spacing:-.055em;
  text-transform:none;
  text-shadow:0 8px 38px rgba(0,0,0,.32);
}
.loaderIntro{
  max-width:500px;
  margin:20px auto 25px;
  color:rgba(248,249,255,.68);
  font-size:clamp(.88rem,1.8vw,1.05rem);
  line-height:1.45;
}
.loaderRule{
  display:grid;
  grid-template-columns:1.15fr 1fr .75fr 1fr 1.15fr;
  gap:5px;
  width:min(430px,72vw);
  height:5px;
  margin:0 auto 30px;
}
.loaderRule span{
  display:block;
  border-radius:99px;
}
.loaderRule span:nth-child(1){background:#00A651}
.loaderRule span:nth-child(2){background:#F58220}
.loaderRule span:nth-child(3){background:#FFD200}
.loaderRule span:nth-child(4){background:#0072BC}
.loaderRule span:nth-child(5){background:#00A7CE}
.correspondanceLoader .loaderStatus{
  min-height:20px;
  margin:0 0 18px;
  color:rgba(248,249,255,.64);
  font-size:.76rem;
  letter-spacing:.045em;
}
.correspondanceLoader .spinner{
  margin:16px auto 22px;
  border-color:rgba(255,255,255,.16);
  border-top-color:#00A7CE;
}
#enterButton{
  min-width:220px;
  padding:13px 22px;
  border:1px solid rgba(255,255,255,.28);
  background:rgba(255,255,255,.10);
  color:#f8f9ff;
  letter-spacing:.08em;
  transition:background .2s ease,transform .2s ease,border-color .2s ease;
}
#enterButton:hover,
#enterButton:focus-visible{
  background:rgba(255,255,255,.17);
  border-color:rgba(255,255,255,.42);
  transform:translateY(-1px);
}
.loaderWmsLink{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  width:max-content;
  max-width:80%;
  margin:28px auto 0;
  color:rgba(248,249,255,.52);
  text-decoration:none;
  font-size:.66rem;
  letter-spacing:.12em;
  text-transform:uppercase;
  transition:opacity .2s ease;
}
.loaderWmsLink:hover,
.loaderWmsLink:focus-visible{
  opacity:.82;
}
.loaderWmsLink img{
  display:block;
  width:180px;
  max-width:45vw;
  height:auto;
}
@media(max-width:600px){
  .correspondanceLoader{
    padding:36px 20px 30px;
  }
  .loaderEyebrow{
    font-size:.58rem;
  }
  .loaderRule{
    width:min(340px,78vw);
  }
  #enterButton{
    min-width:190px;
  }
  .loaderWmsLink img{
    width:145px;
  }
}

/* Correspondance typography + title lockup */
html,body,button,input,select,textarea{
  font-family:"STM Montreal","STM Montréal","Montreal","Arial",sans-serif;
}
.correspondanceLoader,
.correspondanceLoader .loaderTitle{
  text-align:center;
}
.heroTitleRow{
  display:flex;
  align-items:flex-end;
  gap:20px;
  flex-wrap:wrap;
}
.heroTitleRow h1{
  margin:0;
}
.heroTitleRow .byline{
  display:flex;
  align-items:center;
  gap:9px;
  margin:0 0 .38rem;
}
.heroTitleRow .wmsLogoLink{
  width:220px;
  max-width:32vw;
}
.heroTitleRow .wmsLogoImage{
  display:block;
  width:100%;
  height:auto;
}
@media(max-width:650px){
  .heroTitleRow{
    gap:10px;
  }
  .heroTitleRow .wmsLogoLink{
    width:165px;
    max-width:48vw;
  }
}

/* Footer brand lockup: WillMakeSound left, STM right */
.footerBrandRow{
  display:flex;
  align-items:center;
  justify-content:flex-start;
  gap:28px;
  flex-wrap:wrap;
}
.footerWms{
  display:flex;
  align-items:center;
  gap:9px;
}
.footerWms .wmsLogoLink{
  display:block;
  width:220px;
  max-width:38vw;
}
.footerWms .wmsLogoImage{
  display:block;
  width:100%;
  height:auto;
}
.footerWms .byText{
  color:var(--ui-muted);
}
@media(max-width:650px){
  .footerBrandRow{gap:18px;}
  .footerWms .wmsLogoLink{width:165px;max-width:48vw;}
}

/* Correspondance UI refinements */
#dateInput{
  min-width:180px;
  padding:11px 16px;
  border-radius:14px;
  font-size:1rem;
}

/* Slightly shorter master-volume control */
#volumeSlider{
  width:min(210px, 42vw);
  max-width:210px;
}

/* Vertically center the Metro + bus activity controls in their panel. */
.activityPanel,
.serviceActivity,
.serviceActivityPanel,
.activityBox{
  align-content:center;
}

/* Target the right-hand dashboard box without changing the left transport box. */
.dashboard > :last-child,
.mainGrid > :last-child,
.contentGrid > :last-child{
  display:flex;
  flex-direction:column;
  justify-content:center;
}

/* Final loading-title alignment correction */
.correspondanceLoader .loaderTitle{
  position:relative;
  left:-28px;
  width:calc(100% + 28px);
  text-align:center;
}
@media(max-width:600px){
  .correspondanceLoader .loaderTitle{
    left:-14px;
    width:calc(100% + 14px);
  }
}

/* Strong visual correction for the loading-screen wordmark */
.correspondanceLoader .loaderTitle{
  position:relative !important;
  left:-105px !important;
  width:calc(100% + 105px) !important;
  text-align:center !important;
}
@media(max-width:600px){
  .correspondanceLoader .loaderTitle{
    left:-42px !important;
    width:calc(100% + 42px) !important;
  }
}

/* Loading-screen wordmark: stronger leftward visual correction */
.correspondanceLoader .loaderTitle{
  left:-110px !important;
  width:calc(100% + 110px) !important;
}
@media(max-width:600px){
  .correspondanceLoader .loaderTitle{
    left:-82px !important;
    width:calc(100% + 82px) !important;
  }
}

/* Per-layer Mute / Solo controls */
.activityPanel .voice{
  grid-template-columns:72px 38px minmax(70px,1fr) 54px minmax(118px,1.2fr) auto !important;
}
.layerControls{
  display:flex;
  align-items:center;
  justify-content:flex-end;
  gap:5px;
}
.layerToggle{
  min-width:30px;
  height:26px;
  padding:0 7px;
  border:1px solid rgba(255,255,255,.18);
  border-radius:8px;
  background:rgba(255,255,255,.06);
  color:var(--ui-text);
  font-size:.65rem;
  line-height:1;
  cursor:pointer;
}
.layerToggle:hover{background:rgba(255,255,255,.12)}
.layerToggle.active{
  background:var(--route-colour);
  border-color:var(--route-colour);
  color:#071018;
  font-weight:700;
}
.activityPanel .voiceDetail{grid-column:1/-1 !important;}
@media(max-width:650px){
  .activityPanel .voice{
    grid-template-columns:1fr auto !important;
  }
  .layerControls{
    grid-column:2;
    grid-row:1;
  }
}

/* Loading-screen brand pair */
.loaderBrandRow{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:28px;
  margin:28px auto 0;
}
.loaderBrandRow .loaderWmsLink{
  margin:0;
  max-width:none;
}
.loaderBrandRow .loaderWmsLink img{
  width:180px;
  max-width:38vw;
}
.loaderStmLink{
  display:block;
  line-height:0;
  transition:opacity .2s ease;
}
.loaderStmLink:hover,
.loaderStmLink:focus-visible{opacity:.82;}
.loaderStmLink img{
  display:block;
  width:72px;
  max-width:17vw;
  height:auto;
}

/* Bottom attribution: logos together on the left, copy shifted alongside. */
.dataFooter{
  display:grid !important;
  grid-template-columns:auto minmax(0,1fr) !important;
  align-items:center !important;
  column-gap:34px !important;
}
.footerBrandRow{
  display:flex !important;
  align-items:center !important;
  justify-content:flex-start !important;
  gap:24px !important;
  flex-wrap:nowrap !important;
}
.footerWms{display:block !important;}
.footerWms .wmsLogoLink{
  width:205px !important;
  max-width:24vw !important;
}
.stmFooterLink{flex:0 0 auto;}
.stmFooterLogo{
  width:78px !important;
  height:auto !important;
}
.dataCredit{
  min-width:0;
  margin:0 !important;
}

@media(max-width:650px){
  .loaderBrandRow{gap:20px;}
  .loaderBrandRow .loaderWmsLink img{width:145px;}
  .loaderStmLink img{width:58px;}
  .dataFooter{
    grid-template-columns:1fr !important;
    row-gap:20px !important;
  }
  .footerWms .wmsLogoLink{
    width:165px !important;
    max-width:55vw !important;
  }
  .stmFooterLogo{width:64px !important;}
}

.activityPanel .voiceDetail{
  font-size:.66rem !important;
  line-height:1.18 !important;
}

.loaderTitle,.heroTitleRow h1{font-family:"Arial Rounded MT Bold","Trebuchet MS","Avenir Next",Arial,sans-serif!important;font-weight:800!important;letter-spacing:.055em}
.heroTitleRow{display:flex;align-items:center;gap:22px}
.titleRouteRule{display:grid;grid-template-columns:1.15fr 1fr .75fr 1fr 1.15fr;gap:4px;width:190px;height:6px;flex:0 0 auto}
.titleRouteRule span{border-radius:99px}.titleRouteRule span:nth-child(1){background:#00A651}.titleRouteRule span:nth-child(2){background:#0072BC}.titleRouteRule span:nth-child(3){background:#FFD200}.titleRouteRule span:nth-child(4){background:#F58220}.titleRouteRule span:nth-child(5){background:#00A7CE}
.panelHeading{margin:0 0 14px;font-size:.78rem;line-height:1;letter-spacing:.11em;text-transform:uppercase;color:var(--ui-muted)}
.activityHeader{margin-bottom:8px}.activityHeader .panelHeading{margin-bottom:7px}.activityHeader p{margin:0;max-width:560px;color:var(--ui-muted);font-size:.69rem;line-height:1.3}
.timePanel .time,.timePanel .clockPhase,.timePanel .dateText{text-align:center}.timePanel .time{width:100%;margin-left:auto;margin-right:auto}
#volumeSlider{width:145px!important;max-width:145px!important}.audioControls{display:flex;align-items:center;flex-wrap:nowrap;gap:10px}.volumeControl{min-width:0;flex:0 1 auto}
.loaderBrandRow .loaderWmsLink img{width:220px!important;max-width:42vw!important}.loaderStmLink img{width:90px!important;max-width:19vw!important}
.footerWms .wmsLogoLink{width:245px!important;max-width:28vw!important}.stmFooterLogo{width:96px!important}.dataFooter{column-gap:42px!important}
@media(max-width:650px){.titleRouteRule{width:115px;height:5px}.audioControls{flex-wrap:wrap}#volumeSlider{width:125px!important;max-width:125px!important}.loaderBrandRow .loaderWmsLink img{width:175px!important}.loaderStmLink img{width:70px!important}.footerWms .wmsLogoLink{width:190px!important;max-width:58vw!important}.stmFooterLogo{width:76px!important}}

/* Requested brand scale-up */
.loaderBrandRow .loaderWmsLink img{
  width:330px !important;
  max-width:52vw !important;
}
.loaderStmLink img{
  width:180px !important;
  max-width:28vw !important;
}
.footerWms .wmsLogoLink{
  width:368px !important;
  max-width:38vw !important;
}
.stmFooterLogo{
  width:192px !important;
}
@media(max-width:650px){
  .loaderBrandRow .loaderWmsLink img{width:230px !important;max-width:55vw !important;}
  .loaderStmLink img{width:105px !important;max-width:27vw !important;}
  .footerWms .wmsLogoLink{width:235px !important;max-width:58vw !important;}
  .stmFooterLogo{width:110px !important;}
}

/* Stronger panel headings */
.panelHeading{
  font-size:1rem !important;
  letter-spacing:.12em !important;
  font-weight:700 !important;
  margin-bottom:16px !important;
}
.activityHeader .panelHeading{
  margin-bottom:8px !important;
}

/* Center the weekday selector + RIGHT NOW group above the clock. */
.timePanel > .row{
  justify-content:center !important;
  align-items:flex-end !important;
  gap:14px !important;
  width:100%;
}
.timePanel > .row .field{
  flex:0 0 auto !important;
}

/* A little more room for master-volume control while keeping one-row layout. */
#volumeSlider{
  width:165px !important;
  max-width:165px !important;
}
@media(max-width:650px){
  #volumeSlider{
    width:140px !important;
    max-width:140px !important;
  }
}

/* Hide the weekday label visually; the select remains accessible. */
.srOnly{
  position:absolute !important;
  width:1px !important;
  height:1px !important;
  padding:0 !important;
  margin:-1px !important;
  overflow:hidden !important;
  clip:rect(0,0,0,0) !important;
  white-space:nowrap !important;
  border:0 !important;
}

/* Larger section headings for both dashboard boxes. */
.panelHeading{
  font-size:1.25rem !important;
  line-height:1.05 !important;
  letter-spacing:.11em !important;
  font-weight:700 !important;
  margin-bottom:18px !important;
}
.activityHeader .panelHeading{
  margin-bottom:9px !important;
}

#transportPlayPause{display:none !important;}

/* Activity-row spacing after removal of the old LOOP column.
   Five visible items now share five columns:
   name | meter | percentage | activity label | M/S */
@media(min-width:961px){
  .activityPanel .voice{
    grid-template-columns:82px minmax(62px,1fr) 64px minmax(96px,1.15fr) auto !important;
    column-gap:12px !important;
    row-gap:8px !important;
    align-items:center;
  }
}
@media(min-width:651px) and (max-width:960px){
  .activityPanel .voice{
    grid-template-columns:105px minmax(100px,1fr) 68px minmax(130px,1fr) auto !important;
    column-gap:12px !important;
  }
}
.activityPanel .voiceDetail{
  grid-column:1/-1 !important;
}

/* Artist statement + system notes mirror the two-column dashboard above. */
.infoGrid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:22px;
  align-items:stretch;
  margin-top:22px;
}
.infoPanel{
  min-height:0;
}
.infoPanel p{
  margin:10px 0 0;
  color:var(--ui-muted);
  font-size:.82rem;
  line-height:1.55;
  max-width:68ch;
}
.infoPanel em{
  color:var(--ui-text);
}

/* Compact the branding/attribution box to make room for the new information row. */
.dataFooter{
  padding:16px 22px !important;
  column-gap:28px !important;
  margin-top:22px !important;
}
.footerBrandRow{
  gap:20px !important;
}
.dataCredit{
  gap:4px !important;
  line-height:1.35 !important;
}

@media(max-width:960px){
  .infoGrid{
    grid-template-columns:1fr;
  }
}
@media(max-width:650px){
  .infoGrid{
    gap:16px;
    margin-top:16px;
  }
  .infoPanel p{
    font-size:.78rem;
    line-height:1.5;
  }
  .dataFooter{
    padding:14px 16px !important;
  }
}

/* Lower information row: statement gets more room than system explanation. */
.infoGrid{
  grid-template-columns:minmax(0,65fr) minmax(0,35fr) !important;
}

/* Tighter attribution/branding strip. */
.dataFooter{
  padding:10px 16px !important;
  column-gap:20px !important;
}
.footerBrandRow{
  gap:16px !important;
}

/* 60th-anniversary mark sits at the far right of the attribution copy. */
.dataCreditWithAnniversary{
  display:grid !important;
  grid-template-columns:minmax(0,1fr) auto;
  align-items:center;
  gap:20px;
}
.metro60Logo{
  display:block;
  width:250px;
  max-width:24vw;
  height:auto;
}

@media(max-width:960px){
  .infoGrid{
    grid-template-columns:1fr !important;
  }
}
@media(max-width:700px){
  .dataCreditWithAnniversary{
    grid-template-columns:1fr;
    gap:12px;
  }
  .metro60Logo{
    width:220px;
    max-width:72vw;
  }
}

/* Final lower-row balance. */
.infoGrid{
  grid-template-columns:minmax(0,60fr) minmax(0,40fr) !important;
}

/* Let Artist Statement copy use the full available text width. */
.infoPanel:first-child p{
  max-width:none !important;
  width:100%;
}

/* Compact, evenly balanced footer lockup:
   WillMakeSound | STM | attribution | 60th anniversary */
.dataFooter{
  padding:8px 18px !important;
  column-gap:26px !important;
  min-height:0 !important;
}
.footerBrandRow{
  gap:26px !important;
  align-items:center !important;
}
.dataCreditWithAnniversary{
  gap:26px !important;
  align-items:center !important;
}
.footerWms .wmsLogoLink{
  width:230px !important;
}
.stmFooterLogo{
  width:86px !important;
}
.metro60Logo{
  width:280px !important;
  max-width:23vw !important;
}
.dataCreditText{
  max-width:360px;
}

@media(max-width:960px){
  .infoGrid{
    grid-template-columns:1fr !important;
  }
}
@media(max-width:700px){
  .dataFooter{
    padding:12px 14px !important;
  }
  .metro60Logo{
    width:230px !important;
    max-width:72vw !important;
  }
  .dataCreditText{
    max-width:none;
  }
}

/* Footer micro-adjustments: stronger STM mark and bring anniversary mark inward. */
.stmFooterLogo{
  width:112px !important;
}
.dataCreditWithAnniversary{
  gap:18px !important;
}
.metro60Logo{
  width:280px !important;
  max-width:23vw !important;
  margin-right:24px !important;
}
@media(max-width:700px){
  .metro60Logo{
    margin-right:0 !important;
  }
}

/* Lower information copy should fill the usable width of both cards. */
.infoPanel p{
  max-width:none !important;
  width:100% !important;
  box-sizing:border-box;
}
.infoPanel{
  min-width:0;
}
.infoPanel p,
.infoPanel em{
  overflow-wrap:break-word;
}

/* Footer v11: branding row first, attribution beneath. */
.footerStacked{
  display:flex !important;
  flex-direction:column !important;
  align-items:stretch !important;
  gap:10px !important;
  padding:12px 22px !important;
}
.footerLogoRow{
  display:grid;
  grid-template-columns:1fr 1fr 1fr;
  align-items:center;
  gap:34px;
  width:100%;
}
.footerLogoRow .footerWms{
  display:flex;
  justify-content:flex-start;
  align-items:center;
}
.footerLogoRow .stmFooterLink{
  display:flex;
  justify-content:center;
  align-items:center;
}
.footerLogoRow .metro60Logo{
  justify-self:end;
  margin-right:0 !important;
}
.footerCreditBottom{
  display:block !important;
  width:100%;
  max-width:none !important;
  padding-top:8px;
  border-top:1px solid rgba(248,249,255,.10);
  font-size:.74rem;
  line-height:1.35 !important;
}
.footerCreditBottom br{
  display:none;
}
.footerCreditBottom a{
  margin-left:.35em;
}

@media(max-width:700px){
  .footerLogoRow{
    grid-template-columns:1fr;
    gap:14px;
    justify-items:center;
  }
  .footerLogoRow .footerWms,
  .footerLogoRow .stmFooterLink{
    justify-content:center;
  }
  .footerLogoRow .metro60Logo{
    justify-self:center;
  }
  .footerCreditBottom{
    text-align:center;
  }
}

/* Footer v12: larger branding, grouped toward the center instead of the edges. */
.footerLogoRow{
  display:flex !important;
  justify-content:center !important;
  align-items:center !important;
  gap:90px !important;
  width:100%;
}
.footerLogoRow .footerWms,
.footerLogoRow .stmFooterLink{
  display:flex !important;
  justify-content:center !important;
  align-items:center !important;
  flex:0 0 auto;
}
.footerLogoRow .footerWms .wmsLogoLink{
  width:285px !important;
}
.footerLogoRow .stmFooterLogo{
  width:155px !important;
}
.footerLogoRow .metro60Logo{
  width:330px !important;
  max-width:none !important;
  margin:0 !important;
  justify-self:auto !important;
  flex:0 0 auto;
}

@media(max-width:1050px){
  .footerLogoRow{
    gap:52px !important;
  }
  .footerLogoRow .footerWms .wmsLogoLink{
    width:245px !important;
  }
  .footerLogoRow .stmFooterLogo{
    width:135px !important;
  }
  .footerLogoRow .metro60Logo{
    width:285px !important;
  }
}
@media(max-width:700px){
  .footerLogoRow{
    flex-direction:column !important;
    gap:18px !important;
  }
  .footerLogoRow .footerWms .wmsLogoLink{
    width:250px !important;
  }
  .footerLogoRow .stmFooterLogo{
    width:145px !important;
  }
  .footerLogoRow .metro60Logo{
    width:290px !important;
    max-width:82vw !important;
  }
}

@font-face{
  font-family:"Fira Sans Condensed";
  src:url("fonts/FiraSansCondensed-Regular.ttf") format("truetype");
  font-style:normal;
  font-weight:400;
  font-display:swap;
}
@font-face{
  font-family:"Fira Sans Condensed";
  src:url("fonts/FiraSansCondensed-SemiBold.ttf") format("truetype");
  font-style:normal;
  font-weight:600;
  font-display:swap;
}
@font-face{
  font-family:"Fira Sans Condensed";
  src:url("fonts/FiraSansCondensed-Bold.ttf") format("truetype");
  font-style:normal;
  font-weight:700;
  font-display:swap;
}
@font-face{
  font-family:"Fira Sans Condensed";
  src:url("fonts/FiraSansCondensed-ExtraBold.ttf") format("truetype");
  font-style:normal;
  font-weight:800;
  font-display:swap;
}

/* Use the transit-inspired condensed face for headings/labels, while keeping
   longer body copy in the existing readable body face. */
.panelHeading,
.activityPanel h2,
.timePanel h2,
.infoPanel h2,
.eyebrow,
.clockPhase,
.densityWord,
.voice .name,
button,
select,
label{
  font-family:"Fira Sans Condensed",sans-serif !important;
}

/* Give the two explanatory cards more readable body copy. */
.infoPanel p{
  font-size:.94rem !important;
  line-height:1.58 !important;
}

/* Keep the legal/data attribution compact enough to remain one desktop line. */
.footerCreditBottom{
  font-size:.62rem !important;
  line-height:1.2 !important;
  white-space:nowrap;
}
.footerCreditBottom br{
  display:none !important;
}

@media(max-width:1050px){
  .footerCreditBottom{
    font-size:.58rem !important;
  }
}
@media(max-width:820px){
  .footerCreditBottom{
    white-space:normal;
    text-align:center;
    font-size:.62rem !important;
  }
}

/* V14 — title + footer containment fixes */

/* Main title: Fira Sans Condensed, all caps. */
.title{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-weight:600 !important;
  text-transform:uppercase !important;
  letter-spacing:.015em !important;
}

/* Make the five-colour motif beneath CORRESPONDANCE longer. */
.lineMotif{
  width:330px !important;
  max-width:48vw;
}
.lineMotif span{
  flex:1 1 0 !important;
}

/* Keep the entire branding lockup INSIDE the footer card at every width. */
.footerStacked{
  box-sizing:border-box !important;
  overflow:hidden !important;
}
.footerLogoRow{
  display:grid !important;
  grid-template-columns:minmax(0,285px) minmax(0,175px) minmax(0,330px) !important;
  justify-content:center !important;
  align-items:center !important;
  gap:clamp(36px,5vw,90px) !important;
  width:100% !important;
  max-width:100% !important;
  box-sizing:border-box !important;
  padding:4px 18px !important;
}
.footerLogoRow > *{
  min-width:0 !important;
  max-width:100% !important;
}
.footerLogoRow .footerWms{
  display:flex !important;
  justify-content:center !important;
  align-items:center !important;
}
.footerLogoRow .footerWms .wmsLogoLink{
  width:285px !important;
  max-width:100% !important;
}
.footerLogoRow .stmFooterLink{
  display:flex !important;
  justify-content:center !important;
  align-items:center !important;
  width:100% !important;
}
.footerLogoRow .stmFooterLogo{
  width:155px !important;
  max-width:100% !important;
  height:auto !important;
}
.footerLogoRow .metro60Logo{
  width:330px !important;
  max-width:100% !important;
  height:auto !important;
  margin:0 !important;
  justify-self:center !important;
}

/* Scale the three marks together before they can escape the card. */
@media(max-width:1150px){
  .footerLogoRow{
    grid-template-columns:minmax(0,235px) minmax(0,140px) minmax(0,275px) !important;
    gap:clamp(24px,3vw,48px) !important;
  }
  .footerLogoRow .footerWms .wmsLogoLink{ width:235px !important; }
  .footerLogoRow .stmFooterLogo{ width:130px !important; }
  .footerLogoRow .metro60Logo{ width:275px !important; }
}
@media(max-width:820px){
  .footerLogoRow{
    grid-template-columns:1fr !important;
    justify-items:center !important;
    gap:18px !important;
  }
  .footerLogoRow .footerWms .wmsLogoLink{ width:250px !important; }
  .footerLogoRow .stmFooterLogo{ width:145px !important; }
  .footerLogoRow .metro60Logo{
    width:290px !important;
    max-width:82vw !important;
  }
}

@font-face{
  font-family:"Univers 57 Condensed";
  src:url("fonts/Univers57Condensed-Regular.otf") format("opentype");
  font-style:normal;
  font-weight:400;
  font-display:swap;
}

/* Univers 57 Condensed is the body/text face.
   Fira Sans Condensed remains the display/header face. */
body,
input,
.infoPanel p,
.voiceDetail,
.activityPanel .subtitle,
.dataCredit,
.footerCreditBottom,
#status,
#dateDisplay,
.volumeLabel,
.volumeValue{
  font-family:"Univers 57 Condensed",sans-serif !important;
  font-weight:400;
}

/* Preserve Fira Sans Condensed for headings and interface emphasis. */
.title,
.panelHeading,
.activityPanel h2,
.timePanel h2,
.infoPanel h2,
.eyebrow,
.clockPhase,
.densityWord,
.voice .name,
button,
select,
label{
  font-family:"Fira Sans Condensed",sans-serif !important;
}

/* V17 typography assignments */

/* CORRESPONDANCE uses Univers 57 Condensed. */
.title{
  font-family:"Univers 57 Condensed",sans-serif !important;
  font-weight:400 !important;
  text-transform:uppercase !important;
}

/* The large transit clock stays Fira Sans Condensed. */
#timeDisplay{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-weight:700 !important;
}

/* ============================================================
   V19 — 60 ANS / MÉTRO-MOMENTS INSPIRED OPENING TITLE
   ============================================================ */
#loadingOverlay{
  overflow:hidden !important;
  isolation:isolate;
  background:
    radial-gradient(circle at 50% 46%,rgba(28,35,58,.42),transparent 42%),
    #07090d !important;
}
.correspondanceLoader{
  position:relative;
  z-index:3;
  width:min(900px,88vw) !important;
  padding:48px 30px 40px !important;
  background:transparent !important;
  border:0 !important;
  box-shadow:none !important;
  backdrop-filter:none !important;
}
.loaderEyebrow{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-size:clamp(.68rem,1.2vw,.9rem) !important;
  letter-spacing:.16em !important;
  margin-bottom:18px !important;
  color:rgba(248,249,255,.72) !important;
}
.correspondanceLoader .loaderTitle{
  font-family:"Univers 57 Condensed",sans-serif !important;
  font-weight:400 !important;
  font-size:clamp(4.4rem,11.5vw,9.8rem) !important;
  line-height:.78 !important;
  letter-spacing:.005em !important;
  text-transform:uppercase !important;
  margin:0 auto !important;
  text-shadow:0 12px 44px rgba(0,0,0,.36) !important;
}
.loaderIntro{
  font-family:"Univers 57 Condensed",sans-serif !important;
  font-size:clamp(1rem,1.6vw,1.18rem) !important;
  max-width:560px !important;
  margin:28px auto 30px !important;
}
.loaderRule{display:none !important;}

.correspondanceLoader .loaderStatus{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-size:.82rem !important;
  letter-spacing:.06em !important;
  margin-bottom:12px !important;
}
.correspondanceLoader .spinner{
  width:30px !important;height:30px !important;
  margin:14px auto 22px !important;
}
#enterButton{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-weight:700 !important;
  min-width:245px !important;
  border-radius:9px !important;
}
.loaderBrandRow{
  position:relative;
  z-index:4;
  margin-top:30px !important;
}

/* Scattered STM-colour field — deliberately asymmetric like the anniversary identity. */
.metroMomentDots{
  position:absolute;
  inset:0;
  z-index:1;
  pointer-events:none;
}
.metroMomentDots .dot{
  position:absolute;
  display:block;
  border-radius:50%;
  opacity:.96;
  animation:metroDrift 7s ease-in-out infinite alternate,
            metroPulse 4.8s ease-in-out infinite;
  will-change:transform,opacity;
}
.metroMomentDots .d1{width:12px;height:12px;left:5%;top:14%;background:#00A651;animation-delay:-.4s}
.metroMomentDots .d2{width:34px;height:34px;left:10%;top:25%;background:#0072BC;animation-delay:-2.2s}
.metroMomentDots .d3{width:17px;height:17px;left:18%;top:11%;background:#F58220;animation-delay:-1.1s}
.metroMomentDots .d4{width:8px;height:8px;left:25%;top:21%;background:#FFD200;animation-delay:-3.2s}
.metroMomentDots .d5{width:23px;height:23px;left:31%;top:8%;background:#00A651;animation-delay:-.8s}
.metroMomentDots .d6{width:10px;height:10px;left:38%;top:27%;background:#00A7CE;animation-delay:-2.8s}
.metroMomentDots .d7{width:15px;height:15px;left:44%;top:13%;background:#FFD200;animation-delay:-1.7s}
.metroMomentDots .d8{width:9px;height:9px;left:56%;top:19%;background:#F58220;animation-delay:-3.8s}
.metroMomentDots .d9{width:29px;height:29px;right:12%;top:12%;background:#00A651;animation-delay:-1.4s}
.metroMomentDots .d10{width:12px;height:12px;right:5%;top:28%;background:#0072BC;animation-delay:-2.5s}
.metroMomentDots .d11{width:7px;height:7px;left:7%;bottom:18%;background:#FFD200;animation-delay:-4.1s}
.metroMomentDots .d12{width:19px;height:19px;left:17%;bottom:10%;background:#FFD200;animation-delay:-1.9s}
.metroMomentDots .d13{width:11px;height:11px;left:29%;bottom:20%;background:#00A651;animation-delay:-3.4s}
.metroMomentDots .d14{width:25px;height:25px;right:28%;bottom:11%;background:#0072BC;animation-delay:-.9s}
.metroMomentDots .d15{width:13px;height:13px;right:17%;bottom:22%;background:#F58220;animation-delay:-2.9s}
.metroMomentDots .d16{width:8px;height:8px;right:7%;bottom:13%;background:#00A7CE;animation-delay:-1.3s}
.metroMomentDots .d17{width:6px;height:6px;left:48%;bottom:7%;background:#FFD200;animation-delay:-3.7s}
.metroMomentDots .d18{width:7px;height:7px;right:42%;bottom:27%;background:#00A651;animation-delay:-2.1s}

@keyframes metroDrift{
  from{transform:translate3d(-2px,-4px,0)}
  to{transform:translate3d(5px,6px,0)}
}
@keyframes metroPulse{
  0%,100%{opacity:.55}
  50%{opacity:1}
}

/* ENTER becomes the visual transition into the work. */
#loadingOverlay.entering .metroMomentDots .dot{
  animation:metroScatter .9s cubic-bezier(.2,.7,.2,1) forwards !important;
}
#loadingOverlay.entering .correspondanceLoader{
  animation:loaderRelease .9s ease forwards;
}
@keyframes metroScatter{
  to{transform:scale(1.65) translateY(-18px);opacity:0}
}
@keyframes loaderRelease{
  to{transform:scale(.985);opacity:0}
}

@media(max-width:650px){
  .correspondanceLoader{width:min(94vw,620px)!important;padding:32px 18px!important}
  .correspondanceLoader .loaderTitle{font-size:clamp(3.5rem,17vw,6rem)!important}
  .loaderIntro{max-width:82vw!important}
  .metroMomentDots .d2,.metroMomentDots .d9,.metroMomentDots .d14{transform:scale(.72)}
}
@media(prefers-reduced-motion:reduce){
  .metroMomentDots .dot{animation:none!important}
  #loadingOverlay.entering .metroMomentDots .dot,
  #loadingOverlay.entering .correspondanceLoader{animation:none!important}
}

/* ============================================================
   V20 — centered opening title + two-stage ENTER transition
   ============================================================ */

/* Make the title's visual center independent of the loader card width. */
.correspondanceLoader .loaderTitle{
  position:relative !important;
  left:50% !important;
  width:max-content !important;
  max-width:none !important;
  transform:translateX(-50%) !important;
  text-align:center !important;
  white-space:nowrap !important;
}

/* Stage 1 (0–1s): text/buttons/logos fade to black, dots remain visible. */
#loadingOverlay.entering .loaderEyebrow,
#loadingOverlay.entering .loaderTitle,
#loadingOverlay.entering .loaderIntro,
#loadingOverlay.entering .loaderStatus,
#loadingOverlay.entering .spinner,
#loadingOverlay.entering #enterButton,
#loadingOverlay.entering .loaderBrandRow{
  animation:v20TextToBlack 1s ease forwards !important;
}

/* Override v19's immediate loader/dot exit. The card itself stays present
   while its contents fade; the dots get a full extra second on screen. */
#loadingOverlay.entering .correspondanceLoader{
  animation:none !important;
}
#loadingOverlay.entering .metroMomentDots .dot{
  animation:metroDrift 7s ease-in-out infinite alternate,
            metroPulse 4.8s ease-in-out infinite !important;
}

/* Stage 2 (1–2s): reveal the main interface while the coloured dots
   scatter/fade over the transition. */
#loadingOverlay.entering{
  animation:v20OverlayReveal 1s ease 1s forwards !important;
}
#loadingOverlay.entering .metroMomentDots .dot{
  animation:
    metroDrift 7s ease-in-out infinite alternate,
    metroPulse 4.8s ease-in-out infinite,
    v20DotExit 1s cubic-bezier(.2,.7,.2,1) 1s forwards !important;
}

@keyframes v20TextToBlack{
  from{opacity:1;filter:brightness(1)}
  to{opacity:0;filter:brightness(0)}
}
@keyframes v20OverlayReveal{
  from{opacity:1}
  to{opacity:0;visibility:hidden;pointer-events:none}
}
@keyframes v20DotExit{
  0%{opacity:.96}
  55%{opacity:1}
  100%{opacity:0;transform:scale(1.35) translateY(-10px)}
}

/* The old .hidden class must not cut off the requested 2-second transition. */
#loadingOverlay.entering.hidden{
  visibility:visible !important;
  pointer-events:none !important;
  animation:v20OverlayReveal 1s ease 1s forwards !important;
}

@media(max-width:900px){
  .correspondanceLoader .loaderTitle{
    width:auto !important;
    max-width:94vw !important;
    white-space:normal !important;
  }
}

/* V21 — main masthead refinement */
.heroTitleRow{
  display:block !important;
  width:100% !important;
}
.heroTitleTop{
  display:flex;
  align-items:flex-start;
  justify-content:space-between;
  gap:24px;
  width:100%;
}
.heroTitleRow h1{
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-weight:500 !important;
  letter-spacing:-0.025em !important;
  line-height:.82 !important;
  margin:0 !important;
}
.creatorByline{
  font-family:"Univers 57 Condensed",sans-serif;
  font-size:1.08rem;
  letter-spacing:.01em;
  color:rgba(248,249,255,.82);
  white-space:nowrap;
  padding-top:18px;
  text-align:right;
}

/* Five STM colours now span the full content width beneath the masthead. */
.titleRouteRule{
  display:grid !important;
  grid-template-columns:1.15fr 1fr .75fr 1fr 1.15fr !important;
  gap:7px !important;
  width:100% !important;
  max-width:none !important;
  height:6px !important;
  margin-top:22px !important;
}
.titleRouteRule span{width:100% !important;}

/* Opening title keeps Univers. */
.correspondanceLoader .loaderTitle{
  font-family:"Univers 57 Condensed",sans-serif !important;
  letter-spacing:0 !important;
}

@media(max-width:650px){
  .heroTitleTop{align-items:flex-end;gap:12px}
  .creatorByline{font-size:.9rem;padding-top:0;padding-bottom:4px}
  .titleRouteRule{height:5px!important;margin-top:14px!important;gap:4px!important}
}

/* ============================================================
   V22 — livelier loading dots + clock typography refinements
   ============================================================ */

/* Slightly more pronounced idle movement/pulse. */
.metroMomentDots .dot{
  animation:
    metroDriftV22 5.6s ease-in-out infinite alternate,
    metroPulseV22 3.8s ease-in-out infinite !important;
  transition:transform .16s ease, opacity .16s ease, filter .16s ease;
  pointer-events:auto;
  cursor:default;
}
@keyframes metroDriftV22{
  0%   {transform:translate3d(-5px,-7px,0) scale(.96)}
  45%  {transform:translate3d(3px,4px,0) scale(1.02)}
  100% {transform:translate3d(9px,10px,0) scale(1.06)}
}
@keyframes metroPulseV22{
  0%,100%{opacity:.48;filter:brightness(.9)}
  50%{opacity:1;filter:brightness(1.12)}
}

/* Hover: wiggle, then remain visually at the top of the pulse.
   The small alternating rotation/translation gives it a tactile wobble
   without turning the loader into a game. */
.metroMomentDots .dot:hover{
  opacity:1 !important;
  filter:brightness(1.18) !important;
  animation:metroDotWiggle .48s ease-in-out 1 forwards !important;
}
@keyframes metroDotWiggle{
  0%  {transform:scale(1.06) rotate(0deg) translate(0,0)}
  20% {transform:scale(1.18) rotate(-7deg) translate(-2px,1px)}
  40% {transform:scale(1.24) rotate(7deg) translate(2px,-1px)}
  60% {transform:scale(1.20) rotate(-4deg) translate(-1px,1px)}
  80% {transform:scale(1.24) rotate(3deg) translate(1px,0)}
  100%{transform:scale(1.24) rotate(0deg) translate(0,0)}
}

/* Preserve V20's ENTER sequence: dots stay lively during stage one. */
#loadingOverlay.entering .metroMomentDots .dot{
  pointer-events:none !important;
}

/* Give the large clock numerals a little more breathing room. */
#timeDisplay{
  font-family:"Fira Sans Condensed",sans-serif !important;
  letter-spacing:.035em !important;
  font-variant-numeric:tabular-nums;
}

/* V23 — artist-statement references and signature */
.infoPanel p a{
  color:inherit;
  text-decoration:underline;
  text-decoration-thickness:1px;
  text-underline-offset:.14em;
}
.infoPanel p a:hover{
  color:#fff;
}
.infoPanel .artistSignature{
  margin-top:14px !important;
  margin-bottom:0 !important;
  text-align:right !important;
  width:100% !important;
  font-style:normal;
  white-space:nowrap;
}

/* ============================================================
   V24 — larger dot interaction zones + livelier idle motion
   ============================================================ */

/* The visible dot is unchanged in size, but each dot gets a generous,
   stationary-feeling interaction halo via an invisible pseudo-element.
   This prevents hover from dropping when the visible circle drifts
   a few pixels away from the cursor. */
.metroMomentDots .dot{
  pointer-events:auto !important;
  overflow:visible !important;
  animation:
    metroDriftV24 5.2s ease-in-out infinite alternate,
    metroPulseV24 3.6s ease-in-out infinite !important;
}
.metroMomentDots .dot::before{
  content:"";
  position:absolute;
  left:50%;
  top:50%;
  width:max(46px, calc(100% + 30px));
  height:max(46px, calc(100% + 30px));
  transform:translate(-50%,-50%);
  border-radius:50%;
  background:transparent;
  pointer-events:auto;
}

/* A little more travel than v22, still slow/ambient rather than bouncy. */
@keyframes metroDriftV24{
  0%   {transform:translate3d(-9px,-11px,0) scale(.94)}
  28%  {transform:translate3d(1px,-3px,0) scale(.99)}
  58%  {transform:translate3d(7px,7px,0) scale(1.04)}
  100% {transform:translate3d(13px,12px,0) scale(1.08)}
}
@keyframes metroPulseV24{
  0%,100%{opacity:.44;filter:brightness(.88)}
  50%{opacity:1;filter:brightness(1.14)}
}

/* Hover remains locked at peak brightness/scale after the wiggle.
   The enlarged pseudo-element keeps :hover active despite the drift. */
.metroMomentDots .dot:hover{
  opacity:1 !important;
  filter:brightness(1.2) !important;
  animation:metroDotWiggleV24 .52s ease-in-out 1 forwards !important;
}
@keyframes metroDotWiggleV24{
  0%  {transform:scale(1.08) rotate(0deg)}
  18% {transform:scale(1.19) rotate(-8deg) translate(-2px,1px)}
  38% {transform:scale(1.25) rotate(8deg) translate(2px,-1px)}
  58% {transform:scale(1.22) rotate(-5deg) translate(-1px,1px)}
  78% {transform:scale(1.25) rotate(3deg) translate(1px,0)}
  100%{transform:scale(1.25) rotate(0deg)}
}

/* During ENTER, interaction is disabled exactly as before. */
#loadingOverlay.entering .metroMomentDots .dot,
#loadingOverlay.entering .metroMomentDots .dot::before{
  pointer-events:none !important;
}

/* ============================================================
   V25 — cursor-swept loading dots
   ============================================================ */

/* Remove the previous hover wiggle/grow interaction entirely. */
.metroMomentDots .dot:hover{
  opacity:inherit !important;
  filter:inherit !important;
  animation:
    metroDriftV24 5.2s ease-in-out infinite alternate,
    metroPulseV24 3.6s ease-in-out infinite !important;
}

/* The invisible interaction halo stays useful for detecting a nearby sweep,
   but it no longer triggers a visual hover state. */
.metroMomentDots .dot::before{
  pointer-events:none !important;
}

/* V26 — each loading circle has its own idle drift */
.metroMomentDots .dot{
  /* JS owns translation now; CSS keeps only the breathing/pulse. */
  animation:metroPulseV24 3.6s ease-in-out infinite !important;
  transform:none !important;
  will-change:translate,opacity,filter;
}

/* ============================================================
   V27 — larger particles, wider sweep field, longer idle drift
   ============================================================ */

/* Slightly enlarge every visible circle without changing its authored layout. */
.metroMomentDots .dot{
  scale:1.18;
}

/* Much larger invisible interaction area around every circle. */
.metroMomentDots .dot::before{
  width:max(110px, calc(100% + 90px)) !important;
  height:max(110px, calc(100% + 90px)) !important;
}

/* V28 — softly reveal ENTER once the transit state is ready */
#enterButton{
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:
    opacity .9s ease,
    background .2s ease,
    transform .2s ease,
    border-color .2s ease !important;
}
#enterButton.readyToEnter{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
}

/* ============================================================
   V29 — larger, denser opening particle field
   ============================================================ */

/* 100% larger than v28/v27: double the current visible circle scale. */
.metroMomentDots .dot{
  scale:2.36 !important;
}

/* Four additional circles, distributed into open areas of the composition. */
.metroMomentDots .d19{
  width:9px;height:9px;left:14%;top:48%;background:#00A7CE;
}
.metroMomentDots .d20{
  width:12px;height:12px;right:22%;top:38%;background:#FFD200;
}
.metroMomentDots .d21{
  width:8px;height:8px;left:39%;bottom:31%;background:#F58220;
}
.metroMomentDots .d22{
  width:11px;height:11px;right:8%;bottom:39%;background:#00A651;
}

/* Increase the sweep area again so the larger particles remain easy to push. */
.metroMomentDots .dot::before{
  width:max(135px, calc(100% + 110px)) !important;
  height:max(135px, calc(100% + 110px)) !important;
}

/* ============================================================
   V30 — responsive loading title
   ============================================================ */

/* Keep the opening title on one line, but size it from the available
   viewport width so CORRESPONDANCE never clips when the browser narrows. */
.correspondanceLoader .loaderTitle{
  width:100% !important;
  max-width:100% !important;
  left:auto !important;
  transform:none !important;
  white-space:nowrap !important;
  text-align:center !important;
  font-size:clamp(2.65rem, 10.2vw, 9.8rem) !important;
  letter-spacing:-.01em !important;
}

/* Override the older mobile rule that allowed the word to outgrow/wrap. */
@media(max-width:900px){
  .correspondanceLoader .loaderTitle{
    width:100% !important;
    max-width:100% !important;
    white-space:nowrap !important;
    font-size:clamp(2.65rem, 10.2vw, 7rem) !important;
  }
}
@media(max-width:560px){
  .correspondanceLoader{
    width:96vw !important;
    padding-left:10px !important;
    padding-right:10px !important;
  }
  .correspondanceLoader .loaderTitle{
    font-size:10vw !important;
  }
}

/* V31 — reserve ENTER button space before it becomes ready.
   This prevents the status text above from shifting when the button appears. */
#enterButton{
  display:inline-block !important;
  margin-top:10px !important;
}

/* ============================================================
   V32 — loading title centered to viewport at every width
   ============================================================ */

/* The title is centered against the viewport itself rather than the
   loader card. This fixes the wide-desktop offset while preserving
   the responsive sizing that fixed narrow/windowed layouts. */
.correspondanceLoader .loaderTitle{
  position:relative !important;
  left:50% !important;
  width:100vw !important;
  max-width:100vw !important;
  margin-left:-50vw !important;
  transform:translateX(50%) !important;
  box-sizing:border-box !important;
  padding-left:3vw !important;
  padding-right:3vw !important;
  text-align:center !important;
  white-space:nowrap !important;
  font-size:clamp(2.65rem, 10.2vw, 9.8rem) !important;
}

/* On narrower windows use the same viewport-centering method rather
   than reverting to the old card-relative mobile rule. */
@media(max-width:900px){
  .correspondanceLoader .loaderTitle{
    width:100vw !important;
    max-width:100vw !important;
    margin-left:-50vw !important;
    left:50% !important;
    transform:translateX(50%) !important;
    padding-left:3vw !important;
    padding-right:3vw !important;
    font-size:clamp(2.65rem,10.2vw,7rem) !important;
  }
}
@media(max-width:560px){
  .correspondanceLoader .loaderTitle{
    font-size:10vw !important;
  }
}

/* ============================================================
   V33 — zero-reflow loader status/control stage
   ============================================================ */

/* This whole region occupies the same amount of space from first paint
   through readiness, so neither the copy above nor the logos below move. */
.loaderActionSlot{
  height:116px;
  margin-top:26px;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:flex-start;
}
.correspondanceLoader .loaderStatus{
  box-sizing:border-box;
  height:22px;
  line-height:22px;
  margin:0 0 14px !important;
  flex:0 0 22px;
}
.loaderControlStage{
  position:relative;
  width:100%;
  height:58px;
  flex:0 0 58px;
  display:grid;
  place-items:center;
}

/* Spinner and ENTER occupy the exact same reserved stage.
   Readiness changes opacity/visibility only — never display/layout. */
.loaderControlStage #loaderSpinner,
.loaderControlStage #enterButton{
  position:absolute !important;
  left:50% !important;
  top:50% !important;
  margin:0 !important;
}
.loaderControlStage #loaderSpinner{
  display:block !important;
  transform:translate(-50%,-50%);
  opacity:1;
  visibility:visible;
  transition:opacity .45s ease, visibility .45s ease;
}
.loaderControlStage #loaderSpinner.loaderFinished{
  opacity:0;
  visibility:hidden;
}
.loaderControlStage #enterButton{
  display:inline-block !important;
  transform:translate(-50%,-50%) !important;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity .9s ease, background .2s ease, border-color .2s ease !important;
}
.loaderControlStage #enterButton.readyToEnter{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
}

/* Remove old spacing assumptions now handled by the fixed slot. */
#enterButton{margin-top:0 !important;}

/* ============================================================
   V34 — definitive loading-title centering fix
   ============================================================ */

/* V32 accidentally combined margin-left:-50vw with translateX(+50%),
   which pushes the viewport-wide title to the right on large screens.
   The loader card itself is centered, so use its midpoint as the anchor:
   midpoint + half-viewport translation back = true viewport center. */
.correspondanceLoader .loaderTitle{
  position:relative !important;
  left:50% !important;
  width:100vw !important;
  max-width:100vw !important;
  margin-left:0 !important;
  margin-right:0 !important;
  transform:translateX(-50%) !important;
  box-sizing:border-box !important;
  padding-left:3vw !important;
  padding-right:3vw !important;
  text-align:center !important;
  white-space:nowrap !important;
  font-size:clamp(2.65rem,10.2vw,9.8rem) !important;
}

@media(max-width:900px){
  .correspondanceLoader .loaderTitle{
    left:50% !important;
    width:100vw !important;
    max-width:100vw !important;
    margin-left:0 !important;
    transform:translateX(-50%) !important;
    font-size:clamp(2.65rem,10.2vw,7rem) !important;
  }
}
@media(max-width:560px){
  .correspondanceLoader .loaderTitle{
    font-size:10vw !important;
  }
}

/* ============================================================
   V36 — hard reset of spinner + capture-phase particle sweeps
   ============================================================ */

/* The spinner element is now only a fixed centered box. */
.loaderControlStage #loaderSpinner{
  position:absolute !important;
  inset:0 !important;
  width:34px !important;
  height:34px !important;
  margin:auto !important;
  border:0 !important;
  border-radius:50% !important;
  animation:none !important;
  transform:none !important;
  translate:none !important;
  box-sizing:border-box !important;
}
/* The ring itself rotates inside that stationary box. */
.loaderControlStage #loaderSpinner::after{
  content:"";
  position:absolute;
  inset:0;
  box-sizing:border-box;
  border:2px solid rgba(255,255,255,.16);
  border-top-color:#00A7CE;
  border-radius:50%;
  transform-origin:50% 50%;
  animation:v36SpinnerRotate 1s linear infinite;
}
@keyframes v36SpinnerRotate{
  from{transform:rotate(0deg)}
  to{transform:rotate(360deg)}
}

/* Particle artwork remains visually behind all loader content. */
.metroMomentDots{z-index:1 !important;pointer-events:none !important}
.correspondanceLoader{z-index:3 !important}

/* ============================================================
   V37 — responsive service rows + responsive main masthead
   ============================================================ */

/* Main masthead: title must shrink to coexist with the byline. */
.heroTitleTop{
  display:flex !important;
  align-items:flex-start !important;
  justify-content:space-between !important;
  gap:24px !important;
  width:100% !important;
  min-width:0 !important;
}
.heroTitleTop h1{
  min-width:0 !important;
  max-width:100% !important;
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-size:clamp(3rem,7.6vw,7.6rem) !important;
  line-height:.82 !important;
  letter-spacing:-.025em !important;
  white-space:nowrap !important;
}
.creatorByline{
  flex:0 0 auto !important;
}

/* Normal service row order:
   line name → slider → percentage → activity label → M/S */
.activityPanel .voice{
  display:grid !important;
  grid-template-columns:82px minmax(90px,1fr) 62px minmax(120px,1fr) auto !important;
  column-gap:12px !important;
  row-gap:8px !important;
  align-items:center !important;
}
.activityPanel .voice > .name{grid-column:1 !important;grid-row:1 !important;}
.activityPanel .voice > .meter{grid-column:2 !important;grid-row:1 !important;}
.activityPanel .voice > .value{grid-column:3 !important;grid-row:1 !important;}
.activityPanel .voice > .densityWord{grid-column:4 !important;grid-row:1 !important;}
.activityPanel .voice > .layerControls{
  grid-column:5 !important;
  grid-row:1 !important;
  justify-self:end !important;
  white-space:nowrap !important;
}
.activityPanel .voice > .voiceDetail{
  grid-column:1/-1 !important;
  grid-row:2 !important;
}

/* When the row can no longer fit, everything AFTER the slider moves
   above it. M/S stays at the top-right. Name + meter remain below. */
@media(max-width:650px){
  .heroTitleTop{
    display:block !important;
  }
  .heroTitleTop h1{
    width:100% !important;
    font-size:clamp(2.6rem,13.2vw,5.4rem) !important;
    white-space:nowrap !important;
  }
  .creatorByline{
    display:block !important;
    width:100% !important;
    margin-top:10px !important;
    padding:0 !important;
    text-align:right !important;
    font-size:.95rem !important;
  }

  .activityPanel .voice{
    grid-template-columns:82px minmax(0,1fr) auto auto !important;
    grid-template-rows:auto auto auto !important;
    column-gap:10px !important;
    row-gap:8px !important;
    padding:8px 0 10px !important;
  }

  /* top row: percentage + activity + M/S */
  .activityPanel .voice > .value{
    grid-column:2 !important;
    grid-row:1 !important;
    justify-self:start !important;
    text-align:left !important;
  }
  .activityPanel .voice > .densityWord{
    grid-column:3 !important;
    grid-row:1 !important;
    justify-self:end !important;
    text-align:right !important;
    white-space:nowrap !important;
  }
  .activityPanel .voice > .layerControls{
    grid-column:4 !important;
    grid-row:1 !important;
    justify-self:end !important;
  }

  /* second row: line name + slider */
  .activityPanel .voice > .name{
    grid-column:1 !important;
    grid-row:2 !important;
  }
  .activityPanel .voice > .meter{
    grid-column:2/5 !important;
    grid-row:2 !important;
    width:100% !important;
  }

  .activityPanel .voice > .voiceDetail{
    grid-column:1/5 !important;
    grid-row:3 !important;
    margin-top:0 !important;
  }
}

/* Extra-narrow: keep M/S at upper-right and allow the activity label
   to wrap rather than forcing horizontal overflow. */
@media(max-width:430px){
  .activityPanel .voice{
    grid-template-columns:72px minmax(0,1fr) auto !important;
  }
  .activityPanel .voice > .value{
    grid-column:1 !important;
    grid-row:1 !important;
  }
  .activityPanel .voice > .densityWord{
    grid-column:2 !important;
    grid-row:1 !important;
    white-space:normal !important;
  }
  .activityPanel .voice > .layerControls{
    grid-column:3 !important;
    grid-row:1 !important;
  }
  .activityPanel .voice > .name{
    grid-column:1 !important;
    grid-row:2 !important;
  }
  .activityPanel .voice > .meter{
    grid-column:2/4 !important;
    grid-row:2 !important;
  }
  .activityPanel .voice > .voiceDetail{
    grid-column:1/4 !important;
    grid-row:3 !important;
  }
}

/* ============================================================
   V42 — localization presentation layer (v37 runtime untouched)
   ============================================================ */
.languageSelector{
  display:flex;align-items:center;gap:6px;
  font-family:"Fira Sans Condensed",sans-serif;
  font-weight:700;font-size:.78rem;letter-spacing:.08em;
  color:var(--ui-text,#f8f9ff);
}
.languageSelector button{
  appearance:none;border:0;background:none;color:inherit;
  font:inherit;letter-spacing:inherit;cursor:pointer;
  padding:5px 3px;opacity:.46;
}
.languageSelector button[aria-pressed="true"]{
  opacity:1;text-decoration:underline;text-underline-offset:4px;
}
.languageSelector button:hover{opacity:1}
.loaderLanguage{position:absolute;top:22px;right:28px;z-index:30}
.hero{position:relative !important}
.mainLanguage{position:absolute;top:10px;right:0;z-index:10}

/* Masthead repair is CSS-only: byline never competes horizontally with title. */
.heroTitleTop{
  display:grid !important;
  grid-template-columns:minmax(0,1fr) !important;
  gap:5px !important;
  width:100% !important;
  min-width:0 !important;
}
.heroTitleTop h1{
  grid-row:1 !important;
  width:100% !important;max-width:100% !important;min-width:0 !important;
  margin:0 !important;padding:0 !important;
  font-family:"Fira Sans Condensed",sans-serif !important;
  font-size:clamp(2.15rem,7.05vw,7.15rem) !important;
  line-height:.86 !important;letter-spacing:-.035em !important;
  white-space:nowrap !important;text-align:left !important;
}
.creatorByline{
  position:static !important;grid-row:2 !important;
  width:100% !important;margin:4px 0 0 !important;padding:0 !important;
  text-align:right !important;justify-self:stretch !important;
  white-space:nowrap !important;
}
.titleRouteRule{margin-top:12px !important}

@media(max-width:700px){
  .loaderLanguage{top:14px;right:14px}
  .mainLanguage{top:4px;right:0}
  .heroTitleTop h1{
    font-size:clamp(1.75rem,9.8vw,4.6rem) !important;
    letter-spacing:-.045em !important;
  }
  .creatorByline{margin-top:8px !important;font-size:.88rem !important}
}

/* V43 language crossfade + stable loader copy */
.loaderIntro,
.panelHeading,
.activityHeader p,
.infoPanel p,
.artistSignature,
#dateDisplay,
#dayPhase,
#status,
#loaderStatus,
.voice .name,
.densityWord,
.voice .detail,
#rightNow,
#play,
#stop,
#enterButton,
#dateInput,
.languageSelector{
  transition:opacity 180ms ease;
}
html.uiLangFading .loaderIntro,
html.uiLangFading .panelHeading,
html.uiLangFading .activityHeader p,
html.uiLangFading .infoPanel p,
html.uiLangFading .artistSignature,
html.uiLangFading #dateDisplay,
html.uiLangFading #dayPhase,
html.uiLangFading #status,
html.uiLangFading #loaderStatus,
html.uiLangFading .voice .name,
html.uiLangFading .densityWord,
html.uiLangFading .voice .detail,
html.uiLangFading #rightNow,
html.uiLangFading #play,
html.uiLangFading #stop,
html.uiLangFading #enterButton,
html.uiLangFading #dateInput{
  opacity:0;
}
@media (min-width:901px){
  .correspondanceLoader .loaderIntro{
    white-space:nowrap;
    width:max-content;
    max-width:none;
    justify-self:center;
    margin-left:auto;
    margin-right:auto;
  }
}

/* V44 refinements */
@media (min-width:901px){
  .correspondanceLoader .loaderIntro{
    position:relative !important;
    left:50% !important;
    transform:translateX(-50%) !important;
    width:max-content !important;
    max-width:none !important;
    margin-left:0 !important;
    margin-right:0 !important;
    text-align:center !important;
    white-space:nowrap !important;
  }
}

/* Include every visible loading-screen text/control in language crossfade. */
.loaderEyebrow,
.loaderIntro,
#loaderStatus,
#enterButton{
  transition:opacity 180ms ease !important;
}
html.uiLangFading .loaderEyebrow,
html.uiLangFading .loaderIntro,
html.uiLangFading #loaderStatus,
html.uiLangFading #enterButton{
  opacity:0 !important;
  animation:none !important;
}

/* V45 refinements */
.creatorByline{text-align:left !important;justify-self:start !important}
.voiceDetail,.voice .detail,[id$="Detail"]{transition:opacity 180ms ease !important}
html.uiLangFading .voiceDetail,
html.uiLangFading .voice .detail,
html.uiLangFading [id$="Detail"]{opacity:0 !important}

/* V46 — authoritative loading-screen centering.
   The subtitle is centered by its parent/card, with no translate/left offset. */
.correspondanceLoader{
  text-align:center !important;
}
.correspondanceLoader .loaderEyebrow,
.correspondanceLoader .loaderTitle,
.correspondanceLoader .loaderIntro,
.correspondanceLoader .loaderStatus,
.correspondanceLoader #enterButton{
  left:auto !important;
  right:auto !important;
  transform:none !important;
  margin-left:auto !important;
  margin-right:auto !important;
  text-align:center !important;
  justify-self:center !important;
}
.correspondanceLoader .loaderIntro{
  display:block !important;
  width:100% !important;
  max-width:none !important;
}
@media (min-width:901px){
  .correspondanceLoader .loaderIntro{
    white-space:nowrap !important;
  }
}
@media (max-width:900px){
  .correspondanceLoader .loaderIntro{
    white-space:normal !important;
  }
}

/* V47 — loading title centered independently of the loader card width */
.correspondanceLoader .loaderTitle{
  position:relative !important;
  left:50% !important;
  right:auto !important;
  transform:translateX(-50%) !important;
  width:max-content !important;
  max-width:96vw !important;
  margin-left:0 !important;
  margin-right:0 !important;
  text-align:center !important;
  justify-self:auto !important;
  white-space:nowrap !important;
}
@media(max-width:700px){
  .correspondanceLoader .loaderTitle{
    font-size:clamp(2.35rem,10.6vw,4.8rem) !important;
    max-width:94vw !important;
  }
}

/* V49 — full-width master volume control */
.volumeControl{
  display:grid !important;
  grid-template-columns:auto minmax(0,1fr) auto !important;
  align-items:center !important;
  column-gap:10px !important;
  flex:1 1 100% !important;
  width:100% !important;
  min-width:0 !important;
}
.volumeControl #volumeSlider{
  width:100% !important;
  min-width:0 !important;
  max-width:none !important;
  margin:0 !important;
}
.volumeControl #volumeValue{
  min-width:3.4em !important;
  text-align:right !important;
}
@media(max-width:700px){
  .volumeControl{grid-template-columns:auto minmax(0,1fr) auto !important}
}

/* V50 — slower, complete language crossfade */
.loaderEyebrow,
.loaderIntro,
#loaderStatus,
#enterButton,
.languageSelector{
  transition:opacity 450ms ease !important;
}
/* Main interface also crossfades smoothly, but slightly faster than loader copy. */
.panelHeading,
.activityHeader p,
.infoPanel p,
.artistSignature,
#dateDisplay,
#dayPhase,
#status,
.voice .name,
.densityWord,
.voice .detail,
.voiceDetail,
[id$="Detail"],
#rightNow,
#play,
#stop,
#dateInput{
  transition:opacity 360ms ease !important;
}
html.uiLangFading #enterButton{
  opacity:0 !important;
  animation:none !important;
}

/* V51 — slightly smaller default transit clock */
#timeDisplay{
  font-size:clamp(3.65rem,6.5vw,5.65rem) !important;
}
@media(max-width:700px){
  #timeDisplay{
    font-size:clamp(3.1rem,14vw,4.7rem) !important;
  }
}

/* V52 — clock participates in language-format crossfade */
#timeDisplay{
  transition:opacity 360ms ease !important;
}
html.uiLangFading #timeDisplay{
  opacity:0 !important;
}

/* V54 — one-line responsive transit clock */
#timeDisplay{
  display:block !important;
  width:100% !important;
  max-width:100% !important;
  min-width:0 !important;
  white-space:nowrap !important;
  overflow:visible !important;
  text-align:center !important;
  line-height:1 !important;
  font-size:clamp(2.35rem,4.35vw,4.55rem) !important;
}

/* French 24-hour unit format is wider, so scale it slightly more aggressively. */
html[lang="fr-CA"] #timeDisplay{
  font-size:clamp(2rem,3.75vw,4.15rem) !important;
}

@media(max-width:900px){
  #timeDisplay{
    font-size:clamp(2rem,8.5vw,3.75rem) !important;
  }
  html[lang="fr-CA"] #timeDisplay{
    font-size:clamp(1.65rem,7.2vw,3.3rem) !important;
  }
}

@media(max-width:560px){
  #timeDisplay{
    font-size:clamp(1.65rem,9.2vw,2.8rem) !important;
  }
  html[lang="fr-CA"] #timeDisplay{
    font-size:clamp(1.4rem,7.8vw,2.45rem) !important;
  }
}

/* V55 — language transitions happen at the card level */
.timePanel,
.activityPanel,
.infoPanel,
.footerCard,
footer,
.footerCreditBottom{
  transition:opacity 450ms ease !important;
}
/* V57: panel shells intentionally do not change opacity. */

/* Avoid odd partial fades inside cards: their children follow the card as a unit. */
html.uiLangFading .timePanel *,
html.uiLangFading .activityPanel *,
html.uiLangFading .infoPanel *,
html.uiLangFading footer *{
  animation:none !important;
}

/* When Play/Stop wrap above the volume row, keep the button pair centered. */
@media(max-width:700px){
  .audioControls{
    display:flex !important;
    flex-wrap:wrap !important;
    justify-content:center !important;
    align-items:center !important;
    gap:10px !important;
  }
  .audioControls > #play,
  .audioControls > #stop{
    flex:0 0 auto !important;
  }
  .audioControls > .volumeControl{
    flex:1 0 100% !important;
    width:100% !important;
  }
}

/* V56 — keep card shells visible; fade all card contents together */
.timePanel,
.activityPanel,
.infoPanel,
.footerCard,
footer,
.footerCreditBottom{
  opacity:1 !important;
}

/* One shared opacity transition for everything contained by each card. */
.timePanel > *,
.activityPanel > *,
.infoPanel > *{
  transition:opacity 450ms ease !important;
}

html.uiLangFading .timePanel > *,
html.uiLangFading .activityPanel > *,
html.uiLangFading .infoPanel > *{
  opacity:0 !important;
}

/* Footer has no card background to preserve, so only its content fades. */
footer > *{
  transition:opacity 450ms ease !important;
}
html.uiLangFading footer > *{
  opacity:0 !important;
}

/* V57 — the card shell is permanent; only its content wrapper crossfades. */
.timePanel,
.activityPanel,
.infoPanel{
  opacity:1 !important;
  transition:none !important;
}
.timePanel > .langFadeContent,
.activityPanel > .langFadeContent,
.infoPanel > .langFadeContent{
  opacity:1;
  transition:opacity 450ms ease !important;
}
html.uiLangFading .timePanel,
html.uiLangFading .activityPanel,
html.uiLangFading .infoPanel{
  opacity:1 !important;
}
html.uiLangFading .timePanel > .langFadeContent,
html.uiLangFading .activityPanel > .langFadeContent,
html.uiLangFading .infoPanel > .langFadeContent{
  opacity:0 !important;
}

/* Wrapping must not alter the card's existing layout. */
.langFadeContent{
  display:contents;
}

/* V58 — fade every visible thing inside cards, never the card shell itself. */
.timePanel,
.activityPanel,
.infoPanel{
  opacity:1 !important;
  transition:none !important;
}

/* display:contents wrappers cannot themselves render opacity; descendants must fade. */
.langFadeContent{
  display:contents !important;
}

/* Every descendant participates: text, range inputs, tracks/thumbs, meters,
   percentages, mute/solo controls, transport buttons, selects, etc. */
.timePanel .langFadeContent *,
.activityPanel .langFadeContent *,
.infoPanel .langFadeContent *{
  transition-property:opacity !important;
  transition-duration:450ms !important;
  transition-timing-function:ease !important;
}

html.uiLangFading .timePanel .langFadeContent *,
html.uiLangFading .activityPanel .langFadeContent *,
html.uiLangFading .infoPanel .langFadeContent *{
  opacity:0 !important;
}

/* Native controls need their actual elements faded explicitly as well. */
html.uiLangFading .timePanel input,
html.uiLangFading .timePanel select,
html.uiLangFading .timePanel button,
html.uiLangFading .activityPanel input,
html.uiLangFading .activityPanel select,
html.uiLangFading .activityPanel button,
html.uiLangFading .activityPanel .meter,
html.uiLangFading .activityPanel .fill,
html.uiLangFading .activityPanel .value,
html.uiLangFading .activityPanel .layerControls{
  opacity:0 !important;
}

/* V60 — smooth data-driven Service Activity text updates */
.activityPanel .value,
.activityPanel .densityWord,
.activityPanel .voiceDetail{
  transition:opacity 220ms ease !important;
}
.activityPanel .activityDataReadout{
  transition:opacity 220ms ease !important;
}
.activityPanel .activityDataReadout.activityDataTextFading{
  opacity:0 !important;
}

/* V61 — transport/date status crossfade */
#dateDisplay,
#status{
  transition:opacity 260ms ease !important;
}
#dateDisplay.activityDataTextFading,
#status.activityDataTextFading{
  opacity:0 !important;
}

/* V65 — ENTER/loading controls cannot remain clickable over the main interface */
#enterButton:disabled{
  pointer-events:none !important;
}

/* V71 — only genuine live/scheduled mode changes use this transition */
#dateDisplay,
#status{
  transition:opacity 240ms ease !important;
}
#dateDisplay.modeTextFading,
#status.modeTextFading{
  opacity:0 !important;
}

/* V72 — live/scheduled mode transition targets */
#dateDisplay,
#status,
#dayPhase{
  transition:opacity 240ms ease !important;
}
#dateDisplay.modeTextFading,
#status.modeTextFading,
#dayPhase.modeTextFading{
  opacity:0 !important;
}

/* V74 transition states */
#dayPhase.phaseTextFading{
  opacity:0 !important;
}

/* V77 — data text crossfade. dateDisplay/status intentionally excluded. */
.activityPanel .activityDataReadout,
#dayPhase{
  transition:opacity 240ms ease !important;
}
.activityPanel .activityDataReadout.dataTextCrossfade,
#dayPhase.dataTextCrossfade{
  opacity:0 !important;
}

/* Smooth/slew the existing day/night backdrop variables when transport time jumps. */
:root{
  transition:
    --sky-top 900ms ease,
    --sky-bottom 900ms ease,
    --glow 900ms ease,
    --surface 900ms ease,
    --surface-border 900ms ease;
}
body,
body::before,
body::after,
.app,
.pageShell{
  transition:
    background 900ms ease,
    background-color 900ms ease,
    background-image 900ms ease,
    box-shadow 900ms ease !important;
}

@property --sky-top { syntax: '<color>'; inherits: true; initial-value: rgb(5,12,28); }
@property --sky-bottom { syntax: '<color>'; inherits: true; initial-value: rgb(18,16,42); }
@property --glow { syntax: '<color>'; inherits: true; initial-value: rgba(74,84,155,.13); }
@property --surface { syntax: '<color>'; inherits: true; initial-value: rgba(11,15,24,.78); }
@property --surface-border { syntax: '<color>'; inherits: true; initial-value: rgba(255,255,255,.15); }

/* V81 — responsive Service Activity meter slew */
.activityPanel .voice .fill{
  transition:width 420ms cubic-bezier(.22,.61,.36,1) !important;
  will-change:width;
}

/* V84 — meters have a real rendered baseline from first paint. */
.activityPanel .voice .meter .fill{
  width:0%;
  transition:width 420ms cubic-bezier(.22,.61,.36,1) !important;
  will-change:width;
}

/* V88 — only Low/High/etc. activity labels fade. */
#line1Density,#line2Density,#line4Density,#line5Density,#busDensity{
  transition:opacity 240ms ease !important;
}
#line1Density.activityLabelFading,
#line2Density.activityLabelFading,
#line4Density.activityLabelFading,
#line5Density.activityLabelFading,
#busDensity.activityLabelFading{
  opacity:0 !important;
}

/* Explicitly prevent percentage opacity animation. */
#line1Value,#line2Value,#line4Value,#line5Value,#busValue{
  transition:opacity 0s !important;
  opacity:1 !important;
}

/* V91 — authoritative scheduled <-> RIGHT NOW copy transition.
   Card/background stays visible; only these two readouts fade. */
.timePanel #dateDisplay,
.timePanel #status{
  transition:opacity 320ms ease !important;
}
.timePanel.modeCopyHidden #dateDisplay,
.timePanel.modeCopyHidden #status{
  opacity:0 !important;
}

/* Service Activity level words use Univers 57 Condensed. */
.activityPanel .densityWord,
#line1Density,#line2Density,#line4Density,#line5Density,#busDensity{
  font-family:"Univers 57 Condensed",sans-serif !important;
  font-weight:400 !important;
}

/* BUS ARRIVAL EVENT DISPLAY */
.busArrivalPanel{max-width:var(--content-width,1280px);margin:22px auto 0;padding:22px 24px}
.busArrivalHeader{display:flex;align-items:baseline;justify-content:space-between;gap:18px;margin-bottom:16px}
.busArrivalHeader .panelHeading{margin:0}
.busArrivalMode{font-family:"Univers 57 Condensed",sans-serif;font-size:.95rem;opacity:.68;text-align:right}
.busArrivalReadout{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:18px;min-height:58px;transition:opacity 260ms ease}
.busArrivalReadout.busArrivalChanging{opacity:0}
.busArrivalRoute{font-family:"Fira Sans Condensed",sans-serif;font-weight:800;font-size:clamp(2rem,4vw,3.25rem);line-height:1;min-width:2ch}
.busArrivalText{font-family:"Univers 57 Condensed",sans-serif;font-size:clamp(1.15rem,2vw,1.55rem);line-height:1.2}
@media(max-width:700px){
  .busArrivalPanel{margin-top:16px;padding:18px 20px}
  .busArrivalHeader{align-items:flex-start;flex-direction:column;gap:5px}
  .busArrivalMode{text-align:left}
  .busArrivalReadout{grid-template-columns:1fr;gap:8px}
}

/* Main-page margin circles: fixed sizes even in windowed/mobile view. */
.hero,.dashboardGrid,.busArrivalPanel,.infoGrid,.footer{position:relative;z-index:1}
@keyframes marginFloat1{to{transform:translate(54px,-72px)}} @keyframes marginFloat2{to{transform:translate(-68px,88px)}}
@keyframes marginFloat3{to{transform:translate(79px,55px)}} @keyframes marginFloat4{to{transform:translate(-47px,-91px)}}
@keyframes marginFloat5{to{transform:translate(-53px,74px)}} @keyframes marginFloat6{to{transform:translate(72px,-57px)}}
@keyframes marginFloat7{to{transform:translate(61px,-82px)}} @keyframes marginFloat8{to{transform:translate(-75px,48px)}}

/* Onboard-bus-display-inspired arrival panel. */
.busArrivalPanel{max-width:var(--content-width,1280px);margin:22px auto 0;padding:0!important;background:transparent!important;border:0!important;box-shadow:none!important}
.busDisplayHousing{background:#090909;border:7px solid #292929;border-radius:3px;box-shadow:inset 0 0 0 2px #050505,0 5px 13px rgba(0,0,0,.22);padding:15px 20px 18px;color:#ffad20;font-family:"Bus Stop","Courier New",monospace;text-transform:uppercase;letter-spacing:.055em;overflow:hidden}
.busDisplayTopline{display:flex;justify-content:space-between;align-items:center;gap:20px;padding-bottom:10px;margin-bottom:12px;border-bottom:2px solid rgba(255,173,32,.35);font-size:clamp(.78rem,1.35vw,1rem);line-height:1;color:#ffad20;text-shadow:0 0 5px rgba(255,153,0,.38)}
#busArrivalMode{opacity:.76;text-align:right}
.busArrivalReadout{display:grid;grid-template-columns:auto minmax(0,1fr);align-items:center;gap:22px;min-height:62px;color:#ffad20;transition:opacity 260ms ease}
.busArrivalReadout.busArrivalChanging{opacity:0}
.busArrivalRoute{font-family:"Bus Stop","Courier New",monospace!important;font-weight:400!important;font-size:clamp(2.5rem,5vw,4.1rem)!important;line-height:.9;min-width:2ch;color:#ffad20;text-shadow:0 0 7px rgba(255,153,0,.46)}
.busArrivalText{font-family:"Bus Stop","Courier New",monospace!important;font-size:clamp(1.35rem,2.6vw,2.15rem)!important;line-height:1.08;color:#ffad20;text-shadow:0 0 6px rgba(255,153,0,.4)}
@media(max-width:700px){.busArrivalPanel{margin-top:16px}.busDisplayHousing{padding:13px 15px 15px}.busDisplayTopline{align-items:flex-start;flex-direction:column;gap:7px}#busArrivalMode{text-align:left}.busArrivalReadout{grid-template-columns:auto minmax(0,1fr);gap:13px}}

/* ============================================================
   v113 — Bus Stop display font + whole-card translation fade
   ============================================================ */
@font-face{
  font-family:"Correspondance Bus Stop";
  src:url("fonts/BusStopRegular-5yaxv(1).ttf") format("truetype");
  font-style:normal;
  font-weight:400;
  font-display:swap;
}
.busDisplayHousing,
.busDisplayTopline,
#busArrivalHeading,
#busArrivalMode,
.busArrivalRoute,
.busArrivalText{
  font-family:"Correspondance Bus Stop",monospace !important;
  font-style:normal !important;
  font-weight:400 !important;
  text-transform:none !important;
}

/* Fade the CONTENT/display face on language changes while leaving its outer
   placement intact. Uses the same global language-fade state as the rest of UI. */
.busDisplayHousing{
  transition:opacity 360ms ease !important;
}
html.uiLangFading .busDisplayHousing{
  opacity:0 !important;
}

/* ============================================================
   v114 — one continuous particle field
   The opening-screen circles persist into the main interface.
   ============================================================ */
.metroMomentDots.persistentMainDots{
  position:fixed !important;
  inset:0 !important;
  z-index:0 !important;
  pointer-events:none !important;
  overflow:visible !important;
}
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  position:relative;
  z-index:1;
}

/* The old separate main-page particle field is retired. */

/* ============================================================
   v115 — bus translation fades text only
   ============================================================ */
.busDisplayHousing{
  opacity:1 !important;
  transition:none !important;
}
html.uiLangFading .busDisplayHousing{
  opacity:1 !important;
}
#busArrivalHeading,
#busArrivalMode,
.busArrivalRoute,
.busArrivalText{
  transition:opacity 360ms ease !important;
}
html.uiLangFading #busArrivalHeading,
html.uiLangFading #busArrivalMode,
html.uiLangFading .busArrivalRoute,
html.uiLangFading .busArrivalText{
  opacity:0 !important;
}

/* ============================================================
   v116 — larger, continuously visible particle field
   ============================================================ */

/* Enlarge every loading/main particle by 50% around its own centre.
   Width/height are preserved as the layout basis so existing motion coordinates
   and pointer interaction continue to work as before. */
.metroMomentDots .dot{
  scale:1.5 !important;
  transform-origin:center center !important;
}

/* ENTER transition: the particle field is continuous. It must not inherit any
   loader opacity/fade/visibility animation while the loader content disappears
   and the main interface appears. */
.metroMomentDots,
.metroMomentDots.persistentMainDots,
.metroMomentDots .dot,
.metroMomentDots.persistentMainDots .dot{
  opacity:1 !important;
  visibility:visible !important;
}

.metroMomentDots.persistentMainDots{
  transition:none !important;
  animation:none !important;
}

/* Override any historical exit classes/rules that target the loader dots. */
#loadingOverlay.isLeaving .metroMomentDots,
#loadingOverlay.leaving .metroMomentDots,
#loadingOverlay.exit .metroMomentDots,
#loadingOverlay.isLeaving .metroMomentDots .dot,
#loadingOverlay.leaving .metroMomentDots .dot,
#loadingOverlay.exit .metroMomentDots .dot,
.metroMomentDots.dotExit,
.metroMomentDots .dot.dotExit{
  opacity:1 !important;
  visibility:visible !important;
}

/* v117 — particle motion continuity across ENTER.
   Reparenting changes only stacking context; dot animation/transform state remains. */
.metroMomentDots.persistentMainDots .dot{
  animation-play-state:running !important;
}

/* ============================================================
   v118 — 200% larger visible circles, unchanged interaction halo
   ============================================================ */

/* "Increase by 200%" = 3x the v117 visible size.
   v117 used scale:1.5, so the resulting visible scale is 4.5. */
.metroMomentDots .dot{
  scale:4.5 !important;
  transform-origin:center center !important;
}

/* The dot parent is visually scaled, which would normally scale its invisible
   halo too. Counter-scale the halo so its on-screen interaction dimensions stay
   the same as before. */
.metroMomentDots .dot::before{
  scale:0.3333333333 !important;
  transform-origin:center center !important;
}

/* Absolute ENTER rule: dots themselves never receive an exit/fade animation.
   The loader text/background may fade, but the persistent particles do not. */
#loadingOverlay.entering .metroMomentDots .dot{
  opacity:1 !important;
  visibility:visible !important;
  animation:metroPulseV24 3.6s ease-in-out infinite !important;
}

/* Once persistent, opacity is fixed during the page hand-off. */
.metroMomentDots.persistentMainDots,
.metroMomentDots.persistentMainDots .dot{
  opacity:1 !important;
  visibility:visible !important;
}

/* ============================================================
   v119 — particle field is independent of loadingOverlay
   ============================================================ */
.metroMomentDots{
  position:fixed !important;
  inset:0 !important;
  z-index:1 !important;
  pointer-events:none !important;
  opacity:1 !important;
  visibility:visible !important;
  overflow:visible !important;
}

/* Loader content and main UI sit above the permanent particles. */
#loadingOverlay{
  z-index:9999;
}
#loadingOverlay .correspondanceLoader{
  position:relative;
  z-index:3;
}
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  position:relative;
  z-index:2;
}

/* The field is no longer a descendant of loadingOverlay, so historical
   #loadingOverlay.entering .metroMomentDots selectors cannot match it. */

/* ============================================================
   v120 — permanent particles visible through loading overlay
   ============================================================ */

/* Permanent particle plane: above the loader background, below its content. */
.metroMomentDots{
  z-index:10000 !important;
  pointer-events:none !important;
}

/* Loading overlay background remains below the particles. */
#loadingOverlay{
  z-index:9999 !important;
}

/* All actual loading-screen UI stays readable/clickable above the particles. */
#loadingOverlay .correspondanceLoader,
#loadingOverlay .langSwitch{
  position:relative !important;
  z-index:10001 !important;
}

/* Main interface also stays above the persistent particle plane after ENTER. */
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  position:relative !important;
  z-index:10001 !important;
}

/* ============================================================
   v121 — correct loading/main stacking order
   ============================================================ */

/* Main page must stay underneath the entire loading experience. */
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  position:relative !important;
  z-index:2 !important;
}

/* Full-screen loader background covers the main page. */
#loadingOverlay{
  z-index:9999 !important;
}

/* Persistent particles sit above the loader background... */
.metroMomentDots{
  z-index:10000 !important;
}

/* ...while loader text/buttons/logos remain above the particles. */
#loadingOverlay .correspondanceLoader,
#loadingOverlay .langSwitch{
  position:relative !important;
  z-index:10001 !important;
}

/* ============================================================
   v122 — one particle layer, two stacking phases
   ============================================================ */

/* Loading phase:
   circles are deliberately in front of all loader text/content. */
.metroMomentDots{
  z-index:10002 !important;
}
#loadingOverlay{
  z-index:9999 !important;
}
#loadingOverlay .correspondanceLoader,
#loadingOverlay .langSwitch{
  position:relative !important;
  z-index:10001 !important;
}

/* Main UI remains underneath the loader before ENTER. */
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  position:relative !important;
  z-index:2 !important;
}

/* After the loader has completed its exit, lower the SAME particle field behind
   the main interface. No duplicate circles and no animation/state reset. */
html.mainParticlesBehind .metroMomentDots{
  z-index:0 !important;
}
html.mainParticlesBehind .hero,
html.mainParticlesBehind .dashboardGrid,
html.mainParticlesBehind .busArrivalPanel,
html.mainParticlesBehind .infoGrid,
html.mainParticlesBehind footer{
  position:relative !important;
  z-index:2 !important;
}

/* ============================================================
   v124 — loader out -> particle z-order switch -> main page in
   ============================================================ */

/* Main page is held invisible until JS confirms the loader is gone and the
   persistent particle field has already been lowered behind it. */
html.waitingForMainReveal .hero,
html.waitingForMainReveal .dashboardGrid,
html.waitingForMainReveal .busArrivalPanel,
html.waitingForMainReveal .infoGrid,
html.waitingForMainReveal .photoGalleryCard,
html.waitingForMainReveal footer{
  opacity:0 !important;
}

/* The particle layer itself never fades during this sequence. */
.metroMomentDots{
  opacity:1 !important;
}

/* ============================================================
   v125 — coordinated main-page content reveal
   ============================================================ */

/* All major main-page content fades rather than snapping in. */
.hero,
.dashboardGrid,
.busArrivalPanel,
.infoGrid,
footer{
  transition:opacity 400ms ease !important;
}

/* Keep every card/section hidden during the loader -> background -> z-order handoff. */
html.waitingForMainReveal .hero,
html.waitingForMainReveal .dashboardGrid,
html.waitingForMainReveal .busArrivalPanel,
html.waitingForMainReveal .infoGrid,
html.waitingForMainReveal .photoGalleryCard,
html.waitingForMainReveal footer{
  opacity:0 !important;
}

/* Once the background has been revealed and the particles have been lowered,
   release all main content together. */
html.mainContentRevealed .hero,
html.mainContentRevealed .dashboardGrid,
html.mainContentRevealed .busArrivalPanel,
html.mainContentRevealed .infoGrid,
html.mainContentRevealed .photoGalleryCard,
html.mainContentRevealed footer{
  opacity:1 !important;
}

/* ============================================================
   v127 — responsive particle sizing
   ============================================================ */

/* Desktop keeps the current v126 circle size. */
.metroMomentDots .dot{
  scale:4.5 !important;
}
.metroMomentDots .dot::before{
  scale:0.3333333333 !important;
}

/* Smaller windows: reduce visual clutter without changing particle count,
   positions, drift, sweep behaviour, or effective interaction-area size. */
@media (max-width:900px){
  .metroMomentDots .dot{
    scale:3.25 !important;
  }
  .metroMomentDots .dot::before{
    scale:0.4615384615 !important;
  }
}

@media (max-width:600px){
  .metroMomentDots .dot{
    scale:2.25 !important;
  }
  .metroMomentDots .dot::before{
    scale:0.6666666667 !important;
  }
}

@media (max-width:400px){
  .metroMomentDots .dot{
    scale:1.8 !important;
  }
  .metroMomentDots .dot::before{
    scale:0.8333333333 !important;
  }
}

/* ============================================================
   v128 — slew responsive particle size changes
   ============================================================ */
.metroMomentDots .dot{
  transition:scale 500ms ease !important;
}
.metroMomentDots .dot::before{
  transition:scale 500ms ease !important;
}

/* Respect reduced-motion preferences. */
@media (prefers-reduced-motion: reduce){
  .metroMomentDots .dot,
  .metroMomentDots .dot::before{
    transition:none !important;
  }
}

/* ============================================================
   v130 — responsive bus-display typography
   ============================================================ */

/* Full-size desktop: fluid type with no line breaks in the bus display. */
@media (min-width:1101px){
  .busDisplayTopline{
    font-size:clamp(.78rem, .92vw, 1.08rem) !important;
    white-space:nowrap !important;
  }
  #busArrivalHeading,
  #busArrivalMode{
    white-space:nowrap !important;
  }
  .busArrivalRoute{
    font-size:clamp(1.8rem, 2.45vw, 3rem) !important;
    white-space:nowrap !important;
  }
  .busArrivalText{
    font-size:clamp(.9rem, 1.15vw, 1.3rem) !important;
    white-space:nowrap !important;
    overflow:visible !important;
  }
}

/* Windowed/tablet: scale the typography with available width and permit
   natural line breaks instead of overflowing the card. */
@media (max-width:1100px){
  .busDisplayTopline{
    font-size:clamp(.68rem, 1.35vw, .95rem) !important;
    white-space:normal !important;
  }
  #busArrivalHeading,
  #busArrivalMode{
    white-space:normal !important;
  }
  .busArrivalRoute{
    font-size:clamp(1.45rem, 3.4vw, 2.45rem) !important;
    white-space:normal !important;
  }
  .busArrivalText{
    font-size:clamp(.78rem, 1.8vw, 1.1rem) !important;
    white-space:normal !important;
    overflow-wrap:break-word !important;
  }
}

/* Small mobile: keep reducing type proportionally while retaining wrapping. */
@media (max-width:600px){
  .busDisplayTopline{
    font-size:clamp(.62rem, 2.7vw, .8rem) !important;
  }
  .busArrivalRoute{
    font-size:clamp(1.25rem, 6vw, 1.85rem) !important;
  }
  .busArrivalText{
    font-size:clamp(.72rem, 3.3vw, .95rem) !important;
    line-height:1.22 !important;
  }
}

/* ============================================================
   v132 — keep full-screen bus arrival copy on one line
   ============================================================ */
@media (min-width:1101px){
  .busArrivalText{
    /* Scale down with available desktop width; cap low enough for the long
       French "sélectionnée" waiting message to remain on one line. */
    font-size:clamp(.68rem, .86vw, 1.02rem) !important;
    white-space:nowrap !important;
    overflow:visible !important;
    text-overflow:clip !important;
    max-width:100% !important;
    min-width:0 !important;
  }

  /* Ensure the text container itself is allowed to use the full LED panel. */
  .busArrivalPanel .busArrivalText,
  .busArrivalPanel [id*="busArrival"]{
    max-width:100% !important;
  }
}

/* ============================================================
   v133 — muted line visual de-emphasis
   ============================================================ */

/* When muted, gently de-emphasize the line's informational content to make
   the silent state visually obvious. Keep the M/S controls fully opaque so
   they're always easy to operate. */
.voice.serviceMuted > *:not(.msControls):not(.voiceButtons):not(.muteSoloControls){
  opacity:.55;
  transition:opacity 350ms ease !important;
}

/* Catch common nested informational elements without dimming buttons. */
.voice.serviceMuted .voiceName,
.voice.serviceMuted .voiceDetail,
.voice.serviceMuted .detail,
.voice.serviceMuted .densityLabel,
.voice.serviceMuted .activityLabel,
.voice.serviceMuted .percentage,
.voice.serviceMuted [id$="Detail"]{
  opacity:.55 !important;
  transition:opacity 350ms ease !important;
}

.voice.serviceMuted button{
  opacity:1 !important;
}

/* Return smoothly to full visibility when unmuted. */
.voice > *{
  transition-property:opacity;
  transition-duration:350ms;
  transition-timing-function:ease;
}

/* ============================================================
   v134 — normal bus text size; shrink only long waiting sentence
   ============================================================ */
@media (min-width:1101px){
  .busArrivalText{
    font-size:clamp(.9rem, 1.15vw, 1.3rem) !important;
    white-space:nowrap !important;
  }

  .busArrivalText.longWaitingMessage{
    font-size:clamp(.72rem, .92vw, 1.05rem) !important;
  }
}

/* ============================================================
   v136 — larger bus-card typography
   ============================================================ */

/* Desktop: increase the normal bus-card text while keeping the special long
   French waiting sentence slightly smaller so it can remain on one line. */
@media (min-width:1101px){
  .busDisplayTopline{
    font-size:clamp(.88rem, 1.05vw, 1.2rem) !important;
  }
  .busArrivalRoute{
    font-size:clamp(2rem, 2.7vw, 3.25rem) !important;
  }
  .busArrivalText{
    font-size:clamp(1rem, 1.3vw, 1.45rem) !important;
  }
  .busArrivalText.longWaitingMessage{
    font-size:clamp(.78rem, 1vw, 1.12rem) !important;
  }
}

/* Windowed/tablet: increase proportionally while retaining wrapping. */
@media (max-width:1100px){
  .busDisplayTopline{
    font-size:clamp(.74rem, 1.5vw, 1.02rem) !important;
  }
  .busArrivalRoute{
    font-size:clamp(1.6rem, 3.7vw, 2.65rem) !important;
  }
  .busArrivalText{
    font-size:clamp(.86rem, 2vw, 1.2rem) !important;
  }
}

/* Mobile: modest bump so the LED display remains readable. */
@media (max-width:600px){
  .busDisplayTopline{
    font-size:clamp(.68rem, 2.9vw, .86rem) !important;
  }
  .busArrivalRoute{
    font-size:clamp(1.35rem, 6.4vw, 2rem) !important;
  }
  .busArrivalText{
    font-size:clamp(.78rem, 3.6vw, 1.02rem) !important;
  }
}

/* ============================================================
   v138 — STM logo size
   ============================================================ */
.footerLogos img[alt*="STM" i],
.footerLogos a[href*="stm.info"] img,
img.stmLogo,
#stmLogo{
  width:200% !important;
  max-width:none !important;
  height:auto !important;
}

/* ============================================================
   v140 — debugged Service Activity hover / solo zoom
   Actual markup: .name + .name::before + .meter/.fill
   ============================================================ */

.activityPanel .voice .name,
.activityPanel .voice .meter{
  transform:scale(1);
  transition:transform 180ms ease !important;
  will-change:transform;
}

.activityPanel .voice .name::before{
  transform:scale(1);
  transform-origin:center;
  transition:transform 180ms ease, filter 180ms ease, box-shadow 180ms ease !important;
}

/* Hovering the row OR holding Solo applies one persistent zoom state. */
.activityPanel .voice.serviceZoom .name{
  transform:scale(1.055);
}

.activityPanel .voice.serviceZoom .meter{
  transform:scale(1.035);
}

.activityPanel .voice.serviceZoom .name::before{
  transform:scale(1.28);
  filter:brightness(1.18) saturate(1.12);
  box-shadow:
    0 0 7px var(--route-colour),
    0 0 15px color-mix(in srgb,var(--route-colour) 65%,transparent);
}

/* Give the coloured meter itself a slightly stronger glow without changing
   its width/density animation. */
.activityPanel .voice.serviceZoom .fill{
  box-shadow:
    0 0 10px var(--route-colour),
    0 0 20px color-mix(in srgb,var(--route-colour) 58%,transparent) !important;
}

/* These are intentionally NOT transformed:
   .value        = percentage
   .densityWord  = Low/Medium/High activity
   .voiceDetail  = departures/bus detail
   .layerControls = M/S buttons
*/

@media (prefers-reduced-motion:reduce){
  .activityPanel .voice .name,
  .activityPanel .voice .meter,
  .activityPanel .voice .name::before{
    transition:none !important;
  }
}

/* ============================================================
   v146 — STM footer logo 500% bigger than original
   ============================================================ */
.stmFooterLogo{
  width:672px !important; /* 112px + 500% = 672px total */
  max-width:min(672px, 70vw) !important;
  height:auto !important;
}

/* ============================================================
   v147 — tighter footer vertical spacing
   ============================================================ */
.siteFooter,
footer.siteFooter,
footer{
  padding-top:18px !important;
  padding-bottom:18px !important;
}

/* ============================================================
   v149 — 80% black cards + no range focus rectangle
   ============================================================ */
.card,
.panel,
.dataFooter,
.footerStacked,
.transitClockCard,
.activityPanel,
.infoCard{
  background:rgba(0,0,0,.80) !important;
}

/* Remove the browser's persistent focus/highlight rectangle after interacting
   with the clock and volume range controls. This does not change the track,
   thumb, hover state, or existing scrub behavior. */
input[type="range"]:focus,
input[type="range"]:focus-visible,
#slider:focus,
#slider:focus-visible,
#volumeSlider:focus,
#volumeSlider:focus-visible{
  outline:none !important;
  box-shadow:none !important;
}

/* Some Chromium builds apply focus decoration to a wrapper/track via
   appearance. Keep the range itself free of a focus ring. */
#slider,
#volumeSlider{
  -webkit-tap-highlight-color:transparent;
}

/* ============================================================
   v150 — photo gallery card
   ============================================================ */
.photoGalleryCard{
  margin-top:22px;
  overflow:hidden;
}
.photoGalleryGrid{
  display:grid;
  grid-template-columns:repeat(4,minmax(0,1fr));
  gap:10px;
}
.photoGalleryItem{
  margin:0;
  min-width:0;
  aspect-ratio:4/3;
  overflow:hidden;
  border-radius:10px;
  background:#000;
}
.photoGalleryItem img{
  display:block;
  width:100%;
  height:100%;
  object-fit:cover;
  transition:transform 300ms ease,filter 300ms ease;
}
.photoGalleryItem:hover img{
  transform:scale(1.025);
  filter:brightness(1.06);
}
@media(max-width:900px){
  .photoGalleryGrid{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media(max-width:520px){
  .photoGalleryGrid{grid-template-columns:1fr}
  .photoGalleryItem{aspect-ratio:3/2}
}

/* ============================================================
   v151 — gallery card matches the site's standard cards
   ============================================================ */
.photoGalleryCard{
  background:var(--surface) !important;
  border:1px solid var(--surface-border) !important;
  border-radius:16px !important;
  color:var(--text) !important;
  backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  padding:24px !important;
  box-sizing:border-box;
  overflow:hidden;
}

.photoGalleryGrid{
  border-radius:10px;
  overflow:hidden;
}

@media(max-width:700px){
  .photoGalleryCard{
    padding:18px !important;
  }
}

/* ============================================================
   v156 — gallery uses exact established mainContentRevealed fade
   ============================================================ */
html:not(.mainContentRevealed) .photoGalleryCard{
  opacity:0 !important;
}
.photoGalleryCard{
  transition:opacity 400ms ease !important;
}
html.mainContentRevealed .photoGalleryCard{
  opacity:1 !important;
}

/* ============================================================
   v158 — thicker borders on all buttons
   ============================================================ */
button,
.button,
[role="button"],
input[type="button"],
input[type="submit"],
input[type="reset"]{
  border-width:2px !important;
}

/* ============================================================
   v159 — align Service Activity heading block with clock heading
   ============================================================ */

/* Move only the Service Activity title + explanatory sentence upward so the
   title baseline aligns with TRANSIT SCHEDULE CLOCK. All service rows and
   controls remain at their existing positions. */
.activityPanel > h2,
.activityPanel > .activityIntro,
.activityPanel > .activitySubtitle,
.activityPanel > .panelIntro{
  position:relative;
  top:-10px;
}

/* Reserve the original flow footprint so nothing below the heading block moves. */

/* ============================================================
   v160 — active M/S border + glow
   ============================================================ */

/* Preserve a visible, slightly transparent border when Mute or Solo is active.
   aria-pressed is the authoritative state already used by the audio controls. */
.layerToggle[aria-pressed="true"]{
  border:2px solid rgba(255,255,255,.62) !important;
  box-shadow:
    0 0 7px rgba(255,255,255,.42),
    0 0 14px rgba(255,255,255,.20) !important;
  transition:
    border-color 180ms ease,
    box-shadow 180ms ease,
    background-color 180ms ease,
    opacity 180ms ease !important;
}

/* Keep the active indication present while hovering/focusing the button too. */
.layerToggle[aria-pressed="true"]:hover,
.layerToggle[aria-pressed="true"]:focus,
.layerToggle[aria-pressed="true"]:focus-visible{
  border-color:rgba(255,255,255,.72) !important;
  box-shadow:
    0 0 8px rgba(255,255,255,.52),
    0 0 16px rgba(255,255,255,.24) !important;
}

/* ============================================================
   v161 — LIVE transport button
   ============================================================ */
#rightNow{
  display:inline-flex !important;
  align-items:center;
  justify-content:center;
  gap:8px;
}

#rightNow .liveLamp{
  width:9px;
  height:9px;
  flex:0 0 9px;
  border-radius:50%;
  background:#777;
  box-shadow:none;
  transition:
    background-color 180ms ease,
    box-shadow 180ms ease,
    transform 180ms ease;
}

/* Scheduled mode: preserve the button's original appearance; only the lamp is
   gray. Live mode uses the existing aria-pressed state as the authority. */
#rightNow[aria-pressed="true"]{
  background:var(--text) !important;
  color:var(--bg) !important;
  border-color:var(--text) !important;
}

#rightNow[aria-pressed="true"] .liveLamp{
  background:#ff3434;
  box-shadow:
    0 0 5px rgba(255,52,52,.95),
    0 0 11px rgba(255,52,52,.72),
    0 0 18px rgba(255,52,52,.38);
}

#rightNow[aria-pressed="false"] .liveLamp,
#rightNow:not([aria-pressed]) .liveLamp{
  background:#777;
  box-shadow:none;
}

/* ============================================================
   v168 — LIVE placement
   ============================================================ */
#rightNow{
  margin-left:auto !important;
}

/* ============================================================
   v173 — weekday selector fade
   ============================================================ */
#weekdayPickerLabel{
  transition:opacity 180ms ease !important;
}
#weekdayPickerLabel.weekdayChanging{
  opacity:0 !important;
}

/* ============================================================
   v175 — animated weekday dropdown
   ============================================================ */
.weekdayPicker{
  position:relative;
  display:inline-block;
  overflow:visible;
}
.weekdayNativeSelect{
  position:absolute !important;
  width:1px !important;
  height:1px !important;
  opacity:0 !important;
  pointer-events:none !important;
}
.weekdayPickerButton{
  font:inherit;
  color:inherit;
  background:transparent;
  border:inherit;
  border-radius:inherit;
  min-width:100%;
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:.65em;
  cursor:pointer;
}
.weekdayPickerChevron{
  display:inline-block;
  transition:transform 220ms ease;
}
.weekdayPicker.open .weekdayPickerChevron{
  transform:rotate(180deg);
}
.weekdayPickerMenu{
  position:absolute;
  z-index:40;
  left:0;
  top:calc(100% + 7px);
  min-width:100%;
  overflow:hidden;
  opacity:0;
  transform:translateY(-12px) scaleY(.92);
  transform-origin:top center;
  pointer-events:none;
  transition:
    opacity 220ms ease,
    transform 260ms cubic-bezier(.22,.8,.25,1);
}
.weekdayPicker.open .weekdayPickerMenu{
  opacity:1;
  transform:translateY(0) scaleY(1);
  pointer-events:auto;
}
.weekdayPickerOption{
  display:block;
  width:100%;
  border:0;
  text-align:left;
  font:inherit;
  color:inherit;
  cursor:pointer;
}

/* ============================================================
   v177 — weekday selector sizing / menu finish
   ============================================================ */
.weekdayPicker{
  /* Fixed to the longest English weekday rather than changing width by label. */
  width:10.25em !important;
  min-width:10.25em !important;
  max-width:10.25em !important;
}
.weekdayPickerButton{
  width:100% !important;
  min-width:100% !important;
  box-sizing:border-box !important;
}
.weekdayPickerChevron{
  position:relative !important;
  top:-2px !important;
}
.weekdayPickerMenu{
  width:100% !important;
  min-width:100% !important;
  box-sizing:border-box !important;
  background:rgba(0,0,0,.90) !important;
  backdrop-filter:blur(8px);
  -webkit-backdrop-filter:blur(8px);
}

/* ============================================================
   v178 — weekday selector border / arrow alignment
   ============================================================ */
.weekdayPickerButton{
  border:3px solid rgba(255,255,255,.72) !important;
  border-radius:999px !important;
  padding:.48em .9em !important;
}
.weekdayPickerChevron{
  top:-5px !important;
}

/* ============================================================
   v179 — symmetric dropdown chevron + reversible menu motion
   ============================================================ */
.weekdayPickerChevron{
  position:relative !important;
  top:auto !important;
  width:.55em !important;
  height:.55em !important;
  flex:0 0 .55em !important;
  display:block !important;
  border-right:2px solid currentColor !important;
  border-bottom:2px solid currentColor !important;
  transform:rotate(45deg) !important;
  transform-origin:50% 50% !important;
  transition:transform 220ms ease !important;
}
.weekdayPicker.open .weekdayPickerChevron{
  transform:rotate(225deg) !important;
}

/* One transition definition handles both directions. Closing therefore
   retraces the opening motion instead of using a separate exit behavior. */
.weekdayPickerMenu{
  opacity:0 !important;
  transform:translateY(-12px) scaleY(.92) !important;
  transform-origin:top center !important;
  pointer-events:none !important;
  transition:
    opacity 220ms ease,
    transform 260ms cubic-bezier(.22,.8,.25,1) !important;
}
.weekdayPicker.open .weekdayPickerMenu{
  opacity:1 !important;
  transform:translateY(0) scaleY(1) !important;
  pointer-events:auto !important;
}

/* ============================================================
   v181 — larger master-volume percentage
   ============================================================ */
#volumeValue{
  font-size:1.2em !important;
}

/* ============================================================
   v182 — dropdown retract without upward box jump
   ============================================================ */
.weekdayPickerMenu{
  /* Keep the menu anchored at its final screen position at all times. */
  transform:none !important;
  opacity:0 !important;
  clip-path:inset(0 0 100% 0 round 8px) !important;
  transform-origin:top center !important;
  transition:
    opacity 220ms ease,
    clip-path 300ms cubic-bezier(.22,.8,.25,1) !important;
  will-change:opacity,clip-path;
}
.weekdayPicker.open .weekdayPickerMenu{
  transform:none !important;
  opacity:1 !important;
  clip-path:inset(0 0 0 0 round 8px) !important;
}

/* The close completion is now clip-path, not transform. */

/* ============================================================
   v183 — slower dropdown close + visible fade
   ============================================================ */
.weekdayPickerMenu{
  transition:
    opacity 420ms ease,
    clip-path 420ms cubic-bezier(.22,.8,.25,1) !important;
}

/* Opening can stay a little quicker; closing uses the base 420ms transition. */
.weekdayPicker.open .weekdayPickerMenu{
  transition:
    opacity 260ms ease,
    clip-path 300ms cubic-bezier(.22,.8,.25,1) !important;
}

/* During option selection, explicitly preserve the full closing transition.
   This prevents later selector/observer updates from collapsing it early. */
.weekdayPicker.closing .weekdayPickerMenu{
  opacity:0 !important;
  clip-path:inset(0 0 100% 0 round 8px) !important;
  pointer-events:none !important;
  transition:
    opacity 420ms ease,
    clip-path 420ms cubic-bezier(.22,.8,.25,1) !important;
}

/* v186 — unified main audio mute control */
#audioMute{
  flex:0 0 auto;
  transition:
    opacity 140ms ease,
    background-color 180ms ease,
    color 180ms ease,
    border-color 180ms ease,
    box-shadow 180ms ease,
    transform 180ms ease !important;
}
#audioMute.labelFading{
  opacity:0 !important;
}

/* v189 — fixed-size bilingual Mute / Unmute control
   Sized for the longest FR label so EN/FR and state changes never reflow. */
#audioMute{
  box-sizing:border-box !important;
  width:10.75rem !important;
  min-width:10.75rem !important;
  max-width:10.75rem !important;
  flex:0 0 10.75rem !important;
  text-align:center !important;
  white-space:nowrap !important;
}

/* ============================================================
   V211 — CLEAN STORY SYSTEM
   Story CSS rebuilt from the pre-story (V195-era) stylesheet.
   No story-era circle positioning overrides.
   ============================================================ */

.storyIntro{
  position:fixed;
  inset:0;
  z-index:9500;
  visibility:hidden;
  opacity:0;
  pointer-events:none;
  overflow:hidden;
  background:transparent;
  color:#fff;
  font-family:"Univers 57 Condensed","Univers57",Arial,sans-serif;
  transition:opacity 1s ease,visibility 0s linear 1s;
}
.storyIntro.active{
  visibility:visible;
  opacity:1;
  pointer-events:auto;
  transition:opacity 1s ease;
}
.storyIntro.mainReveal{
  opacity:0;
  pointer-events:none;
  transition:opacity 2s ease;
}

.storyLanguage{
  position:fixed;
  top:24px;
  right:28px;
  left:auto;
  z-index:20;
  margin:0;
  transform:none;
}
.storyIntro.line4-active .storyLanguage,
.storyIntro.line4-active .storyLanguage button,
.storyIntro.line4-active .storyLanguage span{
  color:#111;
  border-color:#111;
  transition:color 1.8s ease,border-color 1.8s ease;
}

.storyPage{
  position:absolute;
  inset:0;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:clamp(32px,7vw,96px);
  box-sizing:border-box;
  opacity:0;
  visibility:hidden;
  pointer-events:none;
  transition:opacity 1.35s ease,visibility 0s linear 1.35s;
}
.storyPage.current{
  opacity:1;
  visibility:visible;
  pointer-events:auto;
  transition:opacity 1.35s ease;
}

.storyLine1{background:#00A651}
.storyLine2{background:#F58220}
.storyLine4{background:#FFD200;color:#111}
.storyLine5{background:#0072BC}
.storyAnniversary{background:transparent}

.storyCopy{
  position:relative;
  z-index:4;
  width:min(820px,88vw);
  margin:auto;
  text-align:left;
}
.storyKicker{
  font-size:clamp(.9rem,1.6vw,1.15rem);
  letter-spacing:.13em;
  text-transform:uppercase;
  margin-bottom:18px;
}
.storyTitle{
  margin:0 0 28px;
  max-width:900px;
  font:inherit;
  font-size:clamp(3.4rem,9vw,8.2rem);
  line-height:.88;
  letter-spacing:-.045em;
  text-transform:uppercase;
}
.storyBody{
  margin:0;
  max-width:680px;
  min-height:7.2em;
  font-size:clamp(1.15rem,2.2vw,1.55rem);
  line-height:1.42;
}
.storyNext{
  margin-top:38px;
  min-width:130px;
  padding:12px 22px;
  border:3px solid currentColor;
  border-radius:12px;
  background:transparent;
  color:inherit;
  font:inherit;
  font-size:1rem;
  letter-spacing:.07em;
  text-transform:uppercase;
  cursor:pointer;
  transform:none;
}
.storyLine1 .storyNext:hover{background:#fff;color:#00A651;border-color:#fff}
.storyLine2 .storyNext:hover{background:#fff;color:#F58220;border-color:#fff}
.storyLine4 .storyNext:hover{background:#111;color:#FFD200;border-color:#111}
.storyLine5 .storyNext:hover{background:#fff;color:#0072BC;border-color:#fff}
.storyAnniversary .storyNext:hover{background:#fff;color:#050505;border-color:#fff}

/* One authoritative text animation model. */
.storyKicker,.storyTitle,.storyBody,.storyNext{
  opacity:0;
  transform:none;
  animation:none;
  transition:opacity .55s ease;
}

/* Page reveal: exactly 900 ms between each entrance. */
.storyPage.current.reveal .storyKicker{opacity:1;transition-delay:0ms}
.storyPage.current.reveal .storyTitle{opacity:1;transition-delay:900ms}
.storyPage.current.reveal .storyBody{opacity:1;transition-delay:1800ms}

/* NEXT: all text leaves together. */
.storyPage.storyExit .storyKicker,
.storyPage.storyExit .storyTitle,
.storyPage.storyExit .storyBody,
.storyPage.storyExit .storyNext{
  opacity:0;
  transition-delay:0ms;
}

/* Language: kicker/body/NEXT all behave identically. Year stays untouched. */
.storyPage.languageOut .storyKicker,
.storyPage.languageOut .storyBody,
.storyPage.languageOut .storyNext{
  opacity:0;
  transition-delay:0ms;
}
.storyPage.languageIn .storyKicker,
.storyPage.languageIn .storyBody,
.storyPage.languageIn .storyNext{
  opacity:1;
  transition-delay:0ms;
}

/* Anniversary reuses the untouched pre-story loading composition as its
   background. We alter the loading overlay, not the circles themselves. */
#loadingOverlay.storyAnniversaryHost{
  z-index:9499;
  opacity:1;
  visibility:visible;
  pointer-events:none;
  background:#050505 !important;
}
#loadingOverlay.storyAnniversaryHost .correspondanceLoader{
  visibility:hidden;
  opacity:0;
  pointer-events:none;
}
#loadingOverlay.storyAnniversaryHost::before,
#loadingOverlay.storyAnniversaryHost::after{
  display:none;
}

@media(max-width:650px){
  .storyPage{padding:72px 24px 36px}
  .storyCopy{width:100%}
  .storyTitle{font-size:clamp(3rem,15vw,5.2rem)}
  .storyBody{font-size:1.08rem}
}

/* ============================================================
   V212 — story layering + single circle motion ownership
   ============================================================ */

/* The main Correspondance interface cannot appear between story pages.
   It is released only after the 60 Years overlay finishes its final fade. */
html.storyRunning .hero,
html.storyRunning .dashboardGrid,
html.storyRunning .busArrivalPanel,
html.storyRunning .infoGrid,
html.storyRunning .photoGalleryCard,
html.storyRunning footer{
  opacity:0 !important;
  visibility:hidden !important;
}

/* Story pages sit above the persistent particle plane. Opaque line pages cover
   the circles; the transparent 60 Years page reveals the SAME circle field
   beneath its copy. */
.storyIntro{
  z-index:10003;
}
.storyIntro .storyCopy,
.storyIntro .storyLanguage{
  position:relative;
  z-index:2;
}
.storyIntro .storyLanguage{
  position:fixed;
}

/* The anniversary copy must remain visible above the circles. */
.storyPage.storyAnniversary.current{
  opacity:1;
  visibility:visible;
}
.storyPage.storyAnniversary.current .storyCopy{
  visibility:visible;
}

/* During the anniversary host state, expose only the original dark loading
   background. Do not alter particle position, transform, scale, or animation. */
#loadingOverlay.storyAnniversaryHost{
  display:flex !important;
  opacity:1 !important;
  visibility:visible !important;
}

/* ============================================================
   V220 — SINGLE AUTHORITATIVE STORY TRANSITION SYSTEM
   ============================================================ */
.storyPage{opacity:0;visibility:hidden;pointer-events:none}
.storyPage.current,.storyPage.storyIncoming{opacity:1;visibility:visible;pointer-events:auto}
.storyPage.storyLine1.storyInitial{opacity:0;transition:opacity 1.35s ease}
.storyPage.storyLine1.storyInitial.storyInitialVisible{opacity:1}
.storyPage.storyIncoming{transition:none}
.storyPage.storyExit{opacity:0;transition:opacity .6s ease;pointer-events:none}

.storyKicker,.storyTitle,.storyBody,.storyNext{opacity:0;transform:none;animation:none;transition:opacity .55s ease}
.storyPage.current.reveal .storyKicker{opacity:1;transition-delay:0ms}
.storyPage.current.reveal .storyTitle{opacity:1;transition-delay:900ms}
.storyPage.current.reveal .storyBody{opacity:1;transition-delay:1800ms}
.storyPage.current.reveal.languageOut .storyKicker,
.storyPage.current.reveal.languageOut .storyBody,
.storyPage.current.reveal.languageOut .storyNext{opacity:0;transition:opacity .55s ease;transition-delay:0ms}
.storyPage.current.reveal.languageIn .storyKicker,
.storyPage.current.reveal.languageIn .storyBody,
.storyPage.current.reveal.languageIn .storyNext{opacity:1;transition:opacity .55s ease;transition-delay:0ms}

#loaderAnniversary{position:absolute;inset:0;z-index:40;opacity:0;visibility:hidden;pointer-events:none;background:transparent}
#loaderAnniversary.active{opacity:1;visibility:visible;pointer-events:auto;background:transparent}
#loaderAnniversary .storyPage.storyAnniversary{position:absolute;inset:0;opacity:1;visibility:visible;pointer-events:auto;background:transparent}
#loadingOverlay.anniversaryActive{opacity:1!important;visibility:visible!important;pointer-events:auto;background:transparent!important}
#loadingOverlay.anniversaryActive .loadingContent{opacity:1!important;visibility:visible!important;pointer-events:none!important}
html.storyRunning .hero,html.storyRunning .dashboardGrid,html.storyRunning .busArrivalPanel,
html.storyRunning .infoGrid,html.storyRunning .photoGalleryCard,html.storyRunning footer{opacity:0!important;visibility:hidden!important}
#loadingOverlay.storyFinalFade{opacity:0!important;transition:opacity 2s ease!important;pointer-events:none}

/* ============================================================
   V221 — loader interaction repair
   ============================================================ */

/* A hidden anniversary overlay and ALL of its descendants must be inert.
   This explicitly beats the generic `.storyPage.current {pointer-events:auto}`. */
#loaderAnniversary:not(.active),
#loaderAnniversary:not(.active) *{
  pointer-events:none !important;
}

/* Only activate anniversary controls once Blue has actually opened 60 Years. */
#loaderAnniversary.active{
  pointer-events:auto !important;
}
#loaderAnniversary.active .storyPage.storyAnniversary,
#loaderAnniversary.active .storyNext{
  pointer-events:auto !important;
}

/* Keep the original loader controls above the persistent particle plane. */
#loadingOverlay .correspondanceLoader,
#loadingOverlay .loaderLanguage{
  position:relative !important;
  z-index:10004 !important;
  pointer-events:auto;
}

/* ============================================================
   V222 — restore loader centering + clean 60 Years interaction
   ============================================================ */

/* V221 added position:relative to loader content/language, which changed their
   original positioning context. Restore the original V195/V212 layout instead
   of repositioning the loading composition. */
#loadingOverlay .correspondanceLoader{
  position:static !important;
}
#loadingOverlay .loaderLanguage{
  position:absolute !important;
}

/* During 60 Years, preserve the original loading composition and moving circles,
   but make ALL original loading UI invisible and completely inert. */
#loadingOverlay.anniversaryActive .correspondanceLoader,
#loadingOverlay.anniversaryActive .loaderLanguage,
#loadingOverlay.anniversaryActive .loadingContent,
#loadingOverlay.anniversaryActive .loadingLogos,
#loadingOverlay.anniversaryActive #enterButton{
  opacity:0 !important;
  visibility:hidden !important;
  pointer-events:none !important;
}

/* Do not hide the particle field. It remains the visual background. */
#loadingOverlay.anniversaryActive .metroMomentDots{
  opacity:1 !important;
  visibility:visible !important;
  pointer-events:none !important;
}

/* 60 Years stays transparent, but its story content owns the top interaction
   layer. The final button is explicitly highest and clickable. */
#loaderAnniversary.active{
  z-index:10020 !important;
  pointer-events:none !important;
  background:transparent !important;
}
#loaderAnniversary.active .storyPage.storyAnniversary{
  z-index:10021 !important;
  pointer-events:none !important;
  background:transparent !important;
}
#loaderAnniversary.active .storyNext{
  position:relative !important;
  z-index:10030 !important;
  pointer-events:auto !important;
}

/* ============================================================
   V223 — 60 Years -> main Correspondance handoff
   ============================================================ */

/* As soon as the final button is pressed, the complete anniversary layer is
   inert. Its visual fade is owned by the loading overlay beneath it. */
#loaderAnniversary.finishing,
#loaderAnniversary.finishing *{
  pointer-events:none !important;
}

/* Main content is already laid out underneath, but becomes visible only when
   storyRunning is removed at the end of the 2-second anniversary fade. */
html.storyRunning .hero,
html.storyRunning .dashboardGrid,
html.storyRunning .busArrivalPanel,
html.storyRunning .infoGrid,
html.storyRunning .photoGalleryCard,
html.storyRunning footer{
  opacity:0 !important;
  visibility:hidden !important;
}

/* Once the story flag is removed, restore normal main-page visibility with
   a controlled fade rather than a hot reveal. */
html.mainContentRevealed .hero,
html.mainContentRevealed .dashboardGrid,
html.mainContentRevealed .busArrivalPanel,
html.mainContentRevealed .infoGrid,
html.mainContentRevealed .photoGalleryCard,
html.mainContentRevealed footer{
  opacity:1;
  visibility:visible;
  transition:opacity 1.2s ease;
}

/* ============================================================
   V228 — safe single-surface story colour ramp
   Built directly from known-working V225; no HTML structure changes.
   ============================================================ */
#storyIntro{
  background-color:#00A651;
  transition:background-color .6s ease, opacity .6s ease;
}

/* Route pages no longer own backgrounds; #storyIntro owns the colour. */
#storyIntro .storyPage.storyLine1,
#storyIntro .storyPage.storyLine2,
#storyIntro .storyPage.storyLine4,
#storyIntro .storyPage.storyLine5,
#storyIntro .storyPage.storyLine1.current,
#storyIntro .storyPage.storyLine2.current,
#storyIntro .storyPage.storyLine4.current,
#storyIntro .storyPage.storyLine5.current,
#storyIntro .storyPage.storyIncoming{
  background:transparent !important;
}

/* Existing 600 ms storyExit now fades text only. */
#storyIntro .storyPage.storyExit{
  opacity:0;
  transition:opacity .6s ease;
  pointer-events:none;
}

/* Blue -> anniversary: fade the existing story overlay away to expose loader. */
#storyIntro.storyToAnniversary{
  opacity:0;
  pointer-events:none;
}

/* Anniversary: show ONLY FR/EN from the original loader controls. */
#loadingOverlay.anniversaryActive .loaderLanguage{
  opacity:1 !important;
  visibility:visible !important;
  pointer-events:auto !important;
  z-index:10040 !important;
}
#loadingOverlay.anniversaryActive .correspondanceLoader,
#loadingOverlay.anniversaryActive .loadingContent,
#loadingOverlay.anniversaryActive .loadingLogos,
#loadingOverlay.anniversaryActive #enterButton{
  opacity:0 !important;
  visibility:hidden !important;
  pointer-events:none !important;
}

/* V234 — anniversary language-neutral year */
.storyPage.storyAnniversary.anniversaryLanguageFade .storyKicker,
.storyPage.storyAnniversary.anniversaryLanguageFade.languageOut .storyKicker,
.storyPage.storyAnniversary.anniversaryLanguageFade.languageIn .storyKicker{
  opacity:1 !important;
  transition:none !important;
  transition-delay:0ms !important;
}

/* ============================================================
   V237 — consolidated story timing + fixed language geometry
   Removes competing NEXT delays. JavaScript alone decides when NEXT begins.
   ============================================================ */

/* Fixed story geometry: language changes cannot push later rows vertically. */
.storyCopy{
  display:grid;
  grid-template-rows:auto auto 10em auto;
  align-content:center;
}
.storyKicker{grid-row:1}
.storyTitle{grid-row:2}
.storyBody{
  grid-row:3;
  min-height:0;
  height:10em;
  overflow:hidden;
}
.storyNext{grid-row:4;justify-self:start}

/* NEXT has no CSS stagger timer anymore. It becomes visible only when the
   controller adds storyNextReady after the body fade has actually completed. */
.storyPage .storyNext{
  opacity:0;
  transition:opacity .55s ease;
  transition-delay:0ms;
}
.storyPage.current.reveal.storyNextReady .storyNext{
  opacity:1;
  transition-delay:0ms;
}
.storyPage.storyExit .storyNext{
  opacity:0;
  transition-delay:0ms;
}

/* Language changes may crossfade NEXT only after it has already been revealed. */
.storyPage.languageOut.storyNextReady .storyNext{opacity:0;transition-delay:0ms}
.storyPage.languageIn.storyNextReady .storyNext{opacity:1;transition-delay:0ms}
.storyPage.languageOut:not(.storyNextReady) .storyNext,
.storyPage.languageIn:not(.storyNextReady) .storyNext{opacity:0!important;transition-delay:0ms!important}

/* Anniversary language switch: year and numeral remain fixed. */
.storyPage.storyAnniversary.anniversaryLanguageFade .storyKicker,
.storyPage.storyAnniversary.anniversaryLanguageFade.languageOut .storyKicker,
.storyPage.storyAnniversary.anniversaryLanguageFade.languageIn .storyKicker{
  opacity:1!important;
  transition:none!important;
  transition-delay:0ms!important;
}
.storyPage.storyAnniversary.languageOut .storyTitle,
.storyPage.storyAnniversary.languageIn .storyTitle{
  opacity:1!important;
  transition-delay:0ms!important;
}
.storyPage.storyAnniversary .anniversary60{opacity:1!important}
.storyPage.storyAnniversary .anniversaryTitleText{opacity:1;transition:opacity .55s ease}
.storyPage.storyAnniversary.languageOut .anniversaryTitleText{opacity:0}
.storyPage.storyAnniversary.languageIn .anniversaryTitleText{opacity:1}

@media(max-width:650px){
  .storyCopy{grid-template-rows:auto auto 13.5em auto}
  .storyBody{height:13.5em}
}

/* ============================================================
   V238 — language crossfade specificity + anniversary line geometry
   ============================================================ */

.storyPage.current.reveal.storyNextReady.languageOut .storyNext{
  opacity:0;
  transition:opacity .55s ease;
  transition-delay:0ms;
}
.storyPage.current.reveal.storyNextReady.languageIn .storyNext{
  opacity:1;
  transition:opacity .55s ease;
  transition-delay:0ms;
}
#loaderAnniversary.active .storyPage.storyAnniversary.storyNextReady.languageOut .storyNext{
  opacity:0;
  transition:opacity .55s ease;
  transition-delay:0ms;
}
#loaderAnniversary.active .storyPage.storyAnniversary.storyNextReady.languageIn .storyNext{
  opacity:1;
  transition:opacity .55s ease;
  transition-delay:0ms;
}

.storyPage.storyAnniversary .storyTitle{
  min-height:3.15em;
  display:flex;
  align-items:flex-start;
}
.storyPage.storyAnniversary .anniversaryTitleText{
  display:inline-block;
}

/* ============================================================
   V239 — exact three-line anniversary title + persistent NEXT interactivity
   ============================================================ */
.storyPage.storyAnniversary .storyTitle{
  min-height:3.15em;
  display:flex;
  align-items:flex-start;
  white-space:nowrap;
}
.storyPage.storyAnniversary .anniversary60{
  flex:0 0 auto;
  display:inline-block;
}
.storyPage.storyAnniversary .anniversaryTitleText{
  display:inline-block;
  line-height:inherit;
}

/* ============================================================
   V248 — viewport-contained anniversary presentation frame
   The 60 ANS composition uses a fixed desktop reference canvas and scales
   uniformly to fit smaller/full-screen viewport heights. It is deliberately
   transparent so the existing particle/loading background remains unchanged.
   ============================================================ */
#loaderAnniversary.active{
  position:fixed !important;
  inset:0 !important;
  width:100vw !important;
  height:100dvh !important;
  overflow:hidden !important;
}
#loaderAnniversary.active .storyPage.storyAnniversary{
  position:absolute !important;
  inset:auto !important;
  left:50% !important;
  top:50% !important;
  width:1100px !important;
  height:820px !important;
  box-sizing:border-box !important;
  padding:64px 90px !important;
  transform:translate(-50%,-50%) scale(var(--anniversary-scale,1)) !important;
  transform-origin:center center !important;
  overflow:visible !important;
  background:transparent !important;
}
#loaderAnniversary.active .storyPage.storyAnniversary .storyCopy{
  width:820px !important;
  max-width:none !important;
  margin:auto !important;
  grid-template-rows:auto auto 10em auto !important;
}
#loaderAnniversary.active .storyPage.storyAnniversary .storyBody{
  height:10em !important;
  min-height:0 !important;
  overflow:hidden !important;
}

/* ============================================================
   V252 — delayed dynamic clock text only
   The Transit Schedule card reveals with the dashboard. Only the live
   clock readout waits for LIVE state/time formatting to settle.
   ============================================================ */
html.mainContentRevealed:not(.clockRevealReady) .timePanel #timeDisplay,
html.mainContentRevealed:not(.clockRevealReady) .timePanel #dayPhase,
html.mainContentRevealed:not(.clockRevealReady) .timePanel #dateDisplay{
  opacity:0 !important;
}
html.mainContentRevealed.clockRevealReady .timePanel #timeDisplay,
html.mainContentRevealed.clockRevealReady .timePanel #dayPhase,
html.mainContentRevealed.clockRevealReady .timePanel #dateDisplay{
  opacity:1 !important;
  transition:opacity .8s ease !important;
}

/* ============================================================
   V251 — stagger the initial loading-page copy
   Geometry is unchanged; only the first-page entrance choreography changes.
   ============================================================ */
#loadingOverlay:not(.entering) .correspondanceLoader .loaderEyebrow,
#loadingOverlay:not(.entering) .correspondanceLoader .loaderTitle,
#loadingOverlay:not(.entering) .correspondanceLoader .loaderIntro,
#loadingOverlay:not(.entering) .correspondanceLoader .loaderActionSlot,
#loadingOverlay:not(.entering) .correspondanceLoader .loaderBrandRow{
  opacity:0;
  animation:loaderEntryV251 .55s ease forwards;
}
#loadingOverlay:not(.entering) .correspondanceLoader .loaderEyebrow{animation-delay:.12s}
#loadingOverlay:not(.entering) .correspondanceLoader .loaderTitle{animation-delay:.42s}
#loadingOverlay:not(.entering) .correspondanceLoader .loaderIntro{animation-delay:.72s}
#loadingOverlay:not(.entering) .correspondanceLoader .loaderActionSlot{animation-delay:1.02s}
#loadingOverlay:not(.entering) .correspondanceLoader .loaderBrandRow{animation-delay:1.32s}

@keyframes loaderEntryV251{
  from{opacity:0}
  to{opacity:1}
}

/* ENTER retains its data-ready reveal inside the already-staggered action slot. */
#loadingOverlay:not(.entering) .correspondanceLoader #enterButton{
  animation:none;
}

@media (prefers-reduced-motion:reduce){
  #loadingOverlay:not(.entering) .correspondanceLoader .loaderEyebrow,
  #loadingOverlay:not(.entering) .correspondanceLoader .loaderTitle,
  #loadingOverlay:not(.entering) .correspondanceLoader .loaderIntro,
  #loadingOverlay:not(.entering) .correspondanceLoader .loaderActionSlot,
  #loadingOverlay:not(.entering) .correspondanceLoader .loaderBrandRow{
    animation:none;
    opacity:1;
  }
}

/* ============================================================
   V256 — de-emphasize non-soloed service rows
   Mirrors the existing muted-row opacity while preserving controls.
   ============================================================ */
.voice.serviceSoloDim > *:not(.layerControls):not(.msControls):not(.voiceButtons):not(.muteSoloControls){
  opacity:.55;
  transition:opacity 350ms ease !important;
}
.voice.serviceSoloDim .voiceName,
.voice.serviceSoloDim .voiceDetail,
.voice.serviceSoloDim .detail,
.voice.serviceSoloDim .densityLabel,
.voice.serviceSoloDim .activityLabel,
.voice.serviceSoloDim .percentage,
.voice.serviceSoloDim [id$="Detail"]{
  opacity:.55 !important;
  transition:opacity 350ms ease !important;
}
.voice.serviceSoloDim button,
.voice.serviceSoloDim .layerControls{
  opacity:1 !important;
}

/* ============================================================
   V257 — Green Line story skip
   Appears one stagger step after NEXT and sits at the viewport bottom-right.
   ============================================================ */
.storyPage .storySkip{
  position:fixed;
  right:clamp(24px,4vw,64px);
  bottom:clamp(24px,4vh,48px);
  z-index:8;
  opacity:0;
  pointer-events:none;
  background:transparent;
  color:inherit;
  border:1px solid currentColor;
  border-radius:999px;
  padding:10px 16px;
  font:inherit;
  font-size:.78rem;
  letter-spacing:.12em;
  cursor:pointer;
  transition:opacity .55s ease,background-color .2s ease,color .2s ease;
}
.storyPage.current.reveal.storySkipReady .storySkip{opacity:1;pointer-events:auto}
.storyPage.storyExit .storySkip,
.storyPage.languageOut .storySkip{opacity:0;pointer-events:none}
.storyPage.languageIn.storySkipReady .storySkip{opacity:1;pointer-events:auto}
.storyLine1 .storySkip:hover{background:#fff;color:#00A651;border-color:#fff}

/* ============================================================
   V258 — skip from any Metro chapter
   The historical colour field dissolves over the same 2s window used to
   introduce skipped audio layers before the main interface is revealed.
   ============================================================ */
#storyIntro.storySkipToMain{
  opacity:0 !important;
  transition:opacity 2s ease !important;
  pointer-events:none !important;
}
.storyLine2 .storySkip:hover{background:#fff;color:#F58220;border-color:#fff}
.storyLine4 .storySkip:hover{background:#111;color:#FFD400;border-color:#111}
.storyLine5 .storySkip:hover{background:#fff;color:#0072BC;border-color:#fff}

/* ============================================================
   V260 — persistent bilingual story skip
   Keep one visual footprint across EN/FR and preserve the control while
   transitioning between Metro chapters after its first reveal.
   ============================================================ */
.storyPage .storySkip{
  box-sizing:border-box;
  width:190px;
  min-width:190px;
  text-align:center;
  white-space:nowrap;
}
.storyPage.storyExit.storySkipReady .storySkip{
  opacity:1;
  pointer-events:auto;
}
#storyIntro.storySkipToMain .storyPage.storyExit.storySkipReady .storySkip{
  opacity:0;
  pointer-events:none;
}

/* ============================================================
   V261 — story skip language crossfade
   Language transitions take precedence over the persistent chapter-exit rule,
   so the fixed SKIP control fades with the rest of the translated story copy.
   ============================================================ */
.storyPage.storySkipReady.languageOut .storySkip,
.storyPage.storyExit.storySkipReady.languageOut .storySkip{
  opacity:0 !important;
  pointer-events:none;
}
.storyPage.current.reveal.storySkipReady.languageIn .storySkip,
.storyPage.storyExit.storySkipReady.languageIn .storySkip{
  opacity:1 !important;
  pointer-events:auto;
}

/* ============================================================
   V262 — keep persistent story skip visually static between Metro pages.
   Chapter changes swap identical fixed-position controls instantaneously;
   languageOut/languageIn below retain the intentional 550ms translation fade.
   ============================================================ */
.storyPage.storyExit.storySkipReady:not(.languageOut):not(.languageIn) .storySkip,
.storyPage.current.reveal.storySkipReady:not(.languageOut):not(.languageIn) .storySkip{
  transition:background-color .2s ease,color .2s ease,border-color .2s ease !important;
}
.storyPage.storySkipReady.languageOut .storySkip,
.storyPage.storyExit.storySkipReady.languageOut .storySkip,
.storyPage.current.reveal.storySkipReady.languageIn .storySkip,
.storyPage.storyExit.storySkipReady.languageIn .storySkip{
  transition:opacity .55s ease,background-color .2s ease,color .2s ease,border-color .2s ease !important;
}

/* ============================================================
   V263 rebuild — persistent Metro skip + stable bilingual clock
   One overlay-level skip control is independent of storyPage/storyExit.
   Translation is the only routine allowed to fade it after its first reveal.
   ============================================================ */
.storyPage .storySkip{display:none !important}
#storySkipPersistent{
  position:fixed;
  right:clamp(24px,4vw,64px);
  bottom:clamp(24px,4vh,48px);
  z-index:12;
  box-sizing:border-box;
  width:190px;
  min-width:190px;
  text-align:center;
  white-space:nowrap;
  opacity:0;
  pointer-events:none;
  background:transparent;
  color:#fff;
  border:1px solid currentColor;
  border-radius:999px;
  padding:10px 16px;
  font:inherit;
  font-size:.78rem;
  letter-spacing:.12em;
  cursor:pointer;
  transition:background-color .2s ease,color .2s ease,border-color .2s ease;
}
#storyIntro.storySkipVisible #storySkipPersistent{
  opacity:1;
  pointer-events:auto;
}
#storyIntro.storySkipLanguageFading #storySkipPersistent{
  opacity:0 !important;
  pointer-events:none !important;
  transition:opacity .55s ease,background-color .2s ease,color .2s ease,border-color .2s ease !important;
}
#storyIntro.storySkipLanguageIn #storySkipPersistent{
  opacity:1 !important;
  pointer-events:auto !important;
  transition:opacity .55s ease,background-color .2s ease,color .2s ease,border-color .2s ease !important;
}
#storyIntro.storySkipToMain #storySkipPersistent,
#storyIntro.storySkipEnding #storySkipPersistent{
  opacity:0 !important;
  pointer-events:none !important;
  transition:opacity .55s ease !important;
}
#storyIntro:not(.storySkipLanguageFading):not(.storySkipLanguageIn):not(.storySkipToMain):not(.storySkipEnding) #storySkipPersistent{
  /* V276: normal story state owns both the first Green opacity entrance and
     the Line 4 contrast slew. Do not drop opacity or shorten colour here. */
  transition:opacity .55s ease,background-color .2s ease,color .6s ease,border-color .6s ease !important;
}
#storyIntro[data-story-index="0"] #storySkipPersistent:hover{background:#fff;color:#00A651;border-color:#fff}
#storyIntro[data-story-index="1"] #storySkipPersistent:hover{background:#fff;color:#F58220;border-color:#fff}
#storyIntro[data-story-index="2"] #storySkipPersistent:hover{background:#111;color:#FFD400;border-color:#111}
#storyIntro[data-story-index="3"] #storySkipPersistent:hover{background:#fff;color:#0072BC;border-color:#fff}

/* EN and FR use exactly the same clock geometry. This intentionally overrides
   the older French-only size reductions. */
#timeDisplay,
html[lang="fr-CA"] #timeDisplay{
  font-size:clamp(2.35rem,4.35vw,4.55rem) !important;
}
@media(max-width:900px){
  #timeDisplay,
  html[lang="fr-CA"] #timeDisplay{font-size:clamp(2rem,8.5vw,3.75rem) !important}
}
@media(max-width:560px){
  #timeDisplay,
  html[lang="fr-CA"] #timeDisplay{font-size:clamp(1.65rem,9.2vw,2.8rem) !important}
}

/* Translation crossfade has priority over V252's delayed first clock reveal.
   The first reveal still waits for clockRevealReady; later language changes
   fade time + LIVE + phase together without changing card opacity. */
#timeDisplay,#dateDisplay,#dayPhase{transition:opacity .5s ease !important}
html.uiLangFading #timeDisplay,
html.uiLangFading #dateDisplay,
html.uiLangFading #dayPhase{
  opacity:0 !important;
}

/* ============================================================
   V264 — clock language crossfade + SKIP contrast slew
   Keep the initial delayed clock reveal, but language switching wins while
   uiLangFading is active even after clockRevealReady has been set.
   ============================================================ */
html.mainContentRevealed.clockRevealReady.uiLangFading .timePanel #timeDisplay,
html.mainContentRevealed.clockRevealReady.uiLangFading .timePanel #dayPhase,
html.mainContentRevealed.clockRevealReady.uiLangFading .timePanel #dateDisplay,
html.uiLangFading .timePanel #timeDisplay,
html.uiLangFading .timePanel #dayPhase,
html.uiLangFading .timePanel #dateDisplay{
  opacity:0 !important;
  transition:opacity .5s ease !important;
}
html.mainContentRevealed.clockRevealReady:not(.uiLangFading) .timePanel #timeDisplay,
html.mainContentRevealed.clockRevealReady:not(.uiLangFading) .timePanel #dayPhase,
html.mainContentRevealed.clockRevealReady:not(.uiLangFading) .timePanel #dateDisplay{
  opacity:1 !important;
  transition:opacity .5s ease !important;
}

/* Yellow Line needs dark contrast; only colour/border slew. Opacity remains
   owned exclusively by first reveal, language crossfade, and story ending. */
#storySkipPersistent{
  transition:background-color .2s ease,color .6s ease,border-color .6s ease !important;
}
#storyIntro.storySkipDark #storySkipPersistent{
  color:#111 !important;
  border-color:#111 !important;
}
#storyIntro.storySkipLanguageFading #storySkipPersistent,
#storyIntro.storySkipLanguageIn #storySkipPersistent{
  transition:opacity .55s ease,background-color .2s ease,color .6s ease,border-color .6s ease !important;
}

/* V265 — dedicated BUS story chapter with live Métro-station arrivals. */
.storyBus{
  background:#111;
  color:#fff;
}
.storyBusLayout{
  position:relative;
  z-index:4;
  width:min(1320px,90vw);
  margin:auto;
  display:grid;
  grid-template-columns:minmax(0,1.35fr) minmax(330px,.65fr);
  gap:clamp(42px,6vw,92px);
  align-items:center;
}
.storyBus .storyCopy{
  width:100%;
  margin:0;
}
.storyBusLivePanel{
  border:3px solid currentColor;
  border-radius:18px;
  padding:22px 22px 18px;
  min-height:390px;
  max-height:min(62vh,560px);
  display:flex;
  flex-direction:column;
  opacity:0;
  transition:opacity .55s ease;
  overflow:hidden;
}
.storyPage.storyBus.current.reveal .storyBusLivePanel{
  opacity:1;
  /* V289: one stagger after NEXT (2350ms), one before BUS SKIP (3450ms). */
  transition-delay:2900ms;
}
.storyPage.storyBus.storyExit .storyBusLivePanel,
.storyPage.storyBus.languageOut .storyBusLivePanel{
  opacity:0;
  transition-delay:0ms;
}
.storyPage.storyBus.languageIn .storyBusLivePanel{
  opacity:1;
  transition-delay:0ms;
}
.storyBusLiveHeader{
  display:flex;
  justify-content:space-between;
  gap:16px;
  align-items:baseline;
  padding-bottom:14px;
  border-bottom:2px solid currentColor;
  font-size:.84rem;
  letter-spacing:.09em;
  text-transform:uppercase;
}
#storyBusLiveStatus{opacity:.72;text-align:right}
.storyBusLiveList{
  min-height:0;
  overflow:auto;
  padding-top:8px;
  scrollbar-width:thin;
}
.storyBusArrivalRow{
  display:grid;
  grid-template-columns:64px minmax(0,1fr);
  gap:14px;
  align-items:baseline;
  padding:11px 2px;
  border-bottom:1px solid rgba(255,255,255,.28);
}
.storyBusArrivalRoute{
  font-size:1.2rem;
  font-weight:700;
  letter-spacing:.03em;
}
.storyBusArrivalStation{
  font-size:.98rem;
  line-height:1.25;
}
.storyBusLiveEmpty{
  padding:18px 2px;
  opacity:.7;
  line-height:1.4;
}
.storyBus .storyNext:hover{background:#fff;color:#111;border-color:#fff}
#storyIntro[data-story-index="4"] #storySkipPersistent:hover{background:#fff;color:#111;border-color:#fff}
@media(max-width:900px){
  .storyBusLayout{grid-template-columns:1fr;gap:24px;width:min(820px,88vw)}
  .storyBusLivePanel{min-height:190px;max-height:28vh}
  .storyBus .storyTitle{font-size:clamp(3rem,12vw,6rem)}
}
.storyPage.storyBus.languageOut .storyTitle{opacity:0;transition-delay:0ms}
.storyPage.storyBus.languageIn .storyTitle{opacity:1;transition-delay:0ms}

/* V266 — STM turquoise BUS chapter + six-slot animated live arrival queue. */
.storyBus{background:#00A7A5 !important;color:#fff;}
.storyBusLiveList{position:relative;overflow:hidden !important;padding-top:8px;flex:1 1 auto;}
.storyBusArrivalRow{transition:opacity .48s ease,transform .48s ease;will-change:opacity,transform;}
.storyBusArrivalRow.storyBusArrivalEnter{opacity:0;transform:translateY(16px);}
.storyBusArrivalRow.storyBusArrivalExit{opacity:0;transform:translateY(-14px);}
.storyBusArrivalRow.storyBusArrivalShift{transform:translateY(calc(-1 * var(--story-bus-shift, 0px)));}
.storyBus .storyTitle .storyTitleLine{display:block;}
/* ============================================================
   V268 — BUS arrival-only de-duplication + guaranteed SKIP clearance
   ============================================================ */
/* The BUS page uses a fixed 1320×820 reference frame. Keep the live panel
   comfortably above the persistent bottom-right SKIP control inside that
   reference composition; scaling preserves the clearance in windowed mode. */
#storyIntro.active .storyPage.storyBus.current .storyBusLayout{
  align-items:start !important;
  padding-top:24px !important;
  box-sizing:border-box !important;
}
#storyIntro.active .storyPage.storyBus.current .storyBusLivePanel{
  min-height:0 !important;
  height:456px !important;
  max-height:456px !important;
  align-self:start !important;
}
#storyIntro.active .storyPage.storyBus.current .storyBusLiveList{
  min-height:0 !important;
  overflow:hidden !important;
}

/* ============================================================
   V270 — exact one-row rollover + live-panel content fit
   ============================================================ */
#storyIntro.active .storyPage.storyBus.current .storyBusLivePanel{
  height:auto !important;
  max-height:none !important;
  min-height:0 !important;
  padding-bottom:0 !important;
}
#storyIntro.active .storyPage.storyBus.current .storyBusLiveList{
  flex:0 0 auto !important;
  padding-bottom:0 !important;
}
#storyIntro.active .storyPage.storyBus.current .storyBusArrivalRow:last-child{
  border-bottom:0 !important;
}

/* ============================================================
   V271 — growing BUS feed + scrollable short/mobile BUS chapter
   ============================================================ */
.storyBusLivePanel{
  transition:opacity .55s ease,height .46s cubic-bezier(.22,.61,.36,1) !important;
  box-sizing:border-box;
}

/* Short/windowed and mobile layouts stop using the scaled desktop canvas.
   Let the BUS chapter flow vertically in its viewport: copy/NEXT, feed, then
   enough bottom clearance for the fixed persistent SKIP control. */
@media (max-width:900px), (max-height:720px){
  #storyIntro.active:has(.storyPage.storyBus.current){
    overflow-x:hidden !important;
    overflow-y:auto !important;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior-y:contain;
  }
  #storyIntro.active .storyPage.storyBus.current{
    position:relative !important;
    inset:auto !important;
    left:auto !important;
    top:auto !important;
    width:100% !important;
    height:auto !important;
    min-height:100% !important;
    padding:clamp(32px,6vw,64px) clamp(22px,6vw,54px) 150px !important;
    transform:none !important;
    overflow:visible !important;
    align-items:flex-start !important;
    justify-content:flex-start !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyBusLayout{
    width:min(760px,100%) !important;
    max-width:none !important;
    margin:0 auto !important;
    padding:0 !important;
    display:flex !important;
    flex-direction:column !important;
    gap:30px !important;
    align-items:stretch !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyCopy{
    width:100% !important;
    margin:0 !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyBusLivePanel{
    width:100% !important;
    align-self:stretch !important;
    min-height:0 !important;
    max-height:none !important;
    overflow:hidden !important;
    margin:0 !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyBusLiveList{
    overflow:hidden !important;
  }
}

/* ============================================================
   V272 — BUS feed translation crossfade + desktop column lock
   ============================================================ */
/* Keep the live-feed frame itself stable during language changes; only its
   textual contents join the story language crossfade. */
.storyPage.storyBus .storyBusLiveHeader > *,
.storyPage.storyBus .storyBusArrivalRow,
.storyPage.storyBus .storyBusLiveEmpty{
  opacity:1;
  transition:opacity .55s ease, transform .48s cubic-bezier(.22,.61,.36,1) !important;
}
.storyPage.storyBus.languageOut .storyBusLiveHeader > *,
.storyPage.storyBus.languageOut .storyBusArrivalRow,
.storyPage.storyBus.languageOut .storyBusLiveEmpty{
  opacity:0 !important;
}
.storyPage.storyBus.languageIn .storyBusLiveHeader > *,
.storyPage.storyBus.languageIn .storyBusArrivalRow,
.storyPage.storyBus.languageIn .storyBusLiveEmpty{
  opacity:1 !important;
}
/* Do not fade the panel/border itself for translation. */
.storyPage.storyBus.languageOut .storyBusLivePanel,
.storyPage.storyBus.languageIn .storyBusLivePanel{
  opacity:1 !important;
}

/* Desktop/roomy viewport: force the original split composition. The live
   feed remains in grid column 2; only the responsive rule below 900px or
   720px viewport height is allowed to stack it beneath NEXT. */
@media (min-width:901px) and (min-height:721px){
  #storyIntro.active .storyPage.storyBus.current .storyBusLayout{
    display:grid !important;
    grid-template-columns:minmax(0,1.35fr) minmax(330px,.65fr) !important;
    gap:72px !important;
    align-items:center !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyCopy{
    grid-column:1 !important;
  }
  #storyIntro.active .storyPage.storyBus.current .storyBusLivePanel{
    grid-column:2 !important;
    grid-row:1 !important;
    width:100% !important;
  }
}
/* ============================================================
   V277 — single persistent BUS skip + Line 4 language contrast
   ============================================================ */
/* The original persistent SKIP is the only BUS skip. On BUS it receives the
   opaque white treatment previously used by the redundant local button. */
#storyIntro:has(.storyPage.storyBus.current) #storySkipPersistent{
  display:block !important;
  opacity:1 !important;
  pointer-events:auto !important;
  background:#fff !important;
  color:#00A9A5 !important;
  border-color:#fff !important;
}
#storyIntro:has(.storyPage.storyBus.current) #storySkipPersistent:hover{
  background:#fff !important;
  color:#00A9A5 !important;
  border-color:#fff !important;
}

/* FR / EN uses the same bidirectional contrast slew as the persistent SKIP.
   Keeping the transition on the base state is what makes Line 4 -> Line 5
   ramp back to white instead of snapping. */
#storyIntro .storyLanguage,
#storyIntro .storyLanguage button,
#storyIntro .storyLanguage span{
  transition:color .6s ease,border-color .6s ease !important;
}
#storyIntro.line4-active .storyLanguage,
#storyIntro.line4-active .storyLanguage button,
#storyIntro.line4-active .storyLanguage span{
  color:#111 !important;
  border-color:#111 !important;
}

/* ============================================================
   V278 — BUS live-feed heading is the only translated/faded feed element
   ============================================================ */
/* Earlier BUS translation rules faded the whole header and rows. Override
   those rules explicitly: status/count, arrival rows, and empty-state copy
   remain continuously visible. Only #storyBusLiveHeading participates in
   languageOut/languageIn. */
.storyPage.storyBus #storyBusLiveStatus,
.storyPage.storyBus .storyBusArrivalRow,
.storyPage.storyBus .storyBusLiveEmpty{
  opacity:1 !important;
}
.storyPage.storyBus.languageOut #storyBusLiveStatus,
.storyPage.storyBus.languageIn #storyBusLiveStatus,
.storyPage.storyBus.languageOut .storyBusArrivalRow,
.storyPage.storyBus.languageIn .storyBusArrivalRow,
.storyPage.storyBus.languageOut .storyBusLiveEmpty,
.storyPage.storyBus.languageIn .storyBusLiveEmpty{
  opacity:1 !important;
}
.storyPage.storyBus #storyBusLiveHeading{
  opacity:1;
  transition:opacity .55s ease !important;
}
.storyPage.storyBus.languageOut #storyBusLiveHeading{
  opacity:0 !important;
}
.storyPage.storyBus.languageIn #storyBusLiveHeading{
  opacity:1 !important;
}

/* ============================================================
   V279 — keep story language selector interactive above BUS scroller
   ============================================================ */
/* V248's generic story-layer rule lowered .storyLanguage to z-index:2,
   beneath current story pages (z-index:4). On the natural-flow BUS layout,
   the page covers the selector's hit area even though the selector remains
   visually visible. Keep the selector in the story overlay's top interaction
   layer on every viewport size. */
#storyIntro .storyLanguage{
  position:fixed !important;
  z-index:100 !important;
  pointer-events:auto !important;
}
#storyIntro .storyLanguage button{
  pointer-events:auto !important;
  touch-action:manipulation;
}

/* ============================================================
   V280 — BUS SKIP translation fade + Line 4 -> Line 5 fill ramp
   ============================================================ */
/* Persistent SKIP chapter transitions share a 600ms colour/fill slew. */
#storyIntro #storySkipPersistent{
  transition:opacity .55s ease,background-color .6s ease,color .6s ease,border-color .6s ease !important;
}

/* On BUS, let the persistent SKIP participate in the 550ms language crossfade. */
#storyIntro.storySkipLanguageFading:has(.storyPage.storyBus.current) #storySkipPersistent{
  opacity:0 !important;
  pointer-events:none !important;
  transition:opacity .55s ease,background-color .6s ease,color .6s ease,border-color .6s ease !important;
}
#storyIntro.storySkipLanguageIn:has(.storyPage.storyBus.current) #storySkipPersistent{
  opacity:1 !important;
  pointer-events:auto !important;
  transition:opacity .55s ease,background-color .6s ease,color .6s ease,border-color .6s ease !important;
}

/* ============================================================
   V281 — move SKIP fill/colour ramp to Line 5 -> BUS
   ============================================================ */
/* Line 5 keeps the transparent white-outline treatment. */
#storyIntro.storySkipLine5:not(.storySkipBus) #storySkipPersistent{
  background-color:rgba(255,255,255,0) !important;
  color:#fff !important;
  border-color:#fff !important;
}
/* Begin the BUS treatment as soon as the Line 5 -> BUS transition starts,
   rather than waiting until BUS becomes .current. */
#storyIntro.storySkipBus #storySkipPersistent{
  display:block !important;
  background-color:rgba(255,255,255,1) !important;
  color:#00A9A5 !important;
  border-color:#fff !important;
  transition:opacity .55s ease,background-color .6s ease,color .6s ease,border-color .6s ease !important;
}


/* ============================================================
   V284 — language crossfades affect button labels, not button shells
   ============================================================ */
.buttonLangLabel,
#rightNow .liveLabel,
#weekdayPickerButton #weekdayPickerLabel{
  opacity:1;
  transition:opacity .5s ease !important;
}

/* Global/main/loading language switch: keep every button/control shell visible. */
html.uiLangFading button{
  opacity:1 !important;
}
html.uiLangFading button .buttonLangLabel,
html.uiLangFading #rightNow .liveLabel,
html.uiLangFading #weekdayPickerButton #weekdayPickerLabel{
  opacity:0 !important;
}
/* Structural button decoration is not language copy. */
html.uiLangFading #rightNow .liveLamp,
html.uiLangFading #weekdayPickerButton .weekdayPickerChevron{
  opacity:1 !important;
}

/* Story language switch: NEXT and persistent SKIP retain their shell; label only fades. */
.storyPage.storyNextReady.languageOut .storyNext,
.storyPage.current.reveal.storyNextReady.languageOut .storyNext,
#loaderAnniversary.active .storyPage.storyAnniversary.storyNextReady.languageOut .storyNext,
.storyPage.storyNextReady.languageIn .storyNext,
.storyPage.current.reveal.storyNextReady.languageIn .storyNext,
#loaderAnniversary.active .storyPage.storyAnniversary.storyNextReady.languageIn .storyNext{
  opacity:1 !important;
}
.storyPage.languageOut .storyNext .buttonLangLabel,
.storyPage.languageIn .storyNext .buttonLangLabel{
  transition:opacity .55s ease !important;
}
.storyPage.languageOut .storyNext .buttonLangLabel{opacity:0 !important;}
.storyPage.languageIn .storyNext .buttonLangLabel{opacity:1 !important;}

#storyIntro.storySkipLanguageFading #storySkipPersistent,
#storyIntro.storySkipLanguageIn #storySkipPersistent{
  opacity:1 !important;
}
#storyIntro #storySkipPersistent .buttonLangLabel{
  opacity:1;
  transition:opacity .55s ease !important;
}
#storyIntro.storySkipLanguageFading #storySkipPersistent .buttonLangLabel{opacity:0 !important;}
#storyIntro.storySkipLanguageIn #storySkipPersistent .buttonLangLabel{opacity:1 !important;}

/* BUS empty state now joins the heading-only translation crossfade. */
.storyPage.storyBus .storyBusLiveEmpty{
  transition:opacity .55s ease !important;
}
.storyPage.storyBus.languageOut .storyBusLiveEmpty{opacity:0 !important;}
.storyPage.storyBus.languageIn .storyBusLiveEmpty{opacity:1 !important;}

/* Specificity overrides for older whole-control language fades. */
html.uiLangFading #enterButton,
html.uiLangFading #rightNow,
html.uiLangFading #audioMute,
html.uiLangFading #weekdayPickerButton,
html.uiLangFading .timePanel button,
html.uiLangFading .activityPanel button,
html.uiLangFading .infoPanel button{
  opacity:1 !important;
}
/* Audio mute's internal state-label change also keeps the physical control present. */
#audioMute.labelFading{opacity:1 !important;}
#audioMute.labelFading .buttonLangLabel{opacity:0 !important;}

/* ============================================================
   V285 — synchronize button-label language fades with page copy
   ============================================================ */
/* Story controls are governed by the story controller's 550ms languageOut /
   languageIn sequence. Do not let the global 500ms UI fade start their labels
   early when the language selector is clicked. */
html.uiLangFading #storyIntro .buttonLangLabel,
html.uiLangFading #loaderAnniversary .buttonLangLabel{
  opacity:1 !important;
}
html.uiLangFading #storyIntro .storyPage.languageOut .buttonLangLabel,
html.uiLangFading #loaderAnniversary .storyPage.languageOut .buttonLangLabel,
html.uiLangFading #storyIntro.storySkipLanguageFading #storySkipPersistent .buttonLangLabel{
  opacity:0 !important;
}

/* BUS persistent SKIP: language changes never fade the physical button shell.
   Only .buttonLangLabel participates in the story language crossfade. */
#storyIntro.storySkipLanguageFading:has(.storyPage.storyBus.current) #storySkipPersistent,
#storyIntro.storySkipLanguageIn:has(.storyPage.storyBus.current) #storySkipPersistent{
  opacity:1 !important;
  pointer-events:auto !important;
}

/* ============================================================
   V286 — lock bilingual ENTER buttons to the longer French label
   ============================================================ */
/* Reserve the intrinsic width of the French label at all times. The hidden
   sizing copy participates in layout but is never painted, so EN/FR label
   crossfades cannot resize either ENTER CORRESPONDANCE button. */
#enterButton::after,
#loaderAnniversary .storyAnniversary .storyNext::after{
  content:"ENTRER DANS CORRESPONDANCE";
  display:block;
  height:0;
  overflow:hidden;
  visibility:hidden;
  white-space:nowrap;
  pointer-events:none;
}

/* ============================================================
   V287 — BUS feed minimum height follows the French empty state
   ============================================================ */
/* JS measures the panel with the longer French empty-state copy at the
   current responsive width. Arrival rows may grow the panel beyond this,
   but it never collapses below that bilingual baseline. */
#storyIntro.active .storyPage.storyBus.current .storyBusLivePanel{
  min-height:var(--story-bus-french-empty-min-height, 0px) !important;
}
.storyBusLivePanel.storyBusEmptyMeasure{
  position:absolute !important;
  visibility:hidden !important;
  pointer-events:none !important;
  opacity:0 !important;
  height:auto !important;
  min-height:0 !important;
  max-height:none !important;
  transition:none !important;
}

/* ============================================================
   V288 — responsive 60 Years / anniversary narrow-screen flow
   Match the BUS chapter strategy: preserve the desktop presentation frame
   while roomy, but abandon fixed-canvas scaling once width/height is cramped.
   ============================================================ */
@media (max-width:900px), (max-height:720px){
  #loaderAnniversary.active{
    overflow-x:hidden !important;
    overflow-y:auto !important;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior-y:contain;
  }

  #loaderAnniversary.active .storyPage.storyAnniversary{
    position:relative !important;
    inset:auto !important;
    left:auto !important;
    top:auto !important;
    width:100% !important;
    height:auto !important;
    min-height:100% !important;
    padding:clamp(72px,12vh,112px) clamp(22px,6vw,54px) clamp(56px,10vh,96px) !important;
    transform:none !important;
    overflow:visible !important;
    align-items:flex-start !important;
    justify-content:flex-start !important;
  }

  #loaderAnniversary.active .storyPage.storyAnniversary .storyCopy{
    width:min(760px,100%) !important;
    max-width:none !important;
    margin:auto !important;
    display:grid !important;
    grid-template-rows:auto auto auto auto !important;
    align-content:start !important;
  }

  #loaderAnniversary.active .storyPage.storyAnniversary .storyTitle{
    min-height:0 !important;
    max-width:100% !important;
    font-size:clamp(2.65rem,13.5vw,5.4rem) !important;
  }

  #loaderAnniversary.active .storyPage.storyAnniversary .storyBody{
    height:auto !important;
    min-height:0 !important;
    max-width:680px !important;
    overflow:visible !important;
  }

  #loaderAnniversary.active .storyPage.storyAnniversary .storyNext{
    margin-top:38px !important;
  }
}


/* ============================================================
   V289 — protect unrevealed NEXT + BUS feed/SKIP entrance stagger
   ============================================================ */
/* A language crossfade must never reveal NEXT before reveal() reaches its
   scheduled 2350ms storyNextReady point. This final guard outranks historical
   languageIn rules while leaving already-revealed NEXT labels free to fade. */
.storyPage.current.reveal:not(.storyNextReady) .storyNext,
#loaderAnniversary.active .storyPage.storyAnniversary.reveal:not(.storyNextReady) .storyNext{
  opacity:0 !important;
  pointer-events:none !important;
}

/* Entering BUS temporarily stages the already-persistent SKIP out of view.
   The controller removes this class at 3450ms: NEXT 2350 -> feed 2900 -> SKIP 3450. */
#storyIntro.storyBusEntrancePending #storySkipPersistent{
  opacity:0 !important;
  pointer-events:none !important;
  transition:opacity .55s ease,background-color .6s ease,color .6s ease,border-color .6s ease !important;
}
