@import url('/static/fonts/fonts.css');
@import url('/static/tokens.css');

/* Lieferte vor dem Frontend-Wechsel Tailwinds Preflight über NiceGUI. Ohne
   den Reset addiert der Browser Padding und Border auf jede `width: 100%`:
   .card wurde dadurch 344px breit, wo .result-list 316px anbietet, und die
   Sidebar bekam einen horizontalen Scrollbalken. */
*, *::before, *::after { box-sizing: border-box; }


/* <h1> ist per UA-Stylesheet bold; Bungee liegt nur als 400er-Schnitt vor und
   wuerde synthetisch gefettet — sichtbar als ungleichmaessig verdickte Glyphen
   unter dem 3px-Stroke. */
.font-3d-bungee {
    font-family: 'Bungee', cursive;
    font-weight: 400;
    color: var(--title);
    -webkit-text-stroke: 3px var(--c);
    paint-order: stroke fill;
    text-shadow:
        1px 1px 0 var(--c), 2px 2px 0 var(--c), 3px 3px 0 var(--c),
        4px 4px 0 var(--c), 5px 5px 0 var(--c), 6px 6px 0 var(--c);
    text-transform: uppercase;
    font-size: 1em;
    letter-spacing: 3px;
    line-height: 1;
    margin: 0;
}
/* Die Schriftgroesse hier steuert den ganzen Block: der Titel erbt sie per
   font-size:1em, das Radio haengt mit height:1.3em daran. Eine Stelle statt
   zwei, sonst laufen Schrift und Bild beim Skalieren auseinander.
   Titel plus Radio brauchen rund das 14-fache der Schriftgröße an Breite; die
   calc-Stufe lässt genau so viel übrig, dass die 284px Badges daneben passen.
   Ab 1196px steht der Titel auf vollen 3.5rem, darunter skaliert er, statt
   wie vorher rechts aus dem Viewport zu laufen. */
.header-deco {
    display: inline-flex;
    align-items: flex-end;
    gap: 0.6em;
    margin: 0; padding: 0;
    font-size: clamp(1.5rem, calc((100vw - 356px) / 15), 3.5rem);
    min-width: 0;
}
.header-deco h1 { margin: 0; padding: 0; }
.header-deco img {
    height: 1.3em;
    width: auto;
    flex-shrink: 0;
}

html, body { height: 100%; margin: 0; }

/* Vollbild-Flex-Kontext, den früher Tailwinds w-full/h-screen-Klassen am
   ui.column() lieferten (der selbst schon eine Flex-Column war). Ohne das
   wächst die Sidebar über alle Cards, #map bleibt 0px hoch. Der
   Mobile-Block weiter unten überschreibt height/overflow gezielt. */
.layout-root {
    display: flex;
    flex-direction: column;
    height: 100vh;
    overflow: hidden;
    gap: 0 !important;
}
/* [hidden] steht nur im UA-Stylesheet — jede eigene display-Regel auf einem
   Element gewinnt dagegen unabhängig von der Spezifität (CSS-Cascade-Level,
   nicht Selector-Spezifität). Ohne diese Regel rendert z.B. .card trotz
   hidden-Attribut, weil .card selbst display:flex setzt. Generelle Regel statt
   Einzel-Overrides pro Selektor (.card[hidden], #empty-state[hidden], …), weil
   hidden im Markup an mehreren Stellen genutzt wird (.card, #back-all,
   .card-distance, #empty-state) und jede zukünftige Stelle sonst denselben Bug
   erneut einbauen kann. !important nötig, weil einzelne Regeln (z.B. .card
   mit display:flex) sonst wieder gewinnen würden. */
[hidden] { display: none !important; }

body {
    background: var(--bg) !important;
    font-family: 'Space Mono', monospace !important;
    /* Quasar setzte hier bis zum Frontend-Wechsel 14px; ohne die Angabe erbt
       jedes Element ohne eigene font-size den Browser-Default von 16px. Heute
       traegt jeder Textknoten seine eigene Groesse, die Regel haelt nur die
       Basis fuer alles Kuenftige auf der Skala. */
    font-size: var(--fs-body);
    color: var(--c);
}

/* Material Symbols */
.material-symbols-outlined {
    font-family: 'Material Symbols Outlined';
    font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;
    vertical-align: middle;
    /* .section-label/.legend-label setzen text-transform:uppercase und
       vererben es an dieses Span. Material Symbols löst die Ligatur nur für
       den exakten Kleinbuchstaben-Iconnamen auf ("calendar_month") — in
       Großschreibung ("CALENDAR_MONTH") bricht die Ligatur und der Iconname
       erscheint als Text. */
    text-transform: none;
}
.material-symbols-outlined.ms-filled { font-variation-settings: 'FILL' 1, 'wght' 600, 'GRAD' 0, 'opsz' 24 !important; }

/* width/height/box-shadow halten den Select auf derselben Kante und Höhe wie
   die Buttons darüber. Ohne width:100% ist ein <select> inhaltsbreit: die
   Spalte sprang je nach gewählter Stadt zwischen 160px und 270px. */
/* appearance:none ist Pflicht, nicht Kosmetik: solange ein <select> nativ
   gerendert wird, zeichnet WebKit es als Systemcontrol und laesst den
   box-shadow weg — auf iOS Safari stand das Feld deshalb ohne Schatten da,
   waehrend die Buttons daneben einen hatten. Der native Pfeil faellt damit
   weg, das Dreieck kommt als SVG nach (Farbe hart, weil eine data-URI kein
   var(--c) aufloest; sie entspricht --c). */
.neo-select {
    appearance: none;
    -webkit-appearance: none;
    /* Ohne explizite Farbe faerbt WebKit den Text des Feldes in der
       System-Akzentfarbe (blau). color allein reicht auf iOS nicht immer,
       -webkit-text-fill-color gewinnt dort gegen die UA-Regel. Im
       @supports-Block steht color bereits, den sieht Safari aber nicht. */
    color: var(--c);
    -webkit-text-fill-color: var(--c);
    width: 100%;
    border: var(--border) solid var(--c); border-radius: 0;
    box-shadow: var(--shadow-control);
    background: #fff
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 0h8L4 5z' fill='%231a1a1a'/%3E%3C/svg%3E")
        no-repeat right 10px center;
    height: 28px;
    padding: 0 26px 0 10px;
    font: 700 var(--fs-md) 'Space Mono', monospace;
    transition: transform 80ms, box-shadow 80ms;
}
/* Diagonal wie .card und .legend-reset: das Control wandert um so viel, wie
   der Schatten schrumpft. Runde 1 hatte es auf die rein vertikale Bewegung
   von .neo-btn umgestellt. */
.neo-select:hover {
    transform: translate(1px, 1px);
    box-shadow: 1px 1px 0 var(--c);
}
/* :active/:open statt :focus: der Fokus bleibt nach dem Klick bestehen, das
   Feld stand deshalb dauerhaft eingedrueckt und ohne Schatten da. Den
   Tastatur-Indikator liefert select:focus-visible aus tokens.css. */
.neo-select:active,
.neo-select:open {
    transform: translate(2px, 2px);
    box-shadow: none;
}

/* Aufgeklappte Optionsliste im Projektstil.
   Vor dem Frontend-Wechsel war die Stadtauswahl ein Quasar-Widget, also
   normales HTML — Grund, Markierung und Schrift liessen sich frei setzen. Ein
   natives <select> zeichnet seine Liste dagegen selbst und ignoriert CSS.
   `appearance: base-select` (Chrome/Edge 135+; Safari und Firefox in
   Vorbereitung) holt die Liste ins DOM
   zurueck und macht sie gestaltbar; Firefox kennt die Regel noch nicht, dort
   bleibt schlicht das native Popup stehen. Progressive Enhancement, deshalb
   ohne Fallback-Konstruktion: alles unten ist rein additiv. */
@supports (appearance: base-select) {
    .neo-select,
    .neo-select::picker(select) {
        appearance: base-select;
    }
    /* base-select setzt das Feld selbst auf Button-Defaults zurueck — Rahmen,
       Innenabstand und Ausrichtung deshalb erneut festlegen. */
    .neo-select {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        text-align: left;
        color: var(--c);
        /* Hier zeichnet ::picker-icon den Pfeil, das SVG waere der zweite. */
        background-image: none;
        padding-right: 10px;
    }
    .neo-select::picker-icon {
        content: '';
        width: 0; height: 0;
        border-left: 4px solid transparent;
        border-right: 4px solid transparent;
        border-top: 5px solid var(--c);
        transition: rotate 120ms;
    }
    .neo-select:open::picker-icon { rotate: 180deg; }

    /* Weisser Grund und viewport-abhaengige Hoehe wie im Quasar-Dropdown
       vorher: dort standen 647px und 21 Zeilen, das feste max-height von
       320px hatte die Liste bei 34 Staedten halbiert. */
    .neo-select::picker(select) {
        border: var(--border) solid var(--c);
        border-radius: 0;
        box-shadow: var(--shadow-panel);
        background: #fff;
        padding: 0;
        margin-top: 4px;
        max-height: min(640px, 70vh);
        overflow-y: auto;
    }
    .neo-select option {
        padding: 6px 10px;
        font: 700 var(--fs-md) 'Space Mono', monospace;
        color: var(--c);
        background: #fff;
        border-bottom: 1px solid rgba(26, 26, 26, 0.12);
    }
    .neo-select option:last-child { border-bottom: none; }
    /* Kein Haken-Spalte: die Auswahl zeigt der gelbe Grund an, wie im
       Quasar-Dropdown vorher. */
    .neo-select option::checkmark { display: none; }
    .neo-select option:checked { background: var(--accent); }
    .neo-select option:hover {
        background: var(--c);
        color: #fff;
    }
    /* Tastaturfokus als Rahmen statt als Fuellung: sonst ueberdeckt die
       aktive Zeile beim Aufklappen per Tastatur die gelbe Auswahl-Markierung. */
    .neo-select option:focus {
        outline: 2px solid var(--c);
        outline-offset: -2px;
    }
}

/* Leaflet */
.leaflet-container {
    border: var(--border-thick) solid var(--c) !important;
    box-shadow: var(--shadow-surface) !important;
    /* Leaflets Default ist Helvetica Neue 12px, die einzige Fremdschrift auf
       der Seite. */
    font-family: 'Space Mono', monospace !important;
}
.leaflet-control-zoom, .leaflet-bar {
    border: var(--border) solid var(--c) !important; border-radius: 0 !important;
    box-shadow: var(--shadow-control) !important; overflow: hidden;
}
.leaflet-bar a {
    border: none !important; border-bottom: var(--border) solid var(--c) !important;
    border-radius: 0 !important; background: #fff !important;
    color: var(--c) !important; font-family: 'Space Mono', monospace !important;
    font-weight: 700 !important; font-size: 16px !important;
    width: 28px !important; height: 28px !important; line-height: 28px !important;
    transition: background 0.2s;
}
.leaflet-bar a .material-symbols-outlined { font-size: 16px !important; }
.leaflet-bar a:last-child { border-bottom: none !important; }
.leaflet-bar a:active { background: var(--accent) !important; }
.leaflet-control-attribution {
    font-size: var(--fs-xs) !important;
}
.leaflet-tile-pane {
    filter: grayscale(.6) sepia(.25) contrast(1.05) brightness(1.02);
}
.leaflet-popup-content-wrapper {
    border: var(--border) solid var(--c) !important; box-shadow: var(--shadow-panel) !important;
    border-radius: 0 !important; padding: 0 !important;
}
/* Der Website-Link im Marker-Popup traegt seine Farbe als Inline-Style aus
   popup_html(); die Unterstreichung ist reine Browser-Vorgabe fuer <a> und
   passt nicht zu den Card-Links, die keine haben. */
.leaflet-popup-content a { text-decoration: none; }
.leaflet-popup-content a:hover { text-decoration: underline; }
.leaflet-popup-tip-container {
    margin-top: -2px !important;
    filter: drop-shadow(3px 3px 0 var(--c)) !important;
}
.leaflet-popup-tip {
    border: var(--border-thick) solid var(--c) !important;
    border-top: none !important;
    border-left: none !important;
    background: #fff !important;
    box-shadow: none !important;
    padding: 0 !important;
}
.leaflet-center { left: 50%; transform: translateX(-50%); pointer-events: none; }
.leaflet-center > * { pointer-events: auto; }

/* Markers */
.nb-m-badge {
    background: var(--c); color: #fff;
    font-family: 'Space Mono', monospace; font-weight: 700; font-size: 9px;
    min-width: 12px; height: 12px; padding: 0 2px; line-height: 1;
    display: flex; align-items: center; justify-content: center;
}
.nb-m {
    border: var(--border) solid var(--c); box-shadow: var(--shadow-control);
    width: 28px !important; height: 28px !important;
    display: flex; align-items: center; justify-content: center;
    color: var(--c);
}

/* Button press effect, pattern from https://retroui.dev (MIT, Logging-Studio/RetroUI) */
.neo-btn { transition: transform 0.2s, box-shadow 0.2s; }
.neo-btn:hover { transform: translateY(2px); box-shadow: 1px 1px 0 var(--c) !important; }
.neo-btn:active { transform: translate(4px, 4px); box-shadow: none !important; }

/* Section-Labels (STADT, ZEITRAUM, LEGENDE/FILTER, ERGEBNISSE).
   Werden als <h2> gerendert (A11y: Screenreader-Navigation). line-height
   explizit auf 1 zwingen, Margin/Padding ebenfalls reset. */
.section-label {
    font-family: 'Archivo Black', sans-serif;
    font-size: var(--fs-sm) !important;
    /* Archivo Black liegt nur als 400er-Schnitt vor; 900 laesst den Browser
       synthetisch fetten und verdickt die Glyphen ungleichmaessig. */
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: .08em;
    line-height: 1 !important;
    margin: 0 0 8px 0 !important;
    padding: 0 !important;
    display: flex;
    align-items: center;
    gap: 6px;
}
.section-label.inline { margin: 0 !important; }
.section-label .material-symbols-outlined { font-size: 16px; }

/* Card hover effect */
.card:hover {
    transform: translate(2px,2px); box-shadow: var(--shadow-control) !important;
}
.card:active {
    transform: translate(4px,4px); box-shadow: none !important;
}

/* Sidebar-Footer: Pflicht-Links (Impressum/Datenschutz) am Boden */
.sidebar-legal {
    flex-shrink: 0;
    width: 100%;
    border-top: var(--border) solid var(--c);
    padding: 0 8px;
    background: var(--bg);
    text-align: center;
    font-family: 'Space Mono', monospace;
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: 0.05em;
    line-height: 1;
}
.sidebar-legal a {
    display: inline-block;
    padding: 10px 6px;
    color: var(--c);
    opacity: 0.7;
    text-decoration: none;
    transition: opacity 150ms;
}
.sidebar-legal a:hover { opacity: 1; text-decoration: underline; }
.sidebar-legal .sep { color: var(--c); opacity: 0.35; margin: 0 8px; }

/* z-index 1000 sicher über allen Leaflet-Panes (max popupPane = 700). */
.layout-legend-strip {
    position: absolute;
    bottom: 12px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 6px 8px;
    background: #fff;
    border: var(--border) solid var(--c);
    box-shadow: var(--shadow-panel);
}
.layout-legend-strip .legend-label {
    font-family: 'Archivo Black', sans-serif;
    font-size: var(--fs-sm) !important;
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: .08em;
    display: flex;
    align-items: center;
    gap: 4px;
    line-height: 1 !important;
    margin: 0 !important;
    padding: 0 !important;
}
.layout-legend-strip .legend-label .material-symbols-outlined {
    font-size: var(--fs-body);
}
.layout-legend-strip .legend-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.layout-legend-strip .legend-grid {
    display: grid;
    grid-template-columns: repeat(7, 35px);
    gap: 4px;
}
.layout-legend-strip .legend-divider {
    width: 2px;
    align-self: stretch;
    background: var(--c);
    /* +2px nach rechts, weil der box-shadow der Wochentag-Buttons (2px nach
       rechts) sonst die linke Lücke optisch frisst und der Trenner aus der
       Mitte rutscht. */
    margin-left: 2px;
}
.layout-legend-strip .legend-reset {
    width: 35px;
    height: 35px;
    background: #fff;
    border: var(--border) solid var(--c);
    box-shadow: var(--shadow-control);
    color: var(--c);
    cursor: pointer;
    user-select: none;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.15s, box-shadow 0.15s;
}
.layout-legend-strip .legend-reset:hover {
    transform: translate(1px, 1px);
    box-shadow: 1px 1px 0 var(--c);
}
.layout-legend-strip .legend-reset:active {
    transform: translate(2px, 2px);
    box-shadow: none;
}
.layout-legend-strip .legend-reset .material-symbols-outlined {
    font-size: 18px;
    line-height: 1;
}

.layout-map { position: relative; }

/* Scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--c); }
* { scrollbar-width: thin; scrollbar-color: var(--c) var(--bg); }

/* Touch-Geräte: kein Sticky-Hover/Focus/Active auf Buttons nach Tap.
   iOS Safari (WebKit #158517) lässt :active kleben bis zum nächsten Tap
   woanders; ohne Fix bleibt der Press-Effekt bis zum nächsten Tap sichtbar. */
@media (hover: none) {
    .neo-btn, .day-btn, .legend-reset, .neo-select, .sort-pill, .range-btn {
        -webkit-tap-highlight-color: transparent; touch-action: manipulation;
    }
    .neo-btn:hover { transform: none; box-shadow: var(--shadow-control); }
    .neo-select:hover { transform: none; box-shadow: var(--shadow-control); }
    .layout-legend-strip .legend-reset:hover {
        transform: none; box-shadow: var(--shadow-control);
    }
    /* Die Tooltips haengen an :hover und blieben nach dem Tap stehen, bis man
       woanders hin tippte. Ohne Zeigegeraet gibt es kein Hover, das Kuerzel im
       Button (Mo, Di, …) sagt dasselbe — hier also ganz weg. */
    .day-btn[data-tip]:hover::after,
    .legend-reset[data-tip]:hover::after { display: none; }
}

/* Mobile responsive */
@media (max-width: 768px) {
    /* Body wird der echte Scroll-Container → iOS Safari blendet die URL-Bar
       beim Scrollen aus wie auf jeder normalen Website. html/body werden auf
       auto-Höhe gezwungen, damit nichts in der Mitte den Scroll einfängt. */
    html, body {
        height: auto !important;
        min-height: 100vh;
        min-height: 100dvh;
        overflow: visible !important;
    }
    body { background: var(--bg) !important; }
    .layout-root {
        overflow: visible !important;
        height: auto !important;
        min-height: 100vh;
        min-height: 100dvh;
        gap: 0 !important;
    }
    .layout-header {
        height: auto !important; flex-direction: column !important;
        gap: 0 !important;
        padding: calc(4px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) 4px calc(12px + env(safe-area-inset-left)) !important;
        align-items: stretch !important;
    }
    .layout-header .header-title {
        -webkit-text-stroke: 2px var(--c) !important;
        text-shadow: 1px 1px 0 var(--c), 2px 2px 0 var(--c), 3px 3px 0 var(--c) !important;
    }
    /* Fest auf 1.6rem ergab 362px in einem 351px breiten Container: das Radio
       klebte am Displayrand. Ab 480px steht der Titel unverändert auf 1.6rem,
       darunter skaliert die Schrift. Das Radio bleibt bei 1.3em — kleiner
       skaliert das Browser-Downsampling die feinen Strukturen kaputt. */
    .layout-header .header-deco { font-size: clamp(1.2rem, 6.4vw, 1.6rem) !important; }
    /* Linksbündig unter den Badges, wie vor dem Konsistenz-Durchlauf. Die
       Baseline hatte auf .header-deco ein justify-content:center, das an einem
       inline-flex ohnehin wirkungslos blieb — der Titel stand also immer links.
       Runde 1 hatte die Absicht per text-align wirksam gemacht; Marco will den
       alten Zustand zurück (Entscheidung 2026-09-01). */
    .header-deco-wrapper { padding: 10px 0 !important; }
    .layout-header .header-badges { width: auto !important; justify-content: flex-start; }
    .layout-main {
        flex-direction: column !important; overflow: visible !important;
        padding: 0 0.5rem !important; gap: 0.5rem !important;
        flex: none !important; height: auto !important;
    }
    .layout-sidebar {
        width: 100% !important; height: auto !important;
        box-shadow: var(--shadow-panel) !important;
        order: 2;
    }
    .layout-map {
        order: 1;
        height: 65vh !important; min-height: 65vh !important;
        height: 65dvh !important; min-height: 65dvh !important;
        width: 100% !important;
    }
    .layout-leaflet, .layout-leaflet > div, .layout-leaflet .leaflet-container {
        height: 65vh !important;
        height: 65dvh !important;
    }
    .leaflet-container { box-shadow: var(--shadow-panel) !important; }
    .layout-sidebar .neo-btn { min-height: 28px !important; height: 28px !important; font-size: var(--fs-sm) !important; }
    /* Mobile: Ergebnis-Liste bekommt feste max-Höhe und scrollt intern,
       damit sie die Sidebar nicht endlos in die Länge zieht und die Map
       in Sichtweite bleibt. */
    .layout-sidebar .result-list {
        max-height: 60vh !important;
        overflow-y: auto !important;
    }
    /* Mobile: Legenden-Sticker etwas tiefer ansetzen, knapp über das
       OSM-Attribution-Control (~17px hoch). Buttons kompakter (32px statt
       35px); passt in 320px-Viewports inkl. Trenner und Reset. */
    .layout-legend-strip {
        gap: 3px; padding: 5px 8px;
        bottom: 22px;
    }
    .layout-legend-strip .legend-row {
        gap: 5px;
    }
    .layout-legend-strip .legend-grid {
        grid-template-columns: repeat(7, 32px);
        gap: 3px;
    }
    /* Kein !important mehr noetig, seit .day-btn nur noch einmal und ohne
       Strip-Prefix deklariert ist: zwei Klassen schlagen eine. */
    .layout-legend-strip .day-btn {
        height: 32px; width: 32px;
    }
    .layout-legend-strip .legend-label {
        font-size: var(--fs-sm);
    }
    .layout-legend-strip .legend-label .material-symbols-outlined {
        font-size: var(--fs-md);
    }
    .layout-legend-strip .legend-reset {
        width: 32px; height: 32px;
    }
    .layout-legend-strip .legend-reset .material-symbols-outlined {
        font-size: 16px;
    }
}

/* Card — Werte aus CARD_BASE / CARD_ACTIVE / CARD_DIM (app.py) */
.card {
    display: flex; align-items: flex-start; gap: 8px; padding: 12px; width: 100%;
    border: var(--border) solid var(--c); box-shadow: var(--shadow-card);
    background: #fff; cursor: pointer; flex-wrap: nowrap;
    transition: all .2s;
}
.card.is-active { border: var(--border-thick) solid var(--title); box-shadow: 4px 4px 0 var(--title); }
.card.is-dimmed { opacity: 0.35; }
.card-dot { width: 12px; height: 12px; min-width: 12px; border-radius: 50%;
            border: var(--border) solid var(--c); margin-top: 4px; }
.card-body { display: flex; flex-direction: column; min-width: 0; width: 100%; }
.card-title { font-family: 'Space Grotesk', sans-serif; font-weight: 700; font-size: var(--fs-lg);
              margin: 0; }
.card-venue { font-size: var(--fs-sm); color: var(--c); margin: 0;
              display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
              overflow: hidden; }
.card-date { font-weight: 700; font-size: var(--fs-sm); margin: 0; }
.card-street, .card-addr { font-size: var(--fs-sm); color: var(--subtle); margin: 0; }
.card-distance { font-size: var(--fs-xs); color: var(--subtle); margin: 2px 0 0; }
.card-link { font-weight: 700; font-size: var(--fs-sm);
             text-decoration: none; margin-top: 4px; align-self: flex-start;
             padding: 4px 0; }
.empty-state { color: var(--subtle); text-align: center; padding: 2rem 0; font-size: var(--fs-md); }

/* Zeitraum-Buttons — aus RANGE_BASE_STYLE + range_btn_style (app.py).
   Die Farbe kommt per --range-color aus dem Inline-Style des Buttons. */
.range-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; width: 100%; }
.range-btn {
    width: 100%; font-size: var(--fs-sm); font-weight: 700;
    height: 28px; padding: 0 10px; border: var(--border) solid var(--c); border-radius: 0;
    color: var(--c); cursor: pointer;
    background: color-mix(in srgb, var(--range-color) 60%, transparent);
    box-shadow: var(--shadow-control); transform: translate(0, 0);
}
.range-btn[aria-pressed="true"] {
    background: var(--c); color: var(--range-color);
    box-shadow: 0 0 0 var(--c); transform: translate(2px, 2px);
}

/* Wochentag-Buttons — aus DAY_BASE_STYLE + day_btn_style (app.py) */
.day-btn {
    width: 35px; height: 35px; min-width: 0; min-height: 0;
    padding: 0; border: var(--border) solid var(--c); border-radius: 0;
    font-size: var(--fs-sm);
    color: var(--c); cursor: pointer; display: flex; flex-direction: column;
    align-items: center; justify-content: center; line-height: 1;
    background: var(--day-color); box-shadow: var(--shadow-control); transform: none; opacity: 1;
}
.day-btn[aria-pressed="false"] {
    background: color-mix(in srgb, var(--day-color) 27%, transparent);
    opacity: .3; box-shadow: none; transform: translate(2px, 2px);
}

/* Sortier-Pills — aus SORT_PILL_BASE + sort_pill_style (app.py) */
.sort-pill {
    padding: 0 10px; height: 28px; border: var(--border) solid var(--c); border-radius: 0;
    font-size: var(--fs-xs); font-weight: 700;
    color: var(--c); flex: 1; min-width: 0; white-space: nowrap; cursor: pointer;
    background: #fff; box-shadow: var(--shadow-control);
}
.sort-pill[aria-pressed="true"] {
    background: var(--c); color: var(--bg);
    box-shadow: 1px 1px 0 var(--c); transform: translate(1px, 1px);
}
/* Kein pointer-events:none: der Klick ist ueber `disabled` ohnehin blockiert,
   und ohne Hover zeigt der Browser weder den title ("Standort freigeben …")
   noch den not-allowed-Cursor. */
.sort-pill:disabled {
    background: #eaeaea; opacity: .45; box-shadow: none;
    cursor: not-allowed;
}

/* "← Alle" — aus back_btn (app.py). Türkis statt --accent: der Button war in
   der NiceGUI-Fassung bewusst die einzige Farbe in der Control-Row, weil er
   aus einer Auswahl herausführt statt eine zu setzen. Beim Frontend-Wechsel
   lief er auf das Gelb der übrigen Buttons, Marco will den Unterschied zurück
   (2026-09-06). */
.back-btn {
    padding: 0 10px; height: 28px; background: var(--cyan); border: var(--border) solid var(--c);
    box-shadow: var(--shadow-control);
    font-size: var(--fs-xs); font-weight: 700; border-radius: 0; color: var(--c);
    flex: 0 0 auto; cursor: pointer;
}

/* Header-Badges — aus BADGE_STYLE (app.py) */
.badge {
    padding: 0 12px; border: var(--border) solid var(--c); box-shadow: var(--shadow-control);
    font-size: var(--fs-md); font-weight: 700;
    border-radius: 0; height: 28px; color: var(--c); text-decoration: none;
    display: inline-flex; align-items: center; background: var(--accent);
}

/* Layout-Gerüst — ersetzt die NiceGUI-Row/Column-Utilities */
/* Der Titel sitzt in der Viewport-Mitte, sobald der Platz dafür reicht, sonst
   linksbündig neben den Badges. Echte Zentrierung heißt: rechts vom Titel muss
   so viel frei bleiben wie die Badges links breit sind (284px). Mit 792px
   Titel und 48px Rand geht das ab 1417px auf — darunter würden die Badges in
   den Titel laufen, also fällt der Anspruch dort weg statt den Titel zu
   verkleinern oder umzubrechen. */
.layout-header { display: flex; align-items: center; gap: 24px;
                 padding: 4px 24px; flex-shrink: 0; }
.header-badges { display: flex; gap: 8px; width: auto; flex-shrink: 0; }

@media (min-width: 1420px) {
    /* Zwei gleich breite 1fr-Spalten links und rechts der auto-Spalte setzen
       den Titel exakt auf die Viewport-Mitte. Die dritte Spalte bleibt leer,
       ein Platzhalter-Element braucht es dafür nicht. */
    .layout-header { display: grid; grid-template-columns: 1fr auto 1fr; }
}
.layout-main { display: flex; flex: 1; overflow: hidden; padding: 0.5rem 1.5rem 1rem; gap: 1rem; }
.layout-sidebar { width: 340px; flex-shrink: 0; border: var(--border-thick) solid var(--c);
                  box-shadow: var(--shadow-surface); background: var(--bg);
                  overflow-y: auto; overflow-x: hidden;
                  display: flex; flex-direction: column; height: 100%; }
.layout-map { flex: 1 1 auto; min-width: 0; height: 100%; }
.layout-leaflet { height: 100%; width: 100%; }
.filter-section { padding: 16px; border-bottom: var(--border) solid var(--c); }
.results-header { display: flex; align-items: center; justify-content: space-between;
                  padding: 8px 16px; border-bottom: var(--border) solid var(--c); }
/* Quasar gab der Zahl eine line-height von 36px, wodurch .results-header 54px
   hoch stand; hier sind es 44px (8 + 26 + 8). Bewusst nicht zurueckgestellt
   (Entscheidung 2026-09-02): die 36px waren ein Framework-Default, 44px ist die
   Rechnung, die auch die anderen Sidebar-Sektionen aufmachen. 1.5rem bleibt der
   einzige rem-Wert der Karte, es gibt keine 24px-Stufe in den Tokens. */
#result-count { font-family: 'Archivo Black', sans-serif; font-weight: 400;
               font-size: 1.5rem; color: var(--title); }
.sort-row { display: flex; align-items: center; gap: 8px; padding: 8px 16px;
            border-bottom: var(--border) solid var(--c); }
.result-list { display: flex; flex-direction: column; gap: 8px; padding: 16px;
               flex: 1; overflow-y: auto; min-height: 0; }

.day-label { font-size: var(--fs-xs); line-height: 1; font-weight: 700; margin-top: 1px; }

/* Tooltip-Ersatz für Quasars Tooltip-Direktive — gleiche Optik, ohne Quasar.
   Quasar zeigte seinen Tooltip auch bei Tastaturfokus; :focus-visible holt das
   zurueck, ohne ihn nach einem Mausklick stehen zu lassen. */
.day-btn[data-tip]:hover::after,
.day-btn[data-tip]:focus-visible::after,
.legend-reset[data-tip]:hover::after,
.legend-reset[data-tip]:focus-visible::after {
    content: attr(data-tip);
    position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    white-space: nowrap; z-index: 1001;
    background: var(--accent); color: var(--c);
    border: var(--border) solid var(--c); box-shadow: var(--shadow-control);
    font-family: 'Space Mono', monospace; font-size: var(--fs-sm); font-weight: 700;
    padding: 3px 6px; pointer-events: none;
}
.day-btn, .legend-reset { position: relative; }
