/* ============================================================================
   ViewChat — Stories, desktop landing (>= 992px only).

   Scoped to `.stories-desktop` / `.vs-*` and to `body.stories-page` inside the
   desktop media query. Nothing here reaches another page: the only selector
   outside the media query is the `display:none` default below, which keeps the
   desktop-only markup out of the mobile document until the mobile pass lands.

   House style: the site's own Plus Jakarta Sans, no display face, no outline,
   no text-shadow, no coloured glow. Depth is carried by one hairline border and
   one short shadow per surface. Spacing and type sit on a 4px grid.
   ========================================================================== */

.stories-desktop { display: none; }

/* The closing row lives inside the desktop main, but the gate above is on the
   <main> itself, so it needs no second one — this is only here for symmetry
   with the mobile file's .vm-foot note: nothing in .vs-close is reachable
   below 992px, because its ancestor is display:none there. */

@media (min-width: 992px) {

  /* --- tokens ------------------------------------------------------------ */
  /* Declared on the body, not on .stories-desktop: the header sits outside
     that subtree and still has to reach the coral. */
  body.stories-page {
    --vs-coral: #fd2944;
    --vs-coral-ink: #e51e39;    /* pressed / hover coral, no glow */
    --vs-ink: #0a0b0e;          /* hero ground                    */
    --vs-panel: #14151b;        /* sampled from the CTA artwork's ground */
    --vs-paper: #f7f8fa;        /* light stages                   */
    --vs-text: #101018;
    --vs-muted: #5b5c68;
    --vs-line: rgba(16, 16, 24, .1);
    --vs-white: #ffffff;
    --vs-cool: #a4a8b4;         /* hero secondary copy            */
    --vs-shell: min(92vw, 1760px);

    /* One slot drives the whole story track. Cards are all the same size; the
       centre one plays at full scale, its neighbours sit back. */
    --vs-card-w: 264px;
    --vs-card-h: 469px;         /* = w / (941/1672), kept in step by hand */
    --vs-card-gap: 20px;
    --vs-card-rest: .86;        /* resting scale of a preview card */

    /* The template header is absolutely positioned, so it floats over the hero
       instead of pushing it down and contributes nothing to layout. The hero
       reserves its measured height itself; the number changes at the widths
       where the nav drops onto a second and third row (112 / 182 / 222). */
    --vs-hdr: 112px;

    --vs-hero-h: 680px;   /* whole dark band, header zone included */
    --vs-title: 56px;
    --vs-stage-title: 44px;

    /* Globe stage: the centre column's width and the height of the phone that
       stands in it. Both side cards and the artwork are sized off the height,
       so one number keeps the whole cluster in proportion. */
    --vs-globe-w: 620px;
    --vs-globe-h: 580px;

    /* Journey rail: the shared height of the four step screens. Each column's
       width follows from it and the file's own ratio, so the pictures are the
       section's main object and none of them is ever cut. */
    --vj-h: 430px;
  }

  /* --- page shell -------------------------------------------------------- */
  .stories-desktop {
    display: block;
    position: relative;
    background: var(--vs-paper);
    font-family: 'Plus Jakarta Sans', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  }

  /* menu.js wraps the document in .wsmenucontainer and menu.css gives it
     overflow:hidden. `clip` keeps the same clipping without turning it into a
     scroll container. Scoped to this page at desktop width, where the
     off-canvas menu those rules exist for is never used. */
  body.stories-page .wsmenucontainer { overflow: clip; }
  body.stories-page #page { overflow: clip; }

  .vs-shell {
    width: var(--vs-shell);
    margin: 0 auto;
  }

  /* --- header ------------------------------------------------------------ */
  body.stories-page .header-wrapper { z-index: 1200; }

  body.stories-page .wsmainfull {
    background-color: #ffffff !important;
    border-bottom: 1px solid rgba(16, 16, 22, .07);
    box-shadow: none !important;
  }

  body.stories-page .wsmenu > .wsmenu-list > li > a.h-link {
    color: #16161b;
    font-size: 15px;
    font-weight: 600;
    letter-spacing: -.005em;
  }

  body.stories-page .wsmenu > .wsmenu-list > li > a.h-link:hover {
    color: var(--vs-coral) !important;
  }

  @media (max-width: 1319.98px) {
    body.stories-page .wsmenu > .wsmenu-list > li > a.h-link { font-size: 14px; }
  }

  @media (max-width: 1199.98px) {
    body.stories-page .wsmenu > .wsmenu-list > li > a.h-link { font-size: 13.5px; }
  }

  body.stories-page .stories-nav-item > a.h-link,
  body.stories-page .stories-nav-item.is-current > a.h-link,
  body.stories-page .stories-nav-item > a.h-link:hover {
    position: relative;
    color: var(--vs-coral) !important;
    font-weight: 700;
  }

  body.stories-page .stories-nav-item.is-current > a.h-link::after {
    content: "";
    position: absolute;
    right: 14px;
    bottom: 22px;
    left: 14px;
    height: 2px;
    border-radius: 2px;
    background: var(--vs-coral);
  }

  /* ======================================================================
     STAGE 01 — HERO
     ====================================================================== */
  /* Flat near-black. One long, very shallow gradient is the whole of the
     depth — no radial blobs, no grain layer. */
  .vs-hero {
    position: relative;
    box-sizing: border-box;
    min-height: var(--vs-hero-h);
    padding-top: var(--vs-hdr);
    display: flex;
    align-items: center;
    overflow: hidden;
    background: linear-gradient(180deg, #0e0f13 0%, #0a0b0e 62%, #08090c 100%);
  }

  .vs-hero__shell {
    display: flex;
    align-items: center;
    gap: 48px;
    padding: 48px 0;
  }

  /* --- hero copy --------------------------------------------------------- */
  .vs-hero__copy {
    flex: 0 0 auto;
    width: 30%;
    min-width: 264px;
  }

  .vs-hero__eyebrow {
    display: block;
    margin-bottom: 20px;
    color: var(--vs-coral);
    font-size: 12px;
    font-weight: 700;
    line-height: 16px;
    letter-spacing: .12em;
    text-transform: uppercase;
  }

  .vs-hero__title {
    margin: 0 0 20px;
    color: var(--vs-white);
    font-size: var(--vs-title);
    font-weight: 700;
    line-height: 1.04;
    letter-spacing: -.03em;
    text-transform: uppercase;
  }

  /* The full stop is the one coral accent in the headline; the span travels
     with the i18n string so every locale keeps it. */
  .vs-hero__title span { color: var(--vs-coral); }

  .vs-hero__lede {
    margin: 0 0 32px;
    max-width: 34ch;
    color: var(--vs-cool);
    font-size: 15px;
    font-weight: 400;
    line-height: 24px;
  }

  .vs-hero__actions {
    display: flex;
    align-items: center;
    gap: 24px;
  }

  /* --- buttons ----------------------------------------------------------- */
  /* Flat fills, hairline borders, no shadow of any colour. */
  .vs-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    font-size: 14px;
    font-weight: 600;
    line-height: 20px;
    letter-spacing: -.002em;
    white-space: nowrap;
    text-decoration: none;
    transition: color .18s ease, background-color .18s ease, border-color .18s ease;
  }

  .vs-btn--coral {
    padding: 12px 24px;
    border-radius: 8px;
    background: var(--vs-coral);
    color: #fff;
  }

  .vs-btn--coral:hover,
  .vs-btn--coral:focus-visible {
    background: var(--vs-coral-ink);
    color: #fff;
  }

  /* The hero's secondary action. One pill of the same geometry and height as
     the coral button next to it — 44px tall, 8px radius, the same type — but
     built out of a translucent dark surface and a single light hairline. The
     play mark sits inside the pill; there is no second circle around it, no
     shadow and no glow. */
  .vs-btn--video {
    gap: 9px;
    padding: 11px 22px;
    border: 1px solid rgba(255, 255, 255, .2);
    border-radius: 8px;
    background: rgba(255, 255, 255, .06);
    color: #fff;
  }

  .vs-btn--video:hover,
  .vs-btn--video:focus-visible {
    border-color: rgba(255, 255, 255, .34);
    background: rgba(255, 255, 255, .11);
    color: #fff;
  }

  .vs-btn__play {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: inherit;
  }

  /* Light-stage pills. Same 44px height as the hero pair; the radius is the
     only thing that changes, because these sit on paper and read as chips. */
  .vs-btn--round {
    padding: 12px 22px;
    border-radius: 999px;
  }

  .vs-btn--pill {
    padding: 11px 20px;
    border: 1px solid rgba(253, 41, 68, .3);
    border-radius: 999px;
    background: #fff;
    color: var(--vs-coral);
  }

  .vs-btn--pill:hover,
  .vs-btn--pill:focus-visible {
    border-color: var(--vs-coral);
    background: #fff;
    color: var(--vs-coral-ink);
  }

  .vs-btn__arrow {
    display: inline-flex;
    align-items: center;
    transition: transform .2s ease;
  }

  .vs-btn:hover .vs-btn__arrow,
  .vs-btn:focus-visible .vs-btn__arrow { transform: translateX(2px); }

  .vs-btn:focus-visible {
    outline: 2px solid var(--vs-coral);
    outline-offset: 3px;
  }

  /* ======================================================================
     STORY TRACK
     ====================================================================== */
  /* A real feed, not four posters: one story plays in the centre slot and the
     track slides so the next one travels into that slot. The edges are masked
     rather than cut, so a half-visible preview fades instead of being sliced. */
  .vs-hero__stage {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    height: var(--vs-card-h);
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  }

  .vs-track {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    display: flex;
    align-items: center;
    gap: var(--vs-card-gap);
    will-change: transform;
    /* Matches SLIDE_MS in js/stories-hero.js. The slide is fired at 97% of the
       progress bar, so the two overlap and there is no dead frame between a
       finished line and the next story arriving. */
    transition: transform .46s cubic-bezier(.22, .61, .36, 1);
  }

  /* The track is placed by JS; during the silent wrap back to the middle copy
     the move must not be animated. */
  .vs-track.is-instant { transition: none; }

  .vs-card {
    position: relative;
    flex: 0 0 auto;
    width: var(--vs-card-w);
    aspect-ratio: 941 / 1672;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, .16);
    background: #101116;
    overflow: hidden;
    cursor: default;
    outline: none;
    /* Preview state: a step back in size and light, never a fade to nothing. */
    transform: scale(var(--vs-card-rest));
    opacity: .5;
    box-shadow: 0 10px 28px rgba(0, 0, 0, .3);
    transition:
      transform .46s cubic-bezier(.22, .61, .36, 1),
      opacity .46s cubic-bezier(.22, .61, .36, 1),
      border-color .3s ease;
  }

  /* Two separate states, because they change at different moments.
     `is-current` is the card in the centre slot: it is handed over when the
     slide starts, so the incoming card grows while it travels instead of
     popping to size after it lands. */
  .vs-card.is-current {
    transform: scale(1);
    opacity: 1;
    /* Slightly brighter hairline and a marginally deeper shadow. No glow. */
    border-color: rgba(255, 255, 255, .28);
    box-shadow: 0 16px 40px rgba(0, 0, 0, .42);
  }

  /* Hover and focus change one colour and nothing else: no transform, no
     scale, no reordering, no move in the track. The card under the cursor
     must never run away from it. */
  .vs-card:hover,
  .vs-card:focus-visible { border-color: rgba(255, 255, 255, .3); }

  .vs-card__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* --- scrim over the baked-in Stories UI -------------------------------- */
  /* Each source photo already carries six progress segments (rows 34-41 of
     1672), the city name and the timestamp burned into its top ~14%. This
     layer is fully opaque down to 14.1% of the card in a colour sampled from
     the picture just below that band, so nothing of the old UI survives, then
     dissolves into the photo by 30%. */
  .vs-card__scrim {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    height: 30%;
    pointer-events: none;
    background:
      linear-gradient(180deg, rgba(6, 7, 10, .58) 0%, rgba(6, 7, 10, .4) 47%, rgba(6, 7, 10, .16) 74%, rgba(6, 7, 10, 0) 100%),
      linear-gradient(180deg, var(--vs-scrim) 0%, var(--vs-scrim) 47%, rgba(0, 0, 0, 0) 100%);
  }

  .vs-card--tokyo   { --vs-scrim: #030f2d; }
  .vs-card--bali    { --vs-scrim: #16210e; }
  .vs-card--vietnam { --vs-scrim: #17130b; }

  /* Seoul's photo is a lit wooden table: its boundary row runs dark on the
     left and warm on the right, so a flat fill would read as a pasted band. */
  .vs-card--seoul .vs-card__scrim {
    background:
      linear-gradient(180deg, rgba(6, 7, 10, .58) 0%, rgba(6, 7, 10, .4) 47%, rgba(6, 7, 10, .16) 74%, rgba(6, 7, 10, 0) 100%),
      linear-gradient(180deg, rgba(0, 0, 0, 0) 47%, rgba(0, 0, 0, .001) 47%, rgba(0, 0, 0, 0) 100%),
      linear-gradient(100deg, #4e4040 0%, #7a6256 52%, #a58468 100%);
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 47%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 47%, transparent 100%);
  }

  /* --- live story chrome ------------------------------------------------- */
  .vs-card__head {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    padding: 14px 16px 0;
    z-index: 2;
  }

  /* Exactly one progress line, and it belongs to the story that is playing. */
  .vs-card__bar {
    display: block;
    height: 2px;
    border-radius: 2px;
    background: rgba(255, 255, 255, .22);
    overflow: hidden;
  }

  .vs-card__bar > i {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    background: #fff;
    transform: scaleX(0);
    transform-origin: 0 50%;
  }

  .vs-card.is-current .vs-card__bar { background: rgba(255, 255, 255, .3); }

  /* `is-playing` is applied on the animation frame after the card reaches the
     centre, and stays on the outgoing card while it leaves, so its line is
     never seen resetting. Matches BAR_MS in js/stories-hero.js. */
  .vs-card.is-playing .vs-card__bar > i { animation: vs-fill 4400ms linear forwards; }

  /* Hovering the track pauses the feed; the line freezes where it stands. */
  .vs-hero__stage.is-paused .vs-card.is-playing .vs-card__bar > i {
    animation-play-state: paused;
  }

  @keyframes vs-fill {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
  }

  .vs-card__city {
    display: block;
    margin-top: 16px;
    color: #fff;
    font-size: 16px;
    font-weight: 700;
    line-height: 20px;
    letter-spacing: -.01em;
  }

  .vs-card__ago {
    display: block;
    margin-top: 2px;
    color: rgba(255, 255, 255, .7);
    font-size: 12px;
    font-weight: 500;
    line-height: 16px;
  }

  /* ======================================================================
     LIGHT STAGES — shared
     ====================================================================== */
  .vs-stage {
    position: relative;
    background: var(--vs-paper);
    overflow: hidden;
  }

  .vs-eyebrow {
    display: block;
    margin-bottom: 16px;
    color: var(--vs-coral);
    font-size: 12px;
    font-weight: 700;
    line-height: 16px;
    letter-spacing: .12em;
    text-transform: uppercase;
  }

  .vs-stage__title {
    margin: 0 0 18px;
    color: var(--vs-text);
    font-size: var(--vs-stage-title);
    font-weight: 700;
    /* Tighter than 1.12: at 44px a two-line headline reads as one block rather
       than two lines that happen to sit together. Same family as the rest of
       the site — no display face is loaded for this page. */
    line-height: 1.06;
    letter-spacing: -.032em;
    text-wrap: balance;
  }

  .vs-stage__title span { color: var(--vs-coral); }

  /* A step up in size and two steps down in lightness: at 15px in #5b5c68 the
     description reads as a caption under the headline, which is not what it
     is. */
  .vs-stage__text {
    margin: 0;
    max-width: 38ch;
    color: #3f4049;
    font-size: 16.5px;
    font-weight: 400;
    line-height: 27px;
    letter-spacing: -.004em;
  }

  /* Shared reveal contract. JS writes a 0..1 scroll progress into --vs-p on
     each phone wrapper; the transform is a pure function of it, so the motion
     is tied to the scroll position and not to a timer after load. */
  .vs-globe__phone {
    --vs-p: 0;
    transform: translate3d(0, calc((1 - var(--vs-p)) * var(--vs-rise)), 0)
               scale(calc(.95 + var(--vs-p) * .05));
    opacity: calc(.05 + var(--vs-p) * .95);
    will-change: transform, opacity;
  }

  .vs-phone__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;   /* mockups are finished devices — never crop them */
  }

  /* ======================================================================
     STAGE 02 — STORIES FROM EVERYWHERE
     ====================================================================== */
  /* Three columns on one row: discover copy, the phone, share copy. The centre
     column is a stage rather than a cell — the globe artwork, the two leaning
     story cards and the phone are stacked in it, so the copy either side keeps
     a straight edge whatever the artwork does. */
  .vs-stage--globe { background: var(--vs-paper); }

  .vs-globe {
    display: grid;
    grid-template-columns: minmax(240px, 1fr) var(--vs-globe-w) minmax(240px, 1fr);
    align-items: center;
    column-gap: 32px;
    max-width: 1560px;
    /* The phone is bottom-anchored in the stage, so whatever is left under it
       is this padding alone. Kept short on purpose: the device and the ribbon
       below read as one block instead of being separated by a white field. */
    padding: 104px 0 26px;
  }

  .vs-globe__col--left  { justify-self: start; }
  .vs-globe__col--right { justify-self: end; }

  .vs-globe__stage {
    position: relative;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    height: var(--vs-globe-h);
  }

  /* The delivered artwork is line work on transparency, so it needs no scrim,
     no mask and no rectangle: it simply sits on the section's own paper. */
  .vs-globe__bg {
    position: absolute;
    top: 46%;
    left: 50%;
    z-index: 0;
    width: calc(var(--vs-globe-w) * 1.18);
    height: auto;
    transform: translate(-50%, -50%);
    pointer-events: none;
  }

  /* Second plane: tilted, dropped below the phone's own stacking level and
     pushed far enough under it that the phone's UI is never covered. */
  .vs-globe__side {
    position: absolute;
    bottom: 27%;
    z-index: 1;
    width: calc(var(--vs-globe-h) * .3);
    height: auto;
    border-radius: 16px;
    box-shadow: 0 2px 6px rgba(16, 18, 34, .1), 0 22px 40px -22px rgba(16, 18, 34, .5);
  }

  .vs-globe__side--l { left: 5%;  transform: rotate(-7deg); }
  .vs-globe__side--r { right: 5%; transform: rotate(7deg); }

  .vs-globe__phone {
    --vs-rise: 150px;
    position: relative;
    z-index: 2;
    height: 100%;
    filter: drop-shadow(0 18px 24px rgba(16, 18, 34, .18));
  }

  .vs-globe__phone .vs-phone__img {
    width: auto;
    height: 100%;
  }

  /* --- country pills ------------------------------------------------------ */
  /* One height, one hairline, one small landmark mark per country. The marks
     are drawn, not emoji: an emoji changes shape on every platform and would
     break the row's rhythm. */
  .vs-places {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin: 28px 0 0;
    padding: 0;
    list-style: none;
  }

  .vs-place {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 38px;
    padding: 0 15px 0 8px;
    border: 1px solid rgba(16, 16, 24, .07);
    border-radius: 999px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(16, 18, 34, .035), 0 10px 22px -16px rgba(16, 18, 34, .3);
  }

  /* Все восемь ячеек несут настоящее фото города вместо иконки-глифа —
     ровно так же, как это уже сделано в мобильной раскладке (.vm-place__pic).
     Пять из них берут те же самые файлы, что и мобильная версия
     (images/stories/place-*.webp), три новые — свои.

     Геометрия та же, что была у прежнего .vs-place__mark: 24px, круг, — и
     строка не сдвинулась ни на пиксель. cover не искажает кадр: все файлы
     квадратные, обрезать ему нечего. Правило живёт внутри общего
     @media (min-width: 992px) этого файла, поэтому мобильной раскладки не
     касается: у неё свой класс, свои размеры и свой набор мест.

     У трёх новых городов в разметке стоит .jpg, а рядом лежит .webp того же
     имени: сервер подменяет его по заголовку Accept — тот же приём, что у
     остальных растровых ассетов проекта. Пять переиспользованных — сразу
     .webp, как в мобильной версии. */
  .vs-place__pic {
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    object-fit: cover;
  }

  .vs-place__name {
    color: var(--vs-text);
    font-size: 13.5px;
    font-weight: 600;
    line-height: 18px;
    letter-spacing: -.005em;
  }

  .vs-globe__col--left .vs-btn { margin-top: 28px; }
  .vs-globe__col--right .vs-btn { margin-top: 26px; }

  /* --- share mode cards -------------------------------------------------- */
  .vs-modes {
    display: flex;
    gap: 12px;
    margin: 34px 0 0;
    padding: 0;
    list-style: none;
  }

  .vs-mode {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 9px;
    width: 92px;
    height: 92px;
    padding: 0 8px;
    border: 1px solid rgba(16, 16, 24, .07);
    border-radius: 14px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(16, 18, 34, .035), 0 10px 22px -14px rgba(16, 18, 34, .28);
    transition: border-color .18s ease, box-shadow .18s ease;
  }

  .vs-mode:hover {
    border-color: rgba(16, 16, 24, .14);
    box-shadow: 0 1px 2px rgba(16, 18, 34, .05), 0 14px 28px -14px rgba(16, 18, 34, .34);
  }

  .vs-mode__ico { display: flex; }

  .vs-mode__label {
    font-size: 12.5px;
    font-weight: 600;
    line-height: 16px;
    letter-spacing: -.005em;
  }

  .vs-mode--photo .vs-mode__ico,
  .vs-mode--photo .vs-mode__label { color: var(--vs-coral); }

  .vs-mode--video .vs-mode__ico,
  .vs-mode--video .vs-mode__label { color: #6d5bd0; }

  .vs-mode--voice .vs-mode__ico,
  .vs-mode--voice .vs-mode__label { color: #2f6fd0; }

  /* ======================================================================
     ACTIVE RIBBON
     ====================================================================== */
  /* The markup holds four identical sets and the track travels by exactly one
     of them (-25%), so the frame after the last set is the first set in the
     same place: no seam, no pause, no reset. Only `transform` is animated. */
  /* System-grey rather than white: the band reads as its own surface between
     two light sections instead of as a gap in them, and the two dividers are
     the only edges it needs. */
  .vs-ribbon {
    overflow: hidden;
    border-top: 1px solid #d2d2d7;
    border-bottom: 1px solid #d2d2d7;
    background: #f5f5f7;
  }

  .vs-ribbon__track {
    display: flex;
    width: max-content;
    animation: vs-ribbon-run 11s linear infinite;
    will-change: transform;
  }

  .vs-ribbon__set {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
  }

  .vs-ribbon__word {
    padding: 19px 0;
    /* Set well off black: at this tracking a hard contrast reads as shouting. */
    color: #57575c;
    font-size: 12.5px;
    font-weight: 600;
    line-height: 18px;
    letter-spacing: .3em;
    text-transform: uppercase;
    white-space: nowrap;
  }

  .vs-ribbon__dot {
    flex: 0 0 auto;
    width: 5px;
    height: 5px;
    margin: 0 44px;
    border-radius: 50%;
    background: var(--vs-coral);
  }

  @keyframes vs-ribbon-run {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-25%, 0, 0); }
  }

  /* ======================================================================
     STAGE 03 — FROM A MOMENT TO A CONNECTION
     ====================================================================== */
  /* Four light steps, no cards and no device frames. Each column is exactly as
     wide as its own screen at the shared height — the ratios below are the
     files' own — so nothing is cropped, nothing is stretched, and the arrows
     land in the real gaps between the pictures rather than in empty padding. */
  .vs-stage--journey { background: #fbfbfc; }

  .vs-journey {
    display: flex;
    align-items: center;
    gap: 40px;
    padding: 96px 0;
  }

  .vs-journey__copy {
    flex: 0 1 auto;
    max-width: 380px;
  }

  .vs-journey__copy .vs-btn { margin-top: 30px; }

  .vs-journey__rail {
    display: flex;
    flex: 1 1 auto;
    align-items: stretch;   /* every column is as tall as the tallest one … */
    justify-content: flex-end;
    gap: 12px;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* The column is the picture's own width at --vj-h, so the box and the file
     agree exactly and `contain` has nothing left to letterbox. */
  .vs-jstep {
    display: flex;
    flex: 0 0 auto;
    flex-direction: column;
    width: calc(var(--vj-h) * var(--vj-ar));
  }

  .vs-jstep--discover { --vj-ar: .488; }    /*  853 x 1748 */
  .vs-jstep--react    { --vj-ar: .489; }    /*  877 x 1794 */
  .vs-jstep--profile  { --vj-ar: .4626; }   /*  853 x 1844 */
  .vs-jstep--call     { --vj-ar: .5625; }   /* 2160 x 3840 */

  .vs-jstep__head {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .vs-jstep__num {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--vs-coral);
    color: #fff;
    font-size: 10.5px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: .01em;
  }

  .vs-jstep__name {
    margin: 0;
    color: var(--vs-text);
    font-size: 16.5px;
    font-weight: 700;
    line-height: 22px;
    letter-spacing: -.026em;
  }

  .vs-jstep__text {
    margin: 10px 0 22px;
    color: #6b6d78;
    font-size: 13.5px;
    font-weight: 400;
    line-height: 20px;
    letter-spacing: -.004em;
  }

  /* … and `auto` pushes every picture to that shared bottom edge, so the four
     screens line up however long the sentences above them run. */
  .vs-jstep__visual {
    position: relative;
    margin-top: auto;
    width: 100%;
    height: var(--vj-h);
  }

  /* `contain` on a box the file's own shape: a belt-and-braces guarantee that
     no screen is ever cut, whatever --vj-h is set to. */
  .vs-jstep__img {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: 20px;
    object-fit: contain;
    box-shadow: 0 1px 2px rgba(16, 18, 34, .05), 0 22px 38px -26px rgba(16, 18, 34, .5);
  }

  /* The one piece of UI that step produces, sitting on the picture's edge. */
  /* Straddling the picture's bottom edge rather than sitting on top of the
     app UI inside it: the chip reads as this step's own state, and it never
     lands on a button that is already in the screenshot. */
  .vs-jchip {
    position: absolute;
    bottom: -13px;
    left: -11px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: calc(100% + 26px);
    padding: 7px 13px 7px 11px;
    border: 1px solid rgba(16, 16, 24, .05);
    border-radius: 999px;
    background: #fff;
    color: #1b1c24;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 16px;
    letter-spacing: -.008em;
    white-space: nowrap;
    /* One contact shadow and one short ambient one, both weak: enough to lift
       the chip off the screenshot, not enough to read as a cut-out sticker. */
    box-shadow: 0 1px 2px rgba(16, 18, 34, .05), 0 8px 18px -10px rgba(16, 18, 34, .26);
  }

  .vs-jchip > span:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .vs-jchip__ico {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    color: var(--vs-coral);
  }

  .vs-jchip__ico--live {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--vs-coral);
  }

  .vs-jchip--follow {
    border-color: transparent;
    background: var(--vs-coral);
    color: #fff;
  }

  .vs-jchip--follow .vs-jchip__ico { color: #fff; }

  /* A hairline chevron on the paper, pinned to the middle of the pictures.
     No disc, no border, no shadow — the step is the object, this is only the
     direction it moves in. */
  .vs-jlink {
    display: flex;
    flex: 0 0 auto;
    align-self: flex-end;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-bottom: calc(var(--vj-h) / 2 - 11px);
    color: #c2c4cd;
  }

  /* --- rail reveal --------------------------------------------------------
     Left to right, one pass, opacity and a short lift. The picture and its own
     UI chip are one element, so they always arrive together. */
  .vs-journey__rail > li {
    opacity: 0;
    transform: translate3d(0, 22px, 0);
    transition: opacity .58s cubic-bezier(.22, .61, .36, 1),
                transform .58s cubic-bezier(.22, .61, .36, 1);
  }

  .vs-journey__rail.is-in > li {
    opacity: 1;
    transform: none;
  }

  .vs-journey__rail > li:nth-child(2) { transition-delay: 90ms; }
  .vs-journey__rail > li:nth-child(3) { transition-delay: 130ms; }
  .vs-journey__rail > li:nth-child(4) { transition-delay: 220ms; }
  .vs-journey__rail > li:nth-child(5) { transition-delay: 260ms; }
  .vs-journey__rail > li:nth-child(6) { transition-delay: 350ms; }
  .vs-journey__rail > li:nth-child(7) { transition-delay: 390ms; }

  /* ======================================================================
     STAGE 04 — FINAL CTA
     ====================================================================== */
  /* The CTA is its own near-black band and it ends where it ends: the white
     footer below starts on a clean edge, with no shared ground, no grey seam
     and nothing overlapping the boundary. The band's colour is the artwork's
     own #14151b, so the picture needs no card, no border and no scrim. */
  .vs-final {
    position: relative;
    padding: 72px 0 0;
    background: linear-gradient(180deg, #fbfbfc 0 72px, var(--vs-panel) 72px 100%);
  }

  /* The padding is the artwork's air. Without it the picture sits flush on the
     band's own edges and reads as hanging out of the block. */
  .vs-final__panel {
    display: flex;
    align-items: center;
    gap: 40px;
    min-height: 300px;
    padding: 56px 0 40px;
  }

  .vs-final__copy {
    flex: 0 0 auto;
    width: 32%;
    max-width: 440px;
  }

  /* Centred in whatever the copy leaves, and bounded on every side by the
     panel's padding: the file is scaled to fit that box, never cropped by it. */
  .vs-final__stage {
    display: flex;
    flex: 1 1 auto;
    justify-content: center;
    min-width: 0;
  }

  .vs-final__art {
    display: block;
    width: 100%;
    max-width: min(100%, 860px);
    height: auto;
    object-fit: contain;
    animation: vs-final-drift 12s ease-in-out infinite alternate;
  }

  /* The avatars are flattened into one raster and cannot be moved apart, so
     the whole composition breathes by 3px instead. Slow enough to read as life
     rather than as a loop. */
  @keyframes vs-final-drift {
    from { transform: translate3d(0, -3px, 0); }
    to   { transform: translate3d(0, 3px, 0); }
  }

  .vs-final__copy {
    position: relative;
    z-index: 2;
    padding: 40px 48px;
  }

  .vs-final__logo {
    display: block;
    width: 132px;
    height: auto;
    margin: 0 0 22px;
  }

  .vs-final__title {
    margin: 0 0 14px;
    color: #fff;
    font-size: 32px;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -.03em;
  }

  .vs-final__title span { color: var(--vs-coral); }

  .vs-final__sub {
    margin: 0 0 26px;
    max-width: 40ch;
    color: rgba(255, 255, 255, .62);
    font-size: 15px;
    font-weight: 400;
    line-height: 24px;
    letter-spacing: -.004em;
  }

  .vs-final__actions {
    display: flex;
    gap: 12px;
  }

  .vs-store {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 11px 20px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: var(--vs-coral);
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    line-height: 20px;
    white-space: nowrap;
    text-decoration: none;
    transition: background-color .18s ease, border-color .18s ease, color .18s ease;
  }

  .vs-store:hover,
  .vs-store:focus-visible {
    background: var(--vs-coral-ink);
    color: #fff;
  }

  .vs-store--android {
    border-color: rgba(255, 255, 255, .22);
    background: transparent;
    color: #fff;
  }

  .vs-store--android:hover,
  .vs-store--android:focus-visible {
    border-color: rgba(255, 255, 255, .5);
    background: transparent;
    color: #fff;
  }

  .vs-store__ico { display: flex; }

  /* --- the closing row ----------------------------------------------------
     The whole of what the page keeps from a footer, and it lives inside the
     CTA band rather than under it: one hairline, the two legal links, the
     contact address, three social marks and the copyright. Same set the mobile
     footer keeps — no columns, no QR, no second block. */
  .vs-close {
    display: flex;
    align-items: center;
    gap: 24px;
    margin-top: 8px;
    padding: 22px 0 40px;
    border-top: 1px solid rgba(255, 255, 255, .1);
  }

  .vs-close__legal,
  .vs-close__socials {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .vs-close__legal { gap: 24px; }

  .vs-close__legal a {
    color: rgba(255, 255, 255, .58);
    font-size: 13.5px;
    font-weight: 500;
    line-height: 20px;
    letter-spacing: -.006em;
    text-decoration: none;
    transition: color .18s ease;
  }

  .vs-close__legal a:hover { color: #fff; }

  /* Pushed to the far end, with the copyright between the two groups. */
  .vs-close__socials {
    order: 3;
    gap: 20px;
  }

  .vs-close__socials a {
    display: flex;
    align-items: center;
    color: rgba(255, 255, 255, .5);
    transition: color .18s ease;
  }

  .vs-close__socials a:hover { color: var(--vs-coral); }

  .vs-close__socials span {
    font-size: 18px;
    line-height: 1;
  }

  .vs-close__copy {
    order: 2;
    flex: 1 1 auto;
    margin: 0;
    color: rgba(255, 255, 255, .38);
    font-size: 12.5px;
    font-weight: 400;
    line-height: 20px;
    text-align: right;
  }

  /* --- no template footer -------------------------------------------------
     The page ends on the block above. The template's own footer — the QR, the
     ViewChat / Help / Company columns and the social column — is hidden rather
     than deleted, because the same markup is every other page's footer and the
     mobile landing's. Scoped to this page and to this breakpoint, so neither
     is touched. */
  body.stories-page footer.footer { display: none; }

  /* ======================================================================
     REDUCED MOTION
     ====================================================================== */
  /* Nothing moves. Tokyo sits in the centre slot with its single bar full, and
     every phone is already at its final position. */
  @media (prefers-reduced-motion: reduce) {
    .vs-track,
    .vs-card { transition: none; }

    .vs-card.is-playing .vs-card__bar > i {
      animation: none;
      transform: scaleX(1);
    }

    .vs-globe__phone {
      transform: none;
      opacity: 1;
    }

    /* The ribbon holds still and the journey rail is simply already there. */
    .vs-ribbon__track { animation: none; }

    .vs-journey__rail > li {
      transition: none;
      transform: none;
      opacity: 1;
    }

    .vs-final__art { animation: none; }
  }

  /* ======================================================================
     BREAKPOINTS
     ====================================================================== */
  /* 1800 and up: the shell has stopped growing at 1760, so the room that keeps
     appearing is between the copy and the rail. Both take it. */
  @media (min-width: 1800px) {
    body.stories-page { --vj-h: 500px; }

    .vs-journey__copy { max-width: 440px; }
  }

  @media (max-width: 1599.98px) {
    body.stories-page {
      --vs-card-w: 246px;
      --vs-card-h: 437px;
      --vs-hero-h: 648px;
      --vs-title: 52px;
      --vs-stage-title: 40px;
      --vs-globe-w: 590px;
      --vs-globe-h: 550px;
      --vj-h: 380px;
    }

    .vs-globe { padding: 92px 0 24px; }
    .vs-journey { gap: 36px; }
    .vs-journey__rail { gap: 11px; }
  }

  /* 1280–1439: the nav is still one row, so only the composition and the
     headline step down to keep the copy column off the track. */
  @media (max-width: 1439.98px) {
    body.stories-page {
      --vs-shell: min(94vw, 1200px);
      --vs-card-w: 216px;
      --vs-card-h: 384px;
      --vs-hero-h: 608px;
      --vs-title: 44px;
      --vs-stage-title: 36px;
      --vs-globe-w: 530px;
      --vs-globe-h: 500px;
      --vj-h: 336px;
    }

    .vs-hero__shell { gap: 40px; }
    .vs-hero__copy { width: 31%; min-width: 252px; }
    .vs-card__city { margin-top: 12px; font-size: 15px; }
    .vs-globe { column-gap: 28px; padding: 84px 0 22px; }
    .vs-journey { gap: 32px; padding: 84px 0; }
    .vs-journey__rail { gap: 11px; }
    .vs-journey__copy { max-width: 340px; }
    .vs-jstep__name { font-size: 15px; }
    .vs-final__title { font-size: 28px; }
    .vs-final__sub { font-size: 14.5px; line-height: 23px; }
  }

  /* 1100–1279: the template nav drops onto a second row here (bar ~182px), so
     the reserved header band grows by the measured difference. */
  @media (max-width: 1279.98px) {
    body.stories-page {
      --vs-shell: min(94vw, 1120px);
      --vs-card-w: 196px;
      --vs-card-h: 348px;
      --vs-card-gap: 16px;
      --vs-hdr: 182px;
      --vs-hero-h: 636px;
      --vs-title: 40px;
      --vs-stage-title: 32px;
      --vs-globe-w: 470px;
      --vs-globe-h: 450px;
      --vj-h: 280px;
    }

    .vs-hero__shell { gap: 32px; padding: 40px 0; }
    .vs-hero__copy { width: 30%; min-width: 244px; }
    .vs-hero__lede { margin-bottom: 28px; font-size: 14px; line-height: 22px; }
    .vs-stage__text { font-size: 14px; line-height: 22px; }
    .vs-card__head { padding: 12px 14px 0; }
    .vs-card__city { margin-top: 12px; font-size: 14px; }
    .vs-globe { column-gap: 24px; padding: 76px 0 20px; }
    .vs-places { gap: 8px; margin-top: 24px; }
    .vs-place { height: 34px; padding: 0 13px 0 7px; }
    .vs-place__pic { width: 21px; height: 21px; }
    .vs-place__name { font-size: 12.5px; }
    .vs-modes { gap: 10px; }
    .vs-mode { width: 84px; height: 84px; }
    .vs-ribbon__word { padding: 17px 0; font-size: 11.5px; letter-spacing: .26em; }
    .vs-ribbon__dot { margin: 0 34px; }
    .vs-journey { gap: 28px; padding: 76px 0; }
    .vs-journey__copy { max-width: 300px; }
    .vs-journey__rail { gap: 10px; }
    .vs-jstep__name { font-size: 15px; }
    .vs-jstep__text { margin: 9px 0 18px; font-size: 12.5px; line-height: 18px; }
    .vs-jchip { padding: 6px 11px 6px 9px; font-size: 11px; }
    .vs-final__panel { min-height: 292px; }
    .vs-final__title { font-size: 26px; }
    .vs-final__logo { width: 118px; margin-bottom: 18px; }
    .vs-final__sub { margin-bottom: 22px; font-size: 14px; line-height: 22px; }
    .vs-close { gap: 18px; padding-bottom: 34px; }
    .vs-close__legal { gap: 18px; }
    .vs-close__legal a { font-size: 13px; }
  }

  /* 992–1099: three header rows (bar ~222px). */
  @media (max-width: 1099.98px) {
    body.stories-page {
      --vs-shell: min(94vw, 1040px);
      --vs-card-w: 176px;
      --vs-card-h: 313px;
      --vs-card-gap: 14px;
      --vs-hdr: 222px;
      --vs-hero-h: 640px;
      --vs-title: 34px;
      --vs-stage-title: 28px;
      --vs-globe-w: 420px;
      --vs-globe-h: 400px;
      --vj-h: 250px;
    }

    .vs-hero__shell { gap: 28px; padding: 32px 0; }
    .vs-hero__copy { width: 30%; min-width: 228px; }
    .vs-hero__lede { margin-bottom: 24px; }
    .vs-hero__actions { gap: 16px; }
    .vs-btn--coral { padding: 11px 18px; }
    .vs-btn--video { padding: 10px 16px; }
    .vs-card__city { font-size: 13px; line-height: 18px; }
    .vs-card__ago { font-size: 11px; }
    .vs-globe { column-gap: 20px; padding: 68px 0 18px; }
    .vs-globe__side { width: calc(var(--vs-globe-h) * .32); }
    .vs-mode { width: 76px; height: 78px; }
    .vs-btn--round,
    .vs-btn--pill { padding: 10px 16px; }
    .vs-journey { gap: 24px; padding: 68px 0; }
    .vs-journey__copy { max-width: 280px; }
    .vs-journey__rail { gap: 8px; }
    .vs-jstep__head { gap: 8px; }
    .vs-jstep__num { width: 22px; height: 22px; font-size: 10px; }
    .vs-jstep__name { font-size: 14px; line-height: 19px; }
    .vs-jstep__text { margin: 8px 0 15px; font-size: 12px; line-height: 17px; }
    .vs-jchip { bottom: -11px; left: -8px; padding: 5px 10px 5px 8px; font-size: 10.5px; }
    .vs-jlink { width: 20px; height: 20px; margin-bottom: calc(var(--vj-h) / 2 - 10px); }
    .vs-final__panel { min-height: 268px; }
    .vs-final__copy { width: 34%; padding: 32px 0; }
    .vs-final__logo { width: 108px; margin-bottom: 16px; }
    .vs-final__title { font-size: 23px; }
    .vs-final__sub { margin-bottom: 20px; font-size: 13.5px; line-height: 21px; }
    .vs-close { gap: 14px; padding-bottom: 30px; }
    .vs-close__legal { gap: 14px; }
    .vs-close__legal a { font-size: 12.5px; }
    .vs-close__copy { font-size: 12px; }
    .vs-store { padding: 10px 16px; font-size: 13px; }
  }
}
