﻿/* gemelo OPF website — single stylesheet, no framework.
   Colour system is built from the three gemelo HKS values; everything else is derived from them. */

/* ------------------------------------------------------------------ Tokens */

:root {
    /* Brand, as handed over: HKS 93K, HKS 8K, HKS 16K */
    --brand-grey: #444f50;
    --brand-orange: #ee7203;
    --brand-red: #b61f29;
    --logo-red: #9f2030;

    /* Surfaces, derived from the brand grey */
    --surface-deep: #141a1b;
    --surface: #1b2223;
    --surface-raised: #232c2d;
    --surface-light: #f4f6f7;

    --text: #edf1f1;
    --text-dim: #a4b0b1;
    --text-faint: #8a9698;
    --text-on-light: #2b3334;

    --line: rgba(237, 241, 241, 0.12);
    --line-strong: rgba(237, 241, 241, 0.24);

    --accent: var(--brand-orange);
    --accent-soft: rgba(238, 114, 3, 0.16);
    --accent-line: rgba(238, 114, 3, 0.45);

    --font-sans: "Segoe UI Variable Display", "Segoe UI", system-ui, -apple-system,
                 "Helvetica Neue", Arial, sans-serif;

    --page-width: 1180px;
    --stage-height: min(70svh, 664px);
    --gutter: clamp(20px, 5vw, 64px);
    --section-gap: clamp(88px, 13vh, 168px);
    --radius: 14px;
    --radius-small: 8px;

    --ease-out: cubic-bezier(0.22, 0.75, 0.3, 1);
}

/* ------------------------------------------------------------------ Reset */

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

html {
    scroll-behavior: smooth;
    scroll-padding-top: 96px;
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: clamp(16px, 0.5vw + 15px, 18px);
    line-height: 1.6;
    overflow-x: hidden;
}

h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; text-decoration: none; }
em { font-style: normal; color: var(--accent); }
svg { display: block; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.skip-link {
    position: absolute;
    top: -100px;
    left: var(--gutter);
    z-index: 100;
    padding: 12px 18px;
    border-radius: 0 0 var(--radius-small) var(--radius-small);
    background: var(--accent);
    color: #1b1206;
    font-weight: 600;
}

.skip-link:focus { top: 0; }

/* The scrollbar goes — but only where something takes over its job. It says two things: how far
   in you are and how much is left, and on a page this long that is worth keeping. The thin line
   at the top edge says the first of them; it exists only with scripting, which is also the only
   case in which the bar is hidden. */
.js {
    scrollbar-width: none;
}

.js::-webkit-scrollbar { display: none; }

.progress {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 70;
    width: 100%;
    height: 2px;
    background: linear-gradient(90deg, var(--brand-red), var(--accent));
    transform: scaleX(var(--progress, 0));
    transform-origin: 0 50%;
    pointer-events: none;
}

html:not(.js) .progress { display: none; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

/* ------------------------------------------------------------------ Shared pieces */

.eyebrow {
    margin-bottom: 14px;
    color: var(--accent);
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

.section-head {
    max-width: var(--page-width);
    margin: 0 auto clamp(40px, 6vh, 72px);
    padding: 0 var(--gutter);
}

.section-head h2 {
    font-size: clamp(2rem, 4.4vw, 3.4rem);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.02em;
}

.section-lead {
    max-width: 62ch;
    margin-top: 18px;
    color: var(--text-dim);
    font-size: clamp(1rem, 1.3vw, 1.2rem);
}

.pill {
    display: inline-block;
    margin-right: 10px;
    padding: 3px 12px;
    border: 1px solid var(--accent-line);
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    vertical-align: 2px;
}

/* Smaller than a superscript would be on its own, and lifted by the difference so the
   digit keeps sitting at the same height. */
.footnote-ref a {
    position: relative;
    top: -0.5em;
    display: inline-block;
    color: var(--accent);
    font-size: 0.42em;
    font-weight: 600;
    vertical-align: super;
}

.button {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 14px 26px;
    border: 1px solid transparent;
    border-radius: 999px;
    font-size: 0.98rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    transition: background-color 0.2s var(--ease-out), border-color 0.2s var(--ease-out),
                transform 0.2s var(--ease-out), color 0.2s var(--ease-out);
}

.button:hover { transform: translateY(-2px); }
.button-icon { width: 20px; height: 20px; }

.button-primary {
    background: var(--accent);
    color: #1b1206;
}

.button-primary:hover { background: #ff8317; }

.button-ghost {
    border-color: var(--line-strong);
    color: var(--text);
}

.button-ghost:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.button-small { padding: 9px 18px; font-size: 0.88rem; }

.button-block {
    justify-content: center;
    width: 100%;
    margin-top: auto;
}

/* ------------------------------------------------------------------ Header */

.site-header {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: clamp(16px, 4vw, 48px);
    width: 100%;
    padding: 18px var(--gutter);
    border-bottom: 1px solid transparent;
    transition: background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out),
                padding 0.3s var(--ease-out);
}

.site-header.is-stuck {
    padding: 11px var(--gutter);
    border-bottom-color: var(--line);
    background: rgba(20, 26, 27, 0.82);
    backdrop-filter: blur(14px);
}

.brand { margin-right: auto; }

.brand-logo {
    width: 148px;
    height: 27px;
    color: var(--text);
    transition: color 0.3s var(--ease-out);
}

.logo-dot { fill: var(--brand-red); }

.site-nav {
    display: flex;
    gap: clamp(16px, 2.2vw, 34px);
    font-size: 0.95rem;
    color: var(--text-dim);
}

.site-nav a { transition: color 0.2s var(--ease-out); }
.site-nav a:hover { color: var(--accent); }

@media (max-width: 760px) {
    .site-nav { display: none; }
}

/* ------------------------------------------------------------------ Hero */

.hero {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100svh;
    padding: 140px var(--gutter) 80px;
    overflow: hidden;
    text-align: center;
}

.hero-glow {
    position: absolute;
    top: -30%;
    left: 50%;
    width: min(1300px, 130vw);
    aspect-ratio: 1;
    transform: translateX(-50%);
    background: radial-gradient(circle at 50% 50%,
                rgba(238, 114, 3, 0.26) 0%,
                rgba(238, 114, 3, 0.15) 20%,
                rgba(182, 31, 41, 0.15) 40%,
                transparent 70%);
    pointer-events: none;
}

.hero-inner {
    position: relative;
    max-width: 960px;
}

.hero .eyebrow {
    margin-bottom: clamp(24px, 3.4vh, 40px);
    font-size: clamp(0.92rem, 1.15vw, 1.08rem);
    letter-spacing: 0.2em;
}

.hero-title {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(14px, 2.4vw, 26px);
    font-size: clamp(2.2rem, 6.6vw, 5.1rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.03em;
}

/* The logo travels from here into the header when the page is scrolled, so it is taken out of
   the flow and positioned by the script. The slot keeps its place in the layout, and the header's
   own logo stays invisible — it is only the measuring point for where the journey ends. */
.hero-logo-slot {
    display: block;
    width: min(720px, 88vw);
    aspect-ratio: 691.9 / 127.1;
}

.hero-logo {
    width: min(720px, 88vw);
    height: auto;
    aspect-ratio: 691.9 / 127.1;
    color: var(--text);
}

.js .hero-logo {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 60;
    transform-origin: 0 0;
    will-change: transform;
}

.js .brand-logo { opacity: 0; }

.motion-off .hero-logo { position: static; }
.motion-off .brand-logo { opacity: 1; }

.hero-claim { display: block; }

.hero-lead {
    max-width: 58ch;
    margin: clamp(24px, 3.5vh, 38px) auto 0;
    color: var(--text-dim);
    font-size: clamp(1.05rem, 1.6vw, 1.32rem);
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    justify-content: center;
    margin-top: clamp(28px, 4vh, 44px);
}

.scroll-hint {
    position: absolute;
    bottom: 28px;
    left: 50%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    transform: translateX(-50%);
    color: var(--text-dim);
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: 0.28em;
    text-transform: uppercase;
}

.scroll-hint-line {
    width: 2px;
    height: 62px;
    border-radius: 2px;
    background: linear-gradient(to bottom, transparent, var(--accent));
    animation: scroll-hint 2.4s var(--ease-out) infinite;
    transform-origin: top;
}

@keyframes scroll-hint {
    0%   { transform: scaleY(0); opacity: 0; }
    35%  { transform: scaleY(1); opacity: 1; }
    100% { transform: scaleY(1) translateY(62px); opacity: 0; }
}

/* ------------------------------------------------------------------ Statement */

.statement {
    max-width: 1000px;
    margin: 0 auto;
    padding: var(--section-gap) var(--gutter);
    text-align: center;
}

.statement-line {
    font-size: clamp(1.45rem, 3.6vw, 2.75rem);
    font-weight: 600;
    line-height: 1.22;
    letter-spacing: -0.02em;
    transition: opacity 0.75s var(--ease-out), transform 0.75s var(--ease-out);
}

/* Hidden starting states only apply once scripting is known to be there. */
.js .statement-line { opacity: 0; transform: translateY(var(--enter, 78px)); }

/* The answer stays at the top edge as a headline over the stage. This is real stickiness,
   inside a wrapper that holds both the statements and the stage — a hold computed per frame and
   written as a transform always lags the scroll by a frame, and that lag is visible as a wobble.
   The browser does this one on the compositor, and it does not wobble. */
.act { position: relative; }

.js .statement-answer {
    position: sticky;
    top: 130px;
    z-index: 40;
    max-width: 1000px;
    margin: 0 auto;
    padding: 0 var(--gutter);
    opacity: 1;
    text-align: center;
    transform: none;
    transition: none;
}

.js .statement-answer > strong {
    display: inline-block;
    opacity: calc(1 - var(--leave, 0));
    transform: translateY(var(--enter, 78px));
    transition: opacity 0.75s var(--ease-out), transform 0.9s var(--ease-out);
}

.statement-line + .statement-line { margin-top: clamp(28px, 5vh, 56px); }

.motion-off .statement-answer { position: static; transform: none; opacity: 1; }
.motion-off .statement-answer > strong { opacity: 1; transform: none; }
.statement-line.is-in { opacity: 1; transform: none; }
.js .statement-line.is-in { --enter: 0px; transform: translateY(var(--enter)); }
.js .statement-answer.is-in { transform: none; }
.js .statement-answer.is-in > strong { --enter: 0px; }

.statement-problem { color: #d8535c; }
.statement-answer { color: var(--accent); font-size: clamp(1.8rem, 5vw, 3.6rem); }

/* ------------------------------------------------------------------ Scene: one screen becomes six */

.scene {
    position: relative;
    /* Scroll length of the whole choreography; the stage itself stays put. Long enough that the
       arrived picture can stand still for a good while before it dims. */
    height: 480vh;
    /* A jump to #platforms should land on the stage already stuck to the top edge, not a
       screen-height before it — hence the negative margin. */
    scroll-margin-top: -170px;
}

.scene-sticky {
    position: sticky;
    top: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100svh;
    padding: 90px var(--gutter) 40px;
    overflow: hidden;
}

.scene-stage {
    position: relative;
    width: min(var(--page-width), 100%);
    height: var(--stage-height);
}

/* Desk: the photo that stands at the start; its screen area is covered by the flying artwork. */

/* Without scripting the desk stays out of the way and the six tiles stand on their own. */
.desk {
    position: absolute;
    inset: 0;
    display: none;
}

.js .desk { display: grid; }

/* The opening photo sits in a frame of its own, in the proportion of the picture, so that it
   fills the frame completely — no letterboxing, and no crop that eats the monitor. The frame
   leaves the upper part of the stage to the headline. */
.desk {
    place-items: end center;
    padding-bottom: 2%;
}

.photo-slot.desk-photo {
    position: relative;
    width: min(100%, calc(var(--stage-height) * 0.67 * 1.969));
    height: auto;
    aspect-ratio: 2000 / 1016;
    border-radius: var(--radius);
    background-size: cover;
    background-position: center;
    opacity: var(--desk-photo, 1);
}

/* Tiles: the six target devices. They do not move — the screens travel, the photos rise. */

.tiles {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(2, 1fr);
    column-gap: clamp(14px, 2.2vw, 34px);
    row-gap: clamp(42px, 5.5vh, 68px);
}

.tile {
    position: relative;
    border-radius: var(--radius-small);
}

/* Rises by opacity alone. It must not be scaled: the screens are measured against this box,
   and a box that is still growing hands out target corners that are too small. */
.tile-photo {
    position: absolute;
    inset: 0;
    border-radius: var(--radius);
    opacity: var(--photo, 1);
}

/* No parallax here, and that is a decision, not an omission. The screen artwork has to stay
   inside the screen area of its photo; anything that moves one of them without moving the photo,
   the label and the opening shot by exactly the same amount pulls the picture apart. That is a
   lot of things to keep in step for a few pixels of depth — so nothing moves. */
.js .tile-photo {
    opacity: calc(var(--photo, 0) * (1 - 0.85 * var(--fade, 0)));
}

/* The travelling artwork. Each one is a 640x400 rectangle that a homography lays onto the four
   corners of whatever device it currently belongs to, so it keeps that device's perspective. */

.screens {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* At the end photos and screens dim together, down to a sixth, and nothing moves: a screen
   that sinks away from the photo it lies in comes apart from it. What grows instead are the
   platform names. */
.js .screens {
    opacity: calc(1 - 0.85 * var(--fade, 0));
}
html:not(.js) .screens { display: none; }

.screen {
    position: absolute;
    top: 0;
    left: 0;
    width: 640px;
    height: 400px;
    transform-origin: 0 0;
    filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.5));
    will-change: transform;
}

/* Straddles the bottom edge of the photo: half on the picture, half below it. */
.tile-mark {
    position: absolute;
    left: 50%;
    bottom: 0;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 18px 9px 14px;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: rgba(20, 26, 27, 0.78);
    backdrop-filter: blur(8px);
    color: var(--text);
    font-size: clamp(0.78rem, 1.1vw, 0.98rem);
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
    transform: translate(-50%, 50%);
}

.js .tile-mark {
    /* Straddling the lower edge of the photo, half on it and half below — and at the end it
       travels to the middle of the cell. */
    bottom: calc(50% * var(--fade, 0));
    opacity: var(--mark, 0);
    transform: translate(-50%, calc(50% + 14px * (1 - var(--mark, 0))))
               scale(calc((0.9 + 0.1 * var(--mark, 0)) * (1 + 0.5 * var(--fade, 0))));
}

.tile-icon {
    width: 22px;
    height: 22px;
    color: var(--accent);
}

.scene-caption {
    margin-top: clamp(20px, 3.5vh, 44px);
    font-size: clamp(1.05rem, 2vw, 1.5rem);
    text-align: center;
}

.js .scene-caption {
    opacity: var(--caption, 0);
    transform: translateY(calc(16px * (1 - var(--caption, 0))));
}

.scene-caption-strong { font-weight: 700; color: var(--accent); }
.scene-caption-rest { color: var(--text-dim); }

/* Photo placeholders. Replace the background with the real picture per data-photo value. */

.photo-slot {
    background-color: var(--surface-raised);
    background-image: var(--photo-art, linear-gradient(135deg, #2c3637, #1a2122));
    background-size: cover;
    background-position: center;
}

/* The dashed frame and the caption mark a slot that is still waiting for its photo. */
.photo-slot[data-photo-caption] { border: 1px dashed var(--line-strong); }

/* Sits top left so it never collides with the platform badge at the bottom. */
.photo-slot[data-photo-caption]::after {
    content: attr(data-photo-caption);
    position: absolute;
    left: 10px;
    top: 8px;
    max-width: 78%;
    color: var(--text-faint);
    font-size: 0.68rem;
    letter-spacing: 0.04em;
    text-align: right;
}

[data-photo="desk"] { --photo-art: url("../img/photos/desk.webp"); }

[data-photo="windows"] { --photo-art: url("../img/photos/windows.webp"); }
[data-photo="linux"]   { --photo-art: url("../img/photos/linux.webp"); }
[data-photo="macos"]   { --photo-art: url("../img/photos/macos.webp"); }
[data-photo="android"] { --photo-art: url("../img/photos/android.webp"); }
[data-photo="ios"]     { --photo-art: url("../img/photos/ios.webp"); }
[data-photo="web"]     { --photo-art: url("../img/photos/web.webp"); }

@media (max-width: 860px) {
    .tiles { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(3, 1fr);
             row-gap: clamp(32px, 4.5vh, 52px); }
    .desk-bezel { width: min(84%, 520px); }
    .photo-slot::after { display: none; }
}

/* On a phone the width is what limits the opening photo, so it turns out flat; standing at the
   bottom of a tall stage it would look dropped, so it moves to the middle. */
@media (max-width: 620px) {
    .desk { place-items: center; padding-bottom: 0; }
}

/* On a phone the cell is barely wider than the badge, so the screen grows and the badge shrinks. */
@media (max-width: 620px) {
    .tile-screen { width: 92%; }

    .tile-mark {
        gap: 7px;
        padding: 6px 12px 6px 9px;
        font-size: 0.72rem;
    }

    .tile-icon { width: 17px; height: 17px; }
}

/* ------------------------------------------------------------------ Coverage marquee */

.coverage {
    padding: var(--section-gap) 0;
    background: linear-gradient(180deg, transparent, rgba(20, 26, 27, 0.6) 40%, transparent);
}

.marquee {
    position: relative;
    overflow: hidden;
    padding: clamp(10px, 2.5vh, 26px) 0;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}

.marquee-track {
    display: flex;
    width: max-content;
    animation: marquee-run 46s linear infinite;
    will-change: transform;
}

.marquee:hover .marquee-track,
.marquee:focus-within .marquee-track { animation-play-state: paused; }

/* With scripting the band is driven by the scroll instead, so the keyframes step aside. */
.js .marquee-track { animation: none; }

@keyframes marquee-run {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.marquee-row {
    display: flex;
    gap: clamp(14px, 1.6vw, 24px);
    padding-right: clamp(14px, 1.6vw, 24px);
}

.chip {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 26px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-raised);
    white-space: nowrap;
    transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out),
                border-color 0.25s var(--ease-out);
    transition-delay: calc(var(--chip-index, 0) * 80ms);
}

.js .chip { opacity: 0; transform: translateY(18px); }

.marquee.is-in .chip { opacity: 1; transform: none; }
.chip:hover { border-color: var(--accent-line); }

.chip-icon {
    width: 26px;
    height: 26px;
    color: var(--accent);
    flex: none;
}

.chip-title { font-weight: 600; }

.chip-note {
    color: var(--text-faint);
    font-size: 0.86rem;
}

.chip-note::before { content: "("; }
.chip-note::after { content: ")"; }

/* ------------------------------------------------------------------ Beyond WPF */

.beyond { padding: var(--section-gap) 0; }

.compare {
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

.compare-head,
.compare-row {
    display: grid;
    grid-template-columns: minmax(150px, 1fr) minmax(180px, 1.15fr) minmax(220px, 1.85fr);
    gap: clamp(16px, 3vw, 40px);
    align-items: start;
}

.compare-head {
    padding-bottom: 14px;
    color: var(--text-faint);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.compare-row {
    position: relative;
    padding: clamp(22px, 3vw, 34px) 0;
    transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
}

.js .compare-row { opacity: 0; transform: translateY(22px); }

.compare-row::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 1px;
    background: var(--line-strong);
    transform-origin: left;
    transition: transform 0.7s var(--ease-out);
}

.js .compare-row::before { transform: scaleX(0); }

.compare-row.is-in { opacity: 1; transform: none; }
.compare-row.is-in::before { transform: scaleX(1); }

.compare-topic { font-size: clamp(1.05rem, 1.6vw, 1.32rem); font-weight: 600; }

.compare-hint {
    display: block;
    margin-top: 3px;
    color: var(--accent);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}
/* The old column arrives at full weight and then steps back — what WPF did is context, what
   OPF does is the statement. It has to start near white: between two shades of grey the move is
   there in the code and invisible on the screen. */
.compare-was {
    color: var(--text-faint);
    transition: color 1.1s var(--ease-out) 0.6s;
}

.js .compare-was { color: var(--text); }
.js .compare-row.is-in .compare-was { color: var(--text-faint); }

.compare-now {
    display: flex;
    gap: 12px;
    align-items: start;
    color: var(--text);
    font-weight: 500;
}

.compare-tick {
    width: 22px;
    height: 22px;
    margin-top: 3px;
    color: var(--accent);
    flex: none;
}

@media (max-width: 760px) {
    .compare-head { display: none; }
    .compare-row { grid-template-columns: 1fr; gap: 6px; }
    .compare-was::before { content: "WPF: "; color: var(--text-faint); }
}

/* ------------------------------------------------------------------ Pricing */

.pricing {
    padding: var(--section-gap) 0;
    background: linear-gradient(180deg, transparent, rgba(20, 26, 27, 0.55));
}

.plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(205px, 1fr));
    gap: clamp(16px, 1.8vw, 26px);
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 0 var(--gutter);
    align-items: stretch;
}

.plan {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: clamp(24px, 2.6vw, 34px);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-raised);
    transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out),
                border-color 0.25s var(--ease-out);
    transition-delay: calc(var(--reveal-index, 0) * 90ms);
}

.js .plan { opacity: 0; transform: translateY(26px); }

.plan.is-in { opacity: 1; transform: none; }
.plan:hover { border-color: var(--line-strong); }

.plan-featured {
    border-color: var(--accent-line);
    background: linear-gradient(180deg, rgba(238, 114, 3, 0.10), var(--surface-raised) 48%);
}

.plan-badge {
    align-self: start;
    padding: 3px 12px;
    border-radius: 999px;
    background: var(--accent);
    color: #1b1206;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.plan-name {
    font-size: 1.15rem;
    font-weight: 600;
    line-height: 1.25;
}

.plan-price { display: flex; flex-direction: column; gap: 4px; }

.plan-amount {
    font-size: clamp(1.9rem, 3vw, 2.5rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1;
}

.plan-unit,
.plan-note {
    color: var(--text-dim);
    font-size: 0.92rem;
    line-height: 1.5;
}

.plan-note { flex: 1; }
.plan-note strong { color: var(--text); }

/* Holds for every plan, so it stands once under all of them instead of four times inside. */
.plans-note {
    max-width: var(--page-width);
    margin: clamp(26px, 3.6vh, 40px) auto 0;
    padding: 0 var(--gutter);
    color: var(--text);
    font-size: 1.02rem;
    font-weight: 500;
}

/* All four prices on one line, however long the names above them are and whether or not a card
   carries a badge. Each card becomes a subgrid of the row it stands in, so badge, name, price,
   note and button share their heights across the cards instead of stacking independently.
   Only where the four stand side by side — once they wrap, there is no line to hold. */
@supports (grid-template-rows: subgrid) {
    @media (min-width: 900px) {
        .plans {
            grid-template-rows: auto auto auto 1fr auto;
            row-gap: 14px;
        }

        .plan {
            display: grid;
            grid-row: span 5;
            grid-template-rows: subgrid;
        }

        .plan-badge { grid-row: 1; }
        .plan-name { grid-row: 2; }
        .plan-price { grid-row: 3; }
        .plan-note { grid-row: 4; }
        /* Sitting at the top of its row, not filling it: a subgrid row is as tall as the
           tallest thing in it, and a stretched button grows with whatever else is in that row. */
        .plan .button-block {
            grid-row: 5;
            align-self: start;
            margin-top: 0;
        }
    }
}

.footnotes {
    max-width: var(--page-width);
    margin: clamp(28px, 4vh, 48px) auto 0;
    padding: 0 var(--gutter);
    counter-reset: footnote;
    color: var(--text-faint);
    font-size: 0.85rem;
}

.footnotes li::before {
    counter-increment: footnote;
    content: counter(footnote) ") ";
    color: var(--accent);
}

/* ------------------------------------------------------------------ Closing and footer */

.closing {
    padding: var(--section-gap) var(--gutter);
    text-align: center;
}

.closing-title {
    font-size: clamp(1.9rem, 4.4vw, 3.2rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.1;
}

.closing-lead {
    max-width: 54ch;
    margin: 18px auto 0;
    color: var(--text-dim);
}

.site-footer {
    display: flex;
    flex-wrap: wrap;
    gap: 18px 32px;
    align-items: center;
    justify-content: space-between;
    padding: 34px var(--gutter);
    border-top: 1px solid var(--line);
    color: var(--text-faint);
    font-size: 0.88rem;
}

.footer-logo {
    width: 126px;
    height: 23px;
    color: var(--text-faint);
}

.footer-line a:hover { color: var(--accent); }

/* ------------------------------------------------------------------ Legal pages

   Plain text pages next to the scrollytelling: same colours, same header, no choreography.
   The logo in their header is a normal logo — nothing travels here, so it must not be the
   invisible measuring point it is on the front page. */

.page { padding-top: 96px; }

.js .brand-logo-plain { opacity: 1; }

.legal {
    max-width: 760px;
    margin: 0 auto;
    padding: clamp(32px, 6vh, 72px) var(--gutter) clamp(60px, 10vh, 120px);
}

.legal-title {
    font-size: clamp(2rem, 4.4vw, 3rem);
    font-weight: 700;
    letter-spacing: -0.02em;
}

.legal-lead {
    margin-top: 10px;
    color: var(--text-dim);
}

.legal-block {
    margin-top: clamp(30px, 4.5vh, 48px);
    padding-top: clamp(24px, 3.5vh, 36px);
    border-top: 1px solid var(--line);
}

.legal-block h2 {
    margin-bottom: 12px;
    color: var(--accent);
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}

.legal-block p { line-height: 1.7; }
.legal-block a { border-bottom: 1px solid var(--line-strong); }
.legal-block a:hover { color: var(--accent); border-bottom-color: var(--accent); }

.legal-list {
    display: grid;
    grid-template-columns: minmax(150px, auto) 1fr;
    gap: 8px 28px;
    margin: 0;
}

.legal-list dt { color: var(--text-dim); }
.legal-list dd { margin: 0; }

.legal-back { margin-top: clamp(40px, 6vh, 64px); }

@media (max-width: 560px) {
    .legal-list { grid-template-columns: 1fr; gap: 2px; }
    .legal-list dd { margin-bottom: 12px; }
}

/* ------------------------------------------------------------------ Reduced motion */

/* Motion off: written onto the root element by the inline head script, either because the
   system asks for reduced motion or because "?motion=off" says so. Everything lands in its
   finished state and the scene turns into a plain gallery of the six platforms. */

.motion-off { scroll-behavior: auto; }

.motion-off *,
.motion-off *::before,
.motion-off *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
}

.motion-off .scene { height: auto; }
.motion-off .scene-sticky { position: static; height: auto; padding-block: var(--section-gap); }
.motion-off .scene-stage { height: auto; }
.motion-off .desk { display: none; }

.motion-off .tiles {
    position: static;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: none;
}

.motion-off .tile { aspect-ratio: 16 / 11; }
.motion-off .screens { opacity: 1; }
.motion-off .tile-photo { opacity: 1; transform: none; }
.motion-off .tile-mark { opacity: 1; transform: translate(-50%, 50%); bottom: 0; }
.motion-off .scene-caption { opacity: 1; transform: none; }
.motion-off .scroll-hint { display: none; }

/* A band that does not move hides half of what it carries — so it stops being a band and
   becomes a list that wraps. */
.motion-off .marquee {
    -webkit-mask-image: none;
            mask-image: none;
}

.motion-off .marquee-track { width: auto; }

.motion-off .marquee-row {
    flex-wrap: wrap;
    justify-content: center;
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 0 var(--gutter);
}

@media (max-width: 860px) {
    .motion-off .tiles { grid-template-columns: repeat(2, 1fr); }
}
