/*
 * ZynoSuite v2 — Lander layer
 *
 * An ADDITIVE marketing layer for the public websites. The core system
 * (styles.css + tokens/) is synced byte-for-byte from the SPA's foundation.css
 * and is deliberately operator-dense: hairline-led, almost no shadow, no
 * motion. Those rules are right for a data surface and leave a landing page
 * looking flat, so the sites layer this file on top of it.
 *
 * Rules of engagement:
 *   1. The core token files are never edited.
 *   2. Everything else is namespaced `.zl-*`; the few `.zs-*` enhancements are
 *      scoped under `body.zl` — a hook the app never carries, on a stylesheet
 *      the app never loads.
 *   3. Light and dark are declared together with `light-dark()`, so a colour
 *      lives in exactly one place and the system default needs no JavaScript.
 *
 * Load AFTER styles.css, and put `class="zl"` on <body>.
 */

/* The dark ramp for the CORE tokens (canvas, ink, lines, accent, semantic) and
   the `color-scheme` resolution that makes `light-dark()` work. Lives in the
   token layer because it is not marketing-specific — the support docs site
   consumes the same file. Everything below is the additive lander layer. */
@import './tokens/dark.css';

body.zl {
    /* ── Elevation ──────────────────────────────────────────────────
       In light, shadows lean cobalt rather than neutral grey so the light and
       the accent agree about where this page's colour comes from — at 6–16%
       over white that reads as a cool grey, not as blue. On a dark ground a
       tinted shadow is invisible, so dark goes near-black at much higher alpha. */
    --zl-el-card: 0 1px 2px light-dark(rgba(28, 48, 110, 0.06), rgba(0, 0, 0, 0.4)),
        0 4px 12px -4px light-dark(rgba(28, 48, 110, 0.07), rgba(0, 0, 0, 0.5));
    --zl-el-card-hover: 0 2px 4px light-dark(rgba(28, 48, 110, 0.07), rgba(0, 0, 0, 0.45)),
        0 14px 30px -10px light-dark(rgba(28, 48, 110, 0.16), rgba(0, 0, 0, 0.62));
    --zl-el-float: 0 2px 6px light-dark(rgba(28, 48, 110, 0.05), rgba(0, 0, 0, 0.4)),
        0 14px 30px -12px light-dark(rgba(28, 48, 110, 0.12), rgba(0, 0, 0, 0.5)),
        0 44px 84px -30px light-dark(rgba(28, 48, 110, 0.24), rgba(0, 0, 0, 0.7)),
        0 0 0 1px light-dark(rgba(17, 17, 15, 0.05), rgba(255, 255, 255, 0.06));
    --zl-el-hero: 0 2px 8px light-dark(rgba(28, 48, 110, 0.06), rgba(0, 0, 0, 0.45)),
        0 18px 40px -14px light-dark(rgba(28, 48, 110, 0.16), rgba(0, 0, 0, 0.55)),
        0 56px 110px -34px light-dark(rgba(28, 48, 110, 0.32), rgba(0, 0, 0, 0.78)),
        0 0 0 1px light-dark(rgba(17, 17, 15, 0.06), rgba(255, 255, 255, 0.07));
    --zl-el-btn: 0 1px 2px light-dark(rgba(30, 63, 168, 0.24), rgba(0, 0, 0, 0.4)),
        0 4px 12px -3px light-dark(rgba(40, 85, 212, 0.32), rgba(92, 134, 242, 0.4));
    --zl-el-btn-hover: 0 2px 4px light-dark(rgba(30, 63, 168, 0.26), rgba(0, 0, 0, 0.45)),
        0 10px 22px -5px light-dark(rgba(40, 85, 212, 0.42), rgba(92, 134, 242, 0.52));

    /* Cards are lit, not flat: a barely-there fall to a fractionally cooler base. */
    --zl-card-bg: linear-gradient(
        180deg,
        light-dark(#ffffff, #1e2842) 0%,
        light-dark(#fdfdfe, #1a2336) 100%
    );

    /* ── Canvas ramp ────────────────────────────────────────────────
       The page descends across five zones, still alternating lifted/recessed,
       so the dark closing band is arrived at rather than cut to. In light it
       runs warm cream → slate: zone 1 is the design system's own --bg, and
       every step after it sheds a little red and gains a little blue. In dark
       it runs light → darker, so the CTA stays the terminus either way.

       Seams cool with their surfaces — a warm hairline on a cool ground is the
       kind of mismatch you feel without being able to name. */
    --zl-z1-bg: light-dark(#f7f6f3, #161e2f);
    --zl-z1-seam: light-dark(#e8e6e0, #232c40);
    --zl-z2-bg: light-dark(#eeeeed, #111827);
    --zl-z2-seam: light-dark(#e2e3e2, #1e2638);
    --zl-z3-bg: light-dark(#f4f5f7, #141c2d);
    --zl-z3-seam: light-dark(#e4e6ea, #212a3e);
    --zl-z4-bg: light-dark(#e9ecf1, #0f1624);
    --zl-z4-seam: light-dark(#dce1e8, #1b2334);
    --zl-z5-bg: light-dark(#f0f3f8, #121a2a);
    --zl-z5-seam: light-dark(#dee4ed, #1f2839);

    /* ── Atmosphere ─────────────────────────────────────────────────── */
    --zl-grid-ink: light-dark(rgba(17, 17, 15, 0.06), rgba(255, 255, 255, 0.05));
    --zl-grid-size: 34px;
    /* Promoted to the token layer as --accent-wash so the support docs site can
       tint its hero with the same value without pulling in this whole layer.
       Kept as a --zl-* alias because the .zl-wash--* helpers below read it. */
    --zl-wash: var(--accent-wash);
    --zl-aurora: light-dark(rgba(40, 85, 212, 0.16), rgba(92, 134, 242, 0.2));
    --zl-aurora-warm: light-dark(rgba(182, 107, 0, 0.06), rgba(251, 191, 36, 0.05));
    --zl-tile-bg: linear-gradient(
        180deg,
        light-dark(#ffffff, #2c3b66) 0%,
        light-dark(#e8edfb, #253358) 62%,
        light-dark(#dce4fa, #1f2c50) 100%
    );
    --zl-tile-hi: light-dark(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.1));

    /* ── Glass (sticky header) ──────────────────────────────────────── */
    --zl-header-bg: light-dark(rgba(247, 246, 243, 0.82), rgba(22, 30, 47, 0.82));
    --zl-header-bg-stuck: light-dark(rgba(250, 250, 249, 0.6), rgba(18, 25, 40, 0.6));
    --zl-glass-hi: light-dark(rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.07));

    /* ── Display type scale ─────────────────────────────────────────── */
    --zl-t-hero: clamp(36px, 4.8vw, 56px);
    --zl-t-section: clamp(30px, 4.2vw, 48px);
    --zl-t-sub: clamp(26px, 3.4vw, 38px);

    /* ── Motion ─────────────────────────────────────────────────────── */
    --zl-ease: cubic-bezier(0.4, 0, 0.2, 1);
    --zl-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --zl-dur: 160ms;
    --zl-dur-reveal: 700ms;
}

/* ═══════════════════════════════════════════════════════════════════
   Display type — the wordmark device, at scale

   The brand's signature is a weight contrast: "Zyno" at 200 beside the product
   name at 500. It only ever appeared in the logo. Here it becomes the headline
   system: the setup line in extralight, the payoff in semibold. The sentence
   itself carries the emphasis, so nothing else has to.

   Extralight needs a hair more tracking than semibold at the same size, or the
   thin strokes close up — hence the per-line letter-spacing.
   ═══════════════════════════════════════════════════════════════════ */

.zl-display {
    margin: 0;
    font-family: var(--font-display);
    line-height: 1.04;
    color: var(--ink);
    /* NOT `balance`: it equalises line lengths, which on a hyphenated headline
       means breaking "Enterprise-grade phone." into "Enterprise-" / "grade
       phone." Greedy wrapping breaks at the space first, which is what these
       two-beat lines want. `pretty` still guards against a stranded last word. */
    text-wrap: pretty;
}

.zl-display .zl-set {
    display: block;
    font-weight: 200;
    letter-spacing: -0.018em;
}

.zl-display .zl-pay {
    display: block;
    font-weight: 600;
    letter-spacing: -0.032em;
}

.zl-display--hero {
    font-size: var(--zl-t-hero);
}

.zl-display--section {
    font-size: var(--zl-t-section);
}

.zl-display--sub {
    font-size: var(--zl-t-sub);
}

/* On the dark closing band both lines go to white — the weight does the work. */
.zl-display--on-chrome,
.zl-display--on-chrome .zl-set,
.zl-display--on-chrome .zl-pay {
    color: #ffffff;
}

/* ═══════════════════════════════════════════════════════════════════
   Bento — feature grid with hierarchy

   Two lead cells carry a small live visual; four supporting cells sit beneath.
   Equal cards say everything matters equally, which is never true.
   ═══════════════════════════════════════════════════════════════════ */

.zl-bento {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 14px;
}

.zl-bento__lead {
    grid-column: span 2;
}

@media (max-width: 900px) {
    .zl-bento {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 600px) {
    .zl-bento {
        grid-template-columns: 1fr;
    }

    .zl-bento__lead {
        grid-column: span 1;
    }
}

/* The inset diagram inside a lead cell — a recessed plate, so it reads as a
   window into the product rather than more card. */
.zl-mini {
    margin-top: 18px;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--bg-sunk);
}

/* ═══════════════════════════════════════════════════════════════════
   Atmosphere — the hero plate
   A cobalt aurora over a fine engineering grid that fades out radially.
   This is the page's one bold moment; everything below it stays quiet.
   ═══════════════════════════════════════════════════════════════════ */

.zl-atmos {
    position: relative;
    isolation: isolate;
}

.zl-atmos::before,
.zl-atmos::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: -1;
}

/* Cobalt aurora, with a barely-there warm counterweight so the wash reads as
   light rather than as a blue tint. Both are existing brand hues. */
.zl-atmos::before {
    background:
        radial-gradient(58% 62% at 74% 14%, var(--zl-aurora), transparent 70%),
        radial-gradient(48% 54% at 8% 4%, var(--zl-aurora-warm), transparent 68%);
}

/* Engineering grid — the phone system is infrastructure; the grid says so. */
.zl-atmos::after {
    background-image:
        linear-gradient(to right, var(--zl-grid-ink) 1px, transparent 1px),
        linear-gradient(to bottom, var(--zl-grid-ink) 1px, transparent 1px);
    background-size: var(--zl-grid-size) var(--zl-grid-size);
    -webkit-mask-image: radial-gradient(64% 58% at 50% 16%, #000 0%, transparent 74%);
    mask-image: radial-gradient(64% 58% at 50% 16%, #000 0%, transparent 74%);
}

/* Same treatment for the dark closing band. Its ground is near-black in dark
   mode, so both layers step up to stay legible against it. */
.zl-atmos--chrome::before {
    background:
        radial-gradient(
            52% 68% at 50% 0%,
            light-dark(rgba(96, 134, 233, 0.24), rgba(122, 162, 247, 0.22)),
            transparent 72%
        ),
        radial-gradient(
            40% 50% at 88% 100%,
            light-dark(rgba(40, 85, 212, 0.16), rgba(92, 134, 242, 0.16)),
            transparent 70%
        );
}

.zl-atmos--chrome::after {
    background-image:
        linear-gradient(
            to right,
            light-dark(rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.055)) 1px,
            transparent 1px
        ),
        linear-gradient(
            to bottom,
            light-dark(rgba(255, 255, 255, 0.045), rgba(255, 255, 255, 0.055)) 1px,
            transparent 1px
        );
    background-size: var(--zl-grid-size) var(--zl-grid-size);
    -webkit-mask-image: radial-gradient(58% 70% at 50% 30%, #000 0%, transparent 76%);
    mask-image: radial-gradient(58% 70% at 50% 30%, #000 0%, transparent 76%);
}

/* ═══════════════════════════════════════════════════════════════════
   Zones — full-bleed grounds along the canvas ramp
   ═══════════════════════════════════════════════════════════════════ */

.zl-zone {
    position: relative;
    isolation: isolate;
    background: var(--zl-canvas, var(--bg));
}

/* One seam between neighbours, owned by the lower zone so its colour matches
   the ground it sits on. */
.zl-zone + .zl-zone {
    border-top: 1px solid var(--zl-seam, var(--line));
}

.zl-zone--1 {
    --zl-canvas: var(--zl-z1-bg);
    --zl-seam: var(--zl-z1-seam);
}

.zl-zone--2 {
    --zl-canvas: var(--zl-z2-bg);
    --zl-seam: var(--zl-z2-seam);
}

.zl-zone--3 {
    --zl-canvas: var(--zl-z3-bg);
    --zl-seam: var(--zl-z3-seam);
}

.zl-zone--4 {
    --zl-canvas: var(--zl-z4-bg);
    --zl-seam: var(--zl-z4-seam);
}

.zl-zone--5 {
    --zl-canvas: var(--zl-z5-bg);
    --zl-seam: var(--zl-z5-seam);
}

/* ── Ambient wash ────────────────────────────────────────────────────
   The hero and the closing band used to be the only places with any colour,
   leaving four screens of flat neutral between them. Each zone now carries one
   large, very soft pool of accent, parked somewhere different each time so the
   eye always has something without ever being shown it.

   Zone 1 is exempt: .zl-atmos is already its wash, and both use ::before. */
.zl-wash::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: -1;
}

.zl-wash--left::before {
    background: radial-gradient(48% 64% at 10% 26%, var(--zl-wash), transparent 72%);
}

.zl-wash--right::before {
    background: radial-gradient(48% 64% at 90% 22%, var(--zl-wash), transparent 72%);
}

.zl-wash--center::before {
    background: radial-gradient(56% 72% at 50% 96%, var(--zl-wash), transparent 74%);
}

/* ═══════════════════════════════════════════════════════════════════
   Surfaces
   ═══════════════════════════════════════════════════════════════════ */

.zl-card {
    position: relative;
    background: var(--zl-card-bg);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: var(--zl-el-card);
    transition:
        transform var(--zl-dur) var(--zl-ease),
        box-shadow var(--zl-dur) var(--zl-ease),
        border-color var(--zl-dur) var(--zl-ease);
}

.zl-card:hover {
    transform: translateY(-2px);
    box-shadow: var(--zl-el-card-hover);
    border-color: var(--accent-tint);
}

/* Highlighted card (the recommended plan). Keeps the accent border it already
   had, and adds the ring so it lifts off the row without shouting. */
.zl-card--accent {
    border-color: var(--accent);
    box-shadow:
        0 0 0 3px var(--accent-tint),
        var(--zl-el-card);
}

.zl-card--accent:hover {
    border-color: var(--accent);
    box-shadow:
        0 0 0 3px var(--accent-tint),
        var(--zl-el-card-hover);
}

/* A resting panel: the card's surface and elevation, but no hover response.
   For large static blocks that aren't clickable — a lift there reads as a
   promise of interaction the panel doesn't keep. */
.zl-card--quiet,
.zl-card--quiet:hover {
    transform: none;
    box-shadow: var(--zl-el-card);
    border-color: var(--line);
}

/* Product screenshots. These are the page's proof, so they get real weight. */
.zl-float {
    box-shadow: var(--zl-el-float);
}

.zl-float--hero {
    box-shadow: var(--zl-el-hero);
}

/* ── Icon tile ──────────────────────────────────────────────────────
   The flat accent-soft square, given a light source: a top-down gradient,
   an inner highlight, and a colored shadow. Responds to its card's hover. */
.zl-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--zl-tile-bg);
    border: 1px solid var(--accent-tint);
    color: var(--accent);
    box-shadow:
        inset 0 1px 0 var(--zl-tile-hi),
        0 1px 2px rgba(40, 85, 212, 0.12);
    transition:
        transform var(--zl-dur) var(--zl-ease),
        box-shadow var(--zl-dur) var(--zl-ease);
}

.zl-card:hover .zl-tile {
    transform: translateY(-1px);
    box-shadow:
        inset 0 1px 0 var(--zl-tile-hi),
        0 5px 12px -3px rgba(40, 85, 212, 0.34);
}

/* ── Suite module chips (the 3×2 grid in the funnel card) ───────────── */
.zl-chip {
    transition:
        transform var(--zl-dur) var(--zl-ease),
        box-shadow var(--zl-dur) var(--zl-ease),
        border-color var(--zl-dur) var(--zl-ease);
}

.zl-chip:hover {
    transform: translateY(-2px);
    border-color: var(--accent-tint);
    box-shadow: 0 6px 14px -6px light-dark(rgba(17, 17, 15, 0.16), rgba(0, 0, 0, 0.5));
}

/* ═══════════════════════════════════════════════════════════════════
   Sticky header — glass

   At the top of the page the bar sits flush with the hero and stays fairly
   opaque. Once content is actually moving underneath it, it earns the glass:
   more blur, more saturation, LESS background, plus a hairline of light along
   its top edge — which is what sells a pane as glass rather than as a tint.
   ═══════════════════════════════════════════════════════════════════ */

.zl-header {
    background: var(--zl-header-bg);
    -webkit-backdrop-filter: saturate(1.5) blur(10px);
    backdrop-filter: saturate(1.5) blur(10px);
    transition:
        background-color 240ms var(--zl-ease),
        box-shadow 240ms var(--zl-ease),
        -webkit-backdrop-filter 240ms var(--zl-ease),
        backdrop-filter 240ms var(--zl-ease);
}

.zl-header.is-stuck {
    background: var(--zl-header-bg-stuck);
    -webkit-backdrop-filter: saturate(2) blur(24px);
    backdrop-filter: saturate(2) blur(24px);
    box-shadow:
        inset 0 1px 0 var(--zl-glass-hi),
        0 1px 3px light-dark(rgba(17, 17, 15, 0.05), rgba(0, 0, 0, 0.4)),
        0 10px 30px -14px light-dark(rgba(17, 17, 15, 0.16), rgba(0, 0, 0, 0.6));
}

/* ── Theme toggle ────────────────────────────────────────────────────
   Parked in the gutter, outside the content column, so it reads as furniture
   rather than as another nav item. Borderless at rest — out there on its own
   it needs no chrome to be findable, and a bordered control would pull more
   attention than a theme switch deserves.

   It shows the scheme you'd switch TO, the convention people read fastest.
   Which glyph is visible must follow the RESOLVED theme, so this is the one
   place the system query is still consulted directly. */
.zl-theme-toggle {
    position: absolute;
    top: 50%;
    right: 20px;
    transform: translateY(-50%);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 8px;
    background: transparent;
    color: var(--ink-4);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    transition:
        color var(--zl-dur) var(--zl-ease),
        border-color var(--zl-dur) var(--zl-ease),
        background-color var(--zl-dur) var(--zl-ease);
}

.zl-theme-toggle:hover {
    color: var(--ink-2);
    border-color: var(--line-2);
    background: var(--surface-2);
}

.zl-theme-toggle:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-tint);
}

/* The toggle sits in the gutter beside the 1180px column. Once the viewport is
   too narrow for that gutter to exist, the bar reserves the room on its right
   instead, so the control can never land on top of the CTA or the burger.
   !important because every site sets its header bar's padding inline. */
@media (max-width: 1272px) {
    .zl-header > div {
        padding-right: 72px !important;
    }
}

.zl-theme-toggle .zl-sun {
    display: none;
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) .zl-theme-toggle .zl-sun {
        display: inline;
    }

    :root:not([data-theme='light']) .zl-theme-toggle .zl-moon {
        display: none;
    }
}

:root[data-theme='dark'] .zl-theme-toggle .zl-sun {
    display: inline;
}

:root[data-theme='dark'] .zl-theme-toggle .zl-moon {
    display: none;
}

:root[data-theme='light'] .zl-theme-toggle .zl-sun {
    display: none;
}

:root[data-theme='light'] .zl-theme-toggle .zl-moon {
    display: inline;
}

/* ═══════════════════════════════════════════════════════════════════
   Component polish — scoped to lander pages only
   ═══════════════════════════════════════════════════════════════════ */

body.zl .zs-btn {
    transition:
        transform var(--zl-dur) var(--zl-ease),
        box-shadow var(--zl-dur) var(--zl-ease),
        background-color var(--zl-dur) var(--zl-ease),
        border-color var(--zl-dur) var(--zl-ease);
}

body.zl .zs-btn--primary {
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0));
    box-shadow: var(--zl-el-btn);
}

body.zl .zs-btn--primary:hover {
    transform: translateY(-1px);
    box-shadow: var(--zl-el-btn-hover);
}

body.zl .zs-btn--dark:hover,
body.zl .zs-btn--on-chrome:hover {
    transform: translateY(-1px);
}

body.zl .zs-btn:active {
    transform: translateY(0);
}

/* Accent fills carry dark ink in dark mode — see the accent note above. The
   BaseLayout hover rules force #fff with !important, so these must too. */
body.zl .zs-btn--primary,
body.zl a.zs-btn--primary:hover,
body.zl .zs-conv__bubble.is-us,
body.zl .zl-pop-badge {
    color: light-dark(#ffffff, #0a1120) !important;
}

/* Buttons inside a product mockup are app UI, not marketing chrome — they must
   keep rendering exactly as the real product does. */
body.zl .zs-mock .zs-btn,
body.zl .zs-mock .zs-btn:hover,
body.zl .zs-mock .zs-btn:active {
    background-image: none;
    box-shadow: none;
    transform: none;
}

/* ═══════════════════════════════════════════════════════════════════
   Motion
   Everything opts in via .zl-reveal, and only hides once the observer script
   has marked the document — so a no-JS render shows the full page.
   ═══════════════════════════════════════════════════════════════════ */

html.zl-js .zl-reveal {
    opacity: 0;
    transform: translateY(16px);
    transition:
        opacity var(--zl-dur-reveal) var(--zl-ease-out),
        transform var(--zl-dur-reveal) var(--zl-ease-out);
    transition-delay: calc(var(--zl-i, 0) * 70ms);
    /* No will-change: it would promote every revealed element to its own
       compositor layer for the life of the page, and opacity/transform are
       already composited. */
}

html.zl-js .zl-reveal.is-in {
    opacity: 1;
    transform: none;
}

/* A row of cards should arrive as a sequence, not as a block. Pages that set
   --zl-i inline still win — inline beats a stylesheet — so an explicit order
   overrides this default wherever one is given. */
.zl-reveal:nth-child(2) {
    --zl-i: 1;
}

.zl-reveal:nth-child(3) {
    --zl-i: 2;
}

.zl-reveal:nth-child(4) {
    --zl-i: 3;
}

.zl-reveal:nth-child(5) {
    --zl-i: 4;
}

.zl-reveal:nth-child(6) {
    --zl-i: 5;
}

/* ── The hero thread, played once ────────────────────────────────────
   It's a phone system; something should arrive. The conversation builds in
   sequence on load, then rests in exactly the state it would render statically
   — the animation adds nothing you'd miss and takes nothing away.

   No fake typing indicator: this is the business's own inbox, where you'd never
   watch yourself type. What actually happens is a message landing, so that's
   what's shown — the bubbles arrive, then the unread badge pops.

   Gated on .zl-js for the same reason as the reveal: without JS these must
   render in their final state, not stuck at the animation's first frame. */

@keyframes zl-msg-in {
    from {
        opacity: 0;
        transform: translateY(10px) scale(0.97);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes zl-pop-in {
    0% {
        opacity: 0;
        transform: scale(0.4);
    }
    70% {
        opacity: 1;
        transform: scale(1.14);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

html.zl-js .zl-msg {
    animation: zl-msg-in 460ms var(--zl-ease-out) both;
    animation-delay: var(--zl-msg-d, 0ms);
}

html.zl-js .zl-ping {
    animation: zl-pop-in 520ms var(--zl-ease-out) both;
    animation-delay: var(--zl-msg-d, 0ms);
}

@media (prefers-reduced-motion: reduce) {
    html.zl-js .zl-reveal,
    html.zl-js .zl-reveal.is-in {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    /* Skip straight to the settled conversation. */
    html.zl-js .zl-msg,
    html.zl-js .zl-ping {
        animation: none !important;
        opacity: 1 !important;
        transform: none !important;
    }

    .zl-card,
    .zl-chip,
    .zl-tile,
    .zl-header,
    .zl-theme-toggle,
    body.zl .zs-btn {
        transition: none !important;
    }

    .zl-card:hover,
    .zl-chip:hover,
    .zl-card:hover .zl-tile,
    body.zl .zs-btn--primary:hover,
    body.zl .zs-btn--dark:hover,
    body.zl .zs-btn--on-chrome:hover {
        transform: none !important;
    }
}
