@import url('fonts/fonts.css');
:root {
    color-scheme:light;
    --paper:#F2F1EC;
    --paper2:#E8E6DE;
    --ink:#171712;
    --muted:#6E6A5F;
    --rule:#B5B1A3;
    --red:#D8291C;
    --red2:#F04A3C;
    --deep:#B02116;
    /* One value per role, resolved per tone. --frame outlines components, --accent is red used
       as small-text colour (4.5:1 or better), --lift is the hover offset shadow, --ember the dot. */
    --frame:var(--ink);
    --faint:var(--muted);
    --accent:var(--deep);
    --lift:var(--red);
    /* Fixed chrome (header, progress) has no scene parent: the tone is pushed down from body. */
    --chrome-ink:var(--ink);
    --chrome-muted:var(--muted);
    --chrome-rule:var(--rule);
    --chrome-ember:var(--red);
    --sans:'Inter','Inter Fallback',ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
    --mono:'Fragment Mono','Fragment Fallback',ui-monospace,"SFMono-Regular",Consolas,monospace;
    --dim:var(--muted);
    /* Fixed chrome (header, progress rail) is the only surface outside the film strip. Glass is
       its material, never content's: it tints and diffracts the edge, and re-tones with the scene. */
    --chrome-glass:rgba(242,241,236,.78);
    --chrome-edge:rgba(255,255,255,.55);
    --chrome-chroma:rgba(216,41,28,.22);
    --chrome-inner:rgba(23,23,18,.05);
    --chrome-ink:var(--ink);
    --chrome-muted:var(--muted);
    --chrome-rule:#B5B1A3;
    --chrome-bg:var(--paper);
    --chrome-accent:var(--red);
    --chrome-ember:var(--red);
}
* {
    box-sizing:border-box
}
html,body {
    margin:0;
    background:var(--paper);
    color:var(--ink);
    font-family:var(--sans);
    overflow:hidden
}
body {
    cursor:default
}
a {
    color:inherit;
    text-decoration:none;
    cursor:pointer
}
/* Site-wide keyboard focus indicator. :where() keeps specificity at 0 so component-level
   rules (form fields, tabs) can refine it without fighting the cascade. */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible {
    outline:2px solid var(--accent);
    outline-offset:3px
}
/* A scene that hides content below the fold is a scroll container and stays keyboard-reachable. The
   ring has to sit inside the box: at offset +3px it falls on the world's clipping edge and only a
   stray line survives at the screen border. */
.scene:focus-visible {
    outline-offset:-5px
}
/* The bar is re-toned by whichever scene is centred, so its ring follows the bar: the document's
   deep red sits at 1.4:1 on the red glass, which is no indicator at all. */
.top :where(a,button):focus-visible {
    outline-color:var(--chrome-accent)
}
.mono {
    font:12px/1.45 var(--mono);
    letter-spacing:.14em;
    text-transform:uppercase
}
.skip {
    position:absolute;
    top:-50px;
    left:16px;
    z-index:50;
    background:var(--ink);
    color:#fff;
    padding:12px 16px
}
.skip:focus {
    top:16px
}
.top {
    position:fixed;
    z-index:40;
    top:0;
    left:0;
    right:0;
    height:72px;
    color:var(--chrome-ink);
    border-bottom:1px solid var(--chrome-rule);
    transition:background-color .5s ease,border-color .5s ease,color .5s ease,box-shadow .5s ease;
    /* The only frosted surface on the site: content passing under a frame, not content behind glass. */
    background:var(--chrome-glass);
    -webkit-backdrop-filter:blur(14px) saturate(115%);
    backdrop-filter:blur(14px) saturate(115%);
    box-shadow:inset 0 1px 0 var(--chrome-edge),inset 0 -1px 0 var(--chrome-inner),0 1px 0 var(--chrome-chroma)
}
.top-in {
    width:min(1180px,calc(100% - 48px));
    height:100%;
    margin:auto;
    display:flex;
    align-items:center;
    gap:24px
}
.brand {
    font-weight:800;
    letter-spacing:-.04em;
    white-space:nowrap
}
.brand i {
    display:inline-block;
    width:8px;
    height:8px;
    border-radius:50%;
    background:var(--chrome-ember);
    margin-left:4px
}
.top .label {
    color:var(--chrome-muted)
}
.nav {
    margin-left:auto;
    display:flex;
    gap:30px;
    align-items:center
}
.nav a {
    font:500 15px/1 var(--sans);
    letter-spacing:-.01em;
    color:var(--chrome-ink);
    white-space:nowrap
}
/* The bar is 72px tall, so a text link can carry a 44px target without moving a pixel of ink. */
.nav a:not(.contact) {
    padding:15px 0
}
.nav a:hover {
    color:var(--chrome-accent)
}
.nav .contact {
    border:1px solid var(--chrome-ink);
    color:var(--chrome-ink);
    padding:12px 17px;
    font-weight:650;
    white-space:nowrap
}
.nav .contact:hover {
    background:var(--chrome-ink);
    color:var(--chrome-bg)
}
/* Every page carries the full bar. The destination you are already on keeps its place and takes the
   brand dot — removing it made the navigation change shape mid-visit and left the current page
   unnamed, which is the opposite of what a header is for. */
.nav a[aria-current="page"]:not(.contact):before {
    content:"";
    display:inline-block;
    width:.42em;
    height:.42em;
    border-radius:50%;
    background:var(--chrome-accent);
    margin-right:.55em;
    vertical-align:.08em
}
.nav .contact[aria-current="page"],.nav .contact[aria-current="page"]:hover {
    background:var(--chrome-ink);
    color:var(--chrome-bg)
}
/* tight desktop band: the tagline competes with the nav for width — drop it so the bar stays on one line */
@media(min-width:801px) and (max-width:1120px) {
    .top .label {
        display:none
    }
}
.world {
    position:absolute;
    inset:0;
    /* Not a tab stop: arrow-key paging is handled on window, so only real controls
       take focus rings. tabindex="-1" keeps the skip-link target programmatically focusable. */
    outline:0;
    overflow-x:auto;
    overflow-y:hidden;
    scroll-behavior:auto;
    scrollbar-width:none
}
.world::-webkit-scrollbar {
    display:none
}
.world-track {
    height:100%;
    display:flex;
    width:max-content
}
.scene {
    position:relative;
    flex:none;
    width:100vw;
    height:100vh;
    padding:120px clamp(24px,7vw,110px) 90px;
    display:flex;
    align-items:safe center;
    /* One light ground tone: a 4 % tint difference between neighbours is invisible as rhythm
       but shows up as a hard vertical wall in the seam while panning. */
    background:var(--paper);
    overflow-x:hidden;
    overflow-y:auto;
    /* A focused element has to clear the fixed frame at both ends, or Tab appears to do nothing. */
    scroll-padding:88px 0 56px
}
.scene-inner {
    width:min(1160px,100%);
    margin:auto;
    position:relative
}
.eyebrow {
    display:flex;
    align-items:flex-start;
    gap:10px;
    color:var(--ink);
    margin:0 0 26px
}
.eyebrow:before {
    content:"";
    flex:none;
    width:9px;
    height:9px;
    background:var(--red)
}
.dark .eyebrow {
    color:var(--paper2)
}
.dark .eyebrow:before {
    background:var(--red2)
}
.red .eyebrow {
    color:#fff
}
.red .eyebrow:before {
    background:var(--ink)
}
.scene h1,.scene h2 {
    /* Height is as binding as width here: 6.2vw alone produced 112px headlines on a 1920x1080
       display that no copy column could hold, so every scene wrapped to four or five lines. */
    font-size:clamp(3rem,min(6.2vw,9.2vh),7rem);
    line-height:1.02;
    letter-spacing:-.035em;
    font-weight:600;
    margin:0;
    max-width:1000px
}
.scene h1 span,.scene h2 span {
    color:var(--red)
}
/* hero text must never reach the hero visual: cap the copy column, no geometry move (root-cause, all widths). */
@media(min-width:801px) {
    .scene.opening .scene-inner>*:not(.opening-visual):not(.rail) {
        max-width:min(920px,calc(100% - min(43vw,600px,80vh) - 28px))
    }
}
/* On wide displays the hero earns a real two-column measure instead of being squeezed between a
   capped visual and a capped inner width. */
@media(min-width:1401px) {
    .scene.opening .scene-inner {
        width:min(1500px,100%)
    }
    /* The cap reserves the hero visual's square for pages that have one; on a scene without it the
       column would stop mid-viewport for no reason, so the cap is scoped to the visual's siblings. */
    .scene.opening .scene-inner>*:not(.opening-visual):not(.rail) {
        max-width:calc(100% - min(43vw,600px,80vh) - 48px)
    }
}
.lead {
    font-size:clamp(1rem,1.5vw,1.25rem);
    line-height:1.58;
    max-width:580px;
    color:#4d4b42;
    margin:29px 0 0
}
.actions {
    display:flex;
    gap:12px;
    flex-wrap:wrap;
    margin-top:30px
}
.btn {
    display:inline-flex;
    align-items:center;
    justify-content:center;
    border:1px solid var(--frame);
    padding:15px 22px;
    font-weight:600;
    font-size:15px;
    transition:transform .22s ease,box-shadow .22s ease,border-color .22s ease,background-color .22s ease,color .22s ease
}
.btn.primary {
    background:var(--red);
    border-color:var(--red);
    color:#fff
}
.btn:hover {
    transform:translateY(-3px);
    box-shadow:5px 5px 0 var(--lift)
}
.btn.primary:hover {
    background:var(--deep);
    border-color:var(--deep)
}
.split {
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:8vw;
    align-items:center
}
.opening-visual {
    position:absolute;
    right:0;
    top:50%;
    translate:0 -50%;
    width:min(43vw,600px,80vh);
    /* Visibility is the default. With opacity:0 here the hero left an empty square whenever the
       script never ran, the module was blocked, or WebGL is absent - the on-brand placeholder was
       invisible too. cube3d arms itself and the arm expires on a timer. */
    transition:opacity .6s linear
}
.opening-visual.is-arming {
    opacity:0
}
.opening-visual.live,.opening-visual.no-webgl {
    opacity:1
}
/* The hint invites a gesture; without a live visual there is nothing to turn or expire. */
.cube-hint {
    visibility:hidden
}
.opening-visual.live .cube-hint {
    visibility:visible
}
.opening-visual canvas {
    display:block;
    /* ohne dieses wäre ein Zug auf dem Würfel eine Browser-Geste und keine Drehung */
    touch-action:none;
    width:100%;
    aspect-ratio:1;
    height:auto;
    cursor:grab
}
.opening-visual.held canvas {
    cursor:grabbing
}
/* teaches the two gestures the cube accepts; the touch variant drops "drag", which pans the strip */
.cube-hint {
    position:absolute;
    left:2px;
    bottom:8px;
    font-size:10px;
    letter-spacing:.12em;
    text-transform:uppercase;
    color:var(--muted);
    transition:color .25s
}
.opening-visual:hover .cube-hint {
    color:var(--ink)
}
.hint-touch {
    display:none
}
.opening-visual.no-webgl .cube-hint {
    display:none
}
@media(hover:none) {
    .hint-pointer {
        display:none
    }
    .hint-touch {
        display:inline
    }
}
/* no-WebGL / context-loss: an on-brand static stand-in for the 3D hero */
.opening-visual.no-webgl {
    aspect-ratio:1;
    border:1.5px solid var(--ink);
    background:radial-gradient(60% 60% at 55% 45%,rgba(216,41,28,.28),transparent 70%),var(--paper2);
    display:grid;
    place-items:center;
    overflow:hidden
}
.opening-visual.no-webgl canvas {
    display:none
}
.opening-visual.no-webgl:after {
    content:"";
    width:34%;
    aspect-ratio:1;
    border:1.5px solid var(--ink);
    background:linear-gradient(135deg,transparent 45%,rgba(216,41,28,.55));
    transform:rotate(-18deg) skewY(8deg)
}
/* Available height is as binding as width: 210px of scene padding under a 7rem headline is what
   pushed dense scenes into an internal scroll trap on 1280x720 and 1440x900 displays. Below ~900px
   of viewport height the whole strip scales to a rhythm that fits without its own scrollbar. */
@media(min-width:801px) and (max-height:900px) {
    .scene {
        padding-top:86px;
        padding-bottom:56px;
        scroll-padding:80px 0 40px;
        /* Kurze Fenster: passt der Inhalt nicht, scrollt die Szene selbst. Der Wheel-Handler lässt
           ihr den Weg bis zum Ende und kettet die Geste erst dann zurück an den Streifen. */
        overflow-y:auto;
        overscroll-behavior-y:none
    }
    .scene h1,.scene h2 {
        font-size:clamp(2.15rem,3.9vw,3.5rem)
    }
    .scene .eyebrow {
        margin-bottom:18px
    }
    .scene .lead {
        margin-top:20px
    }
}
/* Short desktop windows: top-align and tighten the hero so it always fits above the fixed progress bar (which has no backdrop and would otherwise be overlapped by overflowing content). */
@media(min-width:801px) and (max-height:760px) {
    .scene {
        /* 80 statt 64: die Kopfzeile ist 72 px hoch, und ein Inhalt, der bei scrollTop 0 hinter ihr
           beginnt, ist unerreichbar - hochscrollen geht nicht. 8 px Luft sind die Kante. */
        padding-top:80px;
        padding-bottom:52px
    }
    .scene.opening {
        align-items:flex-start;
        padding-top:96px
    }
    .scene.opening .scene-inner {
        margin:0 auto
    }
    .scene.opening .eyebrow {
        margin-bottom:16px
    }
    .scene.opening h1 {
        font-size:clamp(2.5rem,4.6vw,4.2rem)
    }
    .scene.opening .lead {
        margin-top:18px
    }
    .scene.opening .actions {
        margin-top:20px
    }
}
/* Two-column scenes put the headline in half the measure, so its size has to come from that column
   rather than from the viewport. Without this a 52-character sentence became six lines of 112px
   type and the scene needed its own scrollbar on every large display. */
@media(min-width:801px) {
    .split {
        gap:clamp(40px,5vw,92px)
    }
    .split h1,.split h2 {
        font-size:clamp(2rem,min(3.7vw,5.4vh),4.4rem)
    }
    .split .lead {
        margin-top:18px
    }
}
/* Scene 2 vignette: a looping terminal micro-story — untrusted code runs with
   unknown source and wide-open permissions, the window dissolves, and no
   evidence remains. One shared 12s timeline, CSS only, no JS. */
.vterminal {
    position:relative;
    max-width:540px
}
.vt-window {
    position:relative;
    /* An ink island inside a light scene: it carries its own tone so rules and dim text
       stay legible on it instead of inheriting the scene's light-ground values. */
    --rule:#57554a;
    --dim:#a29e92;
    --frame:rgba(242,241,236,.34);
    background:var(--ink);
    color:var(--paper);
    box-shadow:14px 14px 0 rgba(23,23,18,.12);
    animation:vtRecede 12s cubic-bezier(.45,0,.25,1) infinite
}
.vt-bar {
    display:flex;
    align-items:center;
    gap:9px;
    padding:11px 14px;
    border-bottom:1px solid var(--rule);
    font:10px var(--mono);
    letter-spacing:.14em;
    color:var(--dim)
}
.vt-bar i {
    width:8px;
    height:8px;
    background:var(--red2);
    flex:none
}
.vt-body {
    padding:18px 16px 22px;
    font:400 13px/1.9 var(--mono);
    display:grid;
    gap:2px
}
.vt-line b {
    color:var(--red2);
    font-weight:400
}
.vt-cmd {
    color:var(--paper)
}
.vt-type {
    display:inline-block;
    overflow:hidden;
    white-space:nowrap;
    vertical-align:bottom;
    max-width:0;
    animation:vtType 12s steps(27,end) infinite
}
.vt-caret {
    display:inline-block;
    width:7px;
    height:13px;
    background:var(--red2);
    margin-left:2px;
    vertical-align:middle;
    animation:vtCaret 1.05s steps(2) infinite
}
.vt-src {
    opacity:0;
    animation:vtFade 12s ease-out infinite
}
.vt-done {
    opacity:0;
    animation:vtFade2 12s ease-out infinite
}
.vt-warn {
    opacity:0
}
.vt-w1 {
    animation:vtW1 12s ease-out infinite
}
.vt-w2 {
    animation:vtW2 12s ease-out infinite
}
.vt-w3 {
    animation:vtW3 12s ease-out infinite
}
.vt-stamp {
    position:absolute;
    left:4%;
    bottom:-3%;
    rotate:-5deg;
    border:2px solid var(--accent);
    color:var(--accent);
    padding:12px 18px;
    font:400 13px var(--mono);
    letter-spacing:.12em;
    background:var(--paper2);
    opacity:0;
    animation:vtStamp 12s ease-out infinite
}
@keyframes vtType {
    0%,4% {
        max-width:0
    }
    22%,92% {
        max-width:100%
    }
    93%,100% {
        max-width:0
    }
}
@keyframes vtCaret {
    50% {
        opacity:0
    }
}
@keyframes vtFade {
    0%,20% {
        opacity:0
    }
    25%,92% {
        opacity:1
    }
    93%,100% {
        opacity:0
    }
}
@keyframes vtW1 {
    0%,28% {
        opacity:0
    }
    34%,92% {
        opacity:1
    }
    93%,100% {
        opacity:0
    }
}
@keyframes vtW2 {
    0%,33% {
        opacity:0
    }
    39%,92% {
        opacity:1
    }
    93%,100% {
        opacity:0
    }
}
@keyframes vtW3 {
    0%,38% {
        opacity:0
    }
    44%,92% {
        opacity:1
    }
    93%,100% {
        opacity:0
    }
}
@keyframes vtFade2 {
    0%,48% {
        opacity:0
    }
    54%,92% {
        opacity:1
    }
    93%,100% {
        opacity:0
    }
}
/* The window gently recedes into the paper: fades while lifting and shrinking,
   then returns softly — no flicker, no jitter. */
@keyframes vtRecede {
    0% {
        opacity:0;
        transform:translateY(-10px) scale(.985)
    }
    4%,60% {
        opacity:1;
        transform:none
    }
    70%,94% {
        opacity:0;
        transform:translateY(-14px) scale(.97)
    }
    100% {
        opacity:1;
        transform:none
    }
}
@keyframes vtStamp {
    0%,70% {
        opacity:0;
        scale:1.04
    }
    76%,88% {
        opacity:1;
        scale:1
    }
    93%,100% {
        opacity:0;
        scale:1.04
    }
}
@media(prefers-reduced-motion:reduce) {
    .vt-window,.vt-type,.vt-caret,.vt-src,.vt-w1,.vt-w2,.vt-w3,.vt-done,.vt-stamp {
        animation:none!important
    }
    .vt-type {
        max-width:100%
    }
    .vt-src,.vt-warn,.vt-done,.vt-stamp {
        opacity:1
    }
}
.dark {
    --dim:#a29e92;
    --rule:#57554a;
    --frame:#98958a;
    --faint:#8b887c;
    --accent:var(--red2);
    --lift:var(--red2);
    background:var(--ink);
    color:var(--paper)
}
.dark .lead {
    color:var(--dim)
}
.phase-row {
    display:grid;
    grid-template-columns:repeat(4,1fr);
    border:1px solid var(--frame);
    margin-top:60px
}
.phase-row>div {
    padding:19px;
    border-right:1px solid var(--frame);
    min-height:200px
}
.phase-row>div:last-child {
    border-right:0
}
.phase-row b {
    color:var(--accent);
    font:10px var(--mono)
}
.phase-row strong {
    display:block;
    font-size:1.35rem;
    letter-spacing:-.02em;
    font-weight:600;
    margin-top:58px
}
.phase-row p {
    color:var(--dim);
    line-height:1.45;
    font-size:.9rem
}
.cell-card {
    border:1px solid var(--frame);
    padding:28px;
    min-height:280px;
    display:flex;
    flex-direction:column;
    justify-content:space-between;
    box-shadow:12px 12px 0 rgba(23,23,18,.1)
}
/* cell.html hero: the architecture as a drawing, not as a list. A status stamp only said that Cell is
   available; this shows what happens to the code that is handed to it — verify, boundary, result, record
   — with the enforced budgets drawn inside the boundary they belong to.
   Type in the drawing scales WITH the figure (inline SVG), so the viewBox is the type control: at 272u
   wide every label reaches >= 10 render px from 320px up, which is why the rows are tall and the two
   longest facts sit on a second <tspan> line instead of being shrunk. max-width keeps the same labels
   from ballooning to 26px when the stacked hero hands the figure the full 640px column. */
.arch {
    margin:0;
    border:1px solid var(--frame);
    padding:16px 12px 12px;
    max-width:330px;
    background:var(--paper);
    box-shadow:12px 12px 0 rgba(23,23,18,.1)
}
.arch > figcaption {
    padding-bottom:13px;
    border-bottom:1px solid var(--rule);
    font:10px var(--mono);
    letter-spacing:.14em;
    color:var(--accent)
}
.arch-svg {
    display:block;
    width:100%;
    height:auto;
    margin:4px 0 2px
}
.arch-svg text {
    font-family:var(--mono)
}
.as-box {
    fill:var(--paper);
    stroke:var(--frame);
    stroke-width:1
}
.as-title {
    font-size:14px;
    font-weight:400;
    letter-spacing:.14em;
    fill:var(--ink)
}
.as-sub {
    font-size:11px;
    letter-spacing:.02em;
    fill:var(--dim)
}
.as-tag {
    font-size:11px;
    letter-spacing:.14em;
    fill:var(--accent)
}
.as-chip {
    fill:none;
    stroke:var(--rule);
    stroke-width:1
}
.as-chip-t {
    font-size:11px;
    letter-spacing:.06em;
    fill:var(--dim)
}
.as-stop {
    stroke:var(--accent)
}
.as-bound {
    fill:none;
    stroke:var(--accent);
    stroke-width:1.5
}
.as-red-t {
    fill:var(--accent)
}
.as-tick line {
    stroke:var(--frame);
    stroke-width:1
}
.as-spine line {
    stroke:var(--frame);
    stroke-width:1;
    stroke-dasharray:4 5
}
.as-flow line {
    stroke:var(--accent);
    stroke-width:1.5;
    stroke-dasharray:4 5;
    animation:as-flow 1.1s linear infinite
}
@keyframes as-flow {
    to {
        stroke-dashoffset:-9
    }
}
.as-node rect {
    fill:var(--paper);
    stroke:var(--ink);
    stroke-width:1
}
.as-node-red rect {
    fill:var(--accent);
    stroke:var(--accent)
}
.as-pulse {
    fill:var(--accent);
    animation:as-pulse 5.4s cubic-bezier(.45,0,.55,1) infinite
}
@keyframes as-pulse {
    0%,3% {
        transform:translateY(0);
        opacity:0
    }
    9% {
        opacity:1
    }
    90% {
        opacity:1
    }
    100% {
        transform:translateY(511px);
        opacity:0
    }
}
.arch-note {
    margin:12px 0 0;
    padding-top:12px;
    border-top:1px solid var(--rule);
    font:10px var(--mono);
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--faint)
}
/* 320px hands the drawing 280px of card. At 12px of side padding the label type would land at 9.7
   render px — under the page's own floor — so the phone card spends its padding on the diagram
   instead of on air. */
@media(max-width:420px) {
    .arch {
        padding:14px 9px 10px
    }
}
/* Short desktop windows: the hero figure hangs off the scene's content midline, the only anchor a
   drawing this tall has at 900px. Below ~790px of window height that anchor pulls the caption under
   the fixed header, where nothing can scroll to it, so in that band the card drops to the top of the
   copy column instead and the scene's own scrollbar (see the max-height:900px rule) carries the last
   row. !important because the anchor lives in the figure's style attribute. */
@media(min-width:801px) and (max-height:790px) {
    .arch {
        top:0!important;
        /* A bottom margin on an absolutely positioned box does not extend the scene's scroll range in
           Chrome; the air has to be inside the card, or the scene runs out of scroll while the caption
           line is still under the fixed rail. */
        padding:16px 12px 76px
    }
}
/* The hero column and the drawing had two different heads: the column was centred in the scene, the
   figure hung 175px (1440x900), 183px (1280x800) and 201px (1024x900) above it - at 1280x800 even 5px
   above the fixed header's lower edge, in a scene with no scroll range left. One line only: the
   column's own top, which is the hero's padding. The figure pays for that line with width, never with
   label size. 296px is only free from 838px of window height up (card bottom 779, rail top 787); one
   step below that the figure gives up 20px of width, which is the smallest card whose 11px viewBox
   labels still reach 10 render px (276px measured). Under 791px the scroll treatment above takes over
   and the figure keeps its full width. */
@media(min-width:801px) and (min-height:791px) {
    .scene.opening:has(.arch) {
        align-items:flex-start;
        padding-top:86px
    }
    .scene.opening:has(.arch) .scene-inner {
        margin:0 auto
    }
    .scene.opening:has(.arch) .arch {
        top:0!important;
        /* max() over the inline 34vw: at 801-806px of window width that term falls to 272px and the
           11px viewBox labels land at 9.96 render px, under the page's own floor. */
        width:max(276px,min(296px,34vw))!important
    }
}
@media(min-width:801px) and (min-height:791px) and (max-height:837px) {
    .scene.opening:has(.arch) .arch {
        /* Measured at 1280x800: 276px is the smallest card that keeps the 11px viewBox labels at
           10.11 render px, and it is also the largest that ends above the rail (card 86-746, rail
           749) with the scene's own scroll untouched. One px more and the foot line falls behind. */
        width:276px!important
    }
}
@media(prefers-reduced-motion:reduce) {
    .as-flow line,.as-pulse {
        animation:none
    }
    .as-pulse {
        opacity:0
    }
}
.cell-card small {
    color:var(--accent)
}
.cell-card strong {
    font-size:2.6rem;
    letter-spacing:-.035em;
    font-weight:650
}
.cell-lines {
    border-top:1px solid var(--rule);
    display:grid;
    gap:12px;
    padding-top:18px;
    font:11px var(--mono);
    text-transform:uppercase
}
.cell-lines span {
    display:flex;
    justify-content:space-between
}
.cell-lines b {
    color:var(--accent);
    font-weight:400
}
.enterprise-scene {
    background:var(--ink)
}
.enterprise-grid {
    display:grid;
    grid-template-columns:1fr 1fr;
    border:1px solid var(--rule)
}
.enterprise-grid span {
    padding:31px 20px;
    border-right:1px solid var(--rule);
    border-bottom:1px solid var(--rule);
    color:var(--dim);
    font:10px var(--mono);
    letter-spacing:.12em
}
.enterprise-grid span:nth-child(even) {
    border-right:0
}
.enterprise-grid span:nth-last-child(-n+2) {
    border-bottom:0
}
.record {
    margin-top:18px
}
.record-row {
    display:grid;
    grid-template-columns:auto 1fr;
    gap:14px;
    align-items:baseline;
    border-top:1px solid var(--rule);
    padding:13px 0
}
.record-row strong {
    font:700 1rem var(--sans);
    letter-spacing:-.02em
}
.record-row:first-child {
    border-top:0
}
/* Scene 06's visual is painted, not laid out: the figure only fixes the box the canvas draws into, and
   carries no border — a hard rectangular edge would cut the field and read as a target again. */
.constellation {
    position:absolute;
    right:3vw;
    top:50%;
    translate:0 -50%;
    /* the UA figure margin would push the field 16px down and 40px in from the right */
    margin:0;
    width:min(38vw,460px);
    height:min(38vw,460px);
    min-width:300px;
    min-height:300px
}
.constellation canvas {
    display:block;
    width:100%;
    height:100%
}
/* From ~1100px down the painted field reaches into the copy column, so loose points were landing on
   top of letters. The field now dissolves as it approaches the text — which is, after all, the idea.
   The named marks sit right of the fade, so the reading of the scene is untouched. */
@media(min-width:801px) and (max-width:1100px) {
    .constellation canvas {
        -webkit-mask-image:linear-gradient(to right,transparent 0,#000 28%);
        mask-image:linear-gradient(to right,transparent 0,#000 28%)
    }
}
/* Scene 06's legend: type with hairline dividers, not a boxed key. A ruled rectangle around it would
   repeat the frame the field above is deliberately drawn without. One term per line, so the block stays
   inside the text column at every width instead of running under the painted field. */
.constellation-key {
    display:flex;
    flex-direction:column;
    gap:6px;
    margin-top:22px;
    font-size:10px;
    letter-spacing:.1em;
    color:var(--muted)
}
.constellation-key b {
    flex:none;
    width:78px;
    margin-right:10px;
    color:var(--accent);
    font-weight:500;
    border-right:1px solid var(--rule)
}
/* 801–960px: the field is absolutely positioned, so its left edge slides under the text column while the
   column still keeps its full 580px. The core is the one solid thing out there, so the lead yields width
   rather than the field moving — below 800px the scene stacks and the figure is back in flow. */
@media (min-width:801px) and (max-width:960px) {
    .future-scene .lead {
        max-width:min(580px,52vw)
    }
}
/* future.html hero: the family as a station rail — one line, four stops, the red run stopping at the
   product being built now. The ruled band before it read as a spec sheet and collapsed into an empty
   2x2 grid below 800px; a rail keeps its meaning at 320px because it never becomes a table. */
.rail {
    margin-top:44px;
    max-width:min(1160px,100%)
}
.rail-head {
    display:flex;
    justify-content:space-between;
    gap:14px;
    padding-bottom:11px;
    border-bottom:1px solid var(--rule);
    font:10px var(--mono);
    letter-spacing:.14em;
    text-transform:uppercase;
    color:var(--faint)
}
.rail-head b {
    font-weight:500;
    color:var(--ink)
}
.rail-line {
    position:relative;
    display:flex
}
.rail-line:before {
    content:"";
    position:absolute;
    left:0;
    right:0;
    top:8px;
    height:1px;
    background:var(--frame)
}
.rail-run {
    position:absolute;
    left:0;
    top:8px;
    /* one quarter, not one half: each stop owns a quarter of the line, and only
       the shipped product's quarter is drawn — the run ends inside Cell's span */
    width:12.5%;
    height:1px;
    background:var(--accent)
}
.world.js .rail-run {
    width:0;
    transition:width 1.1s cubic-bezier(.22,.61,.24,1) .3s
}
.world.js .scene.is-active .rail-run {
    width:12.5%
}
/* Beim Weiterblättern darf die Linie nicht denselben Weg zurücklaufen — sie ist ein Stand,
   keine Pendelbewegung. */
.world.js .scene:not(.is-active) .rail-run {
    transition:none
}
.rail-stop {
    flex:1;
    position:relative;
    padding-right:26px
}
.rail-stop:before {
    content:"";
    position:absolute;
    left:0;
    top:4px;
    width:9px;
    height:9px;
    border:1px solid var(--frame);
    background:var(--paper);
    border-radius:50%
}
.rail-stop.is-done:before {
    background:var(--ink);
    border-color:var(--ink)
}
.rail-stop.is-open:before {
    border-style:dashed
}
.rail-stop b {
    display:block;
    margin-top:28px;
    font:10px/1 var(--mono);
    letter-spacing:.14em;
    color:var(--faint)
}
.rail-stop small {
    display:block;
    margin-top:8px;
    font:10px/1.5 var(--mono);
    letter-spacing:.08em;
    color:var(--faint)
}
/* Status and qualifier on their own lines: one line each, at every width, so the four stops scan as a
   row instead of wrapping wherever the longest one happens to break. */
.rail-stop small span {
    display:block
}
.rail-stop small span:first-child {
    color:var(--ink)
}

.rail-stop.is-done b {
    color:var(--dim)
}
@media(max-width:430px) {
    /* Vier Status über 350 px sind nicht lesbar untergebracht: unter 430 px wird die Schiene zur
       Liste — Punkt für Punkt, ohne Lauf, dafür mit Zeilen, die man lesen kann. */
    .rail-line {
        display:block
    }
    .rail-line:before, .world.js .rail-run {
        display:none
    }
    .rail-stop {
        padding-left:22px;
        margin-bottom:14px
    }
    .rail-stop:before {
        top:3px
    }
    .rail-stop b {
        margin-top:0;
        letter-spacing:.08em
    }
    .rail-stop small {
        margin-top:3px;
        letter-spacing:.02em
    }
}
@media(prefers-reduced-motion:reduce) {
    .world.js .rail-run {
        transition:none
    }
}
.red {
    --rule:rgba(23,23,18,.3);
    --frame:var(--ink);
    --accent:var(--ink);
    --lift:rgba(23,23,18,.45);
    background:var(--red);
    color:#fff
}
.red .lead {
    color:#fff
}
.red h2 span {
    color:var(--ink)
}
.proof-record {
    border:1px solid #fff;
    padding:27px;
    min-height:250px;
    display:flex;
    flex-direction:column;
    justify-content:space-between;
    transform:rotate(-4deg)
}
/* A 638px-tall card tilted four degrees is 44px wider than its box, which at 320px pushes the
   stamped border past the viewport and adds a phantom horizontal scroll inside the scene. */
@media(max-width:430px) {
    .proof-record {
        transform:rotate(-2deg)
    }
}
.proof-record small {
    font:10px var(--mono)
}
.proof-record strong {
    font-size:2.6rem;
    letter-spacing:-.035em;
    font-weight:650
}
.proof-record span {
    font:10px var(--mono)
}
/* The released version and the one in preparation are two different claims; the rule between them says
   so. The next-release line stays concrete - what changes, and that it is not tagged yet. */
.proof-record .pr-foot {
    display:grid;
    gap:10px
}
.proof-record .pr-next {
    padding-left:10px;
    border-left:1px solid currentColor;
    line-height:1.65;
    opacity:.92
}
.proof-record .pr-next b {
    font-weight:400;
    letter-spacing:.1em
}
.end-note {
    margin-top:80px;
    color:var(--muted)
}
/* --muted is a paper colour and reads 1.09:1 on brand red, so the red closing scene
   hands the gesture line its own foreground instead. */
.gesture-hint {
    margin-top:70px;
    color:var(--muted)
}
.red .gesture-hint {
    color:#fff
}
.progress {
    position:fixed;
    z-index:21;
    left:26px;
    right:26px;
    bottom:20px;
    display:flex;
    align-items:center;
    gap:16px;
    color:var(--chrome-muted);
    pointer-events:none
}
.progress i {
    height:2px;
    background:var(--chrome-accent);
    width:100%;
    transform-origin:left;
    transform:scaleX(.03)
}
.progress b {
    font-weight:400;
    white-space:nowrap
}
/* The scene name rides the counter and takes the chrome's own tone, so it reads as one instrument
   with the number rather than a second label. Below 620px the number is what matters. */
.progress-name {
    font:400 10px var(--mono);
    letter-spacing:.1em;
    text-transform:uppercase;
    color:var(--chrome-muted);
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
    flex:none;
    /* Measured: the longest name in the strip is "Public Record" at 122 px inside a 24ch box, which
       tracking of .14em had already narrowed to 144 px of advance. .1em buys the room back where the
       name is the only text on the rail that grows with the content, and matches the tracking the
       card footers already use. */
    max-width:26ch
}
.progress-name:before {
    content:"·";
    margin:0 8px;
    opacity:.55
}
.progress-name:empty {
    display:none
}
@media(max-width:620px) {
    .progress-name {
        display:none
    }
}
/* The rail is the only chrome that is permanently on screen on the strip pages, and those pages have
   no footer — so it carries the two legal documents that must be reachable from every page. The rail is
   pointer-events:none so a wheel gesture over it still pans; the links opt back in. Vertical padding
   brings each target to 26px (WCAG 2.5.8) inside a 12px line. */
.progress-label {
    display:flex;
    align-items:center;
    gap:9px;
    white-space:nowrap
}
.progress-label span {
    opacity:.5
}
.progress-label a {
    pointer-events:auto;
    color:inherit;
    text-decoration:underline;
    text-underline-offset:3px;
    padding:7px 1px
}
.progress-label a:hover {
    color:var(--chrome-accent)
}
/* The rail sits over scene grounds that change tone per scene; the document-wide focus colour is
   invisible on the ink scenes, exactly like the header's was. */
.progress-label a:focus-visible {
    outline-color:var(--chrome-accent)
}
@media(prefers-reduced-motion:reduce) {
    html {
        scroll-behavior:auto
    }
    .world {
        scroll-behavior:auto
    }
}
/* The transition between scenes carries the brand idea: a scene condenses out of a frosted
   edge, block by block, and dissolves back when it is left. Single source of truth at every
   width; gated by .world.js so a script-less page stays fully opaque. */
.world.js .scene-inner {
    opacity:.34;
    translate:26px 0;
    scale:.99;
    transition:opacity .5s ease,translate .64s cubic-bezier(.2,.75,.2,1),scale .64s cubic-bezier(.2,.75,.2,1)
}
.world.js .scene.is-active .scene-inner {
    opacity:1;
    translate:0 0;
    scale:1
}
.world.js .scene.is-past .scene-inner {
    opacity:.3;
    translate:-18px 0;
    scale:.992
}
/* Both figures are absolutely positioned and centred with the `translate` property, and scene-arrive
   animates that same property with fill-mode both — a figure caught by the reveal would keep the
   animation's final `translate:0 0` and lose its centring for the rest of the session. */
.world.js .scene.is-active .scene-inner>*:not(.opening-visual):not(.constellation) {
    animation:scene-arrive .52s cubic-bezier(.2,.75,.2,1) both
}
.world.js .scene.is-active .scene-inner>:nth-child(2) {
    animation-delay:.06s
}
.world.js .scene.is-active .scene-inner>:nth-child(3) {
    animation-delay:.12s
}
.world.js .scene.is-active .scene-inner>:nth-child(4) {
    animation-delay:.18s
}
.world.js .scene.is-active .scene-inner>:nth-child(5) {
    animation-delay:.24s
}
.world.js .scene.is-active .scene-inner>:nth-child(n+6) {
    animation-delay:.3s
}
/* Die Signaturlinie: eine Haarlinie über der Inhaltssäule, die sich aufzieht, hält und in
   Leserichtung die Bühne verlässt. FORM · HOLD · PROVE · DISSOLVE als Bedienung, nicht als Text.
   Nur scaleX, Höhe konstant 1 px — das Ende flackert nie, und es gibt nichts zu verwischen. */

@keyframes scene-arrive {
    0% {
        opacity:0;
        translate:0 15px
    }
    55% {
        opacity:1;
        translate:0 0
    }
    100% {
        opacity:1;
        translate:0 0
    }
}

@media(prefers-reduced-motion:reduce) {
    html {
        scroll-behavior:auto
    }
    .world {
        scroll-behavior:auto
    }
}
/* Mobile base adjustments kept separate from the horizontal-experience block below
   (everything that block re-declares was removed as dead). */
@media(max-width:800px) {
    .phase-row>div:nth-child(even) {
        border-right:0
    }
    .phase-row>div:nth-child(n+3) {
        border-top:1px solid var(--frame)
    }
    .top {
        height:62px
    }
    .top-in {
        width:calc(100% - 32px)
    }
    .top .label,.nav a:not(.contact) {
        display:none
    }
    .world:focus {
        outline:0
    }
}
/* Mobile horizontal experience: preserve the product's left-to-right story on touch devices. */
/* Landscape phones have a phone's height on a tablet's width, so the strip is routed by height as
   well as width: below 560px the stacked, vertically scrolling layout is the only honest one. */
@media(max-width:800px),(max-height:560px) {
    html,body {
        overflow:hidden;
        height:100%;
        overscroll-behavior:none
    }
    .world {
        position:absolute;
        inset:0;
        height:100svh;
        overflow-x:auto;
        overflow-y:hidden;
        scroll-snap-type:x mandatory;
        scroll-padding:0;
        /* pan-y too: scenes whose content exceeds the viewport scroll vertically and
           touch-action inherited from this ancestor would otherwise block that. */
        touch-action:pan-x pan-y;
        overscroll-behavior-x:contain
    }
    .world-track {
        display:flex;
        width:max-content;
        height:100%
    }
    .scene {
        width:100vw;
        height:100svh;
        min-height:0;
        scroll-snap-align:start;
        scroll-snap-stop:always;
        overflow-y:auto;
        overflow-x:hidden;
        padding:94px 20px 54px;
        scroll-padding:78px 0 40px;
        align-items:flex-start
    }
    .scene-inner {
        margin:auto;
        /* The scene already carries 94px of top and 54px of bottom padding, so this air used to stack
           on top of it: 18svh - 115px on a 640px panel - of ground that pushed every call-to-action
           below the fold, where it then collided with the fixed rail. The clearance the header needs is
           already paid for by the scene's own padding. */
        padding-top:2svh;
        padding-bottom:6svh
    }
    .scene h1,.scene h2 {
        font-size:clamp(2.7rem,13vw,5.2rem)
    }
    .constellation {
        position:relative;
        right:auto;
        top:auto;
        translate:none;
        width:min(82vw,360px);
        height:min(82vw,360px);
        min-width:0;
        min-height:0;
        margin:44px auto 0
    }
    .opening-visual {
        position:relative;
        right:auto;
        top:auto;
        translate:none;
        /* Landscape phones reach this band through max-height:560px, where a 340px square cannot clear
           the rail wherever it sits in the column: there the fold, not 70vw, is the binding measure.
           The third term is that fold - rail top (100svh - 51px) minus the head the column gives the
           figure and the 22px of margin above it. It binds from 740px of window height down (210px of
           cube at 360x740, 170px at 320x700) and never touches a portrait phone at 844px and above,
           where 70vw is the smaller term. Measured clearance to the rail at scrollTop 0: 9px at
           680x900, 25px at 320x700, 58px at 390x844, 102px at 439x880. */
        width:min(70vw,340px,max(110px,calc(100svh - 530px)));
        min-width:0;
        margin:22px auto 0
    }
    /* The gesture label lies in the drawing's empty lower-left corner, and that corner only exists
       while the square is large: at 170px the same box wrapped to two lines and struck the wireframe.
       One centred line, allowed to overhang the narrow square into the scene's own 20px of side
       padding, keeps the label off the drawing at every size. */
    .opening-visual .cube-hint {
        left:50%;
        translate:-50% 0;
        bottom:6px;
        white-space:nowrap
    }
    /* The figure used to be the last block of a 626px stack: at 320x700 the copy above it alone ended
       36px above the rail, so the 224px square stood 210px too low and no cap could fit it (14px of
       cube is not a hero). Capping alone was measured and rejected for the short phones; the column
       is reordered instead. Only the lead moves, and the reason is the rail: with the figure left at
       the tail of headline+copy the call-to-action came to straddle the rail line at 390x844, which
       is the same defect one element further down. Headline, action, figure, then the paragraph - the
       paragraph is the only block on the scene that can run under the wash and read as "continues
       below". DOM order is untouched, so screen readers and Tab still get the pitch first. */
    .scene.opening:has(.opening-visual) .scene-inner {
        display:flex;
        flex-direction:column
    }
    .scene.opening:has(.opening-visual) .lead {
        order:1
    }
    .opening .cell-card,.enterprise-scene .cell-card,.scene .cell-card[style],.scene .arch[style] {
        position:relative!important;
        right:auto!important;
        top:auto!important;
        translate:none!important;
        width:100%!important;
        margin:34px 0 0
    }
    .split {
        grid-template-columns:1fr;
        gap:30px
    }
    /* A card's min-content is its widest word, and the status stamp is display type: at phone width
       "AVAILABLE" in 3.8rem made the card wider than the scene, which dragged the whole copy column
       45px off-screen to the right. Containment stops the word from sizing the card; cqi then sizes
       the word to whatever card it is given. */
    .cell-card,.proof-record {
        container-type:inline-size
    }
    .cell-card strong {
        font-size:min(3.8rem,14cqi)
    }
    .proof-record strong {
        font-size:min(4rem,14cqi)
    }
    .phase-row {
        grid-template-columns:1fr 1fr;
        margin-top:30px
    }
    .phase-row>div {
        min-height:160px;
        padding:14px
    }
    .phase-row strong {
        margin-top:28px;
        font-size:1.05rem
    }
    .cell-facts {
        margin-top:30px
    }
    .comparison {
        grid-template-columns:1fr;
        gap:10px;
        margin-top:30px
    }
    .compare-card {
        min-height:0;
        padding:17px
    }
    .compare-card h3 {
        margin:25px 0 10px
    }
    .terminal {
        font-size:10px;
        overflow:auto
    }
    .progress {
        left:14px;
        right:14px;
        bottom:10px
    }
    /* A scroll container clips at its padding box, so the bottom padding is no protection: whenever a
       stacked scene overflows - which is most of them - its copy runs on into the band the fixed rail
       sits in, and the 2px line strikes through headlines, cards and call-to-action buttons. The rail
       carries the only live information on the page, so it is the ground that comes to it: a wash in the
       centred scene's own tone that fades the tail out and reads as "the column continues below".
       Masked solid colour rather than a gradient, because background-colour can transition with the
       re-tone and a gradient image cannot. */
    .progress:before {
        content:"";
        position:absolute;
        left:-14px;
        right:-14px;
        bottom:-10px;
        height:56px;
        z-index:-1;
        pointer-events:none;
        background:var(--chrome-bg);
        transition:background-color .5s;
        -webkit-mask-image:linear-gradient(to top,#000 54%,transparent);
        mask-image:linear-gradient(to top,#000 54%,transparent)
    }
    .progress-label {
        font-size:10px
    }
    /* The wash is the ground the rail needs, never a lid over a call to action: raised above the fixed
       row, a button punches through the fade and the rail reads as passing behind it instead of cutting
       across its label. The header sits at 40, so scrolling copy still slides under the glass bar. */
    .actions {
        position:relative;
        z-index:25
    }
    .progress b {
        font-size:10px
    }
}
/* mobile nav disclosure (single source: existing .nav links, native checkbox, zero JS) */
.nav-toggle {
    position:absolute;
    opacity:0;
    width:1px;
    height:1px;
    pointer-events:none
}
.nav-ham {
    display:none
}
.nav-toggle:focus-visible+.nav-ham {
    outline:2px solid var(--chrome-accent);
    outline-offset:2px
}
@media(min-width:801px) {
    .nav-toggle,.nav-ham {
        display:none!important
    }
}
@media(max-width:800px) {
    /* Two boxed objects of equal weight made the bar read as a form field: an outlined "Talk to us"
       next to an outlined ≡. Now the call-to-action is the only filled thing in the bar, and the
       disclosure is three bare hairlines that fold into a cross over the drawer. */
    .top-in {
        position:relative;
        gap:12px
    }
    .nav {
        order:3;
        margin-left:auto;
        padding-right:46px
    }
    .nav .contact {
        background:var(--chrome-ink);
        border-color:var(--chrome-ink);
        color:var(--chrome-bg)
    }
    .nav-ham {
        display:inline-flex;
        flex-direction:column;
        align-items:flex-start;
        justify-content:center;
        gap:5px;
        position:absolute;
        right:2px;
        top:50%;
        translate:0 -50%;
        min-width:44px;
        min-height:44px;
        padding:0 12px;
        background:none;
        border:0;
        cursor:pointer;
        z-index:30
    }
    .nav-ham i {
        display:block;
        width:20px;
        height:1.5px;
        background:var(--chrome-ink);
        transition:translate .26s cubic-bezier(.4,0,.2,1),rotate .26s cubic-bezier(.4,0,.2,1),opacity .16s ease,width .26s ease
    }
    .nav-ham i:nth-child(2) {
        width:13px
    }
    .top-in:has(.nav-toggle:checked) .nav-ham i {
        width:20px
    }
    .top-in:has(.nav-toggle:checked) .nav-ham i:nth-child(1) {
        translate:0 6.5px;
        rotate:45deg
    }
    .top-in:has(.nav-toggle:checked) .nav-ham i:nth-child(2) {
        opacity:0
    }
    .top-in:has(.nav-toggle:checked) .nav-ham i:nth-child(3) {
        translate:0 -6.5px;
        rotate:-45deg
    }
    @media(prefers-reduced-motion:reduce) {
        .nav-ham i {
            transition:none
        }
    }
    .top-in:has(.nav-toggle:checked) .nav {
        position:absolute;
        top:62px;
        left:0;
        right:0;
        display:flex!important;
        flex-direction:column;
        /* Inherited align-items:center made every row shrink to its word, so "Cell" was a 23px-wide
           target - under the 24px minimum - and the row rules were six floating segments instead of
           one column. Stretch gives each row the full width and the rule its full measure. */
        align-items:stretch;
        gap:0;
        margin:0;
        padding:6px 24px 18px;
        background:var(--paper);
        border-bottom:1px solid var(--rule);
        box-shadow:0 14px 24px rgba(23,23,18,.12);
        z-index:25
    }
    .top-in:has(.nav-toggle:checked) .nav a {
        display:block!important;
        padding:15px 0;
        border-bottom:1px solid var(--rule);
        font-size:13px;
        color:var(--ink)
    }
    .top-in:has(.nav-toggle:checked) .nav .contact {
        margin:16px 0 2px;
        padding:13px 14px;
        text-align:center;
        border:1px solid var(--ink);
        background:var(--ink);
        color:var(--paper)
    }
    .progress {
        display:flex!important
    }
}
@media(prefers-reduced-motion:reduce) {
    .world .scene-inner {
        opacity:1!important;
        translate:none!important;
        scale:none!important;
        transition:none!important
    }
    .world .scene.is-active .scene-inner>* {
        animation:none!important
    }
    .bar-track i {
        animation:none!important
    }
}
/* The terminal vignette is a 12s loop; it has no business painting while its scene is off the
   strip. Only the centred scene runs it. */
.world.js .scene:not(.is-active) :is(.vt-window,.vt-type,.vt-caret,.vt-src,.vt-done,.vt-w1,.vt-w2,.vt-w3,.vt-stamp,.as-flow,.as-pulse,.caret,.term-line,.term-type,.bar-track i) {
    animation-play-state:paused
}
/* The film strip drives the fixed chrome: whichever scene is centred sets the tint of the glass
   frame and the colour of the progress rail. Transitions make the re-tone part of the pan. */
body:has(.scene.dark.is-active) {
    --chrome-glass:rgba(23,23,18,.74);
    --chrome-edge:rgba(242,241,236,.14);
    --chrome-inner:rgba(242,241,236,.05);
    --chrome-chroma:rgba(240,74,60,.3);
    --chrome-ink:var(--paper);
    --chrome-muted:#a29e92;
    --chrome-rule:#57554a;
    --chrome-bg:var(--ink);
    --chrome-accent:var(--red2);
    --chrome-ember:var(--red2)
}
body:has(.scene.red.is-active) {
    --chrome-glass:rgba(216,41,28,.74);
    --chrome-edge:rgba(255,255,255,.3);
    --chrome-inner:rgba(23,23,18,.08);
    --chrome-chroma:rgba(23,23,18,.22);
    --chrome-ink:#fff;
    --chrome-muted:#fff;
    --chrome-rule:rgba(23,23,18,.3);
    --chrome-bg:var(--red);
    --chrome-accent:var(--ink);
    --chrome-ember:var(--ink)
}
/* Touch has no hover to leave behind. */
@media(hover:none) and (pointer:coarse) {
    .btn:hover {
        transform:none;
        box-shadow:none
    }
    .nav a:hover {
        color:inherit
    }
}
/* Landscape phones and short split-screen windows: the stacked layout has ~240px of usable
   content height, so the vertical furniture comes off and multi-column blocks stay side by side
   as long as there is width to spare. */
@media(max-height:560px) {
    .scene {
        padding:78px 24px 40px
    }
    .scene.opening {
        padding-top:78px
    }
    .scene-inner {
        padding-top:0;
        padding-bottom:0
    }
    .scene h1,.scene h2 {
        font-size:clamp(1.85rem,7.4vh,2.9rem)
    }
    .scene .eyebrow {
        margin-bottom:10px
    }
    .scene .lead {
        margin-top:12px;
        font-size:.95rem
    }
    .actions {
        margin-top:14px
    }
    .btn {
        padding:11px 18px;
        font-size:14px
    }
    /* The rail label repeats what the header already names, and it is the element that collides
       with scene content on a 390px-tall viewport. */
    .progress-label {
        display:none
    }
    /* Same reason: the rail runs through the call-to-action row, which is the only thing a hand is
       aiming at here. The counter stays - it is the one piece of furniture that carries information,
       and it holds the right edge once the rail is gone. */
    .progress i {
        display:none
    }
    /* Only the counter needs ground here, and a 390px-tall panel cannot spare 56px of fade. */
    .progress:before {
        height:26px
    }
    .progress b {
        margin-left:auto
    }
    /* Six drawer rows have to fit above a 328px-tall viewport; the row padding is what gives, and
       33px still clears the 24px minimum target. */
    .top-in:has(.nav-toggle:checked) .nav a {
        padding:10px 0
    }
}
@media(max-height:560px) and (min-width:700px) {
    .split,.comparison {
        grid-template-columns:1fr 1fr
    }
    .phase-row {
        grid-template-columns:repeat(4,1fr)
    }
    /* A landscape phone has tablet width: the hero keeps its side-by-side composition instead of
       queueing the cube under the copy, which would double an already scarce height. */
    .scene.opening {
        align-items:flex-start
    }
    .scene.opening .scene-inner {
        padding:0
    }
    .opening-visual {
        position:absolute;
        right:0;
        top:50%;
        translate:0 -50%;
        width:min(34vh,250px);
        margin:0
    }
    .scene.opening .scene-inner>*:not(.opening-visual) {
        max-width:calc(100% - min(34vh,250px) - 24px)
    }
}

/* Windows High Contrast: was die Site in Farbe trägt, muss als Linie überleben. */
@media(forced-colors:active) {
    .brand i,.eyebrow:before,.rail-stop.is-done:before,.term-dots i {
        forced-color-adjust:none
    }
    .rail-stop.is-open:before {
        border-style:dashed
    }
    .btn.primary,.cell-card,.proof-record,.sky-window {
        border:1px solid ButtonText
    }
    .progress i,.rail-line:before,.rule {
        background:CanvasText
    }
    .scene-inner.is-active :is(h1,h2) em,.lead b {
        forced-color-adjust:none
    }
}

/* Zweizeilige Sektionstitel brechen jetzt gleichmäßig; die Hero-Headlines behalten ihre gesetzten
   Umbrüche, weil dort der Zeilenumbruch die Aussage ist. */
.scene h2 {
    text-wrap:balance
}
.eyebrow:before {
    margin-top:.3em
}
