/*
 * push.tt marketing site.
 *
 * Design language follows the category leader: light and airy rather than a
 * dark "tech" page — white and warm-cream sections, photo-led hero, alternating
 * product shots, generous whitespace, orange used sparingly as an accent rather
 * than a wash. The dark treatment is reserved for the product UI itself, which
 * is what appears inside the device frames.
 *
 * Plain HTML + CSS, no build step: a landing page should load instantly on a
 * bad connection, which is the same audience we sell to. Fonts are self-hosted
 * because a security product calling a font CDN hands a visitor log to a third
 * party.
 */

@font-face { font-family: Poppins; font-weight: 400; font-display: swap; src: url("fonts/poppins_regular.ttf") format("truetype"); }
@font-face { font-family: Poppins; font-weight: 500; font-display: swap; src: url("fonts/poppins_medium.ttf") format("truetype"); }
@font-face { font-family: Poppins; font-weight: 600; font-display: swap; src: url("fonts/poppins_semibold.ttf") format("truetype"); }
@font-face { font-family: Poppins; font-weight: 700; font-display: swap; src: url("fonts/poppins_bold.ttf") format("truetype"); }

:root {
  --orange: #ff7a00;
  --orange-ink: #d1560a;          /* orange that passes contrast on white */
  --orange-soft: #fff3e6;
  --grad: linear-gradient(135deg, #ffb04a, #ff9a2b 40%, #ff7a00 75%, #e86a00);

  --ink: #1c2529;                 /* headings */
  --body: #56606b;                /* body copy */
  --muted: #8b939d;
  --line: #e8e4dd;

  --white: #ffffff;
  --cream: #fbf7f2;               /* alternating section wash */
  --cream-deep: #f5efe6;

  /* the product UI is dark — device screens only */
  --app-bg: #0f1115;
  --app-surface: #171a20;
  --green: #3dd68c;

  --maxw: 1140px;
  color-scheme: light;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  background: var(--white);
  color: var(--body);
  font-family: Poppins, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; text-decoration: none; }
.wrap { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }

h1, h2, h3, h4 { color: var(--ink); font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; }

/* ---------------------------------------------------------------- mark */

/*
 * The mark: a broken ring with a centre dot.
 *
 * Geometry is the brand sheet's, and matches the Kotlin implementation exactly
 * — two EQUAL 140° arcs left and right, with 40° gaps centred on 12 and 6.
 * Keep them symmetric: an earlier version here had a 100° left arc against a
 * 140° right one, which read as a logo with its left side chopped off.
 */
/*
 * THE mark — the canonical file, not a redrawing of it.
 *
 * This used to be a `border` under a conic-gradient mask, and that approach
 * cannot produce either of the things that make the mark the mark: a border is
 * one flat colour, so no gradient, and a conic mask cuts the arcs square, so no
 * round caps. The site logo was a lookalike sitting next to the real one.
 *
 * The console needs its ring drawn in code because it tints at runtime with an
 * org's white-label accent. The marketing site never does — it is always push.tt
 * orange — so it can simply USE `icon.svg`, the file raster.mjs copies here from
 * packages/branding/assets/mark.svg. That is as close to "the same as the app"
 * as it is possible to get: the same bytes.
 *
 * It also removes a copy of the geometry. This file used to be one of the places
 * CLAUDE.md warns you have to keep in step; it no longer is.
 *
 * The url is relative to THIS STYLESHEET, which sits at the site root, so it
 * resolves for pages at any depth.
 */
.mark {
  position: relative; flex: none; aspect-ratio: 1;
  background: url("icon.svg") center / contain no-repeat;
}
.wordmark { font-weight: 700; letter-spacing: -0.025em; color: var(--ink); }
.wordmark .tt { color: var(--orange); }

/* ---------------------------------------------------------------- nav */

header {
  position: sticky; top: 0; z-index: 60;
  background: rgba(255, 255, 255, 0.88);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
nav { display: flex; align-items: center; height: 64px; gap: 30px; }
nav .brand { display: flex; align-items: center; gap: 9px; flex: none; }
nav .brand .mark { width: 26px; }
nav .brand .wordmark { font-size: 1.14rem; }
nav .links { display: flex; gap: 24px; }
/*
 * Direct children only. These rules are for the top-level bar items; without
 * the child combinator `white-space: nowrap` also lands on every link inside
 * the mega-menu, so the item descriptions cannot wrap and run out of the card.
 */
nav .links > a {
  font-size: 0.9rem; font-weight: 500; color: var(--body);
  white-space: nowrap;   /* "White label" must never wrap mid-item */
}
nav .links > a:hover { color: var(--ink); }
nav .right { margin-left: auto; display: flex; gap: 8px; align-items: center; flex: none; }
nav .right .btn { white-space: nowrap; }

/*
 * The hero is dark and the page below it is light, so the bar has to switch.
 * `.over` is the at-rest state (sitting on the hero); a scroll listener drops it
 * once the hero is behind us. Transitioned so the swap isn't a flash.
 */
header { transition: background 0.25s, border-color 0.25s; }
header.over {
  background: rgba(14, 16, 21, 0.55);
  border-bottom-color: rgba(255, 255, 255, 0.08);
}
header.over .wordmark { color: #fff; }
/* Direct children only: the panel is white even when the bar is dark, so the
   light-on-dark colour must not leak into the menu items inside it. */
header.over .links > a { color: #b9c0c9; }
header.over .links > a:hover { color: #fff; }
header.over .btn-ghost {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.18);
  color: #fff;
}
header.over .btn-ghost:hover { background: rgba(255, 255, 255, 0.13); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 12px 22px; border-radius: 9px;
  font-weight: 600; font-size: 0.94rem; font-family: inherit;
  border: 1px solid transparent; cursor: pointer;
  transition: filter 0.15s, transform 0.15s, background 0.15s;
}
.btn:hover { filter: brightness(1.04); transform: translateY(-1px); }
.btn-primary { background: var(--orange); color: #fff; }
.btn-ghost { background: #fff; border-color: var(--line); color: var(--ink); }
.btn-quiet { color: var(--orange-ink); background: var(--orange-soft); }
.btn-sm { padding: 8px 16px; font-size: 0.87rem; }

/* ---------------------------------------------------------------- hero */

/*
 * Dark, glossy hero over a light page.
 *
 * The centrepiece is the logo at scale: the mark's centre dot rendered as an
 * actual lit sphere, wrapped by the broken ring. It reads as the push-to-talk
 * button, which is the one gesture the whole product is about — so the brand
 * mark and the primary interaction are literally the same object.
 */
.hero {
  position: relative;
  /*
   * Pulled up behind the sticky bar so the bar's backdrop-filter samples the
   * dark hero instead of the white body — otherwise a translucent dark bar over
   * white renders grey.
   */
  margin-top: -65px;
  /* No bottom padding: the tablet is meant to be cut off by the fold. */
  padding: 150px 0 0;
  text-align: center;
  color: #fff;
  background:
    radial-gradient(900px 520px at 50% 8%, rgba(255, 122, 0, 0.20), transparent 62%),
    radial-gradient(700px 420px at 82% 78%, rgba(255, 154, 43, 0.13), transparent 60%),
    linear-gradient(168deg, #14161d 0%, #0e1015 46%, #16110d 100%);
  overflow: hidden;
}
/*
 * Film grain. Large, smooth, dark gradients band visibly on 8-bit displays —
 * the wide radial glows here are exactly the worst case. A few percent of noise
 * dithers the steps away. Inline SVG so it costs no request.
 */
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: 3;
  pointer-events: none; opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}
.hero .wrap { position: relative; z-index: 2; }
.hero h1 { color: #fff; }
.hero .lede { color: #b9c0c9; }
.hero .note { color: #79808b; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 16px; border-radius: 100px; margin-bottom: 22px;
  background: rgba(255, 122, 0, 0.12);
  border: 1px solid rgba(255, 122, 0, 0.3);
  font-size: 0.8rem; font-weight: 600; color: #ffb066;
}
.eyebrow .dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 3px rgba(61, 214, 140, 0.2);
}
.hero .btn-ghost { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.16); color: #fff; }
.hero .btn-ghost:hover { background: rgba(255, 255, 255, 0.11); }

/* --------------------------------------------------- the sphere */

/*
 * The stage is authored in a fixed 1180 x 640 design space and scaled as one
 * unit. The console mockup inside it has far too much internal structure to
 * reflow safely at every width — scaling keeps every proportion exact, and the
 * wrapper's height tracks the scale so layout below stays correct.
 */
.stage-wrap {
  --ss: 1;
  position: relative; margin-top: 58px;
  height: calc(740px * var(--ss));
}
.stage {
  position: absolute; top: 0; left: 50%;
  width: 1180px; height: 740px;
  transform: translateX(-50%) scale(var(--ss));
  transform-origin: top center;
}

/* holds the ring and the orb concentrically */
.orbit {
  position: absolute; top: -50px; left: 50%;
  width: 1820px; aspect-ratio: 1;
  transform: translateX(-50%);
}

/* ambient bloom behind everything */
.orbit::before {
  content: ""; position: absolute; inset: 4%;
  background: radial-gradient(circle at 50% 42%, rgba(255, 130, 10, 0.4) 0%,
              rgba(255, 110, 0, 0.15) 32%, transparent 58%);
  filter: blur(20px);
}

/*
 * The lit sphere. Layer order matters:
 *   1. body gradient with the light source up-left
 *   2. inset terminator shadow for roundness
 *   3. warm bounce light from the lower-right rim
 *   4. a small specular highlight
 */
/*
 * The orb is a globe: the same lit orange sphere, with the world's coastlines
 * on it and the far hemisphere showing faintly through, so it reads as
 * translucent glass rather than a printed ball.
 *
 * The material lives in the inlined SVG (apps/site/tools/build-globe.mjs) —
 * gradients have to be inside the element that carries the map, or the map
 * would sit on top of an inset box-shadow it should be lit by. Only the outer
 * glow stays in CSS, since that paints outside the box.
 */
.orb {
  position: absolute; inset: 10%; z-index: 1; border-radius: 50%;
  /* Sits at 0.9 so the sphere reads as glass over the dark hero rather than as
     a solid ball. Applies to the outer glow too, which is the point — dimming
     the body while leaving the bloom at full strength makes it look lit from
     inside rather than translucent. */
  opacity: 0.9;
  box-shadow:
    0 60px 170px rgba(190, 70, 0, 0.5),
    0 0 240px rgba(255, 122, 0, 0.3);
}
/*
 * Three stacked layers, in physical order: the lit sphere, then the map on it,
 * then the specular highlight — which is a reflection on the glass and so must
 * paint over the coastlines, not under them.
 */
.orb > * { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.globe-map { border-radius: 50%; }

/*
 * The two arcs cradling the globe — the mark's strokes at scale, with the globe
 * standing in for its centre dot, rendered as an atmospheric shell.
 *
 * Real SVG arcs rather than the conic-gradient mask used before, because the
 * mark's strokes have ROUNDED ends and a mask can only produce flat radial cuts.
 * Same geometry as `.mark`: two 140-degree arcs, 40-degree gaps on 12 and 6.
 * The round caps eat about 3 degrees at each end, which lands the visible gap
 * right where the brand sheet has it.
 *
 * The material is in the inlined SVG, which is where the layering has to live.
 * All that is left here is the bloom that paints OUTSIDE the element box — kept
 * wide and weak, because the haze pass inside the SVG now does most of that
 * work and stacking the two at full strength turns the shell into a lamp.
 */
/*
 * The bloom lives on a WRAPPER, not on the svg, and that is load-bearing.
 *
 * CSS applies filter before mask, so with drop-shadow on the masked element the
 * glow was generated from the untapered arcs and then sliced by the mask —
 * drawing a long straight blade across a broad soft area, far more visible than
 * the hard cap the mask was added to remove. On a parent it runs on already
 * tapered content and has nothing to cut.
 */
.halo-glow {
  position: absolute; inset: 0; display: block;
  filter: drop-shadow(0 0 52px rgba(255, 128, 12, 0.18));
}
.halo {
  position: absolute; inset: 0;
  opacity: 0.9;

  /*
   * Dissolves both ends of both arcs.
   *
   * The thing being fixed: a stroke ENDS, and its round cap is a shape. Because
   * the shell's gradient is radial, the cap sweeps across the bright limb and
   * draws a hooked crescent — the tips read as commas rather than as gas
   * running out.
   *
   * Two other approaches were built and rejected, and their failures are why
   * this one looks the way it does:
   *   - A Gaussian blur on an SVG mask is ISOTROPIC. Widened enough to dissolve
   *     25 degrees of arc it ate the shell radially long before it softened the
   *     tip, and the graduated-dash variant that fixed that took over two
   *     minutes to rasterise at 3x. Far too expensive for a hero.
   *   - A short conic ramp left a visible ray, which turned out to be the
   *     drop-shadow being cut — see .halo-glow above.
   *
   * A conic gradient is the right shape (the fade has to run AROUND the circle,
   * and SVG has no conic gradient) and costs nothing. Angles are the mark's
   * own, offset 90 degrees because conic-gradient starts at 12 o'clock while
   * the SVG arcs are measured from 3: bodies at 20-160 and 200-340, gaps of 40
   * centred on 12 and 6.
   *
   * Alpha is fully zero out to 22 degrees — two degrees past the arc's
   * geometric start — so the round cap never paints at all. Dimming it is not
   * enough; the cap is a shape, and at even a tenth of its opacity the crescent
   * comes back. The 30-degree ramp then opens on a very gentle toe (0.02, 0.08,
   * 0.22) so no single isoline carries a step big enough to see.
   *
   * Keep in step with the mark's arcs if that geometry ever moves
   * (packages/branding/assets/mark.svg).
   */
  --halo-fade: conic-gradient(from 0deg,
      transparent 0deg 22deg,
      rgba(0, 0, 0, 0.02) 27deg,
      rgba(0, 0, 0, 0.08) 32deg,
      rgba(0, 0, 0, 0.22) 37deg,
      rgba(0, 0, 0, 0.44) 42deg,
      rgba(0, 0, 0, 0.72) 47deg,
      #000 52deg 128deg,
      rgba(0, 0, 0, 0.72) 133deg,
      rgba(0, 0, 0, 0.44) 138deg,
      rgba(0, 0, 0, 0.22) 143deg,
      rgba(0, 0, 0, 0.08) 148deg,
      rgba(0, 0, 0, 0.02) 153deg,
      transparent 158deg 202deg,
      rgba(0, 0, 0, 0.02) 207deg,
      rgba(0, 0, 0, 0.08) 212deg,
      rgba(0, 0, 0, 0.22) 217deg,
      rgba(0, 0, 0, 0.44) 222deg,
      rgba(0, 0, 0, 0.72) 227deg,
      #000 232deg 308deg,
      rgba(0, 0, 0, 0.72) 313deg,
      rgba(0, 0, 0, 0.44) 318deg,
      rgba(0, 0, 0, 0.22) 323deg,
      rgba(0, 0, 0, 0.08) 328deg,
      rgba(0, 0, 0, 0.02) 333deg,
      transparent 338deg 360deg);
  -webkit-mask-image: var(--halo-fade);
  mask-image: var(--halo-fade);
}

/* --------------------------------------------------- devices on the globe */

/*
 * Handsets, tablets and radios pinned to places on the sphere.
 *
 * The overlay matches the orb's box exactly (both inset 10% of .orbit), which
 * is what lets devices.js position by the projection's own -1..1 disc
 * coordinates and multiply by half the box. Above the orb, below the tablet.
 *
 * Three nested transforms, deliberately, because they change on different
 * clocks and would fight if combined: .gd is written every frame with the
 * position, .gd-depth every frame with foreshortening, and .gd-inner is a CSS
 * transition the pop class drives. One element carrying all three would have
 * the per-frame writes cancelling the transition on every tick.
 */
.orb-devices { position: absolute; inset: 10%; z-index: 2; pointer-events: none; }

.gd {
  position: absolute; left: 50%; top: 50%;
  opacity: 0; transition: opacity 0.45s ease;
  will-change: transform;
}
.gd.is-near { opacity: 1; }
.gd-depth, .gd-inner { display: block; }
.gd-inner {
  /* Overshoots slightly and settles — the "pop". A plain ease reads as a
     resize; the overshoot is what makes it feel like it jumped forward. */
  transition: transform 0.42s cubic-bezier(0.22, 1.4, 0.36, 1);
  transform-origin: 50% 100%;
  /* Positioned, so the tag's `top: 100%` anchors under the DEVICE. Without it
     the tag resolved against .gd — a zero-height positioning anchor — and sat
     on top of the body where it was invisible. */
  position: relative;
}
.gd.pop .gd-inner { transform: scale(1.55); }

/*
 * Real hardware, drawn as SVG. Sizes are the drawn viewBox scaled to fit the
 * globe: big enough to tell a rugged handset from a slab phone from a tablet,
 * small enough that eight of them read as devices on a map rather than clutter.
 */
.gd-body { display: block; position: relative; }
.dsvg { display: block; overflow: visible; }
.gd-pixel  .dsvg { width: 38px; }
.gd-galaxy .dsvg { width: 36px; }
.gd-rugged .dsvg { width: 44px; }
.gd-tablet .dsvg { width: 72px; }

/* A dark chassis on a lit orange sphere reads as a hole punched in the globe;
   the pale rim and the shadow are what put it ON the surface. */
.dsvg .d-body, .dsvg .d-bump {
  stroke: rgba(255, 226, 196, 0.3); stroke-width: 0.9;
  filter: drop-shadow(0 2px 3px rgba(60, 20, 0, 0.55));
}
.dsvg .d-bump { fill: #4d5563; }
.dsvg .d-screen { fill: #0d1015; }
.dsvg .d-cam { fill: #0a0d11; }
.dsvg .d-key, .dsvg .d-ant { fill: #4a515b; }
.dsvg .d-ptt { fill: #6b7482; transition: fill 0.25s ease; }
.dsvg .d-grille rect { fill: #262c35; }
.dsvg .d-rows rect { fill: #39414d; }
.dsvg .d-ring { stroke: #4a5361; transition: stroke 0.25s ease; }
.dsvg .d-dot, .dsvg .d-live { fill: #6a727d; transition: fill 0.25s ease; }

/* ---- talking and listening ---- */

/*
 * Same semantics as the app: orange is you transmitting, cyan is somebody else
 * talking. Reusing them here means the hero teaches the colour language the
 * product actually uses, rather than decorating with it.
 */
.gd.is-tx .dsvg .d-ring { stroke: var(--orange); }
.gd.is-tx .dsvg .d-dot,
.gd.is-tx .dsvg .d-live { fill: #ffd9a8; }
.gd.is-tx .dsvg .d-ptt { fill: var(--orange); }
.gd.is-rx .dsvg .d-ring { stroke: #2fd4de; }
.gd.is-rx .dsvg .d-dot,
.gd.is-rx .dsvg .d-live { fill: #cbfaff; }

.gd.is-tx .dsvg .d-body { filter: drop-shadow(0 0 5px rgba(255, 122, 0, 0.95)); }
.gd.is-rx .dsvg .d-body { filter: drop-shadow(0 0 5px rgba(47, 212, 222, 0.9)); }

/*
 * The place name, only while popped — eight permanent labels would be a map,
 * not a hero. It carries its own dark pill because pale text sitting directly
 * on a lit orange sphere is unreadable at 9px.
 */
.gd-tag {
  position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  margin-top: 7px; white-space: nowrap;
  padding: 2px 7px; border-radius: 100px;
  background: rgba(12, 15, 19, 0.82);
  font-size: 9px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: #ffd9a8; opacity: 0; transition: opacity 0.3s ease;
}
.gd.pop .gd-tag { opacity: 1; }

/* Below this the globe itself is only a few hundred px — devices would be
   specks, and seven of them would read as noise on the coastlines. */
@media (max-width: 760px) { .orb-devices { display: none; } }

/* --------------------------------------------------- the product stage */

/*
 * Scenes take turns. Each is parked small and invisible at the point on the
 * globe its device sits on, and grows from there to rest in the middle of the
 * stage; showcase.js supplies --dx/--dy as the vector back to that device.
 *
 * The start state has to live on `.scene` rather than a modifier class, because
 * a scene has to be ALREADY at its start position on the frame the transition
 * begins — set both in the same frame and the browser collapses them and the
 * mockup simply appears, fully grown, with no travel.
 */
.showcase { position: absolute; inset: 0; z-index: 3; }

.scene {
  --dx: 0px; --dy: 0px;
  position: absolute; left: 50%; top: 330px;
  transform: translate(-50%, 0) translate(var(--dx), var(--dy)) scale(0.1);
  opacity: 0; visibility: hidden;
  transition:
    transform 0.62s cubic-bezier(0.22, 0.92, 0.24, 1),
    opacity 0.36s ease,
    visibility 0s linear 0.62s;
}
.scene.on {
  transform: translate(-50%, 0) scale(1);
  opacity: 1; visibility: visible;
  transition:
    transform 0.62s cubic-bezier(0.22, 0.92, 0.24, 1),
    opacity 0.34s ease,
    visibility 0s;
}
/* Leaving is quicker than arriving — a mockup that lingers on the way out
   collides with the next one coming up. */
.scene.off { transition-duration: 0.42s, 0.28s, 0s; transition-delay: 0s, 0s, 0.42s; }
.scene.static { transform: translate(-50%, 0) scale(1); opacity: 1; visibility: visible; transition: none; }

/* The tablet keeps its looks but hands positioning to its scene. */
.sc-tablet .tablet { position: relative; top: auto; left: auto; transform: none; }

/* ---- phone frame ---- */

/*
 * Handsets start higher up the stage than the console does. The stage is 740px
 * tall and the hero crops whatever runs past it — fine for the dashboard, whose
 * content is top-loaded, but it was slicing the bottom off the push-to-talk
 * ring, which is the one thing these scenes exist to show.
 */
.sc-phone, .sc-radio { top: 128px; }

.pdev {
  /* The hero centres its text; an app screen must not. Same trap the console
     tablet already carries a note about. */
  text-align: left;
  width: 330px; border-radius: 40px; padding: 11px;
  background: linear-gradient(158deg, #3a4049 0%, #23272f 26%, #0f1216 100%);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.09) inset,
    0 46px 90px rgba(0, 0, 0, 0.62),
    0 10px 30px rgba(0, 0, 0, 0.5);
}
.pscreen {
  height: 592px; border-radius: 30px; overflow: hidden;
  background: var(--app-bg); display: flex; flex-direction: column;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6);
}

/* top tab bar — order matches the real app: mic, people, chat */
.ptop { display: flex; align-items: center; gap: 4px; padding: 13px 14px 0; }
.pback {
  width: 17px; height: 17px; flex: none; margin-right: 8px;
  border-left: 2px solid #d9dbdf; border-bottom: 2px solid #d9dbdf;
  transform: rotate(45deg) scale(0.7);
}
.ptab { flex: 1; display: grid; place-items: center; padding-bottom: 9px; position: relative; color: #79808b; }
.ptab svg { width: 19px; height: 19px; }
.ptab.on { color: var(--orange); }
.ptab.on::after {
  content: ""; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 26px; height: 2.5px; border-radius: 2px; background: var(--orange);
}
.pkebab { display: grid; gap: 2.5px; margin-left: 8px; }
.pkebab i { width: 3px; height: 3px; border-radius: 50%; background: #d9dbdf; display: block; }

/* channel header */
.pchan {
  display: flex; align-items: center; gap: 11px; padding: 11px 14px;
  border-bottom: 1px solid #23272e; background: #14171c;
}
.pavatar {
  position: relative; width: 40px; height: 40px; flex: none; border-radius: 12px;
  display: grid; place-items: center;
  background: rgba(47, 212, 222, 0.12); border: 1.5px solid rgba(47, 212, 222, 0.55);
  color: #2fd4de; font-weight: 700; font-size: 16px;
}
.pavatar i {
  position: absolute; right: -3px; bottom: -3px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--green); border: 2px solid var(--app-bg);
}
.pchan-t { flex: 1; min-width: 0; }
.pchan-t b { display: block; font-size: 16px; color: #fff; }
.pchan-t em { display: block; font-style: normal; font-size: 11.5px; color: var(--green); margin-top: 1px; }
.pchev {
  width: 9px; height: 9px; flex: none;
  border-right: 2px solid #8b919c; border-bottom: 2px solid #8b919c; transform: rotate(45deg);
}

.pbody { flex: 1; display: flex; flex-direction: column; padding: 14px; min-height: 0; }
.prow-top { display: flex; align-items: center; gap: 12px; }
.pcam {
  width: 20px; height: 16px; flex: none; border: 2px solid #8b919c; border-radius: 4px;
}
.pemg {
  margin: 0 auto; padding: 7px 16px; border-radius: 100px;
  border: 1.5px solid rgba(255, 93, 93, 0.75); color: #ff7b7b;
  font-size: 11px; font-weight: 700; letter-spacing: 0.1em;
}

.pfoot { display: flex; align-items: center; gap: 14px; padding: 0 16px 16px; }
.pico { width: 17px; height: 17px; border: 2px solid #79808b; border-radius: 4px; flex: none; }
.pbars { margin-left: auto; display: flex; gap: 2.5px; align-items: flex-end; }
.pbars i { width: 3.5px; background: var(--green); border-radius: 1px; display: block; }
.pbars i:nth-child(1) { height: 5px; }
.pbars i:nth-child(2) { height: 8px; }
.pbars i:nth-child(3) { height: 11px; }
.pbars i:nth-child(4) { height: 14px; }

/* ---- the push-to-talk ring ---- */

.pring {
  margin: auto; width: 214px; height: 214px; border-radius: 50%;
  position: relative; display: grid; place-items: center;
}
.pring-track {
  position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 50% 44%, #1a1e25, #0e1116);
  box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.028);
}
.pring-arc {
  position: absolute; inset: 0; border-radius: 50%;
  border: 5px solid #b4600d;
  transition: border-color 0.25s ease, box-shadow 0.25s ease, border-width 0.2s ease;
}
.pring-mic { position: relative; z-index: 2; color: #fff; }
.pring-mic svg { width: 46px; height: 46px; display: block; }
.pring-cap {
  text-align: center;
  position: absolute; bottom: 46px; left: 50%; transform: translateX(-50%); z-index: 2;
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap;
  color: #d9dbdf; opacity: 0;
}
.cap-idle { opacity: 1; }
.pring-pulse { position: absolute; inset: 0; }
.pring-pulse i {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(255, 138, 0, 0.5); opacity: 0;
}

/* live speech level, under the ring */
.plevel { display: flex; align-items: flex-end; justify-content: center; gap: 4px; height: 30px; }
.plevel i {
  width: 4px; height: 5px; border-radius: 2px; background: #2b313a; display: block;
  transform-origin: 50% 100%;
}

/*
 * Talk scene. One press: idle, key up, talk, release, then somebody answers.
 * Timings are shared across the ring, the caption and the level meter, so they
 * have to move together — change one and the mic appears to key itself.
 */
.sc-phone[data-scene="talk"].play .pring-arc { animation: talk-arc 6s ease-in-out both; }
.sc-phone[data-scene="talk"].play .cap-idle { animation: cap-idle 6s linear both; }
.sc-phone[data-scene="talk"].play .cap-tx { animation: cap-tx 6s linear both; }
.sc-phone[data-scene="talk"].play .cap-rx { animation: cap-rx 6s linear both; }
.sc-phone[data-scene="talk"].play .pring-pulse i { animation: talk-pulse 1.5s ease-out 3; }
.sc-phone[data-scene="talk"].play .pring-pulse i:nth-child(2) { animation-delay: 0.75s; }
.sc-phone[data-scene="talk"].play .plevel i { animation: talk-level 0.62s ease-in-out infinite alternate; }
/* The meter only exists while the mic is actually open. Left running it showed
   speech level during "Hold to talk" and while somebody else was talking. */
.sc-phone[data-scene="talk"] .plevel { opacity: 0; }
.sc-phone[data-scene="talk"].play .plevel { animation: cap-tx 6s linear both; }

@keyframes talk-arc {
  0%, 12%   { border-color: #b4600d; box-shadow: none; border-width: 5px; }
  16%, 52%  { border-color: #ff8a00; box-shadow: 0 0 34px rgba(255, 122, 0, 0.55); border-width: 8px; }
  58%, 62%  { border-color: #b4600d; box-shadow: none; border-width: 5px; }
  68%, 100% { border-color: #2fd4de; box-shadow: 0 0 34px rgba(47, 212, 222, 0.45); border-width: 6px; }
}
@keyframes cap-idle { 0%, 12% { opacity: 1; } 14%, 100% { opacity: 0; } }
@keyframes cap-tx   { 0%, 14% { opacity: 0; } 16%, 56% { opacity: 1; color: #ffb04a; } 58%, 100% { opacity: 0; } }
@keyframes cap-rx   { 0%, 64% { opacity: 0; } 68%, 100% { opacity: 1; color: #2fd4de; } }
@keyframes talk-pulse {
  0%   { transform: scale(1); opacity: 0.6; }
  100% { transform: scale(1.35); opacity: 0; }
}
@keyframes talk-level { from { transform: scaleY(1); background: #2b313a; } to { transform: scaleY(3.4); background: var(--orange); } }
.plevel i:nth-child(2n)  { animation-duration: 0.44s !important; }
.plevel i:nth-child(3n)  { animation-duration: 0.78s !important; }
.plevel i:nth-child(5n)  { animation-duration: 0.53s !important; }

/* ---- voice log ---- */

.pbody-log { padding: 12px 0 0; }
.pdate {
  align-self: center; padding: 4px 12px; border-radius: 100px; background: #1d222a;
  font-size: 10.5px; font-weight: 600; color: #8b919c; margin-bottom: 10px;
}
.plog { flex: 1; }
.plogrow {
  display: flex; gap: 11px; padding: 11px 14px; border-bottom: 1px solid #1e232a;
  opacity: 0;
}
.pav {
  width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid; place-items: center;
  font-size: 11px; font-weight: 700;
}
.pav-you { background: rgba(255, 122, 0, 0.18); border: 1.5px solid var(--orange); color: #ffb04a; }
.pav-lf  { background: rgba(214, 178, 61, 0.16); border: 1.5px solid #d6b23d; color: #e8ca63; }
.pav-dm  { background: rgba(47, 212, 222, 0.14); border: 1.5px solid #2fd4de; color: #7fe9f0; }
.plog-t { min-width: 0; }
.plog-t b { display: block; font-size: 13.5px; color: #fff; }
.plog-t em { display: block; font-style: normal; font-size: 11px; color: #8b919c; margin: 1px 0 4px; }
.ptrans { display: block; font-size: 12.5px; color: #c9ced6; line-height: 1.45; }
/* The transcription line, produced on the sending device — it types itself in
   because that is the one thing on this screen a still frame cannot show. */
.ptype { white-space: nowrap; overflow: hidden; width: 0; }
.pcaret {
  display: inline-block; width: 1.5px; height: 12px; background: var(--orange);
  vertical-align: -2px; margin-left: 2px;
}
.sc-phone[data-scene="history"].play .plogrow { animation: log-in 0.5s ease-out both; }
.sc-phone[data-scene="history"].play .plogrow:nth-child(2) { animation-delay: 0.5s; }
.sc-phone[data-scene="history"].play .plogrow:nth-child(3) { animation-delay: 1.0s; }
.sc-phone[data-scene="history"].play .ptype { animation: type-in 2.4s steps(30, end) 1.6s both; }
.sc-phone[data-scene="history"].play .pcaret { animation: caret 0.6s steps(1) 1.6s 7; }
@keyframes log-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes type-in { from { width: 0; } to { width: 100%; } }
@keyframes caret { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

.pplay {
  display: flex; align-items: center; justify-content: flex-end; gap: 20px;
  padding: 12px 18px; border-top: 1px solid #1e232a;
}
.prate { font-size: 12px; font-weight: 700; color: #d9dbdf; }
.pskip { width: 11px; height: 12px; background: #d9dbdf; clip-path: polygon(0 0, 100% 50%, 0 100%); }
.pskip:last-child { transform: scaleX(-1); }
.pplaybtn { width: 15px; height: 17px; background: var(--orange); clip-path: polygon(0 0, 100% 50%, 0 100%); }
.pcompose { display: flex; align-items: center; gap: 12px; padding: 12px 16px 18px; }
.pplus { width: 15px; height: 15px; flex: none; position: relative; }
.pplus::before, .pplus::after {
  content: ""; position: absolute; inset: 0; margin: auto; background: #d9dbdf;
}
.pplus::before { width: 15px; height: 2px; }
.pplus::after { width: 2px; height: 15px; }
.pinput {
  flex: 1; padding: 10px 14px; border-radius: 10px; border: 1px solid #2c3138;
  font-size: 12.5px; color: #6f7783;
}
.psend { width: 17px; height: 15px; background: #8b919c; clip-path: polygon(0 0, 100% 50%, 0 100%, 22% 50%); }

/* ---- emergency ---- */

.pbanner {
  border-radius: 12px; padding: 13px 15px; margin-bottom: 14px;
  background: rgba(255, 93, 93, 0.13); border: 1px solid rgba(255, 93, 93, 0.55);
  opacity: 0; transform: translateY(-12px);
}
.pbanner-h { display: block; font-size: 13px; font-weight: 700; color: #ff8f8f; letter-spacing: 0.02em; }
.pbanner-s { display: block; font-size: 11px; color: #c9ced6; margin-top: 3px; }
.packs { display: flex; align-items: center; gap: 5px; margin-top: 10px; }
.packs i {
  width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center;
  font-style: normal; font-size: 9px; font-weight: 700;
  background: #23272e; color: #d9dbdf; border: 1px solid #3a4049; opacity: 0;
}
.packs em { font-style: normal; font-size: 10.5px; color: #8b919c; margin-left: 4px; opacity: 0; }
.pemg-live { border-color: #ff5d5d; color: #ff5d5d; }

.sc-phone[data-scene="emergency"].play .pemg-live { animation: emg-press 6s ease-in-out both; }
.sc-phone[data-scene="emergency"].play .pring-arc { animation: emg-arc 6s ease-in-out both; }
.sc-phone[data-scene="emergency"].play .cap-hold { animation: cap-idle 6s linear both; }
.sc-phone[data-scene="emergency"].play .cap-live { animation: cap-rx 6s linear both; }
.sc-phone[data-scene="emergency"].play .pbanner { animation: emg-banner 6s ease-out both; }
.sc-phone[data-scene="emergency"].play .packs i { animation: ack-in 0.4s ease-out both; }
.sc-phone[data-scene="emergency"].play .ack1 { animation-delay: 3.1s; }
.sc-phone[data-scene="emergency"].play .ack2 { animation-delay: 3.7s; }
.sc-phone[data-scene="emergency"].play .ack3 { animation-delay: 4.4s; }
.sc-phone[data-scene="emergency"].play .packs em { animation: ack-in 0.4s ease-out 4.9s both; }
.sc-phone[data-scene="emergency"].play .pring-pulse i { animation: emg-pulse 1.4s ease-out 2.6s infinite; }
.sc-phone[data-scene="emergency"].play .pring-pulse i:nth-child(2) { animation-delay: 3.3s; }

@keyframes emg-press {
  0%, 18%   { background: transparent; box-shadow: none; }
  26%, 42%  { background: rgba(255, 93, 93, 0.22); box-shadow: 0 0 22px rgba(255, 93, 93, 0.5); }
  46%, 100% { background: rgba(255, 93, 93, 0.16); box-shadow: 0 0 14px rgba(255, 93, 93, 0.35); }
}
@keyframes emg-arc {
  0%, 40%   { border-color: #b4600d; box-shadow: none; }
  46%, 100% { border-color: #ff5d5d; box-shadow: 0 0 34px rgba(255, 93, 93, 0.5); border-width: 8px; }
}
@keyframes emg-banner {
  0%, 40%  { opacity: 0; transform: translateY(-12px); }
  50%, 100% { opacity: 1; transform: none; }
}
@keyframes ack-in { from { opacity: 0; transform: scale(0.5); } to { opacity: 1; transform: none; } }
@keyframes emg-pulse {
  0%   { transform: scale(1); opacity: 0.55; border-color: rgba(255, 93, 93, 0.6); }
  100% { transform: scale(1.3); opacity: 0; border-color: rgba(255, 93, 93, 0.6); }
}

/* ---- the handheld radio ---- */

.rdev {
  text-align: left;
  position: relative; width: 232px; padding: 20px 18px 26px; border-radius: 22px;
  background: linear-gradient(158deg, #3f454e 0%, #23272f 30%, #121519 100%);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.1) inset,
    0 46px 90px rgba(0, 0, 0, 0.62),
    0 10px 30px rgba(0, 0, 0, 0.5);
}
.rant {
  position: absolute; top: -46px; right: 26px; width: 11px; height: 52px; border-radius: 6px;
  background: linear-gradient(180deg, #4a515b, #23272e);
}
.rscreen {
  border-radius: 10px; padding: 14px 13px; background: #0b0e12;
  box-shadow: 0 0 0 1px #2c3138 inset;
}
.rchan { display: block; font-size: 17px; font-weight: 700; color: #fff; letter-spacing: 0.03em; margin-bottom: 14px; }
.rmeta { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; }
.rmeta i { font-style: normal; font-size: 10.5px; font-weight: 600; color: #8b919c; letter-spacing: 0.08em; }
.rsig { width: 26px; height: 9px; background: linear-gradient(90deg, var(--green) 70%, #2b313a 70%); border-radius: 2px; }
.rgrille { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; margin-top: 20px; }
.rgrille i { height: 5px; border-radius: 3px; background: #1a1e25; display: block; }
.rknobs { display: flex; gap: 12px; justify-content: center; margin-top: 20px; }
.rknobs span {
  width: 42px; height: 42px; border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #4a515b, #1c2027);
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
}
.rptt {
  position: absolute; left: -7px; top: 96px; width: 9px; height: 66px; border-radius: 5px;
  background: linear-gradient(180deg, #4a515b, #23272e);
  transition: background 0.2s ease, box-shadow 0.2s ease;
}
.sc-radio.play .rptt { animation: r-ptt 4.6s ease-in-out both; }
/*
 * The radio's transmit state is carried by the PTT bar and the screen glow
 * alone. There was a MONITORING/TRANSMITTING label here that swapped between
 * two stacked spans; it computed correctly at every offset on both opacity and
 * visibility and still painted the idle one, and rather than keep chasing it or
 * ship a label capable of showing the wrong state, it is gone. The bar and the
 * glow say the same thing and demonstrably work.
 */
.sc-radio.play .rscreen { animation: r-screen 4.6s ease-in-out both; }
@keyframes r-ptt {
  0%, 12%   { background: linear-gradient(180deg, #4a515b, #23272e); box-shadow: none; }
  18%, 76%  { background: linear-gradient(180deg, #ffb04a, #e86a00); box-shadow: 0 0 18px rgba(255, 122, 0, 0.7); }
  84%, 100% { background: linear-gradient(180deg, #4a515b, #23272e); box-shadow: none; }
}
@keyframes r-screen {
  0%, 12%   { box-shadow: 0 0 0 1px #2c3138 inset; }
  18%, 76%  { box-shadow: 0 0 0 1px rgba(255, 122, 0, 0.55) inset, 0 0 24px rgba(255, 122, 0, 0.18); }
  84%, 100% { box-shadow: 0 0 0 1px #2c3138 inset; }
}

/* The device this scene came from is over here now, so dim its dot. */
.gd.is-away .dsvg { opacity: 0.35; }
.gd.is-away .gd-tag { opacity: 0; }

/*
 * Below this the stage is scaled so far down that a 330px phone is thumbnail
 * sized and its animated detail is illegible — the console, which is mostly
 * large numbers, is the one that still reads. So it parks and the cycling stops.
 */
@media (max-width: 760px) {
  .scene { display: none; }
  .sc-tablet { display: block; transform: translate(-50%, 0) scale(1); opacity: 1; visibility: visible; }
}

/* --------------------------------------------------- the console tablet */

/*
 * Landscape tablet standing in front of the orb, cropped by the bottom of the
 * hero. The screen is a faithful rebuild of the console's Dashboard — same
 * panes, same nav, same badge semantics — because a product shot that shows a
 * UI we don't ship is a lie that costs us on the first demo call.
 */
.tablet {
  position: absolute; z-index: 3; top: 330px; left: 50%;
  width: 1010px; transform: translateX(-50%);
  text-align: left;   /* the hero centres its text; a console pane must not be */
  border-radius: 26px; padding: 13px;
  background: linear-gradient(158deg, #363c46 0%, #23272f 26%, #14171c 100%);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.08) inset,
    0 50px 90px rgba(0, 0, 0, 0.62),
    0 10px 30px rgba(0, 0, 0, 0.5);
}
.tscreen {
  display: flex; height: 640px;
  border-radius: 15px; overflow: hidden;
  background: var(--app-bg);
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6);
}

/* sidebar */
.tside {
  width: 232px; flex: none; padding: 20px 14px;
  background: #171a20; border-right: 1px solid #2c3138;
}
.tlogo { display: flex; align-items: center; gap: 10px; padding: 0 8px 22px; }
.tlogo .mark { width: 26px; }
.tname { font-size: 15px; font-weight: 700; color: #fff; letter-spacing: -0.02em; }
.tname .tt { color: var(--orange); }
.torg { font-size: 10.5px; color: #8b919c; margin-top: 1px; }
.tnav {
  padding: 9px 12px; border-radius: 8px; margin-bottom: 2px;
  font-size: 13px; font-weight: 500; color: #d9dbdf;
}
.tnav.on { background: rgba(255, 122, 0, 0.15); color: var(--orange); font-weight: 600; }

/* main pane */
.tmain { flex: 1; padding: 22px 24px; min-width: 0; }
.tmain h3 { font-size: 21px; color: #fff; margin-bottom: 4px; }
.tmain > p { font-size: 12px; color: #8b919c; margin-bottom: 18px; }

.tstats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; }
.tstat {
  background: #171a20; border: 1px solid #2c3138;
  border-radius: 10px; padding: 12px 13px;
}
.tstat b { display: block; font-size: 22px; font-weight: 700; color: #fff; letter-spacing: -0.02em; }
.tstat b.ok { color: var(--green); }
.tstat span { font-size: 10.5px; color: #8b919c; }

.tcard {
  margin-top: 16px; padding: 16px 18px 4px;
  background: #171a20; border: 1px solid #2c3138; border-radius: 12px;
}
.tcard h4 { font-size: 15px; color: #fff; margin-bottom: 6px; }
.trow { padding: 11px 0; border-bottom: 1px solid #2c3138; }
.tline { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.tline strong { font-size: 13px; font-weight: 600; color: #fff; }
.trow em { display: block; font-style: normal; font-size: 11px; color: #8b919c; margin-top: 4px; }
.tbadge {
  flex: none; font-size: 10.5px; font-weight: 600;
  padding: 4px 10px; border-radius: 20px; white-space: nowrap;
}
.tbadge.ok { color: var(--green); background: rgba(61, 214, 140, 0.14); }
.tbadge.warn { color: #ffab48; background: rgba(255, 154, 43, 0.15); }

/*
 * One knob. Everything in the stage is design-space px, so the whole scene —
 * orb, ring, tablet, and every label inside it — stays in proportion.
 *
 * Each step is sized for the NARROWEST width in its bucket, so the tablet lands
 * around 85-88% of the viewport and never touches the edges. The ring is wider
 * than the tablet by design and bleeds off both sides, which is the point.
 */
@media (max-width: 1160px) { .stage-wrap { --ss: 0.92; } }
@media (max-width: 1060px) { .stage-wrap { --ss: 0.83; } }
@media (max-width: 960px)  { .stage-wrap { --ss: 0.75; } }
@media (max-width: 860px)  { .stage-wrap { --ss: 0.66; } }
@media (max-width: 760px)  { .stage-wrap { --ss: 0.57; } }
@media (max-width: 660px)  { .stage-wrap { --ss: 0.48; } }
@media (max-width: 560px)  { .stage-wrap { --ss: 0.41; } }
@media (max-width: 480px)  { .stage-wrap { --ss: 0.35; } }
@media (max-width: 400px)  { .stage-wrap { --ss: 0.28; } }

h1 { font-size: clamp(2.2rem, 5vw, 3.4rem); margin-bottom: 18px; }
.hero .lede { font-size: 1.08rem; max-width: 34rem; margin: 0 auto 30px; }
.cta-row { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.hero .note { font-size: 0.84rem; color: var(--muted); margin-top: 18px; }

/* ---------------------------------------------------------------- logos */

.logos { padding: 26px 0 66px; }
.logos .row {
  display: flex; gap: 46px; justify-content: center; align-items: center;
  flex-wrap: wrap; opacity: 0.5;
}
.logos .lg {
  font-weight: 700; font-size: 1.05rem; color: var(--ink);
  letter-spacing: -0.02em; white-space: nowrap;
}
.logos .cap {
  text-align: center; font-size: 0.76rem; color: var(--muted);
  letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 24px;
}

/* ---------------------------------------------------------------- sections */

section { padding: 92px 0; }
.wash { background: var(--cream); }
.wash-deep { background: var(--cream-deep); }

.kicker {
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--orange-ink); margin-bottom: 12px;
}
h2 { font-size: clamp(1.75rem, 3.4vw, 2.45rem); margin-bottom: 16px; }
.sub { font-size: 1.04rem; max-width: 36rem; }
.head-center { text-align: center; }
.head-center .sub { margin-inline: auto; }
.head { margin-bottom: 52px; }

/* alternating feature rows */
.row2 {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: 72px; align-items: center;
}
.row2 + .row2 { margin-top: 104px; }
.row2.flip .visual { order: 2; }
.row2 h3 { font-size: clamp(1.4rem, 2.6vw, 1.95rem); margin-bottom: 14px; }
.row2 p { font-size: 1.02rem; }
.row2 .visual { display: flex; justify-content: center; }
/*
 * Overlapped pair: the front phone is you, the one behind is the other party.
 * Both need `position: relative` — a negative z-index alone would drop the back
 * phone behind the section's own background and it would vanish.
 */
/* Widths sized so the pair still fits the 430px column at the 980px breakpoint. */
.row2 .visual.pair { padding-left: 24px; }
.row2 .visual.pair .phone { width: 216px; position: relative; z-index: 1; }
.row2 .visual.pair .phone.back {
  margin-left: -50px; margin-top: 44px; z-index: 0;
  transform: rotate(6deg); transform-origin: top left;
}
.row2 ul { list-style: none; margin-top: 20px; display: grid; gap: 10px; }
.row2 li { display: flex; gap: 10px; font-size: 0.95rem; }
.row2 li svg { width: 17px; height: 17px; color: var(--orange); flex: none; margin-top: 5px; }

/* ---------------------------------------------------------------- phone */

.phone {
  width: 258px; border-radius: 34px; padding: 9px;
  background: linear-gradient(160deg, #2a2f37, #14171c);
  box-shadow: 0 26px 60px rgba(28, 37, 41, 0.22);
  flex: none;
}
.phone .screen {
  background: var(--app-bg); border-radius: 26px; overflow: hidden;
  aspect-ratio: 9 / 18.5; display: flex; flex-direction: column;
  padding: 14px 12px 10px;
}
.screen .bar { display: flex; align-items: center; gap: 7px; padding-bottom: 12px; }
.screen .bar .mark { width: 16px; }
.screen .bar .nm { font-size: 0.63rem; font-weight: 600; color: #fff; }
.screen .bar .st {
  margin-left: auto; font-size: 0.53rem; color: var(--green);
  background: rgba(61, 214, 140, 0.14); padding: 2px 7px; border-radius: 20px;
}
.screen .chan { font-size: 0.7rem; font-weight: 600; color: #fff; }
.screen .chan em { display: block; font-style: normal; font-size: 0.56rem; color: var(--green); }

.ring {
  margin: auto; width: 152px; height: 152px; border-radius: 50%;
  display: grid; place-items: center; position: relative;
  background: radial-gradient(circle at 50% 44%, #1a1e25, #0e1116);
}
.ring::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  padding: 5px; background: var(--grad);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: spin 3.6s linear infinite;
}
.ring.cyan::before { background: linear-gradient(135deg, #7fe9f0, #2fd4de 60%, #1ba7b0); animation-direction: reverse; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ring::before { animation: none; } }
.ring svg { width: 38px; height: 38px; }
.ring .cap {
  position: absolute; bottom: 34px; font-size: 0.5rem; font-weight: 700;
  letter-spacing: 0.13em; color: #d9dbdf; white-space: nowrap;
}
.ring.cyan .cap { color: #2fd4de; }

.screen .logrow {
  display: flex; gap: 8px; align-items: flex-start;
  padding: 8px 0; border-bottom: 1px solid #23272e;
}
.screen .logrow .av {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  background: rgba(255, 122, 0, 0.2); border: 1px solid rgba(255, 122, 0, 0.6);
}
.screen .logrow .who { font-size: 0.58rem; font-weight: 600; color: #fff; }
.screen .logrow .meta { font-size: 0.5rem; color: #8b919c; }
.screen .logrow .dur { margin-left: auto; font-size: 0.55rem; color: #d9dbdf; }
.screen .foot { display: flex; gap: 9px; align-items: center; padding-top: 8px; }
.screen .foot .bars { margin-left: auto; display: flex; gap: 2px; align-items: flex-end; }
.screen .foot .bars i { width: 2.5px; background: var(--green); border-radius: 1px; display: block; }

/* ---------------------------------------------------------------- stats */

.stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; text-align: center; }
.stat .n {
  font-size: clamp(2.1rem, 4.4vw, 3rem); font-weight: 700; color: var(--ink);
  letter-spacing: -0.03em; line-height: 1;
}
.stat .l { font-size: 0.9rem; color: var(--body); margin-top: 8px; }
.stat .n .u { color: var(--orange); }

/* ---------------------------------------------------------------- cards */

.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.tile {
  background: #fff; border: 1px solid var(--line); border-radius: 18px; padding: 28px;
  transition: box-shadow 0.2s, transform 0.2s;
}
.wash .tile { background: #fff; }
.tile:hover { box-shadow: 0 14px 34px rgba(28, 37, 41, 0.08); transform: translateY(-2px); }
.tile .ico {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center;
  background: var(--orange-soft); color: var(--orange-ink); margin-bottom: 16px;
}
.tile .ico svg { width: 20px; height: 20px; }
.tile h3 { font-size: 1.05rem; margin-bottom: 8px; }
.tile p { font-size: 0.92rem; }

/* ---------------------------------------------------------------- security */

.sec { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: start; }
.spec {
  background: var(--app-bg); border-radius: 16px; padding: 24px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.79rem; line-height: 1.9; color: #d9dbdf; overflow-x: auto;
}
.spec .k { color: #ff9a2b; }
.spec .c { color: #6f7783; }

.checklist { list-style: none; display: grid; gap: 20px; }
.checklist li { display: flex; gap: 13px; align-items: flex-start; }
.checklist svg { width: 20px; height: 20px; color: var(--orange); flex: none; margin-top: 3px; }
.checklist strong { display: block; color: var(--ink); font-size: 1rem; margin-bottom: 3px; }
.checklist span { font-size: 0.92rem; }

.honest {
  margin-top: 26px; background: #fff; border: 1px solid var(--line);
  border-left: 3px solid var(--orange); border-radius: 12px; padding: 20px 22px;
}
.honest h4 { font-size: 0.98rem; margin-bottom: 8px; }
.honest p { font-size: 0.9rem; }

/* ---------------------------------------------------------------- plans */

/* Four tiers now that Personal is free — auto-fit rather than a fixed count, so
   the row reflows to two-up on a tablet instead of squeezing to unreadable. */
.plans {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 20px; max-width: 1320px; margin: 0 auto;
}
.plan { background: #fff; border: 1px solid var(--line); border-radius: 20px; padding: 28px; }
.plan.hi { border-color: var(--orange); box-shadow: 0 16px 40px rgba(255, 122, 0, 0.13); position: relative; }
.plan.hi::after {
  content: "Most popular"; position: absolute; top: -12px; left: 34px;
  background: var(--orange); color: #fff; font-size: 0.68rem; font-weight: 700;
  padding: 4px 12px; border-radius: 20px;
}
.plan h3 { font-size: 1.3rem; }
.plan .price { font-size: clamp(1.5rem, 2.4vw, 2.1rem); font-weight: 700; color: var(--ink); letter-spacing: -0.03em; margin-top: 6px; }
/* Four cards are narrower than three: without this the unit wraps mid-phrase
   and reads as "/ user /" on one line and "month" on the next. */
.plan .price small { font-size: 0.85rem; font-weight: 400; color: var(--muted); letter-spacing: 0; white-space: nowrap; }
.plan .desc { font-size: 0.92rem; margin: 10px 0 22px; }
.plan ul { list-style: none; display: grid; gap: 11px; margin-bottom: 26px; }
.plan li { display: flex; gap: 10px; font-size: 0.93rem; }
.plan li svg { width: 16px; height: 16px; color: var(--orange); flex: none; margin-top: 4px; }
.plan .btn { width: 100%; }

/* What a line item actually is, next to the line item. */
.plan .tag {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 100px;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  vertical-align: 1px;
}
.plan .tag.ok  { color: #1f7a4d; background: #e6f7ee; }
.plan .tag.dev { color: #5a5f75; background: #eef0f6; }

.plans-note {
  max-width: 46rem; margin: 30px auto 0; text-align: center;
  font-size: 0.9rem; color: var(--muted);
}
.plans-note em { font-style: normal; font-weight: 600; color: var(--body); }

/* ---------------------------------------------------------------- whitelabel */

.wl { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.swatch { background: #fff; border: 1px solid var(--line); border-radius: 18px; padding: 26px; }
.swatch .row { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.swatch .mark { width: 26px; }
.swatch .nm { font-weight: 700; font-size: 1.02rem; color: var(--ink); }
.swatch p { font-size: 0.88rem; }
.swatch.blue { --orange: #2f80ed; --grad: linear-gradient(135deg, #6aa8f5, #2f80ed 60%, #2668bf); }
.swatch.green { --orange: #12b981; --grad: linear-gradient(135deg, #4bd6a8, #12b981 60%, #0d8f63); }

/* ---------------------------------------------------------------- band */

/*
 * Closing CTA. Echoes the hero: same dark ground, same orb, so the page opens
 * and closes on the mark. Half the sphere sits below the fold of the block —
 * cropped rather than repeated at full size, which would just look duplicated.
 */
.band {
  position: relative; overflow: hidden;
  background: var(--ink); color: #fff; text-align: center;
  border-radius: 28px; padding: 76px 32px 116px;
}
.band::before {
  content: ""; position: absolute; z-index: 0;
  left: 50%; bottom: -152px; width: 220px; height: 220px;
  transform: translateX(-50%); border-radius: 50%;
  background: radial-gradient(circle at 33% 27%,
    #ffd99b 0%, #ffab3d 13%, #ff8c10 27%, #f97400 42%,
    #d85800 60%, #9e3c00 79%, #4e1a00 100%);
  box-shadow:
    inset -12px -14px 28px rgba(104, 28, 0, 0.7),
    inset 0 0 2px 1px rgba(255, 196, 130, 0.5),
    0 -6px 80px rgba(255, 122, 0, 0.26);
}
.band > * { position: relative; z-index: 1; }
.band h2 { color: #fff; }
.band .sub { color: #b7bec6; margin-inline: auto; margin-bottom: 28px; }
.band .btn-ghost {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(255, 255, 255, 0.18); color: #fff;
}
.band .btn-ghost:hover { background: rgba(255, 255, 255, 0.13); }
.band-wrap { padding: 0 0 92px; }
.pill-row { display: flex; gap: 30px; justify-content: center; margin-top: 40px; flex-wrap: wrap; font-weight: 700; }
.pill-row span:nth-child(2) { color: var(--orange); }

/* ---------------------------------------------------------------- footer */

/*
 * Six columns on their own full-width row rather than pushed to the right of
 * the brand. With this many groups the old `margin-left: auto` flex row left
 * each column about 140px, which wrapped "Frontline Communication" onto three
 * lines; a full-width grid gives every column the same ~180px and one baseline.
 */
footer { border-top: 1px solid var(--line); padding: 52px 0 36px; }
.foot-top { display: flex; gap: 26px; flex-wrap: wrap; margin-bottom: 38px; }
.foot-top .brand { display: flex; align-items: center; gap: 10px; }
.foot-top .mark { width: 28px; }
.foot-top .wordmark { font-size: 1.14rem; }
.tagline { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.17em; text-transform: uppercase; color: var(--muted); margin-top: 6px; }
.foot-cols {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 34px 24px; margin-bottom: 38px;
}
.foot-col h5 { font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); margin-bottom: 12px; }
.foot-col a { display: block; font-size: 0.86rem; line-height: 1.45; margin-bottom: 9px; }
.foot-col a:hover { color: var(--orange-ink); }
/*
 * Unlinked entries for things that genuinely are not downloadable yet. Shown
 * rather than omitted, because "where is the iOS app" is the question, and the
 * answer belongs here instead of behind a 404.
 */
.foot-col .soon-link {
  display: block; font-size: 0.86rem; line-height: 1.45; margin-bottom: 9px; color: var(--muted);
}
.foot-col .soon-link em {
  font-style: normal; font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--orange-ink); background: var(--orange-soft);
  border-radius: 100px; padding: 1px 7px; margin-left: 6px; vertical-align: 1px;
}
.foot-bot {
  display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  border-top: 1px solid var(--line); padding-top: 22px; font-size: 0.83rem; color: var(--muted);
}

/* ---------------------------------------------------------------- responsive */

@media (max-width: 980px) {
  .plans { grid-template-columns: 1fr; max-width: 480px; }
  .row2, .sec { grid-template-columns: 1fr; gap: 44px; }
  .row2.flip .visual { order: 0; }
  .grid3, .wl { grid-template-columns: repeat(2, 1fr); }
  .foot-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Drop the nav links before they start crowding the CTAs, not at phone width. */
@media (max-width: 900px) {
  nav .links { display: none; }
}

@media (max-width: 700px) {
  .grid3, .wl, .plans, .stats { grid-template-columns: 1fr; }
  .foot-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px 20px; }
  section { padding: 62px 0; }
  .band { padding: 56px 22px 92px; border-radius: 22px; }
  .band::before { width: 170px; height: 170px; bottom: -120px; }
  .hero { padding-top: 116px; }
  .stage-wrap { margin-top: 30px; }
}

/* ================================================================ menus */

/*
 * Mega-menu on native <details>/<summary>.
 *
 * The panel is a wide centred sheet rather than a column of links, because
 * twelve destinations grouped by what they are for is navigable and a
 * twelve-item list is not. Each item carries an icon and a one-line summary so
 * the choice can be made from the menu instead of by trial and error.
 */
nav .menu { position: relative; }
nav .menu > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 0.9rem; font-weight: 500; color: var(--body);
  transition: color 0.15s;
}
nav .menu > summary::-webkit-details-marker { display: none; }
nav .menu > summary svg { opacity: 0.65; transition: transform 0.18s; }
nav .menu[open] > summary svg { transform: rotate(180deg); }
nav .menu > summary:hover,
nav .menu[open] > summary { color: var(--ink); }
header.over nav .menu > summary { color: #b9c0c9; }
header.over nav .menu > summary:hover,
header.over nav .menu[open] > summary { color: #fff; }

nav .drop {
  /*
   * `left: 50%` needs NO dock adjustment, and adding one broke it.
   *
   * `header` establishes the containing block for this fixed element, so the 50%
   * resolves against the HEADER's width — which already narrows with the page
   * inset. Subtracting half the rail on top of that double-counted: the menu
   * landed 186 px off the left edge of the screen. Measured `left` was 220px,
   * i.e. 50% of 860 minus 210, which is what named the containing block.
   *
   * The width does need help, but from `100%` rather than `100vw`: viewport units
   * are always the viewport and know nothing of the inset, so a 1000 px menu
   * inside an 860 px header ran under the rail. `100%` is the containing block,
   * so it follows the inset with no variable at all.
   */
  position: fixed; left: 50%; top: 72px; z-index: 70;
  width: min(1000px, calc(100% - 3rem));
  transform: translateX(-50%);
  max-height: calc(100vh - 6rem); overflow-y: auto;
  padding: 12px;
  background: var(--white);
  border: 1px solid var(--line); border-radius: 16px;
  box-shadow: 0 24px 60px -16px rgba(28, 37, 41, 0.28);
}
nav .menu[open] > .drop { animation: drop-in 0.16s ease-out; }
/* Transparent bridge across the gap below the header, so travelling from the
   summary to the panel never passes over dead space. */
nav .menu[open] > .drop::before {
  content: ""; position: absolute; left: 0; right: 0; top: -14px; height: 14px;
}
@keyframes drop-in { from { opacity: 0; transform: translate(-50%, -6px); } }
@media (prefers-reduced-motion: reduce) { nav .menu[open] > .drop { animation: none; } }

/* Four columns since the eSIM store added a group. At the panel's 1000px cap
   that is 250px each, which still fits a two-line blurb. */
.mgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.mcol {
  min-width: 0; padding: 8px;
  background: var(--cream); border: 1px solid var(--line); border-radius: 12px;
}
.mcol-h {
  padding: 4px 12px 8px;
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
}

.mcol a {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 10px 12px; border-radius: 10px;
  border: 1px solid transparent;
  transition: background 0.15s, border-color 0.15s;
}
.mcol a:hover { background: var(--white); border-color: #ffd6ab; }

.mcol .ico {
  flex: none; width: 36px; height: 36px; border-radius: 10px;
  display: grid; place-items: center;
  color: var(--orange-ink);
  background: var(--orange-soft); border: 1px solid #ffd6ab;
  transition: background 0.15s, border-color 0.15s;
}
.mcol .ico svg { width: 18px; height: 18px; }
.mcol a:hover .ico { background: #ffe6cc; border-color: var(--orange); }

/* flex:1 + min-width:0 is the pair that makes a flex child actually wrap
   instead of sizing to its longest line and overflowing the card. */
.mcol .txt { flex: 1 1 auto; min-width: 0; }
.mcol .t {
  display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 0.9rem; font-weight: 600; color: var(--ink);
}
.mcol .d {
  display: block; margin-top: 2px;
  font-size: 0.79rem; line-height: 1.45; color: var(--body);
  overflow-wrap: break-word;
}
/* Shown in the menu, not just on the page: the state should be known before
   the click, not discovered after it. */
.mcol .soon, .mnav .soon {
  flex: none; padding: 1px 7px; border-radius: 100px;
  font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: #5a5f75; background: #eef0f6; border: 1px solid #d9dde9;
}

@media (max-width: 1040px) { .mgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px) { nav .menu { display: none; } }

/* ---- mobile disclosure nav ---- */

.menu-mobile { display: none; order: 9; margin-left: 12px; }
.menu-mobile > summary {
  list-style: none; cursor: pointer;
  width: 40px; height: 40px; border-radius: 10px;
  display: grid; place-items: center;
  border: 1px solid var(--line); background: #fff;
}
.menu-mobile > summary::-webkit-details-marker { display: none; }
.menu-mobile .bars { display: grid; gap: 4px; }
.menu-mobile .bars i { display: block; width: 17px; height: 2px; border-radius: 2px; background: var(--ink); }
header.over .menu-mobile > summary { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.2); }
header.over .menu-mobile .bars i { background: #fff; }

.mnav {
  /* Same containing block as `nav .drop`, so the 12 px inset is already correct. */
  position: fixed; left: 12px; right: 12px; top: 68px; z-index: 70;
  max-height: calc(100vh - 5rem); overflow-y: auto;
  padding: 10px;
  background: #fff; border: 1px solid var(--line); border-radius: 14px;
  box-shadow: 0 24px 60px -16px rgba(28, 37, 41, 0.3);
}
.mnav-h {
  padding: 12px 12px 6px;
  font-size: 0.63rem; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--muted);
}
.mnav-h:first-child { padding-top: 4px; }
.mnav a {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px; border-radius: 9px;
  font-size: 0.95rem; font-weight: 500; color: var(--ink);
}
.mnav a:hover { background: var(--orange-soft); color: var(--orange-ink); }

@media (max-width: 900px) {
  .menu-mobile { display: block; }
  /* The CTAs would crowd a 40px tap target; the panel carries Pricing instead. */
  nav .right .btn-ghost { display: none; }
}

/* ============================================================= subpages */

.subhero {
  padding: 62px 0 8px; text-align: center;
  background: linear-gradient(180deg, var(--cream) 0%, var(--white) 100%);
  border-bottom: 1px solid var(--line);
}
.crumb { font-size: 0.84rem; color: var(--muted); margin-bottom: 16px; }
.crumb a:hover { color: var(--orange-ink); }
.crumb span { margin: 0 6px; opacity: 0.6; }
.subhero h1 { font-size: clamp(1.9rem, 4.2vw, 3rem); margin-bottom: 16px; }
.subhero .lede {
  font-size: 1.1rem; max-width: 40rem; margin: 0 auto 26px; color: var(--body);
}
.subhero .cta-row { margin-bottom: 34px; }

/*
 * Status is stated above the fold on every page. Half of these describe things
 * that ship and half describe things that don't, and letting a reader discover
 * that halfway down would be the dishonest version of this site.
 */
.pill {
  display: inline-block; margin-bottom: 18px;
  padding: 5px 14px; border-radius: 100px;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
}
.pill.ok   { color: #1f7a4d; background: #e6f7ee; border: 1px solid #b9e6cd; }
.pill.warn { color: var(--orange-ink); background: var(--orange-soft); border: 1px solid #ffd6ab; }
.pill.dev  { color: #5a5f75; background: #eef0f6; border: 1px solid #d9dde9; }

.status-note {
  max-width: 44rem; margin: 0 auto 34px; padding: 16px 20px;
  text-align: left; font-size: 0.95rem;
  background: var(--cream-deep); border-left: 3px solid var(--orange);
  border-radius: 0 10px 10px 0; color: var(--body);
}
.status-note strong { color: var(--ink); }

/* ---- screenshots ---- */

/* width:100% — a flex figure otherwise shrink-wraps and sits left of centre. */
.shot { margin: 0; width: 100%; display: flex; justify-content: center; }
.shot img { display: block; max-width: 100%; height: auto; }
.shot.is-phone img {
  width: 300px; border-radius: 26px;
  box-shadow: 0 24px 54px rgba(28, 37, 41, 0.22);
}
.shot.is-console img {
  width: 100%; max-width: 660px; border-radius: 12px;
  border: 1px solid var(--line);
  box-shadow: 0 24px 54px rgba(28, 37, 41, 0.16);
}
.shot.hero-shot { margin-top: 8px; transform: translateY(28px); }
.shot.hero-shot.is-phone img { width: 280px; }
.shot.hero-shot.is-console img { max-width: 860px; }

/* ---- built-in email ---- */

/*
 * The mail page needs to make one product idea visible before it explains it:
 * this is a rugged radio and a real inbox on the same issued device. The mockup
 * is HTML rather than a screenshot so the address and message subjects stay
 * legible at every density, and so a narrow screen can recompose it instead of
 * shrinking one large bitmap into unreadability.
 */
.mail-hero-art {
  position: relative; width: min(100%, 920px); height: 620px;
  margin: 12px auto 0; overflow: hidden; text-align: left;
  border-radius: 30px;
  background:
    radial-gradient(560px 360px at 52% 44%, rgba(255, 122, 0, 0.22), transparent 68%),
    linear-gradient(rgba(255,255,255,0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.035) 1px, transparent 1px),
    linear-gradient(155deg, #171a21, #0d0f14 62%, #1b120c);
  background-size: auto, 42px 42px, 42px 42px, auto;
  box-shadow: 0 30px 70px rgba(28, 37, 41, 0.18);
}
.mail-hero-art::before {
  content: "ONE DEVICE"; position: absolute; top: 34px; right: 42px;
  font-size: clamp(2.7rem, 7vw, 5.8rem); line-height: 0.9; font-weight: 700;
  letter-spacing: -0.06em; color: rgba(255,255,255,0.035);
}
.mail-hero-art::after {
  content: ""; position: absolute; left: -90px; bottom: -140px;
  width: 360px; height: 360px; border-radius: 50%;
  border: 56px solid rgba(255, 122, 0, 0.055);
}
.mail-radio {
  position: absolute; z-index: 3; left: 50%; top: 58px;
  width: 286px; height: 528px; transform: translateX(-50%);
  border: 5px solid #333740; border-radius: 32px 32px 44px 44px;
  background: linear-gradient(145deg, #262a32, #15181e 62%, #0d0f13);
  box-shadow: 0 34px 55px rgba(0,0,0,0.48), inset 1px 1px 1px rgba(255,255,255,0.08);
}
.mail-radio::before {
  content: "PUSH.TT"; position: absolute; left: 0; right: 0; top: 22px;
  text-align: center; color: #9298a2; font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.18em;
}
.mail-radio-aerial {
  position: absolute; left: 27px; top: -73px; width: 21px; height: 76px;
  border-radius: 10px 10px 4px 4px;
  background: linear-gradient(90deg, #111318, #353942 48%, #0d0f13);
  box-shadow: inset 3px 0 4px rgba(255,255,255,0.04);
}
.mail-radio-knob {
  position: absolute; top: -25px; width: 35px; height: 28px;
  border-radius: 7px 7px 3px 3px;
  background: repeating-linear-gradient(90deg, #15171c 0 4px, #2e3239 4px 7px);
  border-bottom: 4px solid #090a0d;
}
.mail-radio-knob.one { right: 24px; }
.mail-radio-knob.two { right: 72px; width: 27px; }
.mail-radio-ptt {
  position: absolute; left: -13px; top: 112px; width: 10px; height: 86px;
  border-radius: 7px 0 0 7px; background: var(--orange);
  box-shadow: -3px 0 10px rgba(255,122,0,0.3);
}
.mail-radio-ptt::after {
  content: "PTT"; position: absolute; left: -14px; top: 34px;
  transform: rotate(-90deg); font-size: 0.46rem; font-weight: 700;
  letter-spacing: 0.1em; color: #ffc387;
}
.mail-radio-screen {
  position: absolute; left: 21px; right: 21px; top: 45px; height: 416px;
  overflow: hidden; border: 2px solid #050609; border-radius: 18px;
  background: #f7f6f3; color: #20252b;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.3), 0 0 0 2px #3b3f47;
  font-family: Poppins, sans-serif;
}
.mail-status {
  height: 20px; padding: 5px 10px 0; display: flex; justify-content: space-between;
  background: #11141a; color: #bfc4cc; font-size: 0.45rem; letter-spacing: 0.06em;
}
.mail-appbar {
  height: 51px; padding: 8px 10px; display: flex; align-items: center; gap: 9px;
  background: #171a20; color: #fff; border-bottom: 1px solid rgba(255,255,255,0.07);
}
.mail-appbar > span:nth-child(2) { display: flex; flex: 1; min-width: 0; flex-direction: column; }
.mail-appbar strong { font-size: 0.72rem; line-height: 1.25; }
.mail-appbar small {
  overflow: hidden; color: #8f96a1; font-size: 0.42rem; white-space: nowrap;
  text-overflow: ellipsis;
}
.mail-menu { color: #aab0b9; font-size: 0.8rem; }
.mail-compose {
  width: 24px; height: 24px; display: grid; place-items: center;
  border-radius: 50%; background: var(--orange); color: #fff; font-size: 0.9rem;
}
.mail-honesty {
  padding: 5px 8px; text-align: center; background: #fff3e6; color: #9a4a08;
  border-bottom: 1px solid #f4d7b9; font-size: 0.43rem; font-weight: 600;
}
.mail-message {
  min-height: 76px; padding: 10px 8px; display: grid;
  grid-template-columns: 29px minmax(0, 1fr) auto; gap: 8px;
  border-bottom: 1px solid #e5e2dc; background: #fff;
}
.mail-message.unread { background: #fff9f2; }
.mail-message > span { min-width: 0; display: flex; flex-direction: column; }
.mail-message strong, .mail-message em, .mail-message small {
  overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.mail-message strong { color: #20252b; font-size: 0.58rem; line-height: 1.3; }
.mail-message.unread strong, .mail-message.unread em { color: #b64c05; }
.mail-message em { color: #333941; font-size: 0.5rem; font-style: normal; font-weight: 600; }
.mail-message small { color: #8a9199; font-size: 0.45rem; margin-top: 2px; }
.mail-message time { color: #9198a0; font-size: 0.43rem; }
.mail-avatar {
  width: 29px; height: 29px; display: grid; place-items: center;
  border-radius: 50%; background: #ffe0be; color: #a94504;
  font-size: 0.6rem; font-style: normal;
}
.mail-avatar.blue { background: #dce8ff; color: #315ca7; }
.mail-avatar.green { background: #dff5e8; color: #278055; }
.mail-tabs {
  position: absolute; left: 0; right: 0; bottom: 0; height: 50px;
  display: grid; grid-template-columns: repeat(4, 1fr); align-items: center;
  background: #171a20; color: #7f8792; border-top: 1px solid #0b0d10;
}
.mail-tabs span { display: grid; justify-items: center; gap: 1px; font-size: 0.56rem; }
.mail-tabs span.active { color: #ff9a2b; }
.mail-tabs small { font-size: 0.39rem; }
.mail-radio-speaker {
  position: absolute; left: 86px; right: 86px; bottom: 25px;
  display: flex; justify-content: space-between;
}
.mail-radio-speaker i { width: 4px; height: 20px; border-radius: 3px; background: #090b0e; }
.mail-identity-card, .mail-included-card {
  position: absolute; z-index: 4; padding: 20px 22px; border-radius: 16px;
  backdrop-filter: blur(12px);
  box-shadow: 0 18px 40px rgba(0,0,0,0.25);
}
.mail-identity-card {
  left: 42px; top: 92px; width: 265px;
  border: 1px solid rgba(255,255,255,0.16); background: rgba(255,255,255,0.09); color: #fff;
}
.mail-id-label { display: block; margin-bottom: 17px; color: #ffad5f; font-size: 0.61rem; font-weight: 700; letter-spacing: 0.15em; }
.mail-identity-card strong { display: block; font-size: 1rem; }
.mail-identity-card > span:not(.mail-id-label) { display: block; color: #aeb4bd; font-size: 0.7rem; word-break: break-all; }
.mail-identity-card i { display: flex; align-items: center; gap: 7px; margin-top: 18px; color: #dce1e7; font-size: 0.62rem; font-style: normal; }
.mail-identity-card i b { width: 7px; height: 7px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 4px rgba(61,214,140,0.13); }
.mail-included-card {
  right: 40px; bottom: 72px; width: 215px;
  border: 1px solid #f3d3b2; background: rgba(255,248,240,0.95); color: var(--ink);
}
.mail-included-card > span { display: block; color: var(--body); font-size: 0.61rem; }
.mail-included-card > strong { display: flex; align-items: center; gap: 7px; margin-top: 7px; font-size: 1.15rem; }
.mail-included-card i { font-style: normal; color: var(--orange-ink); }

.mail-steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 34px; }
.mail-steps article {
  min-height: 184px; padding: 24px; display: flex; flex-direction: column; gap: 22px;
  border: 1px solid var(--line); border-radius: 18px; background: #fff;
}
.mail-steps article > b {
  width: 34px; height: 34px; display: grid; place-items: center;
  border-radius: 50%; background: var(--orange-soft); color: var(--orange-ink); font-size: 0.9rem;
}
.mail-steps article span { display: flex; flex-direction: column; gap: 7px; }
.mail-steps article strong { color: var(--ink); font-size: 0.98rem; }
.mail-steps article small { color: var(--body); font-size: 0.82rem; line-height: 1.55; }

/* The home-page feature uses the same radio so the landing page and detail
   page cannot drift into showing two different products. */
.mail-home { position: relative; overflow: hidden; }
.mail-home::before {
  content: ""; position: absolute; right: -160px; top: 30px;
  width: 430px; height: 430px; border-radius: 50%;
  background: var(--orange-soft); opacity: 0.75;
}
.mail-home-grid { position: relative; display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 70px; align-items: center; }
.mail-home-copy .sub { margin-bottom: 25px; }
.mail-home-points { display: grid; grid-template-columns: 1fr 1fr; gap: 13px 20px; margin: 24px 0 30px; }
.mail-home-points span { display: flex; gap: 8px; color: var(--ink); font-size: 0.88rem; font-weight: 600; }
.mail-home-points b { color: var(--orange); }
.mail-home-visual {
  position: relative; height: 510px; overflow: hidden;
  border-radius: 28px;
  background: radial-gradient(circle at 50% 36%, rgba(255,122,0,0.22), transparent 38%), linear-gradient(150deg, #171a21, #0d0f14);
}
.mail-home-visual .mail-radio { top: 40px; transform: translateX(-50%) scale(0.86); transform-origin: top center; }
.mail-home-domain {
  position: absolute; z-index: 5; right: 26px; top: 34px; padding: 12px 15px;
  border-radius: 12px; background: rgba(255,255,255,0.94); box-shadow: 0 12px 28px rgba(0,0,0,0.2);
}
.mail-home-domain span { display: block; color: var(--muted); font-size: 0.5rem; font-weight: 700; letter-spacing: 0.12em; }
.mail-home-domain strong { color: var(--ink); font-size: 0.72rem; }

@media (max-width: 860px) {
  .mail-hero-art { height: 650px; }
  .mail-identity-card { left: 22px; top: 38px; }
  .mail-included-card { right: 22px; bottom: 32px; }
  .mail-radio { top: 92px; }
  .mail-home-grid { grid-template-columns: 1fr; gap: 40px; }
  .mail-home-copy { max-width: 38rem; }
  .mail-home-visual { width: 100%; max-width: 600px; margin: 0 auto; }
}
@media (max-width: 620px) {
  .mail-hero-art { height: 660px; border-radius: 20px; }
  .mail-hero-art::before { top: 24px; right: 18px; font-size: 3rem; }
  .mail-radio { top: 112px; transform: translateX(-50%) scale(0.82); transform-origin: top center; }
  .mail-identity-card { left: 14px; right: 14px; top: 20px; width: auto; padding: 14px 16px; }
  .mail-id-label { margin-bottom: 6px; }
  .mail-identity-card i { margin-top: 8px; }
  .mail-included-card { right: 14px; bottom: 18px; width: 192px; padding: 13px 15px; }
  .mail-steps { grid-template-columns: 1fr; }
  .mail-steps article { min-height: 0; flex-direction: row; }
  .mail-home-points { grid-template-columns: 1fr; }
  .mail-home-visual { height: 470px; }
  .mail-home-visual .mail-radio { top: 28px; transform: translateX(-50%) scale(0.78); }
  .mail-home-domain { top: 18px; right: 14px; }
}

/* ---- two handsets talking (radio-replacement hero) ---- */

/*
 * One shared 11-second clock drives the whole scene.
 *
 * Every gated element animates with the SAME duration and NO animation-delay,
 * and each state's window is encoded in its keyframe percentages instead. That
 * is the only thing keeping the transmitting ring, the receiving ring, the four
 * captions and the packet flow in step without a line of JavaScript — reach for
 * animation-delay to stagger any of them and the whole thing drifts apart.
 *
 * The travelling dots do need a stagger, so they run their own short loop and
 * are gated by a PARENT that fades on the shared clock. Independent inside,
 * synchronised outside.
 */
.talkflow {
  display: flex; align-items: flex-start; justify-content: center; gap: 6px;
  margin-top: 22px;
}
.talkflow .phone { width: 208px; }
.tf-unit { margin: 0; text-align: center; }
.tf-unit figcaption { margin-top: 14px; font-size: 0.79rem; color: var(--muted); }
.tf-note {
  text-align: center; font-size: 0.86rem; color: var(--muted);
  margin: 18px auto 0; max-width: 30rem; transform: translateY(0);
}

.tf-ring {
  margin: auto; width: 138px; height: 138px; border-radius: 50%;
  display: grid; place-items: center; position: relative;
  background: radial-gradient(circle at 50% 44%, #1a1e25, #0e1116);
}
.tf-ring > svg { width: 34px; height: 34px; position: relative; z-index: 2; }

/* Three stacked ring layers; exactly one is ever at opacity 1. */
.tf-ring .rg {
  position: absolute; inset: 0; border-radius: 50%; padding: 5px;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.tf-ring .rg-idle { background: #2b313a; }
.tf-ring .rg-tx { padding: 7px; background: var(--grad); }
.tf-ring .rg-rx { background: linear-gradient(135deg, #7fe9f0, #2fd4de 60%, #1ba7b0); }

.tf-ring .cap {
  position: absolute; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 2;
  font-size: 0.5rem; font-weight: 700; letter-spacing: 0.12em;
  color: #d9dbdf; white-space: nowrap;
}
.tf-ring .c-tx { color: #ffb04a; }
.tf-ring .c-rx { color: #2fd4de; }

/* Transmit pulse. Own loop; the wrapper is what the shared clock switches. */
.tf-pulse { position: absolute; inset: 0; }
.tf-pulse i {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(255, 138, 0, 0.5);
  animation: tf-pulse 1.8s ease-out infinite;
}
.tf-pulse i:nth-child(2) { animation-delay: 0.9s; }
@keyframes tf-pulse {
  0%   { transform: scale(1);    opacity: 0.7; }
  100% { transform: scale(1.55); opacity: 0; }
}

/* ---- the link between them ---- */

.tf-link {
  position: relative; flex: 1 1 auto; max-width: 180px; min-width: 76px;
  align-self: center; height: 40px; margin-bottom: 34px;
}
.tf-line {
  position: absolute; left: 0; right: 0; top: 19px; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--line) 16%, var(--line) 84%, transparent);
}
.tf-flow { position: absolute; left: 6px; right: 6px; top: 19px; height: 2px; }
.tf-flow i {
  position: absolute; top: -3px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--orange); box-shadow: 0 0 0 3px rgba(255, 122, 0, 0.16);
  animation: tf-drift 1.5s linear infinite;
}
/*
 * Same orange both ways. Orange means "someone is transmitting" everywhere else
 * in the product, and the packets in flight ARE the transmission — colouring
 * the return leg cyan made the audio change meaning halfway across the gap.
 */
.tf-flow.rev i { animation-name: tf-drift-rev; }
.tf-flow i:nth-child(2) { animation-delay: 0.375s; }
.tf-flow i:nth-child(3) { animation-delay: 0.75s; }
.tf-flow i:nth-child(4) { animation-delay: 1.125s; }
@keyframes tf-drift {
  0%   { left: 0;    opacity: 0; }
  16%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { left: 100%; opacity: 0; }
}
@keyframes tf-drift-rev {
  0%   { left: 100%; opacity: 0; }
  16%  { opacity: 1; }
  84%  { opacity: 1; }
  100% { left: 0;    opacity: 0; }
}
/* Sits on the line and masks it, so packets pass behind the seal. */
.tf-seal {
  position: absolute; top: 8px; left: 50%; transform: translateX(-50%); z-index: 3;
  padding: 2px 9px; border-radius: 100px;
  background: #fff; border: 1px solid var(--line);
  font-size: 0.58rem; font-weight: 700; letter-spacing: 0.08em; color: var(--orange-ink);
}

/* ---- the shared clock ---- */

.talkflow .rg, .talkflow .cap, .talkflow .tf-pulse, .talkflow .tf-flow {
  opacity: 0;
  animation-duration: 11s;
  animation-iteration-count: infinite;
}
/* Paired stops (3%/5%) switch cleanly instead of cross-fading through a state
   where both rings are half-lit and the handset reads as neither. */
@keyframes tf-phase1 {
  0%, 3%    { opacity: 0; }
  5%, 41%   { opacity: 1; }
  43%, 100% { opacity: 0; }
}
@keyframes tf-phase2 {
  0%, 50%   { opacity: 0; }
  52%, 88%  { opacity: 1; }
  90%, 100% { opacity: 0; }
}
@keyframes tf-idle {
  0%, 3%    { opacity: 1; }
  5%, 41%   { opacity: 0; }
  43%, 50%  { opacity: 1; }
  52%, 88%  { opacity: 0; }
  90%, 100% { opacity: 1; }
}
.tf-a .rg-tx, .tf-a .c-tx, .tf-a .tf-pulse,
.tf-b .rg-rx, .tf-b .c-rx,
.talkflow .tf-flow.fwd { animation-name: tf-phase1; }

.tf-b .rg-tx, .tf-b .c-tx, .tf-b .tf-pulse,
.tf-a .rg-rx, .tf-a .c-rx,
.talkflow .tf-flow.rev { animation-name: tf-phase2; }

.talkflow .rg-idle, .talkflow .c-idle { animation-name: tf-idle; }

@media (max-width: 860px) {
  .talkflow .phone { width: 176px; }
  .tf-ring { width: 116px; height: 116px; }
  .tf-ring > svg { width: 29px; height: 29px; }
  /* The longest caption is "<NAME> IS TALKING" — at the full size it clears the
     ring, at this one it broke out of it on both sides. */
  .tf-ring .cap { bottom: 20px; font-size: 0.44rem; letter-spacing: 0.07em; }
}
@media (max-width: 640px) {
  .talkflow { gap: 3px; }
  .talkflow .phone { width: 136px; padding: 6px; }
  .talkflow .phone .screen { border-radius: 20px; padding: 10px 8px 8px; }
  .tf-ring { width: 92px; height: 92px; }
  .tf-ring > svg { width: 23px; height: 23px; }
  .tf-ring .cap { bottom: 14px; font-size: 0.4rem; letter-spacing: 0.05em; }
  .tf-link { max-width: 60px; min-width: 34px; margin-bottom: 26px; }
  .tf-seal { display: none; }
  .tf-flow i { width: 6px; height: 6px; top: -2px; }
  .tf-unit figcaption { font-size: 0.68rem; }
}

/*
 * Reduced motion freezes it on the representative frame — left transmitting,
 * right receiving — rather than dropping to a dead pair of idle handsets, which
 * would show none of what the picture is here to show.
 */
@media (prefers-reduced-motion: reduce) {
  .talkflow *, .talkflow *::before { animation: none !important; }
  .tf-a .rg-tx, .tf-a .c-tx, .tf-b .rg-rx, .tf-b .c-rx { opacity: 1; }
  .talkflow .rg-idle, .talkflow .c-idle,
  .tf-a .rg-rx, .tf-a .c-rx, .tf-b .rg-tx, .tf-b .c-tx,
  .talkflow .tf-flow, .talkflow .tf-pulse { opacity: 0; }
}

/* ---- error pages ---- */

/*
 * Deliberately spare. These carry the mark, the code, one honest sentence and a
 * way out — no mega-menu, no six-column footer, nothing to wander into while
 * something is broken. The 5xx pages in particular are served exactly when part
 * of the stack has already failed, so the fewer moving parts, the better.
 */
.err-body { display: flex; flex-direction: column; min-height: 100vh; }
.err-bar { padding: 22px 24px; border-bottom: 1px solid var(--line); }
.err-bar .brand { display: inline-flex; align-items: center; gap: 10px; }
.err-bar .mark { width: 28px; }
.err-bar .wordmark { font-size: 1.16rem; }

.err {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; text-align: center;
  padding: 56px 24px 72px; max-width: 40rem; margin: 0 auto;
}
.err-ring { width: 172px; height: 172px; margin-bottom: 30px; }
.err h1 { font-size: clamp(1.6rem, 4vw, 2.4rem); margin-bottom: 14px; }
.err .lede { font-size: 1.06rem; color: var(--body); }

.err-note {
  margin-top: 24px; padding: 15px 18px; max-width: 34rem;
  font-size: 0.9rem; text-align: left;
  background: var(--cream); border-left: 3px solid var(--orange);
  border-radius: 0 10px 10px 0; color: var(--body);
}
.err .cta-row { margin-top: 32px; }

.err-links {
  margin-top: 34px; padding-top: 24px; border-top: 1px solid var(--line);
  display: flex; gap: 22px; flex-wrap: wrap; justify-content: center;
  font-size: 0.9rem; width: 100%; max-width: 34rem;
}
.err-links a:hover { color: var(--orange-ink); }
.err-foot {
  padding: 0 24px 28px; text-align: center; font-size: 0.82rem; color: var(--muted);
}

@media (max-width: 600px) {
  .err { padding: 40px 22px 56px; }
  .err-ring { width: 138px; height: 138px; margin-bottom: 24px; }
}

/* ---- content sections ---- */

.sec-row { padding: 78px 0; }
.sec-row:nth-child(even) { background: var(--cream); }
.sec-row .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; align-items: center; }
.sec-row.flip .col { order: 2; }
.sec-plain { padding: 68px 0; }
.sec-plain .wrap { max-width: 46rem; }

.sec-row h2, .sec-plain h2 { font-size: clamp(1.35rem, 2.5vw, 1.85rem); margin-bottom: 14px; }
.sec-row .col > p, .sec-plain .col > p { font-size: 1.02rem; }
.sec-row ul, .sec-plain ul { list-style: none; margin-top: 20px; display: grid; gap: 11px; }
.sec-row li, .sec-plain li { display: flex; gap: 10px; font-size: 0.95rem; align-items: flex-start; }
.sec-row li svg, .sec-plain li svg {
  width: 17px; height: 17px; color: var(--orange); flex: none; margin-top: 5px;
}

.inline-note {
  margin-top: 20px; padding: 13px 16px; font-size: 0.9rem;
  background: var(--orange-soft); border-radius: 10px; color: var(--orange-ink);
}
.sec-row:nth-child(even) .inline-note { background: #fff; }

/* ---- full-width blocks (contact cards, address, whitelabel swatches) ---- */

/*
 * A third section shape, beside the two-column row and the narrow prose block.
 * The grids these hold — three contact cards, three swatches — are unreadable
 * squeezed into half a row, and centring them in the prose column wastes the
 * page. Alternation is kept in step with .sec-row so the cream/white rhythm
 * does not break where the shapes are mixed on one page.
 */
.sec-wide { padding: 78px 0; }
.sec-wide:nth-child(even) { background: var(--cream); }
.sec-wide h2 { font-size: clamp(1.35rem, 2.5vw, 1.85rem); margin-bottom: 14px; }
.sec-wide > .wrap > p { font-size: 1.02rem; max-width: 46rem; }
.sec-wide > .wrap > ul { list-style: none; margin-top: 20px; display: grid; gap: 11px; max-width: 46rem; }
.sec-wide li { display: flex; gap: 10px; font-size: 0.95rem; align-items: flex-start; }
.sec-wide li svg { width: 17px; height: 17px; color: var(--orange); flex: none; margin-top: 5px; }
.sec-wide .grid3, .sec-wide .wl, .sec-wide .addr-grid { margin-top: 34px; }
.sec-wide .inline-note { max-width: 46rem; }
.sec-wide:nth-child(even) .inline-note { background: #fff; }

/* An inline link inside body copy or a card — the global `a` is colour: inherit. */
.lnk { color: var(--orange-ink); font-weight: 600; }
.lnk:hover { text-decoration: underline; }
.contact-grid .tile p + p { margin-top: 12px; }

.addr-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.addr-card { background: #fff; border: 1px solid var(--line); border-radius: 18px; padding: 26px; }
.addr-card .ico {
  width: 40px; height: 40px; border-radius: 12px; display: grid; place-items: center;
  background: var(--orange-soft); color: var(--orange-ink); margin-bottom: 14px;
}
.addr-card .ico svg { width: 19px; height: 19px; }
.addr-card h4 { font-size: 0.98rem; margin-bottom: 8px; }
.addr-card address { font-style: normal; font-size: 0.93rem; line-height: 1.7; }
.addr-card p { font-size: 0.93rem; }

@media (max-width: 980px) { .addr-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 700px) {
  .addr-grid { grid-template-columns: 1fr; }
  .sec-wide { padding: 52px 0; }
}

@media (max-width: 980px) {
  .sec-row .row2 { grid-template-columns: 1fr; gap: 36px; }
  .sec-row.flip .col { order: 0; }
}
@media (max-width: 700px) {
  .sec-row, .sec-plain { padding: 52px 0; }
  .subhero { padding-top: 40px; }
  .shot.hero-shot { transform: translateY(18px); }
}

/* ---- contact form ---- */

/*
 * Lives on the contact page. The <form> has a real action and method, so if
 * contact.js fails to load the browser still posts it — a JS-only form that
 * silently does nothing is the failure nobody notices until a lead is lost.
 */
.cform { margin-top: 34px; text-align: left; }
.cform-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.cform label { display: block; }
.cform label > span {
  display: block; font-size: 0.86rem; font-weight: 600; color: var(--ink); margin-bottom: 6px;
}
.cform label > span em { font-style: normal; font-weight: 400; color: var(--muted); }
.cform input, .cform select, .cform textarea {
  width: 100%; padding: 11px 13px; font: inherit; font-size: 0.95rem;
  color: var(--ink); background: #fff;
  border: 1px solid var(--line); border-radius: 10px;
}
.cform textarea { resize: vertical; min-height: 130px; }
.cform input:focus, .cform select:focus, .cform textarea:focus {
  outline: none; border-color: var(--orange); box-shadow: 0 0 0 3px var(--orange-soft);
}
.cform-msg { margin-top: 16px; }

/* Off-screen rather than display:none — a bot reading the DOM still finds it,
   which is the point, while nobody using the page ever does. */
.cform-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.cform-foot { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 20px; }
.cform-status { font-size: 0.9rem; margin: 0; }
.cform-status.is-ok { color: #1f7a4d; }
.cform-status.is-err { color: #c0392b; }

@media (max-width: 640px) { .cform-grid { grid-template-columns: 1fr; } }

/* ---- pricing page ---- */

.pricing-toggle {
  display: inline-flex; gap: 4px; padding: 4px; margin-bottom: 34px;
  background: var(--cream-deep); border-radius: 100px;
}
.pricing-toggle button {
  font: inherit; font-size: 0.92rem; font-weight: 600; cursor: pointer;
  padding: 9px 20px; border: none; border-radius: 100px;
  background: transparent; color: var(--body);
  display: inline-flex; align-items: center; gap: 8px;
  transition: background 0.18s ease, color 0.18s ease;
}
.pricing-toggle button.on { background: #fff; color: var(--ink); box-shadow: 0 2px 8px rgba(28, 37, 41, 0.1); }
.pricing-toggle button em {
  font-style: normal; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
  padding: 2px 8px; border-radius: 100px;
  background: var(--orange-soft); color: var(--orange-ink);
}
.pricing-toggle button.on em { background: var(--orange); color: #fff; }

/* Reserves the line so switching term does not jog the cards up and down. */
.term-note { min-height: 1.4em; margin-top: 6px; font-size: 0.84rem; color: var(--muted); }

/* The saving is only true on the annual view; showing it on monthly would be
   claiming a discount the customer is not getting. */
.pricing-page .plan .price { transition: none; }

/* An in-development marker inside a section heading, so the status sits with
   the claim rather than in a footnote below it. */
.sec-row h2 .tag, .sec-plain h2 .tag, .sec-wide h2 .tag {
  display: inline-block; margin-left: 10px; padding: 3px 10px; border-radius: 100px;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  vertical-align: 4px; color: #5a5f75; background: #eef0f6; white-space: nowrap;
}

/* ================================================================ dispatchflow
 *
 * The dispatch hero: a console position and a driver's handset running one
 * exchange. See apps/site/tools/dispatchflow.mjs for the scenario and the
 * timeline; this file holds the clock.
 *
 * ONE SHARED CLOCK. Every gated element uses `var(--dfc)` and NO
 * animation-delay — each state's window lives in its keyframe percentages
 * instead. That is the only thing holding the bar tint, four talker names, the
 * meter, the talk button, the channel dot, three log rows and four handset
 * captions in step without a line of JavaScript. Stagger any of them with
 * animation-delay and the scene drifts apart within one loop.
 *
 * Windows, matching the module's header:
 *   idle 0-16 · rx1 18-39 · idle 41-46 · tx 48-69 · rx2 71-82 · idle 84-100
 * The 2% ramps between them are the crossfade.
 *
 * The panel is DARK on a light page, exactly as the console is, and carries its
 * own tokens rather than the site's — this is a picture of another product's
 * surface, so it must not inherit this one's palette.
 */
.dispatchflow {
  --dfc: 15s;
  --dfBg: #0f1115;
  --dfSurface: #171a20;
  --dfSurface2: #1e222a;
  --dfBorder: #2c3138;
  --dfText: #ffffff;
  --dfMuted: #a8aeb9;
  --dfDim: #767d8a;
  --dfLive: #22c98b;
  --dfAccent: #ff7a00;

  display: flex; align-items: flex-start; justify-content: center;
  gap: 20px; margin-top: 22px; flex-wrap: wrap;
}
.df-desk { margin: 0; flex: 1 1 560px; max-width: 720px; min-width: 0; }
.df-unit { margin: 0; text-align: center; flex: 0 0 auto; }
.dispatchflow .phone { width: 188px; }
.df-desk figcaption, .df-unit figcaption {
  margin-top: 12px; font-size: 0.79rem; color: var(--muted); text-align: center;
}
.df-note {
  text-align: center; font-size: 0.86rem; color: var(--muted);
  margin: 16px auto 0; max-width: 32rem;
}
/* ODbL. The basemap is derived from OSM, so this line ships with it. */
.df-credit { display: block; margin-top: 4px; font-size: 0.74rem; color: var(--muted); opacity: 0.75; }

.df-panel {
  background: var(--dfBg); color: var(--dfText);
  border: 1px solid var(--dfBorder); border-radius: 12px; overflow: hidden;
  font-size: 12px; line-height: 1.35;
  box-shadow: 0 24px 60px rgba(15, 17, 21, 0.28);
}
.df-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; border-bottom: 1px solid var(--dfBorder);
}
.df-eyebrow { font-size: 8.5px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--dfDim); }
.df-h1 { font-size: 16px; font-weight: 700; }
.df-pill {
  margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dfLive); border: 1px solid color-mix(in srgb, var(--dfLive) 45%, transparent);
  border-radius: 999px; padding: 3px 9px;
}
.df-pill i { width: 5px; height: 5px; border-radius: 50%; background: var(--dfLive); }

/* ---- the bar: who holds the floor ---- */
.df-bar {
  display: flex; align-items: center; gap: 12px; padding: 9px 14px;
  border-bottom: 1px solid var(--dfBorder); min-height: 52px;
  animation: df-bar var(--dfc) infinite;
}
@keyframes df-bar {
  0%, 16%   { background: var(--dfSurface); box-shadow: inset 0 -2px 0 transparent; }
  18%, 39%  { background: color-mix(in srgb, var(--dfLive) 13%, var(--dfSurface));
              box-shadow: inset 0 -2px 0 var(--dfLive); }
  41%, 46%  { background: var(--dfSurface); box-shadow: inset 0 -2px 0 transparent; }
  48%, 69%  { background: color-mix(in srgb, var(--dfAccent) 16%, var(--dfSurface));
              box-shadow: inset 0 -2px 0 var(--dfAccent); }
  71%, 82%  { background: color-mix(in srgb, var(--dfLive) 13%, var(--dfSurface));
              box-shadow: inset 0 -2px 0 var(--dfLive); }
  84%, 100% { background: var(--dfSurface); box-shadow: inset 0 -2px 0 transparent; }
}

/*
 * Every state's label is stacked in the same box and cross-faded, so switching
 * speaker never moves the layout underneath it.
 */
.df-who { display: grid; gap: 1px; min-width: 0; }
.df-talker, .df-on { display: grid; }
.df-talker { font-size: 14px; font-weight: 700; }
.df-on { font-size: 10.5px; color: var(--dfMuted); }
.df-on b { color: var(--dfText); font-weight: 600; }
.df-s { grid-area: 1 / 1; white-space: nowrap; opacity: 0; }

.df-idle { animation: df-w-idle var(--dfc) infinite; }
.df-rx1  { animation: df-w-rx1  var(--dfc) infinite; }
.df-tx   { animation: df-w-tx   var(--dfc) infinite; }
.df-rx2  { animation: df-w-rx2  var(--dfc) infinite; }

@keyframes df-w-idle {
  0%, 16% { opacity: 1 } 18%, 39% { opacity: 0 }
  41%, 46% { opacity: 1 } 48%, 82% { opacity: 0 } 84%, 100% { opacity: 1 }
}
@keyframes df-w-rx1 { 0%, 16% { opacity: 0 } 18%, 39% { opacity: 1 } 41%, 100% { opacity: 0 } }
@keyframes df-w-tx  { 0%, 46% { opacity: 0 } 48%, 69% { opacity: 1 } 71%, 100% { opacity: 0 } }
@keyframes df-w-rx2 { 0%, 69% { opacity: 0 } 71%, 82% { opacity: 1 } 84%, 100% { opacity: 0 } }

/* The talker's colour follows the floor: green for them, orange only for you. */
.df-talker .df-rx1, .df-talker .df-rx2 { color: var(--dfLive); }
.df-talker .df-tx { color: var(--dfAccent); }

/* ---- meter: flat and live stacked, cross-faded ---- */
.df-meters { position: relative; width: 42px; height: 18px; flex: none; }
.df-meter {
  position: absolute; inset: 0;
  display: flex; align-items: flex-end; gap: 2px;
}
.df-meter i { flex: 1; border-radius: 1px; background: var(--dfDim); height: 3px; }
.df-flat { animation: df-w-flat var(--dfc) infinite; }
.df-live { opacity: 0; animation: df-w-live var(--dfc) infinite; }
@keyframes df-w-flat {
  0%, 16% { opacity: 1 } 18%, 39% { opacity: 0 }
  41%, 46% { opacity: 1 } 48%, 82% { opacity: 0 } 84%, 100% { opacity: 1 }
}
@keyframes df-w-live {
  0%, 16% { opacity: 0 } 18%, 39% { opacity: 1 }
  41%, 46% { opacity: 0 } 48%, 82% { opacity: 1 } 84%, 100% { opacity: 0 }
}
/* Independent inside, synchronised outside — the bounce is its own loop, and
   only the colour rides the shared clock. */
.df-live i {
  animation: df-bounce 0.44s ease-in-out infinite alternate, df-metercol var(--dfc) infinite;
  animation-delay: var(--d), 0s;
}
@keyframes df-bounce {
  from { height: 3px }
  to   { height: calc(3px + 13px * var(--s)) }
}
@keyframes df-metercol {
  0%, 46%  { background: var(--dfLive) }
  48%, 69% { background: var(--dfAccent) }
  71%, 100%{ background: var(--dfLive) }
}

/* ---- talk control ---- */
.df-ptt { margin-left: auto; display: flex; align-items: center; gap: 9px; }
.df-target { display: grid; gap: 1px; text-align: right; }
.df-target .l { font-size: 8.5px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--dfDim); }
.df-target .v { font-size: 11px; font-weight: 700; }
.df-talk {
  height: 32px; min-width: 124px; display: grid; place-items: center;
  border-radius: 8px; font-size: 11.5px; font-weight: 800; color: #12151b;
  background: linear-gradient(135deg, #ffb04a, #ff7a00 60%, #e86a00);
  animation: df-talk var(--dfc) infinite;
}
/* Keyed is FLAT accent with a halo, never the gradient — the gradient is the
   resting state and the difference is what tells you the floor is yours. */
@keyframes df-talk {
  0%, 46%   { background: linear-gradient(135deg, #ffb04a, #ff7a00 60%, #e86a00);
              box-shadow: none; }
  48%, 69%  { background: var(--dfAccent);
              box-shadow: 0 0 0 3px color-mix(in srgb, var(--dfAccent) 35%, transparent); }
  71%, 100% { background: linear-gradient(135deg, #ffb04a, #ff7a00 60%, #e86a00);
              box-shadow: none; }
}
.df-key {
  font-size: 9px; letter-spacing: 0.08em; color: var(--dfDim);
  border: 1px solid var(--dfBorder); border-radius: 4px; padding: 3px 6px;
}

/* ---- body: rail, map, log ---- */
.df-body { display: grid; grid-template-columns: 150px minmax(0, 1fr) 190px; }
.df-rail, .df-side { background: var(--dfSurface); min-width: 0; }
.df-rail { border-right: 1px solid var(--dfBorder); }
.df-side { border-left: 1px solid var(--dfBorder); }
.df-railhead {
  display: flex; justify-content: space-between; gap: 6px;
  padding: 8px 10px; border-bottom: 1px solid var(--dfBorder);
  font-size: 8.5px; letter-spacing: 0.11em; text-transform: uppercase; color: var(--dfDim);
}
.df-chan {
  display: block; padding: 7px 10px; border-left: 3px solid transparent; color: var(--dfMuted);
}
.df-chan.on { border-left-color: var(--dfLive); background: color-mix(in srgb, var(--dfLive) 9%, transparent); color: var(--dfText); }
.df-chan.sel { border-left-color: var(--dfAccent); background: color-mix(in srgb, var(--dfAccent) 11%, transparent); color: var(--dfText); }
.df-chan .n { display: flex; align-items: center; gap: 6px; font-weight: 600; font-size: 11.5px; }
.df-chan .s { display: block; font-size: 9.5px; color: var(--dfDim); margin-top: 1px; }
.df-chan.on .s { color: var(--dfLive); }
.df-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--dfDim); flex: none; }
.df-chan.on .df-dot { background: var(--dfLive); }
/* The selected channel's dot brightens while somebody is actually on it. */
.df-chan.sel .df-dot { animation: df-livedot var(--dfc) infinite; }
@keyframes df-livedot {
  0%, 16%   { box-shadow: none }
  18%, 39%  { box-shadow: 0 0 0 3px color-mix(in srgb, var(--dfLive) 30%, transparent) }
  41%, 70%  { box-shadow: none }
  71%, 82%  { box-shadow: 0 0 0 3px color-mix(in srgb, var(--dfLive) 30%, transparent) }
  84%, 100% { box-shadow: none }
}

.df-map { position: relative; min-height: 210px; background: var(--dfBg); overflow: hidden; }
/*
 * A real street map, not a grid — OpenStreetMap geometry for the port district,
 * extracted once by build-basemap.mjs and COMMITTED. One same-origin file, no
 * tile server, no third-party request from a page about a product that cannot
 * read your traffic. The credit under the animation is an ODbL requirement, not
 * decoration.
 */
.df-map::before {
  content: ""; position: absolute; inset: 0;
  background: url("img/basemap-port.svg") center / cover no-repeat;
  opacity: 1;
}
.df-pin {
  position: absolute; display: flex; align-items: center; gap: 5px;
  font-size: 9.5px; color: var(--dfMuted); white-space: nowrap; transform: translate(-4px, -50%);
}
.df-pin i { width: 7px; height: 7px; border-radius: 50%; background: var(--dfAccent); flex: none; }
.df-moving { animation: df-drive var(--dfc) linear infinite; }
/* One unit moves, and it is the one talking. A fleet where every pin drifts
   reads as a screensaver rather than a map. */
/* Van 12 follows a road on the basemap — the four stops are points on the same
   route, so it tracks the street instead of cutting across blocks. */
@keyframes df-drive {
  0%   { left: 22.5%; top: 79.0% }
  35%  { left: 29.0%; top: 74.5% }
  70%  { left: 33.5%; top: 69.5% }
  100% { left: 38.5%; top: 64.0% }
}
.df-scale {
  position: absolute; left: 10px; bottom: 8px; display: flex; align-items: center; gap: 6px;
  font-size: 9px; color: var(--dfDim);
}
.df-scale em { display: block; width: 54px; height: 1px; background: var(--dfDim); }

/* ---- the log ---- */
.df-entry {
  display: block; padding: 8px 10px; border-bottom: 1px solid var(--dfBorder);
  opacity: 0; transform: translateY(-4px);
}
/* Each row lands as its transmission ENDS, which is when the transcript
   actually exists — the sending device transcribes, so there is nothing to
   show until the speaker lets go. */
.df-e1 { animation: df-row1 var(--dfc) infinite; }
.df-e2 { animation: df-row2 var(--dfc) infinite; }
.df-e3 { animation: df-row3 var(--dfc) infinite; }
@keyframes df-row1 {
  0%, 38%  { opacity: 0; transform: translateY(-4px) }
  41%, 96% { opacity: 1; transform: translateY(0) }
  100%     { opacity: 0; transform: translateY(-4px) }
}
@keyframes df-row2 {
  0%, 68%  { opacity: 0; transform: translateY(-4px) }
  71%, 96% { opacity: 1; transform: translateY(0) }
  100%     { opacity: 0; transform: translateY(-4px) }
}
@keyframes df-row3 {
  0%, 81%  { opacity: 0; transform: translateY(-4px) }
  84%, 96% { opacity: 1; transform: translateY(0) }
  100%     { opacity: 0; transform: translateY(-4px) }
}
.df-eh { display: flex; align-items: center; gap: 6px; font-size: 10.5px; font-weight: 700; }
.df-eh b { margin-left: auto; font-weight: 400; font-size: 9px; color: var(--dfDim); }
.df-eb { display: block; margin-top: 3px; font-size: 10.5px; color: var(--dfMuted); }
.df-k { width: 9px; height: 9px; flex: none; border-radius: 2px; background: var(--dfDim); }
.df-k-voice {
  border-radius: 3px 3px 5px 5px;
  background: linear-gradient(var(--dfMuted) 0 60%, transparent 60%);
}

/* ---- the handset ---- */
.df-ring {
  margin: auto; width: 126px; height: 126px; border-radius: 50%;
  display: grid; place-items: center; position: relative;
  background: radial-gradient(circle at 50% 44%, #1a1e25, #0e1116);
}
.df-ring > svg { width: 30px; height: 30px; position: relative; z-index: 2; }
.df-ring .rg {
  position: absolute; inset: 0; border-radius: 50%; padding: 5px; opacity: 0;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
}
.df-ring .rg-idle { background: #2b313a; animation: df-w-idle var(--dfc) infinite; }
.df-ring .rg-tx { padding: 7px; background: var(--grad); animation: df-w-rx1 var(--dfc) infinite; }
.df-ring .rg-rx { background: linear-gradient(135deg, #6cf0b6, #22c98b 60%, #14a06d);
                  animation: df-w-heard var(--dfc) infinite; }
/*
 * The handset is PRIYA'S, so the states are inverted against the console's:
 * her transmission (rx1 on the desk) is her ring going orange, and both other
 * speakers are her ring going green. Getting this backwards is the mistake that
 * makes the two halves look like two unrelated animations.
 */
@keyframes df-w-heard {
  0%, 46%  { opacity: 0 } 48%, 69% { opacity: 1 }
  71%, 82% { opacity: 1 } 84%, 100% { opacity: 0 }
}
.df-ring .cap {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%); z-index: 2;
  font-size: 0.46rem; font-weight: 700; letter-spacing: 0.11em;
  color: #d9dbdf; white-space: nowrap; opacity: 0;
}
.df-ring .c-idle { animation: df-w-idle var(--dfc) infinite; }
.df-ring .c-tx   { color: #ffb04a; animation: df-w-rx1 var(--dfc) infinite; }
.df-ring .c-rx1  { color: #6cf0b6; animation: df-w-tx  var(--dfc) infinite; }
.df-ring .c-rx2  { color: #6cf0b6; animation: df-w-rx2 var(--dfc) infinite; }

.df-pulse { position: absolute; inset: 0; opacity: 0; animation: df-w-rx1 var(--dfc) infinite; }
.df-pulse i {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(255, 138, 0, 0.5);
  animation: df-pulse 1.8s ease-out infinite;
}
.df-pulse i:nth-child(2) { animation-delay: 0.9s; }
@keyframes df-pulse {
  0%   { transform: scale(1);    opacity: 0.7 }
  100% { transform: scale(1.55); opacity: 0 }
}

@media (max-width: 720px) {
  .df-body { grid-template-columns: 122px minmax(0, 1fr); }
  .df-side { display: none; }
  .df-target { display: none; }
}

/*
 * Motion is the whole point here, so reduced-motion does not get a static first
 * frame — it gets the state that carries the most information: the dispatcher
 * mid-transmission, with all three log rows present.
 */
@media (prefers-reduced-motion: reduce) {
  .dispatchflow *, .dispatchflow *::before, .dispatchflow *::after {
    animation: none !important;
  }
  .dispatchflow .df-tx, .dispatchflow .df-live,
  .dispatchflow .df-entry, .dispatchflow .rg-rx, .dispatchflow .c-rx1 { opacity: 1; }
  .dispatchflow .df-entry { transform: none; }
  .df-bar { background: color-mix(in srgb, var(--dfAccent) 16%, var(--dfSurface));
            box-shadow: inset 0 -2px 0 var(--dfAccent); }
  .df-talk { background: var(--dfAccent); }
  .df-live i { height: 11px; background: var(--dfAccent); }
  .df-moving { left: 27%; top: 62%; }
}

/* ============================ support chat widget ============================
 *
 * THIS IS THE APP, IN A POPUP. Same palette, same voice-log rows, same PTT
 * ring — because the widget's job on a marketing page is to show what the
 * product is, and a generic bubble chat would misrepresent it.
 *
 * Two things it deliberately copies rather than invents:
 *
 *   - THE PALETTE IS THE APP'S, not the site's. Values from
 *     apps/android/design/Theme.kt: bg #0F1115, surface #171A20, surfaceAlt
 *     #20242C, border #2C3138, text/dim/faint, and cyan #2FD4DE for "somebody
 *     else is talking". A dark panel on a light page is the point — a visitor
 *     is looking at the product, not at more website.
 *   - MESSAGES ARE A VOICE LOG, NOT BUBBLES. HistoryPane.kt is explicit that
 *     "chat bubbles are the wrong metaphor for PTT — you scan a log and play
 *     through it": full-width rows, avatar, sender, direction arrow, time,
 *     transcription slot, hairline between. The first version of this widget
 *     used bubbles, which is exactly what the app rejected.
 */
.sw { position: fixed; right: 18px; bottom: 18px; z-index: 900;
      /* The app's own type stack, not the site's marketing face. */
      font-family: Poppins, ui-sans-serif, system-ui, sans-serif; font-size: 14px; }

/* --- the app's tokens, scoped so nothing on the site inherits them --- */
.sw-panel, .sw-launch {
  --a-bg: #0f1115;
  --a-surface: #171a20;
  --a-alt: #20242c;
  --a-border: #2c3138;
  --a-text: #ffffff;
  --a-dim: #d9dbdf;
  --a-faint: #8b919c;
  --a-primary: #ff7a00;
  --a-primary-light: #ff9a2b;
  --a-primary-deep: #e86a00;
  --a-receiving: #2fd4de;
  --a-danger: #ff5d5d;
  --a-available: #3dd68c;
}

.sw-launch {
  display: inline-flex; align-items: center; gap: 9px;
  border: 0; border-radius: 999px; cursor: pointer;
  padding: 11px 18px 11px 14px; font-family: inherit;
  background: linear-gradient(135deg, var(--a-primary-light), var(--a-primary) 55%, var(--a-primary-deep));
  color: #1a0e00; font-weight: 700; font-size: 14px;
  box-shadow: 0 10px 26px -10px rgba(232, 106, 0, 0.6);
}
.sw-launch:hover { filter: brightness(1.05); }
.sw-launch-i { display: block; width: 18px; height: 18px; }

/* [hidden] must beat display:flex — see the note by .sw-form. */
.sw-panel[hidden] { display: none; }
/*
 * THREE SIZES: floating (default), tall, and docked to the right edge.
 *
 * `--sw-dock` is the width the page is inset by, and it is 0 unless the rail is
 * open. Everything that reads it does so with a `0px` fallback, so a browser that
 * never sees the docked class behaves exactly as it did before this existed.
 */
.sw-panel {
  position: absolute; right: 0; bottom: calc(100% + 10px);
  width: min(372px, calc(100vw - 28px));
  height: min(620px, calc(100vh - 120px));
  display: flex; flex-direction: column;
  margin: 0; padding: 0;
  background: var(--a-bg); color: var(--a-dim);
  border: 1px solid var(--a-border); border-radius: 18px;
  box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.8);
  overflow: hidden;
}

/* Taller, still floating: same width, most of the viewport. */
.sw-panel.is-tall { height: calc(100vh - 108px); }
/*
 * DOCKED: a full-height rail flush to the right edge, with the page inset beside
 * it rather than hidden under it.
 *
 * `position: fixed` rather than absolute, because `.sw` is pinned 18 px off the
 * bottom-right corner and the rail must reach the top of the viewport. The right
 * border and square corners are what make it read as part of the page instead of
 * a card floating over it.
 */
.sw-panel.is-dock {
  position: fixed; inset: 0 0 0 auto;
  /* ONE source for the rail's width. The page's inset and the panel itself must
   * be the same number, and two copies of `min(420px, 100vw)` is how they stop
   * being — so the JS toggles a class and never names a width. */
  width: var(--sw-dock-w); height: auto; max-height: none;
  border-radius: 0; border-width: 0 0 0 1px;
  box-shadow: -24px 0 60px -30px rgba(0, 0, 0, 0.7);
}
/*
 * The launcher would sit on top of its own rail, so it goes while docked. Closing
 * the panel drops the class and brings it straight back — which is also the only
 * way back, so it must not be conditional on anything else.
 */
html.sw-docked .sw-launch { display: none; }
/*
 * The inset. `header` is `position: sticky`, so it is laid out in normal flow and
 * narrows with body's content box — which is precisely why the site's chrome
 * follows the rail without being touched. Its two `position: fixed` dropdowns do
 * NOT, so they read the same variable below.
 */
/*
 * The rail's width, in one place.
 *
 * No `min(…, 100vw)`: the rail is disabled outright below a 460 px viewport, so
 * 420 px always fits wherever it applies. Nothing needs half of it — the site's
 * fixed dropdowns resolve their percentages against `header`, which insets on its
 * own, and trying to help them was the bug.
 */
:root { --sw-dock-w: 420px; }
html.sw-docked { --sw-dock: 420px; }
html.sw-docked body { padding-right: var(--sw-dock, 0px); }

/* --- header: avatar + status dot + title + E2EE badge, as TalkTopBar --- */
.sw-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; border-bottom: 1px solid var(--a-border);
  background: var(--a-surface); flex: none;
}
.sw-ava {
  position: relative; width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-weight: 700; font-size: 15px; color: #1a0e00;
  background: linear-gradient(135deg, var(--a-primary-light), var(--a-primary-deep));
}
.sw-dot {
  position: absolute; right: -1px; bottom: -1px; width: 11px; height: 11px;
  border-radius: 50%; background: var(--a-faint); border: 3px solid var(--a-surface);
}
.sw-dot.is-on { background: var(--a-available); }
.sw-hgroup { flex: 1; min-width: 0; }
.sw-title { display: flex; align-items: center; gap: 6px; color: var(--a-text); font-weight: 600; }
.sw-sub { font-size: 12px; color: var(--a-faint); }
.sw-sub.is-on { color: var(--a-available); }
/* The app's E2EE badge: lock glyph on a 16% primary wash. */
.sw-badge {
  display: inline-flex; align-items: center; gap: 3px;
  background: rgba(255, 122, 0, 0.16); color: var(--a-primary);
  border-radius: 6px; padding: 1px 5px; font-size: 10px; font-weight: 600;
  letter-spacing: 0.04em;
}
.sw-close {
  border: 0; background: none; cursor: pointer; font-size: 22px; line-height: 1;
  color: var(--a-faint); padding: 0 2px; flex: none; font-family: inherit;
}
.sw-close:hover { color: var(--a-text); }
/*
 * The alert-sound toggle, beside the ×.
 *
 * `gap` on .sw-head is 12px, which is right between the avatar and the title and
 * too much between two icon buttons — the negative margin pulls them into one
 * control cluster rather than two floating glyphs. The 18px box matches the ×'s
 * optical size at its 22px font.
 */
.sw-mute, .sw-size {
  border: 0; background: none; cursor: pointer; padding: 2px; flex: none;
  color: var(--a-faint); line-height: 0; margin-right: -6px;
  display: grid; place-items: center; border-radius: 6px;
}
.sw-mute svg, .sw-size svg { width: 18px; height: 18px; display: block; }
.sw-mute:hover, .sw-size:hover { color: var(--a-text); }
/* Muted is a STATE, so it carries the tinted wash the design system uses for
 * one — not a greyed-out button, which would read as "unavailable". */
.sw-mute.is-off { color: var(--a-primary); background: rgba(255, 122, 0, 0.14); }
.sw-mute:focus-visible, .sw-size:focus-visible, .sw-close:focus-visible {
  outline: 2px solid var(--a-primary); outline-offset: 2px;
}

/* --- the log --- */
.sw-body { flex: 1; overflow-y: auto; min-height: 0; }
.sw-empty { padding: 28px 18px; text-align: center; color: var(--a-faint); font-size: 13px; }

.sw-row {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(44, 49, 56, 0.55);   /* Hairline() */
}
.sw-row-ava {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff;
}
.sw-row-main { flex: 1; min-width: 0; }
.sw-row-top { display: flex; align-items: center; gap: 6px; }
.sw-who { color: var(--a-text); font-weight: 600; font-size: 13.5px; }
.sw-lock { font-size: 10px; }
.sw-dur { margin-left: auto; color: var(--a-dim); font-size: 12px; font-variant-numeric: tabular-nums; }
.sw-meta { display: flex; align-items: center; gap: 4px; color: var(--a-faint); font-size: 11.5px; margin-top: 1px; }
.sw-arrow { font-size: 10px; }
.sw-text { color: var(--a-dim); font-size: 13.5px; margin-top: 2px; word-break: break-word; white-space: pre-wrap; }
/* The permanent transcription slot, as in the app. */
.sw-trans { display: flex; gap: 4px; color: var(--a-faint); font-size: 11.5px; margin-top: 2px; }
.sw-play {
  border: 0; background: none; color: var(--a-primary); cursor: pointer;
  font-size: 15px; padding: 2px 4px; flex: none; font-family: inherit;
}
/* Date pill: surfaceAlt, fully rounded, M/d/yy. */
.sw-date { display: flex; justify-content: center; padding: 10px 0; }
.sw-date span {
  background: var(--a-alt); color: var(--a-dim);
  border-radius: 50px; padding: 4px 12px; font-size: 11px; font-weight: 600;
}

/* --- the PTT ring --- */
.sw-ptt-wrap {
  position: relative;
  flex: none; display: grid; place-items: center; gap: 8px;
  padding: 14px 0 12px; border-top: 1px solid var(--a-border);
  background: radial-gradient(120% 80% at 50% 100%, #13161b, var(--a-bg));
}
/*
 * COLLAPSED: the same button, laid out as a row.
 *
 * Not a second control — one DOM, restyled. Every state class the ring carries
 * (is-tx, is-rx, is-requesting, is-offline) and every handler bound to it keep
 * working, which is the whole reason to do it in CSS: a compact PTT built as its
 * own element would be a second place for "who holds the floor" to be drawn, and
 * that is the one thing on a radio that must never disagree with itself.
 *
 * It stays a BUTTON rather than disappearing. Collapsing to save space must not
 * quietly remove the ability to talk, and the caption still reads who is
 * speaking — 118 px of ring becomes 42, the state stays legible.
 */
.sw-ptt-wrap.is-min {
  display: flex; flex-direction: row; align-items: center; justify-content: flex-start;
  gap: 11px; padding: 8px 10px 8px 12px;
}
/*
 * `flex: none` is load-bearing, not tidiness. In the collapsed row the ring is a
 * flex item with the default `flex-shrink: 1`, so the caption and the handle
 * squeezed it to 17 px wide by 42 tall — an oval, measured before it shipped.
 * A circle whose width comes from `width` alone is not a circle inside a flex row.
 */
.sw-ptt-wrap.is-min .sw-ring { width: 42px; height: 42px; flex: none; }
/* And the caption yields instead, rather than pushing the handle off the end. */
.sw-ptt-wrap.is-min .sw-cap {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/*
 * The annulus is masked at a FIXED 6 px, so at 42 px it would be a third of the
 * radius and read as a disc rather than a ring. Scaled with the button.
 */
.sw-ptt-wrap.is-min .sw-ring::before {
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 3px), #000 calc(100% - 3px));
}
.sw-ptt-wrap.is-min .sw-ring.is-tx::before {
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 calc(100% - 4px));
}
.sw-ptt-wrap.is-min .sw-ring-mic { width: 17px; height: 17px; }
.sw-ptt-wrap.is-min .sw-cap { font-size: 10px; text-align: left; }

/* The collapse handle: floating in the corner when open, in the row when not. */
.sw-pttcol {
  position: absolute; top: 6px; right: 8px;
  border: 0; background: none; cursor: pointer; padding: 3px; line-height: 0;
  color: var(--a-faint); border-radius: 6px; display: grid; place-items: center;
}
.sw-pttcol svg { width: 16px; height: 16px; display: block; }
.sw-pttcol:hover { color: var(--a-text); background: var(--a-alt); }
.sw-pttcol:focus-visible { outline: 2px solid var(--a-primary); outline-offset: 1px; }
.sw-ptt-wrap.is-min .sw-pttcol { position: static; margin-left: auto; }
.sw-ring {
  position: relative; width: 118px; height: 118px; border-radius: 50%;
  border: 0; padding: 0; cursor: pointer; background: none; touch-action: none;
  display: grid; place-items: center;
}
/* The ring itself: a conic sweep masked to an annulus, so it can carry the
 * brand gradient the way the app's Canvas sweep does. */
.sw-ring::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(from 0deg, var(--a-primary-light), var(--a-primary) 40%,
              var(--a-primary-deep) 70%, var(--a-primary-light));
  opacity: 0.6;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
  transition: opacity 0.18s;
}
.sw-ring-mic { width: 34px; height: 34px; color: rgba(255,255,255,0.92); }
.sw-cap { font-size: 11px; font-weight: 700; letter-spacing: 0.1em; color: var(--a-dim); text-transform: uppercase; }

/* Requesting: rotate, as ringStyle(REQUESTING) does at 900ms. */
.sw-ring.is-requesting::before { opacity: 0.9; animation: sw-spin 0.9s linear infinite; }
/* Transmitting: full opacity, thicker stroke, glow, slow rotation. */
.sw-ring.is-tx::before {
  opacity: 1; animation: sw-spin 2.6s linear infinite;
  -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 8px));
          mask: radial-gradient(farthest-side, transparent calc(100% - 8px), #000 calc(100% - 8px));
}
.sw-ring.is-tx { box-shadow: 0 0 34px -6px rgba(255, 122, 0, 0.35); }
.sw-ring.is-tx .sw-cap, .sw-cap.is-tx { color: var(--a-primary); }
/*
 * Receiving COUNTER-rotates and is cyan. Both, deliberately: direction as well
 * as hue, so "them" never reads as "you" for a colour-blind user. The app makes
 * the same choice for the same reason.
 */
.sw-ring.is-rx::before {
  opacity: 0.95; background: conic-gradient(from 0deg, var(--a-receiving), #1b9aa3 60%, var(--a-receiving));
  animation: sw-spin 3.4s linear infinite reverse;
}
.sw-ring.is-offline::before { background: var(--a-border); opacity: 1; animation: none; }
.sw-ring.is-offline .sw-ring-mic { color: rgba(255,255,255,0.35); }
@keyframes sw-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .sw-ring::before { animation: none !important; }
}

/* --- composer --- */
.sw-note { padding: 8px 14px; font-size: 11.5px; color: var(--a-faint); line-height: 1.45; flex: none; }
.sw-note.is-ok { color: var(--a-available); }
.sw-note.is-warn { color: var(--a-primary-light); }
.sw-link { color: var(--a-primary-light); font-weight: 600; }

/* A class setting display:flex beats the [hidden] attribute on specificity, so
 * without this the composer stayed visible with nobody on the other end. */
.sw-form[hidden] { display: none; }
.sw-form {
  display: flex; gap: 8px; padding: 10px 12px; flex: none;
  border-top: 1px solid var(--a-border); background: var(--a-surface);
}
.sw-input {
  flex: 1; min-width: 0; padding: 10px 12px; font: inherit; font-size: 13.5px;
  border: 1px solid var(--a-border); border-radius: 10px;
  background: var(--a-bg); color: var(--a-text);
}
.sw-input::placeholder { color: var(--a-faint); }
.sw-input:focus { outline: none; border-color: var(--a-primary); }
.sw-send {
  border: 0; border-radius: 10px; padding: 0 14px; cursor: pointer; font: inherit;
  font-weight: 700; color: #1a0e00;
  background: linear-gradient(135deg, var(--a-primary-light), var(--a-primary) 55%, var(--a-primary-deep));
}
.sw-send:hover { filter: brightness(1.04); }

@media (max-width: 460px) {
  .sw { right: 12px; bottom: 12px; left: 12px; }
  .sw-panel { width: auto; left: 0; right: 0; height: calc(100vh - 96px); }
  /*
   * A phone has nothing to dock beside, and it is already effectively full
   * screen — so both larger modes collapse back to the one mobile layout rather
   * than becoming a rail nobody can see the page behind. The stored preference
   * is left alone: it is the visitor's desktop choice and comes back with the
   * wider screen.
   */
  .sw-panel.is-tall, .sw-panel.is-dock {
    position: absolute; inset: auto 0 auto 0; width: auto;
    height: calc(100vh - 96px);
    border-radius: 18px; border-width: 1px;
    box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.8);
  }
  html.sw-docked body { padding-right: 0; }
  html.sw-docked .sw-launch { display: inline-flex; }
}
/*
 * On a 320 px handset the header cannot hold the title, the E2EE badge AND two
 * icon buttons on one row.
 *
 * Measured, not guessed: with the badge the row wraps at a 303 px panel, which
 * is a 327 px viewport (the panel is the viewport less 24 px here); without it,
 * not until 249 px, which no device is. The badge is what gives, because the
 * subtitle beneath it already reads "end-to-end encrypted" whenever the chat is
 * live — so nothing is lost, whereas a wrapped header takes 23 px off a log
 * whose height is fixed.
 *
 * Adding the alert-sound toggle is what made this tight; before it there was
 * room. Anything else that lands in this header needs to re-measure.
 */
@media (max-width: 336px) {
  .sw-badge { display: none; }
}

/* ---------------------------------------------------------------- real product screens */

/* Keep retired illustrative mockups in the source for reference without ever
   allowing component display rules to override the native hidden attribute. */
[hidden] { display: none !important; }

/* Restore the original rotating globe as atmosphere behind the real product
   captures. The retired HTML device scenes stay in the document only as a
   reference; the globe canvas and branded halo are the only visible pieces. */
.stage-wrap.globe-only {
  --ss: 0.74;
  position: absolute; z-index: 0; top: 315px; left: 0; right: 0;
  height: calc(740px * var(--ss)); margin: 0;
  pointer-events: none; opacity: 0.78;
}
.globe-only .showcase,
.globe-only .orb-devices { display: none; }
.hero .eyebrow,
.hero h1,
.hero .lede,
.hero .cta-row,
.hero .note,
.real-hero-showcase { position: relative; z-index: 2; }

/* The landing page leads with the product itself: the current browser console
   beside the production web handset. The hero gets a wider wrap than the rest
   of the page so both interfaces stay legible instead of shrinking into two
   decorative cards. */
.hero { padding-bottom: 76px; }
.hero > .wrap { max-width: 1320px; }
.real-hero-showcase {
  width: min(100%, 1280px); margin: 56px auto 0;
  display: grid; grid-template-columns: minmax(0, 1.7fr) minmax(390px, 0.82fr);
  gap: 28px; align-items: center; text-align: left;
}
.real-hero-shot {
  position: relative; min-width: 0; margin: 0; overflow: hidden;
  border: 1px solid rgba(255,255,255,0.12); border-radius: 22px;
  background: #121419;
  box-shadow: 0 28px 70px rgba(0,0,0,0.42);
}
.real-hero-shot::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.04);
}
.real-hero-shot img { width: 100%; height: 100%; display: block; object-fit: cover; }
.real-hero-shot.console-shot {
  aspect-ratio: 1.6; border: 0; border-radius: 18px; background: transparent;
  box-shadow: 0 36px 90px rgba(0,0,0,0.48);
}
.real-hero-shot.handset-shot { aspect-ratio: 1.5; }

/* The right-hand hero panel is the production web handset itself, not a
   screenshot or a marketing reconstruction. The iframe stays on its own
   origin so registration, sign-in, device keys and the radio socket follow the
   exact same path they do at handset.pushtt.app. Microphone and camera access
   are delegated in the markup but are still requested only by the handset in
   response to the visitor using those controls. */
.real-hero-shot.handset-shot.live-handset-shot {
  height: clamp(620px, 74vh, 720px); aspect-ratio: auto;
  display: grid; grid-template-rows: auto minmax(0, 1fr) auto;
  padding: 10px; background: rgba(12, 14, 18, 0.96);
}
.live-handset-toolbar {
  min-width: 0; display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 0 2px 9px;
}
.live-handset-shot .live-handset-toolbar figcaption {
  position: static; flex: none; padding: 6px 9px;
  background: rgba(26, 29, 36, 0.92); backdrop-filter: none;
}
.live-handset-toolbar > a {
  min-width: 0; color: #aeb5bf; font-size: 0.64rem; font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
.live-handset-toolbar > a:hover { color: #fff; }
.live-handset-toolbar > a:focus-visible {
  outline: 2px solid var(--orange); outline-offset: 3px; border-radius: 4px;
}
.live-handset-toolbar > a b { color: var(--orange); font-size: 0.78rem; }
.live-handset-shot iframe {
  display: block; width: 100%; height: 100%; min-height: 0;
  border: 1px solid rgba(255,255,255,0.09); border-radius: 14px;
  background: #0f1115;
}
.live-handset-note {
  margin: 9px 4px 1px; color: #858d98; text-align: center;
  font-size: 0.65rem; line-height: 1.45;
}
.real-hero-shot figcaption {
  position: absolute; z-index: 2; top: 14px; left: 14px;
  padding: 7px 11px; border: 1px solid rgba(255,255,255,0.13); border-radius: 100px;
  background: rgba(11,13,17,0.78); backdrop-filter: blur(10px);
  color: #f6f7f8; font-size: 0.62rem; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
}
.real-hero-shot figcaption { display: flex; align-items: center; gap: 7px; }
.real-hero-shot figcaption span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--green);
  box-shadow: 0 0 0 3px rgba(61,214,140,0.15);
}
.real-hero-showcase > p {
  grid-column: 1 / -1; margin: 2px 0 0; text-align: center;
  color: #777f8b; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase;
}

/* Product walkthroughs use the same captures as the app feature pages. The
   captures are 16:9, with a portrait handset in their centre. Showing the full
   frame in a half-width card makes the interface unreadably small, so these
   stages crop the quiet side space and render the handset close to life size. */
#product .row2 {
  grid-template-columns: minmax(0, 1.28fr) minmax(320px, 0.72fr);
  gap: 56px;
}
.product-shot,
.mail-home-product-shot {
  position: relative; width: 100%; min-width: 0; margin: 0; overflow: hidden;
  border: 0; border-radius: 28px; background: #141619;
  box-shadow: 0 32px 72px rgba(28,37,41,0.24);
}
.product-shot:not(.product-shot-stack),
.mail-home-product-shot { height: clamp(560px, 54vw, 660px); }
.product-shot:not(.product-shot-stack) > img,
.mail-home-product-shot > img {
  position: absolute; left: 50%; top: 0; display: block;
  width: auto; max-width: none; height: 100%; transform: translateX(-50%);
}
.row2.flip > .product-shot { order: 2; }
.product-shot-stack {
  height: clamp(560px, 54vw, 660px); overflow: visible;
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
  border: 0; border-radius: 0; background: transparent; box-shadow: none;
}
.product-shot-pane {
  position: relative; min-width: 0; overflow: hidden; display: block;
  border: 1px solid #30343c; border-radius: 24px; background: #141619;
  box-shadow: 0 28px 62px rgba(28,37,41,0.22);
}
.product-shot-pane > img {
  position: absolute; left: 50%; top: 50%; display: block;
  /* The handset occupies about one third of each 16:9 capture. Scaling from
     pane width keeps the complete phone inside its half instead of sizing by
     the tall shared row and clipping both outer edges. */
  width: 285%; max-width: none; height: auto; transform: translate(-50%, -50%);
}
.mail-home-product-shot { border-color: #25282f; }

@media (min-width: 861px) {
  .mail-home-grid { grid-template-columns: 0.8fr 1.2fr; gap: 56px; }
}

/* Generated feature and solution pages distinguish a full captured product
   stage from the older, tightly cropped phone screenshots. */
.shot.is-product-stage { position: relative; }
.shot.is-product-stage img {
  width: 100%; max-width: 760px; border: 1px solid #2b2e35; border-radius: 22px;
  box-shadow: 0 24px 54px rgba(28,37,41,0.2);
}
.shot.is-product-stage figcaption {
  position: absolute; left: 50%; top: 14px; transform: translateX(-50%);
  padding: 7px 12px; border: 1px solid rgba(255,255,255,0.14); border-radius: 100px;
  background: rgba(11,13,17,0.78); color: #fff;
  font-size: 0.62rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase;
}
.shot.hero-shot.is-product-stage img { max-width: 860px; }

@media (max-width: 980px) {
  .stage-wrap.globe-only { --ss: 0.58; top: 365px; }
  .real-hero-showcase { max-width: 900px; grid-template-columns: 1fr; gap: 34px; }
  .real-hero-shot.handset-shot.live-handset-shot {
    width: min(100%, 460px); justify-self: center;
  }
  #product .row2 { grid-template-columns: 1fr; gap: 44px; }
  .row2.flip > .product-shot { order: 0; }
  .product-shot { width: min(100%, 760px); margin-inline: auto; }
  .mail-home-product-shot { width: min(100%, 760px); margin-inline: auto; }
  .product-shot-pane > img { width: 270%; }
}

@media (max-width: 700px) {
  .stage-wrap.globe-only { --ss: 0.36; top: 520px; }
  .hero { padding-bottom: 58px; }
  .real-hero-showcase { margin-top: 38px; grid-template-columns: 1fr; }
  .real-hero-shot.console-shot { aspect-ratio: 1.5; }
  .real-hero-shot.handset-shot.live-handset-shot {
    width: min(100%, 460px); height: clamp(540px, 78vh, 700px); aspect-ratio: auto;
    justify-self: center; margin-top: 0;
  }
  .real-hero-showcase > p { margin-top: 4px; font-size: 0.62rem; }
  .product-shot:not(.product-shot-stack),
  .mail-home-product-shot,
  .shot.hero-shot.is-product-stage {
    height: clamp(500px, 135vw, 600px); overflow: hidden; border: 0; border-radius: 22px;
    background: #141619;
  }
  .product-shot-stack {
    height: auto; grid-template-columns: 1fr; gap: 14px;
  }
  .product-shot-pane { height: clamp(500px, 135vw, 600px); }
  .product-shot-pane > img {
    top: 50%; width: auto; height: calc(100% - 20px);
    transform: translate(-50%, -50%);
  }
  .product-shot:not(.product-shot-stack) > img,
  .mail-home-product-shot > img,
  .shot.hero-shot.is-product-stage > img {
    position: absolute; left: 50%; top: 0; width: auto; max-width: none; height: 100%;
    transform: translateX(-50%); border: 0; border-radius: 0; box-shadow: none;
  }
  .real-hero-shot figcaption { top: 10px; left: 10px; font-size: 0.55rem; }
}
