/* ===========================================================================
   bestes-wetter.de — global styles
   ---------------------------------------------------------------------------
   Lives at wwwroot/css/site.css, loaded once from App.razor.
   Component-scoped styles live next to their .razor file as .razor.css
   (Blazor's scoped-CSS pipeline rewrites class selectors with a unique
   attribute so they can't leak). Only truly global rules belong here:
   typography reset, brand custom-props, .container, link defaults.
   =========================================================================== */

/* ---- Fonts (loaded from Google Fonts in App.razor for simplicity;
        swap for self-hosted .woff2 once the site is live for an extra
        ~50ms LCP win) ----------------------------------------------------- */

/* ---- CSS custom properties (brand tokens) ------------------------------ */
:root {
    /* Brand palette: blueprint-indigo / filament-orange. „Maker / 3D-Druck"-
       Anmutung — die Token-Namen bleiben aus Kompatibilitaet --bw-*, nur die
       Werte sind die eigene 3d-spielplatz-Farbwelt.
         brand      = Filament-Orange (Akzent: Links-Hover, Buttons, Logo)
         brand-dark = dunkles Orange / Blueprint-Indigo-Kontrast fuer Text-Links
       Hinweis: Header/Hero/Footer nutzen ihr eigenes dunkles Indigo (scoped
       CSS bzw. --accent unten ist NICHT die Flaeche, nur der Akzent). */
    --bw-brand:        #f07a2e;    /* filament orange (accent) */
    --bw-brand-dark:   #b4541a;    /* darker orange — text-link colour on light bg */
    --bw-brand-soft:   #fdecdf;    /* tinted backgrounds, accent banners */
    --bw-ink:          #1f2a44;    /* blueprint indigo — dark surfaces (header/hero/footer) */
    --bw-ink-dark:     #161e30;    /* deeper indigo for gradients */
    --bw-earth:        #3b4a6b;    /* secondary accent (steel-blue) */
    --bw-earth-soft:   #e8ecf4;

    /* Foundation-Komponenten (BackToTop) lesen --accent / --accent-dark — auf
       das Filament-Orange mappen, damit der Button im Brand-Akzent erscheint. */
    --accent:          var(--bw-brand);
    --accent-dark:     var(--bw-brand-dark);

    /* Surfaces */
    --bw-bg:           #f7f8fa;    /* page background — cool near-white */
    --bw-bg-muted:     #eceff3;    /* card / footer / muted blocks */
    --bw-surface:      #ffffff;    /* article cards, drawers */

    /* Text */
    --bw-fg:           #1b1e26;    /* near-black, slight cool */
    --bw-fg-soft:      #444a55;
    --bw-fg-muted:     #6b7280;

    /* Lines */
    --bw-border:       #d5dae1;
    --bw-radius:       4px;
    --bw-radius-lg:    8px;

    /* Typography */
    --bw-font-sans:    'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --bw-font-serif:   'Source Serif 4', 'Source Serif Pro', Georgia, 'Times New Roman', serif;
    --bw-font-mono:    ui-monospace, SFMono-Regular, 'Cascadia Mono', Consolas, monospace;

    /* Layout */
    --bw-container:    1180px;
}

/* ---- Reset ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    /* html ist der ultimative Scrolling-Container. body-overflow:clip allein
       reicht auf Android-Chrome offenbar nicht, um Pinch-Zoom-Out zu
       verhindern — der Browser schaut auf die html-Ebene und sieht dort,
       dass irgendwo ein Sub-Pixel ueberhaengt. overflow-x: clip auch hier
       macht das wasserdicht. */
    overflow-x: clip;
}

body {
    margin: 0;
    /* 3 px Sicherheitsabstand zur Device-Kante. Sorgt dafuer, dass auf
       schmalen Phones (Pixel 9 XL: 412 px CSS-px) der Inhalt nicht ganz
       buendig links/rechts klebt. Full-bleed-Baender (Site-Header,
       Site-Footer, Home-Hero, Article-Hero) holen sich diese 6 px ueber
       margin-inline: -3px wieder zurueck, damit ihre Hintergrundfarbe
       weiterhin bis ans Device-Edge laeuft. Content im .container und
       in den Reading-Columns bleibt eingerueckt. */
    padding-inline: 3px;
    /* Mobile-Anti-Pinch-Out: ohne diese Regel laesst sich die Seite auf
       Android-Browsern unter 100 % Zoom ziehen ("Seite schmaler als das
       Browserfenster"). Ursache ist meist ein einzelnes Element, das ein
       paar Sub-Pixel ueber den Viewport hinausragt — Browser interpretiert
       das als "Page ist breiter als Viewport" und erlaubt zoom-out. Clip
       an der Padding-Edge schneidet jeden solchen Ueberlauf weg; die
       full-bleed-Elemente mit margin-inline:-3px reichen genau bis zur
       Padding-Edge und bleiben unbeschnitten. clip ist zu hidden zu
       bevorzugen: erzeugt keinen Scroll-Container, stoert nicht mit
       position:sticky-Elementen. (Chrome 90+/Edge 90+/Firefox 81+/Safari 16+) */
    overflow-x: clip;
    font-family: var(--bw-font-sans);
    font-size: 17px;
    line-height: 1.5;
    color: var(--bw-fg);
    background: var(--bw-bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--bw-font-serif);
    color: var(--bw-fg);
    line-height: 1.2;
    letter-spacing: -.005em;
}

p { margin: 0 0 1em; }

a {
    color: var(--bw-brand-dark);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    /* Sanfter Farbwechsel statt hartem Sprung beim Hover — kaum merklich,
       aber sitewide spuerbar. Komponenten mit eigener transition (Cards,
       Nav, Footer) ueberschreiben das mit ihrer spezifischeren Regel. */
    transition: color .15s ease, text-decoration-color .15s ease;
}
a:hover { color: var(--bw-brand); }

img { max-width: 100%; height: auto; display: block; }

/* Such-Highlight: search.js setzt <mark class="search-mark"> um jedes
   Vorkommen der Such-Tokens, wenn auf der Detail-Page ein
   #:~:text=…-Hash steht. Dezent gelb mit Brand-Tinge — kraeftig genug
   um sofort ins Auge zu fallen, aber nicht so penetrant dass die
   ganze Seite gelb aussieht. */
mark.search-mark {
    background: #fff3a8;
    color: inherit;
    padding: 0 .1em;
    border-radius: 2px;
    box-decoration-break: clone;
    -webkit-box-decoration-break: clone;
}

/* <picture> wrappt das <img> in Foundation's ResponsiveImage. Browser-
   Default ist display: inline — das macht aus dem Wrapper eine line-box,
   die auf die intrinsic-Bildbreite des img schrumpft. Damit sprengt ein
   1920-px-Hero-Image den hero-Container, und alle position:absolute-
   Kinder (.article__hero-overlay mit Title + Teaser) erben die zu grosse
   Breite und laufen ueber den Viewport. Block-display + max-width: 100%
   ist der saubere Fix — picture ist semantisch ein Layout-Wrapper,
   nicht inline. */
picture { display: block; max-width: 100%; }

blockquote {
    margin: 1.5em 0;
    padding: .8em 1.2em;
    border-left: 3px solid var(--bw-brand);
    background: var(--bw-brand-soft);
    color: var(--bw-fg-soft);
    font-style: italic;
}

code, pre, kbd, samp {
    font-family: var(--bw-font-mono);
    font-size: .92em;
}
pre {
    background: var(--bw-bg-muted);
    border: 1px solid var(--bw-border);
    border-radius: var(--bw-radius);
    padding: .9em 1.1em;
    overflow-x: auto;
}

hr {
    border: 0;
    border-top: 1px solid var(--bw-border);
    margin: 2em 0;
}

/* ---- Layout helpers ----------------------------------------------------- */

.container {
    max-width: var(--bw-container);
    margin: 0 auto;
    padding: 0 1.25rem;
}

/* ---- Focus styles ------------------------------------------------------- */

:focus-visible {
    outline: 2px solid var(--bw-brand);
    outline-offset: 2px;
    border-radius: 2px;
}

/* Programmatically-focused, non-interactive targets — the <h1> that
   FocusOnNavigate focuses after every navigation, skip-link anchors, etc.
   carry tabindex="-1". The focus is purely for screen-reader / keyboard
   landing; a visible ring on a huge hero headline just looks like a stray
   border. Interactive controls (links, buttons, inputs) have no tabindex
   or a non-negative one, so they keep their :focus-visible ring. */
[tabindex="-1"]:focus { outline: none; }

/* ---- Collapsible code blocks --------------------------------------------
   ArticleRenderer.CollapsibleCodeBlockRenderer wraps long code blocks
   (> 16 lines) in .code-collapse with a hidden checkbox + label toggle. The
   markup goes into the article body via MarkupString, so these rules must be
   global (not scoped). Reiner CSS-Toggle — kein JS, ueberlebt Enhanced Nav,
   voller Code bleibt im DOM (Copy/SEO). ----------------------------------- */

.code-collapse { position: relative; }

/* Versteckte Steuer-Checkbox — nur das <label> ist sichtbar/klickbar. */
.code-collapse__toggle {
    position: absolute;
    width: 1px; height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Eingeklappt: Hoehe auf die sichtbaren Zeilen begrenzen. --collapsed-lines
   kommt inline vom Renderer; das <pre> hat ~1.45 lh + Padding, daher rechnen
   wir grosszuegig in em statt lh (lh-Unit-Support ist juenger). */
.code-collapse__body {
    position: relative;
    overflow: hidden;
    max-height: calc(var(--collapsed-lines, 10) * 1.5em + 2em);
    transition: max-height .25s ease;
}
.code-collapse__body > pre { margin: 0; }

/* Weicher Fade am unteren Rand des eingeklappten Blocks. */
.code-collapse__fade {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 3em;
    background: linear-gradient(to bottom, rgba(236, 239, 243, 0), var(--bw-bg-muted));
    pointer-events: none;
}

/* Toggle-Button (das Label). Sitzt unter dem Block, dezent im Akzent. */
.code-collapse__btn {
    display: inline-block;
    margin: .5rem 0 0;
    font-size: .85rem;
    font-weight: 600;
    color: var(--bw-brand-dark);
    cursor: pointer;
    user-select: none;
}
.code-collapse__btn:hover { text-decoration: underline; }
.code-collapse__count { font-weight: 400; color: var(--bw-fg-muted); }

/* Default (eingeklappt): „Weniger" verstecken. */
.code-collapse__less { display: none; }

/* Aufgeklappt (Checkbox checked): volle Hoehe, Fade weg, Labels tauschen. */
.code-collapse__toggle:checked ~ .code-collapse__body {
    max-height: 200em;   /* grosszuegige Obergrenze; reicht fuer jeden Block */
}
.code-collapse__toggle:checked ~ .code-collapse__body .code-collapse__fade {
    display: none;
}
.code-collapse__toggle:checked ~ .code-collapse__btn .code-collapse__more { display: none; }
.code-collapse__toggle:checked ~ .code-collapse__btn .code-collapse__less { display: inline; }

/* Tastatur-Fokus: Da die Checkbox visuell versteckt ist, den Ring aufs Label
   ziehen, wenn die Checkbox :focus-visible hat. */
.code-collapse__toggle:focus-visible ~ .code-collapse__btn {
    outline: 2px solid var(--bw-brand);
    outline-offset: 2px;
    border-radius: 2px;
}

/* ---- Gallery + lightbox -------------------------------------------------
   The :::gallery shortcode renders into an article body via MarkupString,
   so its markup is NOT under the scoped-CSS pipeline — these rules have to
   be global. Same for the lightbox overlay, which lightbox.js appends
   straight to <body>. ----------------------------------------------------- */

.bw-gallery__btn {
    display: block;
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
}
.bw-gallery__btn img {
    width: 100%;
    height: auto;
    /* Drop the 4/3 cover crop — Pixel/iPhone shots come in landscape AND
       portrait, and forcing 4/3 chopped portraits' top+bottom off (~33%
       loss). Now: keep the photo's native aspect ratio, just constrain
       max-height so portraits don't tower over neighbouring landscapes
       in the grid. Grid rows will have varying heights — better than
       cropped pictures. */
    max-height: 320px;
    object-fit: contain;
    border-radius: var(--bw-radius);
    transition: transform .15s ease-out;
    background: var(--bw-bg-muted);
}
.bw-gallery__btn:hover img { transform: scale(1.02); }
/* min-width:0 laesst das Item im Grid unter seine intrinsische Content-
   Breite schrumpfen. Ohne das zieht eine breite figcaption (width:max-content)
   das `auto`-Minimum der 1fr-Spur auf ihre Einzeiler-Breite hoch und das Grid
   laeuft auf schmalen Viewports rechts ueber. */
.bw-gallery__item { margin: 0; min-width: 0; }
.bw-gallery__item figcaption {
    margin-top: .3rem;
    font-size: .78rem;
    color: var(--bw-fg-muted);
    font-family: var(--bw-font-mono);
    line-height: 1.4;
    /* Caption ist als Block so breit wie die längste Zeile (max-content),
       per margin:auto im Container zentriert, der Text selbst läuft links.
       Damit beginnen Folgezeilen bei mehrzeiligen Captions linksbündig
       unter der ersten Zeile — sieht ruhiger aus als reine Zentrierung,
       die jede Zeile separat mittig setzt. */
    width: max-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
    text-align: left;
}

.bw-lightbox {
    position: fixed;
    inset: 0;
    /* ueber dem Map-Vollbild (z-index 9999): ein Foto-Marker im Karten-
       Vollbild oeffnet die Lightbox, die als Modal obenauf liegen muss. */
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem;
    background: rgba(15, 18, 14, .92);
}
.bw-lightbox[hidden] { display: none; }
.bw-lightbox__figure {
    margin: 0;
    max-width: 95vw;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .6rem;
}
/* Rahmen [‹ Bild ›]: zentriert, in der Breite GEDECKELT (min(95vw,1100px)).
   Alle Controls sind absolut an diesen Rahmen geheftet (Pfeile an den Seiten,
   ×/Pin an den oberen Ecken) — ihre Position haengt nur vom Fenster ab, NICHT
   vom aktuellen Bild. Dadurch springen sie beim Hoch-/Querformat-Wechsel nicht,
   und der Deckel haelt sie in breiten Fenstern vom Bildschirmrand weg. Das Bild
   ist im Rahmen zentriert (justify-content:center). */
.bw-lightbox__row {
    position: relative;
    width: min(95vw, 1100px);
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Stage = exakt die Bildgroesse; Schliessen/Pin liegen absolut DARUEBER (also
   ausserhalb), kein Overlap aufs Bild. min-width:0 erlaubt das Schrumpfen. */
.bw-lightbox__stage {
    position: relative;
    display: flex;
    min-width: 0;
}
.bw-lightbox__img {
    display: block;
    /* Auf die Rahmen-Innenbreite begrenzen (Rahmen minus beide Pfeilzonen je
       ~3.5rem): das Bild bleibt zentriert zwischen den Pfeilen und ueberlappt
       sie nie. Querformat fuellt den Rahmen, Hochformat bleibt mittig mit Luft. */
    max-width: calc(min(95vw, 1100px) - 7rem);
    /* etwas Luft oben fuer Schliessen/Pin ueber dem Bild lassen */
    max-height: 78vh;
    object-fit: contain;
    border-radius: var(--bw-radius);
}
.bw-lightbox__cap {
    color: #e8e2d2;
    font-family: var(--bw-font-mono);
    font-size: .85rem;
    text-align: center;
}
.bw-lightbox__close {
    position: absolute;
    /* ueber der oberen rechten Rahmenecke — ausserhalb des Bildes */
    bottom: calc(100% + .45rem);
    right: 0;
    z-index: 2;
    width: 2.4rem;
    height: 2.4rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.8rem;
    line-height: 1;
    color: #fff;
    /* dunkel-transluzent statt hell — bleibt ueber hellen Bildstellen lesbar. */
    background: rgba(15, 18, 14, .55);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.bw-lightbox__close:hover { background: rgba(15, 18, 14, .8); }

/* "Auf der Karte zeigen" — sitzt oben links, gespiegelt zum Close-Button.
   Per [hidden] ausgeblendet, wenn das aktuelle Bild keinen Karten-Marker hat. */
.bw-lightbox__locate {
    position: absolute;
    /* ueber der oberen linken Rahmenecke — ausserhalb des Bildes */
    bottom: calc(100% + .45rem);
    left: 0;
    z-index: 2;
    width: 2.4rem;
    height: 2.4rem;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    background: rgba(15, 18, 14, .55);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.bw-lightbox__locate[hidden] { display: none; }
.bw-lightbox__locate:hover { background: rgba(15, 18, 14, .8); }

/* Positions-Zaehler ("3 / 8") im Karten-Modus — eigene Zeile zwischen Bild und
   Caption (vom figure per align-items:center zentriert), ueberdeckt das Bild
   nicht. */
.bw-lightbox__counter {
    padding: .15rem .65rem;
    font-size: .78rem;
    font-variant-numeric: tabular-nums;
    color: #e8e2d2;
    background: rgba(255, 255, 255, .12);
    border-radius: 999px;
    white-space: nowrap;
}
.bw-lightbox__counter[hidden] { display: none; }

/* Mapbar (Karten-Modus, Stufe 3): Mini-Route + Positions-Zaehler in einer Zeile
   UNTER dem Bild — ueberdeckt das Bild nicht (analog zum Zaehler). Die Mini-Route
   ist ein leichtgewichtiges SVG (kein zweites Leaflet): die Etappen-Route als
   Linie, ein Punkt pro Foto, der aktuelle pulsiert in Sonnen-Gold, jeder Punkt
   klickbar zum Springen. */
.bw-lightbox__mapbar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .7rem;
}
.bw-lightbox__mapbar[hidden] { display: none; }
.bw-lightbox__route { line-height: 0; }
.bw-lightbox__route-svg {
    display: block;
    width: 150px;
    height: 64px;
    padding: 3px 6px;
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 8px;
}
.bw-lightbox__route-line {
    fill: none;
    stroke: rgba(230, 239, 232, .5);   /* --bw-brand-soft, gedaempft auf dunklem Grund */
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
    pointer-events: none;
}
.bw-lightbox__dot {
    fill: #cdd9cd;
    stroke: rgba(0, 0, 0, .35);
    stroke-width: .5;
    cursor: pointer;
    transition: fill .15s ease;
}
.bw-lightbox__dot:hover { fill: #ffffff; }
.bw-lightbox__dot.is-current {
    fill: #f0b54a;                     /* Sonnen-Gold — der aktuelle Standort */
    stroke: rgba(0, 0, 0, .25);
    transform-box: fill-box;
    transform-origin: center;
    animation: bw-dot-pulse 1.6s ease-in-out infinite;
}
@keyframes bw-dot-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.5); }
}
@media (prefers-reduced-motion: reduce) {
    .bw-lightbox__dot.is-current { animation: none; }
}

/* Prev/Next-Pfeile innerhalb der Lightbox. Bei Single-Image-Lightboxes
   blendet das JS sie per [hidden] aus; am Anfang/Ende werden sie via
   [disabled] visuell abgesenkt. Größere Touch-Targets via padding +
   transparenter Border, damit man auch auf Mobile gut trifft, ohne
   dass die sichtbare Fläche aufdringlich wird. */
/* Pfeile absolut an den Rahmen-Seiten (vertikal mittig), NICHT als Flex-
   Geschwister — so haengt ihre x-Position am Rahmen, nicht am aktuellen Bild,
   und sie springen beim Hoch-/Querformat-Wechsel nicht. */
.bw-lightbox__nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 3rem;
    height: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.2rem;
    line-height: 1;
    color: #fff;
    background: rgba(15, 18, 14, .55);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
}
.bw-lightbox__nav--prev { left: 0; }
.bw-lightbox__nav--next { right: 0; }
.bw-lightbox__nav:hover { background: rgba(15, 18, 14, .8); }
.bw-lightbox__nav[disabled] {
    opacity: .25;
    cursor: default;
}
.bw-lightbox__nav[hidden] { display: none; }

/* Auf schmalen Bildschirmen Pfeile etwas kompakter, damit sie nicht den
   Bildinhalt überdecken. */
@media (max-width: 480px) {
    .bw-lightbox__nav {
        width: 2.4rem;
        height: 2.4rem;
        font-size: 1.6rem;
    }
    .bw-lightbox__nav--prev { left: .4rem; }
    .bw-lightbox__nav--next { right: .4rem; }
}

/* ---- Cookie-Consent (Foundation-Komponente) -------------------------------
   Die Komponente wird in App.razor außerhalb von <Routes> gerendert und
   liegt damit ausserhalb der scoped-CSS-Welt. Daher hier global. Klassen-
   namen sind die der Foundation-Komponente — bitte beim Update auf neue
   Foundation-Versionen prüfen. ---------------------------------------------- */

.cookie-consent {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    background: var(--bw-fg);
    color: #f4f1e8;
    z-index: 80;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, .18);
}
.cookie-consent__inner {
    display: flex;
    gap: 1.5rem;
    align-items: center;
    padding: 1rem 0;
    flex-wrap: wrap;
}
.cookie-consent__copy { flex: 1; min-width: 280px; }
.cookie-consent__heading {
    font-family: var(--bw-font-serif);
    font-size: 1.1rem;
    margin: 0 0 .3rem;
    color: #fff;
}
.cookie-consent__body { margin: 0; font-size: .9rem; line-height: 1.45; }
.cookie-consent__body a { color: #d0e7d6; }
.cookie-consent__body a:hover { color: #fff; }

.cookie-consent__actions {
    display: flex;
    gap: .6rem;
    flex-shrink: 0;
}
.cookie-consent__btn {
    font: inherit;
    padding: .55rem 1.1rem;
    border-radius: var(--bw-radius);
    cursor: pointer;
    border: 1px solid transparent;
}
.cookie-consent__btn--accept {
    background: var(--bw-brand);
    color: #fff;
}
.cookie-consent__btn--accept:hover { background: var(--bw-brand-dark); }
.cookie-consent__btn--reject {
    background: transparent;
    color: #f4f1e8;
    border-color: rgba(255, 255, 255, .4);
}
.cookie-consent__btn--reject:hover { border-color: #fff; }

/* ---- Tour map (Leaflet canvas) ---------------------------------------------
   Shared between the single-tour <TourMap> and the multi-day <TourOverview>:
   both render canvases with .tour-map__canvas, so scoped CSS in one of the
   .razor.css files would only style one of them. The legend below the map
   is injected at runtime by tour-map.js, also without a scope attribute.
   --------------------------------------------------------------------------- */

/* `isolation: isolate` creates a new stacking context, scoping Leaflet's
   own z-indices (200–1000 across its panes and controls) to inside the
   map. Without it, the tile-pane and overlay-pane would punch through the
   sticky SiteHeader (z-index 50) on scroll. */
.tour-map {
    margin: 1.5rem 0;
    position: relative;
    isolation: isolate;
}

/* `contain: paint` (nicht nur overflow) ist hier der eigentliche Schutz:
   Leaflet positioniert seine Panes per transform und seine Overlay-SVG-
   Geometrie kann weit ueber die Box hinausragen. Solange Tiles als
   Hintergrund liegen, faellt das nicht auf; fehlen sie (Tile-CDN weg ODER
   leaflet.css vom enhanced-navigation-head-diff entfernt → Panes verlieren
   position:absolute) und der Nutzer zoomt per Browser, kann der GPX-SVG-
   Layer aus einem reinen overflow:hidden ausbrechen und quer ueber den
   Article-Body rendern. Paint-Containment clippt den ganzen Subtree hart an
   die Box — auch transformierte/abs-positionierte Descendants — und laesst
   sich, anders als overflow, nicht ueber einen hochgereichten Containing-
   Block umgehen. overflow:clip als harter Begleit-Clip (kein Scroll-
   Container). Greift auch im Fullscreen-Variant: contain wirkt auf
   Descendants, nicht auf das position:fixed des Elements selbst. */
.tour-map__canvas {
    width: 100%;
    height: 360px;
    border-radius: var(--bw-radius-lg);
    overflow: clip;
    contain: paint;
    background: var(--bw-bg-muted);
    border: 1px solid var(--bw-border);
    position: relative;
}

/* Smaller variant used inside the :::leg block, so the per-day map doesn't
   dominate the per-day card. Still tall enough to read on mobile (where it
   becomes effectively full-width). */
.tour-map__canvas--small { height: 240px; }

/* Auto-loading variant: empty canvas before tour-map.js takes over. A subtle
   pulsing gradient acts as a loading hint without needing a spinner. Leaflet
   replaces the background with tile imagery within a few hundred ms — on
   fast networks the pulse barely shows; on slow ones it stops looking like
   a broken empty box. */
.tour-map__canvas--auto {
    background:
        linear-gradient(135deg, var(--bw-bg-muted) 0%, #ece6d8 50%, var(--bw-bg-muted) 100%);
    background-size: 200% 200%;
    animation: tour-map-pulse 2.4s ease-in-out infinite;
}
.tour-map__canvas--auto[data-map-ready="1"] {
    animation: none;
    /* NICHT transparent: sonst scheint bei fehlenden Tiles (CDN-Haenger oder
       beim Zoom-Pan in noch ungeladene Bereiche) der Seitenhintergrund durch.
       Solider Muted-Ton als Tile-Unterlage — von geladenen Kacheln eh verdeckt. */
    background: var(--bw-bg-muted);
}

@keyframes tour-map-pulse {
    0%, 100% { background-position: 0%   50%; }
    50%      { background-position: 100% 50%; }
}

/* Reset-Button (top-right Leaflet-Control). Sitzt im äußeren <div
   class="leaflet-bar leaflet-control"> wie Leaflets eigener Zoom-Control,
   bekommt also weißen Hintergrund + Border + Hover-Style aus leaflet.css.
   Wir tunen hier nur das Crosshair-Glyph: leicht größer als die ±-Zoom-
   Buttons, damit es lesbar bleibt, und farblich abgesenkt, damit es im
   Ruhezustand nicht aufdringt. `a.tour-map__reset` schlägt die Default-
   Link-Farbe (rgb(0,120,168) auf <a>). Explizite 30×30: Leaflet stylt
   .leaflet-bar a nur per Selektoren auf .leaflet-touch — auf Desktop
   bleibt das <a> sonst auf text-width zusammen (12 px Crosshair-Glyph). */
.leaflet-bar a.tour-map__reset,
.leaflet-bar a.tour-map__fullscreen {
    width: 30px;
    height: 30px;
    font-size: 18px;
    line-height: 30px;
    text-align: center;
    color: var(--bw-fg-soft);
}
.leaflet-bar a.tour-map__reset:hover,
.leaflet-bar a.tour-map__fullscreen:hover {
    color: var(--bw-fg);
}
/* Reset-Button traegt jetzt ein Inline-SVG-Crosshair (statt Glyph) — Flex
   zentriert es zuverlaessig im 30×30-Button (line-height-Zentrierung greift
   nur fuer Text). Fullscreen bleibt beim Glyph. */
.leaflet-bar a.tour-map__reset {
    display: flex;
    align-items: center;
    justify-content: center;
}
.tour-map__reset svg { display: block; }

/* Pseudo-Vollbild fuer die Karte: position:fixed deckt den ganzen
   Viewport ab. 100dvh statt 100vh respektiert die mobile Browser-
   Adressleiste (vh ist auf iOS zu hoch, dvh schneidet korrekt).
   Wichtig die !importants — .tour-map__canvas hat sonst height:360px
   (oder --small 240px) hart gesetzt. z-index ueber dem Sticky-Header
   (z-index:50) und ueber dem Cookie-Banner (z-index:80). */
.tour-map__canvas--fullscreen {
    position: fixed !important;
    inset: 0 !important;
    width: 100vw !important;
    height: 100dvh !important;
    max-height: none !important;
    z-index: 9999 !important;
    border-radius: 0 !important;
    border: none !important;
}
/* Parent .tour-map hat default isolation:isolate (siehe oben — schuetzt
   die Tile-Pane vor dem Sticky-Header im Normal-Mode). Das schafft aber
   einen Stacking-Context, in dem das z-index:9999 des canvas eingesperrt
   ist. Im Fullscreen heben wir den ganzen .tour-map-Block per eigenem
   z-index ueber den Header (position:relative ist eh schon da). */
.tour-map--fullscreen {
    z-index: 9999;
}
/* Body-Scroll-Lock waehrend Fullscreen aktiv ist (von tour-map.js
   gesetzt). overflow:hidden statt clip damit ein darunter laufendes
   sticky-Element nicht aufschaut. */
body.bw-fullscreen-active { overflow: hidden; }
/* …und das html-Element gleich mit. body{overflow:hidden} allein begrenzt den
   Body NICHT auf Viewport-Hoehe — er bleibt so hoch wie der ganze Artikel, und
   <html> scrollt ihn weiter. Ergebnis war ein funktionsloser vertikaler
   Scrollbar im Vollbild. :has() ist breit unterstuetzt (Chrome/Edge/Safari/
   Firefox 2023+); faellt es aus, bleibt nur der harmlose Alt-Zustand. */
html:has(body.bw-fullscreen-active) { overflow: hidden; }

/* ---- Multi-leg tour blocks -------------------------------------------------
   :::leg shortcodes render straight into the article body via MarkupString
   and the tour-map.js legend is appended at runtime — both are outside the
   scoped-CSS world, so the styles live here. ------------------------------ */

.bw-leg {
    margin: .8rem 0 1.8rem;
    padding: .8rem 1rem 1rem;
    background: var(--bw-bg-muted);
    border-left: 3px solid var(--bw-brand);
    border-radius: var(--bw-radius);
    /* Fragment-Links aus der Tour-Uebersicht springen auf diesen Block.
       Direkt davor steht im Markdown-Body eine <h3> mit dem Etappen-
       Namen ("### Tag 5 — ..."); die id sitzt aber technisch auf dem
       Block selbst, nicht auf der Heading. Ohne grosszuegigen Scroll-
       Margin wuerde die <h3> beim Sprung oberhalb des Viewports
       landen und der Nutzer haette nur noch den Body ohne Titel.
       160px deckt sticky SiteHeader (~70px) + h3 mit Default-Margin
       (~90px) ab. */
    scroll-margin-top: 160px;
}
.bw-leg__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem 1.4rem;
}
.bw-leg__stats {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem 1rem;
    font-family: var(--bw-font-mono);
    font-size: .95rem;
    color: var(--bw-fg);
    flex: 1 1 auto;
}
.bw-leg__stat { white-space: nowrap; }
.bw-leg__map { margin: .9rem 0 0; }

/* Per-etappe lead photo — the one with lead="1" for this leg, hoisted out
   of the gallery and shown big at the top of the :::leg block. Clicks open
   in the same lightbox as the gallery items. */
.bw-leg__lead {
    margin: 0 -1rem 1rem;   /* pull to the edge of the .bw-leg padding */
}
.bw-leg__lead .bw-gallery__btn { cursor: zoom-in; }
.bw-leg__lead img {
    /* `object-fit: contain` so portrait shots aren't chopped — landscapes
       still get the full edge-to-edge look, portraits are centred with
       letterboxing on either side. */
    width: 100%;
    height: auto;
    max-height: 480px;
    object-fit: contain;
    border-radius: 0;
    display: block;
    background: var(--bw-bg-muted);
}
.bw-leg__lead figcaption {
    padding: .4rem 1rem 0;
    font-family: var(--bw-font-mono);
    font-size: .82rem;
    color: var(--bw-fg-muted);
    line-height: 1.4;
    /* Siehe Kommentar bei .bw-gallery__item figcaption — gleiches Schema:
       zentriert als Box, linksbündig im Text. */
    width: max-content;
    max-width: 100%;
    margin-left: auto;
    margin-right: auto;
    text-align: left;
}

/* Per-leg photo gallery — same .bw-gallery markup as the :::gallery
   shortcode, just slightly tighter spacing and a smaller grid so the
   photos look like a per-day strip, not a hero block. */
.bw-leg__gallery {
    margin-top: .9rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: .5rem;
}
.bw-leg__gallery .bw-gallery__item { margin: 0; }
/* Don't constrain height further than the global .bw-gallery__btn img rule
   (320px) — the earlier 220-px cap stuffed a 2:3 portrait into a strip-
   thin letterbox. Portraits now stand a little taller than their landscape
   neighbours in the grid, but they're actually readable. */

/* Mobile: Galerie auf 1 Spalte zwingen. Auf groesseren Phones (iPhone Pro
   Max & Co., ~430 px Viewport) passen 2× 160 px + Gap rechnerisch knapp ins
   .bw-leg, aber 2 Bilder + Captions nebeneinander sind dort kaum noch
   lesbar. Die Schwelle 640 px sitzt grosszuegig oberhalb aller Phone-
   Breiten und unterhalb der ueblichen Tablet-Portrait-Werte (768 px). */
@media (max-width: 640px) {
    .bw-leg__gallery { grid-template-columns: minmax(0, 1fr); }
}

.bw-leg__download {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .35rem .75rem;
    background: var(--bw-surface);
    border: 1px solid var(--bw-border);
    border-radius: var(--bw-radius);
    color: var(--bw-brand-dark);
    text-decoration: none;
    font-size: .9rem;
    transition: background .12s ease-out;
}
.bw-leg__download:hover {
    background: var(--bw-brand-soft);
    color: var(--bw-brand-dark);
}
.bw-leg__download-icon { font-size: 1.1rem; line-height: 1; }

/* Gesamt-Tour-Download (TourOverview) + Einzeltour-Download (TourMap):
   nutzen denselben .bw-leg__download-Knopf, brauchen nur etwas Abstand
   zur darueberliegenden Karte. */
.tour-overview__download,
.tour-map__download { margin: .9rem 0 0; }

/* Foto-Thumbnail-Marker auf den Leg-Karten (tour-map.js, EXIF-Aufnahmeorte).
   divIcon-Wrapper transparent halten; das runde Bild traegt Ring + Schatten. */
.tour-map__photo-marker { background: none; border: 0; }
.tour-map__photo-marker img {
    display: block;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid #fff;
    box-shadow: 0 1px 5px rgba(0, 0, 0, .45);
    cursor: pointer;
    transition: transform .12s ease-out;
}
/* Marker-Hover / Reverse-Highlight: nur dezent vergroessern + Marken-Ring.
   Die eigentliche Vorschau uebernimmt die schwebende Karte (.tour-photo-preview),
   die tour-map.js bei Marker-Hover ueber dem Canvas einblendet. */
.tour-map__photo-marker:hover img,
.tour-map__photo-marker--active img {
    transform: scale(1.3);
    border-color: var(--bw-brand);
}

/* Schwebende Vollformat-Vorschau beim Marker-Hover. Liegt auf <body> (NICHT im
   Karten-Canvas) — so beschneidet das overflow:clip der Karte sie nie. Zeigt das
   ganze Bild (uncropped, anders als der runde Marker) + Caption. tour-map.js
   positioniert + toggelt .is-visible; pointer-events:none, damit sie nie stoert. */
.tour-photo-preview {
    position: fixed;
    z-index: 10001;            /* ueber dem Karten-Vollbild (9999); Lightbox (10000) koexistiert nie */
    pointer-events: none;
    opacity: 0;
    transform: scale(.96);
    transform-origin: bottom center;   /* poppt sanft aus dem Marker auf */
    transition: opacity .18s ease, transform .18s ease;
    background: var(--bw-surface);
    border: 1px solid var(--bw-border);
    border-radius: var(--bw-radius);
    box-shadow: 0 6px 20px rgba(0, 0, 0, .3);
    overflow: hidden;
    width: max-content;
    max-width: 190px;
}
.tour-photo-preview.is-visible {
    opacity: 1;
    transform: scale(1);
}
.tour-photo-preview img {
    display: block;
    max-width: 190px;
    max-height: 150px;
    width: auto;
    height: auto;
}
.tour-photo-preview__cap {
    padding: .3rem .5rem .4rem;
    font-size: .72rem;
    line-height: 1.3;
    color: var(--bw-fg-soft);
}

@media (prefers-reduced-motion: reduce) {
    .tour-photo-preview { transition: opacity .15s ease; transform: none; }
    .tour-photo-preview.is-visible { transform: none; }
}

/* Huepfen — die Geste fuer "auf der Karte zeigen" (Lightbox -> Karte).
   Animiert NUR das <img>, nicht den Leaflet-positionierten Container. */
@keyframes tour-map-marker-bounce {
    0%, 100% { transform: translateY(0); }
    25%      { transform: translateY(-15px) scale(1.06); }
    45%      { transform: translateY(0); }
    65%      { transform: translateY(-8px); }
    80%      { transform: translateY(0); }
}
.tour-map__photo-marker--bounce img {
    animation: tour-map-marker-bounce .75s ease;
}

/* Reverse-Link: Marker-Hover hebt das zugehoerige Galerie-Thumbnail hervor. */
.bw-gallery__btn.is-located {
    outline: 3px solid var(--bw-brand);
    outline-offset: 2px;
    border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .tour-map__photo-marker--bounce img { animation: none; }
}

/* Legende + Jump-Liste der Etappen in einem — sitzt direkt unter der
   Übersichtskarte. Früher war hier zusätzlich eine reine Map-Legende per
   tour-map.js gerendert; entfernt, weil dieselben Titel ein Pixel weiter
   unten in dieser <ol> nochmal vorkamen — jetzt ein einziger Block, Farbe
   matcht den Polyline-Farben aus tour-map.js, der Titel ist klickbar als
   Sprungmarke zur entsprechenden :::leg-Sektion im Body.

   Grid mit auto-fit/minmax: bei 17 Etappen + breiter Spalte (Body max
   740 px) ergibt das zwei bis drei Spalten je nach Viewport, schmale
   Bildschirme bekommen automatisch eine — dadurch deutlich weniger
   vertikaler Platz als die alte flex-wrap-Liste mit je einer Zeile pro
   langem Titel. */
.tour-overview__legs {
    list-style: none;
    margin: .6rem 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: .2rem .8rem;
    font-size: .78rem;
    color: var(--bw-fg-soft);
}
/* Bis Tablet-Breite (800 px) eine Spalte. Sonst presst die 2-Spalten-Grid
   lange Etappentitel ("Vatikan — Palast und Gärten (private Führung)",
   "Etappe Ib — Corry Hully → Sourlies") in zu schmale Spalten — und wo
   default-Word-Wrap am Em-Dash/Pfeil zaudert, laeuft der Text aus der
   Spalte rechts ueber den Container hinaus. Schwelle 800 px deckt
   Browser-Fenster bis Tablet-Portrait ab; ab 800 px ist genug Breite fuer
   2 Spalten ohne Quetschen. */
@media (max-width: 800px) {
    .tour-overview__legs {
        grid-template-columns: 1fr;
    }
}
.tour-overview__leg {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    margin: 0;
    line-height: 1.4;
}
.tour-overview__leg-swatch {
    display: inline-block;
    width: .9rem;
    height: .9rem;
    border-radius: 2px;
    border: 1px solid rgba(0, 0, 0, .15);
    flex: 0 0 auto;
}
.tour-overview__leg-link {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted transparent;
    transition: border-color .15s ease, color .15s ease;
}
.tour-overview__leg-link:hover,
.tour-overview__leg-link:focus-visible {
    color: var(--bw-fg);
    border-bottom-color: currentColor;
    outline: none;
}

/* === Form fields (global) ===========================================
 * Genutzt vom Foundation-<ContactForm>. Vorher in
 * Components/Shared/ContactForm.razor.css scoped; mit dem Umzug auf
 * Foundation muessen die Input/Label/Button-Regeln site-weit verfuegbar
 * sein (Foundation-Komponente restyled keine Inputs).
 * ==================================================================== */

.form-field {
    display:        flex;
    flex-direction: column;
    gap:            0.3rem;
    margin-bottom:  0;
}
.form-field > span {
    font-size:   0.85rem;
    color:       var(--bw-fg-soft);
    font-weight: 600;
}
.form-field.is-required > span::after {
    content:     '*';
    color:       var(--bw-earth);
    margin-left: 0.2em;
}
.form-field input,
.form-field textarea {
    font:          inherit;
    padding:       0.55rem 0.7rem;
    border:        1px solid var(--bw-border);
    border-radius: var(--bw-radius);
    background:    #fff;
    color:         var(--bw-fg);
    transition:    border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-field input:focus,
.form-field textarea:focus {
    outline:      none;
    border-color: var(--bw-brand);
    box-shadow:   0 0 0 3px rgba(31, 64, 41, .12);
}
.form-field textarea {
    resize:     vertical;
    min-height: 8rem;
    line-height:1.5;
}
/* Invalid-Marker (Blazor setzt .invalid auf den InputText/InputTextArea). */
.form-field .invalid    { border-color: #b53e3e; }
.form-field__error      { color: #b53e3e; font-size: 0.8rem; }

/* === Buttons (global, vom Foundation-<ContactForm> referenziert) ===== */

.btn-primary,
.btn-secondary {
    padding:       0.55rem 1.2rem;
    border:        1px solid var(--bw-brand);
    border-radius: var(--bw-radius);
    font:          inherit;
    cursor:        pointer;
    transition:    background 0.15s ease, color 0.15s ease;
}
.btn-primary           { background: var(--bw-brand); color: #fff; }
.btn-primary:hover:not([disabled]) { background: var(--bw-brand-dark, #163223); }
.btn-secondary         { background: transparent; color: var(--bw-fg-soft); border-color: var(--bw-border); }
.btn-secondary:hover:not([disabled]) { background: var(--bw-bg-muted); }
.btn-primary[disabled],
.btn-secondary[disabled] { opacity: 0.5; cursor: not-allowed; }

/* === Foundation-<ContactForm> Theming-Overrides =====================
 * bestes-wetter ist schlank: nur eine Spalte (kein Phone-Feld), und der
 * "Sent"-Streifen soll in der Brand-Tarnfarbe sitzen. *@
 * ==================================================================== */
:root {
    --contact-form-grid-columns: 1fr;          /* eine Spalte; Foundation default ist 1fr 1fr */
    --contact-form-radius:       var(--bw-radius);
    --contact-form-sent-bg:      #ecf5ee;
    --contact-form-sent-border:  #2d5a3d;
    --contact-form-sent-color:   #2d5a3d;
    --contact-form-error-bg:     #f6ebeb;
    --contact-form-error-color:  #b53e3e;
    --contact-form-footnote-color: var(--bw-fg-muted);
}

/* === Scroll-Reveal-Animation (Foundation /js/reveal.js) ================
 * Elemente mit class="reveal" faden + liften beim Scrollen in den
 * Viewport ein. Progressive Enhancement: der versteckte Start-State
 * greift nur, wenn JavaScript aktiv ist (die `js`-Klasse wird synchron
 * im App.razor-<head> auf <html> gesetzt). Ohne JS bleibt jedes
 * .reveal-Element von Anfang an sichtbar — kein Mystery-Inhalt.
 * reveal.js fuegt 'is-visible' per IntersectionObserver hinzu;
 * prefers-reduced-motion schaltet die Animation komplett aus. */
.js .reveal {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .6s ease, transform .6s ease;
}
.js .reveal.is-visible {
    opacity: 1;
    transform: none;
}
@media (prefers-reduced-motion: reduce) {
    .js .reveal,
    .js .reveal.is-visible {
        opacity: 1;
        transform: none;
        transition: none;
    }
}

/* === Owner Preview Mode Banner =====================================
 * Sticht hervor (warmer Bernstein-Ton), damit der Owner nie aus Versehen
 * vergisst, dass er gerade den ComingSoon-Gate uebergeht. Schmaler
 * Streifen, sitzt oben ueber dem ganzen Layout. Cookie-Setting via
 * /_preview?token=<…> — registriert in Program.cs ueber
 * MapPreviewModeEndpoint() aus Carecom.Foundation.Hosting.PreviewMode.
 * =================================================================== */

.preview-banner {
    display:         flex;
    flex-wrap:       wrap;
    justify-content: center;
    align-items:     center;
    gap:             0.5rem 1.25rem;
    padding:         0.4rem 1rem;
    background:      #fef3c7;            /* amber-100 */
    color:           #7c2d12;            /* amber-900 */
    border-bottom:   1px solid #fbbf24;  /* amber-400 */
    font-size:       0.85rem;
    text-align:      center;
}
.preview-banner a {
    color:           inherit;
    text-decoration: underline;
    font-weight:     600;
}
.preview-banner a:hover,
.preview-banner a:focus-visible {
    text-decoration: none;
}
