/* ===========================================================================
   Capability dial, rebuilt from Figma
   File gSV4KtGZpMu6vyC3mE0B27, page "Website", frames `infocircle-section`
   (600:2211, 594:7320, 594:7385, 594:8284: one frame per state 01 to 04;
   the fifth capability, Automations, was dropped on 17.09.).

   Mechanic: the section is several viewports tall and a viewport-high stage
   sticks while it scrolls through. Scroll progress maps onto the steps with
   a plateau on every point (slow there, quick in between); the wheel turns
   continuously, entry and collage fade in, hold, fade out and the next pair
   follows after a gap. dial.js drives `--s` and the inline opacity/blur.

   Same canvas recipe as the hero: a 1728 x 1117 composition, `--u` is one
   design pixel, every object sits at its Figma coordinate. The section
   backgrounds are Figma's shader renders (assets/dial/bg), exported like the
   hero wash, because "Pattern refraction" needs WebGPU.
   =========================================================================== */

.dial {
    container-type: inline-size;
    position: relative;
    /* one viewport for the first stop, 2.4 more per step; dial.js spends most
       of each step's scroll on the point itself and little on the way */
    height: calc((1 + 3 * 2.4) * 100svh);
    background: #fff;
    --accent: #f86126;
    --ring-alpha: 0.5;
    /* angle between two numbers on the rim (Figma draws 30, wider reads
       better while the wheel turns) */
    --arc: 45deg;
}

.dial__stage {
    position: sticky;
    top: 0;
    height: 100svh;
    display: grid;
    /* a fixed track, so a canvas taller than the stage is centred on it
       instead of pushing the track open and hanging from the top */
    grid-template-rows: 100%;
    grid-template-columns: 100%;
    place-items: center;
    overflow: hidden;
    isolation: isolate;
}

/* ---- backgrounds: one wash per state, crossfaded by dial.js ------------- */

.dial__wash {
    position: absolute;
    inset: 0;
    background: center / cover no-repeat;
    opacity: 0;
    pointer-events: none;
    /* The stage clips, so a coloured field would meet the white sections above
       and below on a ruled line. It dissolves into the page instead: opaque
       through the middle two thirds, gone at both edges. */
    -webkit-mask-image: linear-gradient(180deg,
        rgba(0, 0, 0, 0) 0%, #000 16%, #000 84%, rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(180deg,
        rgba(0, 0, 0, 0) 0%, #000 16%, #000 84%, rgba(0, 0, 0, 0) 100%);
}

.dial__wash--1 { background-image: url('/assets/dial/bg/wash-1.jpg'); }
.dial__wash--2 { background-image: url('/assets/dial/bg/wash-2.jpg'); }
.dial__wash--3 { background-image: url('/assets/dial/bg/wash-3.jpg'); }
.dial__wash--4 { background-image: url('/assets/dial/bg/wash-4.jpg'); }

/* ---- canvas ------------------------------------------------------------- */

.dial__canvas {
    /* Width-first, unlike the hero: the frame edge has to be the screen edge,
       so the circle is half outside on the left and the 03 collage runs off
       on the right, as in Figma. On flat screens the stage clips a little of
       the top and bottom instead (nothing sits there). */
    --fig-w: 100cqw;
    position: relative;
    width: var(--fig-w);
    aspect-ratio: 1728 / 1117;
    /* one design pixel; --fu is the same value for parts that need a scaled
       copy of --u (the 03 chat bar is the hero bar at 108.3 %) */
    --fu: calc(var(--fig-w) / 1728);
    --u: var(--fu);
    font-family: 'Archivo', system-ui, sans-serif;
    /* nothing is clipped at the frame: the circle runs on to the screen edge
       and the entries travel past the canvas; the stage does the clipping */
    overflow: visible;
}

.dial__canvas [data-fig] {
    position: absolute;
}

/* ---- the dial ----------------------------------------------------------- */

/* Circle: 800 at (-443, 159), 1px black. Its centre is (-43, 559). */
.dial__ring,
.dial__wheel {
    left: calc(-443 * var(--u));
    top: calc(159 * var(--u));
    width: calc(800 * var(--u));
    height: calc(800 * var(--u));
    border-radius: 50%;
}

.dial__ring {
    border: 1px solid #000;
    pointer-events: none;
}

.dial__wheel {
    /* turns with the scroll itself, number to number, no snapping */
    transform: rotate(calc(var(--s, 0) * -1 * var(--arc)));
    will-change: transform;
}

/* One 800 x 36 row per number, centred on the circle and turned by --arc
   each; the label starts 415px from the centre, 15px outside the rim. */
.dial__num {
    position: absolute;
    left: 0;
    top: calc(382 * var(--u));
    width: calc(800 * var(--u));
    height: calc(36 * var(--u));
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    transform: rotate(calc(var(--i) * var(--arc)));
    pointer-events: none;
}

.dial__num span {
    position: absolute;
    left: calc(815 * var(--u));
    top: 0;
    display: block;
    padding: 0 calc(6 * var(--u));
    margin-left: calc(-6 * var(--u));
    font-size: calc(32 * var(--u));
    line-height: calc(36 * var(--u));
    letter-spacing: calc(-1.28 * var(--u));
    color: #858282;
    cursor: pointer;
    pointer-events: auto;
    transition: color 0.35s ease, transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The active number sits 20px further out to leave room for the dot. */
.dial__num.is-active span {
    color: #141414;
    transform: translateX(calc(20 * var(--u)));
}

/* The dot stays at the rim while the wheel turns under it: a 44 box centred
   on the rim's rightmost point, 20.56 dot, 37 ring at 2.06. */
.dial__dot {
    left: calc(335 * var(--u));
    top: calc(537 * var(--u));
    width: calc(44 * var(--u));
    height: calc(44 * var(--u));
    pointer-events: none;
}

.dial__dot::before {
    content: '';
    position: absolute;
    left: calc(3 * var(--u));
    top: calc(3 * var(--u));
    width: calc(37 * var(--u));
    height: calc(37 * var(--u));
    border-radius: 50%;
    border: calc(2.056 * var(--u)) solid
        color-mix(in srgb, var(--accent) calc(var(--ring-alpha) * 100%), transparent);
    box-sizing: border-box;
    transition: border-color 0.35s ease;
}

.dial__dot::after {
    content: '';
    position: absolute;
    left: calc(11.722 * var(--u));
    top: calc(11.722 * var(--u));
    width: calc(20.556 * var(--u));
    height: calc(20.556 * var(--u));
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 calc(4.111 * var(--u)) calc(10.278 * var(--u))
        color-mix(in srgb, var(--accent) 20%, transparent);
    transition: background-color 0.35s ease, box-shadow 0.35s ease;
}

/* ---- entries: the Capability Entry component at (517, 420) -------------- */

.dial__entry {
    left: calc(517 * var(--u));
    top: calc(420 * var(--u));
    width: calc(560 * var(--u));
    margin: 0;
    will-change: transform, filter, opacity;
}

.dial__eyebrow {
    margin: 0 0 calc(12 * var(--u));
    font-size: calc(12 * var(--u));
    line-height: calc(18.6 * var(--u));
    letter-spacing: calc(1.2 * var(--u));
    text-transform: uppercase;
    color: #171717;
    white-space: nowrap;
}

.dial__title {
    margin: 0 0 calc(18 * var(--u));
    font-weight: 400;
    font-size: calc(64 * var(--u));
    line-height: calc(64 * var(--u));
    letter-spacing: calc(-0.48 * var(--u));
    color: #212121;
}

.dial__desc {
    width: calc(440 * var(--u));
    margin: 0 0 calc(26 * var(--u));
    font-size: calc(16 * var(--u));
    line-height: calc(24.8 * var(--u));
    color: #6b6b70;
}

.dial__tags {
    display: flex;
    flex-wrap: wrap;
    gap: calc(8 * var(--u));
    margin: 0;
    padding: 0;
    list-style: none;
}

.dial__tag {
    padding: calc(7 * var(--u)) calc(14 * var(--u));
    border: 1px solid #c5c1b9;
    border-radius: 999px;
    font-size: calc(13 * var(--u));
    line-height: calc(20.15 * var(--u));
    color: #212121;
    white-space: nowrap;
}

/* ---- visuals: the product, one collage per state ----------------------- */

.dial__visual {
    position: absolute;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(var(--vdrift, 0px));
    will-change: opacity, filter, transform;
}

.dial__visual.is-near {
    visibility: visible;
}

.dial__visual [data-fig] {
    position: absolute;
}

.dial__cursor {
    width: calc(63 * var(--u));
    height: calc(63 * var(--u));
}

/* SF Pro is what the product renders in; the fallbacks are the platform UI
   font, the same stack the hero uses. */
.dial__sf {
    font-family: -apple-system, 'SF Pro Text', system-ui, sans-serif;
}

.dial__inter {
    font-family: 'Inter', system-ui, sans-serif;
}

/* -- 01: an agenda drafted in chat, on its Figma coordinates (the agenda
   text runs 118px past the frame edge, as designed) ------------------------ */

/* The messages window: the panel above the bar. The thread inside is a
   full-canvas layer shifted back by the window's own offset, so every
   message keeps its Figma coordinate while the window does the clipping.
   Scrolling the thread is what a real chat does; nothing fades away. */
.dial__v1-clip {
    left: calc(1182 * var(--u));
    top: calc(198 * var(--u));
    width: calc(646 * var(--u));
    height: calc(462 * var(--u));
    overflow: hidden;
}

.dial__v1-thread {
    position: absolute;
    left: calc(-1182 * var(--u));
    top: calc(-198 * var(--u));
    width: calc(1728 * var(--u));
    height: calc(1117 * var(--u));
    will-change: transform;
}

.dial__v1-panel {
    left: calc(1182 * var(--u));
    top: calc(198 * var(--u));
    width: calc(646 * var(--u));
    height: calc(642 * var(--u));
    background: rgba(255, 254, 254, 0.4);
    border-radius: calc(21.415 * var(--u));
}

.dial__v1-ask,
.dial__v1-ask2 {
    left: calc(1200 * var(--u));
    top: calc(211 * var(--u));
    width: calc(621 * var(--u));
    height: calc(114 * var(--u));
    box-sizing: border-box;
    padding: calc(19.987 * var(--u)) calc(37.119 * var(--u)) calc(18 * var(--u));
    background: #ffe2d7;
    border-radius: calc(21.415 * var(--u));
    display: flex;
    align-items: flex-end;
}

/* The second exchange, placed on the SAME bottom rhythm as the first.
   A thread grows downward, so it must not reuse the first pair's top edge:
   that left the answer 174 above the bar where the design has 47.5, a hole
   the eye reads as a broken layout. Measured at 1512: the bubble is 75 tall
   with its one line, the confirmation 147.4, the bar starts at 670. So the
   confirmation ends at 622.5 like the agenda does, the bubble keeps the
   design's 39 between question box and answer (both bubbles carry the same
   18 bottom padding, so equal box gaps read as equal text gaps), and the
   pair sits just above the bar. */
.dial__v1-ask2 {
    top: calc(661 * var(--u));
    height: auto;
}

.dial__v1-ask p,
.dial__v1-ask2 p {
    width: calc(582.583 * var(--u));
    margin: 0;
    font-weight: 500;
    font-size: calc(31.44 * var(--u));
    line-height: normal;
    color: #212529;
}

.dial__v1-answer,
.dial__v1-reply {
    left: calc(1228 * var(--u));
    top: calc(348 * var(--u));
    width: calc(618 * var(--u));
    margin: 0;
    font-weight: 500;
    font-size: calc(30.074 * var(--u));
    line-height: calc(49.162 * var(--u));
    color: #212529;
}

.dial__v1-answer strong,
.dial__v1-reply strong {
    display: block;
    font-weight: 700;
}

.dial__v1-reply p {
    margin: 0;
}

/* AFTER the shared `.dial__v1-answer, .dial__v1-reply` block, or the 348 it
   sets would win on source order and the confirmation would keep the first
   answer's top edge. */
.dial__v1-reply {
    top: calc(775 * var(--u));
}

/* The second exchange sits exactly where the first one is and is hidden
   until the send click hands the thread over to it. */
.dial__v1-ask2,
.dial__v1-reply strong,
.dial__v1-reply p {
    opacity: 0;
}

/* Figma holds an empty 49px paragraph here that its renderer collapses, so
   the list follows the headline directly. */
.dial__v1-answer ol {
    margin: 0;
    padding: 0 0 0 calc(45.111 * var(--u));
    line-height: calc(45.065 * var(--u));
}

.dial__v1-answer li {
    margin: 0;
}

.dial__chat {
    background: #fff;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    box-sizing: border-box;
}

.dial__v1-chat {
    left: calc(1200 * var(--u));
    top: calc(670 * var(--u));
    width: calc(637.073 * var(--u));
    height: calc(151.732 * var(--u));
    padding: calc(21 * var(--u)) calc(22.41 * var(--u)) calc(23.624 * var(--u));
    gap: calc(21 * var(--u));
    border-radius: calc(27.919 * var(--u));
    box-shadow: 0 calc(1.748 * var(--u)) calc(8.739 * var(--u))
        calc(3.496 * var(--u)) rgba(168, 168, 168, 0.2);
}

.dial__v1-chat .dial__chat-ph {
    height: calc(33.058 * var(--u));
    font-weight: 400;
    font-size: calc(30 * var(--u));
}

.dial__chat-ph {
    margin: 0;
    color: #62666b;
    line-height: normal;
    white-space: nowrap;
}

/* The text caret after the typed line: Vector 1386, a 53 tall stroke. */
.dial__caret {
    width: calc(3 * var(--u));
    height: calc(53 * var(--u));
}

.dial__v1-caret {
    left: calc(1493.5 * var(--u));
    top: calc(677 * var(--u));
}

.dial__v1-cursor {
    left: calc(1145 * var(--u));
    top: calc(867 * var(--u));
}

/* The chat bar's bottom row, the hero's measurements. Everything is in --u,
   so the 03 bar only redefines --u to get the larger copy. */
.dial__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: calc(51.224 * var(--u));
}

.dial__row-left,
.dial__row-right {
    display: flex;
    align-items: center;
}

.dial__row-left { gap: calc(6.403 * var(--u)); }
.dial__row-right { gap: calc(12.806 * var(--u)); }

.dial__row-btn {
    width: calc(51.224 * var(--u));
    height: calc(51.224 * var(--u));
    display: block;
}

.dial__row-pill {
    position: relative;
    height: calc(51.224 * var(--u));
}

.dial__row-pill > * {
    position: absolute;
}

.dial__row-pill--ask { width: calc(172.079 * var(--u)); }
.dial__row-pill--model { width: calc(153.671 * var(--u)); }

.dial__row-pill .dial__row-chev {
    width: calc(38.418 * var(--u));
    height: calc(38.418 * var(--u));
    top: calc(6.404 * var(--u));
}

.dial__row-pill .dial__row-lock {
    width: calc(19.895 * var(--u));
    height: calc(25.385 * var(--u));
    left: calc(32.475 * var(--u));
    top: calc(11.326 * var(--u));
}

.dial__row-pill span {
    font-family: -apple-system, 'SF Pro Text', system-ui, sans-serif;
    font-weight: 500;
    font-size: calc(19.209 * var(--u));
    color: #62666b;
    top: calc(14.409 * var(--u));
    white-space: nowrap;
}

.dial__row-pill--ask span { left: calc(64.476 * var(--u)); }
.dial__row-pill--ask .dial__row-chev { left: calc(138.464 * var(--u)); }
.dial__row-pill--model span { left: calc(17.608 * var(--u)); }
.dial__row-pill--model .dial__row-chev { left: calc(102.446 * var(--u)); }

.dial__row-icon {
    position: relative;
    flex: none;
    width: calc(38.418 * var(--u));
    height: calc(38.418 * var(--u));
}

.dial__row-icon img {
    position: absolute;
    display: block;
}

.dial__row-icon--mic img {
    left: calc(8.142 * var(--u));
    top: calc(3.733 * var(--u));
    width: calc(22.144 * var(--u));
    height: calc(29.485 * var(--u));
}

.dial__row-icon--audio img {
    left: calc(5.601 * var(--u));
    top: calc(4 * var(--u));
    width: calc(25.611 * var(--u));
    height: calc(28.813 * var(--u));
}

/* -- 02: the Ask first menu ------------------------------------------------ */

/* Rectangle 205: the open pill, white at 80 %. */
.dial__v2-pill {
    left: calc(1152 * var(--u));
    top: calc(371 * var(--u));
    width: calc(285 * var(--u));
    height: calc(73.286 * var(--u));
    background: rgba(255, 255, 255, 0.8);
    border-radius: calc(51.571 * var(--u));
}

.dial__v2-pill img,
.dial__v2-pill span {
    position: absolute;
}

/* the lock glyph is 25.72 x 33.07 at (39.42, 19); the export carries the
   stroke overhang, so it is placed 0.8 up and left of that */
.dial__v2-pill .dial__v2-lock {
    left: calc(38.165 * var(--u));
    top: calc(17.74 * var(--u));
    width: calc(28.235 * var(--u));
    height: calc(35.584 * var(--u));
}

.dial__v2-pill span {
    left: calc(81.752 * var(--u));
    top: calc(16.284 * var(--u));
    width: calc(137 * var(--u));
    height: calc(43 * var(--u));
    font-weight: 400;
    font-size: calc(35.286 * var(--u));
    line-height: calc(43 * var(--u));
    text-align: center;
    color: #212529;
    white-space: nowrap;
}

/* The mode the pill starts on. Same optical box as the lock, contained so
   the small export is not stretched into it. */
.dial__v2-pill .dial__v2-bolt {
    left: calc(38.165 * var(--u));
    top: calc(17.74 * var(--u));
    width: calc(28.235 * var(--u));
    height: calc(35.584 * var(--u));
    object-fit: contain;
}

/* the chosen mode waits underneath and is faded in on the pick */
.dial__v2-pill .dial__v2-label--to,
.dial__v2-pill .dial__v2-lock {
    opacity: 0;
}

/* the caret points up while the menu is open; Figma has it 7px below the
   text's centre line, here it sits on it */
.dial__v2-pill .dial__v2-caret {
    left: calc(243 * var(--u));
    top: calc(32.2 * var(--u));
    width: calc(19.271 * var(--u));
    height: calc(11.129 * var(--u));
    transform: rotate(180deg);
}

/* Group 2756: the menu surface (364.39 x 203 at 1263, 434.71) is an export
   that includes its drop shadow, hence the larger box. */
.dial__v2-menu {
    left: calc(1252.18 * var(--u));
    top: calc(425.88 * var(--u));
    width: calc(386.049 * var(--u));
    height: calc(224.656 * var(--u));
}

.dial__v2-item-icon {
    width: calc(18.936 * var(--u));
    height: calc(23.308 * var(--u));
}

.dial__v2-icon-1 { left: calc(1292.92 * var(--u)); top: calc(463.7 * var(--u)); }
.dial__v2-icon-2 {
    left: calc(1294.53 * var(--u));
    top: calc(553.31 * var(--u));
    width: calc(18.814 * var(--u));
    height: calc(23.315 * var(--u));
}

.dial__v2-name,
.dial__v2-hint {
    left: calc(1330.91 * var(--u));
    margin: 0;
    line-height: normal;
    white-space: nowrap;
}

.dial__v2-name {
    font-weight: 700;
    font-size: calc(23.189 * var(--u));
    color: #212529;
}

.dial__v2-hint {
    font-weight: 400;
    font-size: calc(21.532 * var(--u));
    color: rgba(73, 80, 87, 0.76);
}

.dial__v2-name--1 { top: calc(461.216 * var(--u)); }
.dial__v2-hint--1 { top: calc(497.888 * var(--u)); }
.dial__v2-name--2 { top: calc(550.658 * var(--u)); }
.dial__v2-hint--2 { top: calc(587.793 * var(--u)); }

.dial__v2-cursor {
    left: calc(1583 * var(--u));
    top: calc(488.715 * var(--u));
}

/* -- 03: the Commands panel, everything in the accent ink #dc7479. This one
   keeps Figma's overhang: panel, cards and chat bar run past the frame's
   right edge and are cut by the screen edge (at 1728 that IS the frame). -- */

/* What the pair does when the palette drops.

   While the palette is open the overhang is Figma's own: the palette is a
   wide column and the frame cuts it. With only the card and the bar left
   there is nothing left to justify a cut, so the two step in off the edge
   far enough to stand whole, and the card moves onto the bar's middle.

   203 is the bar's 179 of overhang plus 24 of air. It is the smallest move
   that puts the bar fully on screen, which keeps the collage on the
   right-hand side it is drawn on rather than walking it into the text.
   152 is half the difference between the card's 340 and the bar's 648.

   The pair also drops. Closing the gap the palette left behind pulled it up
   against the top of the frame, with the whole lower half empty under it.
   358 is not a free choice: pulled in this far the bar is only just clear of
   the text column, so it can sit above the title or below the tags and
   nowhere in between. Below is the one that answers "too high" while keeping
   its margin to the frame. */
.dial__visual--3 { --v3-pull: 203; --v3-card-x: 152; --v3-drop: 358; }

.dial__v3-card {
    left: calc(1261 * var(--u));
    top: calc(138 * var(--u));
    width: calc(340 * var(--u));
    height: calc(135 * var(--u));
    box-sizing: border-box;
    padding: calc(24.863 * var(--u)) calc(27.123 * var(--u));
    display: flex;
    gap: calc(18.082 * var(--u));
    align-items: flex-start;
    background: #fff;
    border-radius: calc(25.63 * var(--u));
    filter: drop-shadow(0 calc(3.138 * var(--u)) calc(7.846 * var(--u)) rgba(252, 134, 139, 0.2));
    color: #dc7479;
}

.dial__v3-card-icon {
    flex: none;
    position: relative;
    width: calc(54.246 * var(--u));
    height: calc(54.246 * var(--u));
}

.dial__v3-card-icon img {
    position: absolute;
    left: calc(3.39 * var(--u));
    top: calc(2.145 * var(--u));
    width: calc(47.466 * var(--u));
    height: calc(49.956 * var(--u));
}

.dial__v3-card-text {
    width: calc(228.062 * var(--u));
    display: flex;
    flex-direction: column;
    gap: calc(4.521 * var(--u));
}

.dial__v3-card-text b {
    font-weight: 700;
    font-size: calc(27.123 * var(--u));
    line-height: calc(29.836 * var(--u));
}

.dial__v3-card-text p {
    margin: 0;
    font-weight: 500;
    font-size: calc(24.863 * var(--u));
    line-height: calc(31.079 * var(--u));
}

.dial__v3-panel {
    left: calc(1259 * var(--u));
    top: calc(296 * var(--u));
    /* the same 648 the chat bar below is, and off the same left edge: the
       palette is that bar opened upwards, so the two share a silhouette */
    width: calc(648 * var(--u));
    height: calc(597 * var(--u));
    box-sizing: border-box;
    padding: 0 calc(21.968 * var(--u)) calc(21.968 * var(--u));
    display: flex;
    flex-direction: column;
    background: rgba(255, 255, 255, 0.8);
    border-radius: calc(25.629 * var(--u));
    filter: drop-shadow(0 calc(14.645 * var(--u)) calc(29.29 * var(--u)) #ffdfe1);
    color: #dc7479;
}

.dial__v3-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: calc(21.968 * var(--u)) 0 calc(7 * var(--u));
}

.dial__v3-head b {
    display: block;
    padding: calc(3.661 * var(--u)) calc(7.323 * var(--u)) calc(7.323 * var(--u));
    font-weight: 700;
    font-size: calc(21.968 * var(--u));
    line-height: calc(32.951 * var(--u));
    white-space: nowrap;
}

.dial__v3-close {
    flex: none;
    width: calc(40.274 * var(--u));
    height: calc(40.274 * var(--u));
    display: grid;
    place-items: center;
}

.dial__v3-close img {
    width: calc(23.798 * var(--u));
    height: calc(23.798 * var(--u));
}

.dial__v3-search-wrap {
    padding: 0 calc(3.661 * var(--u));
}

.dial__v3-search {
    height: calc(62.241 * var(--u));
    box-sizing: border-box;
    padding: calc(10.984 * var(--u)) calc(21.968 * var(--u));
    display: flex;
    align-items: center;
    gap: calc(14.645 * var(--u));
    background: #fff;
    border: calc(1.831 * var(--u)) solid #ffe5e6;
    border-radius: calc(181.232 * var(--u));
}

.dial__v3-search img {
    width: calc(25.629 * var(--u));
    height: calc(25.629 * var(--u));
}

.dial__v3-search span {
    font-weight: 400;
    font-size: calc(21.968 * var(--u));
    line-height: calc(32.951 * var(--u));
    color: #994c4f;
    white-space: nowrap;
}

.dial__v3-sub {
    display: block;
    padding: calc(18.306 * var(--u)) calc(7.323 * var(--u)) calc(10.984 * var(--u));
    font-weight: 600;
    font-size: calc(20.137 * var(--u));
    line-height: calc(30.205 * var(--u));
}

.dial__v3-grid {
    display: flex;
    flex-direction: column;
    gap: calc(14.645 * var(--u));
}

/* Command Card: 604 wide in a 525 column, so it runs off the panel and the
   frame edge, exactly as in the design. */
.dial__v3-cmd {
    width: calc(604.065 * var(--u));
    box-sizing: border-box;
    padding: calc(14.645 * var(--u)) calc(21.968 * var(--u)) calc(14.645 * var(--u)) calc(25.629 * var(--u));
    display: flex;
    align-items: center;
    gap: calc(20.137 * var(--u));
    background: #fff;
    border-radius: calc(6 * var(--u));
    filter: drop-shadow(0 calc(7.323 * var(--u)) calc(9.153 * var(--u)) rgba(220, 116, 121, 0.2));
}

.dial__v3-cmd + .dial__v3-cmd {
    filter: drop-shadow(0 calc(5.492 * var(--u)) calc(5.492 * var(--u)) rgba(252, 134, 139, 0.2));
}

.dial__v3-cmd-icon {
    flex: none;
    position: relative;
    width: calc(36.613 * var(--u));
    height: calc(34.782 * var(--u));
}

.dial__v3-cmd-icon img {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.dial__v3-cmd-icon--chase img {
    width: calc(28.832 * var(--u));
    height: calc(35.354 * var(--u));
}

.dial__v3-cmd-icon--report img {
    width: calc(35.344 * var(--u));
    height: calc(35.354 * var(--u));
}

.dial__v3-cmd-text {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: calc(3.661 * var(--u));
}

.dial__v3-cmd-text b {
    font-weight: 700;
    font-size: calc(21.97 * var(--u));
    line-height: calc(24.164 * var(--u));
}

.dial__v3-cmd-text p {
    margin: 0;
    font-weight: 500;
    font-size: calc(20.14 * var(--u));
    line-height: calc(25.171 * var(--u));
}

/* the 03 chat bar is the hero bar at 108.29 % (55.47 buttons, 41.6 icons)
   and runs off the frame like the panel above it */
.dial__v3-chat {
    --u: calc(var(--fu) * 1.08286);
    left: calc(1259 * var(--fu));
    top: calc(745.945 * var(--fu));
    width: calc(648 * var(--fu));
    height: calc(164.526 * var(--fu));
    padding: calc(39.917 * var(--fu)) calc(24.267 * var(--fu)) calc(25.581 * var(--fu));
    gap: calc(11.215 * var(--fu));
    border-radius: calc(30.232 * var(--fu));
    box-shadow: 0 calc(1.893 * var(--fu)) calc(9.463 * var(--fu))
        calc(3.785 * var(--fu)) rgba(220, 162, 162, 0.2);
}

.dial__v3-chat .dial__chat-ph {
    height: calc(61.783 * var(--fu));
    display: flex;
    align-items: center;
    font-weight: 400;
    font-size: calc(35 * var(--fu));
}

.dial__v3-chat .dial__row {
    width: calc(600.374 * var(--fu));
}

.dial__v3-caret {
    left: calc(1406.5 * var(--u));
    top: calc(763.91 * var(--u));
}

/* The armed skill: what the pick leaves on the bar, on the line the typed
   text was on. Sized off --fu, like everything else in this bar. */
.dial__v3-chip {
    position: absolute;
    left: calc(24.267 * var(--fu));
    top: calc(22 * var(--fu));
    display: inline-flex;
    align-items: center;
    gap: calc(12 * var(--fu));
    height: calc(48 * var(--fu));
    padding: 0 calc(18 * var(--fu));
    /* a pill, like every other chip on the page */
    border-radius: 999px;
    background: rgba(252, 134, 139, 0.16);
    color: #cf636a;
    font-size: calc(25 * var(--fu));
    line-height: 1;
    white-space: nowrap;
    opacity: 0;
}

.dial__v3-chip code { font-family: inherit; font-weight: 500; }
.dial__v3-chip-x { font-size: calc(27 * var(--fu)); opacity: 0.66; }

/* On the first skill in the list, not beside the bar: the beat this collage
   plays is picking Chase Payment, so the hand is where the pick happens. */
.dial__v3-cursor {
    left: calc(1424 * var(--u));
    top: calc(528 * var(--u));
}

/* -- 04: the Groups tree ---------------------------------------------------- */

.dial__v4-btn {
    left: calc(1388 * var(--u));
    top: calc(276 * var(--u));
    width: calc(266 * var(--u));
    height: calc(85.417 * var(--u));
}

.dial__v4-btn img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.dial__v4-btn span {
    position: absolute;
    margin: 0;
    font-weight: 700;
    line-height: normal;
    text-align: center;
    color: #fff;
    white-space: nowrap;
}

.dial__v4-btn .dial__v4-plus {
    left: calc(35.558 * var(--u));
    top: calc(18.221 * var(--u));
    width: calc(25 * var(--u));
    font-size: calc(36.445 * var(--u));
    line-height: calc(44 * var(--u));
}

.dial__v4-btn .dial__v4-label {
    left: calc(72.89 * var(--u));
    top: calc(22.777 * var(--u));
    width: calc(157 * var(--u));
    font-size: calc(29.611 * var(--u));
    line-height: calc(36 * var(--u));
}

.dial__v4-panel {
    left: calc(1280 * var(--u));
    top: calc(387 * var(--u));
    width: calc(374 * var(--u));
    height: calc(355.607 * var(--u));
}

/* every glyph and label at its own Figma coordinate */
.dial__v4-icon {
    width: calc(21.336 * var(--u));
    height: calc(21.336 * var(--u));
}

.dial__v4-new { left: calc(1601.88 * var(--u)); top: calc(411.53 * var(--u)); width: calc(18.426 * var(--u)); height: calc(18.426 * var(--u)); }
.dial__v4-folder { left: calc(1307.99 * var(--u)); top: calc(410.76 * var(--u)); width: calc(20.661 * var(--u)); height: calc(19.926 * var(--u)); }
.dial__v4-chev-down { left: calc(1434.09 * var(--u)); top: calc(423.02 * var(--u)); width: calc(14.249 * var(--u)); height: calc(7.971 * var(--u)); transform: rotate(180deg); }

.dial__v4-text {
    margin: 0;
    font-weight: 400;
    font-size: calc(21.459 * var(--u));
    line-height: calc(26 * var(--u));
    color: #212529;
    white-space: nowrap;
}

.dial__v4-head { left: calc(1336.713 * var(--u)); top: calc(410.761 * var(--u)); }
.dial__v4-row { left: calc(1365.836 * var(--u)); }

.dial__v4-icon--sales { left: calc(1332.116 * var(--u)); top: calc(453.677 * var(--u)); }
.dial__v4-row--sales { top: calc(454.446 * var(--u)); }
.dial__v4-icon--customers { left: calc(1332.116 * var(--u)); top: calc(501.194 * var(--u)); }
.dial__v4-row--customers { top: calc(501.962 * var(--u)); }
.dial__v4-icon--finance { left: calc(1334.334 * var(--u)); top: calc(548.71 * var(--u)); width: calc(17.025 * var(--u)); height: calc(21.337 * var(--u)); }
.dial__v4-row--finance { top: calc(549.479 * var(--u)); }
.dial__v4-icon--projects { left: calc(1332.116 * var(--u)); top: calc(596.226 * var(--u)); }
.dial__v4-row--projects { top: calc(596.995 * var(--u)); }
.dial__v4-icon--team { left: calc(1332.116 * var(--u)); top: calc(643.743 * var(--u)); }
.dial__v4-row--team { top: calc(644.511 * var(--u)); }
.dial__v4-icon--marketing { left: calc(1332.115 * var(--u)); top: calc(693.486 * var(--u)); width: calc(21.336 * var(--u)); height: calc(16.669 * var(--u)); }
.dial__v4-row--marketing { top: calc(692.028 * var(--u)); }

/* the row chevrons are the forward icon turned a quarter, at 55 % */
.dial__v4-chev {
    width: calc(14.249 * var(--u));
    height: calc(7.971 * var(--u));
    transform: rotate(90deg);
    opacity: 0.55;
}

.dial__v4-chev--sales { left: calc(1444.135 * var(--u)); top: calc(460.651 * var(--u)); }
.dial__v4-chev--customers { left: calc(1499.635 * var(--u)); top: calc(508.167 * var(--u)); }
.dial__v4-chev--finance { left: calc(1468.414 * var(--u)); top: calc(555.684 * var(--u)); }
.dial__v4-chev--projects { left: calc(1472.704 * var(--u)); top: calc(603.2 * var(--u)); }
.dial__v4-chev--team { left: calc(1500.647 * var(--u)); top: calc(650.716 * var(--u)); }
.dial__v4-chev--marketing { left: calc(1490.989 * var(--u)); top: calc(698.233 * var(--u)); }

/* Each team row is one node for the motion below: the wrapper spans the whole
   collage, so the icon, the label and the chevron keep resolving their Figma
   coordinates against the same box they always did. */
.dial__v4-grp {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

.dial__v4-cursor {
    left: calc(1548 * var(--u));
    top: calc(463 * var(--u));
}

/* ---- reduced motion: no blur, no drift ------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    .dial__num span,
    .dial__dot::before,
    .dial__dot::after {
        transition: none;
    }
}

/* ---- narrow screens: the five entries as a plain list --------------------- */

/* ---- phones: the same stepper, the circle moved to the top ---------------
   As on the reference at phone width: the section still captures a run of
   scroll with a sticky viewport-high stage, but the circle is bigger than the
   screen and sits above it, so only its bottom arc crosses the top and the
   numbers ride along that. The entry sits underneath; the product collages
   are far too wide for 390px and are left out. */

@media (max-width: 900px) {
    .dial {
        /* 20 degrees between numbers, so three of them stay on screen on a
           390px-wide arc instead of one. */
        --arc: 20deg;
        height: calc((1 + 3 * 1.9) * 100svh);
    }

    .dial__canvas {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        aspect-ratio: auto;
        --fig-w: 100cqw;
        --u: 1px;
        --fu: 1px;
    }

    /* 800 across, its centre 260px above the screen, so only the bottom of
       the arc crosses the top and everything else is off screen. */
    .dial__ring,
    .dial__wheel {
        left: 50%;
        top: -660px;
        width: 800px;
        height: 800px;
        margin-left: -400px;
    }

    /* The active number lands at the circle's lowest point, not its right. */
    .dial__wheel {
        transform: rotate(calc(90deg - var(--s, 0) * var(--arc)));
    }

    /* The wheel carries a 90 degree base rotation here, so every number is
       turned back by the same amount: the active one reads upright at the
       bottom of the arc, its neighbours lean with the curve, exactly as on
       the wide layout. */
    .dial__num span {
        font-size: 26px;
        transform: rotate(-90deg);
        transform-origin: 50% 50%;
    }

    .dial__num.is-active span {
        transform: translateX(14px) rotate(-90deg);
    }

    .dial__dot {
        left: 50%;
        top: 118px;
        margin-left: -22px;
    }

    /* Clear of the number, which hangs just under the arc at ~183. */
    .dial__entry {
        left: 24px;
        top: 248px;
        width: calc(100% - 48px);
    }

    .dial__title {
        font-size: clamp(28px, 7.8vw, 36px);
        line-height: 1.04;
    }

    .dial__desc {
        width: auto;
        font-size: 15px;
        line-height: 22px;
    }

    /* The collage keeps its Figma geometry and is fitted into the box under
       the entry. dial.js measures each group once (its children's union box
       in Figma px) and, per viewport, the box that is left between the
       tallest entry and the bottom of the stage; --vk is the scale that fits
       the group into that box on both axes, --vtx/--vty the shift that puts
       its top-left on the 24px gutter under the entry. Nothing is cut off
       and the gap to the text is the same in every state. The drift stays
       unscaled, so it reads the same as on the wide layout. */
    .dial__visual {
        transform: translateY(var(--vdrift, 0px))
            translate(var(--vtx, 0px), var(--vty, 0px))
            scale(var(--vk, 1));
        transform-origin: 0 0;
    }

    /* No step in from the frame here: dial.js already scales the group to
       fit the screen, so nothing is cut and pulling it left would only take
       it off that centre. The card still travels to the bar's middle. */
    .dial__visual--3 { --v3-pull: 0; --v3-drop: 0; }


    /* 04 is the tallest collage: six teams make the tree 467 deep. Hiding
       three of them let the rest sit larger, but the point of the picture is
       that every team has its own group, and a list of three does not make
       it. All six stay and the fit scales the group down to suit; the card
       is then narrower than the gutter, which is the better trade.

       The button is the one part the fit does not suit. Blown up with the
       rest, "New group" sets at 30px, the same size as the section's own
       headline, and a decoration in the picture shouts as loud as the title.
       It keeps its own two thirds and lines up with the card's left edge;
       its top is moved down so the gap to the card stays the 25 it has on
       the wide layout. */
    .dial__v4-btn {
        left: calc(1280 * var(--u));
        top: calc(305 * var(--u));
        transform: scale(0.66);
        transform-origin: 0 0;
    }
}

/* ---- arrival -------------------------------------------------------------
   dial.js sets `is-revealed` the first time the section is on screen. The
   ring draws itself, the numbers arrive one after the other and the dot
   lands last. The entry and the collage are left alone: their opacity is
   owned by the scroll mapping. */

.dial__canvas {
    opacity: 0;
    transform: translateY(calc(18 * var(--u)));
    transition: opacity 700ms ease, transform 900ms cubic-bezier(0.22, 1, 0.36, 1);
}

.dial.is-revealed .dial__canvas {
    opacity: 1;
    transform: none;
}

.dial__ring {
    opacity: 0;
    transform: scale(0.96);
    transform-origin: 50% 50%;
    transition: opacity 800ms ease 120ms, transform 1100ms cubic-bezier(0.22, 1, 0.36, 1) 120ms;
}

.dial.is-revealed .dial__ring {
    opacity: 1;
    transform: none;
}

.dial__num span {
    opacity: 0;
    transition: opacity 500ms ease, color 0.35s ease,
        transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.dial.is-revealed .dial__num span { opacity: 1; }
.dial.is-revealed .dial__num:nth-child(1) span { transition-delay: 280ms; }
.dial.is-revealed .dial__num:nth-child(2) span { transition-delay: 380ms; }
.dial.is-revealed .dial__num:nth-child(3) span { transition-delay: 480ms; }
.dial.is-revealed .dial__num:nth-child(4) span { transition-delay: 580ms; }

.dial__dot {
    opacity: 0;
    transform: scale(0.6);
    transform-origin: 50% 50%;
    transition: opacity 400ms ease 620ms,
        transform 700ms cubic-bezier(0.34, 1.56, 0.64, 1) 620ms;
}

.dial.is-revealed .dial__dot {
    opacity: 1;
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .dial__canvas,
    .dial__ring,
    .dial__num span,
    .dial__dot {
        transition-duration: 200ms;
        transition-delay: 0ms;
        transform: none;
    }
}

/* On phones the wheel carries its own base rotation, so the reveal must not
   overwrite its transform. */
@media (max-width: 900px) {
    .dial.is-revealed .dial__ring {
        transform: none;
    }
}

/* ===========================================================================
   Collages in motion
   ---------------------------------------------------------------------------
   Each collage is a still of the product; on its own it only shows the end of
   an interaction. These rules replay the interaction that produced it, once,
   when the step has fully arrived, and then leave the collage exactly as it
   was drawn. dial.js adds `is-live` at full presence and takes it off when the
   step leaves, which is what rearms the loop for the next visit.

   Two rules keep this out of dial.js's way. Everything here animates a CHILD:
   the collage box itself carries the scroll's opacity, blur and --vdrift, and
   is not ours to touch. And every animation is declared only under `.is-live`
   and ends on the child's authored value, so a collage that never goes live,
   or loses the class mid-flight, simply shows the finished frame. Nothing is
   hidden by a base rule, so nothing can go missing if the motion never runs.

   One timing language for all four: the hero's arrival (700ms, the same
   curve, a 16u lift) with a 90ms stagger, and one 480ms out-and-back for
   anything that answers a click. Only transform and opacity move. The blink
   and the typewriter's steps are the two allowed non-eased exceptions.

   Paste at the END of this file: the reduced-motion block at the bottom wins
   on source order, not specificity.
   =========================================================================== */

.dial {
    --dv-in: cubic-bezier(0.22, 1, 0.36, 1);   /* arrivals */
    --dv-out: cubic-bezier(0.32, 0.72, 0, 1);  /* state changes */
    --dv-dur: 700ms;                           /* one entrance, hero's length */
    --dv-step: 90ms;                           /* one stagger */
    --dv-beat: 480ms;                          /* one answer to a click */
}

/* -- nothing is painted before its turn ------------------------------------
   A collage fades in as a whole (dial.js writes its opacity) and only arms
   `is-live` once it is fully there. Everything with an entrance animation was
   therefore shown finished during that fade, then snapped back to zero when
   the animation armed: the content appeared, vanished and came back. These
   parts start at the value their entrance animation begins on, so the fade-in
   carries only the frame and the choreography owns the reveal. The class is
   held until the collage has left again (dial.js), so the way out keeps what
   was played. */

.dial__v1-ask,
.dial__v1-answer strong,
.dial__v1-answer li,
.dial__v1-caret,
.dial__v1-cursor,
.dial__v1-chat .dial__chat-ph,
.dial__v2-menu,
.dial__v2-item-icon,
.dial__v2-name,
.dial__v2-hint,
.dial__v2-cursor,
.dial__v3-card,
.dial__v3-panel,
.dial__v3-cmd,
.dial__v3-caret,
.dial__v3-cursor,
.dial__v3-chat .dial__chat-ph,
.dial__v4-grp,
.dial__v4-cursor {
    opacity: 0;
}

/* the head chevron is turned down in the design and turns there on play, so
   its resting state before the turn is the collapsed one */
.dial__v4-chev-down {
    transform: rotate(90deg);
}

/* -- the shared moves ----------------------------------------------------- */

@keyframes dv-rise {
    from { opacity: 0; transform: translateY(calc(16 * var(--u))); }
    to { opacity: 1; transform: none; }
}

/* a disclosure opens downward, so its rows come from under the head */
@keyframes dv-unfold {
    from { opacity: 0; transform: translateY(calc(-10 * var(--u))); }
    to { opacity: 1; transform: none; }
}

@keyframes dv-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* a pointer drifts in from where it came, as in the hero */
@keyframes dv-cursor-in {
    from { opacity: 0; transform: translate(calc(-22 * var(--u)), calc(26 * var(--u))); }
    to { opacity: 1; transform: none; }
}

/* the row under the pointer answers the press and comes back to rest */
@keyframes dv-press {
    0% { transform: none; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    38% { transform: translateY(calc(3 * var(--u))); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    100% { transform: none; }
}

/* the match is picked out of the list, then the list settles again */
@keyframes dv-lift {
    0% { transform: none; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    38% { transform: translateY(calc(-5 * var(--u))) scale(1.02); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    100% { transform: none; }
}

@keyframes dv-dim {
    0% { opacity: 1; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    38% { opacity: 0.4; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    100% { opacity: 1; }
}

@keyframes dv-pop {
    0% { transform: none; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    38% { transform: scale(1.14); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    100% { transform: none; }
}

/* -- 01: asked in plain words, answered, and the follow-up typed ----------
   The reading order is the product's own: the person's question, then the
   headline Aimee writes, then the agenda one line at a time, and only then
   the follow-up going into the same bar. The typing is a white patch the
   width of the placeholder shrinking away under the caret, because a real
   text reveal would have to animate width, which is layout. */

/* The pointer drifts in with the question, waits under the bar while the
   follow-up is typed (1260 + 880 = 2140), then travels to the send button
   and presses it. Rest is at 1145/867 with the tip at 6%/3% of the 63 box,
   the send button's centre at 1789/772.5, so the tip lands at 1786/770 after
   a 637/-99 move. dv-v1-send is `forwards`, not `both`, so its 0% does not
   pin the pointer at rest during the drift-in it shares the element with. */
.dial__visual--1.is-live .dial__v1-cursor {
    transform-origin: 6% 3%;
    animation:
        dv-cursor-in var(--dv-dur) var(--dv-in) both,
        dv-v1-send 1000ms linear 2350ms forwards;
}

/* the button answers the press at the moment the pointer is down (2350 +
   70% of 1000 = 3050) */
.dial__visual--1.is-live .dial__v1-chat .dial__row-right .dial__row-btn:last-child {
    animation: dv-pop var(--dv-beat) var(--dv-out) 3050ms forwards;
}

@keyframes dv-v1-send {
    0% { transform: none; animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    70% { transform: translate(calc(637 * var(--u)), calc(-99 * var(--u))); animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    82% { transform: translate(calc(637 * var(--u)), calc(-99 * var(--u))) scale(0.92); animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1); }
    100% { transform: translate(calc(637 * var(--u)), calc(-99 * var(--u))); }
}

.dial__visual--1.is-live .dial__v1-answer strong { --in-delay: calc(3 * var(--dv-step)); }
.dial__visual--1.is-live .dial__v1-answer li:nth-child(1) { --in-delay: calc(4 * var(--dv-step)); }
.dial__visual--1.is-live .dial__v1-answer li:nth-child(2) { --in-delay: calc(5 * var(--dv-step)); }
.dial__visual--1.is-live .dial__v1-answer li:nth-child(3) { --in-delay: calc(6 * var(--dv-step)); }

/* -- after the send click (pointer down at 3050, released at 3350) the
   thread scrolls, exactly as the product does: the bar empties, the sent
   bubble comes into view from below, the confirmation follows, and the first
   exchange slides up under the window's top edge instead of disappearing.
   Two steps, measured: 114 brings the bubble's bottom onto the answer's old
   baseline, 300 does the same for the confirmation. */
.dial__visual--1.is-live .dial__v1-ask {
    animation: dv-rise var(--dv-dur) var(--dv-in) both;
}

.dial__visual--1.is-live .dial__v1-thread {
    animation: dv-v1-scroll 1500ms 3400ms forwards;
}

@keyframes dv-v1-scroll {
    0% { transform: none; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    33% { transform: translateY(calc(-114 * var(--u))); animation-timing-function: linear; }
    60% { transform: translateY(calc(-114 * var(--u))); animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    100% { transform: translateY(calc(-300 * var(--u))); }
}

/* The stagger rides `--in-delay`, NOT `animation-delay`: a separate
   animation-delay declaration applies to every animation in the shorthand's
   list, so it would pull the leave animation forward too and the lines would
   vanish before they were read. */
.dial__visual--1.is-live .dial__v1-answer strong,
.dial__visual--1.is-live .dial__v1-answer li {
    animation: dv-rise var(--dv-dur) var(--dv-in) var(--in-delay, 0ms) both;
}

/* the typed line and its caret go with the message */
.dial__visual--1.is-live .dial__v1-chat .dial__chat-ph {
    animation:
        dv-fade 1ms linear both,
        dv-v1-clear 260ms ease 3400ms forwards;
}

.dial__visual--1.is-live .dial__v1-ask2 {
    animation: dv-fade 260ms ease 3400ms forwards;
}

.dial__visual--1.is-live .dial__v1-reply strong {
    animation: dv-rise var(--dv-dur) var(--dv-in) 4250ms forwards;
}

.dial__visual--1.is-live .dial__v1-reply p {
    animation: dv-rise var(--dv-dur) var(--dv-in) 4340ms forwards;
}

@keyframes dv-v1-clear {
    to { opacity: 0; }
}

/* The patch spans the bar's text origin (1200 + 22.41 padding) to the caret
   at 1493.5, which is 271.09, and retreats toward its right edge one step per
   character of "Add it to the calendar". The caret rides the same 22 steps,
   so the two cannot come apart. `position: relative` only anchors the patch;
   it moves nothing. */
.dial__visual--1.is-live .dial__v1-chat .dial__chat-ph {
    position: relative;
}

.dial__visual--1.is-live .dial__v1-chat .dial__chat-ph::after {
    content: "";
    position: absolute;
    left: 0;
    top: calc(-8 * var(--u));
    width: calc(271.09 * var(--u));
    height: calc(48 * var(--u));
    background: #fff;
    transform-origin: 100% 50%;
    animation: dv-type 880ms steps(22, end) 1260ms both;
}

.dial__visual--1.is-live .dial__v1-caret {
    animation:
        dv-fade 200ms ease 1260ms both,
        dv-type-caret 880ms steps(22, end) 1260ms both,
        dv-blink 1060ms step-end 2200ms infinite,
        dv-v1-clear 260ms ease 3400ms forwards;
}

@keyframes dv-type {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}

@keyframes dv-type-caret {
    from { transform: translateX(calc(-271.09 * var(--u))); }
    to { transform: none; }
}

@keyframes dv-blink {
    0% { opacity: 1; }
    50% { opacity: 0; }
    100% { opacity: 1; }
}

/* -- 02: nothing is written without a yes ---------------------------------
   The still shows the permission menu already open with the pointer on
   "Ask first", which is the end of a choice nobody watched being made. So the
   menu grows out of the pill, the two modes arrive as rows, and the pointer
   comes in from outside, hesitates over "Bypass" and presses the safe one
   instead. The hesitation is the whole point of the beat. */

.dial__visual--2.is-live .dial__v2-menu {
    transform-origin: 11% 6%;
    animation:
        dv-open var(--dv-dur) var(--dv-in) both,
        dv-v2-close 300ms var(--dv-out) 1720ms forwards;
}

/* a row moves as a row: its icon, name and hint share one delay */
.dial__visual--2.is-live .dial__v2-icon-2,
.dial__visual--2.is-live .dial__v2-name--2,
.dial__visual--2.is-live .dial__v2-hint--2 {
    animation:
        dv-rise var(--dv-dur) var(--dv-in) calc(3 * var(--dv-step)) both,
        dv-v2-close 300ms var(--dv-out) 1720ms forwards;
}

/* the chosen row arrives first and then answers the press. dv-press is
   `forwards`, not `both`, or its 0% would apply from time 0 and flatten the
   arrival it shares the element with. */
.dial__visual--2.is-live .dial__v2-icon-1,
.dial__visual--2.is-live .dial__v2-name--1,
.dial__visual--2.is-live .dial__v2-hint--1 {
    animation:
        dv-rise var(--dv-dur) var(--dv-in) calc(2 * var(--dv-step)) both,
        dv-press var(--dv-beat) var(--dv-out) 1220ms forwards,
        dv-v2-close 300ms var(--dv-out) 1720ms forwards;
}

.dial__visual--2.is-live .dial__v2-cursor {
    transform-origin: 6% 3%;
    animation:
        dv-v2-pick 1260ms var(--dv-in) calc(2 * var(--dv-step)) both,
        dv-v2-close 300ms var(--dv-out) 1720ms forwards;
}

/* The press lands at 1220 and runs 480. The pill then takes the chosen mode,
   the menu closes behind it and the caret turns back down, so the frame that
   rests carries the label exactly once. */
.dial__visual--2.is-live .dial__v2-label--from,
.dial__visual--2.is-live .dial__v2-bolt {
    animation: dv-v2-swap-out 200ms ease 1400ms forwards;
}

.dial__visual--2.is-live .dial__v2-label--to,
.dial__visual--2.is-live .dial__v2-lock {
    animation: dv-fade 220ms ease 1440ms forwards;
}

.dial__visual--2.is-live .dial__v2-caret {
    animation: dv-v2-caret 320ms var(--dv-out) 1720ms forwards;
}

@keyframes dv-v2-swap-out {
    to { opacity: 0; }
}

@keyframes dv-v2-close {
    to { opacity: 0; transform: translateY(calc(-10 * var(--u))) scale(0.97); }
}

@keyframes dv-v2-caret {
    to { transform: rotate(0deg); }
}

@keyframes dv-open {
    from { opacity: 0; transform: translateY(calc(-12 * var(--u))) scale(0.96); }
    to { opacity: 1; transform: none; }
}

/* in from the menu's lower right, a look at "Bypass" (one row is 89.44
   apart), then up to "Ask first" and a press. It ends on its Figma spot, on
   the row it chose. */
@keyframes dv-v2-pick {
    0% { opacity: 0; transform: translate(calc(64 * var(--u)), calc(140 * var(--u))); }
    32% { opacity: 1; transform: translate(calc(6 * var(--u)), calc(89 * var(--u))); }
    48% {
        opacity: 1;
        transform: translate(calc(6 * var(--u)), calc(89 * var(--u)));
        animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
    }
    72% {
        opacity: 1;
        transform: none;
        animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
    }
    82% {
        opacity: 1;
        transform: scale(0.92);
        animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
    }
    100% { opacity: 1; transform: none; }
}

/* -- 03: the slash opens the palette, the name picks the skill -----------
   The bar starts empty. The pointer drifts in, a "/" is typed, and on that
   slash the Commands palette grows out of the bar from below while the Skills
   card lands beside it and the team's two skills render into the list. Then
   "Chase" is typed, the match is lifted out and the rest steps back. The
   highlight is not held, so the finished frame stays the frame that was
   designed.

   Typing is the 01 recipe: a white patch over the text from the bar's text
   origin (1259 + 24.267 = 1283.27) to the caret at 1406.5, so 123.23 wide,
   retreating toward its right edge in character steps. "/" is 9.5 % of
   "/Chase" in this face, so the first step stops at scaleX(0.905) and the
   caret at the matching -111.5; the second run takes the remaining five
   characters. The patch and the caret ride the same steps, so they cannot
   come apart. Everything in --fu: the bar carries a scaled --u of its own. */

.dial__visual--3.is-live .dial__v3-cursor {
    animation:
        dv-cursor-in var(--dv-dur) var(--dv-in) 1500ms both,
        dv-cursor-press 260ms var(--dv-out) 2200ms forwards,
        dv-fade-out 220ms var(--dv-in) 2440ms forwards;
}

/* the hand dips into the click the row answers with dv-lift */
@keyframes dv-cursor-press {
    0% { transform: none; animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1); }
    45% { transform: translate(calc(1 * var(--u)), calc(3 * var(--u))) scale(0.9); }
    100% { transform: none; }
}

.dial__visual--3.is-live .dial__v3-chat {
    animation: dv-v3-bar-rise 520ms var(--dv-in) 2480ms forwards;
}

.dial__visual--3.is-live .dial__v3-chat .dial__chat-ph {
    position: relative;
    animation:
        dv-fade 1ms linear both,
        dv-fade-out 180ms var(--dv-in) 2440ms forwards;
}

/* the chip lands where the typing was, a beat after the click */
.dial__visual--3.is-live .dial__v3-chip {
    animation: dv-v3-arm 340ms cubic-bezier(0.34, 1.4, 0.64, 1) 2560ms both;
}

@keyframes dv-v3-arm {
    from { opacity: 0; transform: translateY(calc(6 * var(--fu))) scale(0.92); }
    to { opacity: 1; transform: none; }
}

.dial__visual--3.is-live .dial__v3-chat .dial__chat-ph::after {
    content: "";
    position: absolute;
    left: 0;
    top: calc(-6 * var(--fu));
    width: calc(123.23 * var(--fu));
    height: calc(54 * var(--fu));
    background: #fff;
    transform-origin: 100% 50%;
    animation:
        dv-v3-type-slash 120ms steps(1, end) 300ms both,
        dv-v3-type-name 520ms steps(5, end) 1500ms forwards;
}

.dial__visual--3.is-live .dial__v3-caret {
    animation:
        dv-fade 200ms ease 300ms both,
        dv-v3-caret-slash 120ms steps(1, end) 300ms both,
        dv-v3-caret-name 520ms steps(5, end) 1500ms forwards,
        dv-blink 1060ms step-end 2100ms infinite,
        dv-fade-out 180ms var(--dv-in) 2440ms forwards;
}

/* on the slash (typed at 420) the palette comes up out of the bar and the
   card lands with it; the skills render into the list one after the other */
.dial__visual--3.is-live .dial__v3-panel {
    transform-origin: 50% 100%;
    animation:
        dv-v3-open var(--dv-dur) var(--dv-in) 560ms both,
        dv-v3-shut 420ms var(--dv-in) 2480ms forwards;
}

/* the pick taken: the palette drops back into the bar it came out of */
@keyframes dv-v3-shut {
    to { opacity: 0; transform: translateY(calc(64 * var(--u))) scale(0.96); }
}

@keyframes dv-fade-out { to { opacity: 0; } }

/* The palette took up the middle of the picture; with it gone, the bar would
   sit alone at the bottom under a field of nothing. It comes up to meet the
   card instead, so what is left is a compact pair near the top: the skill,
   and the bar it is now armed on. */
@keyframes dv-v3-bar-rise {
    /* 415, not the 448 that would put the bar right under the card: at phone
       scale a 10px gap let the two white bodies read as one shape. The x is
       in --fu, the frame's own unit, not the bar's scaled --u, so it matches
       the card's step to the same centre. */
    to {
        transform: translate(calc(-1 * var(--v3-pull) * var(--fu)),
                             calc(-415 * var(--u) + var(--v3-drop) * var(--fu)));
    }
}

/* The card sat on the palette's left edge, which left it hanging off to one
   side of the much wider bar once the palette dropped. It steps right onto
   the bar's centre on the same beat the palette falls. */
.dial__visual--3.is-live .dial__v3-card {
    animation:
        dv-rise var(--dv-dur) var(--dv-in) 560ms both,
        dv-v3-card-centre 520ms var(--dv-in) 2480ms forwards;
}

@keyframes dv-v3-card-centre {
    from { transform: none; }
    to {
        transform: translate(calc((var(--v3-card-x) - var(--v3-pull)) * var(--fu)),
                             calc(var(--v3-drop) * var(--fu)));
    }
}

.dial__visual--3.is-live .dial__v3-cmd:first-child {
    animation:
        dv-rise var(--dv-dur) var(--dv-in) 740ms both,
        dv-lift var(--dv-beat) var(--dv-out) 2200ms forwards,
        dv-fade-out 260ms var(--dv-in) 2480ms forwards;
}

.dial__visual--3.is-live .dial__v3-cmd + .dial__v3-cmd {
    animation:
        dv-rise var(--dv-dur) var(--dv-in) 830ms both,
        dv-dim var(--dv-beat) var(--dv-out) 2200ms forwards,
        dv-fade-out 260ms var(--dv-in) 2480ms forwards;
}

.dial__visual--3.is-live .dial__v3-cmd:first-child .dial__v3-cmd-icon {
    animation: dv-pop var(--dv-beat) var(--dv-out) 2260ms forwards;
}

@keyframes dv-v3-type-slash {
    from { transform: scaleX(1); }
    to { transform: scaleX(0.905); }
}

@keyframes dv-v3-type-name {
    from { transform: scaleX(0.905); }
    to { transform: scaleX(0); }
}

@keyframes dv-v3-caret-slash {
    from { transform: translateX(calc(-123.23 * var(--fu))); }
    to { transform: translateX(calc(-111.5 * var(--fu))); }
}

@keyframes dv-v3-caret-name {
    from { transform: translateX(calc(-111.5 * var(--fu))); }
    to { transform: none; }
}

/* the palette pops up from the bar: more travel than dv-grow, from below */
@keyframes dv-v3-open {
    from { opacity: 0; transform: translateY(calc(64 * var(--u))) scale(0.96); }
    to { opacity: 1; transform: none; }
}

/* -- 04: each team keeps its own context ----------------------------------
   The still is an open tree, which reads as a list until you see it open. The
   pointer presses the head, the head chevron turns from collapsed to
   expanded, and the six teams come out from under it one after another. The
   pointer then drops onto Sales, where the design parks it.

   The six rows are wrapped per team in a full-canvas layer (see index.html):
   that keeps every glyph on its own Figma coordinate while one transform and
   one opacity carry icon, label and chevron together, and it holds the whole
   collage to eight animated nodes instead of eighteen. */

.dial__v4-grp {
    position: absolute;
    inset: 0;
}

.dial__visual--4.is-live .dial__v4-grp {
    animation: dv-unfold var(--dv-dur) var(--dv-in) both;
}

.dial__visual--4.is-live .dial__v4-grp--sales { animation-delay: calc(7 * var(--dv-step)); }
.dial__visual--4.is-live .dial__v4-grp--customers { animation-delay: calc(8 * var(--dv-step)); }
.dial__visual--4.is-live .dial__v4-grp--finance { animation-delay: calc(9 * var(--dv-step)); }
.dial__visual--4.is-live .dial__v4-grp--projects { animation-delay: calc(10 * var(--dv-step)); }
.dial__visual--4.is-live .dial__v4-grp--team { animation-delay: calc(11 * var(--dv-step)); }
.dial__visual--4.is-live .dial__v4-grp--marketing { animation-delay: calc(12 * var(--dv-step)); }

/* the head chevron is the row chevrons' glyph: at 90deg it points right, like
   theirs, at its authored 180deg it points down */
.dial__visual--4.is-live .dial__v4-chev-down {
    animation: dv-turn var(--dv-beat) var(--dv-out) 570ms both;
}

.dial__visual--4.is-live .dial__v4-cursor {
    transform-origin: 6% 3%;
    animation: dv-v4-open 1700ms var(--dv-in) both;
}

@keyframes dv-turn {
    from { transform: rotate(90deg); }
    to { transform: rotate(180deg); }
}

/* up to the head chevron (109.4 left, 36.9 up of where the design parks the
   pointer), a press, a wait while the teams unfold, then down onto Sales */
@keyframes dv-v4-open {
    0% { opacity: 0; transform: translate(calc(-90 * var(--u)), calc(-18 * var(--u))); }
    26% {
        opacity: 1;
        transform: translate(calc(-109 * var(--u)), calc(-37 * var(--u)));
        animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
    }
    33% {
        opacity: 1;
        transform: translate(calc(-109 * var(--u)), calc(-37 * var(--u))) scale(0.92);
        animation-timing-function: cubic-bezier(0.22, 1, 0.36, 1);
    }
    40% { opacity: 1; transform: translate(calc(-109 * var(--u)), calc(-37 * var(--u))); }
    66% {
        opacity: 1;
        transform: translate(calc(-109 * var(--u)), calc(-37 * var(--u)));
        animation-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
    }
    100% { opacity: 1; transform: none; }
}

/* ---- reduced motion: the finished frame, no motion ----------------------
   Every animation above is declared only under `.is-live` and every one of
   them ends on the value the collage was authored with, so switching them all
   off leaves each collage exactly as it is drawn: the agenda written out and
   the follow-up in the bar, the menu open on "Ask first", the palette listing
   both skills, the tree open on six teams. Nothing else has to be restored.
   This block must stay last in the file; it wins on source order. */

@media (prefers-reduced-motion: reduce) {
    .dial__visual--1.is-live .dial__v1-ask,
    .dial__visual--1.is-live .dial__v1-cursor,
    .dial__visual--1.is-live .dial__v1-answer strong,
    .dial__visual--1.is-live .dial__v1-answer li,
    .dial__visual--1.is-live .dial__v1-caret,
    .dial__visual--1.is-live .dial__v1-thread,
    .dial__visual--1.is-live .dial__v1-chat .dial__chat-ph,
    .dial__visual--1.is-live .dial__v1-ask2,
    .dial__visual--1.is-live .dial__v1-reply strong,
    .dial__visual--1.is-live .dial__v1-reply p,
    .dial__visual--1.is-live .dial__v1-chat .dial__row-right .dial__row-btn:last-child,
    .dial__visual--2.is-live .dial__v2-menu,
    .dial__visual--2.is-live .dial__v2-icon-1,
    .dial__visual--2.is-live .dial__v2-name--1,
    .dial__visual--2.is-live .dial__v2-hint--1,
    .dial__visual--2.is-live .dial__v2-icon-2,
    .dial__visual--2.is-live .dial__v2-name--2,
    .dial__visual--2.is-live .dial__v2-hint--2,
    .dial__visual--2.is-live .dial__v2-cursor,
    .dial__visual--2.is-live .dial__v2-label--from,
    .dial__visual--2.is-live .dial__v2-label--to,
    .dial__visual--2.is-live .dial__v2-bolt,
    .dial__visual--2.is-live .dial__v2-lock,
    .dial__visual--2.is-live .dial__v2-caret,
    .dial__visual--3.is-live .dial__v3-card,
    /* the bar's rise and slide were the one pair this block missed: with them
       still running the bar climbed out from under an open palette */
    .dial__visual--3.is-live .dial__v3-chat,
    .dial__visual--3.is-live .dial__v3-chat .dial__chat-ph,
    .dial__visual--3.is-live .dial__v3-chip,
    .dial__visual--3.is-live .dial__v3-caret,
    .dial__visual--3.is-live .dial__v3-cursor,
    .dial__visual--3.is-live .dial__v3-panel,
    .dial__visual--3.is-live .dial__v3-cmd:first-child,
    .dial__visual--3.is-live .dial__v3-cmd + .dial__v3-cmd,
    .dial__visual--3.is-live .dial__v3-cmd:first-child .dial__v3-cmd-icon,
    .dial__visual--4.is-live .dial__v4-grp,
    .dial__visual--4.is-live .dial__v4-chev-down,
    .dial__visual--4.is-live .dial__v4-cursor {
        animation: none;
    }

    .dial__visual--1.is-live .dial__v1-chat .dial__chat-ph::after,
    .dial__visual--3.is-live .dial__v3-chat .dial__chat-ph::after {
        content: none;
    }
}
@media (prefers-reduced-motion: reduce) {
    /* Nothing plays, so the collage shows the outcome: the mode taken, the
       menu already closed. */
    .dial__visual--2 .dial__v2-label--from,
    .dial__visual--2 .dial__v2-bolt,
    .dial__visual--2 .dial__v2-menu,
    .dial__visual--2 .dial__v2-item-icon,
    .dial__visual--2 .dial__v2-name,
    .dial__visual--2 .dial__v2-hint,
    .dial__visual--2 .dial__v2-cursor {
        opacity: 0;
    }

    .dial__visual--2 .dial__v2-label--to,
    .dial__visual--2 .dial__v2-lock {
        opacity: 1;
    }

    .dial__visual--2 .dial__v2-caret {
        transform: rotate(0deg);
    }
}
