@layer {
    *,
    *::after,
    *::before {
        box-sizing: border-box;
    }

    :focus-visible {
        outline-offset: 3px;
    }

    html {
        -webkit-text-size-adjust: none;
        text-size-adjust: none;

        line-height: 1.5;
        scrollbar-gutter: stable;
    }

    body{
        margin: 0;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
    }

    h1 {
        font-size: 2em;
        margin-block: 0.67em;
    }

    abbr[title] {
        cursor: help;
        text-decoration-line: underline;
        text-decoration-style: dotted;
    }

    @media (forced-colors: active) {
        mark {
            color: HighlightText;
            background-color: Highlight;
        }
    }

    audio,
    iframe,
    img,
    svg,
    video {
        max-block-size: 100%;
        max-inline-size: 100%;
    }

    fieldset {
        min-inline-size: 0;
    }

    label:has(+ input:not([type="radio"], [type="checkbox"]), select, textarea) {
        display: block;
    }

    textarea:not([rows]) {
        min-block-size: 6em;
    }

    button,
    input,
    select,
    textarea {
        font-family: inherit;
        font-size: inherit;
    }

    [type="search"] {
        -webkit-appearance: textfield;
    }

    @supports (-webkit-touch-callout: none) {
        [type="search"] {
            border: 1px solid -apple-system-secondary-label;
            background-color: canvas;
        }
    }

    input:where([type="tel"], [type="url"], [type="email"], [type="number"]):not(:placeholder-shown) {
        direction: ltr;
    }

    table {
        border-collapse: collapse;
        border: 1px solid;
    }

    th,
    td {
        border: 1px solid;
        padding: 0.25em 0.5em;
        vertical-align: top;
    }

    dialog::backdrop {
        background: oklch(0% 0 0 / 0.3);
    }

    dialog,
    [popover],
    dialog::backdrop {
        opacity: 0;
        transition: opacity 150ms ease-out, display 150ms allow-discrete,
        overlay 150ms allow-discrete;
    }

    dialog[open],
    :popover-open,
    dialog[open]::backdrop {
        opacity: 1;
    }

    @starting-style {
        dialog[open],
        :popover-open,
        dialog[open]::backdrop {
            opacity: 0;
        }
    }

    [hidden]:not([hidden="until-found"]) {
        display: none !important;
    }

    /* Sprungziele unter dem klebenden Header hervorholen; gb.js misst dessen Höhe. */
    :root{
        scroll-padding-block-start: calc(var(--header-height, 0px) + 1rem);
    }

    /* Set by gb.js while the mobile navigation overlay is open. */
    body.nav-open{
        overflow: hidden;
    }

    img {
        display: block;
    }

    summary {
        cursor: default;
    }

    iframe {
        border: none;
    }

    @supports(position-area: block-end span-inline-end) {
        [popover] {
            margin: 0;
            position-area: block-end span-inline-end;
        }
    }

    h1, h2, h3, h4, h5, h6{ font-weight: normal; }
    h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd{  margin: 0; padding: 0; }

    h1, h2, h3, h4, h5, h6{
        text-wrap: balance;

        /* Enger als die 1.3 des Fließtexts: Große Schriftgrade brauchen weniger
           Durchschuss, sonst zerfällt eine umbrechende Überschrift in einzelne
           Zeilen. Die Seitentitel setzen mit `.font-size-xl` noch enger. */
        line-height: 1.15;

        /* Deutsche Komposita sind bei 12vw breiter als eine Smartphone-Zeile.
           Getrennt wird nach dem lang-Attribut der jeweiligen Site. */
        hyphens: auto;
        overflow-wrap: break-word;
    }

    /* `pretty` statt `balance`: wirkt auch bei langem Text noch. */
    p{ text-wrap: pretty; }
}

@layer base {
    @font-face {
        font-family: "Valore";
        src: url("/assets/webfonts/valore-pro/ValorePro-Regular.woff2") format("woff2");
        font-weight: normal;
        font-style: normal;
        font-display: swap;
    }
    @font-face {
        font-family: "Valore";
        src: url("/assets/webfonts/valore-pro/ValorePro-Bold.woff2") format("woff2");
        font-weight: 700;
        font-style: normal;
        font-display: swap;
    }

    @font-face {
        font-family: 'SofiaPro';
        src: url('/assets/webfonts/sofia-pro-regular.woff2') format('woff2');
        font-weight: normal;
        font-style: normal;
        font-display: swap;
    }

    @font-face {
        font-family: 'SofiaPro';
        src: url('/assets/webfonts/sofia-pro-bold.woff2') format('woff2');
        font-weight: bold;
        font-style: normal;
        font-display: swap;
    }

    html{
        /* Ohne dies malen Browser im Dunkelmodus eine schwarze Fläche. */
        color-scheme: light;

        --color-anthrazit: rgb(42,38,37);
        --color-greige: rgb(217,212,204);
        --color-taupe: rgb(88,80,72);
        /* Das eine Grün des Hauses: Flächen (Signature Touches, Apartment-Karten,
           Kontaktformular) und Zustände (freie Tage im Kalender, Erfolgsmeldung)
           teilen es sich.

           Kurzzeitig standen hier zwei Töne nebeneinander, 27° im Farbton
           auseinander — das las sich als Versehen, nicht als Unterscheidung.
           Die Zustände im Kalender halten ihren Unterschied ohnehin nicht über
           die Farbe allein: Belegt trägt eine Schraffur und ein verstecktes
           Wort, der gewählte Tag ist dunkles Taupe mit weißer Schrift. */
        --color-salbei: rgb(164, 193, 175);


        /* Nur für Formularfehler. 6.25:1 auf Greige, 9.22:1 auf Weiß. */
        --color-error: rgb(122,48,38);
        --color-button: var( --color-taupe );

        --padding-side: clamp( 1rem, 4vw, 4rem);

        /* Abstand zwischen den Abschnitten einer Seite — eine Quelle statt
           eines margin-block je Abschnitt. */
        --section-gap: clamp( 3rem, 6vw, 5rem );

        /* Lead type: the step between headings (5rem) and body copy (1rem).
           Without it a 16px paragraph sits directly under an 80px line. */
        --font-size-lead: clamp( 1.125rem, 1.4vw, 1.375rem );
        --line-height-lead: 1.55;

        /* 1ch ist die Breite von „0", nicht die eines Durchschnittszeichens. */
        --measure-lead: 70ch;

        /* Fließtext auf Inhaltsseiten. `1ch` ist die Breite von „0" und damit
           breiter als ein Durchschnittszeichen — 64ch entsprechen hier rund 82
           Zeichen. Das liegt über der klassischen Empfehlung von 45 bis 75,
           passt aber besser zur Spaltenbreite der übrigen Seiten. */
        --measure-body: 64ch;

        /* — Serif-Stufen ——————————————————————————————————————
           Valore hat ~50% Versalhöhe, SofiaPro ~69%. Daher die Faktoren gegen
           die Grundschrift: Gleiche Punktgröße wirkt bei Valore kleiner. */
        --font-serif-1: clamp(2.5rem, 12vw, 5rem);       /* Seitentitel   ~3,6× */
        --font-serif-2: clamp(2.25rem, 3.8vw, 3.3rem);   /* Abschnitt     ~2,4× */
        --font-serif-3: clamp(2rem, 3.2vw, 2.55rem);     /* Thema/Element ~1,85× */
        --font-serif-4: clamp(1.5rem, 2.2vw, 2rem);      /* Karte/Dialog  ~1,45× */

        /* Deckel für die Bühnenhöhe, damit der nächste Abschnitt hereinschaut.
           svh, weil die Browserleiste am Telefon sonst genau das verdeckt. */
        --hero-max-block: calc(100svh - var(--header-height, 0px) - 5rem);

        /* Im Hochformat ist ein 16:9-Streifen nur ein Splitter des Bildes. */
        --hero-ratio: 16 / 9;

        /* Im Hochformat ist ein 16:9-Streifen nur ein Splitter des Bildes,
           4:5 nahm am Telefon aber 58 % des Bildschirms — man sah vom Inhalt
           darunter nichts. Das Quadrat ist der Ausgleich: 46 % statt 58 %, und
           der Ausschnitt bleibt hochformatig genug für ein Motiv. */
        @media (orientation: portrait){
            --hero-ratio: 1 / 1;
        }

        /* Flatter on desktop: otherwise the stage fills over 80% of the viewport
           and the next heading lands exactly on the fold. */
        @media (width >= 64rem){
            --hero-max-block: min(62svh, calc(100svh - var(--header-height, 0px) - 5rem));
        }

        --serif-font: 'Valore', sans-serif;
        --sans-serif-font: 'SofiaPro', sans-serif;
    }

    body{
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;

        background-color: #fff;
        color: var( --color-anthrazit );

        font-family: var(--sans-serif-font);
        line-height: 1.3;
    }

    a {
        cursor: pointer;
        text-underline-offset: 0.25em;
        transition: 0.2s ease-out;

        &:link, &:visited, &:hover, &:active{
            color: inherit;
        }
    }

    button{
        font: inherit;
        border: none;
        border-radius: 0;
        background-color: transparent;
        padding: 0;
        cursor: pointer;
    }

    #skip-to-content-link {
        position: fixed;
        top: 0;

        background: var( --color-anthrazit );
        color: white;
        left: 50%;
        padding: 8px;

        transform: translate(-50%, -150%);
        transition: transform 0.3s;
        z-index: 100;

        border-bottom-left-radius: 3px;
        border-bottom-right-radius: 3px;

        &:focus {
            transform: translate(-50%, 0);
        }
    }

    .visually-hidden:not(:focus):not(:active) {
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
        height: 1px;
        overflow: hidden;
        position: absolute;
        white-space: nowrap;
        width: 1px;
    }

    figure{
        margin: 0;
    }

    img, video{
        width: 100%;
        height: auto;
    }
}

@layer utilities {
    .text-left{ text-align: left; }
    .text-center{ text-align: center; }
    .text-right{ text-align: right; }

    .serif-font{ font-family: var( --serif-font ); }
    .sans-serif-font{ font-family: var( --sans-serif-font ); }

    .uppercase{text-transform: uppercase;}

    .tracking-120{ letter-spacing: 0.12em; }

    /* Also applied to the apartment intro headline, which comes from a CK field
       and so can't carry the class itself. */
    .font-size-xl,
    .single-apartment .intro__text h2{ font-size: var( --font-serif-1 ); line-height: .8; }

    .balance{ text-wrap: balance; }
    .hyphens{ hyphens: auto; }

    .content-width{
        max-width: 75rem;
        margin-inline: auto;
        padding-inline: var( --padding-side );
    }

    /* role="list", weil Safari mit `list-style: none` die Listensemantik
       aus VoiceOver nimmt. */
    ul[role="list"],
    ol[role="list"]{
        list-style: none;
        margin: 0;
        padding: 0;
    }

    /* Nicht aber, wenn die Liste zugleich der Seitencontainer ist: Der
       Attributselektor oben wiegt schwerer als `.content-width`, die Liste
       verlor sonst Innenabstand und Zentrierung. */
    ul[role="list"].content-width,
    ol[role="list"].content-width{
        margin-inline: auto;
        padding-inline: var( --padding-side );
    }

    /* Vorspann-Typografie. Utility statt Bindung an eine Body-Klasse, damit
       sie auf neuen Seiten gilt. */
    .lead{
        max-inline-size: var( --measure-lead );
        font-size: var( --font-size-lead );
        line-height: var( --line-height-lead );
    }

    .lead.text-center{
        margin-inline: auto;
    }
}

@layer gb{

    /* Abstände der Abschnitte an einer Stelle; neue Abschnitte erben sie. */
    main{
        padding-block-end: var( --section-gap );
    }

    main > * + *{
        margin-block-start: var( --section-gap );
    }

    /* The first section clears the header — except full-bleed stages, which
       deliberately sit flush against it. */
    main > :first-child:not(.slider, .hero-video, .overview-header, .lp-hero, .gallery-wrap){
        margin-block-start: var( --section-gap );
    }

    /* Stages stay flush even when a visually-hidden h1 precedes them (home
       page): the sibling rule above would otherwise hand them the full gap. */
    main > :is(.slider, .hero-video, .overview-header, .lp-hero, .gallery-wrap){
        margin-block-start: 0;
    }

    /* Zentrierte Meldungsseiten. Der Reset nimmt Überschriften und Absätzen
       die Abstände; 2rem entspricht dem eigenen Rand von `.lp-button`. */
    .message > * + *{
        margin-block-start: 2rem;
    }

    #main-header{

        padding-block: 2rem;
        background-color: var( --color-greige );
        position: relative;

        /* Eigener Stapelkontext: Sonst malen Slider-Pfeile und Pagination
           durch das Navigations-Overlay. */
        z-index: 10;

        position: sticky;
        inset-block-start: 0;

        /* Wegwischen nur ab Tablet — am Telefon ist der Header der Anker des
           Overlays und bleibt stehen. gb.js setzt die Klasse. */
        @media (width >= 75rem){
            transition: transform 0.3s ease;

            &.is-hidden{
                transform: translateY(-100%);
            }

            @media (prefers-reduced-motion: reduce){
                transition-duration: 0s;
            }
        }

        /* Breiter als das Textraster: Sechs Punkte plus Sprachumschalter und
           Logo brauchen mehr als dessen 75rem.

           Unterhalb von 75rem Fensterbreite passen sie auch so nicht mehr in
           eine Zeile — gemessen bricht „ÜBER UNS" ab etwa 1130px um. Statt die
           Schrift zu verkleinern, übernimmt dort das Burger-Menü. Die Grenze
           steht deshalb an drei Stellen unten und zweimal in gb.js auf 75rem;
           werden die Beschriftungen länger, muss sie mitwandern. */
        .content-width{
            max-width: 90rem;

            display: grid;
            grid-template-columns: 1fr auto 1fr;
            align-items: center;
            gap: 2rem;
        }

        /* Spans the full row, but never places anything in the middle track,
           so the logo sits in the gap it leaves. */
        nav {
            grid-area: 1 / 1 / 1 / -1;
            display: grid;
            grid-template-columns: subgrid;
            align-items: center;
        }

        nav > ul {
            grid-row: 1;
            display: flex;

            /* Richtet die CTA-Pille an den Textlinks aus. */
            align-items: center;

            gap: 2rem;
            list-style: none;
            margin: 0;
            padding: 0;
            justify-content: space-evenly;
        }

        nav > ul:nth-of-type(1) {
            grid-column: 1;
        }

        nav > ul:nth-of-type(2) {
            grid-column: 3;
        }

        .logo {
            grid-area: 1 / 2;
            z-index: 1;

            img{
                height: 3rem;
                width: auto;
            }
        }

        /* Der Knopf existiert nur unter 75rem. Auf 44px Tap-Ziel gebracht,
           das die Balken allein verfehlen. */
        .nav-toggle{
            display: none;
            grid-area: 1 / 3;
            justify-self: end;
            z-index: 1;
            min-inline-size: 2.75rem;
            min-block-size: 2.75rem;
            place-items: center;
        }

        /* Drei Balken aus einem Element: Der mittlere ist der Kasten selbst, die
           äußeren sind seine Pseudo-Elemente. Der mittlere verschwindet über
           `background`, nicht `opacity` — das erbten die anderen beiden. */
        .nav-toggle__bars,
        .nav-toggle__bars::before,
        .nav-toggle__bars::after{
            display: block;
            inline-size: 1.5rem;
            block-size: 1px;
            background: currentColor;
            transition: transform 0.3s ease, background-color 0.2s ease;
        }

        .nav-toggle__bars{
            position: relative;
        }

        .nav-toggle__bars::before,
        .nav-toggle__bars::after{
            content: "";
            position: absolute;
            inset-inline: 0;
            transform: translateY(-0.5rem);
        }

        .nav-toggle__bars::after{
            transform: translateY(0.5rem);
        }

        .nav-toggle[aria-expanded="true"] .nav-toggle__bars{
            background: transparent;
        }

        .nav-toggle[aria-expanded="true"] .nav-toggle__bars::before{
            transform: rotate(45deg);
        }

        .nav-toggle[aria-expanded="true"] .nav-toggle__bars::after{
            transform: rotate(-45deg);
        }

        @media (width < 75rem) {
            padding-block: 1rem;

            /* Kleinerer Abstand, damit beide Außenspalten breiter als der Knopf
               bleiben und das Logo mittig sitzt. Kein Zeilenabstand: Die
               eingeklappte Navigation behielte sonst ihre Rasterzeile. */
            .content-width{ gap: 0 1rem; }

            .logo img{ height: 2.2rem; }

            .nav-toggle{ display: grid; }

            /* Overlay statt Panel; gb.js sperrt derweil das Scrollen der Seite.
               `visibility` nimmt die Links aus dem Tabulator, verzögert bis
               die Blende durch ist. */
            nav{
                position: fixed;
                inset: 0;
                z-index: 1;

                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;

                /* Gleicher Abstand wie innerhalb einer Liste — an der Naht der beiden
                   Listen wirkte ein größerer ungleichmäßig. */
                gap: 1.5rem;

                background: var( --color-greige );

                opacity: 0;
                visibility: hidden;
                transition: opacity 0.25s ease, visibility 0s 0.25s;
            }

            nav.is-open{
                opacity: 1;
                visibility: visible;
                transition-delay: 0s;
            }

            nav ul {
                grid-column: auto;
                flex-direction: column;
                align-items: center;
                gap: 1.5rem;
            }

            /* Room to read and to hit: well past the 44px tap target. */
            nav a{
                font-size: 1.375rem;
            }

            /* Knopf und Logo über dem Overlay: Der Header soll stehenbleiben,
               statt ein einzelnes X zu hinterlassen. */
            .nav-toggle,
            .logo{
                position: relative;
                z-index: 2;
            }

            /* Abgesetzt, weil die Sprachwahl keine Seite ist. */
            .nav-language{
                margin-block-start: 1rem;
            }

            .language-switcher{
                flex-direction: row;
                gap: 1.5rem;
            }

            /* Buchen als umrandeter Knopf — im Overlay ist er die einzige
               Handlungsaufforderung zwischen lauter gleich aussehenden Links. */
            .nav-cta a{
                display: inline-block;
                padding: 0.5em 1.5em;
                border: 1px solid currentColor;
                border-radius: 999px;
            }

            @media (prefers-reduced-motion: reduce){
                nav,
                .nav-toggle__bars,
                .nav-toggle__bars::before,
                .nav-toggle__bars::after{
                    transition-duration: 0s;
                }
            }
        }
    }

    /* Sprachhinweis: taupe statt greige, damit er nicht als Teil des Headers
       gelesen wird. */
    .language-hint{
        display: flex;
        background-color: var( --color-taupe );
        color: var( --color-greige );
        padding-block: 0.75rem;
        font-size: 0.9375rem;

        .language-hint__inner{
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            justify-content: center;
            gap: 0.5rem 1.5rem;
        }

        .language-hint__text{
            margin: 0;
        }

        .language-hint__actions{
            display: flex;
            flex-wrap: wrap;
            align-items: baseline;
            gap: 0.5rem 1.5rem;
        }

        /* Both are actions, so both look like one — the button carries no
           border of its own and would otherwise read as body text. */
        .language-hint__switch,
        .language-hint__dismiss{
            font: inherit;
            color: inherit;
            padding: 0;
            border: 0;
            background: none;
            text-decoration: underline;
            text-underline-offset: 0.25em;
            cursor: pointer;
        }

        .language-hint__dismiss{
            opacity: 0.75;
        }

        .language-hint__switch:hover,
        .language-hint__dismiss:hover{
            text-decoration-thickness: 2px;
            opacity: 1;
        }

        /* Stacked on a phone: side by side the sentence and both actions wrap
           into a ragged block that is harder to scan than three centred rows. */
        @media (width < 48rem){
            text-align: center;

            .language-hint__inner{
                flex-direction: column;

                /* Grundlinie gilt nur für die einreihige Anordnung; in Spaltenrichtung
           würde sie alles gegen die linke Kante drücken. */
                align-items: center;
                gap: 0.5rem;
            }
        }
    }

    .masonry{
        --masonry-gap: 2rem;

        display: grid;
        grid-template-columns: 1fr;
        gap: var( --masonry-gap );
        align-items: start;

        /* Native masonry. Safari 26.4+ ships `grid-lanes`; earlier WebKit builds
           used `masonry`, so declare both and let the valid one win. */
        display: masonry;
        display: grid-lanes;

        @media (width >= 48rem) {
            grid-template-columns: 1fr 1fr;
        }

        .textTile{
            text-wrap: balance;
        }
    }

    /* Set by gb.js only when the browser has neither native value. */
    .masonry.is-polyfilled{
        grid-auto-rows: 1px;
        row-gap: 0;
    }

    .tile{
        figure{
            margin: 0;
        }

        picture{
            display: block;
        }

        img{
            width: 100%;
            height: auto;
        }

        /* Zentriert, bis gb.js die Spalte kennt (`alignPortraits`). Ohne JS
           bleibt alles zentriert — die unauffällige Variante. */
        img.portrait{
            width: 80%;
            margin-inline: auto;
        }

        /* Lean towards the middle of the grid. */
        &[data-align="end"] img.portrait{
            margin-inline: auto 0;
        }

        &[data-align="start"] img.portrait{
            margin-inline: 0 auto;
        }

        .headline{
            font-family: "Valore";
        }

        /* Einspaltig: Über die volle Breite gestapelt lasen sich die Kacheln als
       ein langer Streifen, deshalb wechseln die Bilder die Seite. */
        @media (width < 48rem){
            img{
                inline-size: 85%;
            }

            img.portrait{
                inline-size: 72%;
            }
        }
    }

    @media (width < 48rem){
        /* Die Seite entscheidet gb.js: Ein `nth-` kann nicht nur Bildkacheln
           zählen und die Textkacheln überspringen. */
        .masonry > .tile[data-align="start"] img{
            margin-inline: 0 auto;
        }

        .masonry > .tile[data-align="end"] img{
            margin-inline: auto 0;
        }

        /* The opening image, and any tile the script leaves undecided. */
        .masonry > .tile[data-align="centre"] img{
            margin-inline: auto;
        }

        .masonry > .textTile{
            text-align: center;
        }
    }

    #main-footer{
        color: var( --color-greige );
        background-color: var( --color-taupe );
        padding-block: 3rem;


        /* Vier Spalten am Desktop: Signet, Anschrift, Social Media, Rechtliches.
           Untereinander brauchte der Fuß über 600px Höhe für sechs kurze
           Blöcke.

           `auto` für das Signet, damit es nur seine Breite nimmt; der Rest
           teilt sich gleichmäßig. */
        /* Die ersten drei Spalten nehmen ihre Inhaltsbreite, die vierte den
           Rest — Anschrift und Icon-Reihe brauchen wenig, die Liste rechts
           dagegen zwei Spalten nebeneinander.

           Raster und nicht Flex: Beides könnte das, aber im Raster ist der
           Umbruch am Telefon eine Zeile (`1fr`) statt vier Eigenschaften.

           `center` statt `start`: Die drei sind unterschiedlich hoch — oben
           ausgerichtet lag ihre Mitte jeweils woanders. */
        .content-width{
            display: grid;
            grid-template-columns: repeat(4, auto);
            justify-content: space-between;
            align-items: center;
            gap: 2rem 3rem;
            text-align: start;
        }

        img.signet{
            display: block;
            width: 5rem;
            margin-inline: 0;
        }

        /* Am Telefon zwei Zeilen statt vier Blöcken: Signet und Anschrift
           teilen sich die erste, Social Media und Rechtliches stehen darunter
           über die volle Breite. */
        @media (width < 56rem){
            /* Fluss statt Raster. Im Raster bestimmte die breiteste Zeile die
               Spalten — die Rechtslinks spannen über beide und zogen die erste
               Spalte auf 100px auf, sodass das Signet darin zwar mittig saß,
               das Paar aus Signet und Anschrift aber links klebte. Im Fluss
               bekommen beide ihre eigene Breite und werden gemeinsam
               zentriert. */
            .content-width{
                display: flex;
                flex-wrap: wrap;
                align-items: center;
                justify-content: center;
                gap: 1.75rem 1.5rem;
                text-align: center;
            }

            img.signet{
                margin-inline: 0;
            }

            /* Die Anschrift links ausgerichtet neben dem Signet — mittig
               wirkte sie gegenüber dem Zeichen verrutscht. */
            .address{
                text-align: start;
            }

            /* Beide in eigene Zeilen, über die volle Breite */
            .contact__social,
            nav{
                flex-basis: 100%;
            }
        }

        /* Zwei Spalten zu je zwei Punkten: Vier Zeilen untereinander wären die
           höchste Spalte im Fuß, vier nebeneinander passten in eine
           Viertelbreite nicht. */
        nav{
            ul{
                display: grid;
                grid-template-columns: repeat(2, max-content);
                gap: 0.5em 2rem;
                list-style: none;
            }
        }

        @media (width < 56rem){
            /* Am Telefon fließend statt im Raster: Die vier Punkte sind
               unterschiedlich lang, feste Spalten ließen die Hälfte umbrechen. */
            nav ul{
                display: flex;
                flex-wrap: wrap;
                justify-content: center;
                gap: 0.75em 2em;
            }
        }

        .address{
            margin-block: 0;
        }

        /* "Cookie settings" — reopens the consent dialog. Sieht aus wie die
           Links daneben; dass es ein Knopf ist, sagt die Rolle, nicht die Form. */
        .footer-cookie-settings{
            display: block;
            margin-inline: 0;
            text-align: start;
            padding: 0;
            border: none;
            background: none;
            color: inherit;

            /* Ohne eigene Größe und Deckkraft: Der Punkt steht in derselben
               Liste wie die Rechtslinks und soll sich nicht davon abheben.
               Vorher 0,75rem gegen deren 1rem. */
            font: inherit;
            cursor: pointer;
        }

        .footer-cookie-settings:hover{
            text-decoration: underline;
        }

        .language-switcher{
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 0.5rem;
            margin: 0;
            padding: 0;
            font-size: 0.8rem;

            li{
                display: inline-flex;
                align-items: center;
                gap: 0.5rem;
            }

            li + li::before{
                content: "/";
                opacity: 0.4;
            }

            a{
                text-decoration-color: transparent;
                padding-block: 0.25rem;
            }

            a[aria-current],
            a:hover{
                text-decoration-color: currentColor;
            }
        }
    }


    /* — Apartment entry ——————————————————————————————— */

    /* Video-Bühne der Startseite. Maße wie beim Slider, damit der Wechsel am
       Seitenaufbau nichts ändert. */
    .hero-video{
        position: relative;
        width: 100%;
        aspect-ratio: var( --hero-ratio );
        max-block-size: var( --hero-max-block );

        /* Bis das Video lädt, steht hier dieselbe Fläche wie beim Kartenrahmen
           — kein weißes Loch über der ganzen Bühne. */
        background: var( --color-greige );
    }

    .hero-video__media{
        inline-size: 100%;
        block-size: 100%;
        object-fit: cover;
        display: block;
    }

    /* Gleiche Pille wie beim Slider, damit beide Bühnen sich gleich bedienen. */
    .hero-video__controls{
        position: absolute;
        z-index: 1;
        inset-block-end: 1rem;
        inset-inline-start: 50%;
        transform: translateX(-50%);

        display: flex;
        align-items: center;
        padding: 0.35rem 0.6rem;
        border-radius: 999px;
        background: rgb(0 0 0 / 0.3);
        color: #fff;
    }

    .hero-video__toggle{
        display: flex;
        align-items: center;
        justify-content: center;
        inline-size: 1.5rem;
        block-size: 1.5rem;
        border: 0;
        background: none;
        color: inherit;
        cursor: pointer;

        svg{
            inline-size: 0.85rem;
            block-size: 0.85rem;
        }
    }

    /* Es ist immer genau ein Symbol zu sehen — welches, sagt aria-pressed. */
    .hero-video__toggle .hero-video__play{ display: none; }
    .hero-video__toggle[aria-pressed="true"] .hero-video__pause{ display: none; }
    .hero-video__toggle[aria-pressed="true"] .hero-video__play{ display: block; }

    /* Header slider — used on the homepage and apartment detail pages. */
    .slider{
        /* Randlos unter dem Header, gedeckelt damit der nächste Abschnitt
       hereinschaut. svh gegen die Browserleiste am Telefon. */
        width: 100%;
        aspect-ratio: var( --hero-ratio );
        max-block-size: var( --hero-max-block );

        /* Weiße Pfeile nah am Rand; das 44px-Tap-Ziel kommt aus dem Pseudo-
           Element, nicht aus der sichtbaren Fläche. */
        --swiper-navigation-color: #fff;
        --swiper-navigation-sides-offset: 1rem;

        .swiper-slide{
            figure, picture{
                display: block;
                block-size: 100%;
            }
        }
    }

    .single-apartment{
        .highlights{
            background-color: var( --color-taupe );
            color: var( --color-greige );
            padding-block: 1.5rem;

            /* Zentriert statt in gleich breite Spalten geteilt: Mit vier
               Angaben füllten Spalten die Leiste, mit zweien standen sie an den
               äußeren Rändern und die Trennlinie einsam in der Mitte. Flex legt
               die Angaben nebeneinander in die Mitte, gleich wie viele es
               sind. */
            dl{
                display: flex;
                flex-wrap: wrap;
                justify-content: center;
                margin: 0 auto;
            }

            /* Native CSS nesting doesn't concatenate like Sass, so `&__item`
               would match nothing — write the class out. */
            .highlights__item{
                padding-block: 0.5rem;
                padding-inline: clamp(1.5rem, 4vw, 3rem);
                text-align: center;

                dt{ opacity: 0.7; }
                dd{ margin-block-start: 0.35rem; }
            }

            /* Nicht als `& + &` innerhalb der Regel darüber geschrieben: Die
               Verschachtelung machte daraus `:is(…) + :is(…)` und damit eine
               höhere Spezifität als die Ausnahme im Medienblock unten — die
               Trennlinie blieb dann auch dort stehen, wo sie weg sollte. */
            .highlights__item + .highlights__item{
                border-inline-start: 1px solid currentColor;
            }

            /* Am Telefon zwei je Zeile. Die Trennlinie gehört dann nur zwischen
               die beiden einer Zeile, nicht vor die erste — und beim Umbruch
               fällt sie sonst an den linken Rand. */
            @media (width < 48rem){
                .highlights__item{
                    flex: 1 1 40%;
                    padding-inline: 1rem;
                }

                .highlights__item:nth-child(odd){ border-inline-start: none; }
            }
        }

        .intro{

            .intro__text{

                /* The visible hero heading (comes from the description field).
                   Size/line-height come from the shared .font-size-xl rule. */
                h2{
                    font-family: var( --serif-font );
                    margin-block-end: 2rem;
                }

                /* Paragraphs only: the heading lives in the same CK field and
                   keeps its own size. */
                p{
                    max-inline-size: var( --measure-lead );
                    font-size: var( --font-size-lead );
                    line-height: var( --line-height-lead );
                }
            }
        }

    }

    /* — Apartment overview ————————————————————————————— */

    /* Shared by the apartments overview and the about page. */
    /* Media fill their container. Shared by slider, stage and discover tiles. */
    .slider .swiper-slide :is(img, video),
    .overview-header :is(img, video),
    .discover-tile__media :is(img, video){
        inline-size: 100%;
        block-size: 100%;
        object-fit: cover;
    }

    .overview-header{
        /* Full-bleed hero, same cap and ratio as the single-apartment slider. */
        position: relative;
        width: 100%;
        aspect-ratio: var( --hero-ratio );
        max-block-size: var( --hero-max-block );

        /* Sicherheitsnetz: nichts kann in den folgenden Text ragen. */
        overflow: hidden;

        /* Absolut positioniert statt über prozentuale Höhen: Safari hält eine
           aus `aspect-ratio` abgeleitete Höhe nicht für definit, `inset: 0`
           braucht keine. */
        figure,
        picture{
            display: block;
            position: absolute;
            inset: 0;
            margin: 0;
        }

    }

    .overview-intro{
        text-align: center;
    }

    /* Sichtbarer Titel der Apartment-Detailseite. Steht eine Beschreibung
       darunter, braucht die etwas Luft. */
    .apartment-title + .intro__text{
        margin-block-start: 1.5rem;
    }

    /* Willkommens-Überschrift der Startseite: ungetrennt. */
    .start .overview-intro h2{
        hyphens: none;
    }

    /* Größe, Durchschuss und Zeilenmaß kommen von `.lead`; hier nur der
       Abstand. */
    .overview-intro__text{
        margin-inline: auto;
        margin-block-start: 1.5rem;
    }

    .apartments-overview,
    .about-page{
        .apartment-list{
            display: grid;
            gap: 1.5rem;
        }

        .apartment-card{
            img{
                aspect-ratio: 3 / 2;
                object-fit: cover;
            }

            /* Fester Wert statt der fluiden Serif-Stufe: In einer Liste
               gleichartiger Karten soll die Größe nicht mit der Fensterbreite
               wandern. */
            .apartment-card__title{
                /* Größer als die übrigen Kartentitel: Auf der Übersicht ist der
                   Name das Einzige, was auf der Karte steht — er darf die
                   Fläche auch füllen. Fluid, weil die Karten über die volle
                   Breite laufen. */
                font-size: clamp(1.9rem, 3.4vw, 2.75rem);
                margin-block-end: 0.75rem;
                text-align: center;

                /* Solange keine Eckdaten darunter stehen, ist der Titel der
                   letzte Inhalt der Karte — sein Abstand trüge dann nur
                   Leerraum unter die Klickfläche. Über :last-child statt über
                   einen festen Wert: Sobald die Angaben zurückkommen, greift
                   der Abstand von selbst wieder. */
                &:last-child{
                    margin-block-end: 0;
                }
            }

            /* Rundum derselbe Innenabstand. Vorher war er seitlich 0,75rem und
               oben wie unten 1rem — die Klickfläche zog über die Karte hinaus,
               was bei durchsichtigem Grund niemand sah. Mit dem hellen Grundton
               ist die Karte ihre eigene Form, die Klickfläche also genau sie. */
            a{
                display: grid;
                grid-template-columns: minmax(0, 1fr);
                gap: 1.5rem;
                align-items: start;
                padding: 1.25rem;
                background-color: var( --color-salbei );
                text-decoration: none;
                transition: background-color 0.2s ease-out, color 0.2s ease-out;

                &:hover,
                &:focus{
                    background-color: var( --color-taupe );
                    color: var( --color-greige );
                }
            }

            /* Die Bildspalte nur, wenn die Karte ein Bild hat. Solange keines
               gepflegt ist, stand der Text in einer 18rem schmalen ersten
               Spalte und daneben der Rest der Karte leer — der Name saß
               dadurch weit links statt in der Mitte.

               Über `:has` statt über eine Klasse im Template: Sobald die
               Bilder da sind, kommt die zweispaltige Form von selbst zurück.
               Und ausdrücklich erst ab zwei Spalten — `a:has(img)` wiegt
               schwerer als `a`, eine Abfrage weiter unten könnte es sonst
               nicht mehr überschreiben.

               Oben ausgerichtet, aber nicht bündig: Das Bild hat eine harte
               Kante, die Schrift ihre Oberlänge. */
            @media (width >= 48rem){
                a:has(img){
                    grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);

                    .apartment-card__body{
                        padding-block-start: 0.5rem;
                    }
                }
            }

            /* Zentriert unter dem Titel, der ebenfalls mittig steht. Als
               Spaltenraster füllten zwei Angaben die ganze Kartenbreite und
               standen weit auseinander an ihren Rändern. */
            .apartment-card__highlights{
                display: flex;
                flex-wrap: wrap;
                justify-content: center;
                text-align: center;
                gap: 0.75rem 2.5rem;

                dt{
                    opacity: 0.7;
                    font-size: 0.8rem;
                }

                dd{
                    margin-block-start: 0.15rem;
                }
            }

        }
    }

    nav{
        a{
            text-decoration-color: transparent;


            &:hover, &:active, &:focus, &[aria-current] {
                text-decoration-color: currentColor;
            }
        }
    }

    /* Sprachumschalter im Kopf. Er steht als letzter Punkt in der rechten
       Liste, hier bekommt er nur seine eigene Innenform. */
    #main-nav .language-switcher{
        display: flex;
        align-items: center;
        gap: 0.5rem;
        list-style: none;
        margin: 0;
        padding: 0;

        li{
            display: inline-flex;
            align-items: center;
            gap: 0.5rem;
        }

        li + li::before{
            content: "/";
            opacity: 0.4;
        }

        a{
            text-decoration-color: transparent;
        }

        a[aria-current],
        a:hover{
            text-decoration-color: currentColor;
        }
    }

    /* Am Telefon schob der volle Abschnittsabstand das Formular unter die
       Falz. */
    @media (width < 48rem){
        .home-booking{
            margin-block-start: 1.5rem;
        }
    }

    /* Gefüllte Schaltflächen — ein Aussehen, zwei Orte.

       Vorher standen „Verfügbarkeit prüfen" und „Jetzt buchen" als getrennte
       Regeln nebeneinander und sind auseinandergelaufen: verschiedene
       Schriftfarben, und nur eine der beiden hatte überhaupt einen Hover.
       Was sich unterscheiden darf, ist der Innenabstand — der Knopf im
       Formular richtet sich nach der Höhe der Felder daneben. */
    .booking-form__submit,
    .booking-card__cta{
        font: inherit;
        letter-spacing: inherit;
        cursor: pointer;
        border: 1px solid transparent;
        background: var( --color-button );
        color: #fff;
        text-decoration: none;
        transition: opacity 0.2s ease-out;

        &:hover,
        &:focus-visible{
            opacity: 0.85;
        }
    }

    /* — Buchungsformular (hervorgehobene Karte) ————————— */

    .booking-form{
        display: flex;
        flex-wrap: wrap;
        align-items: end;
        gap: 1.25rem 1.5rem;

        background: var( --color-greige );
        padding: clamp(1.25rem, 3vw, 2.25rem);

        /* Schmäler als die Textspalte: Über die volle Breite zogen sich Datumsfeld
       und Absenden-Knopf auseinander. */
        max-inline-size: 52rem;
        margin-inline: auto;

        /* Der Bereichswert lautet höchstens „12.10.2026 bis 16.10.2026" — mehr
           Breite füllt sich nicht. */
        .booking-form__field:has( > .booking-form__range-input ),
        .booking-form__dates .booking-form__field{
            max-inline-size: 22rem;
        }

        /* Die zwei Datumsfelder verhalten sich wie direkte Flex-Items;
           `hidden` (bei aktivem flatpickr) sticht display:contents. */
        .booking-form__dates{
            display: contents;
        }

        .booking-form__range-input{
            cursor: pointer;

            /* Luft um das „bis": Der Feldwert ist Fließtext, einzelne Wörter sind
           nicht ansprechbar — `word-spacing` wirkt auf seine Leerzeichen. */
            word-spacing: 0.5em;
        }

        /* While only the placeholder shows, keep normal word spacing — otherwise
           "Select dates" would be letterspaced too. */
        .booking-form__range-input:placeholder-shown{
            word-spacing: normal;
        }

        /* Number of nights: positioned below the field so it cannot change the
           field's height, and with it the alignment of the row. */
        .booking-form__field:has(.booking-form__nights){
            position: relative;
        }

        .booking-form__nights{
            position: absolute;
            inset-block-start: calc(100% + 0.3rem);
            inset-inline-start: 0;
            font-size: 0.7rem;
            opacity: 0.7;
            white-space: nowrap;
        }

        /* Umbricht das Formular, sitzt die absolut positionierte Nächtezahl im
       Zeilenabstand — nur dann verbreitern. */
        &:has(.booking-form__nights:not(:empty)){
            row-gap: 2.25rem;
        }

        .booking-form__field{
            display: flex;
            flex-direction: column;
            gap: 0.5rem;
            flex: 1 1 9rem;

            label{ font-size: 0.7rem; }

            /* Date fields (direct input children, not the steppers) */
            > input{
                border: 1px solid var( --color-taupe );
                background: #fff;
                padding: 0.7em 0.8em;
                font: inherit;
                color: inherit;
            }
        }

        /* Guest fields: only as wide as their stepper, so no gap opens up.
           Labels stay left-aligned, as on the date field. */
        .booking-form__field:has(.stepper){
            flex: 0 0 auto;
            align-items: start;
        }

        /* Am Telefon über die volle Breite: Nebeneinander blieben rechts 34px
           ungenutzt und die Tap-Ziele der Stepper waren zu klein. */
        @media (width < 48rem){
            .booking-form__field:has(.stepper){
                flex: 1 1 0;
                align-items: stretch;
            }

            .stepper{
                inline-size: 100%;
            }

            .stepper input{
                inline-size: auto;
                flex: 1 1 0;
                min-inline-size: 0;
            }
        }


        /* Aussehen siehe die gemeinsame Regel weiter oben; hier steht nur,
           was diesen Knopf von dem auf der Karte unterscheidet. Der
           Innenabstand gibt ihm die Höhe der Felder daneben. */
        .booking-form__submit{
            flex: 1 1 auto;
            align-self: end;
            padding: 0.7em 1.75em;
        }
    }

    /* — Anfrageformular ————————————————————————————————
       Erbt von `.booking-form`. */

    /* Auf der Kontaktseite grün wie die Signature-Kacheln und die
       Apartment-Karten. Nur dort: Dasselbe Formular steht auch auf der
       Buchen- und der Apartment-Seite als Anfrage neben oder statt der
       Suche — dort bliebe es beim greigen Grund, sonst konkurrierten zwei
       grüne Flächen auf einer Seite. */
    .enquiry-form--kontakt{
        background: var( --color-salbei );

        /* Auf dem greigen Grund trugen 11px Versalien mit 0,12em Sperrung noch;
           auf dem mittleren Grün wirken sie ausgewaschen. Am Kontrast liegt es
           nicht — der ist mit 7,19:1 unverändert. Es ist die Strichstärke: Je
           dunkler der Grund, desto mehr Fläche braucht dieselbe Schrift.
           Deshalb zwei Stufen größer und enger gesetzt. */
        .booking-form__field label{
            font-size: 0.8125rem;
            letter-spacing: 0.06em;
        }

        /* Der Hinweis unter den Feldern ist Fließtext, keine Beschriftung —
           auf Grün liest er sich ohne Sperrung deutlich ruhiger. */
        .enquiry-form__note{
            letter-spacing: normal;
        }
    }

    .enquiry-form{
        /* Jeder Abschnitt nimmt eine eigene Zeile ein, innerhalb bleibt die
           Feld-Anordnung des Suchformulars unverändert. */
        .enquiry-form__section{
            flex: 1 1 100%;

            display: flex;
            flex-wrap: wrap;
            align-items: end;
            gap: 1.25rem 1.5rem;
        }

        /* Wie beim Suchformular: Die absolut gesetzte Nächte-Angabe säße sonst
           im Zeilenabstand direkt am Label der nächsten Zeile. */
        .enquiry-form__section:has(.booking-form__nights:not(:empty)){
            row-gap: 2.25rem;
        }

        .enquiry-form__field--wide{ flex: 1 1 14rem; }
        .enquiry-form__field--full{ flex: 1 1 100%; }

        textarea{
            border: 1px solid var( --color-taupe );
            background: #fff;
            padding: 0.7em 0.8em;
            font: inherit;
            color: inherit;

            /* `rows` gibt nur die Starthöhe vor — beim Ziehen ließe sich das Feld
       sonst auf eine Zeile schieben. */
            resize: vertical;
            min-block-size: 8rem;
        }

        /* Honeypot: nicht `display: none` — das überspringen Bots. Aus dem Bild
       geschoben, aus Tabulator und Screenreader genommen. */
        .enquiry-form__trap{
            position: absolute;
            inline-size: 1px;
            block-size: 1px;
            overflow: hidden;
            clip-path: inset(50%);
            white-space: nowrap;
        }

        .enquiry-form__error{
            margin: 0;
            font-size: 0.8rem;
            letter-spacing: normal;
            text-transform: none;
            color: var( --color-error );
        }

        :is(input, textarea)[aria-invalid="true"]{
            border-color: var( --color-error );
        }

        /* Zeigt, worauf sich die Anfrage bezieht, wenn sie von einer
           Detailseite kommt. Volle Zeile über den Feldern. */
        .enquiry-form__subject{
            flex: 1 1 100%;
            margin: 0;
            letter-spacing: normal;
            text-transform: none;
        }

        .enquiry-form__note{
            flex: 1 1 100%;
            margin: 0;
            font-size: 0.8rem;
            letter-spacing: normal;
            text-transform: none;
            opacity: 0.8;
        }
    }

    /* Hinweis über dem Formular, solange Anfragen per E-Mail laufen. */
    .booking__hint{
        max-inline-size: var( --measure-lead );
        margin-inline: auto;
        margin-block-end: 1.5rem;
        text-align: center;
    }

    /* Farbe nur als Akzent am Rand, der Text bleibt anthrazit: Eine Meldung,
       die sich allein über Farbe erklärt, kommt nicht überall an. */
    /* Fokussierbar für die Ansage, aber ohne den Ring eines Bedienelements — es
   ist ein Ziel, kein Knopf. */
.form-messages:focus{
    outline: none;
}

.form-message{
        max-inline-size: var( --measure-lead );
        margin-inline: auto;
        margin-block-end: 2rem;
        padding: 1rem 1.25rem;
        border-inline-start: 3px solid;
        background: var( --color-greige );
    }

    .form-message--success{ border-color: var( --color-salbei ); }
    .form-message--error{ border-color: var( --color-error ); }

    /* Personen-Stepper: [−] Wert [+] */
    .stepper{
        display: flex;
        align-items: stretch;
        width: fit-content;
        border: 1px solid var( --color-taupe );
        background: #fff;

        .stepper__btn{
            width: 2.5rem;
            flex: none;
            font-size: 1.2rem;
            line-height: 1;
            color: inherit;
            cursor: pointer;

            &:hover{ background: var( --color-greige ); }
        }

        input{
            width: 3rem;
            border: none;
            border-inline: 1px solid var( --color-taupe );
            background: transparent;
            text-align: center;
            font: inherit;
            color: inherit;
            padding-block: 0.7em;
            -moz-appearance: textfield;
            appearance: textfield;
        }

        input::-webkit-outer-spin-button,
        input::-webkit-inner-spin-button{
            -webkit-appearance: none;
            margin: 0;
        }
    }

    /* — Buchen-Seite ——————————————————————————————————— */

    .booking{

        h1{ margin-block-end: 2rem; }

        .booking__intro{
            margin-block-end: 3rem;
        }
    }

    .booking-results{
        margin-block-start: 3rem;

        .booking-results__note{
            text-align: center;
            opacity: 0.75;
        }

        /* Die Auskunft, dass nichts frei ist, steht nicht als Fußnote da:
           Sie ist das Ergebnis der Suche. Deshalb in Lead-Größe, mit Grund
           und ohne herabgesetzte Deckkraft. */
        .booking-results__leer{
            margin-block: 0 2rem;
            padding: 1.25rem 1.5rem;
            background-color: var( --color-greige );
            font-size: var( --font-size-lead );
            line-height: var( --line-height-lead );
            text-align: center;
            text-wrap: balance;
        }

        /* Die zwei Sätze stehen als eigene Absätze, damit der erste fett sein
           kann, ohne den zweiten mitzunehmen. Der Abstand dazwischen ist
           kleiner als ein Absatzabstand — es ist eine Meldung, kein Text. */
        .booking-results__leer p{
            margin: 0;
        }

        .booking-results__leer p + p{
            margin-block-start: 0.4em;
        }

        .booking-results__leer-titel{
            font-weight: bold;
        }

        .booking-results__leer:focus-visible{
            outline-offset: 4px;
        }

        .booking-results__list{
            margin: 0;
            padding: 0;
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
            gap: 2rem;
        }
    }

    /* Gefüllt statt umrandet — wie die Karten auf der Apartments-Übersicht.
       Dort ist die Fläche die Karte; hier stand bisher nur eine Linie darum,
       obwohl es dasselbe Ding ist. Der Rahmen entfällt damit: Er umrandete
       eine Fläche in seiner eigenen Farbe. */
    .booking-card{
        background-color: var( --color-salbei );

        &.is-unavailable{ opacity: 0.55; }

        img{ aspect-ratio: 3 / 2; object-fit: cover; }

        .booking-card__body{ padding: 1.25rem; }

        /* Eine Stufe über `--font-serif-4`, aber unter `--font-serif-3`: Der
           Name ist das, wonach man auf der Karte sucht. Die Obergrenze hält
           „Apartment 2+2 Garden" in der dreispaltigen Ansicht einzeilig. */
        .booking-card__title{
            font-size: clamp(1.65rem, 2.4vw, 2.05rem);
            margin-block-end: 0.5rem;
            color: black;
            text-align: center;
        }

        /* Fläche und Betten nebeneinander, klein und ruhig: Sie helfen bei der
           Wahl, sollen aber weder den Namen noch den Preis überstimmen. Beim
           Umbruch trennt der Zeilenabstand, nicht eine Linie — die Karte ist
           dafür zu klein. */
        .booking-card__facts{
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            text-align: center;
            gap: 0.35rem 1.75rem;
            margin-block-end: 1rem;
            font-size: 0.8125rem;
            line-height: 1.4;

            dt{
                font-size: 0.7rem;
                opacity: 0.7;
                margin-block-end: 0.1rem;
            }
        }

        /* Mittig wie alles darüber, mit dem Zusatz über dem Betrag. Zwischendurch
           stand der Block rechts an der Kante des Knopfes — neben mittigem Titel
           und mittigen Angaben las sich das als zwei Ausrichtungen auf einer
           Karte. */
        .booking-card__price{
            font-size: 1.25rem;
            margin-block-end: 0.75rem;
            text-align: center;
        }

        /* Der Zusatz nennt den Bezug der Zahl. Ohne ihn liest sich „ab € 781,20"
           als Nachtpreis — es ist aber die Summe für den ganzen Zeitraum.

           Über dem Betrag in eigener Zeile: Er sagt, wofür die Zahl gilt, und
           wer sie liest, hat die Antwort dann schon. Neben der Zahl
           konkurrierte er mit ihr um dieselbe Zeile. */
        .booking-card__nights{
            display: block;
            margin-block-end: 0.15rem;
            font-size: 0.8125rem;
            line-height: 1.3;
            opacity: 0.75;
        }

        /* Der Grund, warum nicht gebucht werden kann, schließt die Karte ab —
           dort, wo bei den freien der Preis steht. Mittig wie Titel und
           Angaben darüber: Auf einer Karte ohne Knopf gäbe es sonst keine
           Kante, an der die Zeile ausgerichtet wäre. */
        .booking-card__status{
            text-align: center;
        }

        .booking-card__cta{
            display: block;
            inline-size: 100%;
            text-align: center;
            padding: 0.5em 1.25em;
        }
    }
}

/* Slider pagination — deliberately UNLAYERED.
   Swiper's bundled pagination CSS is unlayered, and unlayered rules beat any
   layered rule regardless of specificity. To override bullet size/colour these
   must sit outside @layer too; here they win on specificity instead.
   `--autoplay-progress` (0 → 1) is set per animation frame by gb.js. */
/* Eigener Hintergrund, damit die Marken über jedem Bild gleich gut zu
       sehen sind. */
.slider .slider-controls{
    position: absolute;
    z-index: 1;
    bottom: 1rem;
    left: 50%;
    transform: translateX(-50%);

    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0.6rem;
    border-radius: 999px;
    background: rgb(0 0 0 / 0.3);
    color: #fff;
}

/* Pause / play toggle — 1.5rem (24px) target; icon swaps with the .is-paused
   state set by gb.js. */
.slider .slider-toggle{
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    color: inherit;
    cursor: pointer;

    svg{
        width: 0.85rem;
        height: 0.85rem;
    }
}

/* Show pause while playing, play while paused. Kept at matching specificity so
   the generic `svg` rule above can't override the display. */
.slider .slider-toggle__pause{ display: block; }
.slider .slider-toggle__play{ display: none; }
.slider.is-paused .slider-toggle__pause{ display: none; }
.slider.is-paused .slider-toggle__play{ display: block; }

/* Bullets: a static row inside the capsule. */
.slider .swiper-pagination{
    position: static;
    width: auto;
    transform: none;

    display: flex;
    align-items: center;
    /* 1rem gap → dot centres sit 24px apart, so the enlarged hit targets below
       are a full 24px wide without overlapping (WCAG 2.5.8). */
    gap: 1rem;

    --swiper-pagination-color: #fff;
    --swiper-pagination-bullet-inactive-color: #fff;
    --swiper-pagination-bullet-inactive-opacity: 0.45;
    --swiper-pagination-bullet-horizontal-gap: 0;

    /* Halbe Höhe als Radius: Punkt wird Kreis, aktive Marke wird Stadion. */
    --swiper-pagination-bullet-width: 0.5rem;
    --swiper-pagination-bullet-height: 0.5rem;
    --swiper-pagination-bullet-border-radius: 0.25rem;
}

.slider .swiper-pagination-bullet{
    position: relative;
    margin: 0;
    /* Smoothly grow/shrink between dot and pill as the active slide changes. */
    transition: width 0.4s ease, opacity 0.4s ease;
}

/* Tap-Ziel auf 24×27px (WCAG 2.5.8), weit über die sichtbare Marke
       hinaus. */
.slider .swiper-pagination-bullet::before{
    content: "";
    position: absolute;
    inset: -0.6rem -0.5rem;
}

/* Already-played dots read as full, not faded. */
.slider .swiper-pagination-bullet:has(~ .swiper-pagination-bullet-active){
    --swiper-pagination-bullet-inactive-opacity: 1;
}

/* Aktive Marke: Pille, die sich mit dem Autoplay-Timer füllt. Der
       Verlauf mit hartem Stopp liegt auf der Pille selbst und folgt damit
       ihrer Form. */
.slider .swiper-pagination-bullet-active{
    width: 3rem;
    height: 0.5rem;
    opacity: 1;
    background: linear-gradient(to right,
        #fff calc(var(--autoplay-progress, 0) * 100%),
        rgb(255 255 255 / 0.3) calc(var(--autoplay-progress, 0) * 100%));
}

/* Dunkle, durchscheinende Scheiben: Ein Rand allein umrisse den dünnen
       Winkel nicht, ein heller Ring verschwände auf hellen Bildern. */
.slider .swiper-button-prev,
.slider .swiper-button-next{
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    background: rgb(0 0 0 / 0.3);
}

.slider .swiper-button-prev::after,
.slider .swiper-button-next::after{
    font-size: 1.1rem;
}

/* Optical centering: the chevron's mass sits to one side, so nudge it outward
   to look centred in the disc. */
.slider .swiper-button-prev::after{
    transform: translateX(-0.1em);
}

.slider .swiper-button-next::after{
    transform: translateX(0.1em);
}

/* — Kontaktseite + Karte ——————————————————————————————— */

/* — Landingpages —————————————————————————————————————
   Blöcke sind frei kombinierbar, Stile hängen deshalb nur an den
   Block-Klassen. */

.lp-hero__media{
    position: relative;
    inline-size: 100%;
    aspect-ratio: var( --hero-ratio );
    max-block-size: var( --hero-max-block );
    overflow: hidden;
    margin-block-end: 3rem;
}

.lp-hero__media :is(figure, picture){
    display: block;
    position: absolute;
    inset: 0;
    margin: 0;
}

.lp-hero__media :is(img, video){
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
}

.lp-hero__body{
    text-align: center;
}

.lp-hero__body h1{
    font-size: var( --font-serif-2 );
    line-height: 1.05;
    margin-block-end: 1.5rem;
}

.lp-hero__body .lead{
    margin-inline: auto;
}


.lp-content__headline{
    font-size: var( --font-serif-3 );
    line-height: 1.1;
    margin-block-end: 1.5rem;
}

.lp-media{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
    gap: 2rem;
    margin-block-start: 2.5rem;
}


.lp-apartments__headline{
    font-size: var( --font-serif-2 );
    margin-block-end: 3rem;
}

.lp-cta{
    padding-block: 4rem;
    background: var( --color-greige );
}

.lp-cta__headline{
    font-size: var( --font-serif-2 );
    line-height: 1.1;
    margin-block-end: 1rem;
}

.lp-button{
    display: inline-block;
    margin-block-start: 2rem;
    padding: 0.85em 2em;
    border: 1px solid var( --color-button );

    /* Pillenform als durchgehende Form für Handlungsaufforderungen. */
    border-radius: 999px;
    background: var( --color-button );
    color: #fff;
    font-size: 0.8rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.lp-button:hover{
    background: var( --color-anthrazit );
    border-color: var( --color-anthrazit );
}

/* Der Knopf am Ende der Startseite: größer und nur mit Umriss. Gefüllt
   stand er unmittelbar über Karte und dunklem Fuß — drei dunkle Flächen in
   Folge. Taupe auf Weiß ergibt 7,9:1. */
.home-cta .lp-button{
    padding: 1.05em 2.6em;
    font-size: 0.95rem;
    background: transparent;
    color: var( --color-button );
    margin-block-start: 0;
}

.home-cta .lp-button:hover,
.home-cta .lp-button:focus-visible{
    background: var( --color-button );
    border-color: var( --color-button );
    color: #fff;
}

/* — Apartments: Kernthemen ————————————————————————— */

/* Ein Thema je Zeile, darin Text und Bild nebeneinander — nicht mehr zwei
   Themen nebeneinander. Mit Bildern wären zwei Spalten je Zeile zu klein für
   beides. */
.themes__list{
    display: grid;
    gap: 4rem;
    margin: 0;
    padding: 0;
}

/* Ohne Bild bleibt es beim reinen Textblock. */
.theme--mit-bild{
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 3rem;
    align-items: center;
}

/* Die Seite wechselt von Thema zu Thema. Über die Spaltenzuweisung statt über
   die Reihenfolge im Dokument: So bleibt der Text auch beim Vorlesen vor dem
   Bild, unabhängig davon, wo er steht.

   Ausdrücklich erst ab zwei Spalten. Vorher stand die Regel außerhalb einer
   Abfrage und wurde einspaltig von der Regel weiter unten überschrieben —
   dachte ich. Tatsächlich wiegt `.theme--mit-bild[data-seite="links"] .theme__body`
   schwerer als `.theme--mit-bild .theme__body`, und eine Media Query ändert an
   der Spezifität nichts. Am Telefon blieb der Text deshalb in Spalte 2, die es
   dort gar nicht gibt: Das Raster legte sie stillschweigend an und rückte den
   Text nach rechts. */
@media (width >= 48rem){
    .theme--mit-bild[data-seite="links"] .theme__body{ grid-column: 2; grid-row: 1; }
    .theme--mit-bild[data-seite="links"] .theme__media{ grid-column: 1; grid-row: 1; }
}

.theme__media img{
    display: block;
    inline-size: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

.theme__media figure{
    margin: 0;
}

@media (width < 48rem){
    .theme--mit-bild{
        grid-template-columns: 1fr;
        gap: 1.5rem;
    }

    /* Einspaltig immer Bild über Text — abwechselnde Seiten ergeben in einer
       Spalte kein Muster, nur Zufall. Der Text steht im Dokument vor dem Bild,
       deshalb müssen beide hier ausdrücklich zugewiesen werden. */
    .theme--mit-bild .theme__media{ grid-row: 1; }
    .theme--mit-bild .theme__body{ grid-row: 2; }
}

.theme__kicker{
    /* Größer als Formularbeschriftungen: Versalien wirken bei gleicher Größe
   kleiner. Die Deckkraft steigt mit, 0.65 ergaben nur 4.77:1. */
    font-size: 0.8125rem;
    margin: 0 0 0.75rem;
    opacity: 0.75;
}

.theme__headline{
    /* Siehe Serif-Stufen oben: Valore braucht ~1,4× für gleiche Wirkung. */
    font-size: var( --font-serif-3 );
    line-height: 1.0;
    margin: 0 0 1rem;
}

.theme__text{
    max-inline-size: var( --measure-lead );
    margin: 0;
    line-height: 1.6;
}

/* — About: Opening / Services / Discover & Book ——————————— */
.opening{
    text-align: center;
}

.opening__label{
    font-size: 0.75rem;
    margin: 0;
    opacity: 0.7;
}

.opening__date{
    font-size: var( --font-serif-2 );
    line-height: 1.1;
    margin-block: 0.5rem 0;
}

.opening__note{
    max-inline-size: 34em;
    margin: 1rem auto 0;
    line-height: 1.6;
}


/* — Signature Touches ————————————————————————————— */

/* Über die volle Breite und auf eigenem Grund: Die übrigen Abschnitte der
   Seite stehen auf Weiß, dieser hebt sich davon ab. Deshalb liegt hier die
   `.content-width` innen statt am Abschnitt selbst. */
.signature{
    background-color: var( --color-greige );
    padding-block: clamp(2.5rem, 5vw, 4rem);
}

.signature__title{
    font-size: var( --font-serif-2 );
    font-weight: normal;
    margin-block: 0 2.5rem;
}

/* Im Fluss statt im Raster: Sieben Punkte ergeben bei drei Spalten zwei volle
   Zeilen und eine mit einem einzigen Punkt. Ein Raster füllte von links auf
   und ließe den letzten am linken Rand kleben; im Fluss zentriert er sich. */
.signature__list{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.signature__item{
    flex: 0 0 calc((100% - 3rem) / 3);
    min-inline-size: 0;
    display: grid;
    justify-items: center;
    align-content: start;
    gap: 0.6rem;
    padding: clamp(1.5rem, 3vw, 2rem) clamp(1.25rem, 2.5vw, 1.75rem);
    background-color: var( --color-salbei );
    text-align: center;
}

/* Das Signet sitzt auf einer taupefarbenen Scheibe — dieselbe Anordnung wie
   beim Favicon, nur in Braun statt Greige. Auf dem grünen Grund braucht das
   Zeichen einen eigenen Träger, sonst schwimmt es darin.

   Die Scheibe misst mehr als das frühere Zeichen, damit die Marke nach dem
   Innenabstand gleich groß bleibt. */
.signature__signet{
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: 50%;
    background-color: var( --color-taupe );
}

.signature__name{
    font-size: 1.0625rem;
    font-weight: bold;
    line-height: 1.4;
    margin: 0;
}

.signature__text{
    line-height: 1.55;

    p{
        margin: 0;
    }

    /* Die Verweise führen aus dem Haus hinaus — sie sollen erkennbar sein,
       ohne die Zeile zu zerhacken. */
    a{
        text-underline-offset: 0.2em;
        text-decoration-thickness: 1px;
    }

    /* Eine Telefonnummer ist eine Einheit. Umbrochen stand sie als „+43 664"
       am Zeilenende und „5439550" darunter — zwei Zahlen statt einer. */
    a[href^="tel:"]{
        white-space: nowrap;
    }
}

/* Zwei Spalten, sobald drei zu schmal werden; darunter eine. Die Kacheln
   tragen ihren Text zentriert, unter etwa 16rem Breite bricht davon jede
   Zeile. */
@media (width < 62rem){
    .signature__item{
        flex-basis: calc((100% - 1.5rem) / 2);
    }
}

@media (width < 40rem){
    .signature__item{
        flex-basis: 100%;
    }
}


/* — Services: Ausstattung und On Top nebeneinander ——————————— */

.discover__title{
    font-size: var( --font-serif-2 );
    font-weight: normal;
    margin-block: 0 3rem;
}

.services{
    display: grid;
    grid-template-columns: 1.5fr 1fr;
    gap: clamp(2rem, 5vw, 4rem);
    align-items: start;
}

/* Beide Spalten beginnen mit einer Linie und demselben Innenabstand — so
   liegen die zwei Überschriften auf einer Höhe und die Linien lesen sich als
   eine durchgehende, vom Spaltenabstand unterbrochene Kante. */
.services__haupt,
.services__extras{
    border-block-start: 2px solid var( --color-anthrazit );
    padding-block-start: clamp(1.5rem, 3vw, 2.25rem);
}

.services__extras{
    padding-inline: clamp(1.5rem, 3vw, 2.25rem);
    padding-block-end: clamp(1.5rem, 3vw, 2.25rem);
    background: color-mix(in srgb, var( --color-taupe ) 14%, #fff);
}

/* Eine Stufe kleiner als die übrigen Abschnittsüberschriften und linksbündig:
   Hier stehen zwei davon nebeneinander, mittig gesetzt und in voller Größe
   ergäben sie zwei konkurrierende Zentren. */
.services__title{
    font-size: var( --font-serif-3 );
    font-weight: normal;
    line-height: 1.1;
    margin: 0 0 1.5rem;
}

.services__list{
    margin: 0;
    padding: 0;
    list-style: none;
}

/* `columns` statt Raster: Im Raster ist eine Zeile so hoch wie ihr längster
   Eintrag — ein umbrechender Eintrag riss die ganze Zeile auf und ließ seinen
   Nachbarn abgesetzt stehen. Im Spaltensatz läuft die Liste erst die eine
   Spalte hinunter, dann die andere; die Einträge sind voneinander unabhängig. */
.services__haupt .services__list{
    columns: 2;
    column-gap: 2rem;
}

/* Der Abstand zwischen den Einträgen ist genau eine Zeilenhöhe. Damit belegt
   jeder Eintrag ein ganzes Vielfaches davon — einzeilig zwei Zeilen,
   dreizeilig vier — und alle Grundlinien beider Spalten liegen auf demselben
   Raster. Mit einem festen rem-Wert stünde der letzte Eintrag der zweiten
   Spalte um die Differenz verschoben, sobald einer daneben umbricht.

   Die rem-Zeile davor ist der Rückfall für Browser ohne `lh`. */
.services__list li{
    break-inside: avoid;
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    font-size: 1.0625rem;
    line-height: 1.4;
    padding-block-end: 0.9rem;
    padding-block-end: 1lh;
}

/* Der letzte Eintrag braucht den Abstand nicht mehr — unter ihm folgt nichts,
   was auf dem Raster liegen müsste. Sonst steht der Zwischenraum zum nächsten
   Abschnitt doppelt und im Kasten eine Zeile Luft zu viel. */
.services__list li:last-child{
    padding-block-end: 0;
}

/* Ein Strich statt eines Punktes: Der Punkt sitzt optisch zu tief unter der
   Versalhöhe der Grundschrift. */
.services__list li::before{
    content: "";
    flex: none;
    inline-size: 0.75rem;
    block-size: 1px;
    background: var( --color-taupe );
    transform: translateY(-0.35em);
}

@media (width < 56rem){
    .services{
        grid-template-columns: 1fr;
    }
}

/* Unter dieser Breite bleiben von zwei Spalten nur noch Wortfetzen. */
@media (width < 34rem){
    .services__haupt .services__list{
        columns: 1;
    }
}


/* „Discover & book" deutlicher absetzen. Der Abschnitt „Services" ist der
   einzige mit zwei Überschriften darin: 56px trennen dort die erste Liste von
   der zweiten Überschrift, während der Abschnittsabstand nur 80px misst. Der
   Wechsel zum nächsten Thema las sich damit fast wie ein Absatz innerhalb des
   alten. Mit den zusätzlichen 2rem ist der Schritt nach außen doppelt so groß
   wie der nach innen. */
.services + .discover{
    margin-block-start: calc(var( --section-gap ) + 2rem);
}


.discover__list{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 2.5rem 1rem;
    margin: 0;
    padding: 0;
}

/* Quadratisch: Vier Hochformate nebeneinander ergaben eine sehr hohe Reihe,
   und die Motive vertragen den Beschnitt. */
.discover-tile__media{
    aspect-ratio: 1;
    overflow: hidden;
    margin-block-end: 1rem;
}

/* Am Telefon stehen die vier untereinander — quer statt quadratisch, sonst
   nimmt jede Kachel fast eine Bildschirmhöhe ein. */
@media (width < 48rem){
    .discover-tile__media{
        aspect-ratio: 4 / 3;
    }
}

.discover-tile__media :is(figure, picture){
    display: block;
    block-size: 100%;
}


.discover-tile__title{
    font-size: 0.8rem;
    font-weight: normal;
    margin-block: 0 0.5rem;
}

.discover-tile__text{
    margin: 0;
    line-height: 1.6;
}

/* — Standortseite ————————————————————————————————————— */

/* Gleiche Stufe wie `.map-heading`: „Eingänge", „Anreise" und „Karte"
   sind gleichrangig. */
.entrances__title,
.arrival__title{
    font-size: var( --font-serif-3 );
    font-weight: normal;
    margin-block: 0 3rem;
}

.arrival__text{
    margin-inline: auto;
}

/* Schmäler als die Inhaltsbreite: Über etwa 60rem hinaus gewinnt die
   handgeschriebene Zeichnung nichts an Lesbarkeit. */
.entrances__sketch{
    max-inline-size: 60rem;
    margin-inline: auto;
}

.entrances__sketch :is(figure, picture){
    display: block;
}

.entrance-list{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    gap: 2.5rem 2rem;
    margin-block: 3rem 0;
    padding: 0;
}

.entrance__label{
    font-size: 0.8rem;
    font-weight: normal;
    color: var( --color-taupe );
    margin-block: 0 0.5rem;
}

/* Die Anschrift ist das, was man mitnimmt — Serifenschrift, eine Stufe
   größer als der Hinweis. */
.entrance__address{
    font-size: var( --font-serif-4 );
    margin-block: 0 0.5rem;
}

.entrance__note{
    margin: 0;
    line-height: 1.6;
}

/* — Inhaltsseiten (Datenschutz, Impressum, AGB) ————————————— */

.page-content > h1{
    margin-block-end: 3rem;
}

.prose{
    max-inline-size: var( --measure-body );
    line-height: 1.6;
}

.prose h2{
    font-family: var( --serif-font );
    font-size: var( --font-serif-4 );
    font-weight: normal;
    margin-block: 2.5rem 0.75rem;
}

.prose h3{
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    margin-block: 1.75rem 0.5rem;
}

.prose p,
.prose ul,
.prose ol{
    margin-block: 0 1rem;
}

.prose ul,
.prose ol{
    padding-inline-start: 1.25rem;
}

.prose li{
    margin-block-end: 0.35rem;
}

.prose a{
    color: inherit;
}

/* Tabellen im Fließtext — bisher gab es dafür keine Regeln, die
   Stornostaffel der Buchungsbedingungen ist die erste.

   `border: 0` muss sein: Der Reset gibt Tabellen und Zellen ringsum eine
   Linie, sonst stünde die Staffel in einem Gitter aus Kästchen. Gewollt ist
   nur die waagrechte Trennung zwischen den Zeilen. */
.prose table{
    inline-size: 100%;
    border: 0;
    border-collapse: collapse;
    margin-block: 1.5rem;
}

.prose :is(th, td){
    text-align: start;
    vertical-align: top;
    padding: 0.6rem 1rem 0.6rem 0;
    border: 0;
    border-block-end: 1px solid color-mix(in srgb, var( --color-taupe ) 30%, #fff);
}

.prose thead th{
    font-weight: normal;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    opacity: 0.7;
}


/* Anschrift und Kontaktdaten sind auf dieser Seite der Inhalt, nicht Beiwerk —
   deshalb in Lead-Größe statt in Fließtextgröße. */
.contact__address,
.contact__details{
    font-size: var( --font-size-lead );
    margin-block-start: 1.5rem;
}

.contact__details{
    line-height: 1.9;
}

.contact__arrival{
    margin-block-start: 2.5rem;
    margin-inline: auto;
}

.contact__details a{
    color: inherit;
    text-decoration: none;
    border-block-end: 1px solid var( --color-taupe );
    padding-block-end: 1px;
}

.contact__details a:hover{
    border-block-end-color: currentColor;
}

.contact__social{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1.25rem;
    list-style: none;
    margin: 1.5rem 0 0;
    padding: 0;
}

.contact__social a{
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid var( --color-taupe );
    border-radius: 50%;
    color: var( --color-anthrazit );
    transition: background-color 0.15s ease, color 0.15s ease;
}

/* Im Footer: heller Grundton, mittig unter der Anschrift. Auf der
   Kontaktseite bleibt die Liste, wie sie war. */
.contact__social--footer{
    justify-content: center;
    margin-block: 0;
}

.contact__social--footer a{
    border-color: currentColor;
    color: inherit;
}

.contact__social--footer a:hover{
    background: var( --color-greige );
    color: var( --color-taupe );
}

.contact__social a:hover{
    background: var( --color-anthrazit );
    color: var( --color-greige );
}

/* Ohne Unterstreichung bliebe unklar, dass die Anschrift ein Link ist —
       Textfarbe allein reicht als einziges Merkmal nicht. */
.address__link{
    color: inherit;
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 0.2em;
    transition: text-decoration-color 0.15s ease;
}

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

/* Karte im Overlay — Apartment-Detailseiten. Natives <dialog>, deshalb kommen
   Fokusfalle, Escape und Hintergrund vom Browser. */
.map-dialog{
    inline-size: min(64rem, 100vw - 2rem);
    max-inline-size: none;
    max-block-size: min(46rem, 100vh - 2rem);
    padding: 0;
    border: 0;
    background: var( --color-greige );
    color: var( --color-anthrazit );
    overflow: hidden;
}

.map-dialog::backdrop{
    background: rgb(42 38 37 / 0.6);
}

/* Im Dialog ist die Höhe vom Viewport begrenzt, nicht vom Seitenverhältnis —
   sonst ragte die Karte auf schmalen, hohen Displays unten heraus. */
.map-dialog .contact-map{
    margin-block-start: 0;
    aspect-ratio: auto;
    block-size: min(36rem, 70vh);
    min-block-size: 0;
    max-block-size: none;
}

.map-dialog .map-links{
    margin: 0;
    padding-block: 1rem;
}

.map-dialog__bar{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.75rem 0.75rem 0.75rem 1.25rem;
}

.map-dialog__title{
    margin: 0;
    font-size: var( --font-serif-4 );
}

.map-dialog__close{
    flex: none;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: 1px solid transparent;
    border-radius: 50%;
    background: none;
    color: inherit;
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.map-dialog__close:hover,
.map-dialog__close:focus-visible{
    background: var( --color-taupe );
    color: var( --color-greige );
}

/* Der Dialog blockiert zwar die Bedienung dahinter, das Rad scrollt die Seite
   aber weiter — und mit ihm den Dialog aus dem Blick. */
html:has(dialog[open]){
    overflow: hidden;
}

.map-trigger{
    margin-block: 0;
    text-align: center;
}

/* Der Knopf bringt für den Fall Abstand mit, dass Text über ihm steht. Hier ist
   er der einzige Inhalt des Abschnitts — die zwei Zusatzzeilen kämen oben auf
   den ohnehin vorhandenen Abschnittsabstand obendrauf. */
.map-trigger .lp-button{
    margin-block-start: 0;
}

.floorplan__title{
    font-size: var( --font-serif-3 );
    margin-block-end: 1.5rem;
}

/* Über die Höhe begrenzt, nicht die Breite: Die Pläne haben sehr
   unterschiedliche Seitenverhältnisse — der quadratische und der breite wögen
   bei gleicher Breite optisch verschieden schwer.

   40rem statt der früheren 32: Ohne Textspalte daneben hat der Plan die ganze
   Seite, und auf einer handgezeichneten Zeichnung will man die Beschriftungen
   lesen können. Deutlich mehr wäre zu viel — dann steht auf einem üblichen
   Bildschirm nichts weiter als der Plan. */
.floorplan__figure img{
    inline-size: auto;
    max-inline-size: 100%;
    block-size: auto;
    max-block-size: 40rem;

    margin-inline: auto;
}

@media (width < 48rem){
    .floorplan__figure img{
        max-block-size: 26rem;
    }
}

.map-heading{
    font-size: var( --font-serif-3 );
    margin-block-end: 1.5rem;
}

/* Steht eine Überschrift davor, bringt die ihren eigenen Abstand mit. */
.map-heading + .contact-map{
    margin-block-start: 0;
}

/* Links unter der Karte, nicht darin: Innen konkurrierten sie mit Zoom-
   Knöpfen und Marker, am Telefon mit der Schiebefläche. */
.map-links{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem 2rem;
    margin-block-start: 1rem;
    font-size: 0.9rem;
}

.contact-map{
    width: 100%;

    /* Gleiches Verhältnis wie die Bühnen. Am Telefon ließ der 16/9-Streifen
       unter dem Marker kaum Stadt zum Orientieren. */
    aspect-ratio: var( --hero-ratio );
    min-block-size: 22rem;
    max-block-size: 46rem;
    margin-block-start: 3rem;
    background: #e8e4dd; /* Fallback, solange Tiles laden */
}

/* Kompass erst nach dem Drehen — gb.js setzt die Klasse. Ausgeblendet
   statt entfernt, damit MapLibre sein Element behält. */
.contact-map .maplibregl-ctrl-compass{
    display: none;
}

.contact-map.is-rotated .maplibregl-ctrl-compass{
    display: block;
}

/* Helles Rundfeld mit dunklem Signet. Das mitgelieferte Signet ist greige und
   für dunkle Flächen gedacht — für hier liegt eine anthrazitfarbene Fassung
   daneben. Als Hintergrundbild lässt sich die Farbe nicht über CSS steuern. */
.contact-map .map-marker{
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 50%;
    background: #fff url('/assets/goldenbridge-signet-dark.svg') center / 74% no-repeat;
    box-shadow: 0 2px 6px rgb(0 0 0 / 0.4);
    cursor: pointer;
}

/* Weitere Orte: kleiner und ohne Signet, damit das Haus die Hauptsache bleibt.
   2rem hält das Tap-Ziel dennoch über der 24px-Grenze (WCAG 2.5.8). */
.contact-map .map-place{
    display: grid;
    place-items: center;
    inline-size: 2rem;
    block-size: 2rem;
    padding: 0;
    border: 2px solid #fff;
    border-radius: 50%;
    background: var( --color-taupe );
    color: #fff;
    box-shadow: 0 2px 6px rgb(0 0 0 / 0.35);
    cursor: pointer;
}

/* Als Block, damit der Name eine eigene Zeilenhöhe bekommt: Inline übernähme er
   die 1,5 des Fensters, und zweizeilige Namen stünden so weit auseinander wie
   Straße und Ort darunter. */
.map-popup__title{
    display: block;
    line-height: 1.2;
    text-wrap: balance;
}

.map-popup__lines{
    margin-block: 0.3rem 0;
}

.map-popup__note{
    margin-block: 0.5rem 0;
    font-size: 0.85rem;
    opacity: 0.8;
}

.map-popup a{
    display: inline-block;
    margin-block-start: 0.5rem;
    color: inherit;
    font-size: 0.85rem;
}

/* Telefonnummer und Domainname untereinander: Zusammen kämen sie auf rund
   dreißig Zeichen und passten nicht in ein 240 Pixel breites Fenster. Die
   Einzelabstände der Links entfallen dabei, sonst stünden zwei Zeilen so weit
   auseinander wie zwei Absätze. */
.map-popup__actions{
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    margin-block-start: 0.6rem;
}

.map-popup__actions a{
    margin-block-start: 0;
}

.contact-map .maplibregl-popup-content{
    font-family: var( --sans-serif-font );
    background: var( --color-greige );
    color: var( --color-anthrazit );
    border-radius: 0;
    padding: 1rem 1.5rem;
    font-size: 1.05rem;
    line-height: 1.5;
    text-align: center;
    box-shadow: 0 2px 10px rgb(0 0 0 / 0.2);
}

/* Popup tip in the popover colour. Anchor-specific and more specific than
   maplibre's own rules, which would otherwise paint it white. */
.contact-map .maplibregl-popup-anchor-top .maplibregl-popup-tip,
.contact-map .maplibregl-popup-anchor-top-left .maplibregl-popup-tip,
.contact-map .maplibregl-popup-anchor-top-right .maplibregl-popup-tip{
    border-bottom-color: var( --color-greige );
}

.contact-map .maplibregl-popup-anchor-bottom .maplibregl-popup-tip,
.contact-map .maplibregl-popup-anchor-bottom-left .maplibregl-popup-tip,
.contact-map .maplibregl-popup-anchor-bottom-right .maplibregl-popup-tip{
    border-top-color: var( --color-greige );
}

.contact-map .maplibregl-popup-anchor-left .maplibregl-popup-tip{
    border-right-color: var( --color-greige );
}

.contact-map .maplibregl-popup-anchor-right .maplibregl-popup-tip{
    border-left-color: var( --color-greige );
}

/* — Andere Kategorien am Seitenende ——————————————————— */

.more-apartments__list{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1.5rem;
    margin: 0;
    padding: 0;
}

/* Gleiche Karte wie in der Übersicht, nur hochkant statt quer: heller Grund,
   beim Überfahren dunkel. */
.more-apartment a{
    display: block;
    background-color: var( --color-greige );
    text-decoration: none;
    transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.more-apartment a:hover,
.more-apartment a:focus{
    background-color: var( --color-taupe );
    color: var( --color-greige );
}

.more-apartment img{
    aspect-ratio: 3 / 2;
    object-fit: cover;
}

.more-apartment__body{
    padding: 1.25rem;
}

.more-apartment__title{
    font-size: 1.5rem;
}

.more-apartment__highlights{
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: 0.5rem 1rem;
    margin-block-start: 0.75rem;

    dt{
        opacity: 0.7;
        font-size: 0.8rem;
    }

    dd{
        margin-block-start: 0.15rem;
    }
}

/* — Ausstattung aus my-bookings ———————————————————————
   Nach Gruppen (Bad, Küche, Schlafzimmer …) statt der flachen Symbolliste des
   CMS-Felds. */
.amenities__title{
    font-size: var( --font-serif-3 );
    margin-block-end: 1.5rem;
}

.amenity-groups{
    display: grid;
}

/* Register: der Gruppenname links in fester Spalte, die Merkmale rechts
   mehrspaltig. Die Namen bilden dadurch eine eigene Kante — wer nur wissen
   will, ob es eine Waschmaschine gibt, liest die linke Spalte hinunter und
   hält dort an.

   Nebeneinander gestellt ergaben die neun Gruppen vorher Spalten sehr
   verschiedener Länge, untereinander verschwammen sie zu einer Liste, in der
   die Zwischentitel untergingen. Die Linie hält jede Gruppe für sich; die
   erste bekommt keine, dort trennt schon die Überschrift des Abschnitts. */
.amenity-group{
    display: grid;
    grid-template-columns: minmax(0, 11rem) 1fr;
    gap: 0 2rem;
    padding-block: 1.1rem;
    border-block-start: 1px solid color-mix(in srgb, var( --color-taupe ) 35%, transparent);

    &:first-child{
        border-block-start: none;
        padding-block-start: 0;
    }
}

.amenity-group__title{
    font-size: 0.8rem;
    font-weight: normal;
    color: var( --color-taupe );

    /* Rückt den Namen auf die erste Grundlinie der Merkmale daneben — die
       kleinere Schrift säße sonst zu hoch. */
    padding-block-start: 0.15rem;
}

/* Die Merkmale mehrspaltig, und zwar über `columns` statt Grid: Der
   Spaltenumbruch soll dem Text folgen — zwei Merkmale bleiben zwei Zeilen,
   neunundzwanzig füllen drei Spalten. Ein Grid müsste die Zeilenzahl vorher
   kennen. */
.amenity-group__list{
    columns: 11rem 3;
    column-gap: 2rem;
    margin: 0;
    padding: 0;
    line-height: 1.5;

    li{
        /* Ein zweizeiliges Merkmal soll nicht über den Spaltenrand
           auseinandergerissen werden. */
        break-inside: avoid;
        margin-block-end: 0.3rem;
    }
}

/* Am Telefon wird aus dem Register eine Folge von Kacheln: Der Name rückt über
   seine Merkmale, und statt einer Linie fasst eine getönte Fläche jede Gruppe
   zusammen. Auf 343 px Breite steht ohnehin alles untereinander — dort trägt
   die Fläche die Gliederung besser als ein Strich, der zwischen zwei langen
   Listen kaum noch auffällt. */
@media (width < 48rem){
    .amenity-groups{ gap: 0.75rem; }

    .amenity-group,
    .amenity-group:first-child{
        grid-template-columns: 1fr;
        gap: 0.5rem;
        padding: 1rem 1.1rem 1.1rem;
        border-block-start: none;
        background-color: color-mix(in srgb, var( --color-salbei ) 30%, #fff);
    }

    .amenity-group__title{
        padding-block: 0 0.5rem;
        border-block-end: 1px solid color-mix(in srgb, var( --color-taupe ) 30%, transparent);
    }

    .amenity-group__list{ columns: 1; }
}

/* — Grätzelführer ——————————————————————————————————— */

/* Abschnittsgröße aus der Skala: Der Grätzelführer ist der Hauptteil dieser
   Seite und stand vorher auf Browser-Voreinstellung. */
.guide__title{
    font-size: var( --font-serif-2 );
}

/* Nur am Telefon sichtbar; die Regeln dafür stehen unten im Medienblock. */
.guide__toggle{
    display: none;
}

.guide__filters{
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    margin-block: 2rem 1.5rem;
}

.guide__filter{
    display: inline-flex;
    align-items: center;
    gap: 0.45em;
    padding: 0.45em 1.1em;
    border: 1px solid var( --color-taupe );
    border-radius: 999px;
    background: none;
    color: inherit;
    font: inherit;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.guide__filter svg{
    inline-size: 1.15em;
    block-size: 1.15em;
}

.guide__filter.is-active{
    background: var( --color-taupe );
    color: var( --color-greige );
}

/* Karte und Liste nebeneinander. Die Karte bleibt beim Scrollen stehen, die
   Liste läuft daran vorbei — sonst wäre bei 25 Einträgen die Karte längst aus
   dem Bild, wenn man unten etwas anklickt. */
/* Breiter als der Rest der Seite: Eine Karte gewinnt mit jedem Quadratzentimeter,
   ein Fließtext nicht. Der Abschnitt bekommt deshalb 86rem statt der üblichen 75.
   Zurücknehmen heißt: diese Regel streichen. */
.guide{
    max-width: 86rem;
}

/* minmax(0, …) statt schlicht 1fr: Die Karte bringt über ihr Canvas eine
   Mindestbreite mit, und eine Spalte aus 1fr schrumpft nicht darunter — auf dem
   Telefon stünde die Karte sonst über den Bildschirmrand hinaus.

   Die zusätzliche Breite geht ganz an die Karte: Die Liste ist auf 30rem
   gedeckelt, damit die Beschreibungen ihre Zeilenlänge behalten. */
.guide__layout{
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 30rem);
    gap: 2rem;
    align-items: start;
}

.guide__map{
    position: sticky;
    inset-block-start: calc(var(--header-height, 0px) + 1rem);
    min-inline-size: 0;
}

.guide__map .contact-map{
    margin-block-start: 0;
    aspect-ratio: 3 / 4;
    max-block-size: 34rem;
}

.guide__list{
    display: grid;
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    max-block-size: 34rem;
    overflow-y: auto;
}

/* Platz rechts für das Abzeichen freihalten, damit lange Namen nicht darunter
   laufen. */
.guide-place{
    position: relative;
    padding: 1rem 3.5rem 1rem 1.25rem;
    background: var( --color-greige );
    cursor: pointer;
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
}

/* Der angesteuerte Eintrag — gesetzt beim Klick in der Liste wie auf der Karte. */
.guide-place.is-active{
    background: color-mix(in srgb, var( --color-taupe ) 14%, var( --color-greige ));
    box-shadow: inset 3px 0 0 var( --color-taupe );
}

.guide-place__badge{
    position: absolute;
    inset-block-start: 1rem;
    inset-inline-end: 1.25rem;
    display: grid;
    place-items: center;
    inline-size: 2rem;
    block-size: 2rem;
    border-radius: 50%;
    color: #fff;
}

.guide-place__badge svg{
    inline-size: 1.1rem;
    block-size: 1.1rem;
}

.guide-place__kicker{
    font-size: 0.7rem;
    color: var( --color-taupe );
}

/* Die Serife wirkt bei gleicher Punktgröße kleiner als die Grotesk daneben —
   deshalb eine Stufe der Skala statt eines Werts nahe am Fließtext. */
.guide-place__name{
    font-size: var( --font-serif-4 );
    line-height: 1.15;
    margin-block: 0.2rem 0.5rem;
}

.guide-place__button{
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: start;
    cursor: pointer;
}

.guide-place[hidden]{
    display: none;
}

.guide-place__text{
    margin: 0;
    font-size: 0.9rem;
    line-height: 1.5;
}

.guide-place__meta{
    margin-block-start: 0.6rem;
    font-size: 0.7rem;
    opacity: 0.7;
}

.guide-place__actions{
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    margin-block-start: 0.6rem;
    font-size: 0.8rem;
}

/* Farbe je Kategorie als Merkmal, nicht als fertige Regel: Kartenpunkt und
   Abzeichen auf der Kachel greifen dieselbe Quelle ab und können nicht
   auseinanderlaufen. Die Liste nennt die Zuordnung zusätzlich in Worten und mit
   einem Sinnbild — Farbe allein trägt die Auskunft nicht. */
.map-place--eat{      --place-color: var( --color-taupe ); }
.map-place--coffee{   --place-color: #8a6f4e; }
.map-place--drinks{   --place-color: #4e6b8a; }
.map-place--discover{ --place-color: rgb(70,120,110); }
.map-place--fitness{  --place-color: rgb(140,86,60); }
/* Die Garage gehört zum Haus, nicht ins Grätzel — zurückgenommene Farbe. */
.map-place--parking{  --place-color: rgb(58,58,66); }
.map-place--shop{     --place-color: rgb(120,116,110); }
.map-place--service{  --place-color: rgb(120,116,110); }

.contact-map .map-place,
.guide-place__badge{
    background: var( --place-color, var( --color-taupe ) );
}

/* Favoriten etwas größer: Sie sind die Empfehlung, nicht nur ein Eintrag.
   Farbe und Sinnbild bleiben die ihrer Kategorie — sonst wüsste man von einem
   roten Stern nicht, ob dahinter ein Lokal oder ein Museum steckt. */
.contact-map .map-place.is-favorite{
    inline-size: 2.5rem;
    block-size: 2.5rem;
}

/* Steht ein Fenster offen, treten die übrigen Punkte zurück. Nicht ausblenden:
   Sie sollen als Umgebung erkennbar bleiben und anklickbar sein.

   Über `filter` und nicht über `opacity`: MapLibre verwaltet die Deckkraft
   seiner Marker selbst und schreibt bei jeder Kartenbewegung `opacity: 1` als
   Attribut ins Element — dagegen kommt keine Klasse an. `filter` fasst es
   nicht an. */
.contact-map .map-place.is-dimmed{
    filter: opacity(0.65);
}

.contact-map .map-place{
    transition: filter 0.2s ease;
}

/* Ohne das gewinnt die Größenregel oben gegen die Voreinstellung des Browsers. */
.contact-map .map-place[hidden]{
    display: none;
}

/* Wegmarken ohne Fenster: kleiner und ohne Mauszeiger, damit niemand einen
   Klick versucht. `pointer-events` gibt den Klick an die Karte darunter weiter,
   das Verschieben bleibt also möglich. */
.contact-map .map-place.is-static{
    inline-size: 1.7rem;
    block-size: 1.7rem;
    border-width: 1.5px;
    cursor: default;
    pointer-events: none;
}

.contact-map .map-place.is-static svg{
    inline-size: 0.95rem;
    block-size: 0.95rem;
}

/* Stapelordnung von unten nach oben: stumme Wegmarken, bedienbare Punkte, der
   gewählte Punkt, das Haus. MapLibre vergibt keine, es zählte sonst allein die
   Reihenfolge im Dokument — dann deckte etwa der Supermarkt am
   Julius-Tandler-Platz das Fitnesscenter zu, das im selben Haus liegt. */
.contact-map .map-place.is-static{
    z-index: 0;
}

.contact-map .map-place{
    z-index: 1;
}

.contact-map .map-place.is-open{
    z-index: 2;
}

.contact-map .map-marker{
    z-index: 3;
}

/* Über allen Markern. MapLibre gibt dem Fenster keine Stapelordnung; solange
   auch die Marker keine hatten, genügte seine spätere Stelle im Dokument.
   Sobald die Marker eine bekommen, gewinnen sie — das Fenster lag dann
   teilweise darunter. */
.contact-map .maplibregl-popup{
    z-index: 4;
}

/* Der Punkt, dessen Fenster offen ist. */
.contact-map .map-place.is-active{
    box-shadow: 0 0 0 3px var( --color-anthrazit ), 0 2px 6px rgb(0 0 0 / 0.35);
}

.contact-map .map-place svg{
    inline-size: 1.1rem;
    block-size: 1.1rem;
}

.contact-map .map-place.is-favorite svg{
    inline-size: 1.25rem;
    block-size: 1.25rem;
}

/* Vorerst nur ausgeblendet: Unter der Karte des Grätzelführers ging es um die
   Hausadresse, und die steht auf dieser Seite nicht zur Debatte. Soll der Link
   zurück, genügt es, diese Regel zu streichen. */
.guide__map .map-links{
    display: none;
}

@media (width < 62rem){
    .guide__toggle{
        display: block;
        inline-size: 100%;
        margin-block-end: 0.75rem;
        padding: 0.7em 1em;
        border: 1px solid var( --color-taupe );
        background: none;
        color: inherit;
        font: inherit;
        font-size: 0.8rem;
        text-transform: uppercase;
        letter-spacing: 0.12em;
        cursor: pointer;
    }

    /* Bezugsrahmen für die Liste: Sie deckt genau die Karte ab. */
    .guide__layout{
        position: relative;
        grid-template-columns: minmax(0, 1fr);
    }

    /* Einspaltig ist „klebend" sinnlos: Die Karte stünde über der Liste fest
       und nähme die halbe Höhe dauerhaft weg. */
    .guide__map{
        position: static;
    }

    .guide__map .contact-map{
        aspect-ratio: 4 / 3;
    }

    /* Über der Karte statt darunter. Ohne JavaScript bleibt der Umschalter
       verborgen und diese Regeln greifen nicht — dann steht die Liste im Fluss
       unter der Karte, vollständig und ohne Bedienung. */
    .guide.is-switchable .guide__list{
        position: absolute;
        inset: 0;
        z-index: 2;
        max-block-size: none;
        padding: 0.75rem;
        background: #fff;
        box-shadow: 0 0 0 1px var( --color-greige );
    }
}

/* Bilder aus dem Grätzel unter der Karte. 4:3, weil die Aufnahmen in diesem
   Format vorliegen — so bleibt die Bildaufteilung des Fotografen erhalten
   statt an den Seiten wegzufallen. Wer später ein Hochformat einhängt, bekommt
   es beschnitten; die Reihe behält dafür ihre Kante. */
.guide-photos{
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 0.75rem;
    margin-block-start: 2rem;
    padding: 0;
}

.guide-photos figure{
    margin: 0;
}

.guide-photos img{
    display: block;
    inline-size: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

@media (width < 48rem){
    .guide-photos{
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* — Apartment-Galerie ————————————————————————————————— */

/* Ein großes Bild, zwei daneben gestapelt.

   Die Höhe ist gedeckelt und wird nicht aus den Seitenverhältnissen der Bilder
   abgeleitet: Über die volle Breite ergäbe ein 3:2-Bild allein schon eine
   halbe Bildschirmhöhe, mit dem Streifen darunter wäre vom Apartment nichts zu
   sehen, ohne zu scrollen. */
.gallery__top{
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 0.5rem;
    block-size: min(46svh, 27rem);
}

/* Ohne Seitenspalte — bei sechs Bildern der Normalfall — nimmt das große Bild
   die ganze Breite, statt eine leere Spalte stehen zu lassen. */
.gallery__top:not(:has(.gallery__side)){
    grid-template-columns: 1fr;
}

/* Bei fester Höhe darf kein Bild eigene Höhe beisteuern — sonst wüchse die
   Zeile wieder mit. */
.gallery__top .gallery__cell{
    aspect-ratio: auto;
    position: relative;
    min-block-size: 0;
}

.gallery__top .gallery__cell :is(figure, picture){
    position: absolute;
    inset: 0;
}

/* Zeilen implizit: Ein Bild füllt die Höhe, zwei teilen sie sich. `1fr` geht
   auf, weil `.gallery__top` eine feste Höhe hat. */
.gallery__side{
    display: grid;
    grid-auto-rows: minmax(0, 1fr);
    gap: 0.5rem;
}

/* Streifen als eine Zeile, unabhängig von der Bildanzahl: `auto-fit` bräche bei
   vielen Bildern um, und die feste Höhe ginge dann nicht mehr auf. */
.gallery__strip{
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.5rem;
    block-size: min(19svh, 11rem);
    margin-block-start: 0.5rem;
}

.gallery__strip .gallery__cell{
    aspect-ratio: auto;
    position: relative;
    min-block-size: 0;
}

.gallery__strip .gallery__cell :is(figure, picture){
    position: absolute;
    inset: 0;
}

/* Die Zellen sind Knöpfe — Rahmen, Innenabstand und Hintergrund des Browsers
   müssen weg, damit nur das Bild zu sehen ist. */
.gallery__cell{
    display: block;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    overflow: hidden;
    aspect-ratio: 3 / 2;
}

/* Die Höhe der Zeile gibt das große Bild vor, die Seitenspalte teilt sie sich.
   Damit das aufgeht, dürfen die Seitenbilder nicht selbst Höhe beisteuern —
   absolut positioniert tun sie das nicht, und die beiden 1fr-Zeilen können die
   verfügbare Höhe wirklich halbieren. */
.gallery__cell :is(figure, picture){
    display: block;
    block-size: 100%;
}

.gallery__cell img{
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
    transition: transform 0.4s ease;
}

.gallery__cell:hover img,
.gallery__cell:focus-visible img{
    transform: scale(1.03);
}

@media (prefers-reduced-motion: reduce){
    .gallery__cell img{ transition-duration: 0s; }
    .gallery__cell:hover img,
    .gallery__cell:focus-visible img{ transform: none; }
}

/* Am Telefon ein durchgehendes Raster aus drei Spalten: Das große Bild über
   die ganze Breite, alle übrigen gleich groß darunter. Das Mosaik aus zwei
   verschiedenen Blöcken ergäbe hier zwei Bildgrößen auf 390px Breite.

   `display: contents` löst die beiden Zwischencontainer auf, damit ihre Zellen
   Kinder desselben Rasters werden — sonst ließen sich Seitenspalte und
   Streifen nicht zu einer Reihe zusammenfassen. Der Dialog ist geschlossen
   `display: none` und beansprucht deshalb kein Feld. */
@media (width < 48rem){
    .gallery{
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 0.5rem;
    }

    .gallery__top,
    .gallery__side,
    .gallery__strip{
        display: contents;
    }

    .gallery__lead{
        grid-column: 1 / -1;
    }

    /* Feste Höhen und die absolute Positionierung gelten nur dort, wo eine
       Zeile ihre Höhe vorgibt — hier tut das wieder das Seitenverhältnis.

       Die Selektoren wiederholen die der Grundregeln: `.gallery__cell` allein
       wiegt weniger als `.gallery__top .gallery__cell` und käme nicht dagegen
       an — die Bilder blieben dann ohne Höhe und die Galerie unsichtbar. */
    .gallery__top .gallery__cell,
    .gallery__strip .gallery__cell{
        aspect-ratio: 3 / 2;
        position: static;
    }

    .gallery__top .gallery__cell :is(figure, picture),
    .gallery__strip .gallery__cell :is(figure, picture){
        position: static;
    }

    /* Das große Bild flacher als die Kacheln darunter: Über die volle Breite
       ergäbe 3:2 gut 260px und damit ein Drittel des Bildschirms, bevor der
       Titel kommt. Der Selektor wiegt so schwer wie die 3:2-Regel darüber und
       steht danach — mit `.gallery__lead` allein käme er nicht dagegen an. */
    .gallery__top .gallery__lead{
        aspect-ratio: 16 / 9;
    }
}

/* Lightbox. Natives <dialog>, deshalb kommen Fokusfalle, Escape und der
   abgedunkelte Hintergrund vom Browser. */
.gallery-dialog{
    position: relative;
    inline-size: min(94vw, 76rem);
    max-inline-size: none;
    padding: 0;
    border: 0;
    background: var( --color-anthrazit );
    color: var( --color-greige );
}

.gallery-dialog::backdrop{
    background: rgb(0 0 0 / 0.82);
}

.gallery-dialog__bar{
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.5rem 0.5rem 1rem;
}

.gallery-dialog__count{
    font-size: 0.8rem;
    letter-spacing: 0.12em;
}

.gallery-dialog__close{
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: 0;
    background: none;
    color: inherit;
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
}

.gallery-dialog__image{
    display: block;
    inline-size: 100%;
    block-size: auto;
    max-block-size: calc(92vh - 4rem);
    object-fit: contain;
}

.gallery-dialog__nav{
    position: absolute;
    inset-block-start: 50%;
    translate: 0 -50%;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: 0;
    border-radius: 999px;
    background: rgb(0 0 0 / 0.45);
    color: #fff;
    font-size: 1.75rem;
    line-height: 1;
    cursor: pointer;
}

.gallery-dialog__nav--prev{ inset-inline-start: 0.5rem; }
.gallery-dialog__nav--next{ inset-inline-end: 0.5rem; }

/* — Verfügbarkeit & Kalender ——————————————————————————— */

/* Steht jetzt unter dem Kalender und braucht den Abstand nach oben; vorher kam
   er vom Kalender selbst. */
.availability__form{
    margin: 2.5rem auto 0;
}

.availability__result:not(:empty){
    margin-block-start: 1.5rem;
    text-align: center;
}

/* Schmaler als die Inhaltsbreite: Über 1072px verteilt ergäben sieben
   quadratische Spalten Zellen von 150px und einen Kalender von fast tausend
   Pixeln Höhe. Bei 32rem sind es rund 65px je Tag — genug für Datum und Preis. */
.calendar{
    max-inline-size: 32rem;
    margin-inline: auto;
}

.calendar__bar{
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-block-end: 1rem;
}

.calendar__month{
    font-size: var( --font-serif-4 );
}

.calendar__nav{
    display: flex;
    gap: 0.5rem;
}

.calendar__step{
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: 1px solid var( --color-taupe );
    border-radius: 999px;
    background: none;
    color: inherit;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}

.calendar__grid{
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 0.35rem;
}

.calendar__weekday{
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    opacity: 0.7;
    text-align: center;
    padding-block-end: 0.35rem;
}

.calendar__day{
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.1rem;
    aspect-ratio: 1;
    background: var( --color-greige );
    font-size: 0.9rem;

    /* Freie Tage sind <button>: Rahmen und Schriftvorgaben des Browsers weg. */
    border: 0;
    padding: 0;
    color: inherit;
    font-family: inherit;
}

.calendar__day.is-free{
    cursor: pointer;
}

/* Frei und belegt tragen zusätzlich ein verstecktes Wort je Zelle und die
   Legende darunter — Farbe allein trägt die Auskunft nicht. */
.calendar__day.is-free{
    background: var( --color-salbei );
}

/* Belegt als heller Ton mit Schraffur, nicht als volle Fläche: Ein ausgebuchter
   Monat wären sonst dreißig dunkelrote Kacheln. Die Schraffur trägt den
   Unterschied auch dort, wo die Farbe nicht ankommt. */
.calendar__day.is-booked{
    background-color: color-mix(in srgb, var( --color-error ) 10%, #fff);
    background-image: repeating-linear-gradient(
        135deg,
        transparent 0 5px,
        color-mix(in srgb, var( --color-error ) 30%, #fff) 5px 6px
    );
    color: var( --color-taupe );
}

/* Gewählter Zeitraum. An- und Abreise dunkel, die Nächte dazwischen getönt —
   dieselbe Logik wie im Datumswähler des Formulars.

   Steht bewusst hinter `.is-free`: Beide Regeln wiegen gleich schwer, und ein
   gewählter Tag trägt beide Klassen — die spätere gewinnt. */
.calendar__day.is-start,
.calendar__day.is-end{
    background: var( --color-taupe );

    /* Weiß, nicht Greige: Auf Taupe kam Greige nur auf 5,4:1 — dieselbe zu
       schwache Paarung wie zuletzt bei den Knöpfen. Mit Weiß sind es 7,9:1,
       und der Preis in der Zelle (0,7rem bei 80 % Deckkraft) bleibt mit 5,8:1
       auch als Kleinschrift über der Grenze. */
    color: #fff;
}

/* Getönt gegen Taupe, nicht gegen Mint: Aufgehellte Mint-Töne waren heller als
   die freien Tage und lasen sich damit als „weniger", nicht als „gewählt". */
.calendar__day.is-between{
    background: color-mix(in srgb, var( --color-taupe ) 30%, #fff);
}

.calendar__day--empty{
    background: none;
}

.calendar__day--past{
    background: none;
    opacity: 0.35;
}

.calendar__price{
    font-size: 0.7rem;
    opacity: 0.8;
}

.calendar__legend{
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-block-start: 1rem;
    font-size: 0.8rem;
}

.calendar__key{
    inline-size: 0.9rem;
    block-size: 0.9rem;
    border-radius: 0.15rem;
}

.calendar__key--free{ background: var( --color-salbei ); }

.calendar__key--booked{
    margin-inline-start: 1rem;
    background-color: color-mix(in srgb, var( --color-error ) 10%, #fff);
    background-image: repeating-linear-gradient(
        135deg,
        transparent 0 3px,
        color-mix(in srgb, var( --color-error ) 30%, #fff) 3px 4px
    );
}

.calendar.is-loading .calendar__grid{
    opacity: 0.5;
}

.calendar__error{
    grid-column: 1 / -1;
    color: var( --color-error );
}

/* — flatpickr-Thema ————————————————————————————————————————
   flatpickr 4 kennt keine Custom Properties — jede Farbe ist ein fester
   Hex-Wert im mitgelieferten Stylesheet und muss überschrieben werden. */
.flatpickr-calendar{
    /* ——— Stellschrauben ——— */
    --fp-accent: var( --color-button );                                  /* Auswahl */
    --fp-accent-text: var( --color-greige );                             /* Schrift auf Auswahl */
    --fp-range-bg: color-mix(in srgb, var( --fp-accent ) 16%, #fff);     /* Tage dazwischen */
    --fp-hover-bg: var( --color-greige );                                /* Hover */
    --fp-today-border: var( --color-taupe );                             /* heutiger Tag */
}

/* Ausgewählte Tage. flatpickr färbt dieselbe Auswahl in achtzehn blauen
   Varianten, darunter `:focus`, `.inRange` und Nachbarmonatstage. `:is()`
   hebt das Gewicht über deren `.flatpickr-day.selected:focus`, das bei
   gleichem Gewicht sonst später käme. */
.flatpickr-calendar .flatpickr-day:is(.selected, .startRange, .endRange){
    background: var( --fp-accent );
    border-color: var( --fp-accent );
    color: var( --fp-accent-text );
}

.flatpickr-calendar .flatpickr-day:is(.selected, .startRange, .endRange):is(:hover,
:focus, .inRange, .prevMonthDay, .nextMonthDay){
    background: var( --fp-accent );
    border-color: var( --fp-accent );
    color: var( --fp-accent-text );
}

.flatpickr-calendar .flatpickr-day.inRange{
    background: var( --fp-range-bg );
    border-color: var( --fp-range-bg );
    box-shadow: -5px 0 0 var( --fp-range-bg ),
                 5px 0 0 var( --fp-range-bg );
}

/* Directly adjacent start/end days: flatpickr bridges the gap with a
   box-shadow (blue by default) rather than a background. */
.flatpickr-calendar .flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-calendar .flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),
.flatpickr-calendar .flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)){
    box-shadow: -10px 0 0 var( --fp-accent );
}

/* flatpickr zeichnet Nachbarmonatstage immer mit 30% Deckkraft, auch wenn
   sie buchbar sind — am Monatsende sind genau das die relevanten. */
.flatpickr-calendar .flatpickr-day.prevMonthDay:not(.flatpickr-disabled),
.flatpickr-calendar .flatpickr-day.nextMonthDay:not(.flatpickr-disabled){
    color: inherit;
}

.flatpickr-calendar .flatpickr-day.flatpickr-disabled{
    color: color-mix(in srgb, currentColor 28%, transparent);
}

.flatpickr-calendar .flatpickr-day.today{
    border-color: var( --fp-today-border );
}

.flatpickr-calendar .flatpickr-day.today:hover{
    background: var( --fp-today-border );
    color: var( --fp-accent-text );
}

.flatpickr-calendar .flatpickr-day:hover{
    background: var( --fp-hover-bg );
    border-color: var( --fp-hover-bg );
}

/* gb.js setzt `is-closing`, solange der Kalender noch offen ist: Ein
   geschlossener ist `display: none` und könnte nicht überblenden. */
.flatpickr-calendar.open{
    transition: opacity 0.25s ease;
}

.flatpickr-calendar.open.is-closing{
    opacity: 0;
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce){
    .flatpickr-calendar.open{
        transition: none;
    }
}

/* Zwei Monate nebeneinander passen nicht aufs Telefon. flatpickr
   gruppiert nach Art — beide Monatsnamen, dann beide Wochentagsreihen —,
   die Spalten müssen deshalb per Raster wieder zusammengeführt werden. */
@media (width < 48rem){
    /* `.open` nur des Gewichts wegen: flatpickrs eigenes
   `.flatpickr-calendar.open{display:inline-block}` lädt später. */
    .flatpickr-calendar.open.multiMonth{
        display: grid;

        /* Eine Spalte, und die darf nicht über den Kalender hinauswachsen. */
        grid-template-columns: minmax(0, 1fr);

        /* flatpickr schreibt die Breite für zwei Monate (618px) inline auf das
   Element — nur `!important` kommt dagegen an. */
        inline-size: min(21rem, calc(100vw - 2rem)) !important;

        /* Zwei volle Monate sind höher als ein Telefonbildschirm: gedeckelt und
   innen scrollbar. */
        max-block-size: 50dvh;
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .flatpickr-calendar.multiMonth :is(.flatpickr-months, .flatpickr-innerContainer,
    .flatpickr-rContainer, .flatpickr-weekdays, .flatpickr-days){
        display: contents;
    }

    .flatpickr-calendar.multiMonth :is(.flatpickr-month, .flatpickr-weekdaycontainer,
    .dayContainer):nth-of-type(2){
        order: 1;
    }

    /* Alle drei Teile auf Spaltenbreite: Der Monatsname nahm seine Breite
   aus einem `flex: 1` der Zeile, in der er nicht mehr sitzt. */
    .flatpickr-calendar.multiMonth :is(.flatpickr-month, .flatpickr-weekdaycontainer,
    .dayContainer){
        inline-size: 100%;
        min-inline-size: 0;
        max-inline-size: none;
    }

    /* Abstand an die Wochentagsreihe, nicht an den Monatsblock:
   `.flatpickr-current-month` ist absolut positioniert, Innenabstand dort
   bewirkt nichts. */
    .flatpickr-calendar.multiMonth .flatpickr-weekdaycontainer{
        margin-block-start: 0.75rem;
    }

    /* Luft zwischen dem Tagesraster des ersten Monats und dem Namen des
       zweiten, damit die Monate als Blöcke lesbar bleiben. */
    .flatpickr-calendar.multiMonth .flatpickr-month:nth-of-type(2){
        margin-block-start: 1.25rem;
    }

    /* Tageszellen bringen `max-width: 39px` mit, die Wochentage nur
   `flex: 1` — die Reihen standen dadurch verschoben. */
    .flatpickr-calendar.multiMonth .flatpickr-day{
        max-inline-size: none;
    }
}

/* — Consent-Banner (craft-cmp, Marken-Override) ————————————————— */
.cc-banner{
    position: fixed;
    inset-inline: 1rem;
    inset-block-end: 1rem;
    z-index: 9999;
    max-width: 34rem;
    margin-inline: auto;
    padding: 1.5rem;
    background: var( --color-greige );
    color: var( --color-anthrazit );
    box-shadow: 0 8px 40px rgb(0 0 0 / 0.25);
}

.cc-banner__head{
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-block-end: 0.75rem;
}

.cc-banner__title{
    font-size: 1.25rem;
}

.cc-banner .language-switcher{
    display: flex;
    gap: 0.75rem;
    margin: 0;
    padding: 0;
    font-size: 0.8rem;
}

.cc-banner .language-switcher a{
    color: inherit;
    text-decoration: none;
    opacity: 0.6;
}

.cc-banner .language-switcher a[aria-current="true"]{
    opacity: 1;
    text-decoration: underline;
}

.cc-banner__text{
    font-size: 0.9rem;
    line-height: 1.55;
}

.cc-banner__links{
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin: 0.75rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.8rem;
}

.cc-banner__links a{ color: inherit; }

.cc-banner__actions{
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-block-start: 1.25rem;
}

/* Annehmen und Ablehnen müssen gleich aussehen: Ein hervorgehobenes
   Annehmen lenkt die Wahl, die Einwilligung wäre nicht mehr freiwillig. */
.cc-btn{
    /* Basis 0: accept and reject share the space exactly, or word length alone
       would make one button larger. */
    flex: 1 1 0;
    padding: 0.65rem 1rem;
    border: 1px solid var( --color-button );
    background: var( --color-button );
    color: #fff;
    font: inherit;
    font-size: 0.8rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.cc-btn:hover{
    background: var( --color-anthrazit );
    border-color: var( --color-anthrazit );
}

/* Subordinate: "settings" and "cancel". This does not touch the
   accept/reject pair — both decisions stay equally prominent, one click away. */
.cc-btn--quiet{
    flex: 0 1 auto;
    background: transparent;
    border-color: transparent;
    color: var( --color-taupe );
    text-decoration: underline;
}

.cc-btn--quiet:hover{
    background: transparent;
    border-color: transparent;
    color: var( --color-anthrazit );
}

/* Am Telefon reichte der Platz für drei Knöpfe nebeneinander nicht — die
   Breiten fielen ungleich aus, und ungleich große Knöpfe lenken die Wahl.
   Gestapelt in einem Raster sind sie gleich breit. */
@media (width < 48rem){
    .cc-banner__actions{
        display: grid;
        grid-template-columns: 1fr 1fr;
    }

    .cc-btn--quiet{
        grid-column: 1 / -1;
    }
}

.cc-modal{
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgb(0 0 0 / 0.5);
}

.cc-modal__box{
    width: 100%;
    max-width: 32rem;
    max-height: 90vh;
    overflow-y: auto;
    padding: 1.75rem;
    background: var( --color-greige );
    color: var( --color-anthrazit );
}

.cc-modal__title{
    font-size: 1.25rem;
}

.cc-modal__list{
    list-style: none;
    margin: 1.25rem 0;
    padding: 0;
}

.cc-modal__row{
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    justify-content: space-between;
    padding-block: 1rem;
    border-block-start: 1px solid var( --color-taupe );
}

.cc-modal__label{
    margin: 0;
    font-weight: 600;
}

.cc-modal__desc{
    margin: 0.25rem 0 0;
    font-size: 0.85rem;
    line-height: 1.45;
    opacity: 0.8;
}

/* Toggle switch for the categories */
.cc-switch{
    position: relative;
    flex: none;
    inline-size: 2.75rem;
    block-size: 1.5rem;
}

.cc-switch input{
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.cc-switch__track{
    position: absolute;
    inset: 0;
    border-radius: 1rem;
    background: color-mix(in srgb, var( --color-taupe ) 35%, #fff);
    transition: background-color 0.15s ease;
}

.cc-switch__track::before{
    content: "";
    position: absolute;
    inset-block-start: 0.2rem;
    inset-inline-start: 0.2rem;
    inline-size: 1.1rem;
    block-size: 1.1rem;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.15s ease;
}

.cc-switch input:checked + .cc-switch__track{
    background: var( --color-button );
}

.cc-switch input:checked + .cc-switch__track::before{
    transform: translateX(1.25rem);
}

.cc-switch input:disabled + .cc-switch__track{
    opacity: 0.55;
    cursor: not-allowed;
}

.cc-switch input:focus-visible + .cc-switch__track{
    outline: 2px solid var( --color-anthrazit );
    outline-offset: 2px;
}

/* — Seitenwechsel: sanfte Überblendung (View Transitions API) ————
   Browser ohne Unterstützung wechseln einfach ohne Effekt. */
@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root){
    animation-duration: 0.35s;
    animation-timing-function: ease;
}

@media (prefers-reduced-motion: reduce){
    ::view-transition-old(root),
    ::view-transition-new(root){
        animation: none;
    }
}