/* ==========================================================================
   Homepage
   ========================================================================== */

/* --------------------------------------------------------------------------
   Hero
   Two states share one composition: with a photograph the type sits on a
   scrim, without one it sits on the warm ground. Nothing else changes, so an
   empty catalog still opens deliberately.
   -------------------------------------------------------------------------- */

/* The hero is pinned and the rest of the page scrolls over it. Sticky rather
   than fixed, so it releases naturally once .page-stack has covered it and no
   scroll listener is involved. */
.hero {
    position: sticky;
    top: 0;
    z-index: var(--z-base);
    display: flex;
    align-items: flex-end;
    isolation: isolate;
}

/* Carries the page background: without an opaque layer the pinned hero would
   show through every gap between the sections above it. */
.page-stack {
    position: relative;
    z-index: var(--z-raised);
    background-color: var(--color-bg-primary);
}

.hero--plain {
    /* Sized against the viewport minus the header, so hero and header together
       fill exactly one screen and the scroll-over begins immediately. */
    min-block-size: calc(100dvh - var(--header-height));
    padding-block: var(--section-spacing-md);
    background-color: var(--color-bg-secondary);
    border-block-end: var(--border-subtle);
}

.hero--image {
    min-block-size: calc(100dvh - var(--header-height));
    padding-block-end: var(--section-spacing-md);
}

@media (min-width: 1024px) {
    .hero--plain,
    .hero--image {
        min-block-size: calc(100dvh - var(--header-height-lg));
    }
}

.hero__media {
    position: absolute;
    inset: 0;
    z-index: var(--z-below);
    overflow: hidden;
    background-color: var(--color-surface-sunken);
}

/* Vertical scrim so the headline holds up over any photograph, dark or pale. */
.hero__scrim {
    position: absolute;
    inset: 0;
    background: var(--gradient-image-scrim);
}

.hero__content {
    max-inline-size: 46rem;
}

.hero--image .hero__content {
    color: var(--color-on-image);
}

.hero--image .hero__eyebrow {
    /* Solid white. At 13px this is small text needing 4.5:1, and the top of
       the scrim is its weakest point — the label stays quiet through its size
       and tracking, not through transparency. */
    color: var(--color-on-image);
}

.hero__title {
    /* Deliberately below --font-size-display: at full display scale the
       headline fills the hero and pushes the buttons out of the scrim's
       strongest region. */
    max-inline-size: 13ch;
    margin-block-start: var(--space-5);
    font-size: var(--font-size-5xl);
    font-weight: var(--font-weight-light);
    line-height: var(--line-height-display);
    letter-spacing: var(--letter-spacing-display);
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-block-start: var(--space-10);
}

/* The headline is the page's first impression; it arrives rather than appears.
   Content is never hidden waiting for it — this only runs on load. */
.hero__eyebrow,
.hero__title,
.hero__actions {
    animation: hero-rise var(--duration-editorial) var(--ease-out) both;
}

.hero__title { animation-delay: 70ms; }
.hero__actions { animation-delay: 160ms; }

@keyframes hero-rise {
    from {
        opacity: 0;
        transform: translateY(1.25rem);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* On small screens the display size needs a shorter measure to stay readable. */
@media (max-width: 639px) {
    .hero__title {
        font-size: var(--font-size-4xl);
    }

    .hero__actions .button {
        /* Full-width buttons stack cleanly and are easier to hit. */
        flex: 1 1 100%;
    }
}

/* --------------------------------------------------------------------------
   Categories
   -------------------------------------------------------------------------- */

.category-list {
    display: grid;
    grid-template-columns: 1fr;
    border-block-start: var(--border-subtle);
}

@media (min-width: 768px) {
    .category-list {
        grid-template-columns: repeat(2, 1fr);
        column-gap: var(--space-12);
    }
}

/* --------------------------------------------------------------------------
   Editorial spotlight
   Asymmetric on purpose: the image takes the larger share and breaks the
   even-column rhythm of the sections around it (DESIGN.md 9, 40).
   -------------------------------------------------------------------------- */

.spotlight {
    display: grid;
    gap: var(--space-8);
}

.spotlight__media {
    overflow: hidden;
    aspect-ratio: var(--aspect-editorial);
    background-color: var(--color-surface-sunken);
    border-radius: var(--radius-md);
}

.spotlight__title {
    margin-block-start: var(--space-4);
    font-size: var(--font-size-3xl);
}

.spotlight__location {
    margin-block-start: var(--space-3);
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    letter-spacing: var(--letter-spacing-wide);
}

.spotlight__lead {
    max-inline-size: 46ch;
    margin-block-start: var(--space-6);
    color: var(--color-text-secondary);
    font-size: var(--font-size-lg);
    line-height: var(--line-height-relaxed);
}

.spotlight__facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-10);
    margin-block: var(--space-8);
    padding-block-start: var(--space-6);
    border-block-start: var(--border-subtle);
}

.spotlight__facts dt {
    color: var(--color-text-secondary);
    font-size: var(--font-size-xs);
    letter-spacing: var(--letter-spacing-eyebrow);
    text-transform: uppercase;
}

.spotlight__facts dd {
    margin: var(--space-2) 0 0;
    font-family: var(--font-display);
    font-size: var(--font-size-2xl);
}

@media (min-width: 1024px) {
    .spotlight {
        grid-template-columns: 1.35fr 1fr;
        align-items: center;
        gap: var(--space-16);
    }

    .spotlight__body {
        padding-inline-end: var(--space-4);
    }
}

/* --------------------------------------------------------------------------
   Positioning
   -------------------------------------------------------------------------- */

.positioning {
    display: grid;
    gap: var(--space-10);
    list-style: none;
    margin: 0;
    padding: 0;
    counter-reset: positioning;
}

.positioning__item {
    padding-block-start: var(--space-5);
    border-block-start: var(--border-width) solid var(--color-border-strong);
}

.positioning__number {
    color: var(--color-accent-text);
    font-family: var(--font-display);
    font-size: var(--font-size-lg);
    letter-spacing: var(--letter-spacing-wide);
}

.positioning__title {
    margin-block-start: var(--space-4);
    font-size: var(--font-size-lg);
}

.positioning__text {
    max-inline-size: 38ch;
    margin-block-start: var(--space-3);
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-relaxed);
}

@media (min-width: 640px) {
    .positioning {
        grid-template-columns: repeat(2, 1fr);
        gap: var(--space-10) var(--space-8);
    }
}

@media (min-width: 1024px) {
    .positioning {
        grid-template-columns: repeat(4, 1fr);
        gap: var(--space-8);
    }
}


/* --------------------------------------------------------------------------
   Positioning — pinned horizontal pan

   Each point fills the screen; scrolling down moves the row sideways.

   Built on CSS scroll-driven animations: the browser ties the pan to scroll
   position off the main thread, so there is no scroll listener, no rAF loop
   and nothing to throttle (CLAUDE.md 15). Only `transform` is animated.

   Everything below is inside a support-and-motion guard. Where scroll
   timelines are unavailable, or the visitor asked for reduced motion, none of
   it applies and the points render as the grid defined above — which is why
   the fallback needs no separate rules.

   The short-viewport cut-off matters: a full-screen panel on a landscape
   phone would leave no room for the text it exists to show.
   -------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) and (min-height: 34rem) {

        .positioning-section {
            /* The pinned block is full-bleed; the section's own vertical
               rhythm would otherwise leave a gap under the last panel. */
            padding-block-end: 0;
        }

        .positioning-scroll {
            /* One screen of scrolling per panel: the distance the pan needs. */
            block-size: calc(var(--panels, 4) * 100svh);
            /* Names the range the pan is measured against. "contain" then means
               exactly the period this block covers the whole viewport — which
               is precisely how long the child stays pinned. */
            view-timeline-name: --positioning;
            view-timeline-axis: block;
        }

        .positioning-scroll__pin {
            position: sticky;
            inset-block-start: 0;
            block-size: 100svh;
            overflow: hidden;
        }

        .positioning {
            display: flex;
            gap: 0;
            block-size: 100%;
            inline-size: calc(var(--panels, 4) * 100%);

            animation: positioning-pan linear both;
            animation-timeline: --positioning;
            animation-range: contain;
            will-change: transform;
        }

        .positioning__item {
            flex: 0 0 calc(100% / var(--panels, 4));
            display: flex;
            flex-direction: column;
            justify-content: center;
            block-size: 100%;
            padding-inline: var(--container-gutter);
            padding-block: var(--space-16) var(--space-20);
            /* The rule between points belongs to the stacked layout. */
            border-block-start: 0;
        }

        .positioning__number {
            font-size: var(--font-size-2xl);
        }

        .positioning__title {
            max-inline-size: 16ch;
            margin-block-start: var(--space-5);
            font-family: var(--font-display);
            font-size: var(--font-size-3xl);
            line-height: var(--line-height-tight);
        }

        .positioning__text {
            max-inline-size: 46ch;
            margin-block-start: var(--space-5);
            font-size: var(--font-size-base);
        }

        /* -- progress ---------------------------------------------------- */

        .positioning-progress {
            position: absolute;
            inset-block-end: var(--space-10);
            inset-inline: var(--container-gutter);
            block-size: 2px;
            background-color: var(--color-border);
        }

        .positioning-progress__bar {
            display: block;
            block-size: 100%;
            background-color: var(--color-accent);
            transform-origin: left center;
            /* Driven by the same range as the pan, so the bar and the panels
               can never disagree about where the visitor is. */
            animation: positioning-progress linear both;
            animation-timeline: --positioning;
            animation-range: contain;
        }

        /* A full-screen panel needs display-scale type, and something holding
           the far side or two thirds of a wide monitor is empty. That job
           belongs to the drawing below, so the text keeps the left half. */
        @media (min-width: 64rem) {
            .positioning__item {
                position: relative;
                padding-inline: max(
                    var(--container-gutter),
                    calc((100vw - var(--container-max)) / 2 + var(--container-gutter))
                );
            }

            .positioning__item {
                /* The right half belongs to the drawing. */
                padding-inline-end: 44vw;
            }

            .positioning__title {
                max-inline-size: 13ch;
                font-size: clamp(2.5rem, 3.6vw, 3.5rem);
            }

            .positioning__text {
                max-inline-size: 38ch;
                font-size: var(--font-size-lg);
            }
        }
    }
}

@keyframes positioning-pan {
    /* Translate the track by every panel but the last. Percentages resolve
       against the track's own width, so this holds for any --panels. */
    to {
        transform: translateX(calc(-100% + (100% / var(--panels, 4))));
    }
}

@keyframes positioning-progress {
    from { transform: scaleX(0); }
    to { transform: scaleX(1); }
}

/* --------------------------------------------------------------------------
   Positioning — the turning house

   A real three-dimensional object, not a picture of one: eight faces placed in
   space with CSS transforms and a perspective, turned by the scroll on the
   same timeline as the pan. No canvas, no WebGL, no library — the compositor
   handles it like any other transform.

   Geometry. Every face is centred on the house's own centre (left/top 50% and
   a negative margin of half its size), and every placement is a single
   translate3d from there. Anchoring faces to an edge and then translating is
   what makes this kind of model fall apart: the offsets have to account for
   each element's own size, and one of them always gets forgotten.

   The model, in units of --u, origin at the centre of the whole 8u height:

       ridge        y = -4u
       eave         y = -1u          walls 6u × 5u, box 6u deep
       base         y = +4u
       pitch        45°, so each slope is 3√2 ≈ 4.243u

   Drawn as a wireframe: it matches the brand mark's fine-line language, and it
   keeps the shape readable from the angles where a solid model would show
   nothing but a flat silhouette.

   Hidden outside the guarded block, so the fallback layout is exactly the
   stacked grid it has always been, with nothing floating over it.
   -------------------------------------------------------------------------- */

.positioning-figure {
    display: none;
}

@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) and (min-height: 34rem) {

        .positioning-figure {
            display: grid;
            place-items: center;
            position: absolute;
            z-index: 0;
            pointer-events: none;
            /* Shallow enough to read as depth, not as a fisheye. */
            perspective: 52rem;
        }

        .house {
            position: relative;
            inline-size: calc(var(--u) * 6);
            block-size: calc(var(--u) * 8);
            transform-style: preserve-3d;
            animation: house-turn linear both;
            animation-timeline: --positioning;
            animation-range: contain;
        }

        /* Grouping for the markup's sake only: taken out of layout so every
           face is positioned against the house itself. */
        .house__body,
        .house__roof {
            display: contents;
        }

        .house__face,
        .house__slope,
        .house__gable {
            position: absolute;
            inset-inline-start: 50%;
            inset-block-start: 50%;
            border: 1px solid var(--color-accent);
            background-color: color-mix(in srgb, var(--color-accent) 6%, transparent);
            /* A wireframe shows its far side; the back faces are part of the
               drawing rather than something to cull. */
            backface-visibility: visible;
        }

        /* -- walls: 6u × 5u, centred at y = +1.5u ------------------------- */

        .house__face {
            inline-size: calc(var(--u) * 6);
            block-size: calc(var(--u) * 5);
            margin-inline-start: calc(var(--u) * -3);
            margin-block-start: calc(var(--u) * -2.5);
        }

        .house__face--front {
            transform: translate3d(0, calc(var(--u) * 1.5), calc(var(--u) * 3));
        }

        .house__face--back {
            transform: translate3d(0, calc(var(--u) * 1.5), calc(var(--u) * -3)) rotateY(180deg);
        }

        .house__face--right {
            transform: translate3d(calc(var(--u) * 3), calc(var(--u) * 1.5), 0) rotateY(90deg);
        }

        .house__face--left {
            transform: translate3d(calc(var(--u) * -3), calc(var(--u) * 1.5), 0) rotateY(-90deg);
        }

        /* -- slopes: hinge at the eave, meet at the ridge ------------------ */

        .house__slope {
            inline-size: calc(var(--u) * 6);
            block-size: calc(var(--u) * 4.243);
            margin-inline-start: calc(var(--u) * -3);
            margin-block-start: calc(var(--u) * -2.1215);
        }

        /* Centre at (0, -2.5u, ±1.5u): a 45° lean puts the top edge on the
           ridge (y -4u, z 0) and the bottom edge on the eave (y -1u, z ±3u). */
        .house__slope--front {
            transform: translate3d(0, calc(var(--u) * -2.5), calc(var(--u) * 1.5)) rotateX(45deg);
        }

        .house__slope--back {
            transform: translate3d(0, calc(var(--u) * -2.5), calc(var(--u) * -1.5)) rotateX(-45deg);
        }

        /* -- gables: the triangular ends ---------------------------------- */

        .house__gable {
            inline-size: calc(var(--u) * 6);
            block-size: calc(var(--u) * 3);
            margin-inline-start: calc(var(--u) * -3);
            margin-block-start: calc(var(--u) * -1.5);
            clip-path: polygon(50% 0, 100% 100%, 0 100%);
            /* clip-path cuts the border away with the corners, so the triangle
               is read from the slopes and walls meeting around it. */
            border: 0;
            background-color: color-mix(in srgb, var(--color-accent) 11%, transparent);
        }

        .house__gable--right {
            transform: translate3d(calc(var(--u) * 3), calc(var(--u) * -2.5), 0) rotateY(90deg);
        }

        .house__gable--left {
            transform: translate3d(calc(var(--u) * -3), calc(var(--u) * -2.5), 0) rotateY(-90deg);
        }

        /* -- placement ---------------------------------------------------- */

        .positioning-figure {
            --u: clamp(1.1rem, 4.4vw, 1.6rem);
            inset-block-start: 10vh;
            inset-inline: 50%;
            inline-size: min(58vw, 15rem);
            aspect-ratio: 1;
            transform: translateX(-50%);
        }

        .positioning__item {
            padding-block-start: calc(10vh + min(58vw, 15rem) + var(--space-6));
            justify-content: flex-start;
        }

        @media (min-width: 64rem) {
            .positioning-figure {
                --u: clamp(1.8rem, 2.6vw, 2.5rem);
                inset-block-start: 50%;
                inset-inline-start: auto;
                inset-inline-end: max(
                    var(--container-gutter),
                    calc((100vw - var(--container-max)) / 2 + var(--container-gutter))
                );
                inline-size: min(34vw, 26rem);
                transform: translateY(-50%);
            }

            .positioning__item {
                padding-block-start: var(--space-16);
                justify-content: center;
            }
        }
    }
}

/* Two thirds of a turn across the four points.

   The span is 240°, not the more obvious 270°, because four points across 270°
   land 90° apart — and a house with a symmetric gable roof looks the same from
   angles 180° apart, so the first and third points would show the same picture.
   240° puts them 80° apart instead, and no two of the four repeat.

   The start avoids a face-on view, which flattens to a rectangle, and the tilt
   eases slightly so the roof stays part of the silhouette throughout. */
@keyframes house-turn {
    from { transform: rotateX(-19deg) rotateY(-25deg); }
    to   { transform: rotateX(-9deg) rotateY(215deg); }
}
