/* ==========================================================================
   Lojic Loop — home (single-page)
   Minimalist: tinted ground, black type, purple used sparingly as the accent.
   ========================================================================== */

/* Buttons ----------------------------------------------------------------- */

.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    padding: 1.05rem 1.9rem;
    border-radius: 100px;
    font-size: var(--step-0);
    font-weight: 500;
    letter-spacing: .01em;
    overflow: hidden;
    transition: transform var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease-soft);
}

.btn svg {
    transition: transform .42s var(--ease);
}

.btn:hover svg {
    transform: translateX(4px);
}

.btn-lg {
    padding: 1.1rem 2rem;
    font-size: var(--step-0);
}

.btn-primary {
    background: var(--purple);
    color: var(--white);
    box-shadow: 0 8px 22px var(--purple-24);
}

.btn-primary::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--black);
    transform: translate3d(0, 101%, 0);
    transition: transform .52s var(--ease);
}

.btn-primary > * {
    position: relative;
    z-index: 1;
}

.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 14px 34px var(--purple-24);
}

.btn-primary:hover::before {
    transform: none;
}

.btn-ghost {
    color: var(--ink-80);
    background: var(--white);
    box-shadow: inset 0 0 0 1.5px var(--ink-14);
}

.btn-ghost:hover {
    color: var(--purple);
    transform: translateY(-2px);
    box-shadow: inset 0 0 0 1.5px var(--purple), 0 10px 24px rgba(0, 0, 0, .06);
}

/* ── Hero ───────────────────────────────────────────────────────────────── */

.hero {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 100svh;
    padding-block: 9rem 5rem;
    overflow: hidden;
}

.hero-glow {
    position: absolute;
    top: -22vh;
    left: 50%;
    width: min(105vw, 1180px);
    aspect-ratio: 1;
    transform: translateX(-50%);
    background: radial-gradient(circle at 50% 50%, var(--purple-12), transparent 62%);
    pointer-events: none;
    animation: ll-drift 13s var(--ease-soft) infinite;
}

.hero-inner {
    position: relative;
    z-index: 1;
}

.hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    margin: 0 0 clamp(1.6rem, 4vh, 2.6rem);
    padding: .58rem 1.05rem .58rem .9rem;
    border-radius: 100px;
    box-shadow: inset 0 0 0 1px var(--purple-24);
    background: var(--white);
    font-size: var(--step--1);
    font-weight: 500;
    letter-spacing: .03em;
    color: var(--ink-80);
    cursor: pointer;

    /* Mostly at rest — a 5s cycle that only moves for a fraction of a second.
       Enough to catch the eye on a static hero, not enough to nag. */
    animation: ll-boop 5s var(--ease) 2.4s infinite;
    transition: box-shadow var(--dur-fast) var(--ease-soft),
                transform var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease-soft);
}

.hero-eyebrow:hover,
.hero-eyebrow:focus-visible {
    animation-play-state: paused;
    transform: translateY(-2px);
    color: var(--purple);
    box-shadow: inset 0 0 0 1px var(--purple), 0 10px 26px var(--purple-24);
}

.hero-eyebrow-go {
    opacity: 0;
    margin-left: -.35rem;
    transform: translateX(-4px);
    transition: opacity var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease),
                margin-left var(--dur-fast) var(--ease);
}

.hero-eyebrow:hover .hero-eyebrow-go,
.hero-eyebrow:focus-visible .hero-eyebrow-go {
    opacity: 1;
    margin-left: 0;
    transform: none;
}

.hero-eyebrow .dot {
    position: relative;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--purple);
}

.hero-eyebrow .dot::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--purple);
    animation: ll-pulse-ring 2.4s var(--ease-soft) infinite;
}

.hero-title {
    max-width: 15ch;
    font-size: var(--step-4);
    /* 200 read as elegant-but-anaemic at display size; 300 keeps the airiness
       while giving the strokes something to stand on. */
    font-weight: 300;
    letter-spacing: -.045em;
    line-height: .98;
}

.hero-title em {
    font-style: normal;
    font-weight: 400;
}

.hero-lede {
    max-width: 52ch;
    margin-top: clamp(1.6rem, 3.5vh, 2.4rem);
    font-size: var(--step-1);
    font-weight: 300;
    line-height: 1.55;
    color: var(--ink-80);
}

.hero-lede strong {
    font-weight: 500;
    color: var(--purple);
}

.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .8rem;
    margin-top: clamp(2rem, 4.5vh, 3rem);
}

.hero-pills {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    margin: clamp(2.4rem, 5.5vh, 3.8rem) 0 0;
    padding: 0;
    list-style: none;
    font-size: var(--step-0);
}

.hero-pills li {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    padding: .5rem 1rem .5rem .62rem;
    border-radius: 100px;
    background: var(--purple-12);
    box-shadow: inset 0 0 0 1px transparent;
    font-weight: 500;
    letter-spacing: -.005em;
    color: var(--ink-80);
    transition: background var(--dur-fast) var(--ease-soft),
                box-shadow var(--dur-fast) var(--ease-soft),
                transform var(--dur-fast) var(--ease);
}

.hero-pills li:hover {
    background: var(--white);
    box-shadow: inset 0 0 0 1px var(--purple-24);
    transform: translateY(-2px);
}

/* Emoji ignore `color`, so presence has to come from size and a chip behind
   them rather than from a darker ink. */
.hero-pill-ico {
    font-size: 1.32em;
    line-height: 1;
    filter: saturate(1.15);
}

.hero-scroll {
    position: absolute;
    left: 50%;
    bottom: 2.2rem;
    width: 1px;
    height: 54px;
    overflow: hidden;
    transform: translateX(-50%);
}

.hero-scroll-line {
    display: block;
    width: 1px;
    height: 100%;
    background: linear-gradient(to bottom, transparent, var(--ink-40));
    animation: ll-scroll-hint 2.3s var(--ease-soft) infinite;
}

@keyframes ll-scroll-hint {
    0% { transform: translateY(-100%); }
    60%, 100% { transform: translateY(100%); }
}

/* ── Clients — logo wall ────────────────────────────────────────────────── */

.clients {
    padding-block: clamp(3rem, 6vh, 4.6rem);
    background: var(--white);
    border-block: 1px solid var(--ink-08);
}

.clients-h {
    display: block;
    margin-bottom: clamp(1.4rem, 3vh, 2.2rem);
    font-size: var(--step-2);
    font-weight: 300;
    letter-spacing: -.03em;
    text-transform: none;
    color: var(--ink);
}

/* Two-tone, matching the hero headline: black lead, purple emphasis.
   .accent supplies the purple; the weight bump keeps it from looking lighter
   than the black word beside it, since purple reads recessive next to black. */
.clients-h .accent {
    font-weight: 400;
}

.clients-panel {
    overflow: hidden;
}

.marquee {
    position: relative;
    overflow: hidden;

    /* One cell height for every logo, so the row has a single baseline. */
    --cell-h: clamp(88px, 6vw + 52px, 128px);
    --cell-w: clamp(148px, 15vw + 40px, 232px);
}

.marquee-track {
    display: flex;
    width: max-content;
    animation: ll-marquee 42s linear infinite;
}

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

.marquee-row {
    display: flex;
    align-items: center;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* EQUAL cells. Sizing each cell to its own logo is what produced the mess of
   wildly different column widths; a logo wall reads as deliberate only when the
   grid is regular and the artwork varies inside it. */
.client {
    display: grid;
    place-items: center;
    flex: 0 0 var(--cell-w);
    height: var(--cell-h);
    padding-inline: clamp(1rem, 2.2vw, 2rem);
}

/* Contained on both axes, so a wide wordmark is limited by width and a tall
   emblem by height — neither can overflow its cell or crowd its neighbour.
   --k is a small per-logo optical nudge, never a licence to break the grid. */
.client-logo {
    max-width: 100%;
    max-height: calc(var(--cell-h) * .46 * var(--k, 1));
    width: auto;
    height: auto;
    object-fit: contain;
    transition: transform .42s var(--ease);
}

.client:hover .client-logo {
    transform: scale(1.05);
}

.client-type {
    font-size: calc(var(--cell-h) * .17);
    font-weight: 500;
    letter-spacing: -.02em;
    text-align: center;
    text-wrap: balance;
    color: var(--ink-56);
    transition: color .42s var(--ease-soft);
}

.client:hover .client-type {
    color: var(--purple);
}

.marquee-fade {
    position: absolute;
    top: 0;
    bottom: 0;
    /* Wide enough that a logo leaving the row dissolves rather than looking
       chopped off at a hard edge. */
    width: clamp(52px, 8vw, 128px);
    pointer-events: none;
    z-index: 1;
}

.marquee-fade-l {
    left: 0;
    background: linear-gradient(to right, var(--white), rgba(255, 255, 255, 0));
}

.marquee-fade-r {
    right: 0;
    background: linear-gradient(to left, var(--white), rgba(255, 255, 255, 0));
}

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

    .marquee {
        overflow-x: auto;
        scrollbar-width: none;
    }

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

/* ── Capabilities ───────────────────────────────────────────── */

/* Deliberately a different object from the What-we-build showcase, so the page
   does not read as the same component twice: four stages side by side with
   nothing hidden, all four visuals running at once, joined on a hairline grid
   rather than raised on one white card. A spotlight walks the row; hovering
   pins it. Nothing is concealed, so there is nothing to click and no fake tab
   stops for a keyboard user to wade through. */

.caps-h {
    max-width: 18ch;
    font-size: var(--step-3);
    font-weight: 200;
    margin-bottom: clamp(2.6rem, 6vh, 4.2rem);
}

.caps-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1px;
    background: var(--ink-08);
    border: 1px solid var(--ink-08);
}

.cap {
    position: relative;
    display: grid;
    align-content: start;
    padding: clamp(1.5rem, 2.8vw, 2.2rem);
    background: var(--white);
    transition: background var(--dur-fast) var(--ease-soft);
}

/* The spotlight's mark. Fills left to right as the spotlight arrives, so the
   row reads as a progression rather than four independent tiles. */
.cap::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 2px;
    background: var(--purple);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .55s var(--ease);
}

.cap.is-on {
    background: var(--ground);
}

.cap.is-on::after {
    transform: scaleX(1);
}

.cap-viz {
    display: block;
    width: 100%;
    max-width: 210px;
    margin-bottom: clamp(1.1rem, 2.4vh, 1.7rem);
}

.cviz {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.cap-num {
    display: block;
    margin-bottom: .7rem;
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: .1em;
    color: var(--ink-14);
    transition: color var(--dur-fast) var(--ease-soft);
}

.cap.is-on .cap-num {
    color: var(--purple);
}

.cap-t {
    margin-bottom: .7rem;
    font-size: var(--step-2);
    font-weight: 300;
    letter-spacing: -.02em;
}

.cap-b {
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.62;
    color: var(--ink-56);
}

@media (min-width: 620px) {
    .caps-grid { grid-template-columns: repeat(2, 1fr); }
}

/* 4-up only once each cell clears ~275px; below that the titles wrap badly. */
@media (min-width: 1180px) {
    .caps-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ── Shared marks ───────────────────────────────────────────── */

.cviz path,
.cviz rect,
.cviz circle,
.cviz line {
    stroke-linecap: round;
    stroke-linejoin: round;
}

.c-box { fill: none; stroke: var(--ink-14); stroke-width: 2; }
.c-axis { stroke: var(--ink-14); stroke-width: 1.5; }

.c-lab {
    font-size: 8px;
    font-weight: 400;
    letter-spacing: .04em;
    fill: var(--ink-40);
}

.c-lab.is-end { text-anchor: end; }

/* 01 — three sources converging into one live picture */
.c1-src {
    fill: none;
    stroke: var(--ink-14);
    stroke-width: 2;
    transform-box: view-box;
    animation: ll-c-gather 5.4s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.c1-panel rect { fill: none; stroke: var(--purple); stroke-width: 2; }

.c1-trace {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-dasharray: 70;
    animation: ll-c-trace 5.4s var(--ease-soft) infinite;
}

/* 02 — the hand-off disappearing and work flowing straight through */
.c2-op {
    fill: none;
    stroke: var(--ink-14);
    stroke-width: 2;
    transform-box: view-box;
    transform-origin: 80px 52px;
    animation: ll-c-leave 5s var(--ease-soft) infinite;
}

.c2-hop {
    stroke: var(--ink-14);
    stroke-width: 2;
    stroke-dasharray: 3 4;
    animation: ll-c-leave 5s var(--ease-soft) infinite;
}

.c2-drop { stroke: var(--purple-24); stroke-width: 2; }

.c2-direct {
    stroke: var(--purple);
    stroke-width: 2.4;
    stroke-dasharray: 110;
    animation: ll-c-close 5s var(--ease-soft) infinite;
}

.c2-token {
    fill: var(--purple);
    offset-path: path("M25 78H135");
    offset-distance: 0%;
    animation: ll-c-flow 5s var(--ease-soft) infinite;
}

/* 03 — hidden costs surfaced, then engineered out */
.c3-bar {
    fill: var(--purple-24);
    transform-box: view-box;
    transform-origin: 0 92px;             /* every bar shares the axis */
    animation: ll-c-cost 5.6s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.c3-total {
    transform-box: view-box;
    transform-origin: 0 34px;
    animation: ll-c-total 5.6s var(--ease-soft) infinite;
}

.c3-total line { stroke: var(--purple); stroke-width: 2; stroke-dasharray: 5 4; }

/* 04 — the archive fanning forward */
.c4-layer {
    fill: none;
    stroke: var(--ink-14);
    stroke-width: 2;
    transform-box: view-box;
    transform-origin: 33px 55px;
    animation: ll-c-stack 5.2s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.c4-beam path {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-dasharray: 90;
}

.c4-beam circle {
    fill: var(--purple);
    /* Held back until its own beam has finished drawing, so the target appears
       on arrival rather than waiting there for it. */
    animation: ll-c-tip 5.2s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.c4-beam {
    animation: ll-c-beam 5.2s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

@media (prefers-reduced-motion: reduce) {

    .cap::after { transition: none; }

    .c1-src,
    .c1-trace,
    .c2-op,
    .c2-hop,
    .c2-direct,
    .c2-token,
    .c3-bar,
    .c3-total,
    .c4-layer,
    .c4-beam,
    .c4-beam circle {
        animation: none;
    }

    /* Frozen on the frame that carries each argument, not on an empty one. */
    .c1-trace { stroke-dashoffset: 0; }
    .c1-src { opacity: .35; }
    .c2-op { opacity: .2; }
    .c2-hop { opacity: .2; }
    .c2-direct { stroke-dashoffset: 0; }
    .c2-token { offset-distance: 62%; }
    .c3-bar { transform: scaleY(.34); }
    .c3-total { transform: translateY(38px); }
    .c4-beam { opacity: 1; }
    .c4-beam path { stroke-dashoffset: 0; }
}

/* ── What we build (auto-playing showcase) ──────────────────────────────── */

/* The dwell is declared once here and read back by the script, so the progress
   bar and the auto-advance cannot drift apart. */
.work-stage {
    --work-dwell: 6.5s;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.6rem, 3.5vw, 2.8rem);
    margin-top: clamp(2.4rem, 5.5vh, 3.6rem);
}

@media (min-width: 900px) {
    .work-stage {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.12fr);
        align-items: center;              /* the list is taller than the screen;
                                             centring keeps the pair balanced */
    }
}

.work-h {
    font-size: var(--step-3);
    font-weight: 200;
}

/* ── The list ───────────────────────────────────────────────────────────── */

.work-list {
    display: grid;
    align-content: start;
}

.work-item {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: .9rem;
    width: 100%;
    padding: clamp(.85rem, 1.8vh, 1.15rem) 0;
    border: 0;
    border-top: 1px solid var(--ink-08);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur-fast) var(--ease-soft);
}

.work-item:last-of-type {
    border-bottom: 1px solid var(--ink-08);
}

.work-item:focus-visible {
    outline: 2px solid var(--purple);
    outline-offset: 3px;
}

.work-num {
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: .12em;
    color: var(--ink-14);
    transition: color var(--dur-fast) var(--ease-soft);
}

.work-item[aria-selected="true"] .work-num,
.work-item:hover .work-num {
    color: var(--purple);
}

.work-item-copy {
    display: grid;
    gap: .18rem;
}

.work-tag {
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--ink-40);
    transition: color var(--dur-fast) var(--ease-soft);
}

.work-item[aria-selected="true"] .work-tag {
    color: var(--purple);
}

.work-name {
    font-size: var(--step-1);
    font-weight: 300;
    letter-spacing: -.02em;
    line-height: 1.2;
    color: var(--ink-56);
    transition: color var(--dur-fast) var(--ease-soft), transform var(--dur-fast) var(--ease);
}

.work-item[aria-selected="true"] .work-name {
    font-weight: 400;
    color: var(--ink);
}

.work-item:hover .work-name {
    color: var(--ink);
    transform: translateX(3px);
}

/* The bar doubles as the clock: it fills over exactly one dwell, so the next
   move is always visible before it happens. Declared only on the selected
   row, which is what restarts it on every advance. */
.work-bar {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    overflow: hidden;
}

.work-bar i {
    display: block;
    width: 100%;
    height: 100%;
    background: var(--purple);
    transform: scaleX(0);
    transform-origin: left center;
}

.work-item[aria-selected="true"] .work-bar i {
    animation: ll-w-fill var(--work-dwell) linear forwards;
}

/* ── The screen ─────────────────────────────────────────────────────────── */

.work-screen {
    position: relative;
    padding: clamp(1.1rem, 2.4vw, 1.7rem);
    padding-top: clamp(1.6rem, 3vw, 2.2rem);
    border-radius: var(--radius-lg);
    background: var(--white);
    box-shadow: inset 0 0 0 1px var(--ink-08), 0 20px 50px rgba(0, 0, 0, .05);
}

/* A hairline along the top edge, so the white card reads as a screen rather
   than as another content block. */
.work-screen-bar {
    position: absolute;
    top: 0;
    left: clamp(1.1rem, 2.4vw, 1.7rem);
    right: clamp(1.1rem, 2.4vw, 1.7rem);
    height: 2px;
    border-radius: 0 0 2px 2px;
    background: linear-gradient(90deg, var(--purple), var(--purple-24));
}

/* Author display beats the user agent's [hidden] rule, so this is required or
   all six panels stack on top of each other. */
.work-panel[hidden] {
    display: none;
}

.work-panel {
    display: grid;
    gap: clamp(.9rem, 2vh, 1.3rem);
    animation: ll-w-panel .45s var(--ease) both;
}

.work-panel:focus-visible {
    outline: 2px solid var(--purple);
    outline-offset: 6px;
    border-radius: var(--radius);
}

.work-desc {
    margin: 0;
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.6;
    color: var(--ink-56);
}

.viz {
    display: block;
    width: 100%;
    height: auto;
}

/* ── Shared visual language across the six ──────────────────────────────── */

.w-lab {
    font-size: 9px;
    font-weight: 400;
    letter-spacing: .04em;
    fill: var(--ink-40);
}

.w-lab.is-mid { text-anchor: middle; }
.w-lab.is-end { text-anchor: end; }

.w-big {
    font-size: 17px;
    font-weight: 300;
    text-anchor: middle;
    fill: var(--ink);
}

.w-axis { stroke: var(--ink-14); stroke-width: 1; }

/* 01 — production monitoring */
.w-chip { fill: var(--purple); animation: ll-w-blink 2.4s var(--ease-soft) infinite; animation-delay: var(--d, 0s); }
.w-chip.is-idle { fill: var(--ink-14); animation: none; }

.viz-bars rect {
    fill: var(--purple-24);
    transform-box: view-box;
    transform-origin: 0 150px;            /* every bar shares the chart floor */
    animation: ll-w-bar 4.4s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.viz-bars rect.is-lost {
    fill: none;
    stroke: var(--purple);
    stroke-width: 1.5;
    stroke-dasharray: 3 3;
}

.w-track { stroke: var(--ink-08); stroke-width: 7; }

.w-arc {
    stroke: var(--purple);
    stroke-width: 7;
    stroke-linecap: round;
    stroke-dasharray: 213.6;
    stroke-dashoffset: 213.6;
    transform-box: view-box;
    transform-origin: 258px 96px;
    transform: rotate(-90deg);
    animation: ll-w-ring 4.4s var(--ease-soft) infinite;
}

/* 02 — predictive maintenance */
.w-thresh { stroke: var(--ink-14); stroke-width: 1.5; stroke-dasharray: 5 5; }

.w-trace {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 340;
    animation: ll-w-trace 5.2s var(--ease-soft) infinite;
}

.w-flag { transform-box: view-box; transform-origin: 196px 66px; animation: ll-w-flag 5.2s var(--ease-soft) infinite; }
.w-flag-ring { fill: var(--purple); opacity: .16; }
.w-flag-dot { fill: var(--purple); }

/* 03 — inspection and traceability */
.w-belt { stroke: var(--ink-14); stroke-width: 1.5; }
.w-station circle { fill: none; stroke: var(--ink-14); stroke-width: 1.5; }
.w-station circle.is-core { fill: var(--ink-14); stroke: none; }
.w-scan path { stroke: var(--purple); stroke-width: 2; stroke-linecap: round; }
.w-scan-beam { stroke: var(--purple); stroke-width: 1.5; opacity: .3; animation: ll-w-scan 1.8s var(--ease-soft) infinite; }

.w-unit {
    fill: var(--purple-24);
    transform-box: view-box;
    animation: ll-w-belt 4.5s linear infinite;
    animation-delay: var(--d, 0s);
}

.w-unit.is-flagged { fill: none; stroke: var(--purple); stroke-width: 2; }

.w-back {
    stroke: var(--purple);
    stroke-width: 1.5;
    stroke-dasharray: 4 4;
    opacity: 0;
    animation: ll-w-back 4.5s var(--ease-soft) infinite;
    animation-delay: 1.8s;
}

/* 04 — warehouse and inventory */
.w-bin-box { fill: none; stroke: var(--ink-14); stroke-width: 1.5; }

.w-bin-fill {
    fill: var(--purple-24);
    transform-box: view-box;
    transform-origin: 0 122px;            /* all bins share a floor */
    animation: ll-w-stock 4.6s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.w-crate { fill: var(--purple); animation: ll-w-move 4.6s var(--ease-soft) infinite; }
.w-crate rect { fill: var(--purple); }
.w-match-ring { fill: none; stroke: var(--purple-24); stroke-width: 2; }
.w-match-tick {
    fill: none;
    stroke: var(--purple);
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 30;
    animation: ll-w-tick 4.6s var(--ease-soft) infinite;
}

/* 05 — demand forecasting */
.w-hist {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 190;
    animation: ll-w-hist 5s var(--ease-soft) infinite;
}

.w-fore {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-dasharray: 6 5;
    opacity: 0;
    animation: ll-w-fore 5s var(--ease-soft) infinite;
}

.w-band {
    fill: var(--purple);
    opacity: 0;
    transform-box: view-box;
    transform-origin: 170px 84px;
    animation: ll-w-band 5s var(--ease-soft) infinite;
}

.w-pivot { fill: var(--purple); }
.w-split { stroke: var(--ink-08); stroke-width: 1; }

/* 06 — machine and ERP connectivity */
.w-node rect { fill: var(--white); stroke: var(--ink-14); stroke-width: 1.5; }
.w-node-t { font-size: 11px; font-weight: 400; text-anchor: middle; fill: var(--ink-56); }

.w-link {
    stroke: var(--purple-24);
    stroke-width: 1.5;
    stroke-dasharray: 300;
    animation: ll-w-link 4.8s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.w-pkt {
    fill: var(--purple);
    offset-path: path("M58 106H262");
    offset-distance: 0%;
    animation: ll-w-pkt 3.3s linear infinite;
    animation-delay: var(--d, 0s);
}

/* ── Stacked ────────────────────────────────────────────────────────────── */

@media (max-width: 899px) {
    /* Screen first: the visual is the reason to stop scrolling, and the list
       reads as its index rather than as a menu to work through. */
    .work-screen { order: -1; }
    .work-name { font-size: var(--step-0); }
}

@media (prefers-reduced-motion: reduce) {

    .work-bar i { animation: none; transform: scaleX(1); }
    .work-panel { animation: none; }

    .w-chip,
    .viz-bars rect,
    .w-arc,
    .w-trace,
    .w-flag,
    .w-scan-beam,
    .w-unit,
    .w-back,
    .w-bin-fill,
    .w-crate,
    .w-match-tick,
    .w-hist,
    .w-fore,
    .w-band,
    .w-link,
    .w-pkt {
        animation: none;
    }

    /* Frozen on the frame each visual is arguing for, not on an empty one. */
    .viz-bars rect { transform: scaleY(1); }
    .w-arc { stroke-dashoffset: 47; }
    .w-trace { stroke-dashoffset: 0; }
    .w-back { opacity: 1; }
    .w-hist { stroke-dashoffset: 0; }
    .w-fore { opacity: 1; }
    .w-band { opacity: .1; }
    .w-link { stroke-dashoffset: 0; }
    .w-pkt { offset-distance: 55%; }
    .w-match-tick { stroke-dashoffset: 0; }
    .w-unit { transform: translateX(150px); }
}

/* ── Industry 5.0 (dark) ────────────────────────────────────────────────── */

.industry {
    background: var(--black);
    color: var(--white);
}

.eyebrow-light {
    color: var(--paper-56);
}

/* The base .eyebrow dash is purple, which disappears on the purple ground. */
.eyebrow-light::before {
    background: var(--paper-56);
}

.accent-light {
    color: var(--paper-56);
}

.industry-h {
    max-width: 24ch;
    font-size: var(--step-3);
    font-weight: 200;
}

.industry-b {
    max-width: 58ch;
    margin-top: 1.8rem;
    font-size: var(--step-1);
    font-weight: 200;
    line-height: 1.6;
    color: var(--paper-56);
}

.industry-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(1.8rem, 4vw, 3.4rem);
    margin: clamp(3.4rem, 8vh, 5.6rem) 0 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 840px) {
    .industry-list {
        grid-template-columns: repeat(3, 1fr);
    }
}

.industry-list h3 {
    margin-bottom: .7rem;
    font-size: var(--step-1);
    font-weight: 400;
}

.industry-list p {
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.6;
    color: var(--paper-56);
}

/* ── Industry visuals ───────────────────────────────────────── */

/* Same spotlight as Capabilities, on the dark ground. Purple carries the
   graphics only: #6e38cf on #000 is 3.14:1, which clears the 3:1 bar for a UI
   graphic but not the 4.5:1 bar for text, so every label here is --paper-*. */

.industry-list li {
    position: relative;
    padding-top: 1.5rem;
    border-top: 1px solid var(--paper-14);
    transition: opacity var(--dur-fast) var(--ease-soft);
}

/* The spotlight's mark, sitting on the existing hairline. */
.industry-list li::after {
    content: "";
    position: absolute;
    left: 0;
    top: -1px;
    width: 100%;
    height: 2px;
    background: var(--purple);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform .55s var(--ease);
}

.industry-list li.is-on::after {
    transform: scaleX(1);
}

.industry-list li.is-on h3 {
    color: var(--white);
}

.ind-viz {
    display: block;
    width: 100%;
    max-width: 210px;
    margin-bottom: clamp(1rem, 2.2vh, 1.5rem);
}

.iviz {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

.iviz path,
.iviz rect,
.iviz circle,
.iviz line {
    stroke-linecap: round;
    stroke-linejoin: round;
}

.i-lab {
    font-size: 8px;
    font-weight: 400;
    letter-spacing: .04em;
    fill: var(--paper-56);
}

.i-lab.is-mid { text-anchor: middle; }

/* 01 — the system re-forming around the operator, who never moves */
.i1-panel {
    fill: none;
    stroke: var(--paper-24);
    stroke-width: 2;
    transform-box: view-box;
    animation: ll-i-surround 5.6s var(--ease) infinite;
    animation-delay: var(--d, 0s);
}

.i1-person {
    stroke: var(--purple);
    stroke-width: 2.4;
    fill: none;
}

.i1-tie path {
    stroke: var(--purple);
    stroke-width: 1.5;
    stroke-dasharray: 24;
    animation: ll-i-tie 5.6s var(--ease-soft) infinite;
}

/* 02 — a node drops and the work routes around it */
.i2-main {
    stroke: var(--paper-24);
    stroke-width: 2;
    animation: ll-i-drop 5.4s var(--ease-soft) infinite;
}

.i2-bypass {
    stroke: var(--purple);
    stroke-width: 2.2;
    stroke-dasharray: 190;
    animation: ll-i-reroute 5.4s var(--ease-soft) infinite;
}

.i2-node { fill: var(--black); stroke: var(--paper-24); stroke-width: 2; }

.i2-node.is-lost {
    animation: ll-i-drop 5.4s var(--ease-soft) infinite;
}

.i2-x path {
    stroke: var(--paper-56);
    stroke-width: 2;
    opacity: 0;
    animation: ll-i-x 5.4s var(--ease-soft) infinite;
}

.i2-tok { fill: var(--purple); }

.i2-tok-a {
    offset-path: path("M20 60H140");
    animation: ll-i-tok-a 5.4s linear infinite;
}

.i2-tok-b {
    offset-path: path("M20 60C44 22 76 22 100 60H140");
    animation: ll-i-tok-b 5.4s linear infinite;
}

/* 03 — waste, energy and rework retreating under measurement */
.i3-track { stroke: var(--paper-14); stroke-width: 6; }

.i3-fill {
    fill: var(--purple);
    transform-box: view-box;
    transform-origin: 52px 0;             /* every meter retreats from its own
                                             left edge, not from the middle */
    animation: ll-i-meter 5.2s var(--ease-soft) infinite;
    animation-delay: var(--d, 0s);
}

.i3-gauge {
    transform-box: view-box;
    animation: ll-i-gauge 5.2s var(--ease-soft) infinite;
}

.i3-gauge line { stroke: var(--paper-24); stroke-width: 1.5; stroke-dasharray: 4 4; }
.i3-gauge circle { fill: var(--paper-56); }

@media (prefers-reduced-motion: reduce) {

    .industry-list li::after { transition: none; }

    .i1-panel,
    .i1-tie path,
    .i2-main,
    .i2-bypass,
    .i2-node.is-lost,
    .i2-x path,
    .i2-tok-a,
    .i2-tok-b,
    .i3-fill,
    .i3-gauge {
        animation: none;
    }

    /* Frozen on the frame that carries each argument. */
    .i1-panel { transform: translate3d(var(--tx), var(--ty), 0); }
    .i1-tie path { stroke-dashoffset: 0; }
    .i2-main { opacity: .18; }
    .i2-node.is-lost { opacity: .18; }
    .i2-x path { opacity: 1; }
    .i2-bypass { stroke-dashoffset: 0; }
    .i2-tok-a { opacity: 0; }
    .i2-tok-b { offset-distance: 62%; }
    .i3-fill { transform: scaleX(.34); }
    .i3-gauge { transform: translateX(82px); }
}

/* ── Contact ────────────────────────────────────────────────────────────── */

.contact-inner {
    text-align: center;
}

.contact-h {
    font-size: var(--step-3);
    font-weight: 200;
}

.contact-b {
    max-width: 46ch;
    margin: 1.5rem auto 0;
    font-size: var(--step-1);
    font-weight: 200;
    color: var(--ink-56);
}

.contact-actions {
    margin-top: 2.6rem;
}

/* ── Footer ─────────────────────────────────────────────────────────────── */

.footer {
    padding-block: clamp(2.6rem, 6vh, 4rem);
    border-top: 1px solid var(--ink-08);
}

.footer-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem 2rem;
}

.footer-brand {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    font-weight: 500;
    letter-spacing: -.025em;
}

.footer-brand img {
    border-radius: 8px;
}

.footer-copy {
    font-size: var(--step--1);
    color: var(--ink-40);
}

.footer-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.4rem;
    margin-left: auto;
    font-size: var(--step--1);
    color: var(--ink-56);
}

.footer-links a:hover {
    color: var(--purple);
}

.footer-copy {
    flex-basis: 100%;
    padding-top: 1.4rem;
    border-top: 1px solid var(--ink-08);
}

/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 860px) {
    .footer-links {
        margin-left: 0;
        flex-basis: 100%;
    }
}

@media (max-width: 560px) {
    .hero {
        min-height: auto;
        padding-block: 7.5rem 4rem;
    }

    .hero-scroll {
        display: none;
    }

    .hero-actions .btn {
        flex: 1 1 100%;
        justify-content: center;
    }

    .slide {
        flex-basis: 82vw;
    }
}

/* ── Dot grid — shared motif with the ad campaign ───────────────────────── */

/* Present on all four ad slides regardless of ground colour, so it is the one
   element that makes an ad click feel continuous. Kept faint here: in a feed it
   is texture on a 1-second impression; on a page it must not compete with copy. */
.dotted {
    position: relative;
    background-image: radial-gradient(var(--dot, rgba(0, 0, 0, .085)) 1.15px, transparent 1.15px);
    background-size: 26px 26px;
    background-position: -13px -13px;
}

.problem-h {
    max-width: 20ch;
    font-size: var(--step-3);
    font-weight: 300;
}

/* Flow diagram, the centrepiece of this section.

   Counterpart to "The loop" later on the page: there, four stages close into a
   working cycle; here the same line leaks at every stage and frays out without
   ever returning. The rail and the three failures are ONE object, so each node
   sits directly above the item it belongs to. Alignment comes from the grid
   rather than from hand-tuned percentages. */

.flow {
    position: relative;
    margin-top: clamp(2.6rem, 5.5vh, 4rem);
    --rail-gap: 4.4rem;                   /* rail to content: now carries the
                                             dated tick as well as the node */
    --rail-mid: 9px;                      /* the line's centre inside the rail */
    --drop: 2.3rem;                       /* gutter the drop line lives in */
}

.flow-rail {
    position: absolute;
    inset: 0 0 auto;
    height: 20px;
}

/* A wedge, not a rule: 2px at week one, 10px by year one, both centred on
   --rail-mid. The percentages are that geometry over the 18px box. */
.flow-line {
    position: absolute;
    top: 0;
    left: 7px;
    right: 0;
    height: 18px;
    background: linear-gradient(90deg, var(--purple-24), var(--purple));
    clip-path: polygon(0 44.4%, 100% 16.7%, 100% 83.3%, 0 55.6%);
}

/* The snowball. It rolls the whole rail and arrives twice the size it left.
   The off-centre highlight is what sells the rotation: a plain circle spinning
   is indistinguishable from one sliding. */
.flow-snow {
    position: absolute;
    top: var(--rail-mid);
    left: 0;
    width: 13px;
    height: 13px;
    margin: -6.5px 0 0 -6.5px;
    border-radius: 50%;
    background: var(--purple);
    /* Cut out of the rail: without this it is purple on purple and vanishes
       into the line it is rolling along. */
    box-shadow: 0 0 0 3px var(--ground);
    animation: ll-snowball 9s linear infinite;
}

.flow-snow::after {
    content: "";
    position: absolute;
    top: 20%;
    left: 20%;
    width: 27%;
    height: 27%;
    border-radius: 50%;
    background: var(--ground);
    opacity: .8;
}

/* The terminal tick, past the last column. This is what carries the rail all
   the way to the right edge. */
.flow-end {
    position: absolute;
    top: calc(var(--rail-mid) - 9px);
    right: 0;
    width: 2px;
    height: 18px;
    background: var(--purple);
}

.flow-end i {
    position: absolute;
    top: calc(100% + .55rem);
    right: 0;
    font-size: var(--step--1);
    font-style: normal;
    font-weight: 400;
    letter-spacing: .04em;
    white-space: nowrap;
    color: var(--purple);
}

.flow-caption {
    display: flex;
    justify-content: flex-end;            /* one caption left, so space-between
                                             would park it on the left edge */
    gap: 1.5rem;
    margin-top: clamp(1.6rem, 3.5vh, 2.4rem);
    font-size: var(--step--1);
    color: var(--ink-40);
}

.flow-caption span:last-child {
    text-align: right;
}

/* The three failures ---------------------------------------------------- */

.problem-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: clamp(2.2rem, 4vw, 3.4rem);
    margin: 0;
    padding: var(--rail-gap) 0 0;
    list-style: none;
}

@media (min-width: 840px) {
    .problem-list {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* Each stage owns a left gutter. The node, the dated tick and the drop line
   all live in it, so the drop can reach the copy without ever crossing the
   drawing. */
.fail {
    position: relative;
    padding-left: var(--drop);
}

/* The drop: the timeline reaching down to the words under each figure. Fades
   out at the very bottom rather than stopping dead on the last line. */
.fail::before {
    content: "";
    position: absolute;
    top: calc(var(--rail-mid) - var(--rail-gap));
    bottom: .2rem;
    left: 6px;
    width: 2px;
    background: linear-gradient(180deg, var(--purple) 0 62%, var(--purple-24));
    -webkit-mask-image: linear-gradient(180deg, #000 0 84%, transparent);
    mask-image: linear-gradient(180deg, #000 0 84%, transparent);
}

/* The nodes snowball too. --node is the diameter; the top calculation keeps
   every one of them centred on the rail line whatever its size. */
.fail-node {
    position: absolute;
    top: calc(var(--rail-mid) - (var(--node, 14px) / 2) - var(--rail-gap));
    left: calc(7px - (var(--node, 14px) / 2));
    width: var(--node, 14px);
    height: var(--node, 14px);
    border-radius: 50%;
    background: var(--ground);
    box-shadow: inset 0 0 0 2px var(--purple);
}

.fail:nth-child(1) { --node: 12px; }
.fail:nth-child(2) { --node: 19px; }
.fail:nth-child(3) { --node: 27px; }

.fail-node::after {
    content: "";
    position: absolute;
    inset: calc(var(--node, 12px) / -3);
    border-radius: 50%;
    background: var(--purple);
    opacity: .16;
    animation: ll-pulse-ring 3.6s var(--ease-soft) infinite;
}

/* The date on the axis. */
.fail-when {
    position: absolute;
    top: calc(var(--rail-mid) + 13px - var(--rail-gap));
    left: 1.15rem;                       /* clear of the drop line at 6px */
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: .04em;
    color: var(--purple);
}

/* A packet breaking off the rail and falling away: the argument of the whole
   section, rendered in two dots. */


/* The same worker, three times, further gone at each stage.

   Every part is animated and nothing rests: the user asked for exaggerated,
   continuously playing motion, so amplitudes here are deliberately theatrical
   (20-45 degree rotations, 5-12px travel) rather than tasteful twitches.

   Two rules learned the hard way:
   1. Origins are `transform-box: view-box` with px values in viewBox units.
      A fill-box percentage origin is recomputed from the LIVE bounding box
      every frame, so it drifts the moment a child starts moving.
   2. The ground line is stroke 2.4 at y=80, occupying y 78.8-81.2. Anything
      drawn below ~76.5 is swallowed by it, which is why stage 3 lies at 63-76
      and only the one planted foot reaches the line. */

.fig {
    display: block;
    width: 100%;
    max-width: 190px;
    margin-bottom: 1rem;
    color: var(--purple);
}

.fig svg {
    width: 100%;
    height: auto;
    overflow: visible;
}

.fig-desk {
    stroke: var(--ink-14);
    stroke-width: 2;
}

.fig [class^="fig-"],
.fig [class*=" fig-"] {
    transform-box: view-box;
}

/* Stage 1 — frantic. Fastest tempo in the set; he is working extremely hard
   at something a computer already knows the answer to. */
.fig-1 {
    opacity: 1;
}

.fig-1 .fig-bounce {
    transform-origin: 52px 79px;          /* feet, on the ground line */
    animation: ll-f1-bounce .62s var(--ease-soft) infinite;
}

.fig-1 .fig-head {
    transform-origin: 52px 31px;          /* neck */
    animation: ll-f1-snap 1.25s var(--ease-soft) infinite;
}

.fig-1 .fig-arm-r {
    transform-origin: 52px 38px;          /* writing shoulder */
    animation: ll-f1-scribble 1.25s var(--ease-soft) infinite;
}

.fig-1 .fig-arm-l {
    transform-origin: 52px 39px;          /* bracing shoulder */
    animation: ll-f1-brace 1.25s var(--ease-soft) infinite;
}

.fig-1 .fig-board {
    transform-origin: 84px 50px;
    animation: ll-f1-board 1.25s var(--ease-soft) infinite;
}

/* Handwriting drawn on and wiped, so the page is never finished. */
.fig-1 .fig-scrawl path {
    stroke-dasharray: 14;
    animation: ll-f1-scrawl 2.5s var(--ease-soft) infinite;
}

.fig-1 .fig-scrawl path + path {
    animation-delay: -1.25s;
}

.fig-1 .fig-paper {
    transform-origin: 28px 28px;
    animation: ll-f1-paper 2.3s var(--ease-soft) infinite;
}

/* Stage 2 — tearing his hair out. Largest amplitude of the three, and the only
   one with jerky timing: frustration is not smooth. */
.fig-2 {
    opacity: .84;
}

.fig-2 .fig-rock {
    transform-origin: 54px 79px;          /* feet */
    animation: ll-f2-rock 2.1s cubic-bezier(.2, .9, .3, 1) infinite;
}

.fig-2 .fig-upper {
    transform-origin: 54px 39px;          /* shoulders */
    animation: ll-f2-lean 1.55s var(--ease-soft) infinite;
}

.fig-2 .fig-shake {
    transform-origin: 54px 36px;          /* neck */
    animation: ll-f2-shake .42s cubic-bezier(.15, .95, .25, 1) infinite;
}

.fig-2 .fig-tuft {
    transform-origin: 54px 19px;          /* crown */
    animation: ll-f2-tuft .95s var(--ease-soft) infinite;
}

.fig-2 .fig-strand-a {
    transform-origin: 46px 15px;
    animation: ll-f2-strand-a 1.7s var(--ease-soft) infinite;
}

.fig-2 .fig-strand-b {
    transform-origin: 62px 15px;
    animation: ll-f2-strand-b 2.05s var(--ease-soft) infinite;
}

/* Stage 3 — the tantrum. The climax of the three, so it carries the fastest,
   hardest motion in the set.

   The thing that took three rewrites to get right: a rotating limb moves its
   far end TANGENTIALLY, so a pivot sitting directly above the fist can only
   sweep it sideways. Every earlier version hung a forearm down from an elbow
   or a shin down from a knee, and the fists and heels slid along the floor
   instead of hammering it. The upper arms now run out SIDEWAYS from the
   shoulder and the legs lie out near-horizontal from the hip, so rotating each
   limb drives its extremity straight down into the boards.

   Everything is DRAWN at the strike pose and rotates back from it, which makes
   the ground line a hard geometric floor that no keyframe can punch through.
   Arms run .5s against the legs' .64s so the drumming never collapses into a
   single mechanical pulse. */
.fig-3 {
    opacity: .8;
}

/* Seated he is only ~30 units tall against the standing figures' ~72, so
   without this he reads as a smaller drawing rather than the same man on the
   floor. Scaled about the ground contact point so nothing sinks. */
.fig-3 .fig-floored {
    transform-origin: 57px 79px;
    transform: scale(1.35);
    stroke-width: 1.78;                   /* 2.4 / 1.35, so the line weight
                                             still matches figures 1 and 2 */
}

/* He rocks on his seat, hardest just after the fists land. Capped at 6deg:
   the head sits 28 units up the lever. */
.fig-3 .fig-rock {
    transform-origin: 58px 77px;          /* the seat */
    animation: ll-f3-rock .5s cubic-bezier(.3, .9, .4, 1) infinite;
}

.fig-3 .fig-arm-a {
    transform-origin: 65px 58px;          /* shoulder */
    animation: ll-f3-hammer .5s cubic-bezier(.4, 0, .7, 1) infinite;
}

.fig-3 .fig-arm-b {
    transform-origin: 65px 58px;
    animation: ll-f3-hammer .5s cubic-bezier(.4, 0, .7, 1) infinite;
    animation-delay: -.25s;               /* dead opposite, so a fist is always down */
}

/* Legs point LEFT from the hip, so a POSITIVE rotation is what lifts the heel.
   Shallower arc than the arms: the leg is a longer lever, so fewer degrees
   buy the same travel. */
.fig-3 .fig-leg-a {
    transform-origin: 58px 77px;          /* hip */
    animation: ll-f3-drum .64s cubic-bezier(.4, 0, .7, 1) infinite;
}

.fig-3 .fig-leg-b {
    transform-origin: 58px 77px;
    animation: ll-f3-drum .64s cubic-bezier(.4, 0, .7, 1) infinite;
    animation-delay: -.32s;
}

/* Head whipping side to side at twice the arm tempo. */
.fig-3 .fig-thrash {
    transform-origin: 65px 58px;          /* base of the neck */
    animation: ll-f3-thrash .25s cubic-bezier(.3, .9, .3, 1) infinite;
}

/* Splash ticks flash on the frame the limb lands and are gone before it lifts,
   so a slam reads as an impact rather than a wave. */
.fig-3 .fig-hit path {
    stroke-width: 1.7;
}

.fig-3 .fig-hit-arm {
    transform-origin: 76px 76px;
    animation: ll-f3-hit .5s linear infinite;
}

.fig-3 .fig-hit-leg {
    transform-origin: 34px 76px;
    animation: ll-f3-hit .64s linear infinite;
}

@media (prefers-reduced-motion: reduce) {

    .fig [class^="fig-"],
    .fig [class*=" fig-"],
    .fig-1 .fig-scrawl path {
        animation: none;
    }

    /* Frozen on the frame that tells the story, not on the neutral pose. */
    .fig-1 .fig-head { transform: rotate(-17deg); }
    .fig-1 .fig-arm-r { transform: rotate(12deg); }
    .fig-2 .fig-shake { transform: rotate(-11deg); }
    .fig-2 .fig-tuft { transform: rotate(13deg); }
    .fig-3 .fig-arm-a { transform: rotate(-30deg); }
    .fig-3 .fig-leg-b { transform: rotate(16deg); }
    .fig-3 .fig-rock { transform: rotate(5deg); }
    .fig-3 .fig-thrash { transform: rotate(-13deg); }
    .fig-3 .fig-hit-arm { opacity: 0; }
    .fig-3 .fig-hit-leg { opacity: .85; }
}

.fail h3 {
    margin-bottom: .6rem;
    font-size: var(--step-1);
    font-weight: 400;
}

.fail p {
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.62;
    color: var(--ink-56);
}

/* Below 840px the items stack, so the rail turns vertical and runs down the
   left as a timeline instead of a horizontal flow. */
@media (max-width: 839px) {
    .flow-rail {
        height: 10px;
    }

    .flow-line,
    .flow-snow {
        display: none;
    }

    /* The rail becomes the full height of the flow so the terminal tick can
       sit at the foot of the vertical spine. */
    .flow-rail {
        inset: 0;
        height: auto;
        pointer-events: none;
    }

    .flow-end {
        top: auto;
        bottom: 3.15rem;                  /* the caption tail, measured at both
                                             390px and 768px */
        left: 0;
        right: auto;
        width: 14px;
        height: 2px;
    }

    .flow-end i {
        top: -.45rem;
        left: calc(100% + .55rem);
        right: auto;
    }

    /* Stacked, the axis turns vertical: one spine down the left with the dated
       ticks beside each stage, and the nodes still snowballing down it. */
    .problem-list {
        position: relative;
        padding: 0 0 0 2.1rem;
    }

    .problem-list::before {
        content: "";
        position: absolute;
        top: 6px;
        bottom: 0;                        /* meets the Year 1 tick */
        left: 6px;
        width: 2px;
        background: linear-gradient(180deg, var(--purple), var(--purple-24));
    }

    .fail {
        padding-left: 0;
    }

    .fail::before {
        display: none;                    /* the list spine already does this */
    }

    .fail-node {
        top: 6px;
        left: calc(-2.1rem + 7px - (var(--node, 14px) / 2));
    }

    .fail-when {
        position: static;
        display: block;
        margin-bottom: .5rem;
    }
}

@media (prefers-reduced-motion: reduce) {

    .flow-snow,
    .fail-node::after,
    .fig-1 .fig-arm,
    .fig-2 .fig-body,
    .fig-3 .fig-body {
        animation: none;
    }

    /* Without motion the snowball parks at the heavy end of the wedge, which
       is the frame that carries the argument. */
    .flow-snow {
        left: 92%;
        opacity: 1;
        transform: scale(2);
    }
}

/* The closing verdict. Deliberately not body copy: a purple rule, a muted
   setup line and a full-weight payoff, so the section lands on a statement
   rather than trailing off into another paragraph. */

.verdict {
    max-width: 34ch;
    margin: clamp(2.8rem, 6vh, 4.2rem) 0 0;
    padding-left: clamp(1.1rem, 2.4vw, 1.7rem);
    border-left: 3px solid var(--purple);
}

.verdict span {
    display: block;
}

.verdict-a {
    font-size: var(--step-1);
    font-weight: 300;
    line-height: 1.5;
    color: var(--ink-40);
}

.verdict-b {
    margin-top: .25rem;
    font-size: var(--step-2);
    font-weight: 400;
    letter-spacing: -.025em;
    line-height: 1.28;
    color: var(--ink);
}

.verdict-b em {
    font-style: normal;
    color: var(--purple);
}


/* ── The problem ────────────────────────────────────────────────────────── */

.problem {
    border-top: 1px solid var(--ink-08);
}


/* ── The loop (dark) ────────────────────────────────────────────────────── */

/* Deliberately the one dark beat in the first half of the page: the problem
   above it is light and grim, and this is the answer, so the ground flips.

   On black, purple is used for GRAPHICS ONLY -- the lit arc, the active node
   and its glow. #6e38cf on #000 measures 3.14:1, which clears the 3:1 bar for
   a UI graphic but not the 4.5:1 bar for text. All type here is white or
   --paper-*. */
.loop {
    --dot: rgba(255, 255, 255, .06);
    background: var(--black);
    color: var(--white);
}

.loop-h {
    max-width: 20ch;
    font-size: var(--step-3);
    font-weight: 200;
    letter-spacing: -.02em;
}

.loop-stage {
    display: grid;
    place-items: center;
    margin-top: clamp(2.4rem, 6vh, 4.2rem);
}

/* Wide viewports: the question sits beside the answer instead of stacked above
   it, which uses the column width the ring was leaving empty and takes roughly
   300px off the section height. */
@media (min-width: 1080px) {

    /* Two items, one row. The previous version spanned the ring across three
       text rows, and because the ring is taller than they are the surplus was
       distributed between them, prising the heading and the closing line
       apart. One wrapper removes the rows and the problem with them. */
    .loop .wrap {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-items: center;
        column-gap: clamp(2rem, 5vw, 5rem);
    }

    .loop-stage {
        margin-top: 0;
    }
}

/* One custom property drives both the box and the node orbit radius, so the
   nodes cannot drift off the ring when the box resizes. The circle is r=68 of
   a 240 viewBox, i.e. 28.3% of the box, which leaves ~113px outside it at the
   520px cap for the node labels. "Intelligence" is the longest at ~88px. */
.loop-ring {
    --ring: min(88vw, 520px);
    position: relative;
    width: var(--ring);
    height: var(--ring);
}

.loop-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;                    /* the comet's glow must not clip */
}

.loop-track {
    fill: none;
    stroke: var(--paper-14);
    stroke-width: 1.5;
}

.loop-arc {
    fill: none;
    stroke: var(--purple);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-dasharray: 106.8 320.4;        /* a quarter of 2*pi*68 */
    transform-origin: 120px 120px;
    /* -90deg puts the dash start at 12 o'clock, where stage 01 sits; a circle's
       dash would otherwise begin at 3 o'clock. */
    transform: rotate(calc(-90deg + var(--arc-turn, 0deg)));
    transition: transform .75s var(--ease);
}

.loop-orbit {
    transform-origin: 120px 120px;
    animation: ll-loop-orbit 13s linear infinite;
}

.loop-comet {
    fill: var(--white);
    filter: drop-shadow(0 0 7px rgba(110, 56, 207, .9));
}

/* ── Nodes ──────────────────────────────────────────────────────────────── */

.loop-nodes {
    position: absolute;
    inset: 0;
}

.loop-node {
    position: absolute;
    left: 50%;
    top: 50%;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transform:
        translate(-50%, -50%)
        rotate(var(--a))
        translateY(calc(var(--ring) * -.2833))
        rotate(calc(var(--a) * -1));
}

.loop-node:focus-visible {
    outline: 2px solid var(--white);
    outline-offset: 2px;
}

.loop-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--black);
    box-shadow: 0 0 0 1.5px var(--paper-24);
    transition:
        background .32s var(--ease-soft),
        box-shadow .32s var(--ease-soft),
        transform .32s var(--ease);
}

.loop-node:hover .loop-dot {
    box-shadow: 0 0 0 1.5px var(--paper-56);
    transform: scale(1.2);
}

.loop-node[aria-selected="true"] .loop-dot {
    background: var(--purple);
    box-shadow: 0 0 0 2px var(--purple), 0 0 18px 4px rgba(110, 56, 207, .8);
    transform: scale(1.5);
}

.loop-label {
    position: absolute;
    white-space: nowrap;
    font-size: var(--step--1);
    font-weight: 300;
    letter-spacing: .02em;
    color: var(--paper-56);
    transition: color .32s var(--ease-soft);
}

.loop-node:hover .loop-label,
.loop-node:focus-visible .loop-label,
.loop-node[aria-selected="true"] .loop-label {
    color: var(--white);
}

.loop-node[aria-selected="true"] .loop-label {
    font-weight: 400;
}

.loop-node[data-pos="top"] .loop-label { bottom: calc(100% - .1rem); }
.loop-node[data-pos="right"] .loop-label { left: calc(100% - .1rem); }
.loop-node[data-pos="bottom"] .loop-label { top: calc(100% - .1rem); }
.loop-node[data-pos="left"] .loop-label { right: calc(100% - .1rem); }

/* ── Centre readout ─────────────────────────────────────────────────────── */

.loop-core {
    position: absolute;
    left: 50%;
    top: 50%;
    width: min(60%, 250px);
    transform: translate(-50%, -50%);
    display: grid;
    text-align: center;
}

/* An explicit display on an author rule beats the user agent's
   `[hidden] { display: none }`, so without the second rule here the hidden
   attribute is inert and all four stages render on top of each other. */
.loop-panel[hidden] {
    display: none;
}

.loop-panel {
    grid-area: 1 / 1;                     /* all four share one cell, so the
                                             ring never shifts between stages */
    display: grid;
    gap: .5rem;
    justify-items: center;
    animation: ll-loop-in .5s var(--ease) both;
}

.loop-panel:focus-visible {
    outline: 2px solid var(--purple);
    outline-offset: 10px;
    border-radius: var(--radius);
}

.loop-num {
    font-size: var(--step--1);
    font-weight: 400;
    letter-spacing: .3em;
    text-indent: .3em;                    /* re-centre against the trailing track */
    color: var(--paper-56);
}

.loop-word {
    font-size: var(--step-2);
    font-weight: 300;
    letter-spacing: -.03em;
    line-height: 1.05;
}

.loop-note {
    margin: 0;
    font-size: var(--step--1);
    font-weight: 300;
    line-height: 1.55;
    color: var(--paper-56);
}

/* Sits directly under the heading and reads as a signature on it rather than
   as another paragraph: a short rule, the claim at display weight, the aside
   beneath it muted with its own point picked back out. */
.loop-close {
    margin-top: clamp(1.5rem, 3vh, 2.1rem);
    max-width: 30ch;
}

.loop-close::before {
    content: "";
    display: block;
    width: 34px;
    height: 2px;
    margin-bottom: 1.05rem;
    background: var(--purple);
}

.loop-close-a {
    display: block;
    font-size: var(--step-1);
    font-weight: 200;
    letter-spacing: -.015em;
    line-height: 1.3;
    color: var(--white);
}

.loop-close-b {
    display: block;
    margin-top: .5rem;
    font-size: var(--step-0);
    font-weight: 300;
    line-height: 1.5;
    color: var(--paper-56);
}

.loop-close-b em {
    font-style: normal;
    font-weight: 400;
    color: var(--white);
}

/* ── Stacked below the ring's breathing room ────────────────────────────── */

/* The ring needs ~113px outside itself for labels. Below 720px that stops
   fitting, so it becomes a vertical spine, matching what the problem list
   already does at its own breakpoint. */
@media (max-width: 719px) {

    .loop-ring {
        width: 100%;
        height: auto;
    }

    .loop-svg {
        display: none;
    }

    .loop-nodes {
        position: relative;
        inset: auto;
        display: grid;
        gap: .15rem;
    }

    /* The spine sits under the dot centres: each node is a flex row starting
       with an 11px dot, so their centres all land 5.5px from the left edge. */
    .loop-nodes::before {
        content: "";
        position: absolute;
        left: 5px;
        top: 1.1rem;
        bottom: 1.1rem;
        width: 1.5px;
        background: linear-gradient(180deg, var(--purple), var(--purple-24));
    }

    .loop-node {
        position: relative;
        left: auto;
        top: auto;
        width: 100%;
        height: auto;
        transform: none;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        gap: .85rem;
        padding: .55rem 0;
        border-radius: var(--radius);
    }

    .loop-dot {
        flex: 0 0 auto;
    }

    .loop-label {
        position: static;
        font-size: var(--step-0);
    }

    .loop-core {
        position: static;
        width: 100%;
        margin-top: 1.5rem;
        padding-top: 1.4rem;
        border-top: 1px solid var(--paper-14);
        transform: none;
        text-align: left;
        min-height: 6.5rem;               /* holds the tallest note at its
                                             two-line worst case, so tapping
                                             between stages does not jump */
    }

    .loop-panel {
        justify-items: start;
    }
}

@media (prefers-reduced-motion: reduce) {

    .loop-orbit {
        animation: none;
    }

    .loop-arc {
        transition: none;
    }

    .loop-panel {
        animation: none;
    }
}
