/* #region Sections */
.steps-section,
.mechanic-section,
.testimonials,
.b2b-section,
.pricing-section,
.marquee-section,
.patronage-section,
.flip-section,
.strategy-section,
.financial-section,
.portfolio-section,
.usage-mosaic-section,
.artifacts-section,
.results-section,
.filmable-closing {
    padding: var(--section-space) 0;
    background: var(--black);
    border-bottom: 1px solid var(--section-divider);
}

.mechanic-section {
    padding-block: 0;
}

.pricing-section,
.filmable-closing {
    border-bottom: 0;
}

    .steps-section > .container,
    .mechanic-section > .container,
    .testimonials > .container,
    .b2b-section > .container,
    .pricing-section > .container,
    .marquee-section > .container,
    .patronage-section > .container,
    .flip-section > .container,
    .strategy-section > .container,
    .financial-section > .container,
    .portfolio-section > .container,
    .usage-mosaic-section > .container,
    .artifacts-section > .container,
    .results-section > .container,
    .filmable-closing > .container,
    .ecosystem-pairing > .container,
    .ecosystem-map > .container,
    .product-journal > .container {
        width: min(90vw, var(--section-content-max));
        max-width: none;
        margin-inline: auto;
        padding-inline: 0;
    }
/* #endregion */

/* #region Editorial hero */
/* A hero fills the screen, here as everywhere else. The 760 px is only a floor for a short, wide
   window, where a full viewport would crush the exhibit — written flat it overrode the shared
   100svh and left a tall display with a hero ending mid-screen. */
.hero-cinematic--exhibit {
    min-height: max(760px, 100svh);
    isolation: isolate;
    background: var(--hero-surface);
}

    .hero-cinematic--exhibit .hero-bg {
        overflow: hidden;
        background: var(--black);
    }

/* The lattice behind the exhibit, the axis down the middle and their reveal moved to site.css, where
   the home page's hero reads the very same rules. Written out here and again in home.css, one
   behaviour exists twice and a fix to one hero never reaches the other. */

.hero-cinematic--exhibit .hero-content {
    width: min(90vw, var(--hero-content-max));
    max-width: none;
    margin-inline: auto;
    display: grid;
    grid-template-columns: minmax(0, 500px) 1fr;
    align-items: center;
    gap: clamp(4rem, 8vw, 9rem);
    /* Never less than the fixed header: 8vh of a 768px laptop is shorter than it. */
    padding: max(8vh, calc(var(--site-header-h, 72px) + 1.5rem)) 0 3vh;
    text-align: left;
    text-shadow: none;
}

.hero-cinematic--exhibit .hero-copy {
    position: relative;
    z-index: 2;
}

/* A floor under the vh gap: on a short screen (landscape, an in-app browser) 1.8vh fell to a few
   pixels and a wrapped eyebrow met the title. */
.hero-cinematic--exhibit .h-tag {
    margin-bottom: max(.75rem, 1.8vh);
}

.hero-cinematic--exhibit .h-title {
    max-width: 8ch;
    margin-bottom: 4vh;
    font-size: var(--hero-title-size);
    line-height: var(--hero-title-line-height);
    letter-spacing: -0.025em;
}

.hero-cinematic--exhibit .h-desc {
    max-width: 500px;
    margin: 0;
    padding: 0;
    border: 0;
    color: var(--white-soft);
    font-family: var(--font);
    font-size: clamp(1rem, 1.15vw, 1.15rem);
    font-weight: 300;
    line-height: 1.75;
}

.hero-cinematic--exhibit .hero-cta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.6rem;
    margin-top: clamp(2.4rem, 3.6vw, 3.4rem);
}

/* The hero's own call to action, styled here rather than on .home-link: that class lives in home.css,
   which is loaded on the home page alone, so everywhere else the link renders with no styling. */
.hero-cta {
    display: inline-flex;
    align-items: center;
    gap: 14px;
    width: fit-content;
    margin-top: clamp(2.4rem, 3.6vw, 3.4rem);
    /* The line under it and its hover — a gold line drawn over a white hairline — are site.css's
       underlined-CTA rule, shared with .home-product-link. */
    padding-bottom: .8rem;
    font-family: var(--font-sans);
    font-size: clamp(.65rem, .8vw, .78rem);
    font-weight: 300;
    letter-spacing: .22em;
    line-height: 1.5;
    text-decoration: none;
    text-transform: uppercase;
}

/* Inside the row the spacing belongs to the row, not to each link in it. */
.hero-cta-row .hero-cta {
    margin-top: 0;
}

.hero-cinematic--exhibit .hero-cta-note {
    color: var(--text-muted);
    font-family: var(--font);
    font-size: .82rem;
    font-weight: 300;
    letter-spacing: .02em;
}

.hero-exhibit {
    width: min(52vw, 760px);
    max-width: calc(100% + clamp(2rem, 4vw, 4.5rem));
    margin: 0;
    justify-self: end;
    position: relative;
    perspective: 1400px;
    clip-path: none;
    /* The entrance is in site.css, shared with the home page's figure. Pinning the exhibit here with
       `animation: none` leaves only the left half of the hero moving. */
}

    .hero-exhibit::before {
        content: "";
        position: absolute;
        inset: -2.5%;
        z-index: -1;
        padding: 1px;
        background: var(--section-divider);
        -webkit-mask: linear-gradient(#050505 0 0) content-box, linear-gradient(#050505 0 0);
        -webkit-mask-composite: xor;
        mask-composite: exclude;
        pointer-events: none;
    }

    .hero-exhibit::after {
        content: "";
        position: absolute;
        inset: -2.5%;
        z-index: -1;
        padding: 1px;
        background: var(--hairline-faint);
        -webkit-mask: linear-gradient(#050505 0 0) content-box, linear-gradient(#050505 0 0);
        -webkit-mask-composite: xor;
        mask-composite: exclude;
        pointer-events: none;
    }

.hero-exhibit-stage {
    position: relative;
    width: 100%;
    margin-left: 0;
    aspect-ratio: 1;
    overflow: hidden;
    border: 0;
    box-shadow: 0 34px 90px rgba(5, 5, 5, .42);
    transform-style: preserve-3d;
}

    .hero-exhibit-stage::before {
        content: "";
        position: absolute;
        inset: 7% -9% -7% 9%;
        border: 1px solid var(--hairline-faint);
        transform: translateZ(-35px);
    }

    .hero-exhibit-stage::after {
        content: "";
        position: absolute;
        inset: 0;
        background: linear-gradient(145deg, transparent 62%, rgba(5, 5, 5, 0.34));
        pointer-events: none;
        animation: heroExhibitAtmosphere 12s ease-in-out infinite alternate;
    }

@keyframes heroExhibitAtmosphere {
    from {
        opacity: .56;
    }

    to {
        opacity: 1;
    }
}

/* A real <img> since the LCP change: the absolute box comes from inset, but a replaced
   element does not stretch on insets alone, so width and height are stated to match.
   It is not always an <img>, though: Odoardo Research™ and the library index name their one
   photograph in the stylesheet and fill this box with a background instead — and a background
   ignores object-fit. Without the two lines below those heroes drew a 1920px photograph at its
   natural size inside a 600px box, which is to say a corner of it. The pair is stated together so a
   page cannot pick one kind of element and lose the framing. */
.hero-exhibit-image {
    position: absolute;
    inset: 5%;
    width: 90%;
    height: 90%;
    display: block;
    object-fit: cover;
    object-position: center;
    /* The house black under the photograph, so while it loads the lattice behind it does not show
       through as grey. */
    background-color: var(--black);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    filter: var(--photo-at-rest);
    box-shadow: 0 30px 80px rgba(5, 5, 5, 0.32);
    transform: translateZ(28px) scale(1.025);
}

.hero-cinematic--alibi .hero-exhibit,
.hero-cinematic--soul .hero-exhibit,
.hero-cinematic--art .hero-exhibit,
.hero-cinematic--space .hero-exhibit,
.hero-cinematic--lens .hero-exhibit,
.hero-cinematic--manifest .hero-exhibit,
.hero-cinematic--journal .hero-exhibit,
.hero-cinematic--filmable .hero-exhibit,
.hero-cinematic--hotel .hero-exhibit,
.hero-cinematic--city .hero-exhibit,
.hero-cinematic--industry .hero-exhibit,
.hero-cinematic--healthcare .hero-exhibit,
.hero-cinematic--partner .hero-exhibit,
.hero-cinematic--restaurant .hero-exhibit,
.hero-cinematic--museum .hero-exhibit,
.hero-cinematic--residence .hero-exhibit,
.hero-cinematic--research .hero-exhibit,
.hero-cinematic--library .hero-exhibit {
    perspective: none;
}

.hero-cinematic--alibi .hero-exhibit-stage,
.hero-cinematic--soul .hero-exhibit-stage,
.hero-cinematic--art .hero-exhibit-stage,
.hero-cinematic--space .hero-exhibit-stage,
.hero-cinematic--lens .hero-exhibit-stage,
.hero-cinematic--manifest .hero-exhibit-stage,
.hero-cinematic--journal .hero-exhibit-stage,
.hero-cinematic--filmable .hero-exhibit-stage,
.hero-cinematic--hotel .hero-exhibit-stage,
.hero-cinematic--city .hero-exhibit-stage,
.hero-cinematic--industry .hero-exhibit-stage,
.hero-cinematic--healthcare .hero-exhibit-stage,
.hero-cinematic--partner .hero-exhibit-stage,
.hero-cinematic--restaurant .hero-exhibit-stage,
.hero-cinematic--museum .hero-exhibit-stage,
.hero-cinematic--residence .hero-exhibit-stage,
.hero-cinematic--research .hero-exhibit-stage,
.hero-cinematic--library .hero-exhibit-stage {
    transform-style: flat;
    aspect-ratio: 1;
}

    .hero-cinematic--alibi .hero-exhibit-stage::before,
    .hero-cinematic--soul .hero-exhibit-stage::before,
    .hero-cinematic--art .hero-exhibit-stage::before,
    .hero-cinematic--space .hero-exhibit-stage::before,
    .hero-cinematic--lens .hero-exhibit-stage::before,
    .hero-cinematic--manifest .hero-exhibit-stage::before,
    .hero-cinematic--journal .hero-exhibit-stage::before,
    .hero-cinematic--filmable .hero-exhibit-stage::before,
    .hero-cinematic--hotel .hero-exhibit-stage::before,
    .hero-cinematic--city .hero-exhibit-stage::before,
    .hero-cinematic--industry .hero-exhibit-stage::before,
    .hero-cinematic--healthcare .hero-exhibit-stage::before,
    .hero-cinematic--partner .hero-exhibit-stage::before,
    .hero-cinematic--restaurant .hero-exhibit-stage::before,
    .hero-cinematic--museum .hero-exhibit-stage::before,
    .hero-cinematic--residence .hero-exhibit-stage::before,
    .hero-cinematic--research .hero-exhibit-stage::before,
    .hero-cinematic--library .hero-exhibit-stage::before {
        content: none;
    }

    .hero-cinematic--alibi .hero-exhibit-stage::after,
    .hero-cinematic--soul .hero-exhibit-stage::after,
    .hero-cinematic--art .hero-exhibit-stage::after,
    .hero-cinematic--space .hero-exhibit-stage::after,
    .hero-cinematic--lens .hero-exhibit-stage::after,
    .hero-cinematic--manifest .hero-exhibit-stage::after,
    .hero-cinematic--journal .hero-exhibit-stage::after,
    .hero-cinematic--filmable .hero-exhibit-stage::after,
    .hero-cinematic--hotel .hero-exhibit-stage::after,
    .hero-cinematic--city .hero-exhibit-stage::after,
    .hero-cinematic--industry .hero-exhibit-stage::after,
    .hero-cinematic--healthcare .hero-exhibit-stage::after,
    .hero-cinematic--partner .hero-exhibit-stage::after,
    .hero-cinematic--restaurant .hero-exhibit-stage::after,
    .hero-cinematic--museum .hero-exhibit-stage::after,
    .hero-cinematic--residence .hero-exhibit-stage::after,
    .hero-cinematic--research .hero-exhibit-stage::after,
    .hero-cinematic--library .hero-exhibit-stage::after {
        content: "";
        animation: none;
        opacity: 1;
        background: linear-gradient(145deg, transparent 64%, rgba(5, 5, 5, 0.3));
    }

.hero-cinematic--alibi .hero-exhibit-image,
.hero-cinematic--soul .hero-exhibit-image,
.hero-cinematic--art .hero-exhibit-image,
.hero-cinematic--space .hero-exhibit-image,
.hero-cinematic--lens .hero-exhibit-image,
.hero-cinematic--manifest .hero-exhibit-image,
.hero-cinematic--journal .hero-exhibit-image,
.hero-cinematic--filmable .hero-exhibit-image,
.hero-cinematic--hotel .hero-exhibit-image,
.hero-cinematic--city .hero-exhibit-image,
.hero-cinematic--industry .hero-exhibit-image,
.hero-cinematic--healthcare .hero-exhibit-image,
.hero-cinematic--partner .hero-exhibit-image,
.hero-cinematic--restaurant .hero-exhibit-image,
.hero-cinematic--museum .hero-exhibit-image,
.hero-cinematic--residence .hero-exhibit-image,
.hero-cinematic--research .hero-exhibit-image,
.hero-cinematic--library .hero-exhibit-image {
    inset: 0;
    width: 100%;
    height: 100%;
    transform: none;
    box-shadow: none;
    object-position: center 48%;
    background-position: center 48%;
    filter: var(--photo-at-rest);
    transition: transform 1.8s var(--ease-house), filter var(--motion-slow) var(--ease-house);
}

/* The journal's hero holds its colour back — the one place a photograph is a mood rather than a
   product. The exposure is the house's; only the mute belongs to this page. */
.hero-cinematic--journal .hero-exhibit-image {
    filter: var(--photo-mute) var(--photo-at-rest);
}

@media (max-width: 1100px) {
    .hero-cinematic--exhibit {
        height: auto;
        min-height: 100svh;
        align-items: flex-start;
    }

        /* 17vh of a phone held sideways is shorter than the fixed header, which then covers the eyebrow. */
        .hero-cinematic--exhibit .hero-content {
            grid-template-columns: 1fr;
            gap: 7vh;
            padding: max(17vh, calc(var(--site-header-h, 72px) + 2rem)) 0 15vh;
        }

    /* The axis and the lattice divide two columns; in one column they run through the title. */
    .hero-cinematic--exhibit::before,
    .hero-cinematic--exhibit .hero-bg::after {
        content: none;
    }

        .hero-cinematic--exhibit .h-tag {
            margin-bottom: max(.75rem, 1.6vh);
        }

        .hero-cinematic--exhibit .h-title {
            max-width: none;
            margin-bottom: 3vh;
            font-size: var(--hero-title-size);
        }

        .hero-cinematic--exhibit .h-desc {
            max-width: 520px;
            font-size: 1rem;
        }

    /* Under left-aligned copy, so on its left edge rather than pushed to the right one. */
    .hero-exhibit {
        width: min(100%, 620px);
        max-width: none;
        justify-self: start;
    }

    .hero-exhibit-stage {
        width: 100%;
        aspect-ratio: 1;
    }

    .hero-cinematic--exhibit .scroll-indicator {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .hero-exhibit {
        opacity: 1;
        transform: none;
        clip-path: none;
        animation: none;
    }

    .hero-exhibit-stage::after {
        animation: none;
    }

    .hero-cinematic--exhibit .hero-bg::before,
    .hero-cinematic--exhibit .hero-bg::after,
    .hero-cinematic--exhibit::after,
    .hero-exhibit::before,
    .hero-exhibit::after,
    .hero-exhibit-stage,
    .hero-exhibit-image {
        transition: none;
    }

    .hero-cinematic--exhibit .hero-bg::after,
    .hero-cinematic--exhibit::before,
    .hero-cinematic--exhibit::after,
    .hero-exhibit::before {
        animation: none;
    }
}
/* #endregion */

/* #region Manifesto */
.manifesto {
    padding: var(--section-space) 5vw;
    background: var(--black);
    position: relative;
    z-index: 2;
    border-bottom: 1px solid var(--section-divider);
}

.manifesto-grid {
    display: grid;
    grid-template-columns: 1.5fr 1fr;
    gap: 8vw;
    align-items: flex-start;
    max-width: var(--section-content-max);
    width: 100%;
    margin: 0 auto;
}

.manifesto-left p {
    font-family: var(--font-display);
    font-size: clamp(2rem, 3.5vw, 3.5rem);
    font-weight: 300;
    color: var(--white);
    line-height: 1.4;
}

    .manifesto-left p i {
        font-style: italic;
        color: var(--gold);
    }

.manifesto-right {
    border-left: 1px solid var(--line);
    padding-left: 4vw;
}

    .manifesto-right p {
        font-family: var(--font);
        font-size: 1.05rem;
        font-weight: 200;
        color: var(--text-muted);
        line-height: 1.8;
        margin-bottom: 2vh;
    }

    /* The letter's paragraphs already carry a bottom margin; the invitation needs the same distance above it
       so it does not sit against the last line. */
    .manifesto-right .odo-button {
        margin-top: 3vh;
    }

/* #endregion */

/* #region Mechanic (interactive 3D cube) */
.mechanic-section {
    position: relative;
    padding-bottom: clamp(6rem, 10vw, 10rem);
    background: var(--black);
    /* The 3D cube's decorative ring is wider than a phone screen; clip keeps the sticky visual working. */
    overflow-x: clip;
}

.mechanic-inner {
    position: relative;
    display: grid;
    grid-template-columns: minmax(24rem, 1.15fr) minmax(20rem, .85fr);
    gap: clamp(3rem, 8vw, 9rem);
    align-items: center;
    min-height: clamp(48rem, 56vw, 54rem);
    width: min(90vw, var(--hero-content-max));
    margin: 0 auto;
}

.mechanic-header {
    width: min(90vw, var(--hero-content-max));
    margin-inline: auto;
    padding: clamp(7rem, 12vw, 11rem) 0 clamp(3rem, 6vw, 6rem);
}

.mechanic-header--soul {
    padding-bottom: clamp(.75rem, 1.5vw, 1.5rem);
}

.visual-sticky {
    position: relative;
    min-height: 40rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    perspective: 1500px;
}

.artifact-wrapper {
    position: relative;
    /* One step of the film, and the only place its length is written in CSS: every beat below is
       a fraction of it, so a step that plays once (the gesture) never gets cut mid-cycle.
       `playbackStepDelay` in soul.js is the same number — change both together. */
    --soul-step: 5s;
    width: 300px;
    height: 300px;
    display: flex;
    justify-content: center;
    align-items: center;
}

    .artifact-wrapper::before {
        position: absolute;
        inset: -45%;
        border: 1px solid var(--hairline-faint);
        border-radius: 50%;
        content: "";
        opacity: .35;
        transform: scale(.78);
        transition: opacity var(--motion-slow) var(--ease-house), transform var(--motion-slow) var(--ease-house);
    }

    /* The aura widens the moment the object is woken, before it says anything. */
    .artifact-wrapper.state-2::before {
        opacity: .6;
        transform: scale(.88);
    }

    .artifact-wrapper.state-3::before,
    .artifact-wrapper.state-4::before {
        opacity: 1;
        transform: scale(1);
    }

.cube-shadow {
    position: absolute;
    width: 300px;
    height: 300px;
    background: rgba(5,5,5,0.95);
    box-shadow: 0 0 60px 40px rgba(5,5,5,0.95);
    transition: transform 1.65s var(--ease-house), background 1.2s var(--ease-house), box-shadow 1.2s var(--ease-house);
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-150px) scale(1);
}

    .cube-shadow::before,
    .cube-shadow::after {
        position: absolute;
        content: '';
        opacity: 0;
        pointer-events: none;
    }

    .cube-shadow::before {
        inset: -12%;
        border: 1px solid rgba(var(--gold-rgb),.48);
    }

    .cube-shadow::after {
        inset: 15%;
        border: 1px solid rgba(var(--gold-bright-rgb), .7);
        background: linear-gradient(90deg, transparent calc(50% - .5px), rgba(var(--gold-bright-rgb), .5) 50%, transparent calc(50% + .5px)), linear-gradient(0deg, transparent calc(50% - .5px), rgba(var(--gold-bright-rgb), .5) 50%, transparent calc(50% + .5px));
    }

.artifact-cube {
    width: 100%;
    height: 100%;
    position: absolute;
    transform-style: preserve-3d;
    transition: transform 1.65s var(--ease-house);
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(0px);
}

.cube-motion {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    animation: cubeMotion 12s linear infinite;
    will-change: transform;
}

.cube-face {
    position: absolute;
    width: 300px;
    height: 300px;
    border: 1px solid rgba(245, 245, 245, .05);
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
}

    .cube-face.top {
        transform: rotateX(90deg) translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .55), rgba(5, 5, 5, .55)), url('/img/art/frame-wood.webp');
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        transition: all 1s var(--ease-house);
    }

    .cube-face.front {
        transform: translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .55), rgba(5, 5, 5, .8)), url('/img/art/frame-wood.webp');
        display: flex;
        justify-content: center;
        align-items: center;
    }

    .cube-face.right {
        transform: rotateY(90deg) translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .6), rgba(5, 5, 5, .85)), url('/img/art/frame-wood.webp');
    }

    .cube-face.back {
        transform: rotateY(180deg) translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .55), rgba(5, 5, 5, .7)), url('/img/art/frame-wood.webp');
    }

    .cube-face.left {
        transform: rotateY(-90deg) translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .6), rgba(5, 5, 5, .85)), url('/img/art/frame-wood.webp');
    }

    .cube-face.bottom {
        transform: rotateX(-90deg) translateZ(150px);
        background-image: linear-gradient(to bottom, rgba(5, 5, 5, .75), rgba(5, 5, 5, .9)), url('/img/art/frame-wood.webp');
    }

/* The mark on the cube face, held back to a whisper: it is engraved into the object rather than printed
   on it. The holder stays an element of its own because the hairline below is drawn on it, and a
   pseudo-element cannot be drawn on an image. */
.cube-logo {
    position: absolute;
    opacity: 0.15;
    z-index: 5;
    transition: all 0.6s var(--ease-house);
    text-align: center;
    width: 100%;
    left: 0;
}

    .cube-logo img {
        display: inline-block;
        width: 62%;
        height: auto;
    }

    .cube-logo::after {
        position: absolute;
        right: 20%;
        bottom: -18px;
        left: 20%;
        height: 1px;
        background: linear-gradient(90deg, transparent, rgba(var(--gold-bright-rgb), .8), transparent);
        content: '';
        opacity: 0;
        transform: scaleX(0);
    }

.artifact-wrapper.state-boot .artifact-cube {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-20px);
}

.artifact-wrapper.state-boot .cube-shadow {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-170px) scale(1.05);
}

.artifact-wrapper.state-boot .cube-logo,
.artifact-wrapper.state-1 .cube-logo {
    opacity: 0;
}

.microchip-ring {
    position: absolute;
    width: 112px;
    height: 112px;
    display: flex;
    justify-content: center;
    align-items: center;
    background: linear-gradient(90deg, transparent 0, rgba(var(--gold-rgb),.18) 38%, rgba(var(--gold-rgb),.65) 50%, rgba(var(--gold-rgb),.18) 62%, transparent 100%) center / 100% 1px no-repeat, linear-gradient(0deg, transparent 0, rgba(var(--gold-rgb),.12) 40%, rgba(var(--gold-rgb),.42) 50%, rgba(var(--gold-rgb),.12) 60%, transparent 100%) center / 1px 100% no-repeat;
    opacity: 0;
    transform: scale(.55);
    transition: opacity .7s var(--ease-house), transform 1s var(--ease-house);
    z-index: 2;
}

    .microchip-ring::before {
        content: '';
        width: 8px;
        height: 8px;
        background: var(--gold);
        border-radius: 50%;
    }

    .microchip-ring::after {
        position: absolute;
        inset: 17px;
        border: 1px solid rgba(var(--gold-bright-rgb), .66);
        content: '';
        opacity: 0;
        transform: scale(.2) rotate(45deg);
    }

/* The gesture, drawn in the cube's own isometric plane — positive translateZ reads as height above
   the object, the same way the shadow sits below it at -150px. It stops short of the top face on
   purpose: the copy promises a phone brought close, not a touch, and no device is ever drawn.
   The approaching hand is drawn in white; the chip is what answers it in gold. */
.gesture-plane {
    position: absolute;
    width: 250px;
    height: 250px;
    border: 1px solid var(--hairline-mid);
    background: radial-gradient(circle at center, var(--tint-raised), var(--tint) 46%, transparent 72%);
    opacity: 0;
    /* The starting height is what the shorter mobile visual can hold: any higher and the plane
       fades in over the section's heading instead of over the object. */
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(310px) scale(1.05);
    pointer-events: none;
    z-index: 3;
}

/* Step 4's surroundings. The container is wider than the cube's own footprint so every point falls
   outside it — a node under the cube would simply be painted over. */
.space-links {
    position: absolute;
    inset: -40%;
    opacity: 0;
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-148px);
    transition: opacity .9s var(--ease-house);
    pointer-events: none;
}

    .space-links i {
        position: absolute;
        width: 7px;
        height: 7px;
        margin: -3.5px 0 0 -3.5px;
        background: var(--gold);
        border-radius: 50%;
        opacity: 0;
    }

        .space-links i:nth-child(1) {
            top: 8%;
            left: 50%;
        }

        .space-links i:nth-child(2) {
            top: 56%;
            left: 6%;
        }

        .space-links i:nth-child(3) {
            top: 86%;
            left: 70%;
        }

.hologram-chat {
    position: absolute;
    top: 104%;
    left: 50%;
    width: 340px;
    background: rgba(5, 5, 5, 0.7);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border: 1px solid var(--hairline-strong);
    padding: 25px;
    opacity: 0;
    transform: translateX(-50%) translateY(30px) scale(0.9);
    transition: opacity 1s var(--ease-house), transform 1.2s var(--ease-house);
    color: var(--white);
    font-size: 1rem;
    line-height: 1.6;
    font-weight: 300;
    box-shadow: 0 20px 50px rgba(5,5,5,0.8);
    z-index: 10;
}

    /* The voice belongs to the object: a thread is drawn from the cube down to the card before the
       card says anything, so the words are not a panel that merely appeared beside it. */
    .hologram-chat::before {
        position: absolute;
        top: -2.4rem;
        left: 50%;
        width: 1px;
        height: 2.4rem;
        background: linear-gradient(to bottom, transparent, var(--gold));
        content: '';
        opacity: 0;
        transform: scaleY(0);
        transform-origin: top;
    }

.artifact-wrapper.state-3 .hologram-chat {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scale(1);
}

.hologram-chat span {
    color: rgba(245, 245, 245, .5);
    font-size: 0.6rem;
    text-transform: uppercase;
    letter-spacing: 4px;
    display: block;
    margin-bottom: 15px;
    font-family: var(--font-mono);
}

    /* A voice is heard, not typed — one quiet point that breathes says it without a chat interface. */
    .hologram-chat span::after {
        display: inline-block;
        width: 5px;
        height: 5px;
        margin-left: .75rem;
        background: var(--gold);
        border-radius: 50%;
        vertical-align: middle;
        content: '';
        opacity: 0;
    }

.hologram-line {
    margin: 0;
    opacity: 0;
    filter: blur(6px);
    transform: translateY(.35rem);
}

.artifact-wrapper.state-2 .artifact-cube {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(20px);
}

.artifact-wrapper.state-2 .cube-shadow {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-130px) scale(0.95);
}

.artifact-wrapper.state-2 .cube-logo {
    opacity: 0;
}

/* One gesture per step, in this order: the plane descends, the chip answers it, the plane withdraws.
   All four beats run the length of a step, so nothing is cut off half-way and nothing wakes before
   the cause of the waking arrives. */
.artifact-wrapper.state-2 .gesture-plane {
    animation: gestureApproach var(--soul-step) cubic-bezier(.4, 0, .2, 1) both;
}

.artifact-wrapper.state-2 .microchip-ring {
    animation: chipReveal var(--soul-step) var(--ease-house) both;
}

    .artifact-wrapper.state-2 .microchip-ring::before {
        animation: microchipAwaken var(--soul-step) var(--ease-house) both;
    }

    .artifact-wrapper.state-2 .microchip-ring::after {
        animation: sensorSignal var(--soul-step) var(--ease-house) both;
    }

.artifact-wrapper.state-3 .artifact-cube {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(40px);
}

.artifact-wrapper.state-3 .cube-shadow {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-150px) scale(0.9);
}

/* The mark on the cube is engraved, not gilded — cut into the oak, not laid onto it — so it is drawn
   the way a cut reads: a little darker than the face it sits in, with one hairline of light on the
   lower edge where the light from above catches the wall of the groove. No gold gradient sweeping
   through the letters: that is a picture of a different object — gold leaf catching a moving light —
   and a groove has no shine of its own. On a loop it is also the detail that reads cheapest, because
   real gilding flashes when the object or the viewer moves, not every 3.8 seconds for ever. */
.artifact-wrapper.state-3 .cube-logo {
    opacity: 1;
    animation: logoAwaken 1.2s var(--ease-house) both;
}

    /* The engraving is carried by the image rather than by the holder, because the holder's own filter
       is driven by logoAwaken and an animation overrides whatever is declared beside it. `brightness(0)`
       turns the white wordmark into the dark groove; the two drops are the lit lip below and the
       shadow above it. */
    .artifact-wrapper.state-3 .cube-logo img {
        filter: brightness(0) opacity(.55) drop-shadow(0 1px 0 rgba(245, 245, 245, .14)) drop-shadow(0 -1px 1px rgba(5, 5, 5, .45));
    }

    .artifact-wrapper.state-3 .cube-logo::after {
        animation: logoRule 1.4s .25s var(--ease-house) both;
    }

/* The card is spoken into being: thread first, then the words, then the point that keeps breathing. */
.artifact-wrapper.state-3 .hologram-chat::before {
    animation: voiceThread .9s .15s var(--ease-house) both;
}

.artifact-wrapper.state-3 .hologram-line {
    animation: voiceLine 1.1s .55s var(--ease-house) both;
}

.artifact-wrapper.state-3 .hologram-chat span::after {
    animation: voicePulse calc(var(--soul-step) / 2) .9s ease-in-out infinite;
}

.artifact-wrapper.state-4 .artifact-cube {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(60px);
}

.artifact-wrapper.state-4 .cube-shadow {
    transform: rotateX(60deg) rotateZ(-45deg) translateZ(-170px) scale(0.8);
    background: radial-gradient(circle at center, var(--tint-raised), var(--tint) 35%, transparent 70%);
    box-shadow: none;
}

    .artifact-wrapper.state-4 .cube-shadow::before {
        animation: presenceFrame var(--soul-step) linear infinite;
    }

    .artifact-wrapper.state-4 .cube-shadow::after {
        animation: presenceSignal calc(var(--soul-step) / 2) var(--ease-house) infinite;
    }

/* An object that has spoken does not go dark again: the mark settles and stays in view, so the last
   step reads as the object living on rather than as a relapse into the silence of the first. */
.artifact-wrapper.state-4 .cube-logo {
    opacity: .72;
    background: none;
}

    .artifact-wrapper.state-4 .cube-logo img {
        filter: none;
    }

/* The conversation does not disappear at the last step, it steps back — the card recedes while the
   answer travels outward, which is what „Wydane polecenia trafiają do obsługi” actually looks like. */
.artifact-wrapper.state-4 .hologram-chat {
    opacity: .3;
    transform: translateX(-50%) translateY(.4rem) scale(.965);
}

.artifact-wrapper.state-4 .hologram-line {
    opacity: 1;
    filter: none;
    transform: none;
}

.artifact-wrapper.state-4 .space-links {
    opacity: 1;
}

    /* Half a step apart — each point answers as the ripple below it reaches that far. */
    .artifact-wrapper.state-4 .space-links i {
        animation: spaceLink calc(var(--soul-step) / 2) var(--ease-house) infinite;
    }

    .artifact-wrapper.state-4 .space-links i:nth-child(1) {
        animation-delay: .35s;
    }

    .artifact-wrapper.state-4 .space-links i:nth-child(2) {
        animation-delay: .8s;
    }

    .artifact-wrapper.state-4 .space-links i:nth-child(3) {
        animation-delay: 1.25s;
    }

@keyframes cubeMotion {
    0% {
        transform: rotateX(0deg) rotateY(-2deg) rotateZ(0deg);
    }

    25% {
        transform: rotateX(2deg) rotateY(0deg) rotateZ(90deg);
    }

    50% {
        transform: rotateX(0deg) rotateY(2deg) rotateZ(180deg);
    }

    75% {
        transform: rotateX(-2deg) rotateY(0deg) rotateZ(270deg);
    }

    100% {
        transform: rotateX(0deg) rotateY(-2deg) rotateZ(360deg);
    }
}

/* The mark used to be set in type, so the waking was a letter-spacing that closed from 22px to 14px.
   The drawing cannot be tracked, so the same gesture is a horizontal stretch that settles. */
@keyframes logoAwaken {
    from {
        filter: blur(7px);
        opacity: 0;
        transform: scale(1.04, .94);
    }

    to {
        filter: blur(0);
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes logoRule {
    from {
        opacity: 0;
        transform: scaleX(0);
    }

    to {
        opacity: .8;
        transform: scaleX(1);
    }
}

/* Everything from here to presenceSignal is written against one step: the percentages are the
   choreography, so the beats stay in order however long a step becomes. */
@keyframes gestureApproach {
    0% {
        opacity: 0;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(310px) scale(1.05);
    }

    18% {
        opacity: .4;
    }

    32% {
        opacity: .8;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(215px) scale(1);
    }

    /* Closest approach — 26px above the woken cube's top face, and never touching it. */
    38% {
        opacity: 1;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(196px) scale(1);
    }

    46% {
        opacity: .7;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(214px) scale(1);
    }

    68% {
        opacity: .12;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(295px) scale(1.04);
    }

    82%, 100% {
        opacity: 0;
        transform: rotateX(60deg) rotateZ(-45deg) translateZ(310px) scale(1.05);
    }
}

/* The chip arrives and settles without overshoot. */
@keyframes chipReveal {
    0%, 26% {
        opacity: 0;
        transform: scale(.55);
    }

    38% {
        opacity: 1;
        transform: scale(1);
    }

    100% {
        opacity: .92;
        transform: scale(1);
    }
}

@keyframes sensorSignal {
    0%, 30% {
        opacity: 0;
        transform: scale(.2) rotate(45deg);
    }

    38% {
        opacity: .9;
    }

    58% {
        opacity: 0;
        transform: scale(1.05) rotate(120deg);
    }

    /* A 50 ms reset at zero opacity, so the echo below starts from the chip again and not from
       wherever the first wave ended. */
    59% {
        opacity: 0;
        transform: scale(.3) rotate(45deg);
    }

    68% {
        opacity: .35;
    }

    92%, 100% {
        opacity: 0;
        transform: scale(1) rotate(110deg);
    }
}

/* The point wakes by light alone: it grows once to its size and then breathes in opacity. */
@keyframes microchipAwaken {
    0%, 26% {
        opacity: 0;
        transform: scale(.7);
    }

    36% {
        opacity: 1;
        transform: scale(1);
    }

    52% {
        opacity: .7;
    }

    74% {
        opacity: 1;
    }

    100% {
        opacity: .8;
        transform: scale(1);
    }
}

@keyframes voiceThread {
    from {
        opacity: 0;
        transform: scaleY(0);
    }

    to {
        opacity: .85;
        transform: scaleY(1);
    }
}

@keyframes voiceLine {
    from {
        opacity: 0;
        filter: blur(6px);
        transform: translateY(.35rem);
    }

    to {
        opacity: 1;
        filter: blur(0);
        transform: none;
    }
}

@keyframes voicePulse {
    0%, 100% {
        opacity: .25;
    }

    45% {
        opacity: 1;
    }
}

@keyframes spaceLink {
    0% {
        opacity: 0;
        transform: scale(.35);
    }

    18% {
        opacity: 1;
        transform: scale(1);
    }

    34% {
        opacity: .7;
    }

    72% {
        opacity: .35;
    }

    100% {
        opacity: 0;
        transform: scale(.9);
    }
}

@keyframes presenceFrame {
    0% {
        opacity: .1;
        transform: rotate(0deg) scale(.72);
    }

    30%, 70% {
        opacity: .75;
    }

    100% {
        opacity: .1;
        transform: rotate(90deg) scale(1.08);
    }
}

@keyframes presenceSignal {
    0% {
        opacity: 0;
        transform: rotate(45deg) scale(.35);
    }

    35% {
        opacity: .9;
    }

    100% {
        opacity: 0;
        transform: rotate(45deg) scale(1.35);
    }
}

.text-scroll {
    display: grid;
    grid-template-columns: 3rem 1fr;
    align-items: stretch;
    height: clamp(44rem, 52vw, 50rem);
    z-index: 10;
}

.step-block {
    grid-column: 2;
    width: 100%;
    padding: 1.25rem 0 1.25rem 1.5rem;
    border: 0;
    background: transparent;
    color: inherit;
    opacity: .34;
    text-align: left;
    cursor: pointer;
    transition: opacity .6s var(--ease-house);
}

    .step-block.active {
        opacity: 1;
    }

    .step-block:focus-visible {
        outline: 1px solid var(--gold);
        outline-offset: .35rem;
    }

.soul-film-status {
    grid-column: 1;
    grid-row: 1 / span 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
    padding-top: 1.25rem;
    color: rgba(245, 245, 245, .5);
    font-family: var(--font-mono);
    font-size: .58rem;
    letter-spacing: .14em;
}

.soul-film-line {
    position: relative;
    width: 1px;
    min-height: 10rem;
    flex: 1;
    overflow: hidden;
    background: var(--hairline);
}

    /* The clock of the whole sequence, and it runs rather than ticks: soul.js sets the duration to
       the step that is playing, so the gold travels the quarter at one steady speed and reaches the
       next mark exactly as the step changes — the eye sees the next chapter coming. Stepped to a
       quarter, a half, three quarters, one, each reached in an eased 1.2 s and then still for the
       remaining 3.8 s, it reads as four jumps with nothing between them. Hence `linear`: an eased
       quarter arrives early and waits, which is the same fault in miniature. The starting transform
       is here, the moving one is set from the script. */
    .soul-film-line i {
        position: absolute;
        inset: 0;
        background: var(--gold);
        transform: scaleY(0);
        transform-origin: top;
        transition: transform 0s linear;
    }

.step-num {
    font-family: var(--font-mono);
    font-size: 0.65rem;
    text-transform: uppercase;
    color: rgba(245, 245, 245, .5);
    font-weight: 300;
    margin-bottom: .5rem;
    letter-spacing: 4px;
    display: block;
}

.step-title {
    display: block;
    font-family: var(--font-display);
    font-size: clamp(1.55rem, 2.2vw, 2.35rem);
    margin-bottom: .65rem;
    line-height: 1.1;
    font-weight: 300;
}

.step-desc {
    max-height: 0;
    overflow: hidden;
    margin: 0;
    font-size: 1rem;
    color: var(--text-muted);
    line-height: 1.8;
    max-width: 30rem;
    font-weight: 200;
    opacity: 0;
    transform: translateY(.5rem);
    transition: max-height .8s var(--ease-house), opacity .6s var(--ease-house), transform .8s var(--ease-house);
}

.step-block.active .step-desc {
    max-height: 8rem;
    opacity: 1;
    transform: none;
}

/* The step that is playing carries the gold. */
.step-block.active .step-num {
    color: var(--gold);
}
/* #endregion */

/* #region Soul concrete cases */
.soul-cases {
    position: relative;
    /* `clip`, not `hidden`: an ancestor with `overflow: hidden` is a scroll container, and a sticky
       element inside one sticks to that box rather than to the window — which would leave the case
       box pinned to nothing at all. `overflow-x: clip` gives this section the horizontal containment it
       needs and still lets the box below be held under the masthead. */
    overflow-x: clip;
    padding: clamp(6rem, 10vw, 10rem) 0;
    border-bottom: 1px solid var(--section-divider);
    background: var(--surface-editorial);
}

    .soul-cases::before {
        content: none;
    }

.soul-cases-heading,
.soul-case-stage,
.soul-case-index {
    width: min(90vw, var(--section-content-max));
    max-width: none;
    margin-inline: auto;
    padding-inline: 0;
}

.soul-cases-heading {
    position: relative;
    z-index: 1;
    max-width: var(--section-content-max);
    margin-bottom: clamp(3.5rem, 6vw, 6rem);
}

    .soul-cases-heading h2 {
        max-width: 12ch;
        margin: 1.5rem 0 2rem;
        color: var(--white);
        font-family: var(--font-display);
        font-size: var(--section-title-size);
        font-weight: 300;
        letter-spacing: -.04em;
        line-height: var(--section-title-line-height);
    }

        .soul-cases-heading h2 i {
            color: var(--gold);
            font-weight: 300;
        }

    .soul-cases-heading > p {
        max-width: 52rem;
        color: var(--white-soft);
        font-size: clamp(1rem, 1.15vw, 1.12rem);
        font-weight: 300;
        line-height: 1.85;
    }

/* One place, told the same way every place is: the room on the left at the house's own square, the room's
   sentence and the three beats of the gesture on the right. Every scene wears this, and they are stacked in
   the stage's single cell so the box never changes height as the place changes. */
.soul-case-scene {
    grid-area: 1 / 1;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(22rem, .82fr);
    align-items: stretch;
    opacity: 0;
    visibility: hidden;
    transition: opacity .8s var(--ease-house), visibility .8s;
}

    .soul-case-scene.is-active {
        opacity: 1;
        visibility: visible;
    }

/* Square, and squarely square: `align-self: start` keeps the ratio whatever the copy beside it does.
   Stretched instead, the photograph would take the height of the tallest of the seven copy columns
   and quietly become a portrait crop — the house's format is 1:1 and this is where it shows. Where
   the copy runs longer the ground under the picture is the box's own, which reads as a mount. */
.soul-case-shot {
    position: relative;
    align-self: start;
    aspect-ratio: 1;
    overflow: hidden;
    margin: 0;
}

    .soul-case-shot img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
        filter: var(--photo-at-rest);
    }

.soul-case-copy {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(2.25rem, 4vw, 4rem);
    background: var(--surface-card);
}

.soul-case-context {
    display: block;
    margin-bottom: 1.5rem;
    color: rgba(245, 245, 245, .5);
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: .65rem;
    letter-spacing: 8px;
    /* The house eyebrow's tracking, and therefore the amount sup.tm has to give back — see .h-tag,
       which owns this specification. */
    --tm-tracking: 8px;
    text-transform: uppercase;
    opacity: .9;
}

/* The room's own sentence. Two rules described this heading, one after the other, and the second
   only added what the first had left out — they are one rule now.
   The size steps down a little from the feature block's: seven places take their turn in one box,
   and a three-line heading at 3.4rem set the box's height for the longest of them. */
.soul-case-copy h3 {
    margin-bottom: clamp(1.5rem, 2.6vw, 2.4rem);
    color: var(--white);
    font-family: var(--font-display);
    font-size: clamp(1.8rem, 2.5vw, 2.9rem);
    font-weight: 300;
    letter-spacing: -.04em;
    line-height: 1.05;
}

    .soul-case-copy h3 i {
        color: var(--gold);
        font-weight: 300;
    }

.soul-case-flow {
    position: relative;
    display: grid;
    gap: 0;
    margin: 0 0 2rem;
    list-style: none;
    counter-reset: soul-flow-step;
}

    .soul-case-flow li {
        position: relative;
        padding: 1.1rem 0 1.1rem 3.5rem;
    }

        .soul-case-flow li::before {
            content: counter(soul-flow-step, decimal-leading-zero);
            counter-increment: soul-flow-step;
            position: absolute;
            top: 1.1rem;
            left: 0;
            z-index: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 2.25rem;
            height: 2.25rem;
            border: 1px solid var(--hairline-strong);
            border-radius: 50%;
            background: var(--surface-card);
            color: var(--white-soft);
            font-family: var(--font-mono);
            font-size: .58rem;
            letter-spacing: .04em;
            font-weight: 600;
        }

        .soul-case-flow li::after {
            content: "";
            position: absolute;
            top: calc(1.1rem + 2.25rem);
            bottom: -1.1rem;
            left: 1.12rem;
            width: 1px;
            background: var(--hairline);
        }

        .soul-case-flow li:last-child::after {
            content: none;
        }

    .soul-case-flow span {
        display: block;
        margin-bottom: .4rem;
        color: rgba(245, 245, 245, .5);
        font-family: var(--font-mono);
        font-size: .55rem;
        letter-spacing: .3em;
        font-weight: 600;
        text-transform: uppercase;
    }

    .soul-case-flow p {
        margin: 0;
        color: var(--white-soft);
        font-size: 1rem;
        font-weight: 300;
        line-height: 1.75;
    }

    /* The guest's own question: it stands out in full white, not in another face — a change of
       face inside one list reads as a different font. The quotation marks in the copy say whose
       words these are. */
    .soul-case-flow li:nth-child(2) p {
        color: var(--white);
    }

.soul-case-value {
    margin: 0;
    padding: 1.4rem 1.6rem;
    border: 1px solid var(--hairline-mid);
    background: linear-gradient(135deg, var(--tint), transparent 60%);
    color: var(--white-soft);
    font-size: 1rem;
    font-weight: 300;
    line-height: 1.75;
}

    .soul-case-value span {
        display: block;
        margin-bottom: .65rem;
        color: rgba(245, 245, 245, .5);
        font-family: var(--font-mono);
        font-size: .55rem;
        letter-spacing: .3em;
        font-weight: 600;
        text-transform: uppercase;
    }

/* #region The stage */
/* The section is taller than the box, and the box is held under the masthead while the reader
   scrolls the difference — so the scroll itself deals the places one after another. The height is
   the number of places, handed down from the view as --case-count, times the distance a reader
   should travel per place; a screen is added for the last one to be read before the section leaves.
   The custom property has a fallback because a rule that silently collapses to nothing is worse
   than one that shows a wrong number of places. */
.soul-case-theatre {
    height: calc(100vh + (var(--case-count, 7) - 1) * 62vh);
}

.soul-case-sticky {
    position: sticky;
    top: calc(var(--site-header-h) + clamp(1rem, 3vh, 2.5rem));
}

/* The box: one hairline around the pair, and the clock along its own bottom edge — the one gold in it.
   Every scene lives in the single cell, so the box never changes height as the place changes. */
.soul-case-stage {
    position: relative;
    display: grid;
    border: 1px solid var(--hairline-mid);
    background: var(--hero-surface);
    box-shadow: 0 3rem 8rem rgba(5, 5, 5, .35);
}

/* The clock, on the bottom edge of the box itself. Where the scroll deals the places it shows how
   far the reader is through this one; where nothing is pinned the script gives it the interval and
   it crosses at one steady speed. */
.soul-case-progress {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 2;
    height: 2px;
    background: var(--hairline);
}

    .soul-case-progress i {
        display: block;
        width: 100%;
        height: 100%;
        background: var(--gold);
        transform: scaleX(0);
        transform-origin: left;
        transition: transform 0s linear;
    }

/* The places, as a list of chapters under the box. It is also the third way in: a reader who came
   for the clinic does not have to scroll past the yacht to reach it. */
.soul-case-index {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
    column-gap: clamp(1rem, 2vw, 2rem);
    padding: 0;
    margin-top: clamp(1.2rem, 2.4vh, 2rem);
    list-style: none;
}

.soul-case-chapter {
    display: flex;
    align-items: baseline;
    gap: .7rem;
    width: 100%;
    padding: clamp(.8rem, 1.4vh, 1.1rem) 0;
    border: 0;
    border-top: 1px solid var(--line);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    font-family: var(--font-mono);
    font-size: .62rem;
    letter-spacing: .16em;
    text-align: left;
    text-transform: uppercase;
    transition: color var(--motion-quick) var(--ease-house), border-color var(--motion-quick) var(--ease-house);
}

    .soul-case-chapter span {
        font-size: .58rem;
        opacity: .55;
        transition: opacity var(--motion-quick) var(--ease-house), color var(--motion-quick) var(--ease-house);
    }

    .soul-case-chapter:hover,
    .soul-case-chapter:focus-visible {
        color: var(--white-soft);
    }

    .soul-case-chapter.is-active {
        border-top-color: var(--gold);
        color: var(--white);
    }

        .soul-case-chapter.is-active span {
            color: var(--gold);
            opacity: 1;
        }

/* With no scripting there is nothing to deal the places, so the section stops pretending: it takes
   its own height back and the seven scenes stand one under another, which is the whole argument in the
   order it is authored in. */
@media (scripting: none) {
    .soul-case-theatre {
        height: auto;
    }

    .soul-case-sticky {
        position: static;
    }

    .soul-case-stage {
        display: block;
    }

    .soul-case-scene {
        opacity: 1;
        visibility: visible;
    }

        .soul-case-scene + .soul-case-scene {
            border-top: 1px solid var(--hairline-mid);
        }

    .soul-case-progress,
    .soul-case-index {
        display: none;
    }
}
/* #endregion */

@media (max-width: 1000px) {
    .soul-case-scene {
        grid-template-columns: 1fr;
    }
}

/* Nothing is pinned in either of these: narrow, the box turns into one column, and short, the box
   is taller than the window — and a sticky element taller than its viewport stops behaving like
   one, so the reader would be held against a box whose foot they can never reach. The section then
   takes its own height back and the script runs the same bar on a clock instead. The script tests
   exactly this pair of thresholds; change one and change the other. */
@media (max-width: 1000px), (max-height: 980px) {
    .soul-case-theatre {
        height: auto;
    }

    .soul-case-sticky {
        position: static;
    }
}

@media (max-width: 760px) {
    .mechanic-header {
        padding-bottom: 2rem;
    }

    .visual-sticky {
        min-height: 27rem;
    }

    .text-scroll {
        grid-template-columns: 2rem 1fr;
    }

    .step-block {
        padding: 1rem 0 1rem 1rem;
    }

    /* A phone's column wraps a step's sentence to five lines, which 8rem cuts through the last one. */
    .step-block.active .step-desc {
        max-height: 12rem;
    }

    .soul-film-line {
        min-height: 8rem;
    }

    .soul-case-flow li {
        grid-template-columns: 1fr;
        gap: .65rem;
    }
}

/* Between the phone's smaller cube and the tablet's layout the speech card under the cube reached into
   the first step; the stage keeps room for it. */
@media (min-width: 601px) and (max-width: 760px) {
    .visual-sticky {
        min-height: 34rem;
    }
}

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

    .artifact-cube,
    .cube-shadow,
    .artifact-wrapper::before,
    .hologram-chat,
    .soul-film-line i,
    .step-desc {
        transition: none;
    }

    /* Every animation here is declared on a `.artifact-wrapper.state-N …` selector, so the rule that
       stops it has to repeat that selector. A plain element list loses on specificity and the
       animation keeps playing. */
    .artifact-wrapper.state-2 .microchip-ring::before,
    .artifact-wrapper.state-2 .microchip-ring::after,
    .artifact-wrapper.state-3 .cube-logo,
    .artifact-wrapper.state-3 .cube-logo::after,
    .artifact-wrapper.state-3 .hologram-chat::before,
    .artifact-wrapper.state-3 .hologram-chat span::after,
    .artifact-wrapper.state-4 .cube-shadow::before,
    .artifact-wrapper.state-4 .cube-shadow::after {
        animation: none;
    }

    /* Both of these exist only to be moved, so nothing is lost by leaving them out entirely. */
    .gesture-plane,
    .space-links {
        display: none;
    }

    /* Their resting state is invisible because an animation reveals them; with motion refused, each step
       still has to show what it is about. */
    .artifact-wrapper.state-2 .microchip-ring {
        animation: none;
        opacity: 1;
        transform: scale(1);
    }

    .artifact-wrapper.state-3 .hologram-line {
        animation: none;
        opacity: 1;
        filter: none;
        transform: none;
    }

    .soul-case-scene,
    .soul-case-chapter,
    .soul-case-progress i {
        transition: none;
    }
}
/* #endregion */

/* #region Odoardo AI concierge */
.core-interface {
    position: relative;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    min-height: 60vh;
    background: radial-gradient(ellipse 900px 500px at 50% 30%, var(--tint), transparent 70%), var(--black-soft);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    border-top: 1px solid transparent;
    padding: 14vh 5vw;
    overflow: hidden;
    /* No scroll-margin: html's scroll-padding-top already clears the fixed header, and the two
       offsets add up — see .core-interface.home-concierge in product-sections.css. */
}

    /* The seam above the concierge, and it is an ordinary division of sections like every other one
       on the page. A 400px rule fading out at both ends, centred, reads as a decoration under the
       section above rather than as the start of a new one. */
    .core-interface::before {
        content: '';
        position: absolute;
        top: 0;
        right: 0;
        left: 0;
        height: 1px;
        background: var(--section-divider);
    }

    .core-interface::after {
        content: '';
        position: absolute;
        inset: 0;
        pointer-events: none;
        box-shadow: inset 0 80px 120px -80px rgba(5,5,5,0.9);
    }

.canvas-area {
    width: 100%;
    max-width: 850px;
    min-height: 0;
    height: auto;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    position: relative;
    z-index: 5;
    margin-bottom: 3vh;
    transition: min-height 0.4s ease;
}

.voice-line-brand {
    display: flex;
    align-items: center;
    align-self: flex-start;
    gap: 14px;
    margin-bottom: 1.5vh;
}

.voice-line-icon {
    display: block;
    width: 10px;
    height: 10px;
    opacity: .85;
}

/* The mark is the drawing, white, at its cap height — see the note on .logo in site.css. The letters were
   10px type beside the 10px glyph, as on the soul page, so the drawing is 0.72 × 10. */
.voice-line-wordmark {
    display: block;
    width: auto;
    height: 7.2px;
}

/* The wait. Three dots written as text appear once and then sit there, and a conversation that has to
   fetch something — a contact passed to the team, a second turn of the model — can stand still for ten
   seconds or more; a still line reads as a fault. These breathe instead: three small gold points, one
   beat apart, lit and dimmed without moving, quiet enough to belong to the house. Honoured by
   prefers-reduced-motion, where they simply hold at half opacity. */
.voice-thinking {
    display: inline-flex;
    align-items: center;
    gap: .45em;
}

/* What is being done while the dots run. It appears only when a capability is working — addresses looked up,
   works chosen, a contact passed to the team — because that is the one wait long enough to need a word. It is
   set in the answer's own face and size, in italic and a step quieter, so it reads as the voice speaking. */
.voice-working {
    margin-right: .6em;
    font: inherit;
    font-style: italic;
    opacity: .7;
}

    .voice-thinking span {
        width: .34em;
        height: .34em;
        border-radius: 50%;
        background: var(--gold);
        opacity: .25;
        animation: voice-thinking-beat 1.4s ease-in-out infinite;
    }

        .voice-thinking span:nth-child(2) { animation-delay: .2s; }
        .voice-thinking span:nth-child(3) { animation-delay: .4s; }

@keyframes voice-thinking-beat {
    0%, 70%, 100% { opacity: .25; }
    35% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .voice-thinking span {
        animation: none;
        opacity: .5;
    }
}

/* The visitor's last question, a quiet line above the answer. The conversation shows only the
   concierge's latest reply, so this line is the one trace of what was asked — and the one confirmation
   of what the microphone heard when the question was dictated. It ranges with the left edge beside the
   ODOARDO mark, so it is not read as part of the answer. */
.voice-question {
    align-self: flex-start;
    max-width: 100%;
    margin-bottom: 1.5vh;
    font-size: .62rem;
    font-weight: 300;
    letter-spacing: 3px;
    line-height: 1.5;
    text-transform: uppercase;
    text-align: left;
    color: var(--text-muted);
    opacity: .55;
    transition: opacity .4s ease;
}

    .voice-question[hidden] {
        display: none;
    }

.voice-line {
    font-family: var(--font-display);
    font-size: clamp(1rem, 1.35vw, 1.15rem);
    line-height: 1.6;
    font-weight: 300;
    color: var(--text-muted);
    text-align: center;
    opacity: 0;
    transform: translateY(15px);
    transition: opacity 0.5s var(--ease-house), transform 0.5s var(--ease-house);
}

    /* The concierge writes an address when it cannot answer — a mailto in the browser's own blue is
       the one place the widget stops looking like the site. */
    .voice-line a {
        color: var(--white);
        text-decoration: underline;
        text-underline-offset: 2px;
        transition: color var(--motion-quick) var(--ease-house);
    }

        .voice-line a:hover,
        .voice-line a:focus-visible {
            color: var(--gold);
        }

    .voice-line i {
        color: var(--gold);
        font-style: italic;
    }

.input-zone {
    width: 100%;
    max-width: 600px;
    position: relative;
    z-index: 10;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.input-wrapper {
    width: 100%;
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.input-container {
    width: 100%;
    position: relative;
    display: flex;
    align-items: center;
    border-bottom: 1px solid var(--hairline-mid);
    transition: border-color 0.4s var(--ease-house);
}

    .input-container:focus-within {
        border-bottom-color: var(--gold);
    }

.terminal-input {
    flex: 1;
    background: transparent;
    border: none;
    outline: none;
    color: var(--white);
    font-family: var(--font-display);
    font-style: italic;
    font-size: 1.5rem;
    text-align: left;
    caret-color: var(--gold);
    padding: 15px 10px;
}

    /* The same placeholder the home page's concierge shows — see .home-concierge .terminal-input
       in product-sections.css. Differing in face, size or tracking, one component wears two faces
       on one site. */
    .terminal-input::placeholder {
        font-family: var(--font-sans);
        font-weight: 300;
        font-size: 0.92rem;
        letter-spacing: .02em;
        color: var(--white-dim);
        font-style: normal;
    }

.send-btn {
    background: none;
    border: none;
    outline: none;
    padding: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--white);
    transition: transform var(--motion-quick) var(--ease-house), opacity var(--motion-quick) var(--ease-house), color var(--motion-quick) var(--ease-house);
    opacity: 0.5;
}

    .send-btn:hover,
    .send-btn:focus-visible {
        color: var(--gold);
        opacity: 1;
        transform: translateX(3px);
    }

    .send-btn svg {
        width: 24px;
        height: 24px;
        stroke: currentColor;
        stroke-width: 1.5;
        fill: none;
    }

.ai-label {
    font-family: var(--font-mono);
    font-weight: 300;
    font-size: 0.5rem;
    text-transform: uppercase;
    letter-spacing: 4px;
    color: var(--white-subtle);
    margin-top: 10px;
    text-align: center;
    width: 100%;
    display: block;
}

/* The half of that line saying a machine writes the answers. It is the one statement a visitor has
   to be able to read, so it never inherits the label's whispered size or its twelve percent white. */
.ai-label .ai-statement {
    display: block;
    margin-bottom: 4px;
    font-size: 0.72rem;
    letter-spacing: 0.08em;
    text-transform: none;
    color: var(--white-soft);
}

/* A sentence, not a label: the caps and the wide tracking belong to the mono eyebrow above it and
   made a whole line of running text hard to read. */
.ai-label .ai-secondary {
    display: block;
    color: var(--white-soft);
    font-size: 0.66rem;
    letter-spacing: 0.08em;
    text-transform: none;
}

    /* Both links — the voice's name and the privacy policy — carry their sentence's colour (left alone
       they rendered in the browser's own link violet) and answer a hover the same way. */
    .ai-label a {
        color: inherit;
        text-decoration: underline;
        text-underline-offset: 2px;
        transition: color .3s ease;
    }

        .ai-label a:hover {
            color: var(--gold);
        }

.whispers-wrapper {
    min-height: 0;
    height: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    padding-top: 4vh;
    padding-bottom: 1vh;
}

.whisper-label {
    font-family: var(--font-mono);
    font-weight: 300;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 6px;
    color: var(--white-ghost);
    opacity: 1;
    transition: opacity 0.4s ease;
    margin-bottom: 1.5vh;
}

    .whisper-label.hidden {
        opacity: 0;
        pointer-events: none;
    }

.whispers-box {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    opacity: 1;
    transition: opacity 0.4s ease;
}

    .whispers-box.hidden {
        opacity: 0;
        pointer-events: none;
    }

.whisper-tag {
    font-family: var(--font-mono);
    font-weight: 300;
    font-size: 0.65rem;
    letter-spacing: 2px;
    color: var(--text-muted);
    text-transform: uppercase;
    transition: color var(--motion-quick) var(--ease-house), border-color var(--motion-quick) var(--ease-house);
    border: 1px solid var(--white-subtle);
    padding: 12px 24px;
    border-radius: 40px;
    background: transparent;
}

    /* Gold arrives as the line and the words; the chip neither fills nor rises. */
    .whisper-tag:hover,
    .whisper-tag:focus-visible {
        color: var(--gold);
        border-color: var(--gold);
    }
/* #endregion */

/* #region Section headers (explore-header / explore-intro) */
/* The site's own measure, not a raw 8vh. This is the same head-to-content gap --section-head-gap
   names, and a raw 8vh drifts from it by the shape of the window rather than by anyone's decision: 8vh is
   3.5rem on a short laptop and 6rem on a tall monitor, while the token holds between 2.6rem and 4.2rem. So on
   one screen the shared header breathes less than the sections around it and on another half again as
   much. */
.explore-header,
.mechanic-header,
.marquee-header,
.portfolio-header,
.usage-mosaic-header {
    text-align: left;
    margin-bottom: var(--section-head-gap);
}

    /* No eyebrow rule here: it would restate the distance .h-tag gives every eyebrow, and being the shared
       header it is the copy five other pages would follow. The title below carries margin-top: 0 for the
       same reason — the gap has one owner. */
    .explore-header h2,
    .mechanic-header h2,
    .marquee-header h2,
    .portfolio-header h2,
    .usage-mosaic-header h2,
    .journey-header h2,
    .filmable-closing h2,
    .mec-copy h2 {
        margin: 0;
        font-family: var(--font-display);
        font-size: var(--section-title-size);
        color: var(--white);
        font-weight: 300;
        letter-spacing: -.035em;
        line-height: var(--section-title-line-height);
    }

        .explore-header h2 i,
        .mechanic-header h2 i,
        .marquee-header h2 i,
        .portfolio-header h2 i,
        .usage-mosaic-header h2 i,
        .journey-header h2 i,
        .filmable-closing h2 i,
        .mec-copy h2 i {
            color: var(--gold);
            font-weight: 300;
        }

/* The class carries its own typography, and must not be nested under a list of named parents:
   under an eighth parent — the checkout header, for one — the lead paragraph then falls back to
   plain body text and looks unstyled. The parents keep only the spacing they each need. */
.explore-intro {
    max-width: 650px;
    color: var(--text-muted);
    font-size: clamp(1rem, 1.05vw, 1.05rem);
    font-weight: 300;
    line-height: 1.75;
}

    /* Heading to lead — the gap --section-lead-gap exists for, and this shared component has to read the
       token like every page-local heading does. Keeping its own 4vh here is what leaves most of the site's
       headings outside the token that is supposed to govern them. */
    .explore-header .explore-intro,
    .pj-header .explore-intro,
    .mechanic-header .explore-intro,
    .marquee-header .explore-intro,
    .portfolio-header .explore-intro,
    .usage-mosaic-header .explore-intro,
    .mec-copy .explore-intro {
        margin-top: var(--section-lead-gap);
    }

/* #endregion */

/* #region Filmable callout banner */
/* The concierge above keeps its own padding at both edges — see .core-interface.home-concierge in
   product-sections.css. Do not tune that section's foot from here on the argument that a band
   opening with a rule needs less ground above it: changing its padding outright leaves the section
   with a head and a foot of different depths, and measuring them against different axes (10vh
   against the section's 10vw) means the two cannot match at any window size. What separates the two
   blocks is the band's own rule, not the air above it.

   The band sits flush against what it follows — it is a full-width plate, and a strip of page showing
   above its gilded rule reads as a seam rather than as air. So the concierge's foot is cut where this
   band follows it and only there, leaving the section alone everywhere else.

   Nothing here shortens the conversation's foot: that foot is the separation between the two blocks,
   and taking it back leaves the band sitting on the chat. A doubled measure above the band is not
   this file's to cancel — Odoardo Research™'s own stylesheet adds a second full section measure on
   top of the padding the conversation already spends, and it is cancelled there, where it is
   written. */

.filmable-callout {
    background: radial-gradient(circle at 22% 50%, var(--tint), transparent 32%), var(--black-soft);
    border-top: 1px solid var(--hairline-mid);
    border-bottom: 1px solid var(--hairline-mid);
    position: relative;
    overflow: hidden;
}

.filmable-callout-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
}

.filmable-callout-marquee {
    display: flex;
    align-items: stretch;
    width: max-content;
    height: 100%;
    animation: filmable-marquee 200s linear infinite;
    /* The ground the gaps between frames show: the house black, so each frame is separated like a still on a film strip. */
    background: var(--black);
}

.filmable-callout-marquee-track {
    display: flex;
    align-items: stretch;
    height: 100%;
}

.filmable-callout-marquee-item {
    width: auto;
    height: 100%;
    aspect-ratio: 1 / 1;
    background-size: cover;
    background-position: center;
    flex-shrink: 0;
    /* A margin after every frame rather than a flex gap: each track then ends on the same space it
       starts with, so the two tracks stay the same width and the loop at -50% has no seam. */
    margin-right: 6px;
    /* The frames are the argument the strip makes — a yearbook of spaces that photograph well — so
       they have to be legible as photographs. At .55 they are a texture behind the words. */
    filter: var(--photo-mute) var(--photo-as-ground);
}

@keyframes filmable-marquee {
    from {
        transform: translateX(0);
    }

    to {
        transform: translateX(-50%);
    }
}

.filmable-callout-scrim {
    position: absolute;
    inset: 0;
    /* Still heaviest on the left, where the headline sits, and lightest on the right, where the
       frames carry the section on their own. Lifted a notch together with the frames' brightness. */
    background: linear-gradient(90deg, rgba(5,5,5,.72) 0%, rgba(5,5,5,.56) 45%, rgba(5,5,5,.42) 100%);
}

.filmable-callout-inner {
    width: min(90vw, var(--hero-content-max));
    max-width: none;
    margin-inline: auto;
    padding: clamp(3.75rem, 6.6vw, 5.2rem) 0;
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(280px, .9fr);
    gap: clamp(2rem, 5vw, 5rem);
    align-items: center;
    text-decoration: none;
    position: relative;
    z-index: 1;
}

/* The band's eyebrow sits closer than a section's. --section-tag-gap is measured against a heading at
   --section-title-size; this headline is a paragraph at up to 2.6rem inside a compact strip, and the
   same distance over a smaller line reads as a hole rather than as a gap. Proportional to what it
   stands over, which is what the house measure is doing everywhere else. */
.filmable-callout-lead > .h-tag {
    margin-bottom: clamp(.8rem, 1.6vh, 1.2rem);
}

.filmable-callout-headline {
    margin: 0;
    color: var(--white);
    font-family: var(--font-display);
    font-size: clamp(1.7rem, 2.8vw, 2.6rem);
    font-weight: 300;
    letter-spacing: -.02em;
    line-height: 1.2;
}

    .filmable-callout-headline i {
        color: var(--gold);
        font-style: italic;
    }

.filmable-callout-note--lead {
    display: block;
    margin-top: 1.6rem;
    max-width: 30rem;
    color: var(--white-soft);
    font-family: var(--font-sans);
    font-size: clamp(.86rem, 1vw, .98rem);
    font-weight: 300;
    line-height: 1.7;
}

.filmable-callout-aside {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    border-left: 1px solid var(--hairline-mid);
    padding-left: clamp(1.5rem, 2.5vw, 2.5rem);
}

/* The CTA borrows .home-product-link for the underline and the travelling arrow, and that class
   carries a large top margin — the air it needs where it follows a paragraph. Here it stands alone
   in a column that is already centred against the headline, so the margin did nothing but push it
   below the middle. */
.filmable-callout-aside .filmable-callout-cta {
    margin-top: 0;
}

.filmable-callout-cta {
    color: var(--white);
    font-family: var(--font-sans);
    font-size: clamp(.85rem, 1vw, .95rem);
    font-weight: 300;
    letter-spacing: .04em;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 10px;
}

/* The whole band is the link, so the gold line and the arrow answer the hand anywhere on it. */
.filmable-callout-inner:hover .filmable-callout-cta,
.filmable-callout-inner:focus-visible .filmable-callout-cta {
    background-position: left bottom, left bottom;
    background-size: 100% 1px, 100% 1px;
}

    .filmable-callout-inner:hover .filmable-callout-cta > span:last-child,
    .filmable-callout-inner:focus-visible .filmable-callout-cta > span:last-child {
        transform: translateX(4px);
        color: var(--gold);
    }
/* #endregion */

/* #region Ecosystem map */
/* The house rhythm, from the token every other section reads. At 16vh this one alone breathed
   twice as hard as its neighbours, and on a short viewport it collapsed to less than theirs. */
.ecosystem-map {
    padding: var(--section-space) 0;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
}

.ecosystem-map-intro {
    margin-bottom: clamp(4rem, 8vh, 7rem);
}

    .ecosystem-map-intro h2 {
        margin: 0;
        color: var(--white);
        font-family: var(--font-display);
        font-size: var(--section-title-size);
        font-weight: 300;
        letter-spacing: -.035em;
        line-height: var(--section-title-line-height);
    }

        .ecosystem-map-intro h2 i {
            color: var(--gold);
            font-weight: 300;
        }

    .ecosystem-map-intro p {
        max-width: 620px;
        margin: clamp(2rem, 4vh, 3.5rem) 0 0;
        color: var(--white-soft);
        font-size: clamp(1rem, 1.15vw, 1.12rem);
        font-weight: 300;
        line-height: 1.8;
    }

.ecosystem-map-grid {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    border-top: 1px solid var(--hairline-mid);
}

.ecosystem-sense {
    position: relative;
    display: flex;
    min-height: 520px;
    flex-direction: column;
    padding: 2rem 1.35rem 1.6rem;
    border-right: 1px solid var(--line);
    color: var(--white);
    text-decoration: none;
    transition: background-color var(--motion) var(--ease-house);
}

    .ecosystem-sense:last-child {
        border-right: 0;
    }

    /* The column does not rise to meet the hand: the ground warms and the name and arrow take the gold. */
    .ecosystem-sense:hover,
    .ecosystem-sense:focus-visible,
    .ecosystem-sense.is-current {
        background: rgba(var(--gold-rgb),.07);
    }

.ecosystem-sense-number,
.ecosystem-sense-name {
    font-family: var(--font-mono);
    font-weight: 400;
    font-size: 0.65rem;
    letter-spacing: 8px;
    /* The house eyebrow's tracking, and therefore the amount sup.tm has to give back — see .h-tag,
       which owns this specification. */
    --tm-tracking: 8px;
    text-transform: uppercase;
    opacity: 0.9;
}

.ecosystem-sense-number {
    color: var(--white-soft);
    font-weight: 500;
}

.ecosystem-sense-name {
    color: rgba(245, 245, 245, .5);
    transition: color var(--motion) var(--ease-house);
}

.ecosystem-sense-name {
    margin-top: .55rem;
}

    .ecosystem-sense:hover .ecosystem-sense-name,
    .ecosystem-sense:focus-visible .ecosystem-sense-name,
    .ecosystem-sense.is-current .ecosystem-sense-name,
    .ecosystem-sense:hover .ecosystem-sense-arrow,
    .ecosystem-sense:focus-visible .ecosystem-sense-arrow,
    .ecosystem-sense.is-current .ecosystem-sense-arrow {
        color: var(--gold);
    }

.ecosystem-sense-image {
    width: 100%;
    aspect-ratio: 1;
    margin-top: 1.5rem;
    object-fit: cover;
    filter: var(--photo-at-rest);
    transition: filter var(--motion-slow) var(--ease-house), transform 1.8s var(--ease-house);
}

.ecosystem-sense:hover .ecosystem-sense-image,
.ecosystem-sense:focus-visible .ecosystem-sense-image {
    filter: var(--photo-lifted);
    transform: scale(.98);
}

.ecosystem-sense strong {
    margin-top: 1.4rem;
    font-family: var(--font-display);
    font-size: clamp(1.2rem, 1.6vw, 1.65rem);
    font-weight: 400;
}

.ecosystem-sense-role {
    margin-top: 1.4rem;
    color: var(--text-muted);
    font-size: .86rem;
    font-weight: 300;
    line-height: 1.65;
}

.ecosystem-sense-arrow {
    margin-top: auto;
    color: var(--white-soft);
    font-size: 1.2rem;
    transition: color var(--motion) var(--ease-house);
}

@media (max-width: 1000px) {
    .ecosystem-map-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .ecosystem-sense {
        min-height: 0;
        border-bottom: 1px solid var(--line);
    }
}

@media (max-width: 700px) {
    .ecosystem-map-grid {
        grid-template-columns: 1fr;
    }

    .ecosystem-sense {
        min-height: 0;
        border-right: 0;
    }

    .ecosystem-sense-image {
        max-width: 420px;
    }
}
/* #endregion */

/* #region Alibi configurator wizard */
.configurator-modal-content[data-configurator-kind="alibi"] {
    display: block;
    overflow-y: auto;
}

.cfg-wizard {
    min-height: min(760px, calc(100vh - clamp(2rem, 6vw, 6rem)));
    display: flex;
    flex-direction: column;
    padding: clamp(2.4rem, 4vw, 4.2rem);
}

.cfg-wizard-progress {
    position: relative;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    margin-bottom: clamp(3rem, 6vh, 5rem);
    /* An ordered list, so a screen reader hears "step 2 of 3" without a progressbar's numeric guesswork. */
    padding: 0;
    list-style: none;
}

    /* Two-step wizards (Art, Soul, Space) would otherwise leave a third of the rail empty. */
    .cfg-wizard-progress.cfg-wizard-progress--two {
        grid-template-columns: repeat(2, 1fr);
    }

    .cfg-wizard-progress::before {
        content: "";
        position: absolute;
        top: 10px;
        left: 8%;
        right: 8%;
        height: 1px;
        background: var(--line);
    }

.cfg-wizard-progress-item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .65rem;
    color: rgba(245, 245, 245, .32);
    font-size: .58rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    transition: color .3s var(--ease-house);
}

    .cfg-wizard-progress-item b {
        z-index: 1;
        width: 21px;
        height: 21px;
        display: grid;
        place-items: center;
        border: 1px solid rgba(245, 245, 245, .16);
        border-radius: 50%;
        background: #050505;
        font-size: .5rem;
        font-weight: 400;
    }

    .cfg-wizard-progress-item.is-active,
    .cfg-wizard-progress-item.is-complete {
        color: var(--gold);
    }

        .cfg-wizard-progress-item.is-active b {
            border-color: var(--gold);
            box-shadow: 0 0 0 4px rgba(var(--gold-rgb),.08);
        }

        /* A finished step is drawn, not filled: gold is never a surface. */
        .cfg-wizard-progress-item.is-complete b {
            border-color: var(--gold);
            background: var(--black);
            color: var(--gold);
        }

.cfg-wizard-panel[hidden] {
    display: none;
}

.cfg-wizard-panel.is-active {
    flex: 1;
    display: flex;
    flex-direction: column;
    animation: cfgWizardReveal .45s var(--ease-house) both;
}

@keyframes cfgWizardReveal {
    from {
        opacity: 0;
        transform: translateX(18px);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

.cfg-wizard-heading {
    max-width: 620px;
    margin: 0 auto clamp(2.4rem, 5vh, 4rem);
    text-align: center;
}

.cfg-wizard-kicker {
    display: block;
    margin-bottom: .9rem;
    color: rgba(245, 245, 245, .5);
    font-size: .58rem;
    letter-spacing: .24em;
    text-transform: uppercase;
}

.cfg-wizard-heading h3 {
    margin: 0 0 .9rem;
    outline: 0;
    color: var(--white);
    font-family: var(--font-display);
    font-size: clamp(2rem, 3.6vw, 3.4rem);
    font-weight: 300;
    line-height: 1.1;
    letter-spacing: -.025em;
}

.cfg-wizard-heading p {
    max-width: 55ch;
    margin: 0 auto;
    color: var(--text-muted);
    font-size: .82rem;
    font-weight: 300;
    line-height: 1.65;
}

.configurator-modal-content .cfg-wizard-panel .cfg-axis {
    display: block;
    width: min(100%, 680px);
    margin: 0 auto;
    padding: 0;
    border: 0;
}

.configurator-modal-content .cfg-wizard-panel .cfg-edition-choices {
    grid-column: auto;
    gap: 1rem;
}

.configurator-modal-content .cfg-wizard-panel .cfg-choice {
    min-height: 185px;
    padding: 1.6rem;
}

.configurator-modal-content .cfg-wizard-panel .cfg-qty-row {
    grid-column: auto;
    gap: 1rem;
    padding: 1.2rem;
    border: 1px solid var(--hairline);
    background: var(--tint);
}

.configurator-modal-content .cfg-wizard-panel .cfg-qty-btn {
    width: 64px;
    height: 64px;
    font-size: 1.35rem;
}

.configurator-modal-content .cfg-wizard-panel .cfg-qty-value {
    min-width: 100px;
    font-family: var(--font-display);
    font-size: 2rem;
}

.cfg-wizard-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: auto;
    padding-top: 2.5rem;
}

.cfg-wizard-actions--end {
    justify-content: flex-end;
}

.cfg-wizard-next,
.cfg-wizard-back {
    min-width: 150px;
    padding: 1rem 1.4rem;
    border: 1px solid var(--white);
    font-family: var(--font-mono);
    font-size: .62rem;
    letter-spacing: .16em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background .3s var(--ease-house), color .3s var(--ease-house), border-color .3s var(--ease-house);
}

/* The step forward is the house button of --button-layers; the step back is quieter still. */
.cfg-wizard-next {
    background: var(--button-layers);
    border-color: var(--button-border);
    color: var(--white);
    transition: background-size var(--motion) var(--ease-house), border-color var(--motion) var(--ease-house), transform .2s var(--ease-house);
}

    .cfg-wizard-next:hover, .cfg-wizard-next:focus-visible {
        background-size: 100% 1px, 100% 100%;
        border-color: var(--button-border-hover);
    }

    .cfg-wizard-next:active {
        transform: translateY(1px);
    }

.cfg-wizard-back {
    background: transparent;
    border-color: rgba(245, 245, 245, .16);
    color: var(--text-muted);
}

    .cfg-wizard-back:hover, .cfg-wizard-back:focus-visible {
        border-color: var(--gold);
        color: var(--gold);
    }

.cfg-review {
    width: min(100%, 720px);
    display: grid;
    grid-template-columns: minmax(0, .8fr) minmax(280px, 1fr);
    margin: 0 auto;
    border: 1px solid var(--hairline);
}

.configurator-modal-content .cfg-review .cfg-included {
    padding: 2rem;
    border-right: 1px solid var(--line);
}

.configurator-modal-content .cfg-review .configurator-summary {
    position: static;
    display: block;
    border: 0;
    background: var(--tint);
}

.configurator-modal-content .cfg-review .cfg-summary-card {
    display: flex;
    padding: 2rem;
}

.configurator-modal-content .cfg-review .cfg-summary-note {
    margin-bottom: 0;
}

.configurator-modal-content .cfg-review .cfg-add-btn {
    margin-top: 1.6rem;
}

@media (max-width: 760px) {
    .cfg-wizard {
        min-height: calc(100dvh - 245px);
        padding: 1.8rem 1.4rem max(1.5rem, env(safe-area-inset-bottom));
    }

    .cfg-wizard-progress {
        margin-bottom: 2.6rem;
    }

    .cfg-wizard-progress-item span {
        font-size: .5rem;
        letter-spacing: .08em;
    }

    .cfg-wizard-heading {
        margin-bottom: 2rem;
    }

        .cfg-wizard-heading h3 {
            font-size: clamp(1.85rem, 9vw, 2.6rem);
        }

    .configurator-modal-content .cfg-wizard-panel .cfg-edition-choices {
        grid-template-columns: 1fr;
    }

    .configurator-modal-content .cfg-wizard-panel .cfg-choice {
        min-height: 140px;
    }

    .configurator-modal-content .cfg-wizard-panel .cfg-qty-row {
        padding: .8rem;
    }

    .configurator-modal-content .cfg-wizard-panel .cfg-qty-btn {
        width: 52px;
        height: 52px;
    }

    .configurator-modal-content .cfg-wizard-panel .cfg-qty-value {
        min-width: 72px;
        font-size: 1.65rem;
    }

    .cfg-review {
        grid-template-columns: 1fr;
    }

    .configurator-modal-content .cfg-review .cfg-included {
        padding: 1.5rem;
        border-right: 0;
        border-bottom: 1px solid var(--line);
    }

    .configurator-modal-content .cfg-review .cfg-summary-card {
        display: flex;
        padding: 1.5rem;
    }

    .cfg-wizard-actions {
        position: sticky;
        bottom: 0;
        margin-inline: -1.4rem;
        padding: 1rem 1.4rem max(1rem, env(safe-area-inset-bottom));
        background: linear-gradient(to top, #050505 72%, rgba(5, 5, 5, 0));
    }

    .cfg-wizard-next, .cfg-wizard-back {
        min-width: 0;
        flex: 1;
        padding-inline: .8rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .cfg-wizard-panel.is-active {
        animation: none;
    }
}
/* #endregion */

/* #region Alibi configurator modal */
body.configurator-modal-open {
    overflow: hidden;
}

.configurator-modal[hidden] {
    display: none;
}

.configurator-modal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: grid;
    place-items: center;
    padding: clamp(1rem, 3vw, 3rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    opacity: 0;
    pointer-events: none;
    transition: opacity .32s ease;
}

    .configurator-modal.is-open {
        opacity: 1;
        pointer-events: auto;
    }

.configurator-modal-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(5, 5, 5, .82);
    backdrop-filter: blur(14px);
}

.configurator-modal-dialog {
    position: relative;
    width: min(1180px, 100%);
    height: min(880px, calc(100dvh - clamp(2rem, 6vw, 6rem)));
    max-height: min(880px, calc(100vh - clamp(2rem, 6vw, 6rem)));
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(280px, .7fr) minmax(0, 1.65fr);
    overflow: hidden;
    border: 1px solid var(--hairline-strong);
    outline: 0;
    background: #050505;
    box-shadow: 0 40px 120px rgba(5, 5, 5, .8);
    transform: translateY(24px) scale(.985);
    transition: transform .42s var(--ease-house);
}

.configurator-modal.is-open .configurator-modal-dialog {
    transform: translateY(0) scale(1);
}

.configurator-modal-close {
    position: absolute;
    z-index: 4;
    top: 1.35rem;
    right: 1.35rem;
    width: 42px;
    height: 42px;
    display: grid;
    place-items: center;
    padding: 0;
    border: 1px solid rgba(245, 245, 245, .16);
    border-radius: 50%;
    background: rgba(5, 5, 5, .72);
    color: var(--white);
    cursor: pointer;
    transition: border-color .3s var(--ease-house), color .3s var(--ease-house), transform .3s var(--ease-house);
}

    .configurator-modal-close::before,
    .configurator-modal-close::after {
        content: "";
        position: absolute;
        width: 15px;
        height: 1px;
        background: currentColor;
    }

    .configurator-modal-close::before {
        transform: rotate(45deg);
    }

    .configurator-modal-close::after {
        transform: rotate(-45deg);
    }

    .configurator-modal-close:hover,
    .configurator-modal-close:focus-visible {
        border-color: var(--gold);
        color: var(--gold);
        transform: rotate(8deg);
    }

.configurator-modal-intro {
    position: relative;
    min-height: 100%;
    display: flex;
    flex-direction: column;
    padding: clamp(2.4rem, 4vw, 4.5rem) clamp(2rem, 3.4vw, 3.8rem);
    overflow: hidden;
    border-right: 1px solid var(--line);
    background: radial-gradient(circle at 50% 75%, var(--tint-raised), transparent 34%), linear-gradient(145deg, #111 0%, #050505 68%);
}

    .configurator-modal-intro::before {
        content: "";
        position: absolute;
        inset: 16px;
        border: 1px solid var(--hairline-faint);
        pointer-events: none;
    }

    .configurator-modal-intro .h-tag {
        position: relative;
        margin-bottom: 1.3rem;
    }

    .configurator-modal-intro h2 {
        position: relative;
        max-width: 8ch;
        margin: 0 0 1.5rem;
        font-size: clamp(2.35rem, 4vw, 4.25rem);
        line-height: 1.02;
        letter-spacing: -.035em;
    }

    .configurator-modal-intro > p {
        position: relative;
        max-width: 32ch;
        margin: 0;
        color: var(--text-muted);
        font-size: .88rem;
        font-weight: 300;
        line-height: 1.75;
    }

.configurator-modal-content {
    min-width: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(260px, .66fr);
    align-items: stretch;
    overflow-y: auto;
}

    .configurator-modal-content .cfg-axis {
        position: relative;
        display: grid;
        grid-template-columns: 34px minmax(0, 1fr);
        column-gap: 1rem;
        padding-bottom: 2.4rem;
        margin-bottom: 2.4rem;
        border-bottom: 1px solid var(--line);
    }

    .configurator-modal-content .cfg-axis-title {
        margin-bottom: .35rem;
        color: var(--white);
        font-size: .72rem;
        letter-spacing: .2em;
    }

    .configurator-modal-content .cfg-choices,
    .configurator-modal-content .cfg-qty-row {
        grid-column: 2;
    }

    .configurator-modal-content .cfg-edition-choices {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: .8rem;
    }

    .configurator-modal-content .cfg-choice {
        position: relative;
        min-width: 0;
        min-height: 160px;
        gap: .7rem;
        padding: 1.4rem;
        border-color: rgba(245, 245, 245, .1);
        background: rgba(245, 245, 245, .012);
    }

        .configurator-modal-content .cfg-choice:hover {
            border-color: rgba(var(--gold-rgb),.5);
        }

        .configurator-modal-content .cfg-choice.selected {
            border-color: rgba(var(--gold-rgb),.78);
            background: linear-gradient(145deg, rgba(var(--gold-rgb),.1), rgba(var(--gold-rgb),.025));
        }

    .configurator-modal-content .cfg-choice-check {
        width: 16px;
        height: 16px;
        margin-bottom: .2rem;
        border: 1px solid rgba(245, 245, 245, .25);
        border-radius: 50%;
        box-shadow: inset 0 0 0 4px #050505;
        transition: background .3s var(--ease-house), border-color .3s var(--ease-house);
    }

    .configurator-modal-content .cfg-choice.selected .cfg-choice-check {
        border-color: var(--gold);
        background: var(--gold);
    }

    .configurator-modal-content .cfg-choice-name {
        color: var(--white);
        font-size: 1rem;
        font-weight: 500;
    }

    .configurator-modal-content .cfg-choice-description {
        color: var(--text-muted);
        font-size: .72rem;
        font-weight: 300;
        line-height: 1.55;
    }

    .configurator-modal-content .cfg-choice-price {
        margin-top: auto;
        color: rgba(var(--gold-rgb),.88);
        font-size: .6rem;
        letter-spacing: .08em;
        line-height: 1.4;
    }

    .configurator-modal-content .cfg-qty-row {
        display: flex;
        align-items: center;
        gap: .9rem;
    }

    .configurator-modal-content .cfg-qty-control {
        border-color: rgba(245, 245, 245, .14);
    }

    .configurator-modal-content .cfg-qty-btn {
        width: 46px;
        height: 46px;
    }

    .configurator-modal-content .cfg-qty-value {
        min-width: 58px;
        font-size: 1.1rem;
    }

    .configurator-modal-content .cfg-qty-unit {
        color: var(--text-muted);
        font-size: .72rem;
        letter-spacing: .1em;
        text-transform: uppercase;
    }

    .configurator-modal-content .cfg-included {
        padding-left: 50px;
    }

        .configurator-modal-content .cfg-included .cfg-axis-title {
            margin-bottom: 1rem;
            color: rgba(245, 245, 245, .5);
        }

    .configurator-modal-content .configurator-summary {
        position: static;
        display: flex;
        border-left: 1px solid var(--line);
        background: rgba(245, 245, 245, .015);
    }

    .configurator-modal-content .cfg-summary-card {
        width: 100%;
        display: flex;
        flex-direction: column;
        padding: clamp(5.5rem, 7vw, 7rem) clamp(1.8rem, 3vw, 3rem) 2.6rem;
        border: 0;
        background: transparent;
    }

.cfg-summary-eyebrow {
    margin-bottom: .8rem;
    color: var(--text-muted);
    font-size: .58rem;
    letter-spacing: .2em;
    text-transform: uppercase;
}

.configurator-modal-content .cfg-summary-product {
    margin-bottom: 1.5rem;
    color: var(--white-soft);
    font-size: .68rem;
    line-height: 1.6;
    letter-spacing: .16em;
}

.configurator-modal-content .cfg-price-onetime {
    font-size: clamp(2rem, 3.4vw, 3.1rem);
    line-height: 1;
}

.cfg-summary-unit {
    margin: -.5rem 0 1.2rem;
    color: var(--text-muted);
    font-size: .69rem;
}

.configurator-modal-content .cfg-summary-note {
    margin-bottom: auto;
}

/* The media disclosure under the price. Quieter than the note above it, but held to a
   readable size — a disclosure nobody can comfortably read is not a disclosure. */
.cfg-media-note {
    margin-top: 1rem;
    font-size: .62rem;
    line-height: 1.7;
    letter-spacing: .02em;
    opacity: .62;
}

    .cfg-media-note a {
        text-decoration: underline;
        text-underline-offset: .2em;
    }

.configurator-modal-content .cfg-add-btn {
    margin-top: 2rem;
    padding-inline: 1rem;
    font-size: .64rem;
    letter-spacing: .18em;
}

    .configurator-modal-content .cfg-add-btn span {
        margin-left: .5rem;
    }

.configurator-modal-content[aria-busy="true"] .cfg-summary-price,
.configurator-modal-content[aria-busy="true"] .cfg-summary-unit {
    opacity: .45;
}

.configurator-modal-content .cfg-summary-craft {
    font-size: .58rem;
    letter-spacing: .1em;
    line-height: 1.6;
}

@media (max-width: 1080px) {
    .configurator-modal-dialog {
        grid-template-columns: minmax(240px, .55fr) minmax(0, 1.65fr);
    }

    .configurator-modal-content {
        grid-template-columns: 1fr;
    }

        .configurator-modal-content .configurator-summary {
            border-top: 1px solid var(--line);
            border-left: 0;
        }

        .configurator-modal-content .cfg-summary-card {
            display: grid;
            grid-template-columns: 1fr auto;
            column-gap: 2rem;
            padding: 2rem 2.5rem;
        }

        .configurator-modal-content .cfg-summary-eyebrow,
        .configurator-modal-content .cfg-summary-product,
        .configurator-modal-content .cfg-summary-price,
        .configurator-modal-content .cfg-summary-unit,
        .configurator-modal-content .cfg-summary-note {
            grid-column: 1;
        }

        .configurator-modal-content .cfg-summary-note {
            margin-bottom: 0;
        }

        .configurator-modal-content .cfg-add-btn,
        .configurator-modal-content .cfg-summary-craft {
            grid-column: 2;
        }

        .configurator-modal-content .cfg-add-btn {
            grid-row: 1 / span 4;
            align-self: center;
            min-width: 220px;
            margin-top: 0;
        }

        .configurator-modal-content .cfg-summary-craft {
            grid-row: 5;
        }
}

@media (max-width: 760px) {
    .configurator-modal {
        place-items: stretch;
        padding: 0;
    }

    .configurator-modal-dialog {
        width: 100%;
        height: 100dvh;
        max-height: 100dvh;
        display: block;
        overflow-y: auto;
        overscroll-behavior: contain;
        border: 0;
    }

    .configurator-modal-intro {
        min-height: 0;
        padding: 3rem 1.4rem 2rem;
        border-right: 0;
        border-bottom: 1px solid var(--line);
    }

        .configurator-modal-intro::before {
            display: none;
        }

        .configurator-modal-intro h2 {
            max-width: 10ch;
            margin-bottom: 1rem;
            font-size: clamp(2.25rem, 12vw, 3.6rem);
        }

        .configurator-modal-intro > p {
            max-width: 46ch;
        }

    .configurator-modal-close {
        position: fixed;
        top: .85rem;
        right: .85rem;
    }

    .configurator-modal-content {
        display: block;
        overflow: visible;
    }

        .configurator-modal-content .cfg-axis {
            grid-template-columns: 24px minmax(0, 1fr);
            column-gap: .65rem;
        }

        .configurator-modal-content .cfg-edition-choices {
            grid-template-columns: 1fr;
        }

        .configurator-modal-content .cfg-choice {
            min-height: 145px;
        }

        .configurator-modal-content .cfg-included {
            padding-left: 34px;
        }

        .configurator-modal-content .cfg-summary-card {
            display: flex;
            padding: 2.2rem 1.4rem max(2rem, env(safe-area-inset-bottom));
        }

        .configurator-modal-content .cfg-add-btn {
            min-width: 0;
            margin-top: 1.2rem;
        }

        .configurator-modal-content .cfg-summary-craft {
            margin-top: 1rem;
        }
}

@media (prefers-reduced-motion: reduce) {
    .configurator-modal,
    .configurator-modal-dialog {
        transition: none;
    }
}
/* #endregion */

/* #region Testimonials */
.testi-header {
    margin-bottom: 10vh;
}

.testi-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 6vw;
    max-width: 1400px;
    margin: 0 auto;
}

.testi-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
}

.testi-quote {
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 300;
    color: var(--white);
    font-style: italic;
    line-height: 1.7;
    margin-bottom: 3vh;
    position: relative;
}

    .testi-quote::before {
        content: '"';
        font-family: var(--font-display);
        font-size: 4rem;
        color: var(--hairline);
        position: absolute;
        top: -20px;
        left: -20px;
        z-index: -1;
        line-height: 1;
    }

.testi-author {
    font-family: var(--font-mono);
    font-weight: 300;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 2px;
    color: rgba(245, 245, 245, .5);
}

.testi-role {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 5px;
    font-weight: 200;
}
/* #endregion */

/* #region B2B use cases */
.b2b-header {
    margin-bottom: 8vh;
}

.b2b-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4vw;
    max-width: 1400px;
    margin: 0 auto;
}

.b2b-card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 20rem;
    padding: clamp(2rem, 3.4vw, 3.5rem);
    border: 1px solid var(--hairline);
    background: linear-gradient(145deg, var(--tint), transparent 48%), var(--surface-card);
    color: inherit;
    text-decoration: none;
    opacity: 0;
    transform: translateY(30px);
    transition: all 1s var(--ease-house);
}

    /* A short white rule at rest; the card's gold arrives with the hand, in this rule and its index. */
    .b2b-card::after {
        position: absolute;
        right: 0;
        bottom: 0;
        width: 38%;
        height: 1px;
        background: var(--hairline-strong);
        content: "";
        transition: background-color var(--motion) var(--ease-house);
    }

    .b2b-card:hover::after,
    .b2b-card:focus-visible::after {
        background: var(--gold);
    }

.b2b-index {
    display: block;
    margin-bottom: auto;
    padding-bottom: clamp(4rem, 7vw, 7rem);
    color: rgba(245, 245, 245, .5);
    font-family: var(--font-mono);
    font-size: .58rem;
    letter-spacing: .2em;
    transition: color var(--motion) var(--ease-house);
}

    .b2b-card:hover .b2b-index,
    .b2b-card:focus-visible .b2b-index {
        color: var(--gold);
    }

.b2b-card.visible,
.b2b-grid.visible .b2b-card {
    opacity: 1;
    transform: translateY(0);
}

.b2b-card.delay-1 {
    transition-delay: 0.2s;
}

.b2b-card.delay-2 {
    transition-delay: 0.4s;
}

.b2b-img-box {
    width: 100%;
    aspect-ratio: 4/3;
    margin-bottom: 3vh;
    overflow: hidden;
    background: var(--surface-card);
    border: 1px solid var(--hairline);
}

.b2b-img {
    width: 100%;
    height: 100%;
    background-size: cover;
    background-position: center;
    transition: transform 1.8s var(--ease-house), filter var(--motion-slow) var(--ease-house);
    /* The card's title and lead sit over this, so it is a ground rather than a picture shown for
       itself. Restated one exposure lighter in a product's own stylesheet, the identical card reads
       differently on two pages. */
    filter: var(--photo-mute) var(--photo-as-ground);
}

.b2b-card:hover .b2b-img {
    transform: scale(1.025);
    filter: var(--photo-lifted);
}

/* A finger cannot hover, so without this a touch screen never shows the card's photograph at all. It
   belongs here rather than on one product's stylesheet, or the same card looks different per page. */
@media (hover: none) {
    .b2b-img {
        filter: var(--photo-lifted);
    }
}

.b2b-title {
    font-family: var(--font-display);
    font-size: 1.6rem;
    color: var(--white);
    font-weight: 300;
    margin-bottom: 2vh;
}

.b2b-text {
    font-size: 1rem;
    font-weight: 200;
    color: var(--text-muted);
    line-height: 1.8;
}
/* #endregion */

/* #region Pricing */
.pricing-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
    gap: 4vw;
    width: 100%;
    max-width: none;
    margin: 0 auto;
    opacity: 0;
    transform: translateY(40px);
    transition: all 1s var(--ease-house);
}

    .pricing-grid.visible {
        opacity: 1;
        transform: translateY(0);
    }

.pricing-section .inv-box,
.pricing-section .b2b-pricing-box {
    width: 100%;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    justify-content: flex-start;
}

.pricing-section .inv-box {
    padding: clamp(2.5rem, 5vw, 5rem);
}

.pricing-section .b2b-pricing-box {
    position: relative;
    padding: clamp(2.5rem, 5vw, 5rem);
    background: linear-gradient(155deg, var(--tint-raised), transparent 65%), var(--surface-card);
    border: 1px solid var(--hairline-mid);
}

    /* Restated at this weight, or the rule above outranks site.css's hover and the edge never brightens. */
    .pricing-section .b2b-pricing-box:hover,
    .pricing-section .b2b-pricing-box:focus-visible {
        border-color: var(--hairline-strong);
    }

    /* The featured box: a firmer edge, nothing louder. The class rode in the markup
       with no definition, so both boxes rendered identically. */
    .pricing-section .b2b-pricing-box.premium {
        border-color: var(--hairline-strong);
    }

        .pricing-section .b2b-pricing-box.premium:hover,
        .pricing-section .b2b-pricing-box.premium:focus-visible {
            border-color: var(--white-faint);
        }

.bp-header-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .75rem 1.5vw;
    width: 100%;
}


.pricing-section .inv-box .inv-label,
.pricing-section .b2b-pricing-box h3.bp-title-white {
    font-family: var(--font-display);
    font-size: clamp(1.6rem, 7vw, 2.2rem);
    font-weight: 300;
    letter-spacing: normal;
    text-transform: none;
    color: var(--white);
    margin: 0;
}

.pricing-section .inv-box .inv-label {
    margin-bottom: 3.5vh;
}

.pricing-section .bp-header-row {
    margin-bottom: 3.5vh;
}

.pricing-section .bp-features,
.pricing-section .inv-list {
    margin-bottom: 3vh;
}

.price-block {
    width: 100%;
    margin-top: auto;
    padding-top: 1.5vh;
    border-top: 1px solid var(--hairline);
}

.pricing-section .inv-price,
.pricing-section .bp-price {
    font-size: clamp(2rem, 9vw, 3rem);
    margin-bottom: .8vh;
}

.pricing-section .inv-sub,
.pricing-section .bp-sub {
    display: block;
    margin-bottom: 0;
}

/* Both a <button> (the configurator and butler CTAs) and an <a> (a section whose only way forward
   is another page) wear this. The flex centring and the dropped underline are what make the anchor
   land pixel-for-pixel on the button. */
.pricing-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    margin-top: 3vh;
    min-height: 44px;
    padding: 1.1em 2.2em;
    position: relative;
    isolation: isolate;
    background: transparent;
    border: 1px solid var(--hairline-strong);
    color: var(--white);
    font-family: var(--font-mono);
    font-size: .68rem;
    font-weight: 300;
    letter-spacing: .18em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color var(--motion) var(--ease-house), background-color var(--motion) var(--ease-house), transform .2s var(--ease-house);
    align-self: stretch;
    text-align: center;
}

    /* Under the hand a breath of light rises from the foot of the button and a gold line opens from
       the middle of its lower edge; pressed, it gives a pixel, like a key. */
    .pricing-cta::before {
        position: absolute;
        inset: 0;
        z-index: -1;
        background: rgba(245, 245, 245, .045);
        transform: scaleY(0);
        transform-origin: bottom;
        transition: transform var(--motion) var(--ease-house);
        content: '';
    }

    /* Drawn over the lower border, which goes transparent under the hand: the gold replaces the edge. */
    .pricing-cta::after {
        position: absolute;
        left: 50%;
        right: 50%;
        bottom: -1px;
        height: 1px;
        background: var(--gold);
        transition: left var(--motion) var(--ease-house), right var(--motion) var(--ease-house);
        content: '';
    }

    .pricing-cta:hover,
    .pricing-cta:focus-visible {
        border-color: var(--button-border) var(--button-border) transparent;
    }

        .pricing-cta:hover::before,
        .pricing-cta:focus-visible::before {
            transform: scaleY(1);
        }

        .pricing-cta:hover::after,
        .pricing-cta:focus-visible::after {
            left: 0;
            right: 0;
        }

    .pricing-cta:active {
        transform: translateY(1px);
    }

/* The primary of the pair is not filled — a filled surface is the loudest thing on a black page — but
   drawn a step stronger than its neighbour. */
.pricing-cta-premium {
    border-color: var(--button-border);
}

    .pricing-cta-premium:hover,
    .pricing-cta-premium:focus-visible {
        border-color: var(--button-border-hover);
    }

@media (prefers-reduced-motion: reduce) {
    .pricing-cta,
    .pricing-cta::before,
    .pricing-cta::after {
        transition: none;
    }
}

/* Atelier, said quietly. It spans the grid rather than sitting in a column of its own, carries no
   border and no button — a name, a sentence and a link. The bespoke programme is an aside to the
   price above it, not a competing offer. */
/* A section that sells one thing fills the band with one plate, not with a card standing in the
   middle of it. The two-column grid was emptied when the Atelier door moved to the closing band and
   the terms came down to a line; capping the survivor at 46rem and centring it only made the hole
   symmetrical — Odoardo Lens™ and Odoardo Space™ both ended on a small box adrift in a wide,
   otherwise empty section. The two halves of the offer — what it contains, what it costs — go side
   by side inside the plate instead, which is the same reading order as two boxes beside each
   other. */
.pricing-grid-single {
    grid-template-columns: minmax(0, 1fr);
}

    .pricing-grid-single .inv-box {
        display: grid;
        grid-template-columns: minmax(0, 1.1fr) minmax(15rem, .9fr);
        gap: clamp(2.5rem, 5vw, 5rem);
        align-items: center;
        width: 100%;
        text-align: left;
    }

    .pricing-grid-single .inv-label {
        margin-top: 0;
    }

    /* The rule stands between the two halves rather than over the price. */
    .pricing-grid-single .inv-box-offer {
        margin-top: 0;
        align-items: stretch;
        padding-left: clamp(2.5rem, 5vw, 5rem);
        border-left: 1px solid var(--hairline);
    }

    .pricing-grid-single .price-block {
        margin-top: 0;
        padding-top: 0;
        border-top: 0;
    }

/* The two halves of the price box. Side by side they are the single-offer plate above; stacked they
   are the box every two-column section has always shown, which is why the offer keeps the auto top
   margin that pushes the price to the foot of the card. */
.inv-box-copy,
.inv-box-offer {
    width: 100%;
    min-width: 0;
}

.inv-box-offer {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: auto;
}

@media (max-width: 900px) {
    .pricing-grid-single .inv-box {
        grid-template-columns: 1fr;
        text-align: center;
    }

    .pricing-grid-single .inv-box-offer {
        align-items: center;
        padding-left: 0;
        padding-top: clamp(1.6rem, 3vh, 2.2rem);
        border-left: 0;
        border-top: 1px solid var(--hairline);
    }
}

/* The column beside the price on a page that sells one thing: the thing itself.

   Two prefixes here and the split is deliberate. `.pricing-showcase` is this column — where the
   object stands inside the pricing grid, which is a fact about the pricing section. Everything from
   `.oak-frame-plate` down is the OBJECT, and it is shown in more than one place now: beside the
   price, where the whole plate opens the configurator, and in „Jedna rama, nowy rozdział co kwartał",
   where it is the subject and opens nothing. Naming the object `.pricing-showcase-*` too is only
   true while the frame lives in the pricing section alone, and becomes a lie the moment a second
   section shows it — hence the two names. Do not fold them back together. */
.pricing-showcase {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(.8rem, 1.6vh, 1.2rem);
    min-width: 0;
}

    .pricing-showcase .inv-label {
        margin: 0;
        text-align: left;
    }

/* The work hangs inside the frame the patron is buying, and the frame is built rather than
   photographed — the same way the two Lens objects are, and for the same reason: a flat photograph of
   a frame with a print laid over its opening is a picture of the product, while an object that stands
   in space, turns and shows its own profile is the product. It is pure CSS in 3D: no library, no
   canvas, nothing extra to download. Script does two things and neither is the animation — it follows
   the pointer and it marks a model that is actually on the screen, so the sway starts paused and a
   browser with no scripting simply gets the frame in its opening pose.

   Every length is read off the plate through one container, so the whole object scales with the
   column and each number is written once. The profile is the opening in frame-wood.webp, which is the
   number this column already stood on. */
.oak-frame-plate {
    position: relative;
    display: block;
    /* The object is what this column is for, so it takes the room the column has. Every length inside
       it is read off this one width, so the frame, its profile, its depth and the works going through
       it all grow together. */
    width: min(100%, 460px);
    margin: 0 auto;
    aspect-ratio: 1;
    container-type: inline-size;
}

/* The button is the control and the frame is the object, deliberately two elements: a form control is
   the one box browsers are entitled to flatten, and an object built in 3D inside one would come out
   as a squashed picture. So the button is an invisible layer the size of the plate — which is also
   where the focus ring belongs, around the thing it opens. */
.oak-frame-open {
    position: absolute;
    z-index: 3;
    inset: 0;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.oak-frame-open:focus-visible {
    outline: 1px solid var(--gold);
    outline-offset: 10px;
}

/* The angle the frame is standing at, as a plain number beside the transform that turns it. It is
   registered so it can be interpolated at all: the oak reads it to know how much light it is taking,
   so the light crosses the wood as the object turns instead of stepping between poses. */
@property --oak-turn {
    syntax: "<number>";
    inherits: true;
    initial-value: 0;
}

.oak-frame-model {
    /* 11.5% of the plate — the passe-partout in the photograph of the frame, so the print sits in the
       opening and never touches the oak. */
    /* The moulding around the opening, kept narrow: the frame is square either way, but wood a quarter of
       the object wide on every side leaves the work looking like a detail of it. */
    --oak-profile: 7.7cqw;
    /* A solid-oak profile seen from the side. Nobody has fixed the depth of this frame in the
       workshop, so what decides it is whether the object reads as a board or as a frame: at 5% the
       sides are a line at the sway's extremes and the whole thing looks flat. Nine per cent is a profile a
       hand could hold, and it stays inside the plate because
       the face is measured off it. */
    --oak-depth: 9cqw;
    /* The mini-frame each chapter arrives in — thin, so it reads as the print's own edge rather than
       as a second frame competing with the one it goes into. */
    --oak-mini-profile: 1.15cqw;
    /* Air between one chapter and the next. Without it two mini-frames meet edge to edge and their
       two strips of wood read as one thick moulding — the works arrive as a ribbon rather than one
       at a time, which is the whole point of the reel. */
    --oak-work-gap: .9cqw;
    /* The face of the frame, square, and the plate is what it is measured off. */
    --oak-face: calc(100cqw - 2 * var(--oak-depth));
    /* The opening, which is also the height of one work in the reel. */
    --oak-open-height: calc(var(--oak-face) - 2 * var(--oak-profile));
    /* How far above the frame a work is already in the air, coming down. */
    --oak-lead: calc(var(--oak-open-height) * .45);
    display: block;
    width: 100%;
    height: 100%;
}

.oak-frame-scene {
    position: relative;
    display: grid;
    width: 100%;
    height: 100%;
    place-items: center;
    perspective: 150cqw;
    perspective-origin: 50% 44%;
}

/* Two layers, two jobs: the lean follows the pointer, the sway runs on its own. Kept apart so a
   visitor moving the mouse never fights the animation for the same transform. */
.oak-frame-tilt {
    transform: rotateX(calc(var(--oak-tilt-x, 0) * 1deg)) rotateY(calc(var(--oak-tilt-y, 0) * 1deg));
    transform-style: preserve-3d;
    transition: transform 1s var(--ease-house);
}

.oak-frame-plate:hover .oak-frame-tilt {
    transform: rotateX(calc(var(--oak-tilt-x, 0) * 1deg)) rotateY(calc(var(--oak-tilt-y, 0) * 1deg)) scale(1.03);
}

.oak-frame-turn {
    animation: oak-sway 15s ease-in-out infinite;
    animation-play-state: paused;
    transform-style: preserve-3d;
    will-change: transform;
}

.oak-frame-model.is-live .oak-frame-turn {
    animation-play-state: running;
}

/* A frame hangs on a wall, so it sways instead of turning: its back is a wall's business, and a work
   swung right round would be a card trick rather than a picture. */
@keyframes oak-sway {
    0%, 100% {
        --oak-turn: -17;
        transform: rotateY(-17deg) rotateX(3deg);
    }

    50% {
        --oak-turn: 17;
        transform: rotateY(17deg) rotateX(-2deg);
    }
}

.oak-frame-oak {
    /* Oak has no gilding, so nothing here glints. What wood does is take more or less of the room as
       it turns, and only the sides and the back are wood — the print is matt, hangs behind no glass
       and is left alone. It is read here rather than higher up because the angle it is a function of
       is set on the element above this one, and a custom property only ever inherits downwards. */
    --oak-glint: calc(1 + .22 * cos(calc(var(--oak-turn, 0) + var(--oak-tilt-y, 0) * 3) * 1deg));
    position: relative;
    width: var(--oak-face);
    aspect-ratio: 1;
    transform-style: preserve-3d;
}

.oak-frame-face {
    position: absolute;
    box-sizing: border-box;
    backface-visibility: hidden;
    inset: 0;
}

/* The oak is the photographed board the frames are built from, so a frame on this page and a Lens
   anchor are cut from the same wood on the screen as they are in the workshop. */
/* Wood and nothing else. The face must not carry the opening's black as its own background: that
   makes the work a child of the face, and a child paints over its parent's border, so anything moving
   in the opening crosses the oak instead of going behind it. The black is a layer of its own
   (.oak-frame-mat) and the works travel between the two. */
.oak-frame-front {
    display: grid;
    border: var(--oak-profile) solid transparent;
    border-image: url('/img/art/frame-wood.webp') 30% round;
    background: transparent;
    /* Two shadows, two jobs: the first is the one the object throws onto the page, the second is the
       rebate — the moulding standing proud of the print it holds. The face is the layer nearest the
       viewer, so its inset shadow falls across the work behind it, which is what makes the opening
       read as a recess head-on and not only when the frame turns. */
    box-shadow: 0 4cqw 9cqw rgba(5, 5, 5, .6), inset 0 0 3.4cqw rgba(5, 5, 5, .7);
    place-items: center;
    transform: translateZ(calc(var(--oak-depth) / 2));
}

/* The wall inside the frame: it stands a hair behind the works so the opening is never empty, and a
   hair in front of the frame's own back so nothing shows through from behind. */
.oak-frame-mat {
    position: absolute;
    inset: var(--oak-profile);
    background: var(--black);
    transform: translateZ(calc(var(--oak-depth) / 2 - .9cqw));
}

/* The works on their way in. The box begins above the frame — where the mask takes it to nothing, so
   a work appears in the air rather than switching on — and ends at the foot of the opening, where the
   bottom rail simply covers it: nothing fades on the way out, the work goes into the frame. Between
   the two it passes behind the top rail, which is what makes it read as being put in. */
.oak-frame-reel {
    position: absolute;
    top: calc(-1 * var(--oak-lead));
    right: var(--oak-profile);
    bottom: var(--oak-profile);
    left: var(--oak-profile);
    overflow: hidden;
    transform: translateZ(calc(var(--oak-depth) / 2 - .45cqw));
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #050505 var(--oak-lead));
    mask-image: linear-gradient(to bottom, transparent 0, #050505 var(--oak-lead));
}

/* Twice the chapter, moved by half its own height: at the end of the run the strip stands exactly
   where it started and the loop has no seam. The offset by the lead is what puts a work square in
   the opening rather than half of one, which is the pose a stopped reel is left in. */
/* flow-root, not block: the last work's bottom margin would otherwise collapse out through the
   strip's own edge, leaving it one gap short of twice the chapter. The keyframes move it by exactly
   -50% of its height, so a strip that is not exactly two chapters tall jumps sideways in time every
   time the loop closes — the same arithmetic the artist marquee had to pay back by hand. */
.oak-frame-strip {
    display: flow-root;
    transform: translateY(calc(-1 * var(--oak-lead)));
    animation: oak-reel var(--oak-reel-duration, 28s) linear infinite;
    animation-play-state: paused;
    will-change: transform;
}

.oak-frame-model.is-live .oak-frame-strip {
    animation-play-state: running;
}

    /* A frame holding one work — an artist's own page — keeps the reel still even once the object is
       on screen. The work hangs and the object lives. */
    .oak-frame-model.is-held .oak-frame-strip {
        animation: none;
    }

    /* AND NOTHING SHOWS ABOVE THE WOOD. The reel box begins a „lead" above the frame's face, and the
       mask fades that air in so a work can be seen coming down into the opening — which is right
       while the reel runs. On a held frame nothing is coming: the second copy hangs in the opening while the
       first stands over the top rail as a faded picture above the object. So the mask cuts instead of
       fading — fully transparent to exactly the lead, opaque
       from there — and the geometry below is untouched, which is what keeps the work square in the
       opening. */
    .oak-frame-model.is-held .oak-frame-reel {
        -webkit-mask-image: linear-gradient(to bottom, transparent 0 var(--oak-lead), #050505 var(--oak-lead));
        mask-image: linear-gradient(to bottom, transparent 0 var(--oak-lead), #050505 var(--oak-lead));
    }

@keyframes oak-reel {
    from {
        transform: translateY(calc(-50% - var(--oak-lead)));
    }

    to {
        transform: translateY(calc(-1 * var(--oak-lead)));
    }
}

.oak-frame-back {
    background-image: linear-gradient(to bottom, rgba(5, 5, 5, .55), rgba(5, 5, 5, .8)), url('/img/art/frame-wood.webp');
    background-size: cover;
    filter: brightness(var(--oak-glint, 1));
    transform: rotateY(180deg) translateZ(calc(var(--oak-depth) / 2));
}

/* The four sides of the profile, each stood on the edge it belongs to. They are what makes the object
   read as a frame with weight rather than a picture of one the moment it turns off square. */
.oak-frame-edge {
    position: absolute;
    background-image: linear-gradient(rgba(5, 5, 5, .3), rgba(5, 5, 5, .62)), url('/img/art/frame-wood.webp');
    background-size: cover;
    filter: brightness(var(--oak-glint, 1));
}

.oak-frame-edge-left,
.oak-frame-edge-right {
    top: 0;
    width: var(--oak-depth);
    height: 100%;
}

.oak-frame-edge-left {
    left: 0;
    transform: translateZ(calc(var(--oak-depth) / -2)) rotateY(-90deg);
    transform-origin: left center;
}

.oak-frame-edge-right {
    right: 0;
    transform: translateZ(calc(var(--oak-depth) / -2)) rotateY(90deg);
    transform-origin: right center;
}

.oak-frame-edge-top,
.oak-frame-edge-bottom {
    left: 0;
    width: 100%;
    height: var(--oak-depth);
}

.oak-frame-edge-top {
    top: 0;
    transform: translateZ(calc(var(--oak-depth) / -2)) rotateX(90deg);
    transform-origin: center top;
}

.oak-frame-edge-bottom {
    bottom: 0;
    transform: translateZ(calc(var(--oak-depth) / -2)) rotateX(-90deg);
    transform-origin: center bottom;
}

/* A chapter as it is posted: the print already in its own thin oak mini-frame, which is what slides
   into the main frame at home. It is the mini-frame, not the print, that fills the opening exactly —
   border-box, so the wood is taken out of the same height and the reel's loop still closes on
   itself. */
.oak-frame-work {
    display: block;
    box-sizing: border-box;
    width: 100%;
    /* One work fills the opening exactly, which is what makes them arrive one at a time rather than
       as a ribbon of pictures. */
    height: var(--oak-open-height);
    /* Uniform on every chapter, which is what keeps the loop seamless: half the doubled strip is
       still exactly the chapter, gap included. */
    margin-bottom: var(--oak-work-gap);
    border: var(--oak-mini-profile) solid transparent;
    border-image: url('/img/art/frame-wood.webp') 30% round;
    background: var(--black);
}

/* The print inside it: matt cotton paper, no glass, so it carries a shadow from the wood around it
   and nothing else. */
.oak-frame-artwork {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* A visitor who asked for less movement gets the frame with a work in it, hung and still. */
@media (prefers-reduced-motion: reduce) {
    .oak-frame-strip {
        animation: none;
    }
}

/* What the object stands on. It is the one thing that says the frame has been put down somewhere
   rather than pasted onto the page. */
.oak-frame-shadow {
    position: absolute;
    right: 0;
    bottom: 4%;
    left: 0;
    width: 62%;
    height: 4%;
    margin: 0 auto;
    border-radius: 50%;
    background: radial-gradient(50% 50% at 50% 50%, rgba(5, 5, 5, .8), transparent 70%);
    animation: oak-shadow-breathe 20s ease-in-out infinite;
    animation-play-state: paused;
    pointer-events: none;
}

.oak-frame-model.is-live .oak-frame-shadow {
    animation-play-state: running;
}

@keyframes oak-shadow-breathe {
    0%, 100% {
        opacity: .85;
        transform: scaleX(1);
    }

    50% {
        opacity: .58;
        transform: scaleX(.84);
    }
}

/* A visitor who asked for less movement gets the object, not the motion: it stands at the angle its
   opening pose sets, which is where the light would fall on a frame nobody is touching. */
@media (prefers-reduced-motion: reduce) {
    .oak-frame-turn,
    .oak-frame-shadow {
        animation: none;
    }

    .oak-frame-turn {
        --oak-turn: -17;
        transform: rotateY(-17deg) rotateX(3deg);
    }

    .oak-frame-tilt,
    .oak-frame-plate:hover .oak-frame-tilt {
        transform: none;
        transition: none;
    }
}

/* A line or two of small print directly under the tiles, at the full width of the grid — never a column of
   its own with an eyebrow over notes each opened by a gold rule, which gives three sentences about an
   option's price and a delivery time the height and the standing of a section. What they are is a
   disclaimer, so they sit where a disclaimer sits: under the thing it qualifies, one line each. */
.pricing-fineprint-wrap {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    grid-column: 1 / -1;
    width: 100%;
    margin-top: clamp(1rem, 2vh, 1.6rem);
}

/* Where a frame stands beside the price, the note belongs under the box it explains rather than under
   the whole section: full width it sat a column-gap — four per cent of the window — away from the
   offer it is the small print of, with the frame's own column beside it and nothing under that. */
.pricing-grid:has(.pricing-showcase) {
    row-gap: clamp(1.1rem, 1.8vw, 1.8rem);
}

    .pricing-grid:has(.pricing-showcase) .pricing-fineprint-wrap {
        grid-column: 1;
        margin-top: 0;
    }

.pricing-fineprint {
    max-width: 92ch;
    margin: 0;
    color: var(--text-muted);
    font-size: .78rem;
    font-weight: 300;
    line-height: 1.7;
}

    /* The product names carried inside these notes are the reason to read them at all. */
    .pricing-fineprint b {
        color: var(--white-soft);
        font-weight: 400;
    }

    .pricing-fineprint a {
        color: var(--white-soft);
        text-decoration: underline;
        text-underline-offset: 3px;
        transition: color var(--motion-quick) var(--ease-house);
    }

        .pricing-fineprint a:hover,
        .pricing-fineprint a:focus-visible {
            color: var(--gold);
        }

@media (max-width: 1000px) {
    .pricing-section .pricing-grid {
        grid-template-columns: 1fr;
    }

    .pricing-fineprint-wrap {
        max-width: none;
    }

    .pricing-section .inv-box,
    .pricing-section .b2b-pricing-box {
        text-align: center;
        align-items: center;
    }

        /* A column of items, each as wide as its own line, so a centred line keeps its dash beside it
           rather than at the card's left edge. The item stays a block, so a ™ inside it is untouched. */
        .pricing-section .inv-box .inv-list,
        .pricing-section .b2b-pricing-box .bp-features {
            display: flex;
            flex-direction: column;
            align-items: center;
            text-align: center;
        }

    /* The small print belongs to the price above it, not to the frame after it. */
    .pricing-grid:has(.pricing-showcase) .pricing-fineprint-wrap {
        order: 1;
    }

    .pricing-grid:has(.pricing-showcase) .pricing-showcase {
        order: 2;
    }

        /* A work coming down into the frame is already in the air above it — the reel begins a lead
           over the face, about a fifth of the plate's width above the plate itself. Beside the price
           that air is empty page; stacked under the small print it lay over the sentences and made
           them unreadable. The plate keeps that much room above it instead. A percentage margin is
           read off the column's width, and the plate is min(100%, 460px) of it. */
        .pricing-grid:has(.pricing-showcase) .pricing-showcase .oak-frame-plate {
            margin-top: calc(min(100%, 460px) * .26);
        }
}

.b2b-pricing-box {
    position: relative;
    background: linear-gradient(155deg, var(--tint), transparent 55%), var(--surface-card);
    border: 1px solid var(--hairline);
    padding: clamp(2.5rem, 5vw, 5rem);
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: border-color var(--motion) var(--ease-house), background-color var(--motion) var(--ease-house);
}

    /* The box does not rise to meet the hand; its hairline brightens, as site.css states for both price boxes. */
    .b2b-pricing-box:hover {
        border-color: var(--hairline-strong);
    }

.bp-features {
    list-style: none;
}

    /* The dash is placed, not laid out: as a flex row the line's own contents became flex items,
       so the <sup> carrying a ™ was taken out of the sentence and set beside it as a second column
       — „Odoardo Soul" on one line and the mark alone on the next. The same shape .inv-list uses,
       where the mark has always sat where it belongs. */
    .bp-features li {
        position: relative;
        padding-left: 32px;
        color: var(--text-muted);
        font-size: 0.85rem;
        font-weight: 200;
        line-height: 1.6;
        margin-bottom: 1.5vh;
    }

        .bp-features li::before {
            content: '';
            position: absolute;
            top: 0.8em;
            left: 0;
            width: 20px;
            height: 1px;
            background: var(--gold);
        }

        .bp-features li b {
            color: var(--white);
            font-weight: 500;
        }

.bp-price {
    font-family: var(--font-display);
    font-size: clamp(2rem, 8.5vw, 2.8rem);
    font-weight: 300;
    color: var(--white);
    display: block;
    line-height: 1;
    margin-bottom: 1vh;
}

    .bp-price small {
        font-size: 1.1rem;
        color: rgba(245, 245, 245, .5);
        font-family: var(--font-mono);
        letter-spacing: .1em;
        margin-left: .3em;
        text-transform: uppercase;
    }

.bp-sub {
    font-size: 0.85rem;
    font-weight: 200;
    color: var(--text-muted);
    margin-top: 1.5vh;
}

.inv-box {
    position: relative;
    background: linear-gradient(165deg, var(--tint), transparent 60%), var(--surface-card);
    border: 1px solid var(--hairline);
    padding: clamp(2.5rem, 5vw, 5rem);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    justify-content: center;
    transition: border-color var(--motion) var(--ease-house), background-color var(--motion) var(--ease-house);
}

    .inv-box:hover {
        border-color: var(--hairline-strong);
    }

.inv-label {
    font-family: var(--font-mono);
    font-weight: 300;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 5px;
    color: rgba(245, 245, 245, .5);
    display: block;
    margin: 2vh 0 2vh;
}

.inv-price {
    font-family: var(--font-display);
    font-size: clamp(2rem, 9vw, 3rem);
    font-weight: 300;
    color: var(--white);
    display: block;
    margin-bottom: 4vh;
    line-height: 1;
}

    .inv-price small {
        font-size: 1.15rem;
        color: rgba(245, 245, 245, .5);
        font-family: var(--font-mono);
        letter-spacing: .1em;
        margin-left: .3em;
        text-transform: uppercase;
    }

        .inv-price small:first-child {
            margin-left: 0;
        }

.inv-sub {
    font-size: 0.85rem;
    font-weight: 200;
    color: var(--text-muted);
    display: block;
    /* A touch more than the inherited body leading. This note can be three short lines — one term
       each on Lens — and small print at 200 weight set on tight leading reads as a block rather than
       as separate facts. It costs nothing where the note is a single line. */
    line-height: 1.7;
    margin-top: 1.5vh;
    margin-bottom: 4vh;
}

    /* A figure singled out inside the small print under a price. The note is deliberately muted — it
       is small print — and an amount left in that grey is one the reader's eye slides past, which is
       the last thing a charge should do: the series preparation is the largest single number on a
       short dedicated run, larger than the boxes themselves at the minimum. Full white and a heavier
       weight lift it out of the grey, so the emphasis reads as disclosure rather than as decoration.
       Stated for both price boxes, so a note under either says it the same way. */
    .inv-sub b,
    .inv-sub strong,
    .bp-sub b,
    .bp-sub strong {
        color: var(--white);
        font-weight: 500;
    }

.inv-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1.5vh;
    text-align: left;
}

    .inv-list li {
        font-size: 0.85rem;
        font-weight: 200;
        color: var(--text-muted);
        position: relative;
        padding-left: 32px;
    }

        .inv-list li::before {
            content: '';
            position: absolute;
            left: 0;
            top: 0.7em;
            width: 20px;
            height: 1px;
            background: var(--gold);
        }

        .inv-list li .tm {
            font-size: 0.6em;
            letter-spacing: 0;
            margin: 0;
            vertical-align: super;
        }
/* #endregion */

/* #region Responsive */
@media (max-width: 1000px) {
    .manifesto-grid {
        grid-template-columns: 1fr;
        gap: 4vh;
    }

    .manifesto-right {
        border-left: none;
        border-top: 1px solid var(--line);
        padding-left: 0;
        padding-top: 4vh;
    }

    .mechanic-section {
        padding-top: 0;
    }

    .mechanic-inner {
        grid-template-columns: 1fr;
        gap: 1rem;
        min-height: 0;
        padding: 2rem 0 3rem;
    }

    .mechanic-header {
        padding-top: 8vh;
        margin-bottom: 4vh;
    }

    .visual-sticky {
        position: relative;
        top: 0;
        height: auto;
        min-height: 32rem;
        margin-bottom: 1rem;
    }

    .text-scroll {
        height: auto;
        min-height: 42rem;
        padding: 0;
    }

    .step-block {
        text-align: left;
    }

    .step-desc {
        margin: 0;
    }

    /* Two columns on tablets, one on phones — three cards straight into one long column
       wasted the whole 640–1000px band. */
    .testi-grid, .b2b-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 6vh 4vw;
    }

    /* Three testimonials two by two leave one alone in half a row; the last takes the whole row. */
    .testi-card:last-child:nth-child(odd) {
        grid-column: 1 / -1;
    }

    @media (max-width: 640px) {
        .testi-grid, .b2b-grid {
            grid-template-columns: 1fr;
            gap: 8vh;
        }
    }

    .pricing-grid {
        grid-template-columns: 1fr;
    }

    .b2b-pricing-box {
        flex-direction: column;
        text-align: center;
        gap: 6vh;
    }

    .inv-list {
        align-items: center;
        text-align: center;
    }
}

@media (max-width: 800px) {
    .filmable-callout-inner {
        grid-template-columns: 1fr;
        gap: 2.5rem;
    }

    .filmable-callout-aside {
        border-left: none;
        border-top: 1px solid var(--hairline-mid);
        padding-left: 0;
        padding-top: 1.5rem;
    }
}


@media (max-width: 600px) {
    /* The token, not a frozen 3rem: the hard value cut the hero title's fluid scale off at
       600px with a 24px jump at the breakpoint. */
    .h-title {
        font-size: var(--hero-title-size);
    }

    .b2b-pricing-box {
        padding: 5vh 5vw;
    }

    .terminal-input {
        font-size: 1.2rem;
        text-align: center;
    }

    .canvas-area {
        min-height: 200px;
        height: auto;
    }

    .artifact-wrapper {
        transform: scale(0.8);
    }
}
/* #endregion */

/* #region Product configurator */
.cfg-axis-title {
    display: block;
    margin-bottom: 1.2rem;
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .28em;
    text-transform: uppercase;
    color: rgba(245, 245, 245, .5);
}

.cfg-choices {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
}

.cfg-choice {
    display: flex;
    flex-direction: column;
    gap: .45rem;
    min-width: 200px;
    padding: 1.3rem 1.6rem;
    background: transparent;
    border: 1px solid var(--line);
    color: var(--white);
    text-align: left;
    cursor: pointer;
    transition: border-color .35s var(--ease-house), background .35s var(--ease-house);
}

    .cfg-choice:hover {
        border-color: rgba(var(--gold-bright-rgb), .45);
    }

    .cfg-choice.selected {
        border-color: rgba(var(--gold-rgb),.78);
        background: linear-gradient(145deg, rgba(var(--gold-rgb),.1), rgba(var(--gold-rgb),.025));
    }

.cfg-choice-name {
    font-size: 1rem;
    font-weight: 500;
    letter-spacing: .02em;
}

.cfg-choice-price {
    font-size: .72rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--grey);
}

.cfg-choice.selected .cfg-choice-price {
    color: var(--gold);
}

.cfg-qty-control {
    display: inline-flex;
    align-items: center;
    gap: 0;
    border: 1px solid var(--line);
}

.cfg-qty-btn {
    width: 52px;
    height: 52px;
    background: transparent;
    border: 0;
    color: var(--white);
    font-size: 1.1rem;
    cursor: pointer;
    transition: color .3s var(--ease-house);
}

    .cfg-qty-btn:hover {
        color: var(--gold);
    }

/* The figure between the two buttons, which is a field where a count can be typed. It carries no box
   of its own: a stepper with a bordered input inside it reads as a form wedged between two controls,
   and the whole point is that the number looks exactly as it always did until somebody clicks it.
   The browser's own field styling is what has to be taken off — a background, a border, an inherited
   system font — and the caret and the focus ring are what say it can be written in. */
.cfg-qty-value {
    min-width: 64px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    color: inherit;
    font-family: inherit;
    font-size: 1rem;
    letter-spacing: .05em;
    text-align: center;
}

    .cfg-qty-value:focus-visible {
        outline: 1px solid var(--gold);
        outline-offset: 4px;
    }

    /* Nothing in a stepper is ever typed in another alphabet, and the count is never negative. */
    .cfg-qty-value::-webkit-outer-spin-button,
    .cfg-qty-value::-webkit-inner-spin-button {
        appearance: none;
        margin: 0;
    }

.cfg-included-list {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

    .cfg-included-list li {
        position: relative;
        padding-left: 1.4rem;
        font-size: .86rem;
        color: var(--grey);
        letter-spacing: .02em;
        text-transform: none;
    }

        .cfg-included-list li::before {
            content: "";
            position: absolute;
            left: 0;
            top: .55em;
            width: 14px;
            height: 1px;
            background: var(--gold);
        }

.configurator-summary {
    position: sticky;
    top: 12vh;
}

.cfg-summary-card {
    padding: 2.6rem 2.4rem;
    border: 1px solid var(--line);
    background: rgba(245, 245, 245, .015);
}

.cfg-summary-product {
    display: block;
    margin-bottom: 1.6rem;
    font-size: .68rem;
    font-weight: 600;
    letter-spacing: .28em;
    text-transform: uppercase;
    color: rgba(245, 245, 245, .5);
}

.cfg-summary-price {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    margin-bottom: 1.4rem;
}

    .cfg-summary-price > span {
        display: grid;
        gap: .35rem;
    }

        .cfg-summary-price > span > small {
            color: var(--text-muted);
            font-size: .62rem;
            letter-spacing: .12em;
            line-height: 1.5;
            text-transform: uppercase;
            /* The label is tracked, so a ™ inside one has to be told how much to give back. */
            --tm-tracking: .12em;
        }

            /* One word of a price label may be lifted, and only where a label asks for it — the
               emphasis is written in the copy, so a label with no bold in it is untouched. It is for
               the word that says HOW OFTEN: the amount underneath is the same figure whether it is
               charged once or every month, and „miesięczny" is the part a buyer must not misread.
               The weight is inherited: full white already separates it from the grey, and this is a caption. */
            .cfg-summary-price > span > small b {
                color: var(--white);
                font-weight: inherit;
            }

.cfg-price-onetime {
    font-family: var(--font-serif, inherit);
    font-size: clamp(1.9rem, 3vw, 2.6rem);
    font-weight: 300;
    letter-spacing: .02em;
}

.cfg-price-recurring {
    font-size: 1rem;
    color: var(--grey);
    letter-spacing: .04em;
}

    .cfg-price-recurring.hidden {
        display: none;
    }

.cfg-summary-note {
    margin-bottom: 2.2rem;
    font-size: .78rem;
    line-height: 1.7;
    color: var(--grey);
}

/* The house's primary action, wherever it stands: the configurator's rail, the cart drawer and
   the checkout's own submit all wear this class, and they have to be the same button. The family is
   named rather than inherited — a <button> takes the user agent's face unless it is told otherwise,
   which is what made the same label read in a different weight on the checkout than in the wizard.
   The tracking matches the rail's: .28em pulled a long Polish label apart into two ragged lines.
   Drawn, never filled — the house button of --button-layers. */
.cfg-add-btn {
    width: 100%;
    padding: 1.15rem 1.5rem;
    background: var(--button-layers);
    border: 1px solid var(--button-border);
    color: var(--white);
    font-family: var(--font-sans);
    font-size: .72rem;
    font-weight: 400;
    letter-spacing: .18em;
    line-height: 1.5;
    text-transform: uppercase;
    cursor: pointer;
    transition: background-size var(--motion) var(--ease-house), border-color var(--motion) var(--ease-house), transform .2s var(--ease-house);
}

    .cfg-add-btn:hover:not(:disabled),
    .cfg-add-btn:focus-visible:not(:disabled) {
        background-size: 100% 1px, 100% 100%;
        border-color: var(--button-border-hover);
    }

    .cfg-add-btn:active:not(:disabled) {
        transform: translateY(1px);
    }

    .cfg-add-btn:disabled {
        opacity: .45;
        cursor: default;
    }

.cfg-summary-craft {
    margin-top: 1.4rem;
    font-size: .68rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    text-align: center;
    color: var(--grey);
}

@media (max-width: 900px) {
    .configurator-summary {
        position: static;
    }

    .cfg-choice {
        min-width: 100%;
    }
}
/* #endregion */

/* #region Ecosystem pairing */
.ecosystem-pairing {
    position: relative;
    overflow: hidden;
    padding: var(--section-space) 0;
    border-bottom: 1px solid var(--section-divider);
    background: linear-gradient(145deg, var(--tint), transparent 42%), var(--surface-editorial);
}

    .ecosystem-pairing::before {
        position: absolute;
        top: 50%;
        right: -12rem;
        width: 30rem;
        aspect-ratio: 1;
        border: 1px solid var(--hairline-faint);
        border-radius: 50%;
        content: "";
        transform: translateY(-50%);
        pointer-events: none;
    }

.ep-grid {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(280px, .9fr);
    gap: clamp(3rem, 8vw, 8rem);
    align-items: center;
    padding-top: 0;
    border-top: 0;
}

.ep-copy h2 {
    margin: 0 0 3.5vh;
    color: var(--white);
    font-family: var(--font-display);
    font-size: var(--section-title-size);
    font-weight: 300;
    letter-spacing: -.03em;
    line-height: var(--section-title-line-height);
}

    .ep-copy h2 i {
        color: var(--gold);
        font-weight: 300;
    }

.ep-reason {
    max-width: 560px;
    margin-bottom: clamp(2rem, 4vh, 3rem);
    color: var(--white-soft);
    font-size: clamp(1rem, 1.05vw, 1.05rem);
    font-weight: 300;
    line-height: 1.75;
}

    .ep-reason b {
        color: var(--white);
        font-weight: 400;
    }

/* No `gap` here, and the reason is the ™. The label carries it as its own <sup class="tm">, which
   in a flex row is a flex item like any other — so a gap meant for the arrow was applied on both
   sides of the trademark too, and „Poznaj Odoardo Art™ →" read as three separated words. The arrow
   carries its own margin instead, and the ™ closes on the name the way it does everywhere else. */
.ep-cta {
    display: inline-flex;
    align-items: center;
    padding-top: 12px;
    margin-top: -12px;
    padding-bottom: .8rem;
    border-bottom: 1px solid var(--hairline-strong);
    color: var(--white);
    font-size: clamp(.65rem, .8vw, .78rem);
    letter-spacing: .22em;
    text-transform: uppercase;
    text-decoration: none;
    transition: color var(--motion) var(--ease-house), border-color var(--motion) var(--ease-house);
}

    /* A white hairline at rest; under the hand the line and the arrow take the gold. */
    .ep-cta:hover,
    .ep-cta:focus-visible {
        border-bottom-color: var(--gold);
        color: var(--white);
    }

    /* The one thing that really is a separate element in this row. */
    .ep-cta > span {
        display: inline-block;
        margin-left: .8rem;
        transition: transform var(--motion) var(--ease-house) .06s, color var(--motion) var(--ease-house);
    }

        .ep-cta:hover > span,
        .ep-cta:focus-visible > span {
            transform: translateX(4px);
            color: var(--gold);
        }

.ep-visual {
    position: relative;
    display: block;
    overflow: hidden;
    text-decoration: none;
}

    /* No border on the picture. It carries the house's closed passe-partout — .ep-visual::after is on
       that rule's selector list in product-sections.css — and a hairline on the image as well put two
       lines a few pixels apart around the same photograph. */
    .ep-visual img {
        display: block;
        width: 100%;
        height: auto;
        filter: var(--photo-at-rest);
        transition: transform 1.8s var(--ease-house), filter var(--motion-slow) var(--ease-house);
    }

    .ep-visual:hover img,
    .ep-visual:focus-visible img,
    .ep-visual-link:hover .ep-visual img,
    .ep-visual-link:focus-visible .ep-visual img {
        transform: scale(1.025);
        filter: var(--photo-lifted);
    }

.ep-visual-caption {
    display: flex;
    flex-direction: column;
    gap: .3rem;
    padding-top: 1.1rem;
}

    .ep-visual-caption strong {
        color: var(--white);
        font-family: var(--font-display);
        font-size: 1.05rem;
        font-weight: 400;
    }

    .ep-visual-caption span {
        color: rgba(245, 245, 245, .5);
        font-size: .72rem;
        letter-spacing: .18em;
        text-transform: uppercase;
        transition: color var(--motion) var(--ease-house);
    }

    .ep-visual-link:hover .ep-visual-caption span,
    .ep-visual-link:focus-visible .ep-visual-caption span {
        color: var(--gold);
    }

/* A picture with a caption under it: the link carries both, the framed .ep-visual only the picture,
   and the whole link answers the hand as one. */
.ep-visual-link {
    display: block;
    text-decoration: none;
}

@media (max-width: 900px) {
    .ep-grid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 600px) {
    .b2b-card,
    .strat-card {
        min-height: 0;
    }

    .b2b-index {
        padding-bottom: 3.5rem;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ep-cta > span,
    .ep-visual img,
    .b2b-img,
    .ecosystem-sense-image,
    .filmable-callout-cta > span:last-child {
        transition: none;
        transform: none;
    }

    .ep-visual:hover img,
    .ep-visual:focus-visible img,
    .b2b-card:hover .b2b-img,
    .ecosystem-sense:hover .ecosystem-sense-image,
    .ecosystem-sense:focus-visible .ecosystem-sense-image,
    .ep-cta:hover > span,
    .ep-cta:focus-visible > span,
    .filmable-callout-inner:hover .filmable-callout-cta > span:last-child,
    .filmable-callout-inner:focus-visible .filmable-callout-cta > span:last-child {
        transform: none;
    }
}
/* #endregion */

/* #region Configurator */
.cfg-next-title {
    margin-top: 1.6rem;
}

/* Two classes left this region and neither is coming back. .cfg-next-list drew the numbered steps:
   every rail now says what happens after the order in the same plain lines it says what the price
   covers — .cfg-included-list — because one column holding two kinds of list made the reader decide
   which of them mattered more. .cfg-excluded-list drew the „poza abonamentem" bullets, and no rail
   carries one any longer: a column that opens with what a customer gets does not close by arguing
   against the offer. */
/* #endregion */

/* #region Testimonial disclosure */
.testi-disclosure {
    margin: 1.4rem 0 0;
    font-size: .7rem;
    line-height: 1.6;
    color: var(--text-muted);
}
/* #endregion */

/* #region Atelier door */
/* The quiet link under a CTA. */
.pricing-box-link {
    display: inline-flex;
    gap: .5rem;
    align-items: center;
    margin-top: 1rem;
    color: var(--white-soft);
    font-family: var(--font-sans);
    font-size: .68rem;
    font-weight: 400;
    letter-spacing: .18em;
    text-decoration: none;
    text-transform: uppercase;
    transition: color var(--motion-quick) var(--ease-house);
}

    .pricing-box-link:hover,
    .pricing-box-link:focus-visible {
        color: var(--gold);
    }

/* No flex fallbacks: the grid collapses to one column on its own through the minmax above. */
/* #endregion */

/* #region Pricing doors */

/* The right column of a pricing section whose job is to start a composition. Not a place for the
   Atelier note or the house terms: prose beside a price, where a visitor is deciding whether to
   configure anything, answers nothing they are asking. Three forms with three amounts answer the
   question that actually stands there („what would I even be buying?") and each one opens the wizard
   already holding it. */
.pricing-doors {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: clamp(1rem, 2vh, 1.4rem);
    min-width: 0;
}

    .pricing-doors .inv-label {
        margin: 0 0 .4vh;
        text-align: left;
    }

.pricing-door-list {
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: clamp(.8rem, 1.6vh, 1.1rem);
    margin: 0;
    padding: 0;
}

/* A whole card as one button: the same border as .inv-box, so the doors read as members of the same
   family rather than as controls borrowed from a form. It does not lift; its edge takes the gold. */
.pricing-door {
    position: relative;
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    align-items: center;
    gap: .3rem 1.2rem;
    width: 100%;
    padding: clamp(1.1rem, 2.2vw, 1.6rem) clamp(1.2rem, 2.4vw, 1.8rem);
    background: linear-gradient(165deg, var(--tint), transparent 60%), var(--surface-card);
    border: 1px solid var(--hairline);
    color: var(--white);
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color var(--motion) var(--ease-house), background-color var(--motion) var(--ease-house);
}

    .pricing-door:hover,
    .pricing-door:focus-visible {
        border-color: var(--gold);
    }

/* Square, like every other image in the house. A form is a thing you recognise before you read its
   name, and 5 cm beside 10 cm is a comparison words cannot make. */
.pricing-door-image {
    grid-column: 1;
    grid-row: 1 / span 2;
    width: clamp(56px, 9vw, 84px);
    height: clamp(56px, 9vw, 84px);
    object-fit: cover;
    border: 1px solid var(--hairline);
    background: var(--surface-card);
}

.pricing-door-name {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    font-family: var(--font-display);
    font-size: clamp(1.05rem, 2.2vw, 1.3rem);
    font-weight: 300;
    line-height: 1.2;
}

.pricing-door-format {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
    color: var(--text-muted);
    font-size: .8rem;
    font-weight: 200;
}

.pricing-door-price {
    grid-column: 3;
    grid-row: 1 / span 2;
    align-self: center;
    color: var(--gold);
    font-family: var(--font-sans);
    font-size: .85rem;
    font-weight: 300;
    white-space: nowrap;
}

/* At 320px the photograph, the name, the price and the arrow need more than the card holds, and the
   price cannot wrap — the gaps and the photograph give instead. */
@media (max-width: 420px) {
    .pricing-door {
        column-gap: .75rem;
    }

    .pricing-door-image {
        width: 48px;
        height: 48px;
    }
}

.pricing-door-arrow {
    grid-column: 4;
    grid-row: 1 / span 2;
    align-self: center;
    color: var(--white);
    font-size: 1rem;
    opacity: .55;
    transition: opacity var(--motion) var(--ease-house), transform var(--motion) var(--ease-house) .06s, color var(--motion) var(--ease-house);
}

.pricing-door:hover .pricing-door-arrow,
.pricing-door:focus-visible .pricing-door-arrow {
    color: var(--gold);
    opacity: 1;
    transform: translateX(4px);
}

/* One line, not a paragraph: what the wizard asks next, so the click feels like a step rather than
   a commitment. */
.pricing-door-note {
    margin: 0;
    color: var(--text-muted);
    font-size: .8rem;
    font-weight: 200;
    line-height: 1.7;
}

@media (max-width: 900px) {
    .pricing-doors {
        justify-content: flex-start;
    }
}

/* The Atelier band: the section's last word, on its own ground, in the idiom of .letters-band —
   full bleed, flat, a hairline above and below. Flat is deliberate and not an omission: the wash
   that once lit the letters band made it the only lifted surface on a near-black page, and the same
   wash here would do the same. What says "rare" is the room around the line, the display face and
   the two rules, not a filled gold bar. */
.pricing-atelier-band {
    position: relative;
    margin-top: clamp(3rem, 7vh, 5.5rem);
    padding: clamp(2.2rem, 4.5vh, 3.4rem) 0;
    border-top: 1px solid var(--hairline-mid);
    border-bottom: 1px solid var(--hairline-mid);
    background: var(--black-soft);
}

/* The band ends the section, so it ends it flush — otherwise the section's own bottom padding left
   its ground stopping in mid-air, with the next boundary six to nine rem further down. */
.pricing-section.has-atelier-band {
    padding-bottom: 0;
}

/* And the concierge below it gives up its own top boundary, because the band already drew one.
   Two sections each bringing a rule and a --section-space of padding to the same seam is what put
   two lines there, and then — once the section's padding was gone — twice the gap with a single
   line in it. One rule, one gap: the band closes the section, the concierge opens with its own
   spacing and nothing else. General sibling, not adjacent, for the reason the rule above it
   already records: the hidden configurator modal stands between the two in the DOM. */
.pricing-section.has-atelier-band ~ .core-interface.home-concierge {
    border-top: 0;
}

/* The house's measure: .container alone carries no side gutter. */
.pricing-atelier-band-inner {
    width: min(90vw, var(--section-content-max));
    max-width: none;
    margin-inline: auto;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: clamp(1.6rem, 4vw, 4rem);
    align-items: center;
}

.pricing-atelier-band-name {
    display: block;
    margin-bottom: .9rem;
    color: rgba(245, 245, 245, .5);
    font-family: var(--font-sans);
    font-size: .66rem;
    font-weight: 400;
    letter-spacing: .22em;
    text-transform: uppercase;
}

/* Two lines, and the difference between them is the point. The question is what stops a reader, so
   it carries the display face — the same face the house uses for editorial emphasis everywhere
   else, and what separates a programme from a footnote. The answer follows a step quieter, because
   a reader who is still reading has already asked it. */
.pricing-atelier-band-title {
    max-width: 54ch;
    margin: 0 0 .6rem;
    color: var(--white);
    font-family: var(--font-display);
    font-size: clamp(1.15rem, 2.7vw, 1.55rem);
    font-weight: 300;
    line-height: 1.4;
}

.pricing-atelier-band-line {
    max-width: 58ch;
    margin: 0;
    color: var(--text-muted);
    font-size: .92rem;
    font-weight: 200;
    line-height: 1.7;
}

.pricing-atelier-band-cta {
    display: inline-flex;
    gap: .6rem;
    align-items: center;
    padding: clamp(.9rem, 1.8vh, 1.15rem) clamp(1.6rem, 3vw, 2.4rem);
    border: 1px solid var(--hairline-strong);
    color: var(--white);
    font-family: var(--font-sans);
    font-size: .7rem;
    font-weight: 400;
    letter-spacing: .18em;
    text-decoration: none;
    text-transform: uppercase;
    white-space: nowrap;
    transition: border-color var(--motion) var(--ease-house), color var(--motion) var(--ease-house), background-color var(--motion) var(--ease-house);
}

    .pricing-atelier-band-cta:hover,
    .pricing-atelier-band-cta:focus-visible {
        border-color: var(--gold);
        background: var(--gold-subtle);
        color: var(--gold);
    }

@media (max-width: 760px) {
    .pricing-atelier-band-inner {
        grid-template-columns: 1fr;
        gap: 1.6rem;
    }

    .pricing-atelier-band-cta {
        justify-content: center;
    }
}

/* #endregion */
