@charset "UTF-8";

/* ==========================================================================
   1. FONTS
   Rubik variable font from @fontsource-variable/rubik 5.2.8 (SIL OFL 1.1,
   see ../fonts/OFL.txt). latin-ext is only fetched when a page needs it.
   ========================================================================== */

@font-face {
    font-family: 'Rubik';
    font-style: normal;
    font-display: swap;
    font-weight: 300 900;
    src: url("../fonts/rubik-latin-wght-normal.b65daadded74.woff2") format("woff2-variations"),
         url("../fonts/rubik-latin-wght-normal.b65daadded74.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
    font-family: 'Rubik';
    font-style: normal;
    font-display: swap;
    font-weight: 300 900;
    src: url("../fonts/rubik-latin-ext-wght-normal.15c3dca723cb.woff2") format("woff2-variations"),
         url("../fonts/rubik-latin-ext-wght-normal.15c3dca723cb.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ==========================================================================
   2. DESIGN TOKENS
   base.html sets <html data-bs-theme> from prefers-color-scheme before the
   first paint. The chart backgroundColor in charts.js must match --lw-surface.
   ========================================================================== */

:root {
    --lw-bg: #f3f4f6;
    --lw-surface: #ffffff;
    --lw-surface-2: #eef0f3;
    --lw-surface-3: #e2e5ea;
    --lw-border: rgba(16, 24, 40, 0.08);
    --lw-border-strong: rgba(16, 24, 40, 0.16);
    --lw-text: #1f2328;
    --lw-text-muted: #5f6670;
    /* Accent as a fill (header, active buttons) and as text on a surface */
    --lw-accent: #3949ab;
    --lw-accent-hover: #303f9f;
    --lw-on-accent: #ffffff;
    --lw-accent-text: #3949ab;
    /* Min and max: the charts' blue and red, darkened to 4.5:1 on white */
    --lw-min: #2366c2;
    --lw-max: #c42f2e;
    --lw-ok: #2e9d57;
    --lw-warn: #f0a400;
    --lw-bad: #d93025;
    --lw-radius: 0.75rem;
    --lw-gap: 0.75rem;
    --lw-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 2px 6px rgba(16, 24, 40, 0.05);
    /* Tonal buttons: the accent as a tint */
    --lw-accent-soft: #e8eaf6;
    --lw-accent-soft-hover: #d6daf0;
    /* Header: indigo in light mode */
    --lw-header-bg: #3949ab;
    --lw-header-border: transparent;
    --lw-header-text: #ffffff;
    --lw-header-text-muted: rgba(255, 255, 255, 0.82);
    --lw-header-active-bg: rgba(255, 255, 255, 0.16);
    --lw-header-active-line: transparent;
    /* Night bands on the day and week charts; -clear is the same colour at
       zero alpha for the dusk and dawn fades */
    --lw-night-band: rgba(40, 50, 110, 0.06);
    --lw-night-band-clear: rgba(40, 50, 110, 0);
    /* Sun card: the charts' sunshine colour. Moon: the lit side at least 3:1
       against the shadow, the edge framing a full moon on white. */
    --lw-sun: #eda100;
    --lw-moon-lit: #f6e7ad;
    --lw-moon-shadow: #6b7280;
    --lw-moon-edge: #6b7280;

    --bs-primary: #3949ab;
    --bs-primary-rgb: 57, 73, 171;
    --bs-link-color: #3949ab;
    --bs-link-color-rgb: 57, 73, 171;
    --bs-link-hover-color: #283593;
    --bs-link-hover-color-rgb: 40, 53, 147;
}

[data-bs-theme=dark] {
    --lw-bg: #141518;
    --lw-surface: #1e2024;
    --lw-surface-2: #272a30;
    --lw-surface-3: #31353c;
    --lw-border: rgba(255, 255, 255, 0.08);
    --lw-border-strong: rgba(255, 255, 255, 0.16);
    --lw-text: #e3e5e8;
    --lw-text-muted: #9aa1ab;
    --lw-accent: #3f51b5;
    --lw-accent-hover: #4a5cc4;
    --lw-accent-text: #9fa8da;
    /* The charts' dark-palette blue and red */
    --lw-min: #6da7ec;
    --lw-max: #f08080;
    --lw-shadow: none;
    --lw-accent-soft: rgba(159, 168, 218, 0.14);
    --lw-accent-soft-hover: rgba(159, 168, 218, 0.22);
    /* Header: the dark surface; the active link is underlined instead of filled */
    --lw-header-bg: var(--lw-surface);
    --lw-header-border: var(--lw-border);
    --lw-header-text: var(--lw-text);
    --lw-header-text-muted: var(--lw-text-muted);
    --lw-header-active-bg: transparent;
    --lw-header-active-line: var(--lw-accent-text);
    --lw-night-band: rgba(0, 0, 0, 0.25);
    --lw-night-band-clear: rgba(0, 0, 0, 0);
    --lw-sun: #f2b630;
    --lw-moon-lit: #ece3c2;
    --lw-moon-shadow: #3a3f47;
    --lw-moon-edge: #4a505a;

    --bs-primary: #9fa8da;
    --bs-primary-rgb: 159, 168, 218;
    --bs-link-color: #9fa8da;
    --bs-link-color-rgb: 159, 168, 218;
    --bs-link-hover-color: #c5cae9;
    --bs-link-hover-color-rgb: 197, 202, 233;
}

/* Bootstrap's components take their background from --bs-body-bg, so that
   is the card surface; the page itself is painted with --lw-bg on body */
:root,
[data-bs-theme=dark] {
    --bs-body-bg: var(--lw-surface);
    --bs-body-color: var(--lw-text);
    --bs-emphasis-color: var(--lw-text);
    --bs-secondary-color: var(--lw-text-muted);
    --bs-secondary-bg: var(--lw-surface-2);
    --bs-tertiary-bg: var(--lw-surface-2);
    --bs-border-color: var(--lw-border-strong);
    --bs-border-color-translucent: var(--lw-border);
}

/* ==========================================================================
   3. BASE / GLOBAL
   ========================================================================== */

body {
    font-family: "Rubik", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
    font-weight: 400;
    background-color: var(--lw-bg);
    color: var(--lw-text);
    -webkit-tap-highlight-color: transparent;
    overflow-x: hidden;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    margin: 0;
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 400;
}

strong {
    font-weight: 500;
}

a {
    text-decoration: none;
    cursor: pointer;
    transition: color 0.2s ease-in-out;
}

a:not([href]):not([tabindex]),
a:not([href]):not([tabindex]):focus,
a:not([href]):not([tabindex]):hover {
    color: inherit;
    text-decoration: none;
}

.disabled,
:disabled {
    pointer-events: none !important;
}

.font-small {
    font-size: 0.85rem;
}

/* Bootstrap draws the thumb in its compiled primary blue */
.form-range::-webkit-slider-thumb {
    background-color: var(--lw-accent);
}

.form-range::-webkit-slider-thumb:active {
    background-color: var(--lw-accent-hover);
}

.form-range::-moz-range-thumb {
    background-color: var(--lw-accent);
}

.form-range::-moz-range-thumb:active {
    background-color: var(--lw-accent-hover);
}

/* Inline SVG icons from the {% icon %} tag (display/icons.py) */
.icon {
    display: inline-block;
    flex-shrink: 0;
    fill: currentColor;
    vertical-align: -0.125em;
}

/* ==========================================================================
   4. COLORS (only classes actually used)
   ========================================================================== */

.indigo {
    background-color: var(--lw-accent);
}

.blue-text {
    color: var(--lw-accent-text);
}

.red-text {
    color: var(--lw-max);
}

.white-text {
    color: var(--lw-on-accent);
}

.white-ic {
    color: var(--lw-on-accent);
}

/* ==========================================================================
   5. TYPOGRAPHY
   ========================================================================== */

/* Card titles that are links (blog), and the larger ones of the blog */
.card-heading a {
    color: inherit;
}

.card-heading a:hover {
    color: var(--lw-accent-text);
}

.card .card-body .card-title-lg {
    font-size: 1.25rem;
    line-height: 1.3;
}

.card .card-body h1.card-title-lg {
    font-size: 1.75rem;
}

/* ==========================================================================
   6. BLOG
   ========================================================================== */

/* A post reads at about 70 characters per line: its column stops growing,
   and post and sidebar stay centred together. Stacked below lg, the sidebar
   keeps the post's width. */
.blog-main,
.blog-side {
    max-width: 38rem;
}

@media (width >= 992px) {
    .blog-main {
        flex: 0 1 38rem;
    }

    .blog-side {
        flex: 0 0 21rem;
    }
}

.blog-back {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    margin-bottom: 0.25rem;
    font-size: 0.875rem;
    color: var(--lw-text-muted);
}

.blog-back:hover {
    color: var(--lw-accent-text);
}

/* Links in running text are underlined: their colour alone is too close to
   the text's (about 2:1) */
.blog-prose a:not(.video-facade) {
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.2em;
}

.blog-prose a:not(.video-facade):hover {
    text-decoration-thickness: 2px;
}

/* Tables in a post: compact rows divided by lines, figures of equal width.
   A wide table scrolls sideways within the post instead of being cut off. */
.blog-table {
    overflow-x: auto;
    margin-bottom: 1rem;
}

.blog-table table {
    font-variant-numeric: tabular-nums;
}

.blog-table th,
.blog-table td {
    padding: 0.375rem 0.75rem;
    border-bottom: 1px solid var(--lw-border-strong);
    text-align: start;
    vertical-align: top;
}

.blog-table :is(th, td):first-child {
    padding-left: 0;
}

.blog-table :is(th, td):last-child {
    padding-right: 0;
}

.blog-table thead th {
    color: var(--lw-text-muted);
}

.blog-table tbody tr:last-child > * {
    border-bottom: 0;
}

/* Alignment chosen in the editor's table menu */
.blog-table .htCenter {
    text-align: center;
}

.blog-table .htRight {
    text-align: right;
}

.blog-table caption {
    padding: 0.5rem 0 0;
    font-size: 0.875rem;
    color: var(--lw-text-muted);
}

/* A YouTube video before it is played (blog/includes/video.html) */
.video-facade {
    display: block;
    overflow: hidden;
    background-color: #000;
    color: #fff;
}

/* Cropping hides the black bars of YouTube's 4:3 preview image */
.video-facade img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.video-facade-title {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    padding: 0.75rem 1rem 2rem;
    overflow: hidden;
    background: linear-gradient(rgba(0, 0, 0, 0.65), rgba(0, 0, 0, 0));
    font-weight: 500;
    line-height: 1.3;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.video-facade-play {
    position: absolute;
    top: 50%;
    left: 50%;
    display: grid;
    place-items: center;
    width: 4.25rem;
    height: 3rem;
    padding-left: 0.125rem;
    border-radius: 0.75rem;
    background-color: rgba(23, 23, 23, 0.8);
    font-size: 1.25rem;
    transform: translate(-50%, -50%);
    transition: background-color 0.15s ease-in-out;
}

.video-facade:hover .video-facade-play,
.video-facade:focus-visible .video-facade-play {
    background-color: var(--lw-accent);
}

.video-facade-note {
    position: absolute;
    right: 0.75rem;
    bottom: 0.75rem;
    padding: 0.125rem 0.5rem;
    border-radius: 999px;
    background-color: rgba(0, 0, 0, 0.65);
    font-size: 0.75rem;
}

.video-facade:focus-visible {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: 2px;
}

/* The older and the newer post, under a post */
.blog-pager {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--lw-gap);
    margin-top: 1.5rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--lw-border);
}

.blog-pager a {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.blog-pager-newer {
    grid-column: 2;
    align-items: flex-end;
    text-align: end;
}

.blog-pager-label {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.8125rem;
    color: var(--lw-text-muted);
}

.blog-pager-title {
    font-weight: 500;
}

@media (width < 576px) {
    .blog-pager {
        grid-template-columns: 1fr;
    }

    .blog-pager-newer {
        grid-column: auto;
    }
}

/* Post cards (index, Aktuell): the title's link stretches over the whole card */
.post-card:hover {
    border-color: var(--lw-border-strong);
}

.post-card:has(.stretched-link:focus-visible) {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: 2px;
}

.post-card .stretched-link:focus-visible {
    outline: none;
}

.post-card-more {
    color: var(--lw-accent-text);
}

/* Beitragsarchiv: one fold per year, the current year's open */
.blog-archive {
    padding-top: 0.25rem;
    padding-bottom: 0.25rem;
}

.blog-archive details + details {
    border-top: 1px solid var(--lw-border);
}

.blog-archive summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0;
    cursor: pointer;
    list-style: none;
}

.blog-archive summary::-webkit-details-marker {
    display: none;
}

.blog-archive-chevron {
    font-size: 0.75rem;
    color: var(--lw-text-muted);
    transition: transform 0.15s ease-in-out;
}

.blog-archive details[open] .blog-archive-chevron {
    transform: rotate(90deg);
}

.blog-archive-year {
    font-weight: 500;
}

.blog-archive-count {
    margin-left: auto;
    font-size: 0.8125rem;
    color: var(--lw-text-muted);
}

.blog-archive ul {
    margin: 0 0 0.5rem;
    padding-left: 1rem;
    list-style: none;
}

.blog-archive li {
    padding: 0.1875rem 0;
    line-height: 1.35;
}

.blog-archive a[aria-current="page"] {
    color: var(--lw-text);
    font-weight: 500;
}

/* ==========================================================================
   7. MAP
   ========================================================================== */

/* Standort: the climate text and the map stacked on the left, the longer
   station text beside them; one column on a phone */
.location-grid {
    display: grid;
    gap: 1rem;
    align-items: start;
    grid-template-areas: "climate" "station" "map";
}

.location-climate { grid-area: climate; }
.location-station { grid-area: station; }
.location-map { grid-area: map; }

@media (width >= 768px) {
    .location-grid {
        grid-template-columns: 1fr 1fr;
        grid-template-areas: "climate station" "map station";
    }
}

#osm-map {
    height: 300px;
}

.leaflet-popup-content {
    font-family: 'Rubik', sans-serif;
    font-size: 14px;
    line-height: 1.5;
}

.popup-image {
    width: 100%;
    height: auto;
    margin-top: 6px;
    border-radius: .25rem;
}

/* ==========================================================================
   8. CARDS AND GRID
   ========================================================================== */

/* One spacing step between the cards and around the page edge */
.container-fluid {
    --bs-gutter-x: calc(2 * var(--lw-gap));
}

.temprow,
.graphrow {
    --bs-gutter-x: var(--lw-gap);
    margin-top: var(--lw-gap) !important;
    margin-bottom: 0 !important;
}

/* !important to win over the mb-4 utility on the columns */
.temprow > *,
.graphrow > * {
    margin-top: 0 !important;
    margin-bottom: var(--lw-gap) !important;
}

/* An empty full-width item that ends a row early (sun and moon on a phone) */
.temprow > .row-break {
    margin-bottom: 0 !important;
}

/* The cards of one row end level */
.temprow > * > .card {
    height: 100%;
}

.card {
    --bs-card-bg: var(--lw-surface);
    --bs-card-border-width: 0;
    --bs-card-border-radius: var(--lw-radius);
    --bs-card-inner-border-radius: var(--lw-radius);
    --bs-card-spacer-y: 0.75rem;
    --bs-card-spacer-x: 0.875rem;
    font-weight: 400;
    border: 1px solid var(--lw-border);
    box-shadow: var(--lw-shadow);
}

.card .card-body h1, .card .card-body h2, .card .card-body h3,
.card .card-body h4, .card .card-body h5, .card .card-body h6 {
    font-weight: 400;
}

/* The title of every card, the chart cards included */
.card .card-body .card-heading {
    font-size: 1rem;
    font-weight: 500;
    line-height: 1.25;
    color: var(--lw-text);
    margin-bottom: 0.25rem;
}

.card .card-body .card-text {
    font-size: 0.9rem;
    font-weight: 400;
    color: var(--lw-text-muted);
}

.card-body .richtext-image {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

/* ==========================================================================
   9. WEATHER DATA DISPLAY
   ========================================================================== */

/* A measurement card: min | current | max, with equally wide sides so the
   current value stays centred. Stacks when the card is too narrow (container
   query). Period pages use .obs-grid-even. */
.card-body:has(> .obs-grid) {
    container-type: inline-size;
}

/* Centred over the current value; the trend arrow is out of the flow, so it
   does not pull the title off centre */
.card-body:has(> .obs-grid) > .card-heading {
    text-align: center;
}

.obs-grid {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    grid-template-areas: "lo cur hi";
    align-items: center;
    column-gap: 0.5rem;
    row-gap: 0.125rem;
}

/* Past periods: three equal columns, labels and values level */
.obs-grid.obs-grid-even {
    grid-template-columns: 1fr 1fr 1fr;
    align-items: start;
}

/* Hidden until live_updates.js writes the first payload, so the units do not
   shift when the numbers arrive */
.card[data-name]:not(.is-ready) .obs-grid {
    visibility: hidden;
}

.obs-cur {
    grid-area: cur;
    text-align: center;
}

.obs-grid .lo-text,
.obs-grid .rise-text {
    grid-area: lo;
    text-align: left;
}

.obs-grid .hi-text,
.obs-grid .set-text {
    grid-area: hi;
    text-align: right;
}

.obs-grid .lo-text,
.obs-grid .hi-text,
.obs-grid .rise-text,
.obs-grid .set-text {
    white-space: nowrap;
}

/* Stacked: max before min, last hour before 24 hours, rise before set. Period
   cards keep each label above its value. */
@container (width < 17rem) {
    .obs-grid,
    .obs-grid.obs-grid-even {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas: "cur" "hi" "lo";
        align-items: center;
    }

    .obs-grid.obs-grid-even {
        row-gap: 0.5rem;
    }

    .obs-grid.obs-grid-rain,
    .obs-grid:has(> .rise-text) {
        grid-template-areas: "cur" "lo" "hi";
    }

    .obs-grid .lo-text,
    .obs-grid .hi-text,
    .obs-grid .rise-text,
    .obs-grid .set-text {
        text-align: center;
    }

    .obs-grid:not(.obs-grid-even) > :is(.lo-text, .hi-text, .rise-text, .set-text) {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        justify-content: center;
        column-gap: 0.375rem;
    }

    .obs-grid .stat-time {
        flex-basis: 100%;
    }
}

/* Scales with the card width so three hPa values fit on a 360px phone; full
   size from a 26.5rem card on */
.obs-current {
    font-size: clamp(1.25rem, 8cqi - 0.125rem, 2rem);
    font-weight: 400;
    line-height: 1.15;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.obs-grid:not(.obs-grid-even) .stat-value {
    font-size: clamp(0.875rem, 5cqi, 1rem);
}

/* The period pages' values, all one size and weight */
.obs-grid-even .obs-current,
.obs-grid-even .stat-value {
    font-size: clamp(1.0625rem, 5.5cqi, 1.5rem);
    font-weight: 400;
    line-height: 1.15;
}

@container (width < 17rem) {
    .obs-grid .obs-current {
        font-size: clamp(1.125rem, 19cqi, 2rem);
    }

    .obs-grid:not(.obs-grid-even) .stat-value {
        font-size: 1rem;
    }

    .obs-grid-even .obs-current,
    .obs-grid-even .stat-value {
        font-size: clamp(1.0625rem, 15cqi, 1.5rem);
    }
}

/* Sun and moon cards. A column, so the curve sits at the bottom when a
   taller card in the row stretches the sun card. */
.almanac-body {
    display: flex;
    flex-direction: column;
}

.almanac-body > .obs-grid {
    margin-bottom: 0.375rem;
}

/* The sun's altitude over the day (viewBox 1000 x 100, stretched) */
.sun-curve {
    position: relative;
    height: 2rem;
    margin-top: auto;
}

.sun-curve svg {
    display: block;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.sun-curve path,
.sun-curve line {
    vector-effect: non-scaling-stroke;
    fill: none;
}

.sun-curve .sun-day {
    fill: var(--lw-sun);
    fill-opacity: 0.15;
}

.sun-curve .sun-line {
    stroke: var(--lw-sun);
    stroke-width: 2px;
    stroke-linejoin: round;
}

.sun-curve .sun-night {
    stroke: var(--lw-text-muted);
    stroke-opacity: 0.45;
    stroke-width: 1.5px;
}

.sun-curve .sun-horizon {
    stroke: var(--lw-border-strong);
    stroke-width: 1px;
}

/* Where the sun is now; hollow while it is down */
.sun-dot {
    position: absolute;
    left: var(--x);
    top: var(--y);
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 50%;
    background: var(--lw-sun);
    box-shadow: 0 0 0 2px var(--lw-surface);
    transform: translate(-50%, -50%);
}

.sun-dot.is-down {
    background: var(--lw-surface);
    box-shadow: inset 0 0 0 1.5px var(--lw-text-muted), 0 0 0 2px var(--lw-surface);
}

/* Moon drawn from its exact lit share, the size of the digits beside it */
.moon-disc {
    width: 0.75em;
    height: 0.75em;
    margin-right: 0.3em;
    vertical-align: -0.04em;
}

.moon-disc .moon-shadow {
    fill: var(--lw-moon-shadow);
}

.moon-disc .moon-lit {
    fill: var(--lw-moon-lit);
}

.moon-disc .moon-edge {
    fill: none;
    stroke: var(--lw-moon-edge);
    stroke-width: 0.08;
}

/* "+1" after a moon time that falls tomorrow. Bootstrap's sup has no line
   height, so the row keeps its height when it appears. */
.next-day {
    margin-left: 0.1em;
    color: var(--lw-text-muted);
    cursor: help;
}

/* Wind direction arrow: the glyph points north and is turned downwind of
   --wind-from, the direction the wind comes from (set inline) */
.wi-wind:before {
    content: "\f0b1";
}

.wi-wind {
    transform: rotate(calc(var(--wind-from, 0deg) + 180deg));
}

.lo-text .stat-label {
    color: var(--lw-min);
}

.hi-text .stat-label {
    color: var(--lw-max);
}

.stat-label {
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--lw-text-muted);
    margin-bottom: 0;
}

.stat-value {
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

.stat-time {
    font-size: 0.8125rem;
    color: var(--lw-text-muted);
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

/* Reserve the line for the time live_updates.js fills in, to avoid layout
   shift. In a stacked tile it gets its own line: an empty box in the row
   has no baseline and would make the row taller. */
.obs-grid .stat-time {
    min-height: 1.3em;
}

/* A value carried over from an earlier record */
.obs-value.stale {
    opacity: 0.6;
    border-bottom: 1px dotted currentColor;
    cursor: help;
}

/* Out of the flow so the title does not reflow when the arrow changes */
.obs-trend {
    position: absolute;
    margin-left: 0.3em;
    color: var(--lw-text-muted);
}

/* Sized before the data arrives to avoid layout shift; 400px is the Highcharts
   default */
.chart-container {
    width: 100%;
    height: 400px;
    margin-top: 0.5rem;
}

.chart-title {
    margin-bottom: 0;
}

.chart-title a {
    color: inherit;
    text-decoration: none;
}

/* Digits of equal width, so axis ticks and tooltip rows line up */
.chart-container .highcharts-axis-labels text,
.highcharts-tooltip text {
    font-variant-numeric: tabular-nums;
}

/* Reserves the height of the table loaded after the page (--rows from the
   template). A reload keeps the previous table, dimmed. */
.table-skeleton {
    height: calc(var(--rows, 6) * (2.55rem + 1px) + 1rem);
    border-radius: calc(var(--lw-radius) / 2);
    background: linear-gradient(90deg, var(--lw-surface-2) 25%, var(--lw-surface-3) 50%, var(--lw-surface-2) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.4s ease-in-out infinite;
}

@keyframes skeleton-shimmer {
    from { background-position: 100% 0; }
    to { background-position: -100% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .table-skeleton {
        animation: none;
    }
}

.archive-table-container[aria-busy="true"] table {
    opacity: 0.5;
    transition: opacity 0.2s ease-in-out;
}

/* An observation title with an explanation: a button styled as heading text */
.obs-explained {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: inherit;
    text-align: inherit;
    white-space: normal;
    vertical-align: baseline;
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 0.22em;
    /* Fallback for browsers without color-mix() */
    text-decoration-color: currentColor;
    text-decoration-color: color-mix(in srgb, currentColor 55%, transparent);
    transition: text-decoration-color 0.15s ease-in-out;
}

/* Overrides the pointer cursor from Bootstrap's reboot */
button.obs-explained:not(:disabled) {
    cursor: help;
}

.obs-explained:hover,
.obs-explained:focus-visible {
    text-decoration-color: currentColor;
}

.obs-explained:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 3px;
    border-radius: 2px;
}

/* Readable multi-sentence body, wider than the Bootstrap default 276px */
.popover {
    --bs-popover-max-width: 320px;
    --bs-popover-body-font-size: 0.875rem;
    --bs-popover-header-font-size: 0.9rem;
}

/* ==========================================================================
   10. HEADER
   ========================================================================== */

.site-header {
    background-color: var(--lw-header-bg);
    border-bottom: 1px solid var(--lw-header-border);
    color: var(--lw-header-text);
    padding: 0.5rem 1rem;
}

.site-header .navbar-brand {
    color: var(--lw-header-text);
    font-size: 1.125rem;
    line-height: 1.3;
}

.site-header .navbar-brand strong {
    font-weight: 500;
}

.site-header .navbar-brand .font-small {
    color: var(--lw-header-text-muted);
}

.navbar-logo {
    height: 3rem;
    width: auto;
    margin-right: 0.75rem;
}

@media (width >= 992px) {
    .site-header .navbar-brand {
        min-width: 380px;
    }
}

@media (width < 1400px) {
    .site-header .navbar-brand {
        max-width: 350px;
    }
}

.site-header .navbar-collapse {
    padding-top: 0.25rem;
}

.site-header .nav-link {
    color: var(--lw-header-text-muted);
    font-size: 1.0625rem;
    padding: 0.375rem 0.75rem;
    border-radius: 0.5rem;
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out;
}

.site-header .nav-link:hover,
.site-header .nav-link:focus-visible {
    color: var(--lw-header-text);
}

.site-header .nav-item.active > .nav-link {
    color: var(--lw-header-text);
    font-weight: 500;
    background-color: var(--lw-header-active-bg);
    box-shadow: inset 0 -2px 0 var(--lw-header-active-line);
}

[data-bs-theme=dark] .site-header .nav-item.active > .nav-link {
    border-radius: 0;
}

.site-header .navbar-toggler {
    border: 0;
    color: var(--lw-header-text);
}

.site-header .navbar-toggler:focus {
    box-shadow: 0 0 0 2px var(--lw-header-text-muted);
}

.site-header .navbar-toggler-icon {
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.9)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}

.site-header .dropdown-menu {
    position: absolute !important;
    margin-top: 0;
}

@media (1200px <= width < 1400px) {
    .site-header .nav-link {
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }
}

/* ==========================================================================
   11. RANGE STRIP
   ========================================================================== */

/* Below xl: the time ranges as a strip under the brand, scrolling sideways
   if they do not fit, so switching the period never needs the menu. From md
   up the sections join the strip and the menu button goes away.
   The basis adds back the 2rem the negative margins take, so the strip
   reaches the screen edges instead of clipping 2rem short */
.range-strip {
    flex-basis: calc(100% + 2rem);
    margin: 0.5rem -1rem -0.5rem;
    padding: 0 1rem 0.5rem;
    overflow-x: auto;
    scrollbar-width: none;
}

.range-strip::-webkit-scrollbar {
    display: none;
}

.range-strip .navbar-nav {
    flex-direction: row;
    flex-wrap: nowrap;
    gap: 0.25rem;
}

.range-strip .nav-link {
    white-space: nowrap;
}

/* Phones: tighter padding, and text that grows with the screen (16px at
   360px, 18px from about 410px), so all five fill the strip without being
   cut. The open menu gets the same size, so its links line up with the strip */
@media (width < 576px) {
    .site-header .nav-link {
        font-size: clamp(1rem, 4.4vw, 1.125rem);
        padding-left: 0.5rem;
        padding-right: 0.5rem;
    }
}

/* ==========================================================================
   12. TABLES
   ========================================================================== */

table th {
    font-size: 0.9rem;
    font-weight: 500;
}

table td {
    font-size: 0.9rem;
    font-weight: 400;
}

table.table thead th {
    border-top: none;
    color: var(--lw-text-muted);
}

table.table th,
table.table td {
    padding-top: 1.1rem;
    padding-bottom: 1rem;
}

table.table-sm th,
table.table-sm td {
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
}

.archive-table-container .table {
    font-variant-numeric: tabular-nums;
}

/* The year column stays in view while a wide table scrolls sideways on a
   phone; the cells already carry the table background */
.archive-table-container .table th:first-child {
    position: sticky;
    left: 0;
    z-index: 1;
}

/* ==========================================================================
   13. FOOTER
   ========================================================================== */

footer.page-footer {
    margin-top: auto;
    flex-shrink: 0;
    padding: 1rem 0;
    border-top: 1px solid var(--lw-border);
    color: var(--lw-text-muted);
    font-size: 0.875rem;
}

footer.page-footer a {
    color: var(--lw-text-muted);
}

footer.page-footer a:hover {
    color: var(--lw-text);
}

/* ==========================================================================
   14. STATUS INDICATOR
   ========================================================================== */

.status-dot {
    --dot: #9e9e9e;
    width: 0.7em;
    height: 0.7em;
    border-radius: 50%;
    background-color: var(--dot);
    display: inline-block;
    margin-left: 0.5rem;
    margin-right: 0.5rem;
    position: relative;
    transition: background-color 0.5s ease;
}

.status-dot::after {
    content: '';
    position: absolute;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: var(--dot);
    animation: none;
    transition: background-color 0.5s ease;
}

.status-dot.green {
    --dot: var(--lw-ok);
}

.status-dot.green::after {
    animation: pulse 2s infinite;
}

.status-dot.orange {
    --dot: var(--lw-warn);
}

.status-dot.red {
    --dot: var(--lw-bad);
}

@keyframes pulse {
    0% {
        transform: scale(1);
        opacity: 1;
    }
    100% {
        transform: scale(2);
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .status-dot.green::after {
        animation: none;
    }
}

/* ==========================================================================
   15. WEBCAM
   ========================================================================== */

/* The camera's 2688x1520 (168:95, not quite 16:9), sized before the image
   loads, at most 500px tall */
.webcam-container {
    position: relative;
    width: 100%;
    max-width: calc(500px * 168 / 95);
    aspect-ratio: 168 / 95;
    margin: 0 auto 0.75rem;
    border-radius: calc(var(--lw-radius) - 0.25rem);
    background-color: #0e0f11;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #a3a9b3;
}

/* The image is the button that opens the full-screen view */
.webcam-open {
    display: block;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    background: none;
    cursor: zoom-in;
}

.webcam-open:focus-visible {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: -2px;
}

/* cover, so a sub-pixel rounding gap is cropped instead of showing the
   dark backdrop */
.webcam-container img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

#webcamModal .modal-body {
    display: flex;
    flex-direction: column;
    height: 100dvh;
    overflow: hidden;
}

#webcamModal .text-center {
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}

.webcam-modal-img {
    width: 100%;
    max-height: 100%;
    object-fit: contain;
    cursor: pointer;
}

.webcam-modal-close {
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
    z-index: 1;
    opacity: 0.9;
}

.webcam-modal-close:hover {
    opacity: 1;
}

/* The title and the time of the shown frame on one line */
.webcam-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.25rem;
}

.card .card-body .webcam-heading .card-heading {
    margin-bottom: 0;
}

.webcam-time {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    font-size: 0.875rem;
    line-height: 1.25;
    color: var(--lw-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The newest frame is older than the camera's five-minute rhythm allows */
.webcam-stale-dot {
    display: none;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background-color: var(--lw-warn);
}

.webcam-time.is-stale .webcam-stale-dot {
    display: inline-block;
}

/* Playback toolbar: round icon buttons around the time slider. The controls
   are the container, so the bar stacks where the slider would be too short
   to scrub or to carry the hour labels */
.webcam-controls {
    container-type: inline-size;
}

.webcam-bar {
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas: "buttons track latest";
    column-gap: 0.75rem;
    row-gap: 0.25rem;
    align-items: start;
}

.webcam-buttons {
    grid-area: buttons;
    display: flex;
    gap: 0.375rem;
}

.webcam-latest {
    grid-area: latest;
}

.webcam-controls .weather-btn {
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border-radius: 50%;
    flex-shrink: 0;
}

/* Padded so the slider's centre lines up with the buttons' */
.webcam-track {
    grid-area: track;
    min-width: 0;
    padding-top: 0.375rem;
}

.webcam-track .form-range {
    display: block;
}

/* Hour marks under the slider, in line with the thumb's centre (Bootstrap's
   thumb is 1rem wide); webcam.js gives each its --pos from 0 to 1 */
.webcam-ticks {
    position: relative;
    height: 1.125rem;
    margin-top: -0.125rem;
    font-size: 0.6875rem;
    line-height: 1;
    color: var(--lw-text-muted);
    font-variant-numeric: tabular-nums;
}

.webcam-tick {
    position: absolute;
    top: 0;
    left: calc(0.5rem + (100% - 1rem) * var(--pos));
    width: 1px;
    height: 0.25rem;
    background-color: currentColor;
    opacity: 0.45;
}

.webcam-tick.is-major {
    height: 0.375rem;
    opacity: 1;
}

.webcam-tick-label {
    position: absolute;
    top: 0.4375rem;
    left: 0;
    transform: translateX(-50%);
    white-space: nowrap;
}

.webcam-modal-controls {
    flex-shrink: 0;
    margin: 0.5rem 0.25rem;
}

.webcam-modal-controls .webcam-bar {
    grid-template-columns: auto auto 1fr auto;
    grid-template-areas: "time buttons track latest";
}

.webcam-modal-controls .webcam-time {
    grid-area: time;
    align-self: center;
}

.webcam-modal-controls .webcam-time,
.webcam-modal-controls .webcam-ticks {
    color: #b5bbc4;
}

/* Phones: the slider gets a row of its own under the buttons */
@container (width < 26rem) {
    .webcam-bar {
        grid-template-areas: "buttons . latest" "track track track";
    }

    .webcam-track {
        padding-top: 0;
    }
}

@container (width < 34rem) {
    .webcam-modal-controls .webcam-bar {
        grid-template-columns: auto 1fr auto auto;
        grid-template-areas: "time . buttons latest" "track track track track";
    }

    .webcam-modal-controls .webcam-track {
        padding-top: 0;
    }
}

.webcam-modal-controls .weather-btn {
    color: #e8e8e8;
    background-color: rgba(255, 255, 255, 0.1);
}

.webcam-modal-controls .weather-btn:hover {
    color: #fff;
    background-color: rgba(255, 255, 255, 0.2);
}

/* The play button holds a play and a pause icon; webcam.js sets .is-playing */
.webcam-controls .icon-pause,
.webcam-controls .is-playing .icon-play {
    display: none;
}

.webcam-controls .is-playing .icon-pause {
    display: inline-block;
}

/* ==========================================================================
   16. BUTTONS
   ========================================================================== */

/* Filter chips (archive statistics, blog pages) and the webcam's icon
   buttons; the pressed one is filled with the accent */
.weather-btn-group {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    align-items: center;
}

.weather-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;
    min-height: 2rem;
    padding: 0.3125rem 0.75rem;
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.2;
    border: 0;
    border-radius: 999px;
    background-color: var(--lw-surface-2);
    color: var(--lw-text);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out;
}

.weather-btn:hover {
    background-color: var(--lw-surface-3);
    color: var(--lw-text);
}

.weather-btn:focus-visible,
.weather-btn-search:focus-visible {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: 2px;
}

.weather-btn.active,
.weather-btn.active:hover {
    background-color: var(--lw-accent);
    color: var(--lw-on-accent);
}

.weather-btn.disabled {
    background-color: transparent;
}

/* Tonal action buttons: the archive's date pickers and period arrows, the
   newsletter */
.weather-btn-search {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 2.5rem;
    padding: 0.5rem 1rem;
    font-size: 0.9375rem;
    font-weight: 500;
    line-height: 1.25;
    border: 0;
    border-radius: 0.625rem;
    background-color: var(--lw-accent-soft);
    color: var(--lw-accent-text);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.15s ease-in-out;
}

.weather-btn-search:hover {
    background-color: var(--lw-accent-soft-hover);
    color: var(--lw-accent-text);
}


/* A row of them, e.g. the archive search */
.archive-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    justify-content: center;
}

/* The archive index: the jump forms side by side, the years below */
.archive-jump {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
    margin-top: 0.5rem;
}

.archive-years {
    margin-top: 0.75rem;
}

.archive-years .weather-btn {
    min-width: 3.5rem;
}

/* The period bar on every day, month and year page: the period between its
   timeline arrows, the level switch and the jump. One row where it fits,
   else the period on top (container query: the card is half the page wide
   on FHD screens). */
.card-body:has(> .period-bar) {
    container-type: inline-size;
}

.period-bar {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    grid-template-areas: "levels step jump";
    align-items: center;
    gap: 0.5rem;
}

.period-levels {
    grid-area: levels;
    justify-self: start;
}

.period-step {
    grid-area: step;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.period-label {
    min-width: 0;
    font-size: 1.125rem;
    font-weight: 500;
    line-height: 1.25;
    text-align: center;
}

.period-arrow {
    flex-shrink: 0;
    width: 2.5rem;
    padding: 0;
}

/* The newest or oldest period: a placeholder of the same size */
.period-arrow.disabled,
.period-arrow.disabled:hover {
    background-color: transparent;
    color: var(--lw-text-muted);
    opacity: 0.4;
    cursor: default;
}

.period-jump {
    grid-area: jump;
    justify-self: end;
    position: relative;
}

.period-jump > summary {
    list-style: none;
}

.period-jump > summary::-webkit-details-marker {
    display: none;
}

.period-jump[open] > summary {
    background-color: var(--lw-accent-soft-hover);
}

.period-jump-panel {
    position: absolute;
    top: calc(100% + 0.375rem);
    right: 0;
    z-index: 10;
    display: grid;
    gap: 0.75rem;
    width: max-content;
    max-width: calc(100vw - 2rem);
    padding: 0.75rem;
    border: 1px solid var(--lw-border-strong);
    border-radius: var(--lw-radius);
    background-color: var(--lw-surface);
    box-shadow: 0 4px 16px rgba(16, 24, 40, 0.12);
}

.period-jump-label {
    display: block;
    margin-bottom: 0.25rem;
    font-size: 0.875rem;
    color: var(--lw-text-muted);
}

/* On a phone the button wraps below the fields */
.period-jump-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.375rem;
    align-items: center;
}

.period-jump-row .form-control,
.period-jump-row .form-select {
    width: auto;
}

.period-jump-row .weather-btn-search {
    min-height: 2rem;
    padding: 0.25rem 0.75rem;
    font-size: 0.875rem;
}

@container (width < 44rem) {
    .period-bar {
        grid-template-columns: auto auto;
        grid-template-areas: "step step" "levels jump";
        row-gap: 0.625rem;
    }

    .period-step {
        justify-content: space-between;
    }

    .period-label {
        flex: 1;
    }
}

/* A phone: the level switch and the jump share a row, so the jump keeps
   only its icon (the text stays for screen readers) */
@container (width < 22rem) {
    .period-jump > summary {
        width: 2.5rem;
        padding: 0;
    }

    .period-jump-text {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* A month's days: Monday first, each tinted by its mean temperature once
   archive_nav.js has the values. The value lines keep their height while
   empty, so filling them in shifts nothing. */
.month-cal {
    width: 100%;
    table-layout: fixed;
    border-collapse: separate;
    border-spacing: 3px;
    font-variant-numeric: tabular-nums;
}

.month-cal th {
    padding: 0 0 0.125rem;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--lw-text-muted);
    text-align: center;
}

.month-cal td {
    padding: 0;
    font-size: inherit;
}

.cal-day {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.125rem 0 0.1875rem;
    border-radius: 0.375rem;
    background-color: var(--lw-surface-2);
    color: var(--lw-text);
    font-size: 0.75rem;
    line-height: 1.2;
    text-decoration: none;
}

a.cal-day:hover {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: -2px;
}

.cal-day-out {
    background-color: transparent;
    color: var(--lw-text-muted);
}

.cal-num {
    align-self: stretch;
    padding-left: 0.25rem;
    font-size: 0.6875rem;
    opacity: 0.75;
}

.cal-hi,
.cal-lo,
.cal-rain {
    min-height: 1.2em;
    white-space: nowrap;
}

.cal-hi {
    font-weight: 500;
}

/* Wide cards have room for larger numbers */
.card-body:has(> .month-cal) {
    container-type: inline-size;
}

@container (width >= 40rem) {
    .cal-day {
        padding: 0.25rem 0 0.3125rem;
        font-size: 0.875rem;
    }

    .cal-num {
        font-size: 0.75rem;
    }
}

/* A year's months */
.year-months {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 0.375rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-variant-numeric: tabular-nums;
}

@container (width >= 30rem) {
    .year-months {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

@container (width >= 44rem) {
    .year-months {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
}

.card-body:has(> .year-months) {
    container-type: inline-size;
}

.month-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.375rem 0.25rem;
    border-radius: 0.5rem;
    background-color: var(--lw-surface-2);
    color: var(--lw-text);
    font-size: 0.8125rem;
    line-height: 1.25;
    text-decoration: none;
}

a.month-tile:hover {
    outline: 2px solid var(--lw-accent-text);
    outline-offset: -2px;
}

/* Months before the first record or still to come: just the name */
.month-tile-out {
    background-color: transparent;
    color: var(--lw-text-muted);
}

.month-tile-out > :not(.month-tile-name) {
    display: none;
}

.month-tile-name {
    font-weight: 500;
}

.month-tile-temp,
.month-tile-rain {
    min-height: 1.25em;
    white-space: nowrap;
}

/* Statistics table cells link to their month; the link takes over the cell's
   padding, so the whole cell is the target. Only these: the record dates in
   the extremes table are links inside running text. */
.archive-table-container td:has(> .month-link) {
    padding: 0;
}

.archive-table-container .month-link {
    display: block;
    padding: 0.6rem 0.25rem;
    color: inherit;
    text-decoration: none;
}

.archive-table-container .month-link:hover {
    text-decoration: underline;
}

@media (width < 576px) {
    .weather-btn-group {
        justify-content: center;
    }

    .weather-btn-search {
        padding: 0.4rem 0.8rem;
        font-size: 0.875rem;
    }
}

/* ==========================================================================
   17. WIDE SCREEN LAYOUT (FHD+)
   ========================================================================== */

@media (width >= 1660px) {
    .temprow {
        display: flex;
        width: 50%;
        margin: var(--lw-gap) 0 0 0 !important;
    }

    .graphrow {
        display: flex;
        width: 50%;
        margin: var(--lw-gap) 0 0 0 !important;
    }

    .d-flex-xxl {
        display: flex !important;
    }

    .temprow .col-xl-4 {
        flex: 0 0 50%;
        max-width: 50%;
    }
}

/* ==========================================================================
   18. ERROR PAGE
   On the 404 page a tumbleweed rolls along the footer's top border
   (script in templates/404.html)
   ========================================================================== */

/* main's bottom edge is the ground; its stacking context keeps the tumbleweed
   behind the card but in front of the footer border */
.page-not-found main {
    flex: 1 0 auto;
    position: relative;
    isolation: isolate;
}

.tumbleweed-track {
    position: absolute;
    left: 0;
    right: 0;
    /* the ball's drawn edge sits ~5px inside its box: let it touch the line */
    bottom: -5px;
    height: 9rem;
    overflow: hidden;
    pointer-events: none;
    z-index: -1;
}

/* Parked just off the left edge; the script moves this one along x, hops
   .tumbleweed-hop and spins the svg */
.tumbleweed {
    position: absolute;
    bottom: 0;
    left: -56px;
    width: 56px;
    height: 56px;
    color: #8a6a3f;
}

[data-bs-theme=dark] .tumbleweed {
    color: #c9a66b;
}

.tumbleweed-hop,
.tumbleweed svg {
    display: block;
    width: 100%;
    height: 100%;
}

.tumbleweed-shadow {
    position: absolute;
    left: 20%;
    right: 20%;
    bottom: 2px;
    height: 6px;
    border-radius: 50%;
    background-color: rgba(0, 0, 0, 0.12);
}

[data-bs-theme=dark] .tumbleweed-shadow {
    background-color: rgba(0, 0, 0, 0.45);
}

@media (prefers-reduced-motion: reduce) {
    .tumbleweed-track {
        display: none;
    }
}
