/* ═══════════════════════════════════════════════════════════
   CAC SPORT — core styles
   Design tokens mirror the CAC Budget app so the two feel like
   one product family: navy base, gold accent, Inter + Space Grotesk.
   ═══════════════════════════════════════════════════════════ */

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

:root{
  --bg:#060a14;
  --bg2:#0c1322;
  --bg3:#0f1828;
  --bg4:#15203a;
  --border:rgba(148,163,184,.14);
  --border2:rgba(148,163,184,.3);
  --gold:#f59e0b;
  --gold2:#fbbf24;
  --gold-bg:rgba(245,158,11,.12);
  --gold-border:rgba(245,158,11,.38);
  --gold-light:rgba(245,158,11,.14);
  --green:#34d399;
  --green-bg:rgba(52,211,153,.12);
  --green-border:rgba(52,211,153,.35);
  --red:#f87171;
  --red-bg:rgba(248,113,113,.12);
  --red-border:rgba(248,113,113,.35);
  --blue:#60a5fa;
  --blue-bg:rgba(96,165,250,.12);
  --blue-border:rgba(96,165,250,.35);
  --purple:#a78bfa;
  --text:#e8eefc;
  --text2:#9fb0cc;
  /* 4.75:1 on the darkest card (bg4) and more on the rest. It was
     #64748b — 3.4 to 4.2:1, under the 4.5 a hint at 11px needs, and it
     is the colour of five hundred hints (impeccable audit, 5 Oct 2026). */
  --text3:#7f8ca2;
  --bright:#f1f5f9;
  /* The ring a keyboard user follows, and the edge of a field: both at
     least 3:1 on every surface (WCAG 1.4.11). --border2 stays the soft
     line between cards; a field's edge has to be seen to be found. */
  --focus:var(--gold2);
  --field-border:rgba(148,163,184,.6);
  /* Read by screens that were written before these had names: --warn by
     the climb list, --line by the medical letterbox. Undefined, the one
     drew yellow on white and the other dropped its border altogether. */
  --warn:#fbbf24;
  --line:var(--border);
  /* the podium and the medal table, readable in either theme */
  --medal-silver:#cbd5e1;--medal-silver-bg:rgba(203,213,225,.08);--medal-silver-border:rgba(203,213,225,.3);
  --medal-bronze:#e0904a;--medal-bronze-bg:rgba(224,144,74,.1);--medal-bronze-border:rgba(224,144,74,.35);
  --shadow:0 1px 3px rgba(0,0,0,.4),0 1px 2px rgba(0,0,0,.3);
  --shadow-md:0 8px 28px rgba(0,0,0,.5);
  --glow-gold:0 0 22px rgba(245,158,11,.22);
  --radius:12px;
  --radius-lg:16px;
  /* --z-auth sits BELOW --z-modal on purpose. The sign-in overlay used to
     carry a hard-coded 200, off this scale entirely and above the modal,
     so a dialog opened from that screen rendered behind it — present in
     the DOM, invisible on the glass. Nothing inside the app can compete
     with it at 60: #app-shell is display:none whenever the overlay is up. */
  --z-nav:20; --z-sticky:40; --z-auth:60; --z-modal:100; --z-toast:1000;

  /* Tokens the components ported from the Golazo Sports Manager read,
     mapped onto CAC's own palette and type. CAC has no second display
     face, so the display font is Space Grotesk like the header. */
  --font-body:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;
  --font-display:'Space Grotesk','Inter',sans-serif;
  --display-stretch:normal;
  --primary:var(--gold);
  --accent-fill:var(--gold);
  --accent-fill2:var(--gold2);
  --on-accent:#0b1120;
  --amber:#f59e0b;
  --amber-bg:rgba(245,158,11,.12);
  --amber-border:rgba(245,158,11,.38);
  /* The ported screens name the brand colour --accent and the good-news
     green --ok. Aliases rather than new colours: one palette, two
     vocabularies, so a rule copied either way round still reads right. */
  --accent:var(--gold);
  --accent2:var(--gold2);
  --ok:var(--green);
}

body.light{
  --bg:#eef2f7;--bg2:#ffffff;--bg3:#f8fafc;--bg4:#f1f5f9;
  --border:#e2e8f0;--border2:#cbd5e1;
  /* gold and green a shade deeper than amber-700 / green-700: those were
     4.47 and 4.46:1 on the page grey, a hair under the 4.5 text needs */
  --gold:#a84d08;--gold2:#d97706;--gold-bg:rgba(180,83,9,.09);--gold-border:rgba(180,83,9,.32);--gold-light:#fef3c7;
  --green:#137a3a;--green-bg:rgba(21,128,61,.1);--green-border:rgba(21,128,61,.32);
  --red:#b91c1c;--red-bg:rgba(185,28,28,.1);--red-border:rgba(185,28,28,.32);
  --blue:#1d4ed8;--blue-bg:rgba(29,78,216,.1);--blue-border:rgba(29,78,216,.32);
  --purple:#6d28d9;
  --text:#0f172a;--text2:#475569;--text3:#5b6b80;--bright:#0f172a;
  --focus:#a84d08;--field-border:#7c8a9e;--warn:#a84d08;
  --medal-silver:#4b5563;--medal-silver-bg:rgba(75,85,99,.07);--medal-silver-border:rgba(75,85,99,.28);
  --medal-bronze:#8a4b1d;--medal-bronze-bg:rgba(138,75,29,.08);--medal-bronze-border:rgba(138,75,29,.3);
  --shadow:0 1px 3px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.06);
  --shadow-md:0 6px 20px rgba(0,0,0,.12);
  --glow-gold:0 0 18px rgba(180,83,9,.16);
  --on-accent:#ffffff;
  --amber:#b45309;
  --amber-bg:rgba(180,83,9,.09);
  --amber-border:rgba(180,83,9,.32);
}
body.light{color-scheme:light}
body.light #bgfx{opacity:.35}
body.light #header{background:rgba(15,23,42,.96)}

html{-webkit-text-size-adjust:100%}
body{
  font-family:'Inter',-apple-system,BlinkMacSystemFont,sans-serif;
  background:var(--bg);color:var(--text);
  /* Tells the browser which theme its own widgets should follow. Without it
     the native date/time pickers keep their light styling, and the little
     calendar glyph is painted near-black — invisible on a dark field. */
  color-scheme:dark;
  display:flex;flex-direction:column;height:100vh;overflow:hidden;
  font-size:15px;line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
@supports (height:100dvh){body{height:100dvh}}

/* ── THE BACKGROUND ────────────────────────────────────────────
   Two soft lights and a faint grid, still. They drifted for ever behind
   every screen — two viewport-sized blurred layers moving on a phone in
   the field, for a glow nobody watches (impeccable audit, 5 Oct 2026). */
#bgfx{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
#bgfx::before{content:'';position:absolute;width:65vw;height:65vw;border-radius:50%;top:-25vw;right:-18vw;
  background:radial-gradient(circle,rgba(245,158,11,.13),transparent 62%);filter:blur(50px)}
#bgfx::after{content:'';position:absolute;width:55vw;height:55vw;border-radius:50%;bottom:-22vw;left:-14vw;
  background:radial-gradient(circle,rgba(37,99,235,.16),transparent 62%);filter:blur(55px)}
#bgfx .grid-lines{position:absolute;inset:0;
  background-image:linear-gradient(rgba(148,163,184,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(148,163,184,.05) 1px,transparent 1px);
  background-size:46px 46px;-webkit-mask-image:radial-gradient(ellipse at 50% 30%,#000 30%,transparent 75%);
  mask-image:radial-gradient(ellipse at 50% 30%,#000 30%,transparent 75%)}
@keyframes glowPulse{0%,100%{opacity:.65}50%{opacity:1}}
@keyframes livePulse{0%,100%{box-shadow:0 0 0 0 currentColor;opacity:1}70%{box-shadow:0 0 0 9px transparent;opacity:.85}}
@keyframes fadeUp{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes shimmer{0%{background-position:-200% 0}100%{background-position:200% 0}}
@keyframes spin{to{transform:rotate(360deg)}}

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

/* ── THE BOOT SPLASH ───────────────────────────────────────────
   Visible from the first paint, gone as soon as a real screen is up —
   see bootSplashDone(). It sits on the page's own background so the
   swap is a fade rather than a flash, and it says nothing: the reader's
   language is not known until JavaScript has run, and a wordless mark
   is honest in all three. */
/* Navy in BOTH themes, like the sign-in card and like the /s/ waiting page
   the share links pass through: this is the app's front door, and a light
   theme's near-white page here is the blank white sheet this splash exists
   to remove. */
#boot-splash{position:fixed;inset:0;z-index:calc(var(--z-toast) + 1);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:22px;
  padding:24px;background:#060a14;
  background-image:radial-gradient(circle at 50% 0,#15203a 0,#060a14 62%);
  transition:opacity .25s ease}
/* CAC's mark is drawn for a light ground — on the navy it gets the same
   white plate the header gives it. */
#boot-splash img{width:170px;max-width:60vw;height:auto;
  background:#fff;border-radius:12px;padding:10px 16px}
.boot-spin{width:26px;height:26px;border-radius:50%;
  border:3px solid rgba(245,158,11,.14);
  border-top-color:#f59e0b;animation:boot-spin .8s linear infinite}
@keyframes boot-spin{to{transform:rotate(360deg)}}
/* THE MAKER, UNDER THE EVENT'S OWN MARK. A signature, not a headline, and
   it reads in that order: what is moving, whose race it is, who built the
   thing. The ID selector above sets a white plate on every image in here —
   this one is already white artwork for exactly this navy, so it takes the
   plate back off. 124 px clears the 120 px floor the Noribel guidelines
   set for the horizontal lockup. */
.boot-maker{display:flex;flex-direction:column;align-items:center;gap:6px;margin-top:-8px}
.boot-maker span{font-size:9px;font-weight:700;text-transform:uppercase;
  letter-spacing:.1em;color:rgba(255,255,255,.42)}
#boot-splash .boot-maker img{width:124px;max-width:46vw;height:auto;
  background:none;border-radius:0;padding:0;opacity:.82}
/* Faded out rather than removed, so a repaint cannot bring it back. */
body.booted #boot-splash{opacity:0;pointer-events:none;visibility:hidden}
@media (prefers-reduced-motion:reduce){
  .boot-spin{animation:none}
  #boot-splash{transition:none}
}

/* ── SKIP LINK ─────────────────────────────────────────────── */
.skip-link{position:absolute;left:-9999px;top:0;z-index:var(--z-toast);background:var(--gold);color:#0b1120;
  padding:10px 16px;border-radius:0 0 8px 0;font-weight:700;font-size:14px}
.skip-link:focus{left:0}

/* ── FOCUS ─────────────────────────────────────────────────── */
:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}
/* THE FLOOR UNDER EVERY FIELD. Some twenty rules across the sheets set
   `outline:none` on a field and swapped in a 1px border tint — orange on
   white, 2:1, a focus nobody could find (impeccable audit, 5 Oct 2026).
   Whatever a component does on focus, a field, a select, a row or a
   span that acts as a button keeps a ring a keyboard user can follow. */
:is(input,select,textarea,[contenteditable="true"],[role="button"],[tabindex]:not([tabindex="-1"])):focus-visible{
  outline:2px solid var(--focus)!important;outline-offset:1px!important}

/* ── HEADER ────────────────────────────────────────────────── */
#header{position:relative;z-index:var(--z-sticky);
  background:rgba(8,12,24,.82);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  min-height:62px;display:flex;align-items:center;padding:0 16px;gap:14px;flex-shrink:0;
  border-bottom:1px solid var(--gold-border);box-shadow:0 4px 24px rgba(0,0,0,.45);
  /* Longhands, not the `padding` shorthand. A shorthand further down this
     file resets padding-top to zero along with the sides, which is how the
     inset went missing on exactly the phones that have a cutout. */
  padding-top:env(safe-area-inset-top,0px);padding-bottom:0;
  padding-left:max(16px,env(safe-area-inset-left,0px));
  padding-right:max(16px,env(safe-area-inset-right,0px))}
.hdr-cac{display:flex;align-items:center;gap:10px;flex-shrink:0}
.hdr-cac img{height:40px;width:auto;object-fit:contain;background:#fff;border-radius:8px;padding:4px 8px}
/* Back to the portal: an arrow and a house, sat between the mark and
   the title. Icon-only on purpose — the bar is the one strip of the app
   with no room for a word. */
.hdr-home{display:flex;align-items:center;justify-content:center;gap:1px;flex-shrink:0;
  width:38px;height:38px;padding:0;border-radius:9px;cursor:pointer;
  border:1px solid rgba(245,158,11,.34);background:rgba(245,158,11,.12);
  color:var(--gold);transition:background .16s,border-color .16s,transform .12s}
.hdr-home:hover{background:rgba(245,158,11,.22);border-color:rgba(245,158,11,.6);
  transform:translateY(-1px)}
.hdr-home svg{width:15px;height:15px;stroke-width:2.4;display:block}
.hdr-home svg:first-child{width:12px;height:12px;margin-right:-1px}
.hdr-divider{width:1px;height:32px;background:rgba(255,255,255,.18);flex-shrink:0}
.hdr-title{flex:1;min-width:0}
.hdr-title h1{font-family:'Space Grotesk','Inter',sans-serif;font-size:15px;font-weight:700;color:#fff;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;letter-spacing:-.2px}
.hdr-title p{font-size:12px;color:rgba(255,255,255,.55);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* THE MAKER'S CREDIT. Noribel's white horizontal lockup — this bar is dark
   in both themes, and the guidelines put the white variant on any dark
   ground. 22 px of height is ~139 px of width on this artwork, over the
   120 px floor the guidelines set for the horizontal lockup; the 10 px gap
   is the clear space they ask for (the height of the N). */
/* A BUTTON THAT LOOKS LIKE A SIGNATURE. It opens the note about who
   built this (see openMakerModal() in js/app.js), so it has to be a real
   button for a keyboard and a screen reader — but a bordered control in
   the middle of the bar would read as an action rather than a credit. It
   answers to a hover and a focus ring and nothing else. */
.hdr-maker{display:flex;align-items:center;gap:10px;flex-shrink:0;
  background:none;border:0;padding:4px 6px;margin:0;border-radius:8px;
  font:inherit;cursor:pointer;transition:background .16s,opacity .16s}
.hdr-maker:hover{background:rgba(255,255,255,.08)}
.hdr-maker:focus-visible{outline:2px solid var(--gold2);outline-offset:1px}
.hdr-maker-label{font-size:9px;text-transform:uppercase;letter-spacing:.09em;color:rgba(255,255,255,.4);font-weight:700}
.hdr-maker img{height:22px;width:auto;object-fit:contain}
.hdr-actions{display:flex;align-items:center;gap:8px;flex-shrink:0}

/* Follows the theme, because these sit on cards far more often than on the
   header. The white-on-transparent version below is scoped to the navy bars
   that actually need it — as a global default it turned every pencil and
   trash button on a light card invisible, which read as "you cannot delete
   this". */
.icon-btn{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;
  border-radius:9px;border:1px solid var(--border2);background:var(--bg3);
  color:var(--text2);cursor:pointer;transition:background .16s,border-color .16s,color .16s,transform .16s}
.icon-btn:hover{background:var(--bg4);border-color:var(--gold-border);color:var(--text)}
.icon-btn:active{transform:scale(.94)}
.icon-btn svg{width:19px;height:19px}

/* The header and the read-only banner stay navy in both themes. */
#header .icon-btn,#ro-banner .icon-btn{border-color:rgba(255,255,255,.18);
  background:rgba(255,255,255,.07);color:rgba(255,255,255,.88)}
#header .icon-btn:hover,#ro-banner .icon-btn:hover{background:rgba(255,255,255,.15);
  border-color:rgba(255,255,255,.34);color:#fff}
/* On the light theme the page around the navy bar is bright, and seven
   percent white reads as nothing beside it — the eye is adapted to the
   white page, not to the bar. Same buttons, a step brighter. */
body.light #header .icon-btn,body.light #ro-banner .icon-btn{
  background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.34);color:#fff}
body.light #header .icon-btn:hover,body.light #ro-banner .icon-btn:hover{
  background:rgba(255,255,255,.26);border-color:rgba(255,255,255,.52)}
/* The gear is a navigation item that happens to live in the header, so it
   shows the same gold "you are here" state the sidebar items do. */
#header .icon-btn.active{background:linear-gradient(135deg,var(--gold),var(--gold2));
  border-color:var(--gold2);color:#0b1120}
/* The header is navy in both themes, so the menu button carries the gold
   accent rather than a white-on-white outline that vanishes in dark mode. */
#header #menuBtn{display:none;border-color:var(--gold-border);background:rgba(245,158,11,.16);color:#fbbf24}
#header #menuBtn:hover{background:rgba(245,158,11,.28);border-color:#f59e0b}
#menuBtn svg{width:23px;height:23px;stroke-width:2.6}

/* Language toggle */
.lang-tabs{display:flex;background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.18);border-radius:9px;padding:3px;gap:2px}
.lang-btn{min-width:40px;min-height:36px;padding:0 10px;border-radius:7px;border:none;background:none;cursor:pointer;
  font-family:inherit;font-size:12px;font-weight:800;letter-spacing:.04em;color:rgba(255,255,255,.6);transition:all .16s}
.lang-btn.active{background:linear-gradient(135deg,var(--gold),var(--gold2));color:#0b1120}
.lang-btn:hover:not(.active){background:rgba(255,255,255,.12);color:#fff}

/* THE SAME STRIP, ON A CARD. The header's colours are white-on-navy, and
   in the settings card — white in the light theme — they were a row of
   invisible buttons. Scoped rather than made theme-aware wholesale,
   because the bar and the sign-in screen still want the navy version. */
.set-lang{flex:0 0 auto;background:var(--bg4);border-color:var(--border2)}
.set-lang .lang-btn{color:var(--text2)}
.set-lang .lang-btn:hover:not(.active){background:var(--bg3);color:var(--text)}
.set-lang .lang-btn.active{background:linear-gradient(135deg,var(--accent-fill),var(--accent-fill2));
  color:var(--on-accent)}

/* ── LAYOUT ──────────────────────────────────────────────────
   Every link in this chain needs `min-height:0`. A flex item defaults to
   `min-height:auto`, meaning it refuses to shrink below its content — so
   without these the shell grows past the viewport and #content never
   overflows, which reads as "the page won't scroll".
   ──────────────────────────────────────────────────────────── */
#app-shell{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;overflow:hidden}
#body{display:flex;flex:1 1 auto;min-height:0;overflow:hidden;position:relative;z-index:1}
/* CLIPPED, NOT MERELY HIDDEN. A box with overflow:hidden still scrolls
   when a script or a focus asks it to: adding a road work focused
   something past the right edge and slid the whole frame sideways — the
   sidebar half gone, its orange tab hanging over the work (Janny, 25 Sep
   2026). overflow:clip cannot be scrolled at all; boot() puts back any
   browser that still manages it. */
#app-shell,#body,#main{overflow:clip}

/* ── SIDEBAR ───────────────────────────────────────────────── */
#sidebar{width:236px;background:var(--bg2);border-right:1px solid var(--border);
  display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto}
.nav{flex:1;padding:10px 0}
/* The section heading is the fold control for its own group — see
   renderNav(). Styled to stay the quiet label it always was: the chevron
   only announces itself on hover, and the tap target is the full width
   so a thumb on a phone does not have to find the 14 px arrow. */
.nav-section{display:flex;align-items:center;gap:6px;width:100%;
  padding:12px 14px 4px;font-size:10px;font-weight:800;text-transform:uppercase;
  letter-spacing:.1em;color:var(--text3);background:none;border:none;
  font-family:inherit;text-align:left;cursor:pointer;user-select:none}
.nav-section:hover{color:var(--text2)}
.nav-sec-lbl{flex:1;min-width:0}
.nav-section svg{width:14px;height:14px;flex-shrink:0;opacity:.5;
  transition:transform .18s ease,opacity .16s}
.nav-section:hover svg{opacity:1}
.nav-section.folded svg{transform:rotate(-90deg)}
/* Folded away rather than merely hidden: display:none also takes the
   buttons out of the tab order, which is what a collapsed group should
   do for a keyboard as well as for the eye. */
.nav-group.folded{display:none}
.nav-item{display:flex;align-items:center;gap:10px;padding:11px 12px;border-radius:9px;margin:2px 8px;
  cursor:pointer;font-size:14px;font-weight:500;color:var(--text2);
  transition:background .16s,color .16s;user-select:none;border:none;background:none;
  font-family:inherit;width:calc(100% - 16px);text-align:left;min-height:44px}
.nav-item:hover{background:var(--bg4);color:var(--text)}
.nav-item.active{background:var(--gold-bg);color:var(--gold);font-weight:700}
.nav-item.active::before{content:'';position:absolute;left:0;width:3px;height:22px;background:var(--gold);border-radius:0 3px 3px 0}
.nav-item{position:relative}
.nav-item svg{width:18px;height:18px;flex-shrink:0}
.nav-badge{margin-left:auto;font-size:10px;font-weight:800;padding:2px 7px;border-radius:20px;
  background:var(--red-bg);color:var(--red);border:1px solid var(--red-border)}

/* ── SIDEBAR, COLLAPSED TO ICONS ────────────────────────────────
   Twenty-odd screens is a lot of width to keep spent once somebody
   knows the icons by heart. Collapsed, the labels and the section
   headings disappear and the native title="" attribute on each
   .nav-item stands in for them — a real tooltip, not a custom one,
   so it costs nothing here and works the same as any other title in
   the browser. The edition chip hides rather than truncates: picking
   an edition from a sliver of its name would be worse than asking for
   the sidebar back first. */
#sidebar.collapsed{width:60px}
#sidebar.collapsed .nav-sec-lbl,#sidebar.collapsed .nav-section svg,#sidebar.collapsed .nav-item span,
#sidebar.collapsed .nav-badge{display:none}
/* With its label and chevron gone, the heading was an empty block of
   padding — the groups ran into one another with nothing to say where
   one ended. A hairline stands in for the word: still the group's own
   fold button (hence the padding around it, so there is something to
   press), dotted rather than solid once the group is folded shut. */
#sidebar.collapsed .nav-section{padding:7px 12px;justify-content:center;min-height:0}
#sidebar.collapsed .nav-section::after{content:'';display:block;width:100%;height:0;
  border-top:1px solid var(--border);border-radius:1px;transition:border-color .16s}
#sidebar.collapsed .nav-section:hover::after{border-top-color:var(--text3)}
#sidebar.collapsed .nav-section.folded::after{border-top-style:dotted}
#sidebar.collapsed .nav-item{justify-content:center;padding:11px 0;margin:2px 8px;gap:0}
#sidebar.collapsed .nav-item.active::before{border-radius:0 3px 3px 0}
/* At the top, under the edition chip, rather than at the foot of a
   list that can run to two screens tall — a control worth reaching
   without scrolling past every section to find it. Carries the same
   gradient as .btn.primary so it reads as a real action, not another
   quiet row of chrome. */
/* NEAR THE TOP, NOT HALF WAY DOWN. Centred vertically it landed in the
   middle of whatever screen was open — over a map, over a panel of
   settings, over a table — and it read as part of that screen rather
   than as the edge of the sidebar. Up beside the first row it is where
   the sidebar begins, which is what it belongs to. */
#nav-collapse-toggle{position:absolute;top:22px;
  left:236px;z-index:5;width:22px;height:52px;padding:0;
  display:flex;align-items:center;justify-content:center;
  background:linear-gradient(135deg,var(--accent-fill),var(--accent-fill2));
  border:0;border-radius:0 8px 8px 0;color:var(--on-accent);
  cursor:pointer;font-family:inherit;
  box-shadow:0 2px 8px rgba(0,0,0,.22);
  transition:left .2s ease,filter .16s}
#nav-collapse-toggle:hover{filter:brightness(1.1)}
#sidebar.collapsed ~ #nav-collapse-toggle{left:60px}
#nav-collapse-toggle svg{width:14px;height:14px;flex-shrink:0;stroke-width:2.6;
  transition:transform .18s ease}
#sidebar.collapsed #nav-collapse-toggle svg{transform:rotate(180deg)}
#sidebar.collapsed #nav-collapse-toggle{margin-left:8px;margin-right:8px;padding:9px 0}
#sidebar.collapsed #nav-collapse-toggle span{display:none}

/* ── MAIN ──────────────────────────────────────────────────── */
#main{flex:1 1 auto;display:flex;flex-direction:column;overflow:hidden;min-width:0;min-height:0}
#subbar{background:var(--bg2);border-bottom:1px solid var(--border);padding:8px 18px;
  min-height:54px;display:flex;align-items:center;gap:10px;flex-shrink:0;overflow-x:auto;scrollbar-width:none}
#subbar::-webkit-scrollbar{display:none}
#content{flex:1 1 auto;min-height:0;overflow-y:auto;padding:22px 24px 60px;
  -webkit-overflow-scrolling:touch}
.view{display:none;animation:fadeUp .28s ease-out}
.view.active{display:block}

/* CLEAR OF THE COLLAPSE HANDLE. That handle used to sit half way down
   the sidebar, where it met nothing; up at the top it lands beside the
   first thing every screen draws, and a title with a tab through its
   first letter is a title nobody finishes reading. Every heading in
   the app starts a handle's width in, so none of them has to know the
   handle exists. The max-width grows by the same amount, or the
   indent would quietly cost every heading a word. */
.page-hd{margin-bottom:20px;max-width:calc(72ch + 30px);padding-left:30px}
/* Except on a phone, where the sidebar is a drawer and there is no
   handle to clear. */
@media (max-width:860px){ .page-hd{padding-left:0;max-width:72ch} }
.page-hd h2{font-family:var(--font-display);font-stretch:var(--display-stretch);font-size:23px;font-weight:700;
  color:var(--text);margin-bottom:3px;letter-spacing:-.4px}
.page-hd p{font-size:14px;color:var(--text2)}

/* ── DAY CALENDAR STRIP ────────────────────────────────────────
   Schedule and Road closures both mount this, so the two pages navigate
   identically. Months are labelled because an edition can straddle one.
   ──────────────────────────────────────────────────────────── */
.day-cal{display:flex;flex-wrap:wrap;gap:10px;align-items:stretch;
  background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);
  padding:10px 12px;box-shadow:var(--shadow)}
.dc-month{display:flex;flex-direction:column;gap:6px;min-width:0}
.dc-month+.dc-month{padding-left:12px;border-left:1px solid var(--border)}
.dc-month-hd{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.09em;
  color:var(--gold2);white-space:nowrap}
.dc-month-hd span{color:var(--text3);font-weight:700}
body.light .dc-month-hd{color:var(--gold)}
.dc-days{display:flex;gap:6px;flex-wrap:wrap}

.dc-day{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;min-width:58px;padding:7px 9px 6px;border-radius:10px;cursor:pointer;font-family:inherit;
  border:1px solid var(--border);background:var(--bg3);color:var(--text2);
  transition:background .16s,border-color .16s,color .16s}
.dc-day:hover:not(.active){background:var(--bg4);border-color:var(--border2);color:var(--text)}
.dc-wd{font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.07em;opacity:.75}
.dc-num{font-size:20px;font-weight:800;line-height:1.05;font-family:'Space Grotesk','Inter',sans-serif;
  color:var(--text);font-variant-numeric:tabular-nums}
.dc-meta{display:flex;align-items:center;gap:7px;font-size:10px;font-weight:700;opacity:.8;
  white-space:nowrap;min-height:13px;margin-top:1px;font-variant-numeric:tabular-nums}
.dc-bit{display:inline-flex;align-items:center;gap:2px}
.dc-bit svg{width:10px;height:10px;stroke-width:2.4}
.dc-day.is-rest .dc-num{color:var(--text2)}
.dc-day.active{background:linear-gradient(135deg,var(--gold),var(--gold2));border-color:transparent;
  color:#0b1120;box-shadow:var(--glow-gold)}
.dc-day.active .dc-num{color:#0b1120}
.dc-day.is-today::after{content:'';width:6px;height:6px;border-radius:50%;background:var(--red);
  position:absolute;top:4px;right:4px}
@media (max-width:640px){
  /* THE FLOOR, NOT THE SIZE. min-width used to be what a day tile
     measured, and seven of them plus the "All" tile came to one pixel
     more than a phone's row — so the last day dropped onto a line of its
     own and the strip read as two weeks. The tiles grow into whatever
     row there is instead (flex:1 1 auto below), and this is only how
     small they are allowed to get before the strip starts to scroll. */
  .dc-day{min-width:40px;padding:6px 5px;flex:1 1 auto}
  .dc-num{font-size:17px}
  .dc-month+.dc-month{padding-left:0;border-left:none}
}

/* The schedule's top bar: the day calendar on the left, every action
   gathered at the top right — see renderSchedule(). */
.sched-topbar{display:flex;align-items:flex-start;justify-content:space-between;
  gap:14px;flex-wrap:wrap;margin-bottom:18px}
.sched-topbar-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;
  align-items:center;margin-left:auto}

/* The topbar's two menus: everything that adds behind the green +,
   everything that exports behind one button. Anchored to their button and
   right-aligned, so the rightmost menu never leaves the viewport. */
.sched-menu{position:relative}
.sched-menu-list{position:absolute;top:calc(100% + 6px);right:0;z-index:60;min-width:230px;max-width:calc(100vw - 32px);
  background:var(--bg2);border:1px solid var(--border2);border-radius:12px;padding:6px;
  box-shadow:0 12px 32px rgba(0,0,0,.18);display:flex;flex-direction:column;gap:2px}
.sched-menu-list>button{display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  background:none;border:0;border-radius:8px;padding:9px 12px;font-size:13px;font-weight:600;
  color:var(--text);cursor:pointer;font-family:inherit}
.sched-menu-list>button:hover{background:var(--bg3)}
/* On a contact card the menu holds two words, not a toolbar. */
.sched-menu-list.compact{min-width:150px}

/* ── A MENU THAT IS NOT A MENU ON A BIG SCREEN ──
   Two items behind a ⋮ is two items nobody finds: a menu earns its
   keep when it holds more than fits, and on a desktop header two
   buttons fit. On a phone they do not, so there the ⋮ stays exactly as
   it was. Marked per menu rather than applied to all of them — the
   schedule's own menu holds eight things and belongs behind a button. */
@media (min-width:861px){
  .sched-menu.flat-desk > [data-menu-btn]{display:none}
  .sched-menu.flat-desk > .sched-menu-list,
  .sched-menu.flat-desk > .sched-menu-list[hidden]{
    /* IN A ROW. The menu stacks its items in a column, which is what a
       menu is; unfolded onto the header it is a toolbar, and a toolbar
       that came out as a vertical stack pushed the page down by three
       buttons. */
    display:flex!important;flex-direction:row;align-items:center;
    position:static;min-width:0;max-width:none;gap:8px;
    background:none;border:0;box-shadow:none;padding:0;border-radius:0}
  .sched-menu.flat-desk > .sched-menu-list > button{
    width:auto;white-space:nowrap;border:1px solid var(--border);border-radius:9px;
    background:var(--bg2);padding:7px 12px;font-size:12.5px;font-weight:700}
  .sched-menu.flat-desk > .sched-menu-list > button:hover{border-color:var(--gold-border)}

  /* ── AND WHERE THE WORDS DO NOT FIT, THE SHAPES ──
     Three names beside a heading is a sentence of toolbar; the same three
     as icons is a row the hand goes to. The name stays on the button —
     out of sight, in the tooltip, and read aloud by anything that reads
     the page — so nothing is lost by not printing it. */
  .sched-menu.flat-desk.icon-desk > .sched-menu-list > button{
    padding:0;width:34px;height:34px;justify-content:center}
  .sched-menu.flat-desk.icon-desk > .sched-menu-list > button > span{
    position:absolute;width:1px;height:1px;padding:0;margin:-1px;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
}

/* ── SCHEDULE TWO-COLUMN LAYOUT ────────────────────────────────
   Races carry wide tables and take the flexible column; accreditation
   and the day's other activities are narrow blocks that used to sit
   below the fold with a half-empty page beside them.
   ──────────────────────────────────────────────────────────── */
.sched-layout{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:16px;align-items:start}
.sched-main{min-width:0}
.sched-side{min-width:0;position:sticky;top:0}
.sched-side .card:last-child{margin-bottom:0}
@media (max-width:1180px){
  .sched-layout{grid-template-columns:1fr}
  .sched-side{position:static}
}

/* ── BUTTONS ───────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;min-height:44px;padding:0 16px;
  border-radius:9px;font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;
  border:1px solid var(--border2);background:var(--bg3);color:var(--text);
  transition:background .16s,border-color .16s,transform .16s;white-space:nowrap}
.btn:hover{background:var(--bg4);border-color:var(--gold-border)}
.btn:active{transform:scale(.97)}
.btn:disabled{opacity:.45;cursor:not-allowed;transform:none}
.btn svg{width:16px;height:16px}
.btn.primary{background:linear-gradient(135deg,var(--gold),var(--gold2));border-color:transparent;color:#0b1120;font-weight:700}
.btn.primary:hover{filter:brightness(1.08)}
.btn.danger{color:var(--red);border-color:var(--red-border);background:var(--red-bg)}
.btn.sm{min-height:36px;padding:0 11px;font-size:12px}
.btn.ghost{background:none;border-color:transparent;color:var(--text2)}
.btn.ghost:hover{background:var(--bg4);color:var(--text)}
.btn .spin{width:14px;height:14px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:spin .7s linear infinite}

/* ── CARDS ─────────────────────────────────────────────────── */
.card{background-color:var(--bg2);background-image:linear-gradient(160deg,rgba(255,255,255,.045),rgba(255,255,255,.012) 55%);
  border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);
  backdrop-filter:blur(8px);padding:18px 20px}
.card h3{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--text);margin-bottom:10px}
.card h3 svg, h3 svg{width:18px!important;height:18px!important;flex-shrink:0;vertical-align:middle;display:inline-block}
.card .c-sub{font-size:12px;color:var(--text2);margin-bottom:14px}

/* ── STAT CARDS ────────────────────────────────────────────── */
.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px,100%),1fr));gap:16px;margin-bottom:24px;margin-top:24px}
.stat-card{position:relative;overflow:hidden;
  background-color:var(--bg2);background-image:linear-gradient(160deg,rgba(255,255,255,.045),rgba(255,255,255,.012) 55%);
  border:1px solid var(--border);border-radius:var(--radius);padding:18px 20px;box-shadow:var(--shadow);
  transition:transform .25s,box-shadow .25s,border-color .25s;backdrop-filter:blur(8px)}
body.light .stat-card{background:#fff;backdrop-filter:none}
.stat-card:hover{box-shadow:var(--shadow-md);transform:translateY(-3px);border-color:var(--gold-border)}
.stat-card::before{content:'';position:absolute;top:0;left:0;right:0;height:3px;animation:glowPulse 3s ease-in-out infinite}
.stat-card.c-gold::before{background:linear-gradient(90deg,#d97706,#f59e0b)}
.stat-card.c-green::before{background:linear-gradient(90deg,#16a34a,#22c55e)}
.stat-card.c-blue::before{background:linear-gradient(90deg,#2563eb,#3b82f6)}
.stat-card.c-red::before{background:linear-gradient(90deg,#dc2626,#ef4444)}
.stat-card.c-purple::before{background:linear-gradient(90deg,#7c3aed,#a78bfa)}
.stat-label{display:flex;align-items:center;gap:6px;font-size:10px;font-weight:800;text-transform:uppercase;
  letter-spacing:.08em;color:var(--text3);margin-bottom:9px}
.stat-label svg{width:13px;height:13px}
.stat-val{font-family:'Space Grotesk','Inter',sans-serif;font-size:26px;font-weight:700;color:var(--bright);
  letter-spacing:-.6px;line-height:1.1;margin-bottom:4px;font-variant-numeric:tabular-nums}
.stat-val.c-red{color:var(--red)} .stat-val.c-green{color:var(--green)} .stat-val.c-gold{color:var(--gold2)}
.stat-val.c-purple{color:var(--purple)}
body.light .stat-val.c-gold{color:var(--gold)}
.stat-sec{font-size:12px;color:var(--text2)}

/* ── DASHBOARD TOP BAND (map + road status) ────────────────── */
.dash-top{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:16px;margin-bottom:24px;
  align-items:stretch}
.dash-top.no-map{grid-template-columns:1fr}
.dash-top > .card{display:flex;flex-direction:column;min-width:0}
.dash-top .mini-map-card .mini-map-shell{flex:1}
/* The status list scrolls inside the card rather than stretching it past the
   map — an edition can have more zones than the map is tall. */
.dash-top .rs-list{flex:1;overflow-y:auto;min-height:0}

/* ── LIVE ROAD STATUS ──────────────────────────────────────── */
.road-status{margin-bottom:0}
.rs-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:9px;
  border:1px solid transparent;margin-bottom:6px}
.rs-row:last-child{margin-bottom:0}
.rs-row.closed{background:var(--red-bg);border-color:var(--red-border)}
.rs-row.open{background:var(--green-bg);border-color:var(--green-border)}

.rs-pill{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;min-width:88px;
  padding:4px 10px;border-radius:20px;border:1px solid;
  font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.rs-row.closed .rs-pill{background:rgba(248,113,113,.16);border-color:var(--red-border);color:var(--red)}
.rs-row.open   .rs-pill{background:rgba(52,211,153,.14);border-color:var(--green-border);color:var(--green)}
body.light .rs-row.closed .rs-pill{background:rgba(185,28,28,.1)}
body.light .rs-row.open   .rs-pill{background:rgba(21,128,61,.1)}
.rs-pill svg{width:12px;height:12px;flex-shrink:0}

.rs-zone{font-size:14px;font-weight:700;color:var(--text);flex-shrink:0}
.rs-meta{flex:1;min-width:0;text-align:right;font-size:12px;color:var(--text2);line-height:1.4}
.rs-meta strong{color:var(--text);font-variant-numeric:tabular-nums;font-weight:700}

/* Beside the map the column is narrow, so the detail drops under the pill. */
.dash-top:not(.no-map) .rs-row{flex-wrap:wrap;gap:6px 10px}
.dash-top:not(.no-map) .rs-meta{flex-basis:100%;text-align:left}

@media (max-width:480px){
  /* minmax(0,1fr), never a bare 1fr: `1fr` is minmax(AUTO,1fr), so one
     wide child — a favourite's name and its chips — stretches the track
     past the screen and the whole page scrolls sideways. */
  .stat-grid{grid-template-columns:minmax(0,1fr)}
  .hdr-title p{display:none}
}

/* ── FEDERATIONS & MATRIX STYLING ──────────────────────────── */
.fed-stats-row { display: flex; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.fed-matrix-table { width: 100%; border-collapse: collapse; font-size: 13px; text-align: left; }
.fed-matrix-table th { padding: 12px 10px; background: var(--bg3); color: var(--text2); font-weight: 700; font-size: 11px; text-transform: uppercase; border-bottom: 2px solid var(--border); white-space: nowrap; }
.fed-matrix-table td { padding: 10px; border-bottom: 1px solid var(--border); }
.fed-matrix-table td.has-riders { font-weight: 700; color: var(--text); }
.fed-matrix-table td.zero-riders { color: var(--text3); }
.fed-matrix-table tr.total-row td { background: var(--bg3); border-top: 2px solid var(--border); font-weight: 800; }

/* ── MEDAL TABLE STYLING ────────────────────────────────────── */
.medal-table th { text-align: center; }
.medal-table td { text-align: center; }
.medal-table td:nth-child(2) { text-align: left; }

/* ── ANIMATIONS ────────────────────────────────────────────── */
@keyframes pulse {
  0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(244, 63, 94, 0.7); }
  70% { transform: scale(1.15); box-shadow: 0 0 0 12px rgba(244, 63, 94, 0); }
  100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(244, 63, 94, 0); }
}

@media (max-width:1100px){
  .dash-top{grid-template-columns:1fr}
  .dash-top:not(.no-map) .rs-row{flex-wrap:nowrap}
  .dash-top:not(.no-map) .rs-meta{flex-basis:auto;text-align:right}
}
@media (max-width:640px){
  .rs-row,.dash-top:not(.no-map) .rs-row{flex-wrap:wrap;gap:8px 10px}
  .rs-meta,.dash-top:not(.no-map) .rs-meta{flex-basis:100%;text-align:left}
}

/* ── CLOSURE TIMELINE (GANTT) ──────────────────────────────── */
/* HOW WIDE THE NAMES ARE, IN ONE PLACE. The column, the axis above it,
   every gridline and the now-marker all measure from the same edge, and
   that number used to be written as 120 in five places — three of them in
   js/views.js. One custom property, and a narrow screen changes it once. */
.tl-wrap{--tl-lab:210px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--radius);
  padding:18px 20px 8px;box-shadow:var(--shadow);margin-bottom:18px;overflow:hidden}
.tl-head{display:flex;align-items:baseline;gap:10px;margin-bottom:4px;flex-wrap:wrap}
.tl-head h3{font-family:'Space Grotesk','Inter',sans-serif;font-size:16px;font-weight:700;color:var(--text);
  text-transform:none;letter-spacing:-.2px}
/* What every race of the day is called, once, beside the day's own
   name — see tlCommonName() in js/views.js. */
.tl-head .tl-what{font-size:13px;font-weight:700;color:var(--text2)}
.tl-head .tl-date{font-size:12px;color:var(--text2)}
.tl-scroll{overflow-x:auto;padding-bottom:6px;-webkit-overflow-scrolling:touch}
.tl-inner{min-width:760px;position:relative}

.tl-axis{display:flex;position:relative;height:22px;margin-left:var(--tl-lab);
  border-bottom:1px solid var(--border)}
.tl-tick{position:absolute;top:0;font-size:10px;font-weight:700;color:var(--text3);
  transform:translateX(-50%);font-variant-numeric:tabular-nums;white-space:nowrap}
.tl-gridline{position:absolute;top:22px;bottom:0;width:1px;background:var(--border);pointer-events:none}

/* ── A COLUMN OF NAMES, AND THE DAY BESIDE IT ──
   The column was 120 px, and what goes in it is every race the closure
   covers, joined: "Individual Time Trial — Women Elite & Women U23 ·
   Individual Time Trial — Men Elite & Men U23". In 120 px that came out
   as nine lines of one or two words, and the row grew until the bar it
   belongs to was a stripe somewhere beside a paragraph.

   The names are short now — what every race of the day is called is said
   once in the heading, see tlCommonName() — so the column can hold them:
   210 px, and anything still too long falls to a second line and stops
   there. A gantt is read down the left edge and across the bars, and
   both of those want a straight edge to start from. */
.tl-row{display:flex;align-items:center;min-height:40px;position:relative}
.tl-label{width:var(--tl-lab);flex:0 0 var(--tl-lab);padding-right:14px;
  font-size:12.5px;font-weight:700;color:var(--text);line-height:1.25}
.tl-name{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.tl-label small{display:block;font-size:9.5px;font-weight:700;color:var(--text3);
  text-transform:uppercase;letter-spacing:.05em;margin-top:1px}
/* ── AND THE TWO KINDS OF ROW, SAID APART ──
   A closure and a race are two different questions — when is this road
   shut, and when is this race on it — and they were stacked in one run
   of rows that read as one list. A line of small capitals over each
   group costs a row and answers it. */
.tl-group{font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;
  color:var(--text3);margin:14px 0 4px;padding-left:1px}
.tl-group:first-of-type{margin-top:4px}
.tl-track{flex:1;position:relative;height:32px;background:var(--bg3);border-radius:7px;overflow:hidden}

.tl-bar{position:absolute;top:0;bottom:0;border-radius:6px;display:flex;align-items:center;
  padding:0 8px;font-size:11px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  cursor:pointer;transition:filter .16s,transform .16s;border:1px solid}
.tl-bar:hover{filter:brightness(1.16);z-index:2}
.tl-bar:focus-visible{z-index:3}
.tl-bar.closed{background:linear-gradient(180deg,rgba(248,113,113,.34),rgba(248,113,113,.2));
  border-color:var(--red-border);color:#fecaca}
body.light .tl-bar.closed{background:rgba(185,28,28,.16);color:#7f1d1d}
.tl-bar.open{background:linear-gradient(180deg,rgba(52,211,153,.26),rgba(52,211,153,.14));
  border-color:var(--green-border);color:#a7f3d0}
body.light .tl-bar.open{background:rgba(21,128,61,.14);color:#14532d}
.tl-bar .bar-x{display:inline-flex;align-items:center;gap:5px;min-width:0}
.tl-bar svg{width:12px;height:12px;flex-shrink:0}
/* hatch pattern doubles for closed state so colour is not the only cue */
.tl-bar.closed::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:.4;
  background:repeating-linear-gradient(45deg,transparent 0 5px,rgba(255,255,255,.14) 5px 10px)}

.tl-race{position:absolute;top:0;bottom:0;border-radius:6px;border:1px dashed;opacity:.95;
  display:flex;align-items:center;padding:0 7px;font-size:10px;font-weight:800;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* "now" marker */
/* The green flag: where the broom wagon is due on this row (hollow) or
   where it actually was (filled). Above the bars, below nothing, and
   never in the way of a press. */
.tl-flag{position:absolute;top:2px;bottom:2px;width:22px;margin-left:-11px;z-index:4;
  pointer-events:none;display:flex;align-items:center;justify-content:center}
.tl-flag::before{content:'';position:absolute;top:0;bottom:0;left:50%;width:2px;
  background:#16a34a;opacity:.55}
.tl-flag svg{position:relative;width:13px;height:13px;color:#16a34a;background:var(--bg2);
  border:1.5px solid #16a34a;border-radius:4px;padding:1px;box-sizing:content-box}
.tl-flag.real svg{background:#16a34a;color:#fff}
.tl-flag.real::before{opacity:1}
.tl-now{position:absolute;top:22px;bottom:0;width:2px;background:var(--gold2);z-index:5;pointer-events:none;
  box-shadow:0 0 10px 1px rgba(251,191,36,.8)}
.tl-now::before{content:'';position:absolute;top:-5px;left:-4px;width:10px;height:10px;border-radius:50%;
  background:var(--gold2);box-shadow:0 0 10px 2px rgba(251,191,36,.7)}

.tl-legend{display:flex;flex-wrap:wrap;gap:14px;padding:12px 0 6px;font-size:12px;color:var(--text2)}
.tl-legend span{display:inline-flex;align-items:center;gap:7px}
.tl-swatch{width:22px;height:12px;border-radius:3px;border:1px solid;flex-shrink:0}
.tl-swatch.closed{background:rgba(248,113,113,.3);border-color:var(--red-border)}
.tl-swatch.open{background:rgba(52,211,153,.24);border-color:var(--green-border)}

/* ── RACE CARDS ────────────────────────────────────────────── */
.race-list{display:grid;gap:12px}
.race-card{background:var(--bg2);border:1px solid var(--border);border-left:4px solid var(--cat,#f59e0b);
  border-radius:var(--radius);padding:16px 18px;box-shadow:var(--shadow);
  transition:transform .2s,border-color .2s,box-shadow .2s}
.race-card:hover{transform:translateX(3px);box-shadow:var(--shadow-md)}
.rc-top{display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;margin-bottom:12px}
.rc-time{font-family:'Space Grotesk','Inter',sans-serif;font-size:24px;font-weight:700;color:var(--bright);
  letter-spacing:-.6px;font-variant-numeric:tabular-nums;line-height:1}
.rc-time small{display:block;font-family:'Inter',sans-serif;font-size:10px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;color:var(--text3);margin-top:3px}
.rc-title{flex:1;min-width:160px}
.rc-title h4{font-size:16px;font-weight:700;color:var(--text);margin-bottom:5px;letter-spacing:-.2px}
.rc-tags{display:flex;flex-wrap:wrap;gap:5px}
/* Tags never break across lines: a two-word tag split down the middle reads
   as two tags. They wrap as whole chips inside .rc-tags instead, and a long
   course name is cut with an ellipsis rather than stacking letter by letter. */
.tag{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;padding:3px 8px;border-radius:20px;border:1px solid;
  white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
.tag svg{flex-shrink:0}
.tag.cat{background:color-mix(in srgb,var(--c) 16%,transparent);border-color:color-mix(in srgb,var(--c) 45%,transparent);color:var(--c)}
.tag.disc{background:var(--bg4);border-color:var(--border2);color:var(--text2)}
.tag.zone{background:var(--blue-bg);border-color:var(--blue-border);color:var(--blue)}
.tag.warn{background:var(--gold-bg);border-color:var(--gold-border);color:var(--gold2);cursor:help}
body.light .tag.warn{color:var(--gold)}
.rc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:12px;
  padding-top:12px;border-top:1px solid var(--border)}
.rc-cell dt{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--text3);margin-bottom:3px}
.rc-cell dd{font-size:15px;font-weight:600;color:var(--text);font-variant-numeric:tabular-nums;line-height:1.25}
/* A cell is one value with its footnote under it. Inline, the footnote wrapped
   into the value — "08:35 2.6 km @ 30" / "km/h" — and the two read as one
   number split over two lines. Its own line keeps the value whole. */
.rc-cell .rc-sub{display:block;font-size:11px;font-weight:500;line-height:1.35;
  color:var(--text3);margin-top:2px;font-variant-numeric:normal}
.rc-note{margin-top:10px;font-size:12px;color:var(--text2);display:flex;gap:7px;align-items:flex-start}
.rc-note svg{width:14px;height:14px;flex-shrink:0;margin-top:2px;color:var(--text3)}

/* ── ACTIVITY LIST ─────────────────────────────────────────── */
.act-list{display:grid;gap:2px}
.act-row{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:9px;transition:background .16s}
.act-row:hover{background:var(--bg3)}
.act-time{font-size:13px;font-weight:700;color:var(--text2);font-variant-numeric:tabular-nums;
  min-width:96px;flex-shrink:0}
.act-ico{width:32px;height:32px;border-radius:8px;background:var(--bg4);display:flex;align-items:center;
  justify-content:center;color:var(--text2);flex-shrink:0}
.act-ico svg{width:16px;height:16px}
.act-text{flex:1;min-width:0;font-size:14px;color:var(--text)}
/* The Chronorace report buttons on an .act-row. They carry names the
   timekeeper chose, not names that fit — so they wrap onto their own
   line rather than stretching the row, and a name wider than the screen
   is cut rather than pushing the page sideways. */
.crr-acts{display:flex;gap:6px;flex-wrap:wrap;min-width:0;justify-content:flex-end}
.crr-acts .btn{min-width:0;max-width:100%}
.crr-acts .btn span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (max-width:760px){
  .act-row{flex-wrap:wrap}
  .act-row .crr-acts{flex:1 1 100%;justify-content:flex-start}
}
/* Row controls are secondary to the row itself. Boxed and filled, three of
   them ate half the width of a phone; they are ghost buttons here and only
   draw a border on hover or focus. */
/* --text2 rather than --text3: dropping the border and fill already makes
   these quiet, and the faintest tone on a white card reads as disabled. */
.act-row .icon-btn{min-width:36px;min-height:36px;border-color:transparent;
  background:transparent;color:var(--text2)}
.act-row .icon-btn svg{width:17px;height:17px}
.act-row .icon-btn:hover,.act-row .icon-btn:focus-visible{background:var(--bg4);
  border-color:var(--gold-border);color:var(--text)}
.act-text strong{font-weight:700;line-height:1.35}
.act-row .rc-tags{margin-top:5px}

/* Narrow screens: the four-column row squeezed the title and its tags into
   ~100 px, which broke every label onto its own line. Time, icon and the
   controls keep one line; the text takes the full width underneath. */
@media (max-width:640px){
  .act-row{flex-wrap:wrap;gap:8px 10px;padding:11px 12px}
  .act-row .act-ico{order:-1;width:28px;height:28px;border-radius:7px}
  .act-row .act-ico svg{width:15px;height:15px}
  .act-row .act-time{flex:1 1 auto;min-width:0;font-size:12.5px}
  .act-row .act-text{order:5;flex:1 1 100%}
  .act-row .icon-btn{flex-shrink:0}
}

/* ── ACTIVITY CARD ─────────────────────────────────────────────
   The stacked form used in the day's side column: time and controls on
   one line, then the title, then the location — each with the full
   width of the card, which a four-column row never had.
   ──────────────────────────────────────────────────────────── */
.act-list{gap:8px}
/* Same cards in the main column on a preparation day: there they have the
   full page width, so they run in columns instead of one tall stack. */
.act-list.wide{grid-template-columns:repeat(auto-fill,minmax(min(250px,100%),1fr))}
.act-card{padding:11px 13px;border:1px solid var(--border);border-radius:10px;
  background:var(--bg2);transition:border-color .16s}
body.light .act-card{background:#fff}
.act-card:hover{border-color:var(--gold-border)}
.ac-top{display:flex;align-items:center;gap:9px}
.ac-top .act-ico{width:28px;height:28px;border-radius:7px}
.ac-top .act-ico svg{width:15px;height:15px}
.ac-time{flex:1;min-width:0;font-size:13px;font-weight:700;color:var(--text2);
  font-variant-numeric:tabular-nums;white-space:nowrap}
.ac-top .icon-btn{flex-shrink:0}
.ac-title{font-size:14px;font-weight:600;color:var(--text);line-height:1.35;margin-top:8px}
.ac-loc{display:flex;align-items:flex-start;gap:6px;margin-top:5px;font-size:12px;
  line-height:1.4;color:var(--gold2)}
body.light .ac-loc{color:var(--gold)}
.ac-loc svg{width:13px;height:13px;flex-shrink:0;margin-top:2px}
.ac-loc-sub{color:var(--text3);font-weight:400}

/* ── PLANNING CHECKS ───────────────────────────────────────────
   Only ever on screen when something is wrong, so it is allowed to be
   loud. Errors are contradictions in the data; warnings are things to
   confirm.
   ──────────────────────────────────────────────────────────── */
.checks-card{border-color:var(--gold-border)}
/* A <details> now: the header is the summary and clicking it opens the list.
   The default triangle goes — the header already carries a Show/Hide word. */
.checks-hd{display:flex;align-items:center;gap:12px;cursor:pointer;list-style:none;min-height:44px}
.checks-hd::-webkit-details-marker{display:none}
.checks-card[open] .checks-hd{margin-bottom:12px}
.checks-hd>div{flex:1;min-width:0}
/* The way to put a line away, beside the way to fold the panel. Its own
   button rather than a word like the toggle beside it, because one of
   the two changes the data somebody sees tomorrow and the other only
   changes what is on the screen now. */
.checks-clear{flex-shrink:0;margin-left:auto}
.checks-hd .checks-clear + .checks-toggle{margin-left:0}
/* Nothing left to check, because it was all put away — see the note in
   js/checks.js about why this is not an "all clear" card. */
.checks-cleared .checks-hd{cursor:default}
.checks-ico.ok{background:var(--ok-bg,rgba(22,163,74,.14));color:#16a34a}
.checks-toggle{flex-shrink:0;font-size:11px;font-weight:800;text-transform:uppercase;
  letter-spacing:.07em;color:var(--gold2);border:1px solid var(--gold-border);
  border-radius:20px;padding:5px 12px}
body.light .checks-toggle{color:var(--gold)}
.checks-hd:hover .checks-toggle{background:var(--gold-bg)}
.checks-hd h3{margin-bottom:2px}
.checks-ico{width:36px;height:36px;border-radius:9px;flex-shrink:0;display:flex;
  align-items:center;justify-content:center}
.checks-ico svg{width:18px;height:18px}
.checks-ico.err{background:var(--red-bg);color:var(--red)}
.checks-ico.warn{background:var(--gold-light);color:var(--gold2)}
body.light .checks-ico.warn{color:var(--gold)}

.checks-list{display:grid;gap:6px}
.check-row{display:flex;align-items:flex-start;gap:11px;width:100%;text-align:left;
  padding:10px 12px;border:1px solid var(--border);border-radius:9px;background:var(--bg2);
  font-family:inherit;cursor:pointer;transition:border-color .16s,background .16s}
body.light .check-row{background:#fff}
.check-row:hover{border-color:var(--gold-border);background:var(--bg3)}
.check-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0;margin-top:6px}
.check-row.error .check-dot{background:var(--red)}
.check-row.warn  .check-dot{background:var(--gold2)}
body.light .check-row.warn .check-dot{background:var(--gold)}
.check-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.check-txt strong{font-size:13.5px;font-weight:700;color:var(--text);line-height:1.35}
.check-txt span{font-size:12px;color:var(--text2);line-height:1.4}
.check-arrow{flex-shrink:0;color:var(--text3);margin-top:2px}
.check-arrow svg{width:15px;height:15px}

/* ── ROAD STATUS (residents) ───────────────────────────────────
   Deliberately larger than anything else in the app: this screen gets
   read on a phone, outdoors, by somebody who wants one word.
   ──────────────────────────────────────────────────────────── */
.rst-day{display:inline-flex;align-items:center;gap:8px;margin-bottom:18px;padding:8px 13px;
  border:1px solid var(--border);border-radius:999px;background:var(--bg2);
  font-size:13.5px;font-weight:600;color:var(--text2)}
.rst-day svg{width:16px;height:16px;color:var(--text3)}

.rst-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:16px}
.rst-card{position:relative;overflow:hidden;padding:20px 22px;border-radius:var(--radius);
  border:1px solid var(--border);background:var(--bg2);box-shadow:var(--shadow);
  transition:transform .2s,box-shadow .2s,border-color .2s}
body.light .rst-card{background:#fff}
.rst-card::before{content:'';position:absolute;top:0;left:0;right:0;height:4px;background:var(--z)}

/* ── COLOUR AND MOVEMENT ───────────────────────────────────────
   This is the one screen a resident opens, outdoors, wanting one word.
   The state is carried by the whole card rather than by the word alone:
   a wash of its colour behind it, a bar of it down the leading edge, and
   the closed ones stirring so the eye lands on them first. --st is the
   state's colour, set per card so every rule below can read it. */
.rst-card{--st:var(--green)}
.rst-soon{--st:var(--gold2)}
.rst-closed{--st:var(--red)}
body.light .rst-soon{--st:var(--gold)}

.rst-card::after{content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(120% 90% at 100% 0%,color-mix(in srgb,var(--st) 16%,transparent),transparent 62%);
  opacity:.85}
.rst-card > *{position:relative;z-index:1}
.rst-card{border-left:4px solid var(--st)}
.rst-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-md);border-color:var(--st)}

/* The state line gets a disc of its own colour behind the padlock, and the
   closed ones pulse it — slowly, once every two and a half seconds. Nothing
   here animates a transform on anything the layout depends on. */
.rst-state svg{padding:7px;border-radius:50%;width:40px;height:40px;box-sizing:content-box;
  background:color-mix(in srgb,var(--st) 15%,transparent);
  box-shadow:0 0 0 0 color-mix(in srgb,var(--st) 55%,transparent)}
.rst-closed .rst-state svg{animation:rst-pulse 2.4s ease-out infinite}
@keyframes rst-pulse{
  0%  {box-shadow:0 0 0 0    color-mix(in srgb,var(--st) 55%,transparent)}
  70% {box-shadow:0 0 0 14px color-mix(in srgb,var(--st) 0%,transparent)}
  100%{box-shadow:0 0 0 0    color-mix(in srgb,var(--st) 0%,transparent)}
}

/* The cards arrive one after the other rather than all at once — a tenth of
   a second apart, so the eye reads down the column instead of at a wall. */
.rst-card{animation:rst-in .34s ease-out both}
.rst-card:nth-child(2){animation-delay:.05s}
.rst-card:nth-child(3){animation-delay:.1s}
.rst-card:nth-child(4){animation-delay:.15s}
.rst-card:nth-child(5){animation-delay:.2s}
.rst-card:nth-child(n+6){animation-delay:.25s}
@keyframes rst-in{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

/* Somebody who has asked their system to stop moving things gets a still
   page: the colours already carry the whole message. */
@media (prefers-reduced-motion:reduce){
  .rst-card{animation:none}
  .rst-closed .rst-state svg{animation:none}
  .rst-card:hover{transform:none}
}

.rst-zone{font-family:'Space Grotesk','Inter',sans-serif;font-size:17px;font-weight:700;
  color:var(--text);letter-spacing:-.2px;margin-bottom:14px;text-transform:none}
.rst-zone small{display:block;font-size:12px;font-weight:500;color:var(--text3);margin-top:2px}

.rst-state{display:flex;align-items:center;gap:12px;font-family:'Space Grotesk','Inter',sans-serif;
  font-size:30px;font-weight:700;letter-spacing:-.5px;line-height:1;color:var(--st)}
.rst-state svg{width:26px;height:26px;flex-shrink:0}

.rst-sub{margin-top:12px;font-size:14px;color:var(--text2);line-height:1.45}

.rst-windows{margin-top:16px;padding-top:13px;border-top:1px solid var(--border)}
/* The closed windows read as bars of the state's colour rather than a list. */
.rst-windows li{border-radius:8px;padding:6px 10px;
  background:color-mix(in srgb,var(--st) 10%,transparent);
  border-left:3px solid color-mix(in srgb,var(--st) 60%,transparent)}
.rst-w-hd{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:var(--text3)}
.rst-windows ul{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:5px}
.rst-windows li{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  font-size:14px;color:var(--text);font-variant-numeric:tabular-nums}
.rst-w-time{font-weight:600}
.rst-w-dur{font-size:12.5px;color:var(--text3)}

.rst-foot{display:flex;align-items:flex-start;gap:9px;margin-top:20px;padding:12px 14px;
  border-radius:10px;background:var(--bg3);border:1px solid var(--border);
  font-size:12.5px;color:var(--text2);line-height:1.5}
.rst-foot svg{width:16px;height:16px;flex-shrink:0;margin-top:1px;color:var(--text3)}

/* ── EMPTY STATES ──────────────────────────────────────────── */
.empty{text-align:center;padding:44px 20px;color:var(--text2)}
.empty svg{width:40px;height:40px;color:var(--text3);margin-bottom:12px}
.empty p{font-size:14px;margin-bottom:14px}

/* ── FORMS ───────────────────────────────────────────────────
   `> label` on purpose: the chips inside .chk-grid are labels too, and a
   blanket `.field label` rule made every option shout in uppercase.

   `:not(.chk)` for the same reason one level up. A .chk pill IS a label,
   and a form that puts one straight into a .field — the roadworks "On the
   share link" tick — handed it this rule instead of its own: display:block
   dropped the pill's flex row, so the tick box and its text landed on top
   of each other, shouting in uppercase. The pill wins wherever it sits.
   ──────────────────────────────────────────────────────────── */
.field{margin-bottom:16px}
.field > label:not(.chk),.field .field-hd{display:block;font-size:11px;font-weight:800;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text2);margin-bottom:7px}
.field > label .req,.field .field-hd .req{color:var(--red);margin-left:2px}
.field input,.field select,.field textarea{width:100%;min-height:44px;background:var(--bg3);
  border:1px solid var(--field-border);border-radius:9px;color:var(--text);padding:10px 12px;
  font-size:15px;font-family:inherit;outline:none;transition:border-color .16s}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--gold)}
/* 16px on a touch screen: an iPhone zooms the page in on any field
   under 16px, and a marshal typing a name at a junction loses the
   form off the side of the screen. */
@media (pointer:coarse){
  .field input,.field select,.field textarea,.input{font-size:16px}
}
/* A HINT IS A HINT WHEREVER IT STANDS. This was written as `.field
   .hint` only, so a <p class="hint"> standing on its own in a dialogue
   — the ticks under a new person, the currency door, the allocations —
   printed at full body size in full ink and read as an instruction
   rather than as the aside it is. The scoped rule stays: it is the same
   size, and the containers that override the margin still win. */
.hint{font-size:12px;line-height:1.5;color:var(--text3);margin:6px 0 0}
.hint+.btn,.hint+button{margin-top:10px}
.field .hint{font-size:12px;color:var(--text3);margin-top:5px}
.field .err{font-size:12px;color:var(--red);margin-top:5px;display:none}
.field.invalid input,.field.invalid select{border-color:var(--red)}
.field.invalid .err{display:block}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
/* ── OPTION CHIPS ──────────────────────────────────────────── */
.chk-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(158px,1fr));gap:8px}
.chk-grid.compact{grid-template-columns:repeat(auto-fill,minmax(104px,1fr))}
.chk-grid.swatches{grid-template-columns:repeat(auto-fill,minmax(52px,1fr))}

.chk{display:flex;align-items:center;gap:9px;padding:9px 11px;border-radius:9px;cursor:pointer;
  border:1px solid var(--border2);background:var(--bg3);min-height:44px;margin:0;
  font-size:13px;font-weight:600;text-transform:none;letter-spacing:0;color:var(--text);
  transition:background .16s,border-color .16s;user-select:none;position:relative}
.chk:hover{border-color:var(--border2);background:var(--bg4)}
/* The native control stays for keyboard and screen readers; .chk-box is the
   visible one, so the checkbox looks the same in every browser and theme. */
.chk input{position:absolute;opacity:0;width:1px;height:1px;margin:0;pointer-events:none}
.chk:focus-within{outline:2px solid var(--gold2);outline-offset:2px}

.chk-box{width:19px;height:19px;border-radius:6px;flex-shrink:0;background:var(--bg2);
  border:1.5px solid var(--border2);display:flex;align-items:center;justify-content:center;
  transition:background .16s,border-color .16s}
.chk-box svg{width:12px;height:12px;stroke-width:3;color:#0b1120;opacity:0;transition:opacity .16s}
.chk-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0;box-shadow:0 0 0 2px rgba(0,0,0,.25)}

.chk.on, .chk:has(input:checked) {
  border-color: var(--gold-border);
  background: var(--gold-bg);
}
.chk.on .chk-box, .chk:has(input:checked) .chk-box {
  background: var(--gold2);
  border-color: var(--gold2);
}
.chk.on .chk-box svg, .chk:has(input:checked) .chk-box svg {
  opacity: 1;
}
/* ── SEARCH DROPDOWN (Mapbox Geocoding Autocomplete) ──────── */
.search-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 9999;
  background: var(--bg2);
  border: 1px solid var(--gold-border);
  border-radius: 9px;
  max-height: 230px;
  overflow-y: auto;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
  margin-top: 4px;
}
.search-item {
  padding: 10px 14px;
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  gap: 10px;
  transition: background 0.15s, color 0.15s;
}
.search-item:hover {
  background: var(--gold-light);
  color: var(--gold2);
}
.search-item:last-child {
  border-bottom: none;
}

/* Colour pickers: the swatch is the whole point, so drop the label and box. */
.chk-grid.swatches .chk{justify-content:center;padding:9px}
.chk-grid.swatches .chk-box{display:none}
.chk-grid.swatches .chk-dot{width:20px;height:20px;box-shadow:none}
.chk-grid.swatches .chk-t{display:none}
.chk-grid.swatches .chk.on{border-color:var(--gold2);border-width:2px;padding:8px}

/* THE FINANCE TICK IS A .chk THAT IS ALSO .field's DIRECT CHILD, so the
   heading rule above (.field > label: block, uppercase) lands on it and
   broke the row open — the box sat on top of its own sentence. Restated
   as the row it is, with the box on the RIGHT, where every other control
   in the access dialog keeps its answers. */
#f-hostcities .chk{display:flex;flex-direction:row-reverse;align-items:center;gap:12px;
  font-size:13px;font-weight:600;text-transform:none;letter-spacing:normal;
  color:var(--text);margin-bottom:0}
#f-hostcities .chk .chk-t{flex:1;min-width:0}

/* ── START LISTS ───────────────────────────────────────────── */
.sl-bar{display:flex;gap:10px;align-items:flex-end;flex-wrap:wrap;margin-bottom:16px}
/* The actions live on the right: the import, the team sheet, and the ⋮
   holding everything else. margin-left pushes the whole group over; the
   inner flex keeps the menu button glued to its dropdown anchor. */
.sl-actions{display:flex;gap:10px;align-items:center;margin-left:auto}
.sl-actions .ht-more-btn{width:38px;height:38px}
/* justify-content:flex-end with nowrap pushed the row's overflow off the
   LEFT of the screen — the import button, the one that gets pressed
   daily, began outside the phone. They wrap and start at the left, and
   the team sheet sheds its label at that width. */
@media (max-width:760px){
  .sl-actions{margin-left:0;width:100%;justify-content:flex-start;flex-wrap:wrap}
  .sl-actions .btn{min-width:0}
  .sl-actions .btn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
}
.sl-pick{display:flex;flex-direction:column;gap:5px;flex:1;min-width:220px}
.sl-pick span{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:var(--text2)}
.sl-pick select{min-height:44px;background:var(--bg3);border:1px solid var(--border2);border-radius:9px;
  color:var(--text);padding:9px 11px;font-size:14px;font-family:inherit;outline:none}
.sl-pick select:focus{border-color:var(--gold)}
.sl-or{text-align:center;font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;
  color:var(--text3);margin:14px 0;position:relative}
.sl-or::before,.sl-or::after{content:'';position:absolute;top:50%;width:42%;height:1px;background:var(--border)}
.sl-or::before{left:0} .sl-or::after{right:0}
.sl-apply{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin-bottom:16px;
  padding:11px 14px;border-radius:9px;background:var(--blue-bg);border:1px solid var(--blue-border);
  font-size:13px;color:var(--text2)}
.sl-apply > svg{width:16px;height:16px;color:var(--blue);flex-shrink:0}
.sl-apply span{flex:1;min-width:180px}

/* ── SUB-HEADINGS inside the race editor ───────────────────── */
.calc-hd{display:flex;align-items:center;gap:8px;margin:6px 0 12px;padding-top:14px;
  border-top:1px solid var(--border);font-size:11px;font-weight:800;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text2)}
/* A heading inside a section rather than between two — the relay legs sit
   under one heading, so they get a rule-less, lighter one of their own. */
.calc-hd.sub{border-top:0;padding-top:4px;margin:14px 0 10px;color:var(--text3)}
.calc-hd svg{width:15px;height:15px;color:var(--gold2)}
body.light .calc-hd svg{color:var(--gold)}
.calc-note{display:flex;align-items:flex-start;gap:9px;margin:0 0 16px;padding:11px 13px;border-radius:9px;
  background:var(--bg3);border:1px solid var(--border);font-size:12.5px;color:var(--text2);line-height:1.45}
.calc-note svg{width:16px;height:16px;flex-shrink:0;margin-top:1px;color:var(--text3)}

/* three-tier finish preview */
.ft-grid{display:flex;flex-wrap:wrap;gap:8px 20px}
.ft-cell{display:inline-flex;flex-direction:column;gap:1px;font-family:'Space Grotesk','Inter',sans-serif;
  font-size:17px;font-weight:700;color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.2px}
.ft-cell b{font-family:'Inter',sans-serif;font-size:9px;font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:var(--text3)}

/* ── START SCHEDULE + SUMMARY ──────────────────────────────────
   The panel and its four numbers side by side. Below 1180px the column
   drops underneath and the numbers spread out four across instead.
   ──────────────────────────────────────────────────────────── */
/* align-items:start, not stretch. Stretching made the schedule panel as tall
   as whatever the four cards beside it happened to need, and the difference
   came out as a band of empty tint between its heading and its controls —
   the taller the cards grew, the emptier the panel looked. Each column now
   takes the height it actually needs. */
/* The left column stacks the panel, the apply line and the search bar
   on ONE edge; the counters beside them stretch their four squares over
   the same height — no ragged widths, no dead corner. */
.sl-sched-left{display:flex;flex-direction:column;gap:12px;min-width:0}
.sl-sched-left .sl-apply{margin:0}
.sl-sched-row .sl-minis{grid-auto-rows:1fr;align-content:stretch}
.sl-sched-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(min(340px,100%),440px);
  gap:12px;align-items:stretch;margin-bottom:12px}
.sl-sched-row .auto-panel{margin-bottom:0;padding:12px 14px}
.sl-sched-row .ap-ico{width:30px;height:30px;border-radius:8px}
.sl-sched-row .ap-ico svg{width:15px;height:15px}
.sl-sched-row .ap-t strong{font-size:13px}
.sl-sched-row .ap-t small{font-size:11.5px}

/* The six controls share out whatever width the panel has instead of
   sitting at their natural size — otherwise the yellow block runs on
   half-empty to the right of the last dropdown.
   A grid rather than the flex row used elsewhere: with flex, the sixth
   control lands alone on the second line and stretches the full width
   of the panel. Three even columns give two tidy rows instead. */
.sl-sched-row .ap-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:10px 14px;margin-top:11px;padding-top:11px;align-items:end}
.sl-sched-row .ap-f{min-width:0}
.sl-sched-row .ap-f input,.sl-sched-row .ap-f select{width:100%;min-width:0}

.sl-minis{display:flex;flex-direction:column;gap:8px}
.sl-minis.wide{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin-bottom:16px}

.sl-mini{position:relative;display:flex;align-items:center;gap:12px;min-width:0;
  padding:14px 18px 14px 20px;border:1px solid var(--border);border-radius:10px;
  background:var(--bg2);overflow:hidden}
body.light .sl-mini{background:#fff}
.sl-mini::before{content:'';position:absolute;top:0;bottom:0;left:0;width:3px}
.sl-mini.c-gold::before{background:linear-gradient(180deg,#d97706,#f59e0b)}
.sl-mini.c-blue::before{background:linear-gradient(180deg,#2563eb,#3b82f6)}
.sl-mini.c-green::before{background:linear-gradient(180deg,#16a34a,#22c55e)}
.sl-mini.c-red::before{background:linear-gradient(180deg,#dc2626,#ef4444)}
/* Grey into red — DNS into DNF — so the out-of-race counter is not read
   as a second "slowest finish" out of the corner of the eye. */
.sl-mini.c-out::before{background:linear-gradient(180deg,#94a3b8,#dc2626)}
.sl-mini.c-out .slm-ico{color:#e0524b}
body.light .sl-mini.c-out .slm-ico{color:#b91c1c}

.slm-ico{display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--text3)}
.slm-ico svg{width:15px;height:15px}
.sl-mini.c-gold .slm-ico{color:var(--gold2)}
body.light .sl-mini.c-gold .slm-ico{color:var(--gold)}
.sl-mini.c-blue .slm-ico{color:var(--blue,#3b82f6)}
.sl-mini.c-green .slm-ico{color:var(--green)}
.sl-mini.c-red .slm-ico{color:var(--red)}

.slm-txt{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}
.slm-label{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:var(--text2)}
.slm-sub{font-size:11.5px;color:var(--text3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.slm-val{font-family:'Space Grotesk','Inter',sans-serif;font-size:25px;font-weight:700;
  letter-spacing:-.3px;font-variant-numeric:tabular-nums;color:var(--bright);flex-shrink:0}
.sl-mini.c-gold .slm-val{color:var(--gold2)}
body.light .sl-mini.c-gold .slm-val{color:var(--gold)}
.sl-mini.c-green .slm-val{color:var(--green)}
.sl-mini.c-red .slm-val{color:var(--red)}

@media (max-width:1180px){
  .sl-sched-row{grid-template-columns:1fr}
  .sl-sched-row .sl-minis{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}
}
/* Four across or two across, never three with an orphan on its own row. */
@media (max-width:760px){
  .sl-minis.wide,.sl-sched-row .sl-minis{grid-template-columns:repeat(2,1fr)}
}

/* ── START SHEET (branded) ─────────────────────────────────── */
.sl-sheet{padding:0;overflow:hidden}
.sl-sheet-hd{display:flex;align-items:center;gap:16px;padding:18px 20px;
  background-color:var(--bg3);background-image:linear-gradient(135deg,rgba(245,158,11,.14),rgba(245,158,11,.02) 60%);
  border-bottom:1px solid var(--gold-border)}
.ssh-logo{height:52px;width:auto;object-fit:contain;background:#fff;border-radius:10px;padding:5px 9px;flex-shrink:0}
.ssh-mid{flex:1;min-width:0}
.ssh-title{font-family:'Space Grotesk','Inter',sans-serif;font-size:18px;font-weight:700;color:var(--text);
  letter-spacing:-.3px;line-height:1.25}
.ssh-meta{font-size:12.5px;color:var(--text2);margin-top:3px;font-variant-numeric:tabular-nums}
.ssh-event{font-size:11px;color:var(--text3);margin-top:2px;text-transform:uppercase;letter-spacing:.06em;font-weight:700}
.ssh-golazo{height:26px;width:auto;object-fit:contain;flex-shrink:0}

.sl-table-wrap{overflow-x:auto}
.sl-tbl{min-width:640px}
.sl-tbl th,.sl-tbl td{padding:10px 14px}
.sl-tbl th.c-chk,.sl-tbl td.c-chk{width:36px;text-align:center;padding-left:8px;padding-right:8px}
.sl-tbl td.c-chk input[type="checkbox"],.sl-tbl th.c-chk input[type="checkbox"]{cursor:pointer;width:16px;height:16px;accent-color:var(--gold,#f59e0b)}
.sl-tbl tbody tr.selected-row{background:rgba(245,158,11,.15)!important}
body.light .sl-tbl tbody tr.selected-row{background:rgba(245,158,11,.18)!important}
.sl-tbl tbody tr:nth-child(even){background:rgba(148,163,184,.045)}
body.light .sl-tbl tbody tr:nth-child(even){background:rgba(15,23,42,.03)}
.sl-tbl tbody tr:hover{background:var(--gold-light)}
.sl-tbl .c-pos{width:38px;color:var(--text3);text-align:right;font-variant-numeric:tabular-nums}
.sl-tbl .c-start{color:var(--gold2);font-weight:700}
body.light .sl-tbl .c-start{color:var(--gold)}
.sl-tbl th,.sl-tbl td{vertical-align:middle}
.sl-tbl td.c-name{font-weight:600;color:var(--text)}
.sl-tbl td.c-team{color:var(--text2)}
/* Shown only when the team column is dropped — see the phone rules below. */
.sl-tbl .c-name-sub{display:none}
.sl-tbl .c-eta{color:var(--text3)}
/* ONE FILE, SEVERAL RACES. A ChronoRace start list stacks every category
   of the day into one document; this is the line across the sheet where
   one ends and the next begins. Sticky, because scrolling through
   eighteen hundred names is how you lose track of which race you are in. */
.sl-tbl tr.sl-cat-band td{position:sticky;top:0;z-index:2;
  padding:7px 14px;background:var(--bg4);border-top:1px solid var(--border2);
  border-bottom:1px solid var(--border2);font-size:11.5px;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;color:var(--gold2)}
body.light .sl-tbl tr.sl-cat-band td{color:var(--gold)}
.sl-tbl tr.sl-cat-band td span{margin-left:8px;padding:1px 7px;border-radius:20px;
  background:var(--bg3);border:1px solid var(--border);font-size:11px;font-weight:700;
  letter-spacing:0;color:var(--text2)}
/* The course and the gun for this group — the start board, on the sheet. */
.sl-tbl tr.sl-cat-band td em{margin-left:10px;font-style:normal;font-size:11px;
  font-weight:700;letter-spacing:.02em;color:var(--text3)}
.sl-tbl tbody tr.sl-cat-band:hover td{background:var(--bg4)}
/* The band a rider is classified in, where it is finer than the group
   they leave with — "Heren 70-74" inside the Heren 65-74 start. Beside
   the name and quiet: it is what they win a jersey in, not where they
   have to stand at half past twelve. */
.sl-tbl .c-series{display:inline-block;margin-left:8px;padding:1px 7px;border-radius:20px;
  background:var(--bg3);border:1px solid var(--border);font-size:10.5px;font-weight:700;
  letter-spacing:.02em;color:var(--text3);vertical-align:1px;white-space:nowrap}

/* ── WHICH CATEGORY THIS RIDER IS IN ────────────────────────
   Its own column, and only on a sheet that mixes them: Elite and U23
   leave behind one gun and the heading can only name both. Narrow, next
   to the name, in the same chip the entries desk uses for a category —
   so a row read across says name, category, team, nation. */
.sl-tbl .c-cat{width:1%;white-space:nowrap}
/* THE WORDS IN THE TEXT COLOUR, THE COLOUR ROUND THEM. The category
   palette was drawn for dots on a map and blocks on a timeline, where a
   hue is the whole signal; set as type it is a pale green on a white
   sheet — "MEN U23" in #6ee7b7 could not be read at all. So the chip
   keeps the category's colour as its tint and its edge, and the letters
   are the ordinary text colour, which is legible in both themes. */
.sl-tbl td.c-cat .tag.cat{font-size:9.5px;padding:2px 7px;color:var(--text);
  background:color-mix(in srgb,var(--c) 22%,transparent);
  border-color:color-mix(in srgb,var(--c) 55%,transparent)}

/* ── DISTANCE AND GUN PER CATEGORY ─────────────────────────────
   The race dialogue's own little table: one row per ticked category, a
   kilometre box and a time box. Scrolls inside itself — thirteen rows on
   a phone must not push the Save button off the modal. */
.cp-field .cp-wrap{max-height:280px;overflow:auto;border:1px solid var(--border);
  border-radius:10px;background:var(--bg2)}
.cp-tbl{width:100%;min-width:0}
.cp-tbl th{position:sticky;top:0;z-index:1;background:var(--bg3);font-size:10.5px;
  letter-spacing:.05em;padding:7px 10px}
.cp-tbl td{padding:5px 10px;vertical-align:middle}
.cp-tbl td.cp-cat{font-size:12.5px;font-weight:600;color:var(--text);white-space:nowrap}
.cp-dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px;
  vertical-align:0}
.cp-tbl input{width:100%;min-width:74px;min-height:32px;padding:3px 8px;
  background:var(--bg3);border:1px solid var(--border2);border-radius:7px;
  color:var(--text);font-size:12.5px;outline:none;font-variant-numeric:tabular-nums}
.cp-tbl input:focus{border-color:var(--gold)}
/* The wave table is the same table with more columns, so the cells have
   to give: a name wants room, a runner count and a pace do not. */
.wv-tbl input{min-width:0}
.wv-tbl td:first-child input{min-width:96px}
.wv-tbl .wv-cap,.wv-tbl .wv-v{min-width:62px;text-align:center}
.wv-tbl .wv-t{min-width:86px}
.wv-tbl td.wv-act{white-space:nowrap;padding-left:2px;padding-right:6px}
.wv-tbl td.wv-act .icon-btn{vertical-align:middle}
.cp-tbl th:nth-child(2),.cp-tbl td:nth-child(2),
.cp-tbl th:nth-child(3),.cp-tbl td:nth-child(3){width:110px}
.bib{display:inline-block;min-width:30px;text-align:center;padding:2px 7px;border-radius:6px;
  background:var(--bg4);border:1px solid var(--border);font-size:12px;font-weight:700;color:var(--text)}
.nat-pill{display:inline-block;padding:2px 9px;border-radius:20px;
  background:var(--bg4);border:1px solid var(--border);font-size:11px;font-weight:700;
  letter-spacing:.04em;color:var(--text2);font-variant-numeric:tabular-nums}
.nat-name{display:block;font-size:11px;color:var(--text3);margin-top:3px;white-space:nowrap}

/* ── START SHEET ON A PHONE ────────────────────────────────────
   Seven columns inside a 640 px table meant every rider name broke over
   two or three lines and the sheet still scrolled sideways. Team, nation
   and ETA fold into the rider cell, which leaves four columns that fit
   the screen — one rider, one line. */
@media (max-width:640px){
  .sl-sheet-hd{flex-wrap:wrap;gap:10px 12px;padding:14px}
  .ssh-logo{height:38px;padding:4px 7px}
  .ssh-mid{flex:1 1 100%;order:3}
  .ssh-title{font-size:15px}
  .ssh-meta{font-size:12px}
  .ssh-golazo{height:20px;margin-left:auto}

  /* Not while exporting: the PDF and the printed sheet keep every column,
     so the official document is the same whatever device made it. */
  .sl-sheet:not(.pdf-exporting) .sl-tbl{min-width:0}
  .sl-sheet:not(.pdf-exporting) .sl-tbl th,
  .sl-sheet:not(.pdf-exporting) .sl-tbl td{padding:9px 8px}
  /* The category goes the same way as the team and the nation: into the
     line under the name, where the renderer already writes it first. */
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-team,
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-cat,
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-nat,
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-eta{display:none}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-pos{width:22px;padding-right:2px}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-start{white-space:nowrap}
  /* Every pixel taken off the fixed columns goes to the rider name. */
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-bib{padding-left:4px;padding-right:4px}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .bib{min-width:26px;padding:2px 5px}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-name{width:100%;line-height:1.3}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-name-sub{display:block;font-size:11px;font-weight:500;
    color:var(--text2);margin-top:2px;overflow:hidden;text-overflow:ellipsis}
  .sl-sheet:not(.pdf-exporting) .sl-tbl .c-edit{width:36px;padding-left:0}
}

@media print{
  .sl-sheet-hd{background:#f3f4f6;border-color:#ccc}
  .ssh-logo{background:#fff}
  .sl-tbl tbody tr:nth-child(even){background:#f6f7f9}
  .sl-tbl tbody tr:hover{background:transparent}
  .sl-tbl .c-start{color:#000}
  .sl-tbl .c-cat .tag.cat{background:#f1f5f9;border-color:#ccc;color:#000}
}

/* ── CALCULATED PREVIEW (race editor) ──────────────────────── */
.calc-box{display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 14px;margin:-4px 0 16px;
  padding:11px 13px;border-radius:9px;background:var(--gold-light);border:1px solid var(--gold-border)}
.calc-t{display:inline-flex;align-items:center;gap:6px;font-size:10px;font-weight:800;
  text-transform:uppercase;letter-spacing:.07em;color:var(--gold2)}
body.light .calc-t{color:var(--gold)}
.calc-t svg{width:13px;height:13px}
.calc-v{font-family:'Space Grotesk','Inter',sans-serif;font-size:17px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;letter-spacing:-.2px}
.calc-v strong{font-weight:700}
.calc-d{flex-basis:100%;font-size:12px;color:var(--text2)}

/* ── AUTO / MANUAL CLOSURES PANEL ──────────────────────────── */
.auto-panel{border:1px solid var(--border);border-radius:var(--radius);background:var(--bg2);
  padding:14px 16px;margin-bottom:16px}
/* "On" is a tint, not a highlighter. The solid --gold-light block read as a
   warning the size of half the screen and pulled the eye away from the
   numbers beside it, which are the part worth looking at. */
.auto-panel.on{border-color:var(--gold-border);background:var(--gold-bg)}
body.light .auto-panel.on{background:rgba(180,83,9,.045);border-color:rgba(180,83,9,.22)}
.ap-head{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.ap-ico{width:36px;height:36px;border-radius:9px;flex-shrink:0;display:flex;align-items:center;
  justify-content:center;background:var(--bg4);color:var(--text2)}
.auto-panel.on .ap-ico{background:rgba(245,158,11,.2);color:var(--gold2)}
body.light .auto-panel.on .ap-ico{color:var(--gold)}
.ap-ico svg{width:17px;height:17px}
.ap-t{flex:1;min-width:180px;display:flex;flex-direction:column;gap:2px}
.ap-t strong{font-size:14px;font-weight:700;color:var(--text)}
.ap-t small{font-size:12px;color:var(--text2);line-height:1.4}
.ap-fields{display:flex;flex-wrap:wrap;gap:12px;align-items:flex-end;
  margin-top:13px;padding-top:13px;border-top:1px solid var(--border)}
.ap-f{display:flex;flex-direction:column;gap:5px}
.ap-f span{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;color:var(--text2)}
.ap-f input{width:132px;min-height:40px;background:var(--bg3);border:1px solid var(--border2);
  border-radius:8px;color:var(--text);padding:8px 11px;font-size:14px;font-family:inherit;outline:none;
  font-variant-numeric:tabular-nums}
.ap-f input:focus{border-color:var(--gold)}
.ap-note{flex:1;min-width:200px;font-size:12px;color:var(--text3);padding-bottom:10px}

/* Closures header: day picker + exports on the left, the auto/manual panel
   on the right, so the timeline starts a screen higher. */
.clos-hd{display:grid;grid-template-columns:minmax(0,1fr) minmax(min(320px,100%),420px);
  gap:14px;align-items:start;margin-bottom:16px}
.clos-hd-left{display:flex;flex-direction:column;gap:10px;min-width:0}
.clos-hd-right{min-width:0}
/* The stat cards live inside the header now, so they lose the standalone
   block's outer margins and a little of its padding. In the left column
   (a day without a broom wagon) four of them share the width the day
   picker gets; in the right column (a day with one) they stack, one
   under the other, beside the map. */
.clos-hd .stat-grid{margin:0;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}
.clos-hd .stat-card{padding:13px 15px}
.clos-hd .stat-val{font-size:23px}
.clos-hd-right .stat-grid{grid-template-columns:1fr}
/* THE MAP FILLS THE COLUMN. Stretched to whatever the settings and the
   four figures add up to on the right, never shorter than a map worth
   looking at; the rows and the messages under it keep their own height
   and the map takes the rest. */
.clos-hd.has-map{align-items:stretch}
.clos-hd.has-map .clos-hd-left .swp-desk{margin-bottom:0!important;flex:1;display:flex;
  flex-direction:column;min-height:0}
.clos-hd.has-map .clos-hd-left .swp-desk-map{flex:1;height:auto;min-height:380px}
/* Two cards stacked in the column, so neither may claim its full
   height: the settings card used to, and it pushed the broom wagon
   panel out of the column it was put in. */
.clos-hd-right{display:flex;flex-direction:column;gap:14px}
.clos-hd-right .auto-panel{margin-bottom:0;height:auto}
.clos-hd .swp-desk-row{font-size:12px;gap:8px 12px}
.clos-hd-right .ap-f input{width:100%;min-width:90px}
.clos-hd-right .ap-f{flex:1 1 110px}
.clos-hd-right .ap-note{padding-bottom:0;flex-basis:100%}
@media (max-width:1180px){
  .clos-hd{grid-template-columns:1fr}
}

/* ── MODAL ─────────────────────────────────────────────────── */
.modal-bg{position:fixed;inset:0;background:rgba(2,6,15,.76);backdrop-filter:blur(5px);
  z-index:var(--z-modal);display:none;align-items:center;justify-content:center;padding:18px;overflow-y:auto}
.modal-bg.open{display:flex;animation:fadeUp .2s ease-out}
.modal{background:var(--bg2);border:1px solid var(--border2);border-radius:var(--radius-lg);
  width:100%;max-width:560px;max-height:88vh;display:flex;flex-direction:column;box-shadow:var(--shadow-md)}
.modal-hd{display:flex;align-items:center;gap:12px;padding:18px 20px;border-bottom:1px solid var(--border)}
.modal-hd h3{flex:1;font-family:'Space Grotesk','Inter',sans-serif;font-size:17px;font-weight:700;
  color:var(--text);text-transform:none;letter-spacing:-.2px}
.modal-bd{padding:20px;overflow-y:auto;flex:1}
.modal-ft{display:flex;gap:10px;justify-content:flex-end;padding:16px 20px;border-top:1px solid var(--border);flex-wrap:wrap}

/* ── TOAST ─────────────────────────────────────────────────── */
#toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);opacity:0;
  z-index:var(--z-toast);background:var(--bg4);border:1px solid var(--border2);border-radius:11px;
  padding:13px 20px;font-size:14px;font-weight:600;color:var(--text);box-shadow:var(--shadow-md);
  pointer-events:none;transition:opacity .22s,transform .22s;max-width:90vw;text-align:center}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
#toast.ok{border-color:var(--green-border);color:var(--green)}
#toast.err{border-color:var(--red-border);color:var(--red)}

/* ── THE CONNECTION NOTICE ─────────────────────────────────────
   Bottom LEFT, because the toasts are bottom centre and the two must be
   able to be on screen at once — the notice is a state that persists and
   a toast is an event that passes. Where the sidebar's chip used to be,
   which is the corner an eye already checks for this.

   It is not a toast: a toast that says "you are offline" is gone three
   seconds later while you are still offline. This stays for exactly as
   long as the condition does. */
#net-note{position:fixed;left:16px;bottom:16px;z-index:var(--z-toast);
  display:flex;align-items:center;gap:9px;max-width:min(340px,calc(100vw - 32px));
  padding:11px 14px;border-radius:11px;font-size:13px;font-weight:600;line-height:1.35;
  background:var(--bg4);border:1px solid var(--border2);color:var(--text);
  box-shadow:var(--shadow-md);
  animation:netNoteIn .22s ease-out}
#net-note[hidden]{display:none}
#net-note .nn-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0;background:var(--text3)}
#net-note .nn-t{min-width:0}
#net-note .nn-t b{display:block;font-weight:800}
#net-note .nn-t i{display:block;font-style:normal;font-weight:600;font-size:12px;
  color:var(--text2);margin-top:2px}
/* Amber: the connection is gone but the work is not — writes are queued
   on this device. Red: nothing is being kept. See renderNetNote(). */
/* The tints are translucent; laid over a solid card, so the sidebar
   under the notice no longer reads through its text (Janny, 2 Oct 2026). */
#net-note.warn{border-color:var(--gold-border);background:linear-gradient(var(--gold-bg),var(--gold-bg)),var(--bg2)}
#net-note.warn .nn-dot{background:var(--gold2)}
body.light #net-note.warn .nn-dot{background:var(--gold)}
#net-note.err{border-color:var(--red-border);background:linear-gradient(var(--red-bg),var(--red-bg)),var(--bg2)}
#net-note.err .nn-dot{background:var(--red)}
@keyframes netNoteIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){#net-note{animation:none}}

/* ── READ-ONLY BANNER ──────────────────────────────────────── */
#ro-banner{display:none;align-items:center;gap:11px;padding:9px 18px;position:relative;z-index:2;
  background:linear-gradient(90deg,var(--blue-bg),transparent);border-bottom:1px solid var(--blue-border);
  font-size:12px;color:var(--text2)}
#ro-banner.show{display:flex}
#ro-banner svg{width:16px;height:16px;color:var(--blue);flex-shrink:0}
#ro-banner strong{color:var(--text)}

/* ── TABLE (printable closures) ────────────────────────────── */
.tbl{width:100%;border-collapse:collapse;font-size:13px}
.tbl th{text-align:left;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.07em;
  color:var(--text3);padding:9px 10px;border-bottom:1px solid var(--border2);white-space:nowrap}
.tbl td{padding:11px 10px;border-bottom:1px solid var(--border);vertical-align:top}
.tbl tr:last-child td{border-bottom:none}
.tbl .t-time{font-variant-numeric:tabular-nums;font-weight:700;white-space:nowrap}
.pill{display:inline-flex;align-items:center;gap:5px;font-size:10px;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;padding:3px 9px;border-radius:20px;border:1px solid}
.pill.closed{background:var(--red-bg);border-color:var(--red-border);color:var(--red)}
.pill.open{background:var(--green-bg);border-color:var(--green-border);color:var(--green)}
.pill svg{width:11px;height:11px}

/* ── RESPONSIVE ────────────────────────────────────────────── */
@media (max-width:1024px){
  #content{padding:18px 16px 70px}
}
@media (max-width:860px){
  #header #menuBtn{display:inline-flex}
  /* The drawer is already collapsed by default down here — closed until
     the hamburger opens it — so the icons-only mode this button offers
     on desktop has nothing left to do and the drawer keeps its full
     width whenever it IS open. */
  #nav-collapse-toggle{display:none}
  #sidebar.collapsed{width:262px}
  #sidebar.collapsed .nav-sec-lbl,#sidebar.collapsed .nav-section svg,#sidebar.collapsed .nav-item span,
  #sidebar.collapsed .nav-badge{display:inline}
  #sidebar.collapsed .nav-section{padding:12px 14px 4px;justify-content:flex-start}
  #sidebar.collapsed .nav-section::after{display:none}
  #sidebar.collapsed .nav-item{justify-content:flex-start;padding:11px 12px;gap:10px}
  /* Anchored to #body, not the viewport. #body carries `z-index:1`, which
     makes it a stacking context — so a fixed drawer at z-index 100 still
     paints *below* the header and its first item (the edition chip) ends up
     hidden behind it. Positioning against #body starts the drawer under the
     header instead, and inherits the safe-area padding the header already
     applies. */
  #sidebar{position:absolute;top:0;left:0;bottom:0;z-index:var(--z-modal);width:262px;
    transform:translateX(-100%);transition:transform .24s ease-out;
    box-shadow:8px 0 32px rgba(0,0,0,.5)}
  #sidebar.open{transform:none}
  #sidebar-scrim{position:absolute;inset:0;background:rgba(2,6,15,.6);z-index:calc(var(--z-modal) - 1);display:none}
  #sidebar-scrim.show{display:block}
  .hdr-maker,.hdr-divider{display:none}
  .hdr-cac img{height:34px}
  .hdr-title h1{font-size:14px}
  .page-hd h2{font-size:20px}
  .tl-wrap{--tl-lab:136px}
  .tl-label{font-size:11.5px;padding-right:10px}
  .rc-time{font-size:21px}
}
@media (max-width:520px){
  #content{padding:16px 12px 76px}
  .field-row{grid-template-columns:1fr}
  .act-time{min-width:74px;font-size:12px}
  .modal{max-height:92vh}

  /* The header, tightened. Five controls at desktop size left the edition
     name about forty pixels to live in — "CAC ..." and nothing else, which
     is the one thing up there anybody needs to read. Everything stays a
     comfortable tap target: 38 px is still well over the 24 px minimum,
     and the row loses roughly 60 px of padding and gaps. */
  #header{min-height:54px;padding:0 10px;gap:8px}
  #header .icon-btn{min-width:38px;min-height:38px;border-radius:9px}
  #header .icon-btn svg{width:17px;height:17px}
  .hdr-cac{gap:6px}
  .hdr-cac img{height:28px;padding:3px 5px;border-radius:6px}
  .lang-tabs{padding:2px}
  .lang-btn{min-width:30px;min-height:32px;padding:0 6px;font-size:11px}
  /* And it shrinks with the icon buttons, so the row stays one row of
     one height. */
  #header .lang-cur{min-width:46px;min-height:38px;padding:0 9px}
  .hdr-actions{gap:5px}
  .hdr-title h1{font-size:13px}
}
@media (max-width:420px){
  /* The edition name gets the room instead of the crest. At this width the
     five controls left it 93 px — "CAC ..." and nothing else, which is the
     one thing in the bar anybody needs to read; without the crest it is 157
     and the name is legible. The crest is back at 421 px, and it is on the
     menu, every printed sheet and every share link regardless. */
  #header{gap:6px;padding:0 8px}
  .hdr-cac{display:none}
  .lang-btn{min-width:26px;padding:0 5px;font-size:10px}
  .hdr-actions{gap:4px}
}

/* ═══════════════════════════════════════════════════════════
   CHARTS
   Every chart is drawn at its final size in the markup; the CSS
   only animates *from* zero, so `.pdf-exporting` (set during a PDF
   export) leaves a complete, correct chart behind.
   ═══════════════════════════════════════════════════════════ */
.ch-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));gap:14px;margin-bottom:18px}
.ch-card{background-color:var(--bg2);background-image:linear-gradient(160deg,rgba(255,255,255,.045),rgba(255,255,255,.012) 55%);
  border:1px solid var(--border);border-radius:var(--radius);box-shadow:var(--shadow);padding:16px 18px 18px}
body.light .ch-card{background:#fff}
.ch-card-hd{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;text-transform:uppercase;
  letter-spacing:.07em;color:var(--text2);margin-bottom:14px}
.ch-card-hd svg{width:15px;height:15px;color:var(--gold2);flex-shrink:0}
body.light .ch-card-hd svg{color:var(--gold)}

.ch-donut-wrap{display:flex;align-items:center;gap:20px;flex-wrap:wrap;justify-content:center}
.ch-donut{flex-shrink:0;max-width:100%;height:auto}
.ch-donut-v{font-family:'Space Grotesk','Inter',sans-serif;font-size:34px;font-weight:700;
  fill:var(--bright);letter-spacing:-1px}
.ch-donut-l{font-size:10.5px;font-weight:800;fill:var(--text3);text-transform:uppercase;letter-spacing:.08em}
.ch-seg{animation:chSeg .9s cubic-bezier(.32,.72,.3,1) both;transform-origin:center}
@keyframes chSeg{from{stroke-dasharray:0 var(--circ)}to{stroke-dasharray:var(--dash) var(--circ)}}

.ch-legend{display:flex;flex-direction:column;gap:7px;min-width:150px;flex:1}
.ch-leg{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--text2);
  animation:fadeUp .5s ease-out both}
.ch-leg i{width:11px;height:11px;border-radius:3px;flex-shrink:0}
.ch-leg b{font-weight:700;color:var(--text);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ch-leg small{font-size:11.5px;color:var(--text3);font-variant-numeric:tabular-nums;white-space:nowrap}

.ch-bars{display:flex;flex-direction:column;gap:9px}
.ch-bar-row{display:flex;align-items:center;gap:10px}
.ch-bar-lab{width:88px;flex-shrink:0;font-size:12px;font-weight:700;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* With a flag the label is two things on one line, so it becomes a flex
   row and the ellipsis moves onto the text inside it — a country code
   that ran out of room used to take its own flag with it. Wider too:
   the flag costs 21 pixels and BEL still has to fit beside it. */
.ch-bar-lab.has-flag{display:flex;align-items:center;gap:7px;width:104px}
.ch-bar-lab.has-flag > span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ch-bar-flag{width:21px;height:16px;flex-shrink:0;border-radius:2px;
  box-shadow:0 0 0 1px rgba(0,0,0,.12)}
/* The shirt beside a team's bar. Square and unboxed, unlike a flag: the
   artwork is a cut-out on nothing, and a border around it would draw a
   frame around empty corners. */
.ch-bar-jersey{width:22px;height:22px;object-fit:contain;flex-shrink:0}
/* A team name is not three letters. Where the rows are teams the label
   column doubles, and the bars give up the width — a chart of "Soudal
   Quick…" beside "Team Flander…" tells you nothing a chart should. */
.ch-bars-wide .ch-bar-lab{width:150px}
.ch-bars-wide .ch-bar-lab.has-flag{width:176px}
.ch-bar-track{flex:1;min-width:0;height:20px;border-radius:6px;background:var(--bg4);overflow:hidden;display:flex}
.ch-bar-fill{display:flex;height:100%;border-radius:6px;
  animation:chBar .8s cubic-bezier(.32,.72,.3,1) both}
@keyframes chBar{from{width:0}to{width:var(--w)}}
.ch-bar-fill.ch-stack{background:none}
.ch-stack-part{height:100%}
.ch-bar-val{width:44px;flex-shrink:0;text-align:right;font-size:12.5px;font-weight:800;color:var(--text);
  font-variant-numeric:tabular-nums}
/* A medal row's value is a string of icons and counts — 🥇 3 🥈 1 🥉 1 —
   not a number that fits 44px. Doubled selector on purpose: this must
   outrank the base rule above whatever order they sit in, or the string
   keeps its 44px box and runs off the right edge of the card, which is
   exactly what phones showed. The track has min-width:0 and gives up the
   room instead. */
.ch-bar-val.ch-bar-val-medals{width:auto;min-width:44px;white-space:nowrap}

.ch-empty{display:flex;flex-direction:column;align-items:center;gap:9px;padding:28px 12px;
  text-align:center;font-size:12.5px;color:var(--text3);line-height:1.45}
.ch-empty svg{width:28px;height:28px;color:var(--text3);opacity:.6}

/* PDF capture: no animation (everything is already at final geometry), and
   no working controls — a printed start list should not show edit buttons. */
.pdf-exporting *{animation:none!important;transition:none!important}
.pdf-exporting .c-edit,.pdf-exporting .c-chk,.pdf-exporting .sl-sheet-ft{display:none!important}

/* ═══════════════════════════════════════════════════════════
   LIVE RACE MONITOR (big screen + phone)
   ═══════════════════════════════════════════════════════════ */
.lt-wrap{background:#090d16;border:1px solid #1e293b;border-radius:16px;padding:clamp(16px,3vw,32px);
  color:#fff;box-shadow:0 20px 40px rgba(0,0,0,.5)}
.lt-hd{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;
  border-bottom:1px solid rgba(255,255,255,.1);padding-bottom:18px}
.lt-brand{display:flex;align-items:center;gap:14px;min-width:0;flex:1 1 240px}
/* The CAC mark is dark-on-transparent, so it needs its own light plate here. */
.lt-logo{height:clamp(38px,7vw,52px);width:auto;object-fit:contain;flex-shrink:0;
  background:#fff;border-radius:10px;padding:5px 9px}
.lt-names{min-width:0}
.lt-name{font-family:'Space Grotesk','Inter',sans-serif;font-size:clamp(15px,3.4vw,24px);font-weight:800;
  margin:0;letter-spacing:.4px;line-height:1.2;overflow-wrap:anywhere}
.lt-tag{font-size:clamp(10px,2.2vw,14px);color:#f59e0b;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;margin-top:2px}
.lt-clock{text-align:right;flex:0 0 auto;margin-left:auto}
/* The house display face rather than whatever the device calls "monospace" —
   on Android that resolved to a narrow typewriter face that matched nothing
   else on the screen. Tabular figures keep the digits from shuffling as the
   seconds tick, which is the only thing monospace was there for. */
.lt-clock-v{font-size:clamp(30px,9vw,44px);font-weight:800;
  font-family:'Space Grotesk','Inter',system-ui,sans-serif;
  color:#38bdf8;letter-spacing:1px;line-height:1;font-variant-numeric:tabular-nums}
.lt-clock-l{font-size:11px;color:#94a3b8;margin-top:4px;text-transform:uppercase;letter-spacing:.07em}
/* The banner above the monitor: which race, and when. */
.lt-banner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;
  gap:12px;padding:13px 20px;border-radius:14px;margin-bottom:16px}
.lt-banner-hd{display:flex;align-items:center;gap:12px;min-width:0;flex-wrap:wrap}
.lt-banner-hd strong{font-family:'Space Grotesk','Inter',sans-serif;font-size:17px;line-height:1.25}
.lt-banner-d{font-size:13px}
.lt-banner.live{background:linear-gradient(90deg,#15803d,#166534);color:#fff;
  border:1px solid #4ade80;box-shadow:0 6px 24px rgba(34,197,94,.35)}
.lt-banner.live .lt-banner-d{font-weight:700;opacity:.95}
.lt-banner.next{background:linear-gradient(90deg,rgba(245,158,11,.15),rgba(245,158,11,.22));
  border:1px solid rgba(245,158,11,.4);color:var(--text)}
.lt-banner.next .lt-banner-d{color:var(--text2)}

/* ── THE MONITOR ON A PHONE ────────────────────────────────────
   One centred column. Everything here is read at a glance from across
   a room or held at arm's length, and a row that wraps puts each piece
   at its own left edge — which is what made the screen look like it was
   jumping from side to side. Centred, there is one axis and nothing to
   follow across the screen. */
@media (max-width:640px){
  .lt-banner{flex-direction:column;align-items:center;text-align:center;gap:8px;padding:12px 14px}
  .lt-banner-hd{flex-direction:column;gap:7px}
  .lt-banner-hd strong{font-size:16px}
  .lt-hd{flex-direction:column;align-items:center;text-align:center;gap:12px;padding-bottom:14px}
  .lt-brand{flex:0 1 auto;flex-direction:column;align-items:center;gap:8px;text-align:center}
  .lt-clock{margin-left:0;text-align:center}
  .lt-clock-v{font-size:clamp(34px,11vw,46px);letter-spacing:2px}
  /* The heading and its badge, one above the other rather than pushed apart. */
  .lt-box-hd{flex-direction:column;align-items:center;gap:6px;text-align:center}
  .lt-box{text-align:center}
}

.lt-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,250px),1fr));
  gap:clamp(12px,2vw,24px);margin-top:clamp(18px,3vw,32px)}
/* THE TWO-UP ROWS UNDER THE TILES. The accreditation desk beside the
   weather, the broadcast feed beside the podium. Its own container rather
   than more members of .lt-grid: a full-width item inside an auto-fit grid
   stops the empty tracks collapsing, which is what left the four tiles
   above ending two columns short of the right edge. */
.lt-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr));
  gap:clamp(12px,2vw,24px);margin-top:clamp(12px,2vw,20px)}
.lt-cols > *{min-width:0}
.lt-box{border-radius:12px;padding:clamp(16px,2.4vw,24px);background:rgba(255,255,255,.03);
  border:1px solid rgba(255,255,255,.08);min-width:0}
.lt-box.gold{background:rgba(245,158,11,.08);border-color:rgba(245,158,11,.3);text-align:center}
.lt-box.blue{background:rgba(56,189,248,.08);border-color:rgba(56,189,248,.3);text-align:center}
.lt-box-hd{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:6px}
.lt-k{font-size:11px;text-transform:uppercase;letter-spacing:.09em;color:#94a3b8;line-height:1.35}
.lt-box.gold .lt-k{color:#f59e0b}
.lt-box.blue .lt-k{color:#38bdf8}
.lt-v{font-size:clamp(20px,5vw,22px);font-weight:700;color:#f8fafc;margin-top:8px;overflow-wrap:anywhere}
.lt-big{font-size:clamp(26px,8vw,36px);font-weight:800;margin-top:8px;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
.lt-box.gold .lt-big{color:#fbbf24}
.lt-box.blue .lt-big{color:#38bdf8}
.lt-d{font-size:12px;color:#cbd5e1;margin-top:4px;line-height:1.4;overflow-wrap:anywhere}
.lt-s{font-size:11.5px;color:#94a3b8;margin-top:6px}

/* Live broadcast feed rows. These used to carry their layout inline, so the
   message column could not be re-flowed on a narrow screen: the three action
   buttons claimed a fixed 140px of a 279px row and squeezed the update text
   into a ~19px ribbon, one character per line. Below 700px the row stacks
   instead — message on top, actions on their own line. */
.lt-ticker-row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:10px 14px;background:var(--bg3);border-radius:10px;border-left:3px solid var(--gold2);
  margin-bottom:8px}
.lt-ticker-main{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}
.lt-ticker-time{font-family:'Space Grotesk','Inter',sans-serif;font-size:13px;color:var(--gold2);flex-shrink:0}
.lt-ticker-tag{font-size:10px;padding:3px 8px;flex-shrink:0}
.lt-ticker-msg{font-size:13.5px;font-weight:600;color:var(--text);flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
.lt-ticker-acts{display:flex;gap:4px;flex-shrink:0}
@media (max-width:700px){
  .lt-ticker-row{flex-direction:column;align-items:stretch;gap:8px}
  .lt-ticker-main{flex-wrap:wrap;align-items:baseline;gap:8px}
  .lt-ticker-msg{flex:1 1 100%;min-width:100%}
  .lt-ticker-acts{justify-content:flex-end}
}

/* The monitor was built for a darkened press room and hard-codes its palette,
   so switching the app to light left this one view stubbornly black. The panel
   keeps its own identity in light mode — a white board with the same accent
   colours — rather than inheriting the generic card styling. */
body.light .lt-wrap{background:#fff;border-color:var(--border);color:var(--text);
  box-shadow:0 10px 30px rgba(15,23,42,.10)}
body.light .lt-hd{border-bottom-color:var(--border)}
body.light .lt-logo{background:transparent;padding:0}
body.light .lt-tag{color:var(--gold)}
body.light .lt-clock-v{color:#0369a1}
body.light .lt-clock-l{color:var(--text2)}
body.light .lt-box{background:var(--bg3);border-color:var(--border)}
body.light .lt-box.gold{background:rgba(180,83,9,.07);border-color:var(--gold-border)}
body.light .lt-box.blue{background:rgba(3,105,161,.07);border-color:rgba(3,105,161,.28)}
body.light .lt-k{color:var(--text2)}
body.light .lt-box.gold .lt-k{color:var(--gold)}
body.light .lt-box.blue .lt-k{color:#0369a1}
body.light .lt-v{color:var(--text)}
body.light .lt-box.gold .lt-big{color:var(--gold)}
body.light .lt-box.blue .lt-big{color:#0369a1}
body.light .lt-d{color:var(--text2)}
body.light .lt-s{color:var(--text3)}

/* Medical dashboard: hospital list beside the incident log. The fixed 340px
   log column used to hold its width on a phone, crushing the hospital column
   to 40px, so the two stack below 900px. */
.med-cols{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:20px;align-items:start}
@media (max-width:900px){
  .med-cols{grid-template-columns:minmax(0,1fr)}
}

/* A countdown that has not started running yet must not read as a live figure.
   Declared after the light-mode block so it wins in both themes. */
.lt-box.is-idle{opacity:.62}
.lt-box.is-idle .lt-big,
body.light .lt-box.is-idle .lt-big{color:var(--text3)}

/* ═══════════════════════════════════════════════════════════
   BRANDED EXPORT HEADER + THEME-AWARE PARTNER LOGO
   The Golazo mark ships in white and green; showing the wrong one
   made it invisible on a light card, so both are in the DOM and CSS
   picks. Keeping it in CSS means a theme toggle needs no re-render.
   ═══════════════════════════════════════════════════════════ */
.golazo-light{display:none}
body.light .golazo-dark{display:none}
body.light .golazo-light{display:inline-block}

.rep-hd{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;
  padding-bottom:12px;margin-bottom:16px;border-bottom:1px solid var(--border)}
.rep-hd-l{display:flex;align-items:center;gap:12px;min-width:0}
.rep-logo{height:38px;width:auto;object-fit:contain;background:#fff;border-radius:8px;padding:4px 7px;flex-shrink:0}
.rep-t{min-width:0}
.rep-t strong{display:block;font-size:15px;font-weight:700;color:var(--text);line-height:1.3}
.rep-t span{display:block;font-size:11.5px;color:var(--text2);margin-top:2px}
.rep-hd img.rep-golazo{height:24px;width:auto;object-fit:contain;flex-shrink:0}

/* ═══════════════════════════════════════════════════════════
   FEDERATIONS — matrix on desktop, stacked cards on a phone
   ═══════════════════════════════════════════════════════════ */
.fed-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
.fed-cards{display:none;flex-direction:column;gap:10px}
/* A <details> per nation, SHUT by default: thirty-six of them open is
   four hundred rows of numbers to scroll past to reach the one that was
   being looked for. The head keeps the flag, the code and the total —
   the line that answers most visits without opening anything. */
.fed-card{border:1px solid var(--border);border-radius:11px;background:var(--bg3);padding:12px 14px}
.fed-card-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding-bottom:9px;margin-bottom:9px;border-bottom:1px solid var(--border)}
details.fed-card > summary.fed-card-hd{cursor:pointer;list-style:none;
  padding-bottom:0;margin-bottom:0;border-bottom:0}
details.fed-card > summary.fed-card-hd::-webkit-details-marker{display:none}
details.fed-card[open] > summary.fed-card-hd{padding-bottom:9px;margin-bottom:9px;
  border-bottom:1px solid var(--border)}
.fed-card-caret{display:flex;flex:none;color:var(--text3)}
.fed-card-caret svg{width:16px;height:16px;transition:transform .18s ease}
details.fed-card[open] .fed-card-caret svg{transform:rotate(180deg)}
.fed-card-hd .nat-pill{font-weight:800;font-size:13px}
.fed-card-tot{font-family:'Space Grotesk','Inter',sans-serif;font-size:19px;font-weight:700;color:var(--gold2);
  font-variant-numeric:tabular-nums}
body.light .fed-card-tot{color:var(--gold)}
.fed-card-tot small{display:block;font-family:'Inter',sans-serif;font-size:9px;font-weight:800;
  text-transform:uppercase;letter-spacing:.08em;color:var(--text3);text-align:right}
.fed-card-rows{display:flex;flex-direction:column;gap:5px}
.fed-card-row{display:flex;align-items:center;justify-content:space-between;gap:10px;font-size:12.5px}
.fed-card-row span{color:var(--text2);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fed-card-row b{font-weight:800;color:var(--text);font-variant-numeric:tabular-nums;flex-shrink:0}
.fed-card-row.zero b{color:var(--text3);font-weight:600}

@media (max-width:760px){
  .fed-table-wrap{display:none}
  .fed-cards{display:flex}
  .ch-bar-lab{width:66px;font-size:11px}
  .ch-bar-lab.has-flag{width:80px;gap:5px}
  .ch-bar-flag{width:18px;height:13px}
  .ch-bar-jersey{width:19px;height:19px}
  .ch-bars-wide .ch-bar-lab{width:96px}
  .ch-bars-wide .ch-bar-lab.has-flag{width:112px}
}
/* The PDF of this screen is a sheet of its own — fedSheetHtml() in
   js/startlists.js, styled under .pfs-doc in css/components.css. */

/* A field the app is filling in for you reads as a result, not an input. */
.field input.is-derived{background:var(--bg4);color:var(--gold2);font-weight:700;cursor:default}
body.light .field input.is-derived{color:var(--gold)}

/* ── START-LIST ROW EDITING ────────────────────────────────── */
.sl-tbl .c-edit{width:44px;text-align:right;padding-right:8px}
.sl-tbl .c-edit .icon-btn{min-width:32px;min-height:32px;border-color:var(--border);
  background:var(--bg3);color:var(--text3)}
.sl-tbl .c-edit .icon-btn:hover{border-color:var(--gold-border);color:var(--gold2);background:var(--bg4)}
.sl-tbl .c-edit .icon-btn svg{width:14px;height:14px}
body.light .sl-tbl .c-edit .icon-btn:hover{color:var(--gold)}
.sl-sheet-ft{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:12px 20px 16px;border-top:1px solid var(--border)}
.sl-sheet-ft .hint{font-size:12px;color:var(--text3)}

/* ── WEATHER FORECAST ──────────────────────────────────────── */
.weather-day-box svg{width:12px;height:12px;vertical-align:-2px}

/* ═══════════════════════════════════════════════════════════
   LOCATION PICKER (country + city search)
   Results sit in the flow rather than floating: the modal body is
   the scroll container, and an absolutely positioned dropdown gets
   clipped by it.
   ═══════════════════════════════════════════════════════════ */
.geo-row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);gap:10px}
.geo-search{position:relative;min-width:0}
.geo-search input{width:100%;padding-right:38px}
.geo-search-ico{position:absolute;right:11px;top:50%;transform:translateY(-50%);
  color:var(--text3);pointer-events:none;display:flex}
.geo-search-ico svg{width:16px;height:16px}

.geo-results{margin-top:8px;border:1px solid var(--border2);border-radius:10px;
  background:var(--bg3);overflow:hidden;max-height:264px;overflow-y:auto}
.geo-results[hidden]{display:none}
.geo-hit{display:grid;grid-template-columns:1fr auto;gap:2px 10px;width:100%;text-align:left;
  padding:10px 13px;background:none;border:none;border-bottom:1px solid var(--border);
  cursor:pointer;font-family:inherit;transition:background .14s;min-height:44px}
.geo-hit:last-child{border-bottom:none}
.geo-hit:hover,.geo-hit:focus-visible{background:var(--gold-bg)}
.geo-hit-n{font-size:14px;font-weight:700;color:var(--text)}
.geo-hit-m{grid-column:1;font-size:12px;color:var(--text2)}
.geo-hit-tz{grid-row:1 / span 2;align-self:center;font-size:11px;font-weight:800;
  color:var(--gold2);font-variant-numeric:tabular-nums;white-space:nowrap}
body.light .geo-hit-tz{color:var(--gold)}
.geo-note{padding:13px;font-size:12.5px;color:var(--text2);line-height:1.45}

.geo-picked{display:none}
.geo-picked.on{display:flex;align-items:center;gap:11px;margin-top:10px;padding:11px 13px;
  border-radius:10px;background:var(--gold-bg);border:1px solid var(--gold-border)}
.geo-pin{display:flex;color:var(--gold2);flex-shrink:0}
body.light .geo-pin{color:var(--gold)}
.geo-pin svg{width:17px;height:17px}
.geo-picked-b{min-width:0}
.geo-picked-b strong{display:block;font-size:13.5px;font-weight:700;color:var(--text);line-height:1.3}
.geo-picked-b small{display:block;font-size:11.5px;color:var(--text2);margin-top:2px;
  font-variant-numeric:tabular-nums;overflow-wrap:anywhere}

.geo-toggle{display:inline-flex;align-items:center;gap:6px;margin-top:10px;padding:6px 2px;
  background:none;border:none;cursor:pointer;font-family:inherit;font-size:12px;
  font-weight:600;color:var(--text3);min-height:34px}
.geo-toggle:hover{color:var(--gold2)}
body.light .geo-toggle:hover{color:var(--gold)}
.geo-toggle svg{width:13px;height:13px}
.geo-manual[hidden]{display:none}
.geo-manual .field-row{margin-bottom:0}

@media (max-width:520px){
  .geo-row{grid-template-columns:1fr}
}

/* ═══════════════════════════════════════════════════════════
   PWA INSTALL PROMPT
   A dismissible banner rather than a button that sits in the nav
   forever — the point is to get the real browser install dialog up.
   ═══════════════════════════════════════════════════════════ */
#pwa-banner{position:fixed;z-index:var(--z-toast);left:16px;right:16px;bottom:calc(16px + env(safe-area-inset-bottom));
  max-width:430px;margin:0 auto;display:flex;align-items:center;gap:13px;
  background:var(--bg2);border:1px solid var(--gold-border);border-radius:14px;padding:14px 16px;
  box-shadow:0 18px 44px rgba(0,0,0,.45);animation:fadeUp .35s ease-out}
#pwa-banner[hidden]{display:none}
.pwa-icon{width:46px;height:46px;border-radius:11px;flex-shrink:0;object-fit:contain;box-shadow:var(--shadow)}
.pwa-txt{flex:1;min-width:0}
.pwa-txt strong{display:block;font-size:13.5px;font-weight:700;color:var(--text)}
.pwa-txt span{display:block;font-size:11.5px;color:var(--text2);line-height:1.4;margin-top:2px}
.pwa-acts{display:flex;flex-direction:column;gap:6px;flex-shrink:0}
.pwa-acts .btn{min-height:34px;padding:0 12px;font-size:12px}

@media (max-width:520px){
  #pwa-banner{flex-wrap:wrap}
  .pwa-acts{flex-direction:row;width:100%}
  .pwa-acts .btn{flex:1}
}

/* ── PRINT ─────────────────────────────────────────────────── */
@media print{
  #pwa-banner{display:none!important}
  .ch-seg,.ch-bar-fill,.ch-leg{animation:none!important}
  .fed-table-wrap{display:block!important}
  .fed-cards{display:none!important}
  #bgfx,#sidebar,#header,#subbar,#ro-banner,.btn,.icon-btn,#toast{display:none!important}
  body{height:auto;overflow:visible;background:#fff;color:#000;font-size:11pt}
  #body,#main,#content{display:block;overflow:visible;padding:0}
  .view{display:none} .view.active{display:block}
  .card,.tl-wrap{border:1px solid #ccc;box-shadow:none;background:#fff;page-break-inside:avoid}
  .tl-inner{min-width:0}
  .tbl th,.tbl td{border-color:#ccc}
  /* On a printed start list keep the branded sheet only — the picker,
     schedule controls and apply banner are working tools, not output. */
  .sl-bar,.auto-panel,.sl-apply,#v-startlists .stat-grid,#v-startlists .page-hd{display:none!important}
  .sl-sheet{page-break-inside:auto}
  .sl-tbl tbody tr{page-break-inside:avoid}
}

/* ── LIVE STATUS BADGES ───────────────────────────────────── */
@keyframes live-pulse {
  0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.8); transform: scale(1); }
  50% { box-shadow: 0 0 0 12px rgba(34, 197, 94, 0); transform: scale(1.04); }
  100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); transform: scale(1); }
}
@keyframes live-dot-blink {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.2; transform: scale(0.7); }
}

.badge-live-now {
  background: linear-gradient(135deg, #22c55e, #15803d) !important;
  color: #ffffff !important;
  font-weight: 800 !important;
  letter-spacing: 0.07em !important;
  text-transform: uppercase !important;
  border: 1px solid #4ade80 !important;
  padding: 6px 14px !important;
  border-radius: 20px !important;
  animation: live-pulse 1.8s infinite !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 8px !important;
  font-size: 12px !important;
  box-shadow: 0 0 16px rgba(34, 197, 94, 0.6) !important;
  vertical-align: middle;
}
/* Smooth scrolling and the LIVE badge's pulse only for somebody who has
   not asked for less motion. The badge's own !important beat the global
   reduced-motion rule, so it kept pulsing (impeccable audit, 5 Oct 2026). */
@media (prefers-reduced-motion:no-preference){#content{scroll-behavior:smooth}}
@media (prefers-reduced-motion:reduce){
  .badge-live-now{animation:none!important}
  .badge-live-now .live-dot{animation:none}
}
.badge-live-now .live-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #ffffff;
  animation: live-dot-blink 1s infinite;
  display: inline-block;
  box-shadow: 0 0 6px #ffffff;
}

.badge-upcoming {
  background: rgba(245, 158, 11, 0.15) !important;
  color: #f59e0b !important;
  border: 1px solid rgba(245, 158, 11, 0.4) !important;
  font-weight: 800 !important;
  letter-spacing: 0.05em !important;
  text-transform: uppercase !important;
  padding: 5px 12px !important;
  border-radius: 20px !important;
  font-size: 11.5px !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 6px !important;
  vertical-align: middle;
}

.badge-finished {
  background: rgba(239, 68, 68, 0.15) !important;
  color: #ef4444 !important;
  border: 1px solid rgba(239, 68, 68, 0.4) !important;
  font-weight: 800 !important;
  letter-spacing: 0.05em !important;
  text-transform: uppercase !important;
  padding: 5px 12px !important;
  border-radius: 20px !important;
  font-size: 11.5px !important;
  display: inline-flex !important;
  align-items: center !important;
  gap: 6px !important;
  vertical-align: middle;
}


/* The dashboard's road card, and the way through to the full screen. */
.road-status .rs-hd{display:flex;align-items:center;justify-content:space-between;gap:10px}
.rs-more{display:inline-flex;align-items:center;gap:5px;flex-shrink:0;min-height:30px;
  padding:0 10px;border-radius:8px;border:1px solid var(--border2);background:var(--bg3);
  color:var(--text2);font-family:inherit;font-size:11px;font-weight:800;text-transform:uppercase;
  letter-spacing:.05em;cursor:pointer;transition:color .16s,border-color .16s}
.rs-more:hover{color:var(--gold2);border-color:var(--gold-border)}

/* ── DIRECTORY & ANTI-DOPING ─────────────────────────────────
   A phone list read one-handed, often outdoors in the sun. Hence the
   generous tap targets on the tel: rows and the group accent carried as a
   left border rather than as a coloured background — a tinted card behind a
   phone number is the first thing to become unreadable in daylight. */
/* A page heading with its actions in the top right. Kept as a modifier
   rather than folded into .page-hd: that one is capped at 72ch so the
   sentence under a title stays readable, and every other screen still wants
   exactly that. */
.page-hd.with-actions{display:flex;align-items:flex-start;justify-content:space-between;
  gap:16px;flex-wrap:wrap;max-width:none}
.page-hd-text{max-width:72ch;min-width:0}
/* `margin-left:auto` and not only the container's space-between: when the
   sentence under the title is long enough to take the whole first line,
   the actions wrap onto a line of their own — and space-between puts a
   lone item at the START of its line, which dropped them back to the left
   exactly where they used to be. */
.page-hd-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex-shrink:0;
  margin-left:auto;justify-content:flex-end}
/* A CARD WHOSE HEADING CARRIES ITS OWN ACTIONS — the same bargain as the
   page heading above, one level in. Buttons belong in the corner of the
   thing they act on, not at the foot of it where a long card puts them
   under the fold. */
.card-hd{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap}
.card-hd > div:first-child{flex:1;min-width:0}
.card-hd-acts{display:flex;align-items:center;gap:8px;flex-shrink:0;flex-wrap:wrap;
  justify-content:flex-end}

.dir-group{margin-bottom:26px}
.dir-group-hd{display:flex;align-items:center;gap:10px;margin:0 0 12px;padding-left:10px;
  font-family:'Space Grotesk','Inter',sans-serif;font-size:13px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;color:var(--text2)}
.dir-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;
  padding:0 6px;border-radius:10px;background:var(--bg3);border:1px solid var(--border);
  font-size:11px;font-weight:700;color:var(--text3)}

.dir-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(268px,100%),1fr));gap:12px}
.dir-card{padding:14px}
.dir-card-top{display:flex;align-items:flex-start;justify-content:space-between;gap:8px}
.dir-actions{display:flex;align-items:center;gap:4px;flex-shrink:0}
.dir-actions .tag{padding:2px 5px;color:var(--text3)}
.dir-name{font-size:15px;font-weight:700;color:var(--text);line-height:1.25}
.dir-role{font-size:12.5px;color:var(--gold2);font-weight:600;margin-top:2px}
.dir-org{font-size:12px;color:var(--text3);margin-top:1px}
.dir-meta{display:flex;flex-wrap:wrap;align-items:center;gap:6px 12px;margin-top:9px;
  font-size:12px;color:var(--text2)}
.dir-meta svg{width:13px;height:13px;color:var(--text3);vertical-align:-2px;margin-right:3px}
.dir-langs{padding:1px 7px;border-radius:6px;background:var(--bg3);border:1px solid var(--border);
  font-size:11px;font-weight:700;letter-spacing:.03em;color:var(--text3)}

/* 40 px of height is the point of these rows: they are pressed with a thumb
   while holding something else. */
.dir-call{display:flex;align-items:center;gap:8px;min-height:40px;margin-top:8px;padding:0 11px;
  border-radius:9px;border:1px solid var(--border2);background:var(--bg3);
  color:var(--text);font-size:13.5px;font-weight:600;font-variant-numeric:tabular-nums;
  text-decoration:none;transition:border-color .16s,color .16s}
.dir-call:hover{border-color:var(--gold-border);color:var(--gold2)}
.dir-call svg{width:15px;height:15px;flex-shrink:0;color:var(--text3)}
.dir-call:hover svg{color:var(--gold2)}
.dir-call.alt{font-size:12.5px;font-weight:500;color:var(--text2);min-height:34px}
/* WhatsApp keeps its own colour because on this screen it is the row that
   gets pressed: for a foreign number it is the difference between ringing
   and not ringing. Green on the border and the icon only — a filled green
   button would outrank the emergency numbers elsewhere in the app.

   Dark is the base here, as everywhere in this file; body.light overrides.
   WhatsApp green (#25d366) is legible on the dark ground but too pale on
   white, so the light theme drops to a deeper green rather than reusing it. */
.dir-call.wa{border-color:rgba(37,211,102,.42);background:rgba(37,211,102,.07)}
.dir-call.wa svg{color:#25d366}
.dir-call.wa:hover{border-color:#25d366;color:#4ade80}
.dir-call.wa:hover svg{color:#4ade80}
.dir-call-tag{margin-left:auto;flex-shrink:0;padding:1px 6px;border-radius:5px;
  background:rgba(37,211,102,.14);color:#4ade80;
  font-size:9.5px;font-weight:800;text-transform:uppercase;letter-spacing:.05em}
body.light .dir-call.wa{border-color:rgba(18,140,74,.4);background:rgba(18,140,74,.07)}
body.light .dir-call.wa svg{color:#128c4a}
body.light .dir-call.wa:hover{border-color:#128c4a;color:#0f6f3b}
body.light .dir-call.wa:hover svg{color:#0f6f3b}
body.light .dir-call-tag{background:rgba(18,140,74,.13);color:#0f6f3b}
.dir-call span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.dir-note{margin-top:9px;padding:8px 10px;border-radius:8px;background:var(--bg3);
  border:1px dashed var(--border2);font-size:12px;color:var(--text3);line-height:1.45}

/* The doping station now lives in the schedule's side column, beside the
   accreditation centre it resembles — hence the narrower margin than the
   full-width card it used to be on the directory screen. */
.dope-card{padding:16px;margin-bottom:16px;border-left:4px solid #06b6d4}
.dope-card.empty-dope{border-left-style:dashed}
.dope-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
.dope-hd h3{display:flex;align-items:center;gap:8px;margin:0;
  font-family:'Space Grotesk','Inter',sans-serif;font-size:15px;font-weight:700;color:var(--text)}
.dope-hd h3 svg{width:17px;height:17px;color:#06b6d4}
.dope-empty{margin-top:10px;font-size:13px;color:var(--text3);line-height:1.5;max-width:68ch}
.dope-body{display:flex;flex-wrap:wrap;align-items:flex-start;gap:14px 26px;margin-top:13px}
.dope-venue{font-size:15px;font-weight:700;color:var(--text)}
.dope-room{font-size:12.5px;color:var(--gold2);font-weight:600;margin-top:2px}
.dope-addr{font-size:12.5px;color:var(--text3);margin-top:2px}
.dope-hours{display:flex;align-items:center;gap:7px;font-size:14px;font-weight:700;
  color:var(--text);font-variant-numeric:tabular-nums}
.dope-hours svg{width:15px;height:15px;color:var(--text3)}
.dope-officer{padding-left:16px;border-left:1px solid var(--border)}
.dope-officer .dir-call{margin-top:6px}
.dope-notes{margin-top:12px;font-size:12.5px;color:var(--text2);line-height:1.5;max-width:72ch}

@media (max-width:640px){
  .dir-grid{grid-template-columns:1fr}
  .dope-officer{padding-left:0;border-left:0;border-top:1px solid var(--border);padding-top:12px;width:100%}
}

/* ── CONTACT PORTRAIT & BUSINESS CARD ────────────────────────
   The disc is 44 px on the card: big enough to recognise a face at arm's
   length, small enough that the name still leads. Initials rather than a
   silhouette when there is no photograph — a coloured disc reading "AO"
   looks like a person, a grey placeholder looks like a broken image. */
.dir-card-top{align-items:flex-start}
.dir-who{flex:1;min-width:0}
.dir-avatar{width:44px;height:44px;flex-shrink:0;margin-right:11px;border-radius:50%;
  overflow:hidden;background:var(--bg3);border:1px solid var(--border2)}
.dir-avatar img{width:100%;height:100%;object-fit:cover;display:block}
.dir-avatar.is-initials{display:flex;align-items:center;justify-content:center;
  background:color-mix(in srgb, var(--acc) 18%, var(--bg3));
  border-color:color-mix(in srgb, var(--acc) 40%, var(--border2));
  color:var(--acc);font-size:14px;font-weight:800;letter-spacing:.02em}

.dir-vcard-btn{display:flex;align-items:center;justify-content:center;gap:7px;width:100%;
  min-height:34px;margin-top:10px;padding:0 10px;border-radius:9px;
  border:1px dashed var(--border2);background:transparent;color:var(--text3);
  font-family:inherit;font-size:11.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.04em;cursor:pointer;transition:color .16s,border-color .16s}
.dir-vcard-btn:hover{color:var(--gold2);border-color:var(--gold-border)}
.dir-vcard-btn svg{width:14px;height:14px}

.dir-photo-row{display:flex;align-items:flex-start;gap:14px}
.dir-photo-actions{flex:1;min-width:0;display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.dir-photo-actions .hint{width:100%;margin:0;font-size:12px;color:var(--text3)}
.dir-avatar.dir-photo-lg{width:76px;height:76px;margin-right:0;font-size:23px}

/* The card carries its own light palette, like the QR poster and the
   booklet: it gets printed and screenshotted, and one photographed out of
   the dark theme arrives as a black rectangle. */
.vcard{max-width:390px;margin:0 auto;background:#fff;color:#0f172a;border-radius:14px;
  overflow:hidden;border:1px solid #cbd5e1}
.vcard-band{height:6px}
.vcard-top{display:flex;align-items:center;gap:14px;padding:18px 20px 14px}
.vcard .dir-avatar.vcard-face{width:66px;height:66px;margin-right:0;border-color:#cbd5e1;background:#f1f5f9}
.vcard-who{min-width:0}
.vcard-name{font-size:19px;font-weight:800;line-height:1.2;color:#0f172a}
.vcard-role{font-size:13px;font-weight:700;margin-top:2px}
.vcard-org{font-size:12.5px;color:#64748b;margin-top:1px}
.vcard-body{display:flex;gap:16px;padding:0 20px 16px;align-items:flex-start}
.vcard-row{display:flex;flex-direction:column;padding:5px 0;border-bottom:1px solid #eef2f7}
.vcard-row:last-child{border-bottom:0}
.vcard-row span{font-size:9.5px;font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:#94a3b8}
.vcard-row b{font-size:13px;font-weight:600;color:#0f172a;word-break:break-word}
/* Fixed width, so whatever ends up in the box — a code, a message, nothing
   at all — the details column keeps its share of the card. */
.vcard-qr{flex:0 0 148px;width:148px;max-width:148px;text-align:center;overflow:hidden}
.vcard-rows{flex:1 1 auto;min-width:0}
.vcard-qr img,.vcard-qr canvas{display:block;border:1px solid #e2e8f0;border-radius:8px}
.vcard-qr p{margin-top:6px;font-size:10px;font-weight:700;text-transform:uppercase;
  letter-spacing:.05em;color:#94a3b8}
.vcard-foot{padding:9px 20px;background:#f8fafc;border-top:1px solid #eef2f7;
  font-size:11px;color:#64748b;text-align:center}

@media (max-width:520px){
  .vcard-body{flex-direction:column;align-items:center}
  .vcard-rows{width:100%}
}

/* ── DIRECTORY: PHOTO, CAMERA, IMPORT ────────────────────────
   The camera replaces the avatar slot rather than opening a second dialog:
   a modal inside a modal is where focus handling goes wrong, and the slot
   is exactly where the result is going to land anyway. */
.dir-photo-row{display:flex;align-items:flex-start;gap:14px;flex-wrap:wrap}
.dir-photo-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;flex:1;min-width:200px}
.dir-photo-actions .hint{flex-basis:100%;margin:0}
.dir-avatar.dir-photo-lg{width:76px;height:76px;font-size:24px;flex-shrink:0}

.dir-cam{display:flex;flex-direction:column;gap:8px}
.dir-cam video{width:180px;height:180px;object-fit:cover;border-radius:12px;
  background:#000;border:1px solid var(--border2)}
.dir-cam-btns{display:flex;gap:6px;flex-wrap:wrap}

.imp-actions{display:flex;gap:8px;margin:-4px 0 12px}
#i-imp-text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}
#i-imp-text.drop{border-color:var(--gold2);background:var(--gold-bg)}
.imp-preview:empty{display:none}
.imp-preview{margin-top:12px;padding:12px;border-radius:10px;
  background:var(--bg3);border:1px solid var(--border)}
.imp-sum{font-size:14px;color:var(--text);margin-bottom:4px}
.imp-n{font-weight:800;color:var(--gold2);font-size:16px}
.imp-dupe{font-weight:800;color:var(--text3)}
.imp-cols{font-size:11.5px;color:var(--text3);margin-bottom:10px}
.imp-warn{font-size:13px;color:var(--red);line-height:1.5}
.imp-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.imp-tbl th{text-align:left;font-size:10px;text-transform:uppercase;letter-spacing:.05em;
  color:var(--text3);padding:0 8px 5px 0;border-bottom:1px solid var(--border)}
.imp-tbl td{padding:5px 8px 5px 0;border-bottom:1px solid var(--border);vertical-align:top;
  color:var(--text2)}
.imp-tbl td b{color:var(--text)}
.imp-tbl svg{width:14px;height:14px;color:#25d366}
.imp-dim{font-size:11px;color:var(--text3)}
.imp-more{font-size:11.5px;color:var(--text3);margin-top:8px}
.imp-note{font-size:11.5px;color:var(--text2);margin-top:10px;padding-top:9px;
  border-top:1px dashed var(--border2);line-height:1.45}

/* ── FLAG BESIDE A NATIONALITY ─────────────────────────────────
   A hairline border and a slight radius, because a lot of national flags
   have white at the edge and would otherwise bleed into a pale card with
   no shape at all. 21x16 is the 4:3 artwork at the size the code pill
   beside it already occupies, so the row height does not change.
   ──────────────────────────────────────────────────────────── */
.nat-cell{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.flag{width:21px;height:16px;flex-shrink:0;object-fit:cover;border-radius:2.5px;
  box-shadow:0 0 0 1px rgba(15,23,42,.22)}
body.light .flag{box-shadow:0 0 0 1px rgba(15,23,42,.18)}
/* On the printed sheet the shadow is not printed by most browsers, so the
   edge is drawn as a real border there instead. */
@media print{ .flag{box-shadow:none;border:.5pt solid #999} }

/* ── NAMING THE DIRECTORY CATEGORIES ───────────────────────────
   A row per category: its colour, its name, how many people are in it,
   and the way to strike it off. See openContactGroupsModal(). */

/* ── Ported from the Golazo Sports Manager (see components.css) ── */
[hidden]{display:none !important}
@media (max-width:1100px){
.lt-grid{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:640px){
.lt-grid{grid-template-columns:1fr}
}
.ev-logo{background:#fff;border-radius:8px;padding:4px 7px}
body.light .ev-logo{background:transparent;padding:0}
.ssh-golazo.ev-logo{height:34px}
@media (max-width:640px){
.ssh-golazo.ev-logo{height:28px}
}
.fed-team{display:flex;align-items:center;gap:10px;min-width:0}
.fed-team .jersey.fed-shirt{width:30px;height:30px;object-fit:contain;flex-shrink:0}
.fed-shirt-none{width:30px;height:30px;flex-shrink:0;border:1px dashed var(--border2);
  border-radius:6px}
.fed-team-t{min-width:0;display:flex;flex-direction:column;gap:2px}
.fed-team-t strong{font-size:13px;font-weight:700;color:var(--text);line-height:1.25}
.fed-team-t small{display:flex;align-items:center;gap:5px;font-size:10.5px;color:var(--text3);
  font-weight:700;letter-spacing:.03em}
.fed-team-t small img.flag{width:16px;height:auto;border-radius:2px}
.fed-jersey-btn{margin-left:auto;flex-shrink:0;opacity:0;transition:opacity .12s ease}
tr:hover .fed-jersey-btn,.fed-jersey-btn:focus-visible{opacity:1}
@media (hover:none){ .fed-jersey-btn{opacity:1} }
.sl-sheet.pdf-paper{
  --bg:#ffffff; --bg2:#ffffff; --bg3:#f8fafc; --bg4:#f1f5f9;
  --border:#cbd5e1; --border2:#e2e8f0;
  --text:#0f172a; --text2:#334155; --text3:#64748b; --bright:#0f172a;
  --green:#15803d; --green-bg:#eafaf1; --green-border:#a7e3c4;
  --red:#b91c1c; --red-bg:#fdeaea; --red-border:#f0b4b4;
  --amber:#a16207; --gold:#b45309; --gold2:#a16207;
  --gold-bg:#f1f5f9; --gold-border:#cbd5e1; --gold-light:#f8fafc;
  --blue:#1d4ed8; --accent:#b45309; --accent2:#a16207;
  --shadow:none; --shadow-md:none;
  background:#ffffff; color:#0f172a; border:1px solid #cbd5e1;
}
.sl-sheet.pdf-paper .sl-sheet-hd{background:#ffffff;background-image:none;
  border-bottom:2px solid #0f172a}
.sl-sheet.pdf-paper .ssh-title{color:#0f172a}
.sl-sheet.pdf-paper .ssh-meta{color:#334155}
.sl-sheet.pdf-paper .ssh-event{color:#64748b}
.sl-sheet.pdf-paper .sl-tbl th{background:#f1f5f9;color:#0f172a;border-color:#cbd5e1}
.sl-sheet.pdf-paper .sl-tbl td{color:#0f172a;border-color:#e2e8f0}
.sl-sheet.pdf-paper .sl-tbl tbody tr:nth-child(even) td{background:#f8fafc}
.sl-sheet.pdf-paper .bib{background:#f1f5f9;color:#0f172a;border:1px solid #cbd5e1}
.sl-sheet.pdf-paper .nat-pill{background:#e2e8f0;color:#0f172a;border-color:#cbd5e1}
.sl-sheet.pdf-paper .c-name-sub{color:#64748b}
/* THE CATEGORY CHIP PRINTS IN INK. On screen it carries the category's
   own colour, which on a white sheet is a pale green nobody can read at
   a barrier — and html2canvas does not understand the color-mix() that
   mixes it. Flat ink on light grey, like the bib and the nation pill
   beside it. */
.sl-sheet.pdf-paper .c-cat .tag.cat{background:#f1f5f9;border-color:#cbd5e1;color:#0f172a}
.sl-sheet.pdf-paper .c-chk,.sl-sheet.pdf-paper .c-edit{display:none}
.clos-tbl-wrap{overflow-x:auto}
@media (max-width:640px){
.clos-tbl-wrap{overflow-x:visible}
.clos-tbl,.clos-tbl tbody,.clos-tbl tr,.clos-tbl td{display:block;width:auto}
.clos-tbl thead{display:none}
.clos-tbl tr{border:1px solid var(--border);border-radius:11px;background:var(--bg2);
    padding:11px 13px;margin-bottom:10px}
.clos-tbl td{border:none;padding:3px 0;font-size:13px}
.clos-tbl tr:last-child td{border-bottom:none}
.clos-tbl td.c-place{font-size:15px;line-height:1.3;padding-bottom:6px}
.clos-tbl td.c-place strong{font-weight:800}
.clos-tbl td.c-from,.clos-tbl td.c-to,.clos-tbl td.c-dur{
    display:inline-block;width:auto;margin-right:14px}
.clos-tbl td[data-lbl]::before{
    content:attr(data-lbl);
    display:block;font-size:9.5px;font-weight:800;text-transform:uppercase;
    letter-spacing:.07em;color:var(--text3);margin-bottom:1px}
.clos-tbl td.c-state{padding-top:7px}
.clos-tbl td.c-why:empty{display:none}
.clos-tbl td.c-act{padding-top:9px;display:flex;gap:8px}
.clos-tbl .pill{white-space:nowrap}
}

/* The closures toolbar lives in the page-hd corner now, like every other
   screen — see renderClosures(). Its old .clos-title-row phone block went
   with it. */

/* ── NOTHING DRAGS THE PAGE SIDEWAYS ───────────────────────────
   Several screens put their exports in a row beside the heading, and the
   row was flex-shrink:0 so it would never be squeezed on a desk. On a
   390 px phone that same rule makes the row as wide as its buttons laid
   end to end — and because the row is the width of the page, the whole
   page could be dragged to the right, taking the heading and the content
   off the screen with it.

   Two rules rather than one per screen: the toolbars share their width,
   and #content refuses to be wider than the phone. Anything that genuinely
   has to scroll sideways — a wide table, the closures timeline — carries
   its own overflow-x container, and those keep working inside a clipped
   parent. */
@media (max-width:640px){
  #content{overflow-x:hidden}

  /* The heading's actions STAY in the top corner on a phone — the full-width
     row of stretched buttons under the title is gone. The cluster shares the
     row with the title, sheds its labels where a button carries .icon-only-m
     (the icon plus the title attribute say the rest), and wraps under itself
     if a screen genuinely has more buttons than the corner can hold. */
  /* nowrap and a shrinkable title: .page-hd-text is capped at 72ch, which on
     a 375 px screen is wider than the screen, so the cluster was pushed onto
     a line of its own and sat at the LEFT — the corner it was asked to keep
     was the one place it never appeared. */
  .page-hd.with-actions{flex-wrap:nowrap;gap:10px}
  .page-hd-text{flex:1 1 auto;min-width:0}
  .page-hd-actions{flex-shrink:0;min-width:0;justify-content:flex-end}
  .page-hd-actions .btn{min-width:0}
  .page-hd-actions .btn span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .btn.icon-only-m span{display:none}

  .page-hd{min-width:0}
}
.cg-list{display:flex;flex-direction:column;gap:8px}
.cg-row{display:flex;align-items:center;gap:9px}
.cg-row .cg-color{width:38px;height:38px;flex-shrink:0;padding:2px;cursor:pointer;
  background:var(--bg3);border:1px solid var(--border2);border-radius:8px}
.cg-row .cg-label{flex:1;min-width:0}
.cg-row .cg-count{flex-shrink:0;min-width:30px;text-align:right;font-size:11.5px;
  font-weight:800;color:var(--text3);font-variant-numeric:tabular-nums}
.cg-row .cg-keep{flex-shrink:0;width:34px;display:flex;align-items:center;
  justify-content:center;color:var(--text3)}
.cg-row .cg-keep svg{width:14px;height:14px}

/* The category pills over the directory. They wrap on a phone rather than
   scrolling sideways: a filter you cannot see is a filter nobody uses. */
.dir-filter-tabs .btn{max-width:100%}
.dir-filter-tabs .btn span{flex-shrink:0}


/* ══════════════════════════════════════════════════════════
   PORTED FROM THE GOLAZO BUILD
   The screens that arrived with this port bring their own rules:
   wind and heat on the map, the medical plan, municipal permits,
   the daily communique, the personal day page, the hotel record
   and the jersey library. Palette mapped onto the CAC amber; the
   sheets above are untouched.
   ══════════════════════════════════════════════════════════ */
/* The H1 carries both names; the width decides which one speaks. */
.hdr-ev{display:none}
@media (max-width:640px){
  .hdr-app{display:none}
  .hdr-ev{display:inline}
}
/* Language toggle. The bare .lang-tabs strip stays as-is for the OTHER
   places that use it — the sign-in screen and the modals' language tabs.
   The header's picker wraps its strip in .lang-pick and collapses it to
   one button saying the language you are in, the other two dropping out
   of it on a tap — the phone treatment, now every width's: three tabs
   cost 100 px of bar and only one of them ever says anything. */
.lang-pick{position:relative}
/* THE SAME HEIGHT AS THE ICON BUTTONS BESIDE IT. It sits in a row with
   the share, settings and theme buttons — 44 px each — and at 38 it read
   as a smaller, lesser control rather than the fourth member of the row.
   Wider than the icons because it holds two letters and a caret, but the
   height and the corner are theirs. */
.lang-cur{display:flex;align-items:center;justify-content:center;gap:4px;
  min-width:52px;min-height:44px;padding:0 11px;border-radius:9px;cursor:pointer;
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.18);
  font-family:inherit;font-size:12px;font-weight:800;letter-spacing:.04em;color:#fff}
.lang-cur::after{content:'';width:0;height:0;margin-top:1px;
  border:4px solid transparent;border-top-color:currentColor;
  transition:transform .18s ease-out}
.lang-pick.open .lang-cur{color:var(--on-accent);border-color:transparent;
  background:linear-gradient(135deg,var(--accent-fill),var(--accent-fill2))}
.lang-pick.open .lang-cur::after{transform:rotate(180deg) translateY(2px)}
.lang-pick .lang-tabs{position:absolute;top:calc(100% + 6px);right:0;z-index:var(--z-modal);
  flex-direction:column;gap:2px;padding:4px;min-width:100%;
  background:rgba(3,37,42,.98);backdrop-filter:blur(14px);
  border:1px solid var(--gold-border);border-radius:11px;
  box-shadow:0 12px 28px rgba(0,0,0,.5);
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity .16s ease-out,transform .18s ease-out,visibility 0s linear .18s}
.lang-pick.open .lang-tabs{opacity:1;visibility:visible;transform:none;transition-delay:0s}
.lang-pick .lang-btn{min-width:54px;min-height:38px;padding:0 12px;font-size:12px}
@media (max-width:640px){
  /* A WEEK IS ONE ROW. Smaller squares and no per-day counters — the
     number is readable once the day is tapped — and, above all, the
     days never wrap: a calendar that folds Sunday under Tuesday stops
     reading as a week at all. A long edition scrolls sideways rather
     than stacking, which is the gesture a row of dates already invites.
     The month strips themselves still wrap, so an edition straddling
     November and December gets a row each. */
  .day-cal{gap:6px;padding:8px 9px;min-width:0}
  /* A month takes the whole row on a phone, so its days have a row to
     spread across — left to size itself it was only ever as wide as its
     own tiles at their smallest, and the strip sat in the left third of
     the card with the rest of the width empty beside it. An edition
     straddling two months gets a row each, which is what it should. */
  .dc-month{flex:1 1 100%;min-width:0}
  .dc-days{gap:4px;flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;
    scrollbar-width:thin}
  /* A scrolling strip clips what leans out of it, and a glow cut off
     square looks like a rendering fault rather than a highlight. The
     tile is a gold gradient under dark text — it does not need the halo
     to be the one that is chosen. */
  .dc-day.active{box-shadow:none}
  .dc-wd{font-size:9px;letter-spacing:.05em}
  .dc-meta{display:none}
}
/* The schedule's top bar: the day calendar on the left, every action
   gathered at the top right — see renderSchedule(). */
.sched-topbar{display:flex;align-items:flex-start;justify-content:space-between;
  gap:14px;flex-wrap:wrap;margin-bottom:18px}
.sched-topbar-actions{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;
  align-items:center;margin-left:auto}
/* Start → finish. Town names are proper nouns, so this is the one tag
   that keeps its own capitals instead of being shouted in caps. */
.tag.route{background:var(--bg4);border-color:var(--border2);color:var(--text);text-transform:none;
  letter-spacing:0;font-size:10.5px;font-weight:700}
.tag.route svg{width:11px;height:11px;color:var(--gold2)}
/* The forecast for this race's own town. Hidden until it has something to
   say — an empty strip under every card would be worse than no forecast. */
.rc-wx{display:none}
.rc-wx.on{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-top:10px;
  padding:7px 11px;border:1px solid var(--border);border-radius:9px;background:var(--bg3);
  font-size:12px;color:var(--text2)}
.rc-wx-i{font-size:16px;line-height:1}
.rc-wx strong{font-size:14px;color:var(--text)}
.rc-wx-lo{color:var(--text3)}
.rc-wx-s{display:inline-flex;align-items:center;gap:3px}
.rc-wx-s svg{width:12px;height:12px}
.rc-wx-s.rain{color:var(--blue)}
.rc-wx-p{margin-left:auto;font-weight:700;color:var(--text3)}
/* The UCI heat index under the forecast strip. Same bargain as .rc-wx:
   hidden until the hourly forecast reaches the race day and the number
   exists. Zone colour arrives inline from js/wbgt.js. */
.rc-wbgt{display:none}
/* The hour-by-hour chip beside it — see paintRaceHours() in js/race-weather.js. */

/* The hour-by-hour chip sits with the heat chip and wears the same
   shape — see paintRaceHours() in js/race-weather.js. Hidden until it
   has something to say, like the two forecasts above it. */
.rc-hours{display:none}

.rc-hours.on{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:8px}
.rc-wbgt.on{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin-top:8px}
.wbgt-chip{display:inline-flex;align-items:center;gap:7px;padding:6px 11px;border-radius:9px;
  border:1px solid var(--border);background:var(--bg3);color:var(--text2);
  font-size:12px;font-weight:600;cursor:pointer;font-family:inherit}
.wbgt-chip strong{font-size:13.5px;color:var(--text);font-variant-numeric:tabular-nums}
.wbgt-chip .wbgt-zone{font-weight:700;color:var(--text)}
.wbgt-dot{width:9px;height:9px;border-radius:50%;flex-shrink:0}
.wbgt-pill{display:inline-block;padding:2px 8px;border-radius:7px;border:1px solid var(--border);
  font-weight:700;font-variant-numeric:tabular-nums}
.sl-pick select:hover{border-color:var(--border)}
.sl-pick select option{background:#11252b;color:#f8fafc}
body.light .sl-pick select option{background:#fff;color:#0f172a}
/* ── START SCHEDULE + SUMMARY ──────────────────────────────────
   The panel and its four numbers side by side. Below 1180px the column
   drops underneath and the numbers spread out four across instead.
   ──────────────────────────────────────────────────────────── */
/* align-items:start, not stretch. Stretching made the schedule panel as tall
   as whatever the four cards beside it happened to need, and the difference
   came out as a band of empty tint between its heading and its controls —
   the taller the cards grew, the emptier the panel looked. Each column now
   takes the height it actually needs. */
/* The left column stacks the panel, the apply line and the search bar
   on ONE edge; the counters beside them stretch their four squares over
   the same height — no ragged widths, no dead corner. */
.sl-sched-left{display:flex;flex-direction:column;gap:12px;min-width:0}
.sl-sched-left .sl-apply{margin:0}
.sl-sched-row .sl-minis{grid-auto-rows:1fr;align-content:stretch}
.ap-f input, .ap-f select{min-height:40px;background:var(--bg3);border:1px solid var(--border2);
  border-radius:8px;color:var(--text);padding:8px 11px;font-size:13px;font-family:inherit;outline:none;
  font-variant-numeric:tabular-nums;transition:border-color .15s ease, background .15s ease}
.ap-f select{appearance:none;-webkit-appearance:none;padding-right:32px;cursor:pointer;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 10px center;background-size:16px}
.ap-f input:focus, .ap-f select:focus{border-color:var(--gold);background:var(--bg4)}
.ap-f select option{background:#11252b;color:#f8fafc;padding:8px}
body.light .ap-f select option{background:#ffffff;color:#0f172a}
@media (max-width:860px){
  .hdr-divider{display:none}
}
@media (max-width:520px){
  /* The header, tightened. The controls at desktop size left the edition
     name about forty pixels to live in, and it is the one thing up there
     anybody needs to read. A third language button costs another 30 px, so
     this breakpoint gives more back than it used to. Everything stays a
     comfortable tap target: 38 px is still well over the 24 px minimum. */
  /* Sides only. The `padding:0 10px` that used to be here also reset
     padding-top, and with it the whole camera clearance — on the one class
     of device that needs it. */
  #header{min-height:calc(54px + env(safe-area-inset-top,0px));gap:8px;
    padding-left:max(10px,env(safe-area-inset-left,0px));
    padding-right:max(10px,env(safe-area-inset-right,0px))}
}
@media (max-width:420px){
  /* The logo used to step aside here entirely: the "GO WITH GOLAZO"
     wordmark left the edition name under 100 px, and the name is the one
     thing in the bar anybody needs to read. The square GO mark costs a
     quarter of that, so it stays — and on a shared link it is the
     edition's own logo, which is what tells the reader which race the
     page belongs to. */
  #header{gap:6px;
    padding-left:max(8px,env(safe-area-inset-left,0px));
    padding-right:max(8px,env(safe-area-inset-right,0px))}
  /* The tabs are a drop-down at this width, not a row in the bar — they
     keep their tap size. Only the trigger tightens. */
  .lang-cur{min-width:42px;padding:0 7px;font-size:11px}
}
/* The federation screen's pair: the nations chart and the races chart
   side by side under a full row of tiles. Fixed halves, not auto-fit —
   minmax(0,…), never bare 1fr: a card's content minimum was pushing the
   pair past a phone's edge. min-width:0 is the medicine; `overflow:hidden`
   was belt and braces and the braces were cutting the legend off at the
   knee — overflow clips BOTH axes, and only the horizontal one was ever
   the problem. */
.ch-grid-fed{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
.ch-grid-fed .ch-card{min-width:0}
/* THE BENCH ON THE TABLE, AND THE RACES ON THE RIDER CARD. The band of
   the waiting group is amber, not gold: it is a to-do, not a race. The
   card's race list is one tick per race, scrolling past eight. */
.sl-band-bench td{color:var(--gold2)!important}
.sl-bench-why{color:var(--gold2);font-size:11px;font-weight:500;white-space:normal;line-height:1.3}
.sl-race-pick{display:flex;flex-direction:column;gap:6px;max-height:260px;overflow:auto;padding:2px}
.sl-race-pick .chk{min-height:38px;padding:7px 10px;font-weight:500}
.sl-race-pick .chk-t{flex:1;min-width:0}
.sl-race-warn{color:var(--gold2);font-size:11px;font-weight:600;white-space:nowrap}
/* Five tiles in one row above the pair. The general tile floor is 200 px,
   which at a laptop's width leaves the fifth tile alone on a second row
   beside nothing; a little narrower and the five share the one row. */
.fed-tiles{margin:0 0 16px;grid-template-columns:repeat(auto-fit,minmax(min(168px,100%),1fr))}
@media (max-width:900px){
  .ch-grid-fed{grid-template-columns:1fr}
}
/* THE EMERGENCY NUMBERS, SIDE BY SIDE AND NO WIDER. auto-fit with a 1fr
   ceiling stretched four cards across the whole desk: a label at the far
   left, its edit buttons a hand-span away at the far right, and a phone
   number stranded in between — four boxes that read as eight. Capped at
   300px they stay card-shaped and gather at the left, where the eye
   starts. */
.med-ec-grid{display:grid;gap:14px;margin-bottom:24px;
  grid-template-columns:repeat(auto-fit,minmax(220px,300px))}
.geo-hit:hover,.geo-hit:focus-visible,.geo-hit.on{background:var(--gold-bg)}
.geo-hit.on{box-shadow:inset 3px 0 0 var(--gold)}
@media (max-width:640px){
  /* The road first and loudest — it is what somebody is looking for. */
  .clos-tbl td.c-place{font-size:15px;line-height:1.3;padding-bottom:6px}
  /* From, to and duration read as one line rather than three. */
  .clos-tbl td.c-from,.clos-tbl td.c-to,.clos-tbl td.c-dur{
    display:inline-block;width:auto;margin-right:14px}
  /* A passage pill breaks between "Passage" and its number otherwise, and
     ten of them become twenty ragged lines. */
  .clos-tbl .pill{white-space:nowrap}
}
@media (max-width:640px){
  /* A heading row that puts its buttons beside the title stacks instead. */
  .page-hd{min-width:0}
}
/* ── THE FINANCIAL SHEET ───────────────────────────────────────
   Printed through printSheetVector(), so it is laid out for paper and
   never appears on screen: black on white, no theme variables, no
   shadows. A4 at 96 dpi is 794 px; the sheet is handed 782 with the
   6 mm padding of #print-root.pad around it. */
.hc-sheet{width:100%;background:#fff;color:#111;font-size:12px;line-height:1.45;
  padding:4px 2px 0}
.hc-sheet-hd{border-bottom:2px solid #111;padding-bottom:8px;margin-bottom:14px}
.hc-sheet-hd h1{font-family:var(--font-display);font-stretch:var(--display-stretch);
  font-size:24px;font-weight:800;margin:0;color:#111}
.hc-sheet-hd p{margin:2px 0 0;font-size:12px;color:#555;text-transform:uppercase;
  letter-spacing:.08em;font-weight:700}
.hc-sheet-stats{display:flex;gap:10px;margin-bottom:16px}
.hc-sheet-stats > div{flex:1;border:1px solid #ccc;border-radius:8px;padding:9px 11px}
.hc-sheet-stats span{display:block;font-size:9.5px;font-weight:800;text-transform:uppercase;
  letter-spacing:.06em;color:#666}
.hc-sheet-stats b{display:block;font-family:var(--font-display);font-stretch:var(--display-stretch);
  font-size:19px;font-weight:800;color:#111;margin-top:1px}
.hc-sheet-stats i{display:block;font-style:normal;font-size:11px;color:#555}
.hc-sheet h2{font-size:13px;font-weight:800;margin:18px 0 6px;color:#111;
  text-transform:uppercase;letter-spacing:.05em}
.hc-sheet-tbl{width:100%;border-collapse:collapse}
.hc-sheet-tbl th{text-align:left;font-size:9.5px;text-transform:uppercase;letter-spacing:.06em;
  color:#555;border-bottom:1px solid #111;padding:5px 6px}
.hc-sheet-tbl td{padding:5px 6px;border-bottom:1px solid #e2e2e2;color:#111}
.hc-sheet-tbl .r{text-align:right}
.hc-sheet-tbl tfoot td{border-top:2px solid #111;border-bottom:0;font-weight:800;padding-top:7px}
.hc-sheet-tbl tr.on td{background:#f1f5f2;font-weight:700}
.hc-sheet-ft{margin-top:16px;padding-top:7px;border-top:1px solid #ccc;
  font-size:10px;color:#777}
/* Tables that run past one page keep their heading on the next. */
@media print{
  .hc-sheet-tbl{page-break-inside:auto}
  .hc-sheet-tbl tr{page-break-inside:avoid}
  .hc-sheet-tbl thead{display:table-header-group}
}

/* ── THE APP IS WORKING ────────────────────────────────────────
   One overlay for any job long enough to be doubted — reading a GPX
   against the terrain model, writing a batch of permit files. It wears
   the mark rather than a bare spinner so a reader can tell at a glance
   that it is this app thinking, not the browser stalling. */
#app-busy{position:fixed;inset:0;z-index:9000;display:flex;align-items:center;
  justify-content:center;background:rgba(3,37,42,.55);backdrop-filter:blur(3px)}
/* A STACK, NOT A ROW. Side by side the box grew to 420 px of banner and
   the mark read as an icon in front of a sentence; stacked, the turning
   ring is the thing at the centre of the screen and the sentence is its
   caption. Narrow enough that two lines of text keep it taller than it
   is wide. */
/* AMBER GLASS, NOT A WHITE CARD. The overlay behind it is a dark wash
   of the brand navy, so the box is a tinted pane standing on it rather
   than a solid slab dropped on the map: the course keeps showing
   through, and the same panel reads the same in both themes — which is
   why the light-mode override went. The text is white on purpose; the
   ground behind the glass is dark whichever theme is on.

   THE PANE IS THE COLOUR OF THE RING IN IT. The ring has always turned
   in the app's own gold, and the glass around it was mint — one box
   wearing two brands, which is exactly what it looked like. The wash
   behind stays the deep teal: amber on teal is the app's own pairing,
   and warming the wash as well would tint the whole map under it. */
.app-busy-box{display:flex;flex-direction:column;align-items:center;gap:14px;
  background:linear-gradient(150deg,rgba(251,191,36,.24),rgba(251,191,36,.08) 55%,rgba(3,37,42,.34));
  backdrop-filter:blur(18px) saturate(1.35);-webkit-backdrop-filter:blur(18px) saturate(1.35);
  color:#fff8ec;text-align:center;
  border:1px solid rgba(251,191,36,.4);border-radius:18px;padding:26px 28px;
  box-shadow:0 18px 44px rgba(1,18,21,.44),inset 0 1px 0 rgba(255,255,255,.22);
  width:min(86vw,268px)}
/* The mark sits still inside a ring that turns: a spinning logo reads
   as a toy, a ring around it reads as work. */
.app-busy-mark{position:relative;width:56px;height:56px;flex:none;display:flex;
  align-items:center;justify-content:center}
.app-busy-mark img{width:34px;height:34px;border-radius:8px}
.app-busy-mark::after{content:'';position:absolute;inset:0;border-radius:50%;
  border:3px solid color-mix(in srgb,var(--primary,#f59e0b) 22%,transparent);
  border-top-color:var(--primary,#f59e0b);animation:app-busy-spin .9s linear infinite}
.app-busy-text{font-size:14.5px;font-weight:600;line-height:1.4;text-wrap:balance;
  text-shadow:0 1px 2px rgba(2,6,23,.35)}
/* The bar, for a job that can count: the app's gold on a faint track,
   sliding rather than jumping because the element lives as long as the
   box does. */
.app-busy-bar{display:block;width:100%;height:5px;border-radius:3px;overflow:hidden;
  background:rgba(255,255,255,.16)}
.app-busy-bar i{display:block;height:100%;width:0;border-radius:3px;
  background:var(--primary,#f59e0b);transition:width .3s ease}
.app-busy-bar[hidden]{display:none}
.app-busy-text i[hidden]{display:none}
.app-busy-text i{display:block;font-style:normal;font-size:12px;font-weight:700;
  color:var(--text3);margin-top:3px;letter-spacing:.02em}
@keyframes app-busy-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.app-busy-mark::after{animation-duration:2.6s}}

/* ── PASSAGE TIMES, THE BRIEFING DOOR, TWO RACES ON ONE ROAD ──
   Under the closures timeline — see js/briefing.js. */
.pass-card{margin-bottom:18px}
.pass-hd{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:10px}
.pass-hd h3{margin:0}
.pass-btns{display:flex;gap:6px;flex-wrap:wrap}
.pass-table{width:100%;border-collapse:collapse;font-size:13px}
.pass-table th{text-align:left;font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.06em;
  color:var(--text3);padding:6px 8px;border-bottom:2px solid var(--border)}
.pass-table td{padding:7px 8px;border-bottom:1px solid var(--border);vertical-align:top}
.pass-table .num,.pass-table small.num{font-variant-numeric:tabular-nums;white-space:nowrap}
.pass-table small{color:var(--text3)}
.pass-v{display:inline-block;font-size:11.5px;font-weight:700;padding:2px 8px;border-radius:999px}
.pass-v.ok{background:rgba(22,163,74,.12);color:#15803d}
.pass-v.warn{background:rgba(217,119,6,.14);color:#b45309}
.ch-more{margin-top:12px;align-self:flex-start;gap:6px}
.ch-more svg{width:14px;height:14px}
