/* ------------------------------------------------------------------
   The app's two themes: Tour Roads dark (the default) and Tour Roads
   light. js/theme.js puts `tr` on <html> for both, and `tr-dark` or
   `tr-light` beside it.

   One styling, two palettes: the fonts, radii, rules and layout below
   are shared, and every colour is read off the tokens in the two blocks
   that follow - ink (text, hairlines, washes) and paper (panels, pills)
   swap places between them. Only what belongs to a dark MAP - the
   inverted tiles, the pale line art over them - is written for
   `tr-dark` alone; the light theme's map is style.css's own light one,
   and its "other" map look the dark one, exactly as the old app had it.

   Type: Oxanium for anything that names something, in mixed case, and
   Barlow for prose and controls (fonts loaded in js/theme.js). Panels
   sit behind a 12px blur on ink hairlines; colour is kept for the races.
   css/style.css stays the base underneath and is not restyled here.

   A third theme, Velora (`tr-velora`, 2026-09-28, the owner's ask): the
   dark palette set in the Race Atlas's own type - Oswald and DM Sans,
   measured off veloracycling.com/race-atlas - with "Race Atlas" for the
   wordmark. js/theme.js keeps `tr-dark` beside it, so every dark-map
   rule and JS darkness check holds; only the type and the wordmark
   differ, in the block after the palettes.
   ------------------------------------------------------------------ */

html.tr {
  /* --- the dark palette --- */
  --t-ink-rgb: 255, 255, 255;
  --t-paper-rgb: 18, 20, 26;
  --t-raise-rgb: 30, 33, 41;        /* a select's face, a step off the panel */
  --t-raise2-rgb: 40, 44, 54;       /* ...and a map button under the pointer */
  --t-panel-solid: #12141a;
  --t-map: #0a0a0a;                 /* the map's floor, before tiles load */
  --t-shadow: 0 20px 25px rgba(0, 0, 0, 0.4), 0 8px 10px rgba(0, 0, 0, 0.3);
  /* races: hues a step brighter than the app's to hold on near-black
     (the fills), and the same again where they are text on the panel */
  --t-tdf: #ffd21e;
  --t-giro: #ff5aa6;
  --t-vuelta: #ff4d4d;
  --t-tdf-ink: #ffd21e;
  --t-giro-ink: #ff5aa6;
  --t-vuelta-ink: #ff6b6b;
  --t-men-ink: #7fc4e8;
  --t-women-ink: #f3c8e1;
  /* the approach picker's chevron, drawn in the ink */
  --t-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23ffffff' stroke-opacity='0.7' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
/* --- the light palette: the same styling with black and white swapped --- */
html.tr.tr-light {
  --t-ink-rgb: 18, 20, 26;
  --t-paper-rgb: 255, 255, 255;
  --t-raise-rgb: 246, 246, 247;
  --t-raise2-rgb: 236, 237, 239;
  --t-panel-solid: #ffffff;
  --t-map: #dddddd;
  --t-shadow: 0 12px 24px rgba(0, 0, 0, 0.12), 0 4px 10px rgba(0, 0, 0, 0.08);
  /* on white the Tour's yellow is a fill, never text: text takes the app's
     darker gold, and the other races the app's own hues */
  --t-tdf: #ffd21e;
  --t-giro: #e05fa0;
  --t-vuelta: #d64545;
  --t-tdf-ink: #a87c00;
  --t-giro-ink: #c9427f;
  --t-vuelta-ink: #c93535;
  --t-men-ink: #1f6f99;
  --t-women-ink: #b0487f;
  --t-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%2312141a' stroke-opacity='0.7' stroke-width='1.8' stroke-linecap='round'/%3E%3C/svg%3E");
}
/* --- Velora: the dark palette in the Race Atlas's type (measured off the
   live page, 2026-09-28: Oswald 600 for anything that names something,
   DM Sans for the rest, the same fallbacks they list) --- */
html.tr.tr-velora {
  --t-display: Oswald, Impact, "Arial Narrow", sans-serif;
  --t-body: "DM Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
html.tr {
  --t-panel: rgba(var(--t-paper-rgb), 0.94);
  --t-line: rgba(var(--t-ink-rgb), 0.10);    /* every hairline and row rule */
  --t-line-mid: rgba(var(--t-ink-rgb), 0.18);
  --t-line-strong: rgba(var(--t-ink-rgb), 0.28);
  --t-fill: rgba(var(--t-ink-rgb), 0.05);    /* a resting control */
  --t-fill-2: rgba(var(--t-ink-rgb), 0.14);  /* a selected one */
  --t-hover: rgba(var(--t-ink-rgb), 0.07);
  --t-dim: rgba(var(--t-ink-rgb), 0.55);

  --t-display: Oxanium, "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
  --t-body: Barlow, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* --- and the app's own tokens, re-pointed at it ---
     style.css hangs ~110 declarations off these three, so flipping them
     carries most of the page over on its own. The handful of places that
     pair --ink with a coloured face (the year chip, the distance badge,
     the locate button) are put right individually further down. */
  --ink: rgb(var(--t-ink-rgb));
  --ink-soft: rgba(var(--t-ink-rgb), 0.72);
  --paper: var(--t-panel-solid);
  --line: var(--t-line);
  --hover: var(--t-hover);
  --sheet-radius: 16px;
}

/* ---------- type ---------- */

html.tr, html.tr body { font-family: var(--t-body); background: var(--t-map); }
html.tr body { color: rgb(var(--t-ink-rgb)); }

/* Anything that NAMES something — a road, a race, a rider, a section —
   is Oxanium, semibold, in the name's own case. Prose, figures
   and controls stay Barlow. */
html.tr h1,
html.tr .sheet-title,
html.tr .route-head-title,
html.tr .route-item .route-title,
html.tr .stage-item .stage-route,
html.tr .stage-edition,
html.tr .race-stage-name,
html.tr .search-name,
html.tr .race-day-name,
html.tr .filter-title,
html.tr .year-grid-title,
html.tr .race-sub-title,
html.tr .rider-title,
html.tr .approach-picker .face-from,
html.tr .approach-menu li .face-from {
  font-family: var(--t-display);
  font-weight: 600;
  letter-spacing: 0.01em;
}
/* ...except the title at the head of a panel - a stage, a rider, a race,
   "Roads raced near you" - which is set in capitals (the owner's call,
   2026-09-25): one heading per panel in caps, everything under it in its
   own case. A famous road's name keeps its own case even as a title. */
html.tr .sheet-title:not(.route-head-title) {
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
/* a stage's start and finish towns, a step heavier (the owner's call, 2026-09-26) */
html.tr #stage-title { font-weight: 700; }
/* the small all-caps rules above lists: the display face too, but tracked right out */
html.tr .stages-heading,
html.tr .race-stages-heading,
html.tr .search-group,
html.tr .route-figs,
html.tr .route-meta,
html.tr .approach-label {
  font-family: var(--t-display);
  font-weight: 600;
  letter-spacing: 0.18em;
  color: rgba(var(--t-ink-rgb), 0.78);
}
/* those two carry a whole sentence about the road, not a two-word rule, so
   they take a tracking that still lets a line be read across */
html.tr .route-figs,
html.tr .route-meta { letter-spacing: 0.08em; }

/* ---------- Velora: capitals where the atlas sets them ----------
   Measured off veloracycling.com/race-atlas (2026-09-28). The atlas sets
   in capitals: the eyebrow above a panel's title (a road's region, a
   stage's race and year, an edition's year), every title - a famous
   road's name too - the small rules above its lists, and the filter
   panel's rules and the column heads of its editions grid. Names in
   lists, prose, figures, dates and buttons stay in their own case. The
   rules and the search groups are capitals in every theme already
   (style.css), so only what this theme adds is here; each takes the
   tracking capitals need. (The atlas caps its profile's summit labels
   too, but here a famous road's label on the profile keeps the case of
   the classified climb's label beside it - the owner's call, 2026-09-28.) */
html.tr.tr-velora .route-head-title,
html.tr.tr-velora .race-pill { text-transform: uppercase; letter-spacing: 0.03em; }
html.tr.tr-velora .stage-year-chip { text-transform: uppercase; letter-spacing: 0.12em; }
html.tr.tr-velora .route-meta { text-transform: uppercase; letter-spacing: 0.16em; }
html.tr.tr-velora .year-grid-title,
html.tr.tr-velora .yg-head { text-transform: uppercase; letter-spacing: 0.06em; }
/* ...and in the roads-raced-near lists (near you, near a tapped point) a
   row's name line: a famous road's name, a stage's race and year (the
   owner's ask, 2026-09-28). The stage line under it keeps its towns in
   their own case, as every list does (the owner's call, 2026-10-04); see
   "a list row" below. */
html.tr.tr-velora .route-list .stage-edition { text-transform: uppercase; letter-spacing: 0.04em; }

/* ---------- Velora: the atlas's weights ----------
   Measured off veloracycling.com/race-atlas (2026-10-02). The atlas keeps
   bold for three things: a panel's title, the names in its lists, and the
   small capitals that rule them. Everything that serves them - figures,
   labels, a stage's line under its race, a rider's results - is DM Sans at
   400-500, so the names stand out from it. Here those were Oswald or
   DM Sans 600-900, and the panel read as all headings. Only weights, faces
   and the eyebrows' size change; the layout does not. */

/* a road's region, the eyebrow over its figures, at the atlas's size, a
   step under the title rather than level with it. (A stage's race and
   number keep their size: the owner's call, 2026-10-04.) */
html.tr.tr-velora .route-meta { font-size: 11px; letter-spacing: 0.16em; }
/* ...and the climb picker's label the same size again */
html.tr.tr-velora .approach-label { font-size: 11px; letter-spacing: 0.16em; }
/* The rules above lists - a stage's result, a road's appearances, a race's
   general classification and its stages - all alike, in the display face
   tracked right out at the climb picker's 11px (the owner's call,
   2026-10-05, after a day in DM Sans) */
html.tr.tr-velora .stages-heading,
html.tr.tr-velora .race-gc-heading,
html.tr.tr-velora .race-stages-heading {
  font-family: var(--t-display);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  color: rgba(var(--t-ink-rgb), 0.78);
}
/* ...spaced like it too: 17px of air over the text and 8px under it. A
   race's stage rows carry 9px of padding of their own on top, which left
   Stages sitting 13px under the table and 16px over its first stage. (The
   top margin collapses into the table's own 10px, so it has to outgrow it.) */
html.tr.tr-velora .race-stages-heading { margin: 14px 0 -2px; }

/* a road's figures ("Mountain pass 2,115 m") are a caption, not a heading */
html.tr.tr-velora .route-figs {
  font-family: var(--t-body);
  font-weight: 400;
  letter-spacing: 0;
  color: rgba(var(--t-ink-rgb), 0.9);
}
/* the climb picker names a town, in the caption face */
html.tr.tr-velora .approach-picker .face-from,
html.tr.tr-velora .approach-menu li .face-from {
  font-family: var(--t-body);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
}

/* A list row - near you, near a tapped point, a road's timeline, a race's
   stages: the race and year (or the famous road) is the name, Oswald in its
   race's colour; the stage under it is DM Sans 500 in its own case, as the
   atlas sets it, with only the "STAGE 6:" label in capitals (the owner's
   call, 2026-10-04) */
html.tr.tr-velora .stage-item .stage-route,
html.tr.tr-velora .race-stage-name {
  font-family: var(--t-body);
  font-weight: 500;
  letter-spacing: 0;
}

/* The filter pane: its rules and the grid's heads and years are labels,
   DM Sans at the atlas's weights, so the races' colours carry the grid */
html.tr.tr-velora .year-grid-title {
  font-family: var(--t-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
}
/* (DM Sans sets wider than Oswald: FEMENINA fits its column at 9.5px) */
html.tr.tr-velora .yg-head {
  font-family: var(--t-body);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: 0.04em;
}
html.tr.tr-velora .yg-year { font-weight: 400; color: rgba(var(--t-ink-rgb), 0.7); }
html.tr.tr-velora .yr-thumb-label { font-weight: 500; }

/* the profile: its labels and caption whisper under the chart */
html.tr.tr-velora .elevation .elev-poi-label { font-weight: 500; }
/* a classified climb's name line at the famous roads' weight, so the two
   labels read alike (owner, 2026-10-09); the map's .kom-name is its twin */
html.tr.tr-velora .elevation .elev-climb-tip,
html.tr.tr-velora .kom-dot .kom-name { font-weight: 500; }
html.tr.tr-velora .elevation .elev-cap { font-weight: 400; }
html.tr.tr-velora .elevation .elev-cap b { font-weight: 600; }

/* a stage's winners and its result table: names at 500, the rest at 400 */
html.tr.tr-velora .stage-winner,
html.tr.tr-velora .stage-leader { font-weight: 500; }
html.tr.tr-velora .result-list .rank { font-weight: 600; }
html.tr.tr-velora .result-list .rider { font-weight: 500; }
html.tr.tr-velora .result-list .rteam { font-weight: 400; }

/* a rider's tiers and results: the place in the display face, as the atlas
   ranks them; the race named at 600, the rest of the line at 400 */
html.tr.tr-velora .rider-tier,
html.tr.tr-velora .rider-tier-n { font-weight: 500; }
/* (Oswald is narrower than the 30px column cut for DM Sans: "10th", the
   widest place listed, is 22px, so the year closes up to it - the owner's
   ask, 2026-10-04) */
html.tr.tr-velora .rr-place { font-family: var(--t-display); font-weight: 600; width: 23px; }
html.tr.tr-velora .rr-race { font-weight: 600; }
html.tr.tr-velora .rider-class .rr-cls { font-weight: 500; }
html.tr.tr-velora .rr-text { color: rgba(var(--t-ink-rgb), 0.9); }

/* Back is a control, not a label: the atlas's 400 */
html.tr.tr-velora .back-btn { font-weight: 400; }

/* The <button> colour gap - `font: inherit` carries the face but leaves
   `color` at the UA's buttontext, so the row titles (.route-item,
   .stage-item, .search-item) came out black on near-black paper - is closed
   in style.css now, unscoped: iOS Safari reads the same keyword as link
   blue, which broke the old white app too. The theme needs nothing of its own. */

/* ---------- map ---------- */

/* The dark theme's map. Ours is OSM raster, already
   rendered flat grey by style.css — inverting that grey is what turns it
   into the same near-black ground with pale labels. The route lines live
   in vector panes above the tile pane and are untouched, exactly as in
   the light theme. */
html.tr-dark #map, html.tr-dark .map-wrap { background: var(--t-map); }
html.tr-dark #map .leaflet-tile-pane {
  /* Contrast is the knob here, not brightness. OSM's land is a very light
     grey, so inverting it lands near 0 and the old contrast(1.08) pushed it
     the rest of the way: land came out at RGB 3, a black sheet with roads on
     it. Pulling contrast BELOW 1 lifts everything towards mid-grey instead -
     land sits at about RGB 35, near enough Mapbox Dark's own ground, and the
     labels stay a legible 178. The one thing an inverted raster cannot do is
     keep water darker than land: OSM draws the sea darker than the fields,
     so the flip makes it lighter. It was invisible while everything was
     black; at this level it reads as pale water, which is the price of
     having labels at all.
     Brightness ABOVE 1 before that contrast (owner: less dark, more
     contrast): the inverted land is near 0, so the push barely moves it,
     while the pale labels and roads go most of the way to white - then the
     contrast lifts the floor. Land about RGB 41, labels 227, water 85 (it
     was 33 / 182 / 66 at brightness 0.95, contrast 0.82). */
  filter: grayscale(1) invert(1) brightness(1.3) contrast(0.78);
}
html.tr.vp-clipped, html.tr.vp-clipped body { background: var(--t-map); }

/* map labels: a dark pill on a dark map, and a light pill on the light theme's light map */
html.tr .climb-label .climb-name,
html.tr .leaflet-tooltip.road-tip {
  color: rgb(var(--t-ink-rgb));
  background: rgba(var(--t-paper-rgb), 0.86);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
  font-family: var(--t-display);
  font-weight: 500;
  letter-spacing: 0.01em;
}
/* Barlow Condensed in mixed case sets far smaller than the 11px its sizes
   were cut for (Oswald in capitals), and on a desktop the names read as
   footnotes. A wider screen has the room, so they come up to match; the
   placement probe measures these same boxes, so it follows. A phone comes
   up too, a little less: its map is short of room. */
html.tr .climb-label .climb-name { font-size: 12.5px; }
html.tr body.labels-marquee-only .climb-label .climb-name { font-size: 11px; }
html.tr .route-item .route-title { font-size: 15px; }
@media (min-width: 700px) {
  /* a famous road's name a touch under that: at 13.5 it shouted over the map */
  html.tr .climb-label .climb-name { font-size: 12.75px; }
  html.tr body.labels-marquee-only .climb-label .climb-name { font-size: 11.5px; }
  /* ...and a road's name in the list, which at 13px sat smaller than the
     line of prose under it */
  html.tr .route-item .route-title { font-size: 15px; }
}
/* A famous road's name at rest on the flat map: the pill see-through and
   its shadow soft, so the names sit lighter on the map. Lit (:hover / .hot)
   it goes solid as before (style.css), and the ribbon 3D keeps it solid
   (owner, 2026-10-02: "on all 2D map views"). Over the 3D terrain the names
   left on the map (not on a pole) are dressed the same (owner, 2026-10-07). */
html.tr .map-wrap:not(.is-3d) .climb-label:not(.hot) .climb-name {
  background: rgba(var(--t-paper-rgb), 0.45);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
}
/* (lit under a mouse too - and only a mouse: see the :hover rules in
   style.css) */
@media (hover: hover) and (pointer: fine) {
  html.tr .map-wrap:not(.is-3d) .climb-label:hover .climb-name {
    background: rgb(var(--t-paper-rgb));
    box-shadow: 0 0 0 2px var(--road-c, #1a1a1a), 0 2px 6px rgba(0, 0, 0, 0.4);
  }
  html.tr .map-wrap:not(.is-3d) .climb-label.unraced:hover .climb-name {
    box-shadow: 0 0 0 2px #a2a2a2, 0 2px 6px rgba(0, 0, 0, 0.4);
  }
}
/* ...and its words a step off the full ink, toward the pill's own shade -
   off-white on the dark map, dark grey on the light one - lit or not: a
   hover changes the pill, never the words (owner, 2026-10-02) */
html.tr .map-wrap:not(.is-3d) .climb-label .climb-name {
  color: rgba(var(--t-ink-rgb), 0.88);
  color: color-mix(in srgb, rgb(var(--t-ink-rgb)) 88%, rgb(var(--t-paper-rgb)));
}
html.tr .climb-label .climb-dot { border-color: rgba(var(--t-ink-rgb), 0.85); }
html.tr .kom-dot .kom-pin { border-color: rgba(var(--t-ink-rgb), 0.85); }
html.tr .kom-dot .kom-pin.kom-hc,
html.tr .climb-label .climb-dot.kom-hc { border-color: #0b0b0b; }
html.tr-dark .climb-label.unraced .climb-dot { background: #6b6f76 !important; border-color: #9a9ea5; }
/* The stage numbers over a single edition, as the atlas draws them: not a
   race-coloured pill but a dark disc ringed in the race's colour with the
   number in white, sized by the zoom (app.js sets --stage-badge*; see
   stageBadgeAt). Two characters stay a circle; a longer label - a
   multi-select's "2018-3", a Worlds "ITT" - stretches into a capsule. The
   inline fill the app's own theme uses is overridden here. */
html.tr .stage-num-label .stage-num-pill {
  box-sizing: border-box;
  height: var(--stage-badge, 18px);
  min-width: var(--stage-badge, 18px);
  padding: 0 3px;
  border-radius: 999px;
  background: rgba(var(--t-paper-rgb), 0.92) !important;
  color: rgb(var(--t-ink-rgb)) !important;
  border: var(--stage-badge-ring, 1.6px) solid var(--race, var(--t-tdf)) !important;
  box-shadow: none;
  font-family: var(--t-body); /* theirs: DIN Pro Medium */
  font-weight: 600;
  font-size: var(--stage-badge-font, 11px);
  line-height: calc(var(--stage-badge, 18px) - 2 * var(--stage-badge-ring, 1.6px));
  font-variant-numeric: tabular-nums;
}
/* the Worlds' near-black would vanish on the dark map, as their lines do
   (!important: it has to beat the --race the pill carries inline) */
html.tr-dark .stage-num-pill[data-race="wc"] { --race: rgb(var(--t-ink-rgb)) !important; }

/* Leaflet's own furniture (zoom buttons, attribution) */
html.tr .leaflet-bar a,
html.tr .leaflet-bar a:hover {
  background: rgba(var(--t-paper-rgb), 0.92);
  color: rgb(var(--t-ink-rgb));
  border-bottom-color: var(--t-line);
}
html.tr .leaflet-bar a:hover { background: rgba(var(--t-raise2-rgb), 0.95); }
html.tr .leaflet-bar { border: 1px solid var(--t-line); }
html.tr .leaflet-control-attribution {
  background: rgba(var(--t-paper-rgb), 0.8);
  color: rgba(var(--t-ink-rgb), 0.6);
}
html.tr .leaflet-control-attribution a { color: rgba(var(--t-ink-rgb), 0.8); }

/* the map's own buttons (3D/2D, what a drag does, the compass) are line art
   with no box (style.css); on the dark map the lines are pale and the casing
   under them is the map's own near-black */
html.tr-dark .map-tool {
  --tool-line: rgba(var(--t-ink-rgb), 0.62);
  --tool-line-on: rgb(var(--t-ink-rgb));
  --tool-line-off: rgba(var(--t-ink-rgb), 0.28);
  --tool-halo: rgba(10, 10, 10, 0.85);
}
html.tr .map-tool {
  font-family: var(--t-display);
  font-weight: 500;
}
/* In this theme the top bar is a card floating over a map that runs to the top
   edge, 10px down from it. On a desktop it is the left-hand corner, clear of
   these buttons; on a phone it spans the width, over them and the compass
   too. They hang under it either way - level with where the app's own bar
   would have left them. --topbar-h is the bar's height as
   positionMapBelowBanner publishes it; where that is not set the fallback
   leaves them a few px lower than the old white app's, and nothing worse. */
html.tr .map-tools {
  top: calc(var(--topbar-top) + var(--topbar-h, 0px) + 8px);
}
/* Where the card is only the left-hand corner (tablet and up), the right-hand
   one is free: the buttons go back up to the top, level with the card - the
   row centred on its row, so the two read as one strip across the top. The
   row's own height, not the card's: the tour's banner grows the card under
   its row and would otherwise take the buttons down to its middle. */
@media (min-width: 700px) {
  html.tr .map-tools {
    top: calc(var(--topbar-top) + (var(--topbar-row-h, var(--topbar-h, 48px)) - 38px) / 2);
  }
}
/* ...and the compass at every width: it lives in the top LEFT corner, which on
   a desktop is exactly where the card is (it was only moved for a phone, and
   on a desktop sat underneath the card where it could be neither seen nor
   tapped) */
html.tr .map3d-compass {
  top: calc(var(--topbar-top) + var(--topbar-h, 0px) + 8px);
}
/* How the map looks (style.css), turned round: this theme's own grey is the
   dark one, so its OTHER grey is the light one - and both that and full
   colour are a pale map, which wants the old white app's grey lines, pale haze
   and dark ink rather than this theme's. */
html.tr-dark.map-look-alt #map .leaflet-tile-pane { filter: grayscale(1) contrast(0.92) brightness(1.05); }
html.tr-dark.map-look-colour #map .leaflet-tile-pane { filter: none; }
html.tr-dark.map-look-alt .map-wrap, html.tr-dark.map-look-alt #map,
html.tr-dark.map-look-colour .map-wrap, html.tr-dark.map-look-colour #map { background: #ddd; }
html.tr-dark.map-look-alt .map3d-canvas,
html.tr-dark.map-look-colour .map3d-canvas {
  --map3d-tile-filter: grayscale(1) contrast(0.92) brightness(1.05);
  --map3d-haze: 221, 221, 221;
  --map3d-ink: 0, 0, 0;
  --map3d-wall-alpha: 0.5;
}
html.tr-dark.map-look-colour .map3d-canvas { --map3d-tile-filter: none; }
html.tr-dark.map-look-alt .map-tool,
html.tr-dark.map-look-colour .map-tool {
  --tool-line: #737373;
  --tool-line-on: #1a1a1a;
  --tool-line-off: #b5b5b5;
  --tool-halo: rgba(var(--t-ink-rgb), 0.9);
}
html.tr-dark.map-look-alt .map3d-note,
html.tr-dark.map-look-colour .map3d-note {
  color: #555;
  text-shadow: 0 0 3px rgb(var(--t-ink-rgb)), 0 0 3px rgb(var(--t-ink-rgb)), 0 0 2px rgb(var(--t-ink-rgb));
}
html.tr-dark .map3d-note {
  color: rgba(var(--t-ink-rgb), 0.75);
  text-shadow: 0 0 3px #000, 0 0 3px #000;
}
/* the tilted map fades into the dark ground, not into grey paper; the
   raised route's stalk and compass are white ink, and its wall a little
   stronger against near-black than it needs to be on a pale map */
html.tr-dark .map3d-canvas {
  --map3d-ink: 255, 255, 255;
  --map3d-wall-alpha: 0.6;
  --map3d-haze: 10, 10, 10;
  /* (the dark theme's tile filter, for the tilted map's copy of the tiles) */
  --map3d-tile-filter: grayscale(1) invert(1) brightness(1.3) contrast(0.78);
}
/* the names over the raised route: the pill the map's labels wear here, in
   every theme, set in the theme's own faces - the name in the display face,
   a classified climb's category chip in the body face, as the profile's
   own marker (.elev-climb) is - so the third theme's Oswald and DM Sans
   reach the canvas too (until v1337 the name was Oxanium by name and the
   chip the system font, in every theme) */
html.tr .map3d-canvas {
  --map3d-label-bg: var(--t-paper-rgb);
  --map3d-label-ink: var(--t-ink-rgb);
  --map3d-label-font: 500 12px var(--t-display);
  --map3d-chip-font: 800 8.5px var(--t-body);
  --map3d-label-upper: 0;
}
html.tr .map3d-attrib {
  background: rgba(var(--t-paper-rgb), 0.8);
  color: rgba(var(--t-ink-rgb), 0.6);
}
html.tr .map3d-attrib a { color: rgba(var(--t-ink-rgb), 0.8); }

/* ---------- top bar ---------- */

/* Where the card sits: 10px under the status bar (the corner, on a desktop).
   In the app installed on an iPhone (html.ios-app, set by js/theme.js) it
   sits 18px under it: iOS 26 blurs the top of the screen, and at 10px that
   blur ran over the card. A fixed distance (owner, 2026-09-30): it had been
   a third of the card's height, which a banner opening under the row - the
   tour's - grew, dropping the whole bar with it. Safari and Chrome tabs
   keep the 10px. */
html.tr { --topbar-top: calc(env(safe-area-inset-top, 0px) + 10px); }
html.tr.ios-app { --topbar-top: calc(env(safe-area-inset-top, 0px) + 18px); }

/* THE BAR FLOATS. Over there it is not a bar across the top of the page at
   all: it is a card - absolute, inset from the corner, its own hairline and
   blur - with the map running underneath it, at the top LEFT and exactly as
   wide as their sidebar ("w-[300px] max-w-[calc(100%-1.75rem)]"). Ours takes
   the same shape: the same corner, our filter panel's width and gutter, and
   on a phone - where that panel spans the screen - so does the bar.
   The map's box no longer starts below it (see --topbar-h, published by
   positionMapBelowBanner): here the map runs to the top edge and the card
   sits on it. */
html.tr .topbar {
  top: var(--topbar-top);
  right: 10px;
  left: 10px;
  /* a slimmer card (was 9px top and bottom), the owner's call on
     2026-09-25: the wordmark keeps its size and the row is the Filters
     button's height. A pixel more below than above: the card's shadow
     falls under its bottom edge and eats into the space there, so equal
     padding read as the button sitting low. */
  padding: 6px 10px 7px;
  background: var(--t-panel, rgba(var(--t-paper-rgb), 0.92));
  border: 1px solid var(--t-line);
  border-radius: 16px;
  box-shadow: var(--t-shadow);
  backdrop-filter: blur(12px);
}
/* the map fills the window; the bar is on top of it, not above it */
html.tr .map-wrap { top: 0; }
/* (the banners hang from the card, edge to edge with it: .top-banners) */
@media (min-width: 700px) {
  /* the filter panel's width, held at the left corner the atlas uses */
  html.tr .topbar { right: auto; width: 360px; }
  /* ...and the filter panel hangs under it, at the same left edge and width:
     the atlas keeps its filters in a column under its bar, and ours opens
     from the bar's own button, so it drops from there rather than from the
     far corner. (The app's own theme keeps it on the right.) */
  html.tr .year-panel { left: 10px; right: auto; }
}
html.tr .topbar-title h1 {
  font-family: var(--t-display);
  /* a size up on the line the bar was cut for, so the bar keeps its
     height (Oxanium's letters still fall inside the 22px line); 21px since
     2026-09-25, the owner's call, a touch under the 23px it had */
  font-size: 21px;
  line-height: 22px;      /* (the line it had at 15px: the bar keeps its height) */
  /* ...and a pixel low: the caps sat high of the bar's middle, and the
     line keeps its box, so the bar's height does not move */
  position: relative;
  top: 1px;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-transform: none;   /* their wordmark is title case, not caps */
  /* a card is one row: the name gives way with an ellipsis rather than
     wrapping and making it two (theirs is "min-w-0 truncate" for the same
     reason - a phone with a long filter summary in the button would push it
     over) */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
html.tr .topbar-title { min-width: 0; }
/* The theme's bar reads [TourRoads] [Filters]: the name set solid in two
   weights - "Tour" bold, "Roads" a step lighter and dimmer, the change of
   weight the break between them. (There are no race dots in the bar - the
   owner's call, 2026-09-25: the filter panel's race chips are the switches.) */
html.tr .topbar { color: rgb(var(--t-ink-rgb)); }
html.tr .topbar-title .wm-tour { font-weight: 700; }
html.tr .topbar-title .wm-roads { margin-left: 0; font-weight: 500; color: rgba(var(--t-ink-rgb), 0.62); }
/* Velora's bar reads "Race Atlas", as the atlas's own does. Both wordmarks
   are in the page (index.html, leaflet.html) and the swap is display, not
   text: no flash, nothing for a translator to catch mid-change. */
html.tr .app-name-velora { display: none; }
html.tr.tr-velora .app-name-default { display: none; }
html.tr.tr-velora .app-name-velora { display: inline; }
/* ...at the size Oswald had on this bar (18px on its 22px line), tracked
   tight like theirs (-0.025em); Oswald's caps sit on the bar's middle
   without Oxanium's pixel down */
html.tr.tr-velora .topbar-title h1 { font-size: 18px; letter-spacing: -0.025em; top: 0; }
/* ...and led, as the atlas's bar is, by a Velora pill and a hairline:
   [Velora] | Race Atlas ... [Filters]. Theirs is "← Velora" in text; ours
   drops the arrow and carries the veloracycling.com logo instead, cut to
   the Filters button's box (same border, fill, radius and height) so the
   bar's two pills are a pair. A plain link out, in a new tab. */
.velora-pill,
.velora-rule { display: none; }
html.tr.tr-velora .topbar-title { align-self: stretch; }
html.tr.tr-velora .velora-pill {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  /* the row is the Filters button's height (its 13px line and 2.5px
     padding, whatever the font makes of them): the title stretches to
     the row and the pill to the title, so the two match to the pixel */
  align-self: stretch;
  padding: 0 12px;
  border: 1px solid var(--t-line-mid);
  border-radius: 12px;
  background: var(--t-fill);
  color: rgba(var(--t-ink-rgb), 0.85);
  text-decoration: none;
}
html.tr.tr-velora .velora-pill:hover { border-color: var(--t-line-strong); color: rgb(var(--t-ink-rgb)); }
html.tr.tr-velora .velora-logo { display: block; height: 11px; width: auto; }
html.tr.tr-velora .velora-rule {
  display: block;
  flex-shrink: 0;
  width: 1px;
  height: 20px;
  margin: 0 2px;
  background: var(--t-line-mid);
}

/* The welcome (Velora only; shown by app_gl.js through the opening draw):
   text laid straight on the map, no card - under the bar and the map's
   buttons, centred, haloed in the map's own shade so the lines drawing in
   behind it do not break it up. It takes no touches: a tap goes through to
   the map, which is what hurries the draw - and fades it out at once
   (.welcome-quick). Left alone, its skip line goes when the draw lands,
   then the rest fades (.welcome-out). */
.welcome {
  position: fixed;
  z-index: 900; /* over the map and its buttons' row, under the bar (1000) */
  top: calc(var(--topbar-top, 10px) + var(--topbar-h, 48px) + 58px);
  left: 50%;
  transform: translateX(-50%);
  width: min(460px, calc(100% - 32px));
  text-align: center;
  font-size: 15px;
  line-height: 1.4;
  color: rgb(var(--t-ink-rgb));
  text-shadow:
    0 0 3px rgb(var(--t-paper-rgb)),
    0 0 8px rgb(var(--t-paper-rgb)),
    0 0 16px rgba(var(--t-paper-rgb), 0.9);
  pointer-events: none;
  animation: welcome-in 0.8s ease both;
  transition: opacity 1.5s ease;
}
@keyframes welcome-in { from { opacity: 0; } }
.welcome[hidden] { display: none; }
.welcome p { margin: 0 0 6px; }
.welcome p:last-child { margin-bottom: 0; }
.welcome-title {
  font-family: var(--t-display);
  font-size: 24px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.welcome-ic {
  display: inline-block;
  width: 18px;
  height: 18px;
  vertical-align: -3px;
  margin: 0 1px;
  overflow: visible;
  filter: drop-shadow(0 0 2px rgb(var(--t-paper-rgb))) drop-shadow(0 0 5px rgb(var(--t-paper-rgb)));
}
.welcome-ic.line-icon .ink { stroke-width: 2; }
.welcome-q { margin-right: -4px; } /* (the glyph is narrow in its box: no gap before the stop) */
.welcome .welcome-skip { margin-top: 10px; color: rgba(var(--t-ink-rgb), 0.7); font-style: italic; }
.welcome.welcome-done .welcome-skip { display: none; }
.welcome.welcome-out { opacity: 0; }
/* (a tap mid fade-in stops the fade-in where it is, so the fade-out runs
   from there instead of waiting on it) */
.welcome.welcome-quick { animation: none; transition-duration: 0.25s; }


/* the two chrome buttons lose the yellow: colour is kept for races */
html.tr .year-btn,
html.tr .icon-btn {
  border: 1px solid var(--t-line-mid);
  background: var(--t-fill);
  color: rgba(var(--t-ink-rgb), 0.85);
  font-weight: 500;
  border-radius: 12px;
}
/* the Filters button is a touch slimmer than the app's (4px): the bar is
   cut to its height, and the text keeps its 13px */
html.tr .year-btn { padding-top: 2.5px; padding-bottom: 2.5px; }
html.tr .year-btn:hover,
html.tr .icon-btn:hover { border-color: var(--t-line-strong); color: rgb(var(--t-ink-rgb)); }
html.tr .icon-btn.on {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}

/* ---------- filter panel ---------- */

html.tr .year-panel {
  /* clear of the floating card: its inset, its measured height, and a gap */
  top: calc(var(--topbar-top) + var(--topbar-h, 48px) + 8px);
  background: var(--t-panel);
  color: rgb(var(--t-ink-rgb));
  border: 1px solid var(--t-line);
  border-radius: 16px;
  box-shadow: var(--t-shadow);
  backdrop-filter: blur(12px);
}
html.tr .filter-title,
html.tr .year-grid-title { color: rgb(var(--t-ink-rgb)); }
html.tr .filter-reset,
html.tr .fbar-btn,
html.tr .update-btn,
html.tr .multi-toggle,
html.tr .show-more {
  border: 1px solid var(--t-line-mid);
  background: var(--t-fill);
  color: rgba(var(--t-ink-rgb), 0.85);
  border-radius: 12px;
  font-weight: 500;
}
html.tr .filter-reset:hover,
html.tr .fbar-btn:hover,
html.tr .update-btn:hover,
html.tr .multi-toggle:hover,
html.tr .show-more:hover { border-color: var(--t-line-strong); color: rgb(var(--t-ink-rgb)); }
html.tr .filter-reset:active,
html.tr .fbar-btn:active,
html.tr .update-btn:active,
html.tr .show-more:active { background: var(--t-fill-2); color: rgb(var(--t-ink-rgb)); }
html.tr .multi-toggle.on {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}
html.tr .year-note, html.tr .app-version { color: var(--t-dim); }
/* the lists' filter bar: the filters as the pane's chips wear them */
html.tr .fbar-text { color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .fbar-tag {
  background: var(--t-fill-2);
  border: 1px solid var(--t-line-mid);
  color: rgb(var(--t-ink-rgb));
}

/* race + sex chips: a round-ended pill */
html.tr .race-chip,
html.tr .sex-chip {
  border: 1px solid var(--t-line-mid);
  background: var(--t-fill);
  color: rgba(var(--t-ink-rgb), 0.55);
  border-radius: 999px;
  font-weight: 500;
}
html.tr .race-chip:hover,
html.tr .sex-chip:hover { border-color: var(--t-line-strong); }
html.tr .race-chip.on, html.tr .sex-chip.on { color: rgb(var(--t-ink-rgb)); }
html.tr .race-chip.race-tdf.on {
  background: rgba(255, 210, 30, 0.16);
  border-color: rgba(255, 210, 30, 0.65);
  color: var(--t-tdf-ink);
}
html.tr .race-chip.race-giro.on {
  background: rgba(255, 90, 166, 0.16);
  border-color: rgba(255, 90, 166, 0.6);
  color: var(--t-giro-ink);
}
html.tr .race-chip.race-vuelta.on {
  background: rgba(255, 77, 77, 0.16);
  border-color: rgba(255, 77, 77, 0.6);
  color: var(--t-vuelta-ink);
}
html.tr .race-chip.race-wc.on {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}
html.tr .sex-chip[data-sex="men"].on {
  border-color: rgba(43, 135, 182, 0.85);      /* the men's blue */
  background: rgba(43, 135, 182, 0.2);
  color: var(--t-men-ink);
}
html.tr .sex-chip[data-sex="women"].on {
  border-color: rgba(235, 179, 213, 0.8);      /* --viz-women */
  background: rgba(235, 179, 213, 0.18);
  color: var(--t-women-ink);
}

/* year range slider */
html.tr .yr-track { background: rgba(var(--t-ink-rgb), 0.14); }
/* the chosen range in grey, not the Tour's yellow: it is a span of years
   for every race, and yellow here read as "Tour only" */
html.tr .yr-fill { background: rgba(var(--t-ink-rgb), 0.5); }
html.tr .yr-thumb-label { color: rgba(var(--t-ink-rgb), 0.78); }
html.tr .yr-slider input[type="range"]::-webkit-slider-thumb {
  background: rgb(var(--t-ink-rgb));
  border: 2px solid rgba(var(--t-paper-rgb), 0.9);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}
html.tr .yr-slider input[type="range"]::-moz-range-thumb {
  background: rgb(var(--t-ink-rgb));
  border: 2px solid rgba(var(--t-paper-rgb), 0.9);
}

/* the race × year grid */
html.tr .yg-head { color: var(--t-dim); font-family: var(--t-display); letter-spacing: 0.02em; }
html.tr .yg-head.race-tdf { color: var(--t-tdf-ink); }
html.tr .yg-head.race-giro { color: var(--t-giro-ink); }
html.tr .yg-head.race-vuelta { color: var(--t-vuelta-ink); }
html.tr .yg-head.race-wc { color: rgba(var(--t-ink-rgb), 0.85); }
html.tr .yg-head:hover, html.tr .yg-year:hover { background: var(--t-hover); }
html.tr .yg-year { color: rgba(var(--t-ink-rgb), 0.6); }
html.tr button.yg-cell { background: var(--t-fill); border-color: var(--t-line); }
html.tr button.yg-cell.race-tdf {
  background: rgba(255, 210, 30, 0.34);
  border-color: rgba(255, 210, 30, 0.55);
}
html.tr button.yg-cell.race-giro {
  background: rgba(255, 90, 166, 0.3);
  border-color: rgba(255, 90, 166, 0.5);
}
html.tr button.yg-cell.race-vuelta {
  background: rgba(255, 77, 77, 0.28);
  border-color: rgba(255, 77, 77, 0.5);
}
html.tr button.yg-cell.race-wc {
  background: rgba(var(--t-ink-rgb), 0.16);
  border-color: var(--t-line-strong);
}
html.tr .yg-host { color: rgb(var(--t-ink-rgb)); }
html.tr .yg-pct { color: rgba(var(--t-ink-rgb), 0.6); }
html.tr .yg-flag { box-shadow: 0 0 0 1px rgba(var(--t-ink-rgb), 0.35); }
/* A race outside the current filter is dimmed, not hidden. The old white app's
   0.32 + grayscale leaves nothing at all on near-black, so the dark one dims
   by less and lifts what is left back off the ground. */
html.tr-dark button.yg-cell.disabled { opacity: 0.5; filter: grayscale(0.7) brightness(1.3); }
html.tr-dark button.yg-cell.disabled.active { opacity: 0.75; }
/* the old white app's hover darkens; on near-black it has to lift instead.
   Not with brightness(): that scales each channel and clips the bright one,
   which tints the race colours. A thin white wash over the face lifts them
   and keeps their tone. The on-chip fade and the grid's .linked-flip preview
   are style.css's, re-tuned for the dark ground. */
@media (hover: hover) {
  html.tr .race-chip:hover, html.tr .sex-chip:hover,
  html.tr .filter-reset:hover, html.tr .multi-toggle:hover,
  html.tr button.yg-cell:hover {
    filter: none;
    background-image: linear-gradient(rgba(var(--t-ink-rgb), 0.1), rgba(var(--t-ink-rgb), 0.1));
  }
  html.tr .race-chip.muted:hover { filter: grayscale(1); }
  html.tr .race-chip[data-race].on:not(.muted):not(.quiet):hover,
  html.tr body.rider-mode .race-chip.race-wc.on:not(.quiet):hover,
  html.tr .sex-chip.on:not(.quiet):hover { background-image: none; opacity: 0.55; }
  html.tr-dark button.yg-cell.disabled:hover { filter: grayscale(0.7) brightness(1.3); }
  html.tr button.yg-cell.linked-flip { opacity: 0.72; filter: none; }
}
html.tr .yg-cell.empty::after { color: rgba(var(--t-ink-rgb), 0.22); }
html.tr button.yg-cell.femmes::after { background: #ffe680; }
html.tr button.yg-cell.donne::after { background: #ffb3d6; }
html.tr button.yg-cell.femenina::after { background: #ff9d9d; }

/* ---------- search ---------- */

/* In this theme search lives in the filter pane (theme.js moves it): the
   magnifier just right of "Filters", Reset still at the far end, and the box
   in the pane's own flow under that row, its results too, so opening it
   moves the rest of the pane down rather than covering it. */
html.tr .filter-head-row { justify-content: flex-start; }
html.tr .filter-head-row > .filter-reset { margin-left: auto; }
html.tr .filter-head-row > .rider-filter:not([hidden]) + .filter-reset { margin-left: 0; }
html.tr .year-panel .topbar-search { margin: -4px 0 12px; }
html.tr .year-panel .search-results {
  position: static;
  margin-top: 6px;
  max-height: none;
  box-shadow: none;
  backdrop-filter: none;
}

html.tr #search-input {
  background: var(--t-fill);
  border: 1px solid var(--t-line);
  color: rgb(var(--t-ink-rgb));
  border-radius: 12px;
}
html.tr #search-input::placeholder { color: var(--t-dim); }
html.tr #search-input:focus { outline: none; border-color: var(--t-line-strong); }
html.tr .search-results {
  background: var(--t-panel);
  border: 1px solid var(--t-line);
  border-radius: 12px;
  box-shadow: var(--t-shadow);
  backdrop-filter: blur(12px);
}
html.tr .search-item:hover, html.tr .search-item:active { background: var(--t-hover); }
html.tr .search-name { color: rgb(var(--t-ink-rgb)); }
html.tr .search-sub, html.tr .search-empty { color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .search-empty kbd {
  background: var(--t-fill-2);
  border-color: var(--t-line-mid);
  color: rgb(var(--t-ink-rgb));
}
html.tr .search-pin { background: var(--t-panel-solid); border-color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .search-swatch { border-color: rgba(var(--t-ink-rgb), 0.35); }

/* ---------- race-day banner ---------- */

html.tr .race-day {
  background: var(--t-panel);
  color: rgb(var(--t-ink-rgb));
  border: 1px solid var(--t-line);
  box-shadow: var(--t-shadow);
  backdrop-filter: blur(12px);
}
html.tr .race-day-row:hover, html.tr .race-day-row:active { background: var(--t-hover); }
html.tr .race-day-row + .race-day-row { border-top-color: var(--t-line); }
html.tr .race-day-stage { color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .race-day-chev { color: var(--t-dim); }
html.tr .race-day-close { color: rgba(var(--t-ink-rgb), 0.75); border-left-color: var(--t-line); }
html.tr .race-day-close:active { background: var(--t-hover); }

/* ---------- bottom sheet ---------- */

html.tr .sheet {
  background: var(--t-panel);
  color: rgb(var(--t-ink-rgb));
  border: 1px solid var(--t-line);
  border-bottom: 0;
  box-shadow: var(--t-shadow);
  backdrop-filter: blur(12px);
}
html.tr .sheet-handle span { background: rgba(var(--t-ink-rgb), 0.3); }
/* Their scrollbars, copied off .scrollbar-dark on the live page: a 6px thin
   one, a rounded thumb at 18% white that lifts to 32% under the pointer, and
   no track at all. Firefox takes the two-value scrollbar-color; WebKit and
   Blink take the pseudo-elements. Everything in this theme that scrolls gets
   it - the sheet's views, the filter pane, the search list and the
   release notes. */
html.tr .view,
html.tr .year-panel,
html.tr .search-results,
html.tr .wn-body {
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--t-ink-rgb), 0.22) transparent;
}
html.tr .view::-webkit-scrollbar,
html.tr .year-panel::-webkit-scrollbar,
html.tr .search-results::-webkit-scrollbar,
html.tr .wn-body::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}
html.tr .view::-webkit-scrollbar-track,
html.tr .year-panel::-webkit-scrollbar-track,
html.tr .search-results::-webkit-scrollbar-track,
html.tr .wn-body::-webkit-scrollbar-track,
html.tr .view::-webkit-scrollbar-corner,
html.tr .year-panel::-webkit-scrollbar-corner,
html.tr .search-results::-webkit-scrollbar-corner,
html.tr .wn-body::-webkit-scrollbar-corner {
  background: transparent;
}
html.tr .view::-webkit-scrollbar-thumb,
html.tr .year-panel::-webkit-scrollbar-thumb,
html.tr .search-results::-webkit-scrollbar-thumb,
html.tr .wn-body::-webkit-scrollbar-thumb {
  background-color: rgba(var(--t-ink-rgb), 0.18);
  border-radius: 999px;
}
html.tr .view::-webkit-scrollbar-thumb:hover,
html.tr .year-panel::-webkit-scrollbar-thumb:hover,
html.tr .search-results::-webkit-scrollbar-thumb:hover,
html.tr .wn-body::-webkit-scrollbar-thumb:hover {
  background-color: rgba(var(--t-ink-rgb), 0.32);
}
html.tr .sheet-title { color: rgb(var(--t-ink-rgb)); }
html.tr .sheet-hint { color: rgba(var(--t-ink-rgb), 0.7); }

/* list rows: a hairline under each, a wash on touch — their whole list idiom */
html.tr .route-list li,
html.tr .stage-list li,
html.tr .race-stage-list li,
html.tr .result-list li { border-bottom-color: var(--t-line); }
html.tr .route-item:hover,
html.tr .stage-item:hover,
html.tr .rider-result:hover { background: var(--t-hover); }
html.tr .route-item:active,
html.tr .stage-item:active,
html.tr .rider-result:active { background: rgba(var(--t-ink-rgb), 0.09); }
html.tr .route-item .route-sub,
html.tr .stage-item .stage-winner-mini { color: rgba(var(--t-ink-rgb), 0.7); }
/* a famous road's timeline row: an edition we cannot draw goes grey rather
   than spending a race colour on itself, and the stage's own story is set at
   the size this theme sets all its prose */
html.tr .stage-edition.no-route,
html.tr .appearance-row { color: rgba(var(--t-ink-rgb), 0.55); }
/* the side a stage climbed is a note on the edition line, not a name: the
   body face, not the display face the line itself is set in */
html.tr .stage-edition .stage-side {
  color: rgba(var(--t-ink-rgb), 0.55);
  font-family: var(--t-body);
  font-weight: 400;
  letter-spacing: 0;
}
html.tr .stage-story {
  font-size: 13px;
  line-height: 1.625;
  color: rgba(var(--t-ink-rgb), 0.78);
}
html.tr .route-item .dist,
html.tr .stage-item .dist {
  background: var(--t-tdf);
  color: #12141a;
  font-weight: 600;
}
html.tr .route-item .dist.far,
html.tr .stage-item .dist.far {
  background: var(--t-fill-2);
  color: rgba(var(--t-ink-rgb), 0.8);
}
html.tr .stage-item .chev,
html.tr .route-item .chev,
html.tr .rider-result .chev { color: rgba(var(--t-ink-rgb), 0.35); }

/* Links. The old white app's #0a58c2 needed replacing, and the obvious candidate
   was a bright cyan - but a link here is plain ink: a rider's
   name in a result is ink at 12.5px/500, and Back and "See the whole
   ..." are ink-text pills. So: ink, and the underline stays on the ones
   that only look like controls when you are over them. */
html.tr .linklike,
html.tr .back-btn,
html.tr a.rider-link { color: rgb(var(--t-ink-rgb)); }

html.tr a.rider-link { text-decoration: none; }
html.tr a.rider-link:hover { text-decoration: underline; }

/* Back is a button over there, not a link: a hairline pill with a breath of
   white behind it, at the body font's smallest size. Measured off their own
   ("shrink-0 rounded-lg border border-white/25 bg-white/[0.07] px-2.5 py-1
   text-[11.5px]"), with one departure - the vertical padding is theirs plus
   two pixels, so the button stays the height the thumb already had it. The
   row it sits in aligns on the baseline, which is where the text of a pill
   wants to be anyway. */
html.tr .back-btn {
  border: 1px solid rgba(var(--t-ink-rgb), 0.25);
  border-radius: 12px;
  background: rgba(var(--t-ink-rgb), 0.07);
  padding: 6px 11px;
  /* a <button> takes the UA's font, not the page's - the old white app never
     said otherwise, so this one has to name it */
  font-family: var(--t-body);
  font-size: 11px;
  font-weight: 500;
  line-height: 1.2;
  transition: border-color 0.15s, background-color 0.15s;
}
html.tr .back-btn:hover {
  border-color: rgba(var(--t-ink-rgb), 0.5);
  background: rgba(var(--t-ink-rgb), 0.12);
}
html.tr .back-btn:active { background: rgba(var(--t-ink-rgb), 0.18); }
/* A stage or edition that Back names: no fill on a dark panel (the Worlds'
   near-black would vanish, as on the race pill) - the race's colour is a dot
   before the name instead, from the --race set beside the inline fill. */
html.tr .back-race {
  background: none !important;
  color: inherit !important;
  padding: 0;
  font-size: inherit;
  font-weight: inherit;
}
html.tr .back-race::before {
  content: "";
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--race, var(--t-tdf));
  margin: 0 5px 0 1px;
  vertical-align: 1px;
}

html.tr .tap-coords { color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .tap-coords-val { background: var(--t-fill-2); color: rgb(var(--t-ink-rgb)); }
html.tr .copy-btn {
  background: var(--t-fill);
  border-color: var(--t-line-mid);
  color: rgba(var(--t-ink-rgb), 0.85);
}
html.tr .copy-btn:active { background: var(--t-fill-2); }

/* ---------- route view ---------- */

html.tr .route-figs, html.tr .route-meta { color: rgba(var(--t-ink-rgb), 0.78); }

/* background-COLOR, not the `background` shorthand: the shorthand resets
   background-repeat and background-position too, which pin the chevron once
   at the right-hand end (the rule below). Reset, the chevron tiled across
   the whole face - a zigzag of them, with the road's name somewhere behind. */
html.tr .approach-select,
html.tr .approach-menu {
  background-color: rgba(var(--t-raise-rgb), 0.96);
  border-color: var(--t-line-mid);
  color: rgb(var(--t-ink-rgb));
  border-radius: 12px;
}
/* the chevron is drawn into the face as a data URI, so it needs its own
   light-stroke copy rather than a colour override */
html.tr .approach-select {
  background-image: var(--t-chevron);
  background-repeat: no-repeat;
  background-position: right 12px center;
}
html.tr .approach-select:focus {
  border-color: var(--t-line-strong);
  box-shadow: 0 0 0 3px rgba(var(--t-ink-rgb), 0.12);
}
html.tr .approach-picker .face-from { color: rgb(var(--t-ink-rgb)); }
html.tr .approach-picker .face-count { color: var(--t-dim); }
html.tr .approach-menu li:hover,
html.tr .approach-menu li:focus { background: var(--t-hover); }
html.tr .approach-menu li[aria-selected="true"] { background: var(--t-fill-2); }
html.tr .approach-note { color: rgba(var(--t-ink-rgb), 0.7); }

/* ---------- elevation profile ---------- */

/* the box the profile is drawn in: their panel fill rather than white paper.
   The gradient bands inside keep their colours; only the ridge line and the
   baseline, drawn black by app.js as presentation attributes, are re-struck
   here — a CSS property beats an attribute, so no markup changes. */
html.tr .elevation .elev-svg {
  border-color: var(--t-line);
  background: linear-gradient(rgba(var(--t-ink-rgb), 0.06), rgba(var(--t-ink-rgb), 0.02));
}
html.tr .elevation .elev-svg > polyline { stroke: rgba(var(--t-ink-rgb), 0.85); }
html.tr .elevation .elev-svg > line { stroke: rgba(var(--t-ink-rgb), 0.38); }
/* The gradient bands. On near-black a muted scale reads best - saturation in the
   20-55% band, nearly all of it around half lightness - while ours is cut to
   carry on white paper, and on near-black it shouts. One filter over the
   whole chart brings it down: cheaper than a filter per band, and with no
   seams where neighbouring bands share an edge. It costs the ridge and the
   baseline a little, which is what the two strokes above are struck brighter
   to pay back. The 3D view is coloured off the same scale, so it comes along.

   The numbers are halfway back from the first attempt (saturate .66,
   brightness .9), which landed on their register but read dull: the bands
   are the one place this app is allowed to be loud, since they are carrying
   the gradient. */
html.tr-dark .elevation .elev-svg,
html.tr-dark .road3d-canvas { filter: saturate(0.83) brightness(0.95); }
html.tr .elevation .elev-cursor { background: rgba(var(--t-ink-rgb), 0.6); }
html.tr .elevation .elev-dot { background: rgb(var(--t-ink-rgb)); border-color: var(--t-panel-solid); }
html.tr .elevation .elev-cap { color: rgba(var(--t-ink-rgb), 0.72); }
html.tr .elevation .elev-cap b { color: rgb(var(--t-ink-rgb)); }
html.tr .elevation .elev-close,
html.tr .elevation .elev-box-label {
  background: rgba(var(--t-paper-rgb), 0.85);
  color: rgba(var(--t-ink-rgb), 0.8);
}
/* The annotation layer over the profile: the line from a famous road's label
   down to its point on the ridge, the dot at the foot of it, the dashed lap
   marks and their arrowheads. All of it is near-black at low alpha - a
   whisper on white paper, nothing at all here, so the labels floated free of
   the graph they belong to. Same weights, opposite ink. */
html.tr .elevation .elev-poi-line { background: rgba(var(--t-ink-rgb), 0.42); }
html.tr .elevation .elev-climb-stem { background: rgba(var(--t-ink-rgb), 0.5); }
html.tr .elevation .elev-poi-dot { background: rgb(var(--t-ink-rgb)); }
html.tr .elevation .elev-lap-mark,
html.tr .elevation .elev-lap-arrow { border-color: rgba(var(--t-ink-rgb), 0.38); }
html.tr .elevation .elev-lap-arrow.to-right::after { border-left-color: rgba(var(--t-ink-rgb), 0.45); }
html.tr .elevation .elev-lap-arrow.to-left::after { border-right-color: rgba(var(--t-ink-rgb), 0.45); }
/* every label on the chart is one kind of chip, whether or not it opens a
   road: the old white app's white face would be two different things here */
html.tr .elevation .elev-poi-label,
html.tr .elevation .elev-climb-tip,
html.tr .km0-dot .km0-name,
html.tr .kom-dot .kom-name,
html.tr .elevation .elev-lap-toggle {
  background: rgba(var(--t-paper-rgb), 0.85);
  color: rgba(var(--t-ink-rgb), 0.92);
}
html.tr .elevation .elev-dim { fill: rgba(var(--t-paper-rgb), 0.62); }
html.tr .elevation .elev-climb-tip .elev-climb-derived,
html.tr .kom-dot .kom-name .elev-climb-derived { color: rgba(var(--t-ink-rgb), 0.6); }
html.tr .elevation .elev-poi-label.is-link {
  color: rgb(var(--t-ink-rgb));
  border-color: var(--t-line-mid);
}
html.tr .elevation .elev-poi-label.is-link:hover,
html.tr .elevation .elev-poi-label.is-link:focus-visible {
  background: rgba(var(--t-paper-rgb), 0.95);
  box-shadow: 0 0 0 1px rgba(var(--t-ink-rgb), 0.45);
}
html.tr .elevation .elev-3d-btn {
  background: var(--t-fill);
  border-color: var(--t-line-mid);
  color: rgba(var(--t-ink-rgb), 0.85);
}
html.tr .elevation .elev-3d-btn[aria-pressed="true"] {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}
html.tr .road3d-canvas {
  border-color: var(--t-line);
  background: linear-gradient(rgba(var(--t-ink-rgb), 0.06), rgba(var(--t-ink-rgb), 0.02));
  /* the floor, its grid, the road's track and the north arrow (see the
     variables' note in style.css): white, and carried a little further than
     on paper - these alphas were chosen against a white ground, and the same
     numbers over a near-black one barely register */
  --road3d-ink: 255, 255, 255;
  --road3d-ink-alpha: 1.8;
}
html.tr .road3d-note { color: rgba(var(--t-ink-rgb), 0.7); }

/* ---------- stage + race views ---------- */

html.tr .stage-year-chip { background: var(--t-tdf); color: #12141a; }
html.tr .stage-date { color: rgba(var(--t-ink-rgb), 0.65); }
/* Every block of prose is set the same way - a road's description, a
   stage's story, an edition's opening - at 13px over 1.625 in white/90.
   Ours are cut for black on white paper at 14-15px, which is a size too loud
   on their near-black panel. Measured off the live page, not guessed. */
html.tr .stage-summary,
html.tr .route-blurb,
html.tr .race-intro {
  font-size: 13px;
  line-height: 1.625;
  color: rgba(var(--t-ink-rgb), 0.9);
}
/* ...but held at arm's length on a phone, 13px was a strain to read: the
   notes - an edition's opening, a stage's story on its panel and in a list,
   a famous road's description - come up a size there. */
@media (max-width: 699px) {
  html.tr .stage-summary,
  html.tr .route-blurb,
  html.tr .race-intro,
  html.tr .stage-story { font-size: 14px; }
}
html.tr .race-stage-item:hover { background: var(--t-hover); }
html.tr .race-stage-no,
html.tr .race-stage-km,
html.tr .race-stage-gc { color: rgba(var(--t-ink-rgb), 0.65); }
html.tr .race-stage-item.no-route { border-left-color: var(--t-line-mid); }
/* The body face sets "MOUNTAINS" a shade wider than the system face the 74px
   classification column was cut for, and it ended up flush against the
   rider's name. Half a point off the size and the tracking puts the label
   back inside its own column; the column itself does not move. */
html.tr .jersey-cls { font-size: 11.5px; letter-spacing: 0.01em; }
html.tr .result-list .rank { color: rgba(var(--t-ink-rgb), 0.6); }
html.tr .result-list li:first-child .rank { color: var(--t-tdf-ink); }
html.tr .result-list .rteam, html.tr .result-list .rtime { color: rgba(var(--t-ink-rgb), 0.7); }
html.tr .route-ico.dim { color: rgba(var(--t-ink-rgb), 0.4); }

/* Race colours that app.js writes INLINE (RACE_META textColor, the race
   pill's face). They are picked to read on white paper, so on near-black
   the Tour's dark gold goes muddy and the Worlds' near-black vanishes.
   Matching the inline value is the only hook CSS has on them, and it keeps
   each race its own colour rather than flattening the column to one grey. */
html.tr-dark .stage-item .year[style*="#cc9900"],
html.tr-dark .stage-edition[style*="#cc9900"],
html.tr-dark .rr-race[style*="#cc9900"] { color: var(--t-tdf) !important; }
html.tr-dark .stage-item .year[style*="#e05fa0"],
html.tr-dark .stage-edition[style*="#e05fa0"],
html.tr-dark .rr-race[style*="#e05fa0"] { color: var(--t-giro) !important; }
html.tr-dark .stage-item .year[style*="#d64545"],
html.tr-dark .stage-edition[style*="#d64545"],
html.tr-dark .rr-race[style*="#d64545"] { color: #ff6b6b !important; }
html.tr-dark .stage-item .year[style*="#1a1a1a"],
html.tr-dark .stage-edition[style*="#1a1a1a"],
html.tr-dark .rr-race[style*="#1a1a1a"] { color: rgba(var(--t-ink-rgb), 0.92) !important; }
/* A race doesn't go in a pill. The name is set in ink and rules it
   with a short bar in the race's colour - the Col du Tourmalet page does the
   same under the road's name - so the stage header and the edition heading do
   that here. The colour arrives as --race, set inline beside the background
   it replaces: an inline background-color is not something a stylesheet can
   read back and re-use. (This also settles the Worlds, whose near-black pill
   was invisible on a near-black panel.) */
html.tr .stage-year-chip,
html.tr .race-pill {
  position: relative;
  background: none !important;
  color: rgb(var(--t-ink-rgb)) !important;
  border-radius: 0;
  padding: 0 0 8px;
  font-family: var(--t-display);
  font-weight: 600;
  letter-spacing: 0.01em;
}
/* ...and the road's own name on its page, which is where the atlas does it:
   the bar there is the colour of the road's line on the map, handed over as
   --race by openRoute. The name is right-aligned in its row, so the bar ends
   where the name does. */
html.tr .route-head-title {
  position: relative;
  padding-bottom: 8px;
  /* the bar is pinned to the bottom of this box, so what keeps it off the
     line beneath is margin, not padding: style.css ends the road's title with
     margin-bottom 0 (the row it shares needs no gap) and the bar was landing
     on the figures under it. The race panel's heading already stands 10px
     clear of its intro; these two now stand the same. */
  margin-bottom: 8px;
}
html.tr .stage-year-chip { margin-bottom: 4px; }

/* Back rides at the TOP of its row. It is a control standing beside a
   heading, not a word in it, and in this theme the heading's box runs well
   past its own text - the bar under the name, and the air under the bar -
   so centring or baselining Back against that box drops it. */
html.tr .route-topbar,
html.tr .stage-topbar,
html.tr .race-topbar { align-items: flex-start; }
/* ...and on the centre of the first line of the heading across from it -
   a stage's race and number, a road's, an edition's, a rider's name, "Roads
   raced near here" - the same in every panel (the owner's call, 2026-10-07).
   Back can't rise to meet the line: this row is the first thing in a
   scrolling view, which clips whatever rises above its own edge (the 3px
   lift style.css gives Back took the top off it once). So Back goes to that
   edge, and the heading comes down to Back's middle: half of what Back
   stands taller than the heading's capitals. Every Back is the same height
   for that - 29px, the height a race name's 1.35 line gives it, which a
   plain "Back" or a rider's name used to fall 2px short of; a button
   centres its label in whatever height it has. */
html.tr {
  --back-h: 29px;
}
html.tr .route-topbar > .back-btn,
html.tr .stage-topbar > .back-btn,
html.tr .race-topbar > .back-btn {
  top: 0;
  margin-top: 0;
  height: var(--back-h);
}
/* (a Back with no heading beside it keeps a breath of air above it) */
html.tr .view > .back-btn {
  top: 0;
  margin-top: 4px;
}
/* The heading's box is trimmed to the top of its capitals - the line box
   puts a font's own air above them, which differs from engine to engine -
   so its margin places the capitals themselves. (Where text-box is
   unsupported this lands within a few pixels; where the cap unit is, the 9px
   is what it computes to for the stage's chip.) A rider's title is a row of
   name and flag, which no trim reaches: the name trims instead, and the
   flag centres on what is left, its capitals. */
html.tr .stage-year-chip,
html.tr .race-title,
html.tr .route-head-title,
html.tr .here-title,
html.tr .rider-title > span { text-box: trim-start cap alphabetic; }
html.tr .stage-year-chip,
html.tr .race-title,
html.tr .route-head-title,
html.tr .here-title,
html.tr .rider-title {
  margin-top: 9px;
  margin-top: calc((var(--back-h) - 1cap) / 2);
}
/* The chip never leaves Back's row for one of its own: it takes what the row
   has left, and when "GIRO D'ITALIA WOMEN 2026 · STAGE 1" is more than that,
   the stage drops whole onto a second line. The " · " is then the wrong
   mark - it would end the first line or open the second - so it is not text
   but a mark hung just past the race name's right end, in the stage's left
   margin. While the two share a line it stands between them; once the stage
   has dropped, the race name ends the line at the chip's right edge and the
   mark falls outside the chip, where the clip cuts it away (the clip leaves
   the other three sides open: accents rise past the trimmed cap line). The
   race name is an inline-block so the mark has an edge to hang from, wrapped
   in its own words if it alone is too long - and hung from the line's top:
   on the baseline, a wrapped name ("LA COURSE BY LE TOUR DE FRANCE 2014")
   sits on its LAST line and its first lines rise out of the trimmed chip,
   leaving only the year. A one-line name lands on the same pixel either way. */
html.tr #view-stage > .stage-topbar { flex-wrap: nowrap; }
html.tr .stage-year-chip {
  flex: 0 1 auto;
  min-width: 0;
  text-align: right;
  clip-path: inset(-1em 0 -1em -100vw);
}
html.tr .stage-year-chip .stage-race {
  display: inline-block;
  position: relative;
  max-width: 100%;
  vertical-align: top;
}
html.tr .stage-year-chip .stage-race::after {
  content: "·";
  position: absolute;
  left: 100%;
  width: calc(0.25em + 0.6em);
  text-align: center;
}
html.tr .stage-year-chip .stage-n {
  display: inline-block;
  margin-left: 0.6em;
  white-space: nowrap;
}
/* On the edition's own page the name is not a label beside a title - it IS
   the title, the way the road's name is on a road page - so here, where it
   is no longer a pill but a ruled name, it takes the sheet title's size. (A
   multi-select block keeps the smaller one: there it heads one of several
   sections. The app's own theme keeps its 15px pill.) */
html.tr .race-title .race-pill { font-size: inherit; }
html.tr .stage-year-chip::after,
html.tr .race-pill::after,
html.tr .route-head-title::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: 0;
  width: 42px;
  height: 3px;
  border-radius: 2px;
  background: var(--race, var(--t-tdf));
}

/* ---------- rider view ---------- */

html.tr .rider-tier {
  border: 1px solid var(--t-line-mid);
  background: var(--t-fill);
  color: rgba(var(--t-ink-rgb), 0.8);
}
html.tr .rider-tier.on {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}
html.tr .rider-tier-n { color: rgba(var(--t-ink-rgb), 0.6); }
html.tr .rider-chip {
  background: var(--t-fill-2);
  border-color: var(--t-line-strong);
  color: rgb(var(--t-ink-rgb));
}
html.tr .rider-chip-x { background: rgba(var(--t-ink-rgb), 0.14); color: rgb(var(--t-ink-rgb)); }
html.tr .rider-chip:hover .rider-chip-x { background: rgba(var(--t-ink-rgb), 0.26); }
html.tr .rider-bio { color: rgba(var(--t-ink-rgb), 0.85); }
/* The rider's photo from their Velora profile (js/velora_rider.js; Velora
   theme only): the portrait whole, in its own tall shape with rounded
   corners as the profile page shows it, set beside the bio the way the
   profile sets it beside the rider's story. What follows the bio starts
   below it. */
html.tr .velora-photo {
  float: right;
  width: 96px;
  height: auto;
  margin: 10px 0 8px 14px;
  border-radius: 8px;
  background: rgba(var(--t-ink-rgb), 0.06);
}
html.tr.tr-velora #rider-body .velora-articles,
html.tr.tr-velora #rider-body .rider-tiers { clear: both; }
/* Velora's latest articles on the rider (js/velora_rider.js; drawn in the
   Velora theme only): the Race Atlas's own block, measured off its page -
   a hairline, a caps label, three rows of a 72x48 picture beside a two-line
   title and its date, then the way to their profile */
html.tr .velora-articles {
  /* 12px either side of the hairline, and a little more than the chips'
     own 12px under the profile link (owner, 2026-09-30) - as padding, which
     does not fold into the chips' margin as a margin would */
  margin: 12px 0 0;
  padding: 12px 0 6px;
  border-top: 1px solid var(--t-line);
}
html.tr .va-head {
  margin: 0;
  font: 600 10.5px/1.5 var(--t-display);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: rgba(var(--t-ink-rgb), 0.78);
}
html.tr .va-list { list-style: none; margin: 10px 0 0; padding: 0; }
html.tr .va-list li + li { margin-top: 4px; }
html.tr .va-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 -6px;
  padding: 6px;
  border-radius: 6px;
  color: rgb(var(--t-ink-rgb));
  text-decoration: none;
}
html.tr .va-item:hover { background: rgba(var(--t-ink-rgb), 0.06); }
html.tr .va-item:focus-visible {
  background: rgba(var(--t-ink-rgb), 0.09);
  outline: 1px solid rgba(var(--t-ink-rgb), 0.6);
}
html.tr .va-thumb {
  flex: none;
  width: 72px;
  height: 48px;
  overflow: hidden;
  border-radius: 4px;
  background: rgba(var(--t-ink-rgb), 0.06);
}
html.tr .va-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
html.tr .va-text { flex: 1; min-width: 0; }
html.tr .va-title {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  font-size: 12.5px;
  line-height: 1.375;
}
html.tr .va-item:hover .va-title {
  text-decoration: underline;
  text-decoration-color: rgba(var(--t-ink-rgb), 0.5);
  text-underline-offset: 2px;
}
html.tr .va-date {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: rgba(var(--t-ink-rgb), 0.62);
}
html.tr .va-more {
  display: inline-block;
  margin-top: 8px;
  font-size: 12px;
  color: rgb(var(--t-ink-rgb));
  text-decoration: underline;
  text-decoration-color: rgba(var(--t-ink-rgb), 0.4);
  text-underline-offset: 2px;
}
html.tr .va-more:hover { text-decoration-color: rgb(var(--t-ink-rgb)); }
html.tr .rr-place.win { color: var(--t-tdf-ink); }
html.tr .rider-flag,
html.tr .search-flag,
html.tr .rider-chip-flag,
html.tr .result-flag { box-shadow: 0 0 0 1px rgba(var(--t-ink-rgb), 0.3); }
/* ...but not in Velora's: there a flag is the flag alone, as the atlas sets
   it - the ring read as a grey frame round it (the owner's call, 2026-10-02) */
html.tr.tr-velora .rider-flag,
html.tr.tr-velora .search-flag,
html.tr.tr-velora .rider-chip-flag,
html.tr.tr-velora .result-flag { box-shadow: none; border-radius: 1px; }

/* ---------- the theme switch itself ----------
   Last line of the filter pane, bottom left: a quiet text link, the way
   the way the panel footnotes things. It names the theme it takes you to
   (see js/theme.js). */

/* One row at the foot of the pane: the switch on the left, the build stamp
   holding the right-hand end it has always had. */
.panel-foot {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 14px 0 0;
}
.panel-foot .app-version { margin: 0 0 0 auto; }
.theme-switch {
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  font-size: 11.5px;
  color: #777;
  letter-spacing: 0.02em;
  cursor: pointer;
}
.theme-switch:hover { color: #1a1a1a; }
.base-switch { margin-left: 16px; }
.base-switch[hidden] { display: none; }

html.tr .theme-switch {
  font-family: var(--t-body);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.01em;
  text-decoration: none;
  color: rgba(var(--t-ink-rgb), 0.55);
}
html.tr .theme-switch:hover { color: rgb(var(--t-ink-rgb)); }
/* Velora shows no way to the other themes (the owner's call, 2026-09-28):
   it is reached by its address alone, and the link switches dark and light */
html.tr.tr-velora #theme-switch { display: none; }

/* ---------- Release notes ----------
   The build stamp opens them (js/whats_new.js): a window over the page,
   one section per release, each line tagged New / Fix / Route. */
.app-version.wn-link { cursor: pointer; }
.app-version.wn-link:hover,
.app-version.wn-link:focus-visible { text-decoration: underline; color: rgb(var(--t-ink-rgb)); }
.wn-overlay {
  position: fixed;
  inset: 0;
  z-index: 5000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.45);
}
.wn-overlay[hidden] { display: none; }
.wn-box {
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: min(80vh, 760px);
  background: var(--t-panel-solid);
  color: rgb(var(--t-ink-rgb));
  border: 1px solid var(--t-line-mid);
  border-radius: 12px;
  box-shadow: var(--t-shadow);
  font-family: var(--t-body);
  overflow: hidden;
}
.wn-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 12px 20px;
  border-bottom: 1px solid var(--t-line);
}
.wn-title {
  margin: 0;
  font-family: var(--t-display);
  font-size: 17px;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.wn-close {
  border: 0;
  background: none;
  color: var(--t-dim);
  font-size: 24px;
  line-height: 1;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  cursor: pointer;
}
.wn-close:hover { background: var(--t-hover); color: rgb(var(--t-ink-rgb)); }
.wn-body {
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 20px 16px;
}
.wn-release { padding: 12px 0 10px; border-bottom: 1px solid var(--t-line); }
.wn-release:last-child { border-bottom: 0; }
.wn-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin: 0 0 6px;
  font-size: 13px;
  font-weight: 600;
}
.wn-v { font-family: var(--t-display); font-size: 14px; }
.wn-date { color: var(--t-dim); font-weight: 400; font-size: 12px; }
.wn-items { list-style: none; margin: 0; padding: 0; }
.wn-item {
  display: flex;
  gap: 10px;
  align-items: baseline;
  padding: 3px 0;
  font-size: 13.5px;
  line-height: 1.4;
}
.wn-kind {
  flex: 0 0 44px;
  font-family: var(--t-display);
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--t-dim);
}
.wn-feature .wn-kind { color: var(--t-men-ink); }
.wn-route .wn-kind { color: var(--t-tdf-ink); }
