/* =====================================================================
   DESIGN TOKENS - shared across the components and the profiles
   ===================================================================== */
:root {
    /* Typografie */
    --font-ui: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

    /* Colours - backgrounds */
    --color-bg:             #fff;
    --color-bg-translucent: rgba(255, 255, 255, 0.9);
    --color-bg-overlay:     rgba(0, 0, 0, 0.4);
    --color-bg-subtle:      #f0f0f0;
    --color-bg-hover:       #e0e0e0;
    --color-bg-soft:        rgba(0, 0, 0, 0.05);
    --color-bg-soft-hover:  rgba(0, 0, 0, 0.08);
    --color-bg-tag:         #e8e8e8;
    --color-bg-icon:        rgba(0, 0, 0, 0.06);
    --color-bg-icon-hover:  rgba(0, 0, 0, 0.14);
    --color-bg-tooltip:     rgba(0, 0, 0, 0.72);

    /* Colours - borders */
    --color-border:         #e0e0e0;
    --color-border-strong:  #ccc;

    /* Colours - text */
    --color-text:           #222;
    --color-text-strong:    #1a1a1a;
    --color-text-mid:       #333;
    --color-text-pale:      #444;
    --color-text-light:     #555;
    --color-text-muted:     #666;
    /* Measured, not picked: #888 was 3.54:1 on the page and 3.11:1 on a hovered
       row, against the 4.5 that small text needs. This clears both, at 5.41 and
       4.75, and is still a step below --color-text-muted - which is what the
       token is for. */
    --color-text-faint:     #6a6a6a;

    /* The gold that means "there is something behind this" - the same signal as
       the ring icons.js draws round a POI with an article (WIKI_STROKE,
       #d4a000). The title of a bubble is a link to that article, so it carries
       the same meaning and wants the same colour; what it cannot carry is the
       same *value*, because #d4a000 on the white bubble measures 2.38:1 and the
       title is 15px semibold, which needs 4.5. Darkened to the first step of
       that hue which clears it (5.22:1), and the dark theme takes the sprite's
       own gold, where it reads at 7.32:1. */
    --color-wiki-link:      #8a6700;

    /* The three stops of a planned route and the shared point, which the map
       draws as markers. They are deliberately *not* overridden in the dark
       theme: like the route and POI colours they are data rather than chrome -
       a green start is the start - and the theme switch leaves the overlays
       alone (CLAUDE.md). The markers themselves are MapLibre's, which cannot
       read a custom property, so routing.js and map-actions.js read these back
       out of the DOM - the same way the hover photo reads its width. Before
       that, three of them were written once here and once in JavaScript. */
    --color-stop-start:     #2db82d;
    --color-stop-end:       #e53935;
    --color-stop-via:       #ff9800;
    --color-shared-point:   #7b1fa2;

    /* The elevation profile. The terrain wash is the accent at low alpha rather
       than the opaque tint, so the chart's own background still reads through
       it; the sight line and the ground that blocks it are their own pair,
       because they mean something the accent does not. */
    --color-chart-wash:     rgba(233, 30, 99, 0.12);
    --color-los:            #1565c0;
    --color-los-blocked:    rgba(244, 67, 54, 0.35);

    /* The white outline that keeps a shape on the map readable over whatever
       is underneath it - the position arrow, the off-screen arrow, the cone of
       the photo minimap. Not a shadow: it is the shape's own contrast, which
       is why it does not change with the theme either. Its twin for map *paint*
       is `colors.halo` in config.js - MapLibre cannot read a custom property -
       and the two carry the same value on purpose. */
    --color-halo:           rgba(255, 255, 255, 0.9);

    /* Colours - accent (pink) */
    /* The GPS blue. track.js repeats it as rgba() for the MapLibre line paint,
       which cannot read a custom property - keep the two in step. */
    --color-gps:            #1e78ff;

    /* The accent in three strengths, and the reason there are three is measured
       rather than aesthetic. `--color-accent` is the identity: the dot, the
       border, the route line - places where it is a shape and a contrast ratio
       is the wrong instrument for it. It is *not* safe for text or for a label
       on top of it: against white it measures 4.35:1 and 13px text needs 4.5.
       So anything that has to be read takes `--color-accent-strong`, which
       clears it at 5.87:1, and a filled button hovers to a darker value rather
       than to a brighter one - the fill is the colour the hover used to be.
       The dark theme needs none of this; see its own block. */
    --color-accent:         #e91e63;
    --color-accent-strong:  #c2185b;
    --color-accent-hover:   #a9134f;
    --color-accent-tint:    #fce4ec;

    /* Colours - error */
    --color-error:          #c00;
    --color-error-bg:       #fff3f3;
    --color-error-border:   #f5c0c0;

    /* Shadows */
    --shadow-md:            0 2px 8px rgba(0, 0, 0, 0.2);
    --shadow-lg:            0 4px 16px rgba(0, 0, 0, 0.2);
    --shadow-xl:            0 8px 32px rgba(0, 0, 0, 0.3);

    /* Radii */
    --radius-sm:            4px;
    --radius-md:            6px;
    --radius-lg:            10px;
    --radius-pill:          20px;

    /* UI sizes */
    /* The photo in a hover preview, shared by the Wikipedia bubble and the
       Panoramax one so the two are the same size. The height cap is what keeps
       a portrait photo from becoming a column taller than the window; the
       image keeps its aspect ratio and the bubble shrinks to whichever of the
       two constraints binds. */
    --preview-photo-width:  min(520px, 86vw);
    --preview-photo-height: 55vh;

    --ctrl-icon:            24px;
    --panel-width:          clamp(320px, 25vw, 420px);  /* left search/routing panel */

    /* Typography - four logical sizes.
       xs = fine print/meta, sm = body text, md = headings/input, lg = large icons */
    --text-xs:              12px;
    --text-sm:              13px;
    --text-md:              15px;
    --text-lg:              20px;

    /* Transitions */
    --transition-fast:      0.15s ease;

    /* z-index layers */
    --z-overlay-panel:      10;
    --z-modal-overlay:      100;
    --z-tooltip:            200;
    --z-gps-arrow:          150;   /* above the panels, below a modal */
}

/* =====================================================================
   DARK THEME - colour tokens only
   ===================================================================== */
/* Set by js/theme.js from the URL, not by prefers-color-scheme: the switch is
   explicit and shareable, and light is the default whatever the system says.
   Only colour tokens are overridden here - a size or a radius that differed
   between the two would be a second layout to keep in step.
   The map is not styled by any of this; its colours live in hiking-base.json
   and theme.js derives the dark palette from them. */
:root[data-theme="dark"] {
    /* Tells the browser which way its own widgets go: scrollbars, form
       controls and the canvas's default backdrop. Without it a dark page keeps
       light scrollbars. */
    color-scheme: dark;

    /* Colours - backgrounds */
    --color-bg:             #1e2024;
    --color-bg-translucent: rgba(30, 32, 36, 0.92);
    --color-bg-overlay:     rgba(0, 0, 0, 0.6);
    --color-bg-subtle:      #272a2f;
    --color-bg-hover:       #32363c;
    --color-bg-soft:        rgba(255, 255, 255, 0.06);
    --color-bg-soft-hover:  rgba(255, 255, 255, 0.10);
    --color-bg-tag:         #32363c;
    --color-bg-icon:        rgba(255, 255, 255, 0.08);
    --color-bg-icon-hover:  rgba(255, 255, 255, 0.16);
    --color-bg-tooltip:     rgba(0, 0, 0, 0.85);

    /* Colours - borders */
    --color-border:         #383c42;
    --color-border-strong:  #4c525a;

    /* Colours - text. The light theme runs from #222 to #888; this is that
       scale reflected, so a component that reaches for "fainter" still gets
       something fainter rather than something arbitrary. */
    --color-text:           #e6e8ea;
    --color-text-strong:    #f5f6f7;
    --color-text-mid:       #dcdee1;
    --color-text-pale:      #ccd0d4;
    --color-text-light:     #b8bdc2;
    --color-text-muted:     #9aa0a6;
    /* 4.26:1 on the page and 3.76 on a hovered row before; 5.72 and 5.05 now. */
    --color-text-faint:     #929aa2;

    /* The elevation profile, which was drawn for the light theme only - a
       near-white chart inside a dark panel, with #1565c0 on it at 2.51:1. */
    --color-chart-wash:     rgba(255, 75, 132, 0.18);
    --color-los:            #64b5f6;
    --color-los-blocked:    rgba(244, 67, 54, 0.4);

    /* Colours - accent. The pink is lightened rather than changed: it is the
       project's accent in both themes, and the light one fails against a dark
       panel. The GPS blue is left alone - it is the same dot on the same map. */
    --color-accent:         #ff4b84;
    /* Readable as text and under a label at 5.12:1 already, so the strong
       variant is the accent itself here. The light theme is the one that
       needed a darker value for those two jobs. */
    --color-accent-strong:  #ff4b84;
    --color-accent-hover:   #ff6f9c;
    --color-accent-tint:    #3b1f2b;

    /* Colours - error */
    --color-error:          #ff6b6b;
    --color-error-bg:       #2e1c1c;
    --color-error-border:   #5a2e2e;

    /* The sprite's own gold, which the dark bubble can take unchanged. */
    --color-wiki-link:      #d4a000;

    /* Shadows. On a dark surface a black shadow does nothing, so the elevation
       cue becomes a light rim plus a deeper shadow. */
    --shadow-md:            0 2px 8px rgba(0, 0, 0, 0.55);
    --shadow-lg:            0 4px 16px rgba(0, 0, 0, 0.6);
    --shadow-xl:            0 8px 32px rgba(0, 0, 0, 0.7);
}

/* MapLibre's own controls come from maplibre-gl.css with hardcoded light
   colours, so they are the one part of the chrome that custom properties
   cannot reach. The icons are inline SVG in background-image and cannot be
   recoloured, hence the inversion. */
:root[data-theme="dark"] .maplibregl-ctrl-group {
    background: var(--color-bg);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button + button {
    border-top-color: var(--color-border);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button {
    color: var(--color-text);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button:hover {
    background: var(--color-bg-hover);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button .maplibregl-ctrl-icon {
    filter: invert(1) brightness(1.4);
}

/* A control button that is a switch rather than an action says so by colour -
   the GPS button is the only one, and nothing in MapLibre's own stylesheet
   covers a pressed state for a custom control. */
.maplibregl-ctrl-group button.ctrl-toggle-on {
    background: var(--color-accent-tint);
    color: var(--color-accent-strong);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button.ctrl-toggle-on {
    background: var(--color-accent);
    color: var(--color-bg);
}
/* The automatic GPS mode suspended by a gesture: still on, not in charge of
   the camera. Muted rather than a third colour - the state is temporary and
   the button says the rest in its title. */
.maplibregl-ctrl-group button.ctrl-toggle-on.ctrl-toggle-wait {
    background: var(--color-bg-subtle);
    color: var(--color-text-muted);
}
:root[data-theme="dark"] .maplibregl-ctrl-group button.ctrl-toggle-on.ctrl-toggle-wait {
    background: var(--color-bg-hover);
    color: var(--color-text);
}

:root[data-theme="dark"] .maplibregl-ctrl-scale {
    background: var(--color-bg-translucent);
    border-color: var(--color-border-strong);
    color: var(--color-text);
}
:root[data-theme="dark"] .maplibregl-ctrl-attrib {
    background: var(--color-bg-translucent);
    color: var(--color-text-muted);
}
:root[data-theme="dark"] .maplibregl-ctrl-attrib a {
    color: var(--color-text-light);
}
:root[data-theme="dark"] .maplibregl-popup-content {
    background: var(--color-bg);
    color: var(--color-text);
}
:root[data-theme="dark"] .maplibregl-popup-tip {
    border-top-color: var(--color-bg);
    border-bottom-color: var(--color-bg);
}

/* =====================================================================
   BASE / RESET
   ===================================================================== */
* { margin: 0; padding: 0; }

html, body { width: 100%; height: 100%; }
#map       { width: 100%; height: 100%; }

/* The page's <h1>. The map is a WebGL canvas with no readable text, so the
   heading exists for crawlers and screen readers. Clipped rather than
   display:none, which would take it out of the accessibility tree too. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* Shown only when the map cannot run (<noscript>), so it sits above the empty
   map container rather than replacing it. */
.noscript-note {
    position: absolute;
    inset: 0;
    z-index: 10;
    overflow: auto;
    padding: 24px;
    background: var(--color-bg);
    font-family: var(--font-ui);
    line-height: 1.6;
}
.noscript-note h2 { margin-bottom: 12px; }
.noscript-note p  { max-width: 60ch; margin-bottom: 12px; }

/* =====================================================================
   COMPONENTS - the default (desktop) profile
   The mobile deviations live in the "PROFILE: MOBILE" section at the end.
   ===================================================================== */

/* ---------- Shared popup overlay ---------- */
.popup-overlay {
    position: fixed;
    inset: 0;
    background: var(--color-bg-overlay);
    z-index: var(--z-modal-overlay);
    display: flex;
    align-items: center;
    justify-content: center;
}

.popup-panel {
    background: var(--color-bg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xl);
    max-width: 520px;
    max-height: 80vh;
    width: 90vw;
    display: flex;
    flex-direction: column;
    font-family: var(--font-ui);
}

.popup-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
    font-size: var(--text-md);
    font-weight: 700;
}

.popup-close {
    background: none;
    border: none;
    font-size: var(--text-lg);
    cursor: pointer;
    color: var(--color-text-faint);
    line-height: 1;
    padding: 0 4px;
}
.popup-close:hover { color: var(--color-text-mid); }

.popup-body {
    padding: 12px 16px;
    overflow-y: auto;
}

/* ---------- Wiki hover preview ---------- */
.wiki-preview {
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    line-height: 1.45;
    color: var(--color-text);
    max-width: var(--preview-photo-width);
}

.wiki-preview-title {
    font-weight: 600;
    font-size: var(--text-md);
    margin-bottom: 4px;
    color: var(--color-text-strong);
}

/* The text is capped well short of the photo: a 250-character extract set
   across the full width of a large picture is a single unreadable line. */
.wiki-preview-body {
    color: var(--color-text-pale);
    max-width: 62ch;
}

/* The photo, in both bubbles. It used to be an 80 px thumbnail floated beside
   the text; it is now the content, so it leads and the text follows. `auto`
   dimensions against the two caps are what preserve the aspect ratio - a
   fixed width plus `object-fit` would either crop the picture or letterbox it
   inside a box the bubble had already reserved. */
.wiki-preview-thumb {
    display: block;
    width: auto;
    height: auto;
    max-width: 100%;
    max-height: var(--preview-photo-height);
    margin: 0 0 8px;
    border-radius: var(--radius-sm);
    background: var(--color-bg-subtle);  /* visible while it decodes */
}

/* ---------- Popup legendy ---------- */
.legend-toolbar {
    display: flex;
    justify-content: flex-start;
    margin-bottom: 8px;
}

.legend-section            { margin-bottom: 12px; }
.legend-section:last-child { margin-bottom: 0; }

.legend-section-title {
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 6px 4px;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 4px;
    border-radius: var(--radius-sm);
    user-select: none;
}
.legend-section-title:hover {
    background: var(--color-bg-icon);
    color: var(--color-text-mid);
}

.legend-toggle-all {
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    padding: 4px 12px;
    font-size: var(--text-xs);
    cursor: pointer;
    font-family: inherit;
}
.legend-toggle-all:hover { background: var(--color-bg-hover); }

.legend-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2px 16px;
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 4px;
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
    transition: opacity var(--transition-fast);
    user-select: none;
}
.legend-item:hover { background: var(--color-bg-soft); }

.legend-item-off { opacity: 0.35; }

.legend-icon-wrap {
    flex-shrink: 0;
    width: var(--ctrl-icon);
    height: var(--ctrl-icon);
    display: flex;
    align-items: center;
    justify-content: center;
}
.legend-icon {
    width: var(--ctrl-icon);
    height: var(--ctrl-icon);
    object-fit: contain;
}

/* ---------- Stop popup (line selection) ---------- */
.route-popup-title {
    font-weight: 700;
    font-size: var(--text-md);
    margin-bottom: 6px;
    padding-bottom: 4px;
    border-bottom: 1px solid var(--color-border);
}

.route-popup-line {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 6px;
    cursor: pointer;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
.route-popup-line:hover { background: var(--color-bg-soft-hover); }

.route-popup-ref {
    font-weight: 700;
    min-width: 28px;
    text-align: center;
    background: var(--color-bg-tag);
    border-radius: 3px;
    padding: 1px 5px;
    font-size: var(--text-xs);
}

#route-reset-btn {
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: var(--z-overlay-panel);
    background: var(--color-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    padding: 8px 16px;
    font-size: var(--text-sm);
    font-weight: 600;
    font-family: var(--font-ui);
    cursor: pointer;
    box-shadow: var(--shadow-md);
}
#route-reset-btn:hover { background: var(--color-bg-subtle); }

/* ---------- Left panel: search + route planning ---------- */
/* Collapsed = a floating bar in the top left; expanded = a full-height panel.
   The map stays whole behind the panel (the panel covers it). */
.side-panel {
    position: fixed;
    top: 12px;
    left: 12px;
    width: var(--panel-width);
    max-width: calc(100vw - 24px);
    z-index: var(--z-overlay-panel);
    background: var(--color-bg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    font-family: var(--font-ui);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.side-panel.sp-expanded {
    top: 0;
    left: 0;
    bottom: 0;
    height: 100dvh;
    border-radius: 0;
}

/* Header: search bar + directions button */
.sp-header {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px;
    flex-shrink: 0;
}

/* The logo, which is also the way back to the default view. */
.sp-logo {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    border-radius: var(--radius-sm);
}
.sp-logo img { display: block; width: 28px; height: 28px; }
.sp-logo:hover { opacity: 0.8; }

.sp-search {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    height: 40px;
    padding: 0 10px;
    background: var(--color-bg-subtle);
    border-radius: var(--radius-pill);
}
.sp-search-icon { flex: 0 0 auto; display: flex; color: var(--color-text-faint); }

.sp-input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    outline: none;
    font-family: inherit;
    font-size: var(--text-md);
    color: var(--color-text);
}
.sp-input::-webkit-search-cancel-button { display: none; }

/* Header buttons: directions (enters planning) + the single close cross */
.sp-directions {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-accent-strong);
    cursor: pointer;
}
.sp-directions:hover  { background: var(--color-bg-hover); }
.sp-directions.active { background: var(--color-accent-strong); color: var(--color-bg); }

.sp-close {
    flex: 0 0 auto;
    margin-left: auto;         /* keeps the cross right even with the bar/directions hidden */
    width: 40px;
    height: 40px;
    display: none;             /* only appears in the expanded state */
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-md);
    background: var(--color-bg-subtle);
    color: var(--color-text-light);
    font-size: var(--text-md);
    line-height: 1;
    cursor: pointer;
}
.sp-close:hover { background: var(--color-bg-hover); color: var(--color-text-mid); }
/* The cross only when expanded; directions hides during planning (the cross closes it) */
.side-panel.sp-expanded .sp-close { display: flex; }
.side-panel.sp-mode-directions .sp-directions { display: none; }

/* Body - visible only in the expanded state */
.sp-body {
    display: none;
    flex: 1 1 auto;
    overflow-y: auto;
    border-top: 1px solid var(--color-border);
}
.side-panel.sp-expanded .sp-body { display: block; }

/* Switching between the search and route planning views */
.side-panel.sp-mode-search    .sp-routing-view { display: none; }
.side-panel.sp-mode-directions .sp-search-view { display: none; }

.search-result {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 12px;
    cursor: pointer;
    border-bottom: 1px solid var(--color-bg-subtle);
}
.search-result:last-child { border-bottom: none; }
.search-result:hover      { background: var(--color-bg-soft); }

.search-result-name {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--color-text);
}
.search-result-meta {
    font-size: var(--text-xs);
    color: var(--color-text-faint);
    margin-top: 2px;
}

.search-result-empty,
.search-result-error {
    color: var(--color-text-faint);
    font-style: italic;
    cursor: default;
}
.search-result-error { color: var(--color-error); }

/* Photo on the left (a square thumbnail, added once loaded) */
.search-result-thumb {
    flex: 0 0 auto;
    width: 64px;
    height: 64px;
    object-fit: cover;
    border-radius: var(--radius-sm);
}

/* Text body on the right */
.search-result-body {
    flex: 1 1 auto;
    min-width: 0;
}

/* Body header: the name (left) + the Wikipedia button (right) */
.search-result-head {
    display: flex;
    align-items: flex-start;
    gap: 8px;
}
.search-result-head .search-result-name {
    flex: 1 1 auto;
    min-width: 0;
}

.search-result-wiki {
    flex: 0 0 auto;
    width: var(--ctrl-icon);
    height: var(--ctrl-icon);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    background: var(--color-bg-icon);
    color: var(--color-text-light);
    font-size: var(--text-sm);
    font-weight: 700;
    text-decoration: none;
    line-height: 1;
}
.search-result-wiki:hover {
    background: var(--color-bg-icon-hover);
    color: var(--color-text-strong);
}

/* The "route from my position to here" icon - only when the position is known */
.search-result-route {
    flex: 0 0 auto;
    width: var(--ctrl-icon);
    height: var(--ctrl-icon);
    display: flex;
    align-items: center;
    justify-content: center;
    border: none;
    border-radius: var(--radius-sm);
    background: var(--color-bg-icon);
    color: var(--color-accent-strong);
    cursor: pointer;
    line-height: 1;
}
.search-result-route:hover { background: var(--color-bg-icon-hover); }

.search-result-extract {
    font-size: var(--text-xs);
    line-height: 1.4;
    color: var(--color-text-light);
    margin-top: 4px;
}

/* List sentinel - triggers the lazy load of the next page of results (5 at a time). */
.search-sentinel { height: 1px; }

/* ---------- Route planning (renders inside the panel body) ---------- */
.rp-head {
    display: flex;
    align-items: center;
    padding: 10px 12px;
    border-bottom: 1px solid var(--color-border);
    font-size: var(--text-md);
    font-weight: 700;
}

.rp-body { padding: 12px 16px 14px; font-size: var(--text-sm); }

.rp-profiles { display: flex; gap: 4px; margin-bottom: 12px; }
.rp-profile-btn {
    flex: 1;
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    padding: 4px 0;
    font-size: var(--text-xs);
    color: var(--color-text-mid);
    cursor: pointer;
    font-family: inherit;
    transition: background var(--transition-fast);
}
.rp-profile-btn:hover        { background: var(--color-bg-hover); }
.rp-profile-btn.active       { background: var(--color-accent-strong); color: var(--color-bg); border-color: var(--color-accent-strong); }
.rp-profile-btn.active:hover { background: var(--color-accent-hover); }

.rp-wp-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--text-sm);
    text-align: left;
    margin-bottom: 8px;
    box-sizing: border-box;
    font-family: inherit;
    background: var(--color-bg-subtle);
    border: 1px solid var(--color-border-strong);
    transition: background var(--transition-fast);
}
.rp-wp-btn:hover  { background: var(--color-bg-hover); }
.rp-wp-btn.active {
    background: var(--color-accent-tint);
    border-color: var(--color-accent);
    color: var(--color-accent-strong);
    font-weight: 600;
}

/* --- The direction of travel on the position dot -------------------------- */
/* MapLibre's own dot has both pseudo-elements taken - the pulse and the white
   ring - so the arrow is a child. It is hidden until the distance gate in
   track.js has a course to show, which is also why it never twitches: the
   course only changes when somebody has actually covered ground. */
.maplibregl-user-location-dot { position: relative; }
.maplibregl-user-location-dot .gps-heading-arrow { display: none; }
.maplibregl-user-location-dot.has-heading .gps-heading-arrow {
    display: block;
    position: absolute;
    left: 50%;
    top: 50%;
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-bottom: 9px solid var(--color-gps);
    filter: drop-shadow(0 0 1.5px var(--color-halo));
    /* Centred on the dot, turned to the course, then pushed out along it - so
       the triangle both points the right way and sits on that side. */
    transform: translate(-50%, -50%) rotate(var(--gps-heading, 0deg)) translateY(-15px);
}

/* --- Ending navigation ---------------------------------------------------- */
/* Bottom centre, where the followed position is not: navigation puts that in
   the lower third, and the attribution line is below this again. */
.nav-end {
    position: fixed;
    left: 50%;
    bottom: 34px;
    transform: translateX(-50%);
    z-index: 4;
    padding: 9px 18px;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-bg);
    background: var(--color-accent-strong);
    border: none;
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-lg);
    cursor: pointer;
}
.nav-end:hover { background: var(--color-accent-hover); }

/* --- The planner's form: a row per stop ----------------------------------- */
/* Each row is [handle] [dot] [field] [buttons]; the field holds the input and
   the suggestion list, which is why it is the positioned ancestor. */
.rp-form { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }

.rp-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.rp-row-dragging { opacity: 0.6; }
.rp-row-picking .rp-input {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 2px var(--color-accent-tint);
}

/* The dot says which stop this is, in the colours of the markers on the map. */
.rp-dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid var(--color-bg);
    box-shadow: 0 0 0 1px var(--color-border-strong);
}
.rp-dot-start { background: var(--color-stop-start); }
.rp-dot-via   { background: var(--color-accent); }
.rp-dot-end   { background: var(--color-stop-end); }

.rp-handle {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    /* A thumb needs more than the glyph: the hit area is the button size the
       rest of the row uses, which is also what stops a drag starting with a
       miss onto the field beside it. */
    width: 22px;
    height: 28px;
    cursor: grab;
    color: var(--color-text-faint);
    font-size: 15px;
    line-height: 1;
    user-select: none;
    /* Without this a phone scrolls the page instead of reordering the row. */
    touch-action: none;
}
.rp-handle:hover { color: var(--color-text); }
.rp-handle:active { cursor: grabbing; }

.rp-field { position: relative; flex: 1 1 auto; min-width: 0; }
.rp-input {
    width: 100%;
    box-sizing: border-box;
    padding: 6px 8px;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-text);
    background: var(--color-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
}
.rp-input:focus { outline: none; border-color: var(--color-accent); }

.rp-icon-btn {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    line-height: 1;
    color: var(--color-text-light);
    background: var(--color-bg-soft);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.rp-icon-btn:hover { background: var(--color-bg-soft-hover); color: var(--color-text); }

.rp-add-via {
    align-self: flex-start;
    margin-bottom: 10px;
    padding: 4px 8px;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-accent-strong);
    background: none;
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.rp-add-via:hover { background: var(--color-accent-tint); }

/* The clear button is a full-width block on its own; inside the row it has to
   give the navigate button the space instead. */
.rp-buttons { display: flex; gap: 6px; margin-bottom: 10px; }
.rp-buttons .rp-wp-btn {
    width: auto;
    margin-bottom: 0;
    justify-content: center;
    text-align: center;
}
.rp-nav-btn {
    flex: 1 1 auto;
    padding: 8px 12px;
    text-align: center;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-bg);
    background: var(--color-accent-strong);
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.rp-nav-btn:hover:not(:disabled) { background: var(--color-accent-hover); }
.rp-nav-btn:disabled {
    background: var(--color-bg-hover);
    color: var(--color-text-faint);
    cursor: default;
}

/* --- The suggestion list, shared by every field that searches the index --- */
.sg-list {
    position: absolute;
    z-index: 5;
    left: 0;
    right: 0;
    top: calc(100% + 2px);
    max-height: 220px;
    overflow-y: auto;
    background: var(--color-bg);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
}
.sg-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 6px 8px;
    font-size: var(--text-sm);
    cursor: pointer;
}
.sg-item:hover, .sg-active { background: var(--color-bg-subtle); }
.sg-item-name { color: var(--color-text); }
.sg-item-kind { color: var(--color-text-faint); font-size: var(--text-xs); white-space: nowrap; }

/* --- The action row inside a place bubble --------------------------------- */
/* The glyphs read as what happens to the point: an arrow into the dot is
   arriving, out of it is leaving, through it is passing. */
.pb-actions {
    display: flex;
    gap: 6px;
    margin-top: 8px;
}
.pb-action {
    min-width: 34px;
    height: 28px;
    padding: 0 6px;
    font-size: 13px;
    line-height: 1;
    color: var(--color-text);
    background: var(--color-bg-soft);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.pb-action:hover { background: var(--color-accent-tint); border-color: var(--color-accent); }
.pb-action-done  { background: var(--color-accent-tint); }
.pb-note { margin-top: 6px; font-size: var(--text-xs); color: var(--color-text-muted); }

/* The bubble does not take the pointer: MapLibre gives a popup's content
   `pointer-events: auto`, and a press on the bubble is then a press on nothing -
   grabbing the map where a bubble happens to be would not pan it. Only the
   bubble's own controls take the pointer, so everything else passes through to
   the canvas. What that costs, and how the bubble still survives a hover, is in
   place-bubble.js. The class is shared with the photo bubble in panoramax.js,
   which has no controls and is therefore transparent throughout. */
.wiki-preview-popup .maplibregl-popup-content { pointer-events: none; }
.wiki-preview-popup .pb-action,
.wiki-preview-popup .pb-title-link { pointer-events: auto; }

/* The article is reached from the title, the POI itself being no longer
   clickable, so the title has to look like the link it is - in the gold that
   already means "there is something behind this" on the map. */
.pb-title-link {
    color: var(--color-wiki-link);
    text-decoration: none;
}
.pb-title-link:hover { text-decoration: underline; }
/* A bubble opened on a bare point has no photo and no text, so the box must
   not keep the preview's picture width. */
.pb-box { min-width: 150px; }

.rp-info { font-size: var(--text-sm); line-height: 1.7; margin-bottom: 8px; }
.rp-info strong { color: var(--color-accent-strong); }

.rp-error {
    font-size: var(--text-xs);
    color: var(--color-error);
    background: var(--color-error-bg);
    border: 1px solid var(--color-error-border);
    border-radius: var(--radius-sm);
    padding: 6px 8px;
    margin-bottom: 8px;
}
.rp-loading {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    font-style: italic;
    margin-bottom: 8px;
}

.rp-elevation { margin: 0 0 10px; }

/* The elevation profile is an SVG built in JavaScript, and its colours are
   here rather than in the attributes for one reason: it used to be painted for
   the light theme alone - a near-white chart inside a dark panel - and a
   presentation attribute cannot be overridden by a theme. CSS beats the
   attribute, so a class per element is all it takes. */
.rp-el-bg          { fill: var(--color-bg-subtle); }
.rp-el-area {
    fill: var(--color-chart-wash);
    stroke: var(--color-accent);
    stroke-width: 1.5;
    stroke-linejoin: round;
}
.rp-el-label {
    fill: var(--color-text-light);
    font-family: var(--font-ui);
    font-size: 10px;
    text-anchor: middle;
}
.rp-el-hover-line {
    stroke: var(--color-text-light);
    stroke-width: 1;
    stroke-dasharray: 3 2;
}
.rp-el-hover-dot {
    fill: var(--color-accent);
    stroke: var(--color-bg);
    stroke-width: 1.5;
}
.rp-el-los {
    fill: none;
    stroke: var(--color-los);
    stroke-width: 1.5;
    stroke-dasharray: 4 3;
    stroke-linecap: round;
}
.rp-el-los-blocked { fill: var(--color-los-blocked); stroke: none; }
.rp-el-los-key     { color: var(--color-los); }
.rp-elevation-title {
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 4px;
}
.rp-elevation svg { display: block; width: 100%; height: auto; border-radius: 3px; cursor: crosshair; }
.rp-elevation-stats {
    display: flex;
    justify-content: space-between;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    margin-top: 3px;
}

/* A waypoint (via) dragged onto the route line */
.rp-via-marker {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: var(--color-stop-via);
    border: 2px solid var(--color-bg);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
    cursor: grabbing;
}

#routing-cursor-hint {
    position: fixed;
    bottom: 36px;
    left: 50%;
    transform: translateX(-50%);
    background: var(--color-bg-tooltip);
    color: var(--color-bg);
    padding: 7px 16px;
    border-radius: var(--radius-pill);
    font-size: var(--text-sm);
    font-family: var(--font-ui);
    pointer-events: none;
    z-index: var(--z-tooltip);
    white-space: nowrap;
}

/* =====================================================================
   PROFILE: MOBILE  (<= 768 px)
   Overrides only the components that behave differently on a narrow viewport.
   Everything else inherits the default desktop style.
   ===================================================================== */
@media (max-width: 768px) {

    /* The search field needs >=16px, or iOS Safari zooms the page on focus. */
    .sp-input { font-size: 16px; }

    /* Collapsed panel = a full-width bar at the top. */
    .side-panel {
        left: 12px;
        right: 12px;
        width: auto;
        max-width: none;
    }

    /* Move the map controls (zoom, geolocation, POI, base map, wiki) below the
       search bar so they do not overlap it. */
    .maplibregl-ctrl-top-right { top: 128px; }

    /* Expanded search = results across the whole screen. */
    .side-panel.sp-expanded.sp-mode-search {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        height: 100dvh;
    }

    /* Route planning = a low bottom bar (~1/5 of the screen), so as much of the
       map as possible stays clickable for picking start/destination. Longer
       content (the elevation profile) scrolls inside the body. Only the close
       cross remains in the header. */
    .side-panel.sp-mode-directions {
        top: auto;
        bottom: 0;
        left: 0;
        right: 0;
        width: auto;
        max-height: 50dvh;
        border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    }
    /* The planning bar is a fifth of the screen; the search field and the logo
       both give up their room to the form. */
    .side-panel.sp-mode-directions .sp-search,
    .side-panel.sp-mode-directions .sp-logo { display: none; }
}

/* =====================================================================
   GPS OFF-SCREEN ARROW
   ===================================================================== */
/* Shown while the tracked position is outside the viewport: the map stops
   following after the first fix, so the dot can leave the screen and this is
   what says where it went. Positioned in viewport coordinates by track.js,
   which also supplies the rotation - the triangle points right at 0rad. */
.gps-arrow {
    position: fixed;
    width: 22px;
    height: 22px;
    pointer-events: none;
    z-index: var(--z-gps-arrow);
    background: var(--color-gps);
    clip-path: polygon(100% 50%, 15% 100%, 40% 50%, 15% 0%);
    /* The map underneath is arbitrary, so the shape needs its own contrast
       rather than relying on a background it cannot know. */
    filter: drop-shadow(0 0 1.5px var(--color-halo))
            drop-shadow(0 1px 3px rgba(0, 0, 0, 0.35));
}

.gps-arrow[hidden] { display: none; }

/* =====================================================================
   Panoramax - the street-level photo viewer
   ===================================================================== */
/* A modal over the map rather than a panel beside it: a 360 degree picture is
   something to look around in, and it wants the whole viewport.
   Everything inside is ours now - the viewer that used to live here brought its
   own shadow DOM and its own typography, which is half of why it was replaced. */
.panoramax {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal-overlay);
    display: flex;
    flex-direction: column;
    background: #000;
}

.panoramax[hidden] {
    display: none;
}

.panoramax-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    background: var(--color-bg-translucent);
    border-bottom: 1px solid var(--color-border);
}

/* The sequence steps, which the left and right arrow keys do as well. */
.panoramax-step,
.panoramax-hd,
.panoramax-close {
    flex: none;
    height: 32px;
    min-width: 32px;
    padding: 0 8px;
    border: 0;
    border-radius: var(--radius-sm);
    background: var(--color-bg-soft);
    color: var(--color-text);
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    line-height: 1;
    cursor: pointer;
}

.panoramax-step {
    font-size: var(--text-lg);
}

.panoramax-close {
    font-size: var(--text-lg);
}

.panoramax-step:hover:not(:disabled),
.panoramax-hd:hover:not(:disabled),
.panoramax-close:hover {
    background: var(--color-bg-soft-hover);
}

.panoramax-step:disabled,
.panoramax-hd:disabled {
    opacity: 0.35;
    cursor: default;
}

/* Date, author and licence. It takes the slack in the bar so the buttons stay
   where they are from picture to picture. */
/* The caption - file name, date, producer, licence - in the corner the map's
   own attribution line occupies, rather than in the toolbar. It sits over the
   picture, so it carries its own translucent background instead of relying on
   the bar's; `pointer-events: none` keeps it from eating a drag that starts on
   it, now that a flat picture can be panned. */
.panoramax-meta {
    position: absolute;
    right: 12px;
    bottom: 12px;
    max-width: min(60%, 520px);
    margin: 0;
    padding: 3px 8px;
    overflow: hidden;
    border-radius: var(--radius-sm);
    background: var(--color-bg-translucent);
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
    pointer-events: none;
}

/* With the caption gone from the bar, nothing pushed the right-hand controls
   over - and it cannot be the HD button, which is hidden for a picture with no
   original to fetch. */
#panoramax-next {
    margin-right: auto;
}

.panoramax-link {
    flex: none;
    color: var(--color-text-mid);
    font-size: var(--text-sm);
    text-decoration: none;
}

.panoramax-link:hover {
    text-decoration: underline;
}

/* The stage holds all four layers in one stacking context: the thumbnail
   backdrop, the sphere, the flat image and the minimap. */
.panoramax-stage {
    position: relative;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* The thumbnail while the real picture is in flight. For a panorama it is a
   rectilinear crop rather than a usable texture, so it is blurred - it is
   there to say roughly what is coming, not to be looked at. */
.panoramax-preview {
    position: absolute;
    inset: 0;
    background-color: #000;
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    filter: blur(8px);
    transform: scale(1.06);  /* hides the blur fading out at the edges */
}

.panoramax-canvas {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    cursor: grab;
    touch-action: none;  /* the pointer handlers own the gestures */
}

.panoramax-canvas.is-grabbing {
    cursor: grabbing;
}

.panoramax-flat {
    position: absolute;
    inset: 0;
    display: none;
    width: 100%;
    height: 100%;
    object-fit: contain;
    /* Pan and zoom live on this element (js/pano-flat.js). `touch-action: none`
       is what stops the browser scrolling the page instead of panning the
       picture, and the transform origin is the centre because the zoom-about-
       the-cursor correction in that module assumes it. */
    touch-action: none;
    transform-origin: center;
    will-change: transform;
    user-select: none;
    -webkit-user-select: none;
    cursor: grab;
}

.panoramax-flat.is-zoomed {
    cursor: grabbing;
}

/* A flat picture has nothing to look around in, so the canvas gives way to the
   image and the minimap's cone stops following the view. */
.panoramax.is-flat .panoramax-canvas {
    display: none;
}

.panoramax.is-flat .panoramax-flat {
    display: block;
}

/* Left rather than right, and twice the area on hover - the zoom stays put, so
   what the bigger box buys is more ground rather than a closer look, which is
   what makes it easier to steer by. `min()` keeps the grown box inside a phone
   screen; pano-minimap.js watches the element and resizes the map with it,
   because a MapLibre canvas does not follow its container on its own. */
.panoramax-minimap {
    position: absolute;
    left: 12px;
    bottom: 12px;
    width: 200px;
    height: 200px;
    transition: width 150ms ease, height 150ms ease;
    overflow: hidden;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background: var(--color-bg);
    box-shadow: var(--shadow-lg);
}

.panoramax-minimap:hover,
.panoramax-minimap:focus-within {
    width: min(400px, 70vw);
    height: min(400px, 70vw);
}

/* The view cone, a Marker on the minimap. Its wedge is redrawn when the field
   of view changes and the whole marker is rotated to the bearing, so a drag
   inside the picture costs one CSS rotation and no map work. */
.pano-cone svg {
    display: block;
    width: 48px;
    height: 48px;
    overflow: visible;
}

.pano-cone-beam {
    fill: var(--color-gps);
    fill-opacity: 0.3;
    stroke: var(--color-gps);
    stroke-opacity: 0.5;
    stroke-width: 1;
}

.pano-cone-dot {
    fill: var(--color-gps);
    stroke: var(--color-halo);
    stroke-width: 2;
}

.panoramax-status {
    margin: 0;
    padding: 10px 12px;
    background: var(--color-bg-translucent);
    color: var(--color-text-mid);
    font-size: var(--text-sm);
    text-align: center;
}

.panoramax-status:empty {
    display: none;
}

/* On a phone the minimap would cover a quarter of the picture. */
@media (max-width: 600px) {
    .panoramax-minimap {
        left: 8px;
        bottom: 8px;
        width: 120px;
        height: 120px;
    }

    .panoramax-meta {
        right: 8px;
        bottom: 8px;
        /* Clear of the 120px minimap beside it, whatever the screen width. */
        max-width: calc(100% - 150px);
    }
}
