2026-07-01 20:26:30 +01:00
|
|
|
/* Preview band that blends into the hero title; expands to fullscreen.
|
|
|
|
|
isolation:isolate contains the map + fade + open-button z-indices (which
|
|
|
|
|
must beat Leaflet's internal panes) so they don't leak out and outrank the
|
|
|
|
|
header's Compare button. */
|
2026-07-01 18:12:12 +01:00
|
|
|
.wrapper {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 210px;
|
2026-08-06 12:13:04 +01:00
|
|
|
background: var(--bg-secondary);
|
2026-07-01 20:26:30 +01:00
|
|
|
isolation: isolate;
|
2026-07-01 18:12:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 640px) {
|
|
|
|
|
.wrapper {
|
|
|
|
|
height: 150px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-02 16:17:12 +01:00
|
|
|
/* Preview: move the OSM attribution off the bottom edge, where the school
|
|
|
|
|
title slides up under the fade — top-left is the band's only quiet corner
|
|
|
|
|
(Compare floats top-right). Fullscreen keeps Leaflet's default bottom-right,
|
|
|
|
|
since the zoom control occupies the top-left there. */
|
|
|
|
|
.wrapper:not([data-fullscreen]) :global(.leaflet-bottom.leaflet-right) {
|
|
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: auto;
|
|
|
|
|
bottom: auto;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 18:12:12 +01:00
|
|
|
/* Fullscreen: the Fullscreen API promotes this element to fill the viewport. */
|
|
|
|
|
.wrapper[data-fullscreen] {
|
|
|
|
|
height: 100vh;
|
|
|
|
|
height: 100dvh;
|
|
|
|
|
background: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-05 21:27:41 +01:00
|
|
|
/* Fallback fullscreen (iOS Safari — no Element.requestFullscreen): the API
|
|
|
|
|
can't promote the element, so pin it over the page ourselves. Above the
|
|
|
|
|
comparison toast (3000) and everything else except modals (9999+). */
|
|
|
|
|
.wrapper[data-fs-fallback] {
|
|
|
|
|
position: fixed;
|
|
|
|
|
inset: 0;
|
|
|
|
|
z-index: 5000;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 18:12:12 +01:00
|
|
|
.skeleton {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
background:
|
2026-08-26 21:20:57 +01:00
|
|
|
/* Sweeps toward the card colour, which is a shade lighter than this
|
|
|
|
|
ground in both themes. Hardcoded white was a bright flash across a
|
|
|
|
|
dark page every 1.4s while the tiles loaded. */
|
|
|
|
|
linear-gradient(100deg,
|
|
|
|
|
rgba(var(--bg-card-rgb), 0) 40%,
|
|
|
|
|
rgba(var(--bg-card-rgb), .5) 50%,
|
|
|
|
|
rgba(var(--bg-card-rgb), 0) 60%) var(--bg-secondary);
|
2026-07-01 18:12:12 +01:00
|
|
|
background-size: 200% 100%;
|
|
|
|
|
animation: shimmer 1.4s infinite;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes shimmer {
|
|
|
|
|
to { background-position: -200% 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Full-band click target. Transparent so the map shows through; reveals a
|
|
|
|
|
hint pill on hover/focus. Sits above the map but below the header's fade
|
|
|
|
|
and Compare button (which carry higher stacking in the header). */
|
|
|
|
|
.openBtn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: 0;
|
2026-07-01 20:26:30 +01:00
|
|
|
/* Above Leaflet's marker pane (600) so the whole band — pin included — is
|
|
|
|
|
one click target. Transparent, so map + pin still show through. */
|
|
|
|
|
z-index: 800;
|
2026-07-01 18:12:12 +01:00
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
padding: 0;
|
|
|
|
|
border: 0;
|
|
|
|
|
background: transparent;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-26 21:20:57 +01:00
|
|
|
/*
|
|
|
|
|
* Controls that float ON the map.
|
|
|
|
|
*
|
|
|
|
|
* The map tiles are light in both themes, so these deliberately do NOT follow
|
|
|
|
|
* the theme — they follow the map. The literal ink below is the point: paired
|
|
|
|
|
* with a hardcoded white background, `color: var(--text-primary)` resolved to
|
|
|
|
|
* #E9EEF0 in the dark theme and put near-white text on a near-white button.
|
|
|
|
|
* A themed token is the wrong tool for a surface that never changes.
|
|
|
|
|
*/
|
2026-07-01 18:12:12 +01:00
|
|
|
.openHint {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 7px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
padding: 8px 14px;
|
|
|
|
|
border-radius: 999px;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
font-weight: 600;
|
2026-08-26 21:20:57 +01:00
|
|
|
/* See "Controls that float ON the map" above. */
|
|
|
|
|
color: #1C2731;
|
2026-07-01 18:12:12 +01:00
|
|
|
background: rgba(255, 255, 255, .85);
|
|
|
|
|
-webkit-backdrop-filter: blur(6px);
|
|
|
|
|
backdrop-filter: blur(6px);
|
2026-08-06 12:13:04 +01:00
|
|
|
box-shadow: 0 2px 10px rgba(var(--shadow-rgb), .16);
|
2026-07-01 18:12:12 +01:00
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(4px);
|
|
|
|
|
transition: opacity .16s ease, transform .16s ease;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.openBtn:hover .openHint,
|
|
|
|
|
.openBtn:focus-visible .openHint {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Diffuse fade: map dissolves to the card background before the title. */
|
|
|
|
|
.fade {
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
top: 33%;
|
2026-07-01 20:26:30 +01:00
|
|
|
/* Above Leaflet's tile (200) and overlay (400) panes so the gradient is
|
|
|
|
|
actually visible, but below the marker pane (600) so the pin stays crisp
|
|
|
|
|
on top of the blend. */
|
|
|
|
|
z-index: 450;
|
2026-07-01 18:12:12 +01:00
|
|
|
pointer-events: none;
|
2026-08-26 21:20:57 +01:00
|
|
|
/* The card colour, not white.
|
|
|
|
|
This ramp was hardcoded white and ended at var(--bg-card). In the light
|
|
|
|
|
theme that is white into white and invisible, as intended. In the dark
|
|
|
|
|
theme it climbed to 95% WHITE and then met a near-black card — a bright
|
|
|
|
|
band across the full width, right where the map is supposed to dissolve
|
|
|
|
|
into the header. Fading to the same colour the gradient lands on is the
|
|
|
|
|
whole trick, and it only works if that colour is a token. */
|
2026-07-01 18:12:12 +01:00
|
|
|
background: linear-gradient(to bottom,
|
2026-08-26 21:20:57 +01:00
|
|
|
rgba(var(--bg-card-rgb), 0) 0%,
|
|
|
|
|
rgba(var(--bg-card-rgb), .35) 35%,
|
|
|
|
|
rgba(var(--bg-card-rgb), .75) 62%,
|
|
|
|
|
rgba(var(--bg-card-rgb), .95) 82%,
|
2026-08-06 12:13:04 +01:00
|
|
|
var(--bg-card) 100%);
|
2026-07-01 18:12:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.closeBtn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0.75rem;
|
|
|
|
|
right: 0.75rem;
|
|
|
|
|
z-index: 1000;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 40px;
|
|
|
|
|
height: 40px;
|
|
|
|
|
border: none;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: rgba(255, 255, 255, .92);
|
2026-08-26 21:20:57 +01:00
|
|
|
/* See "Controls that float ON the map" above. */
|
|
|
|
|
color: #1C2731;
|
2026-07-01 18:12:12 +01:00
|
|
|
cursor: pointer;
|
2026-08-06 12:13:04 +01:00
|
|
|
box-shadow: 0 2px 10px rgba(var(--shadow-rgb), .2);
|
2026-07-01 18:12:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.closeBtn:hover {
|
|
|
|
|
background: #fff;
|
|
|
|
|
}
|