/* ============================================================
   MARKETING — public home (/) + self-serve signup (/register) +
   auth surfaces (login, choose-role) + the floating Alfred widget.

   Design direction: "precision instrument" — near-black surface with a
   directional blue-tinted glow, restrained cyan-blue accent, glass-panel
   overlays, light geometric type (Inter, 400 throughout; 500 only for small
   caps labels and numerals). This file
   overrides shared.css's --g0..--g8 / --line / --line-strong tokens
   inside `body.mk`, so every shared component (.btn, .a-chat/.a-msg/
   .bubble/.a-input, .pill …) picks up the dark palette automatically —
   shared.css itself is untouched, so the authenticated dashboard (which
   never loads this file) is unaffected.
   ============================================================ */

* { box-sizing:border-box; }
/* Deliberately NOT scroll-behavior:smooth — chapter navigation sets the scroll
   position every tick, and the browser would re-animate each of those writes.
   Anchor links are animated in landing-motion.js instead. */

body.mk {
  /* ---- token overrides (cascade into every var(--gN)/var(--line*) use) ---- */
  --g0:#05070b; --g1:#0a0f16; --g2:#121822; --g3:#1c2531;
  --g4:#2d3946; --g5:#7c8794; --g6:#aab4c0; --g7:#e9edf2; --g8:#ffffff;
  --line:1px solid var(--g3); --line-strong:1px solid var(--g4);

  --accent:#86b6e8; --accent-2:#c3dcf5; --accent-dim:rgba(134,182,232,0.14);
  --accent-line:rgba(134,182,232,0.35);
  --danger:#ff8a76;
  --glass-bg:rgba(14,19,27,0.62); --glass-bg-strong:rgba(10,14,20,0.82);
  --glass-border:rgba(255,255,255,0.08);
  --font-display:'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-body:'Inter', -apple-system, BlinkMacSystemFont, sans-serif;

  background:var(--g0); color:var(--g7);
  font-family:var(--font-body);
  position:relative; isolation:isolate;
  /* shared.css pins `html,body { height:100% }` for the authenticated app
     shell. Here it would cap <body> at one viewport, and the sticky header —
     which can only travel inside its containing block — would come unstuck
     the moment you scrolled past the first screenful. */
  height:auto; min-height:100%;
}

/* ---- ambient light: fixed directional glow + faint star-field, sits behind all content ----
   Both layers slowly drift (60-90s loops) so the page never reads as a static
   screenshot, even before the visitor scrolls or moves the mouse. */
body.mk::before, body.mk::after {
  content:""; position:fixed; inset:-10%; z-index:-1; pointer-events:none;
}
body.mk::before {
  background:
    radial-gradient(1100px 720px at 88% -8%, rgba(134,182,232,0.16), transparent 60%),
    radial-gradient(900px 600px at -10% 30%, rgba(90,120,170,0.10), transparent 65%),
    radial-gradient(1400px 900px at 50% 115%, rgba(70,100,150,0.08), transparent 60%);
  animation:glow-drift 70s ease-in-out infinite alternate;
}
body.mk::after {
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, rgba(255,255,255,0.35) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 70% 15%, rgba(255,255,255,0.25) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 40% 70%, rgba(255,255,255,0.30) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 85% 60%, rgba(255,255,255,0.22) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 12% 85%, rgba(255,255,255,0.28) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 60% 45%, rgba(255,255,255,0.18) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 92% 90%, rgba(255,255,255,0.24) 50%, transparent 51%);
  background-repeat:no-repeat; opacity:0.6;
  animation:stars-drift 90s linear infinite alternate;
}
@keyframes glow-drift {
  0%   { transform:translate3d(0,0,0) scale(1); }
  50%  { transform:translate3d(-1.5%, 1%, 0) scale(1.03); }
  100% { transform:translate3d(1.5%, -1%, 0) scale(1); }
}
@keyframes stars-drift {
  0%   { transform:translate3d(0,0,0); }
  100% { transform:translate3d(-2%, -1.5%, 0); }
}
@media (prefers-reduced-motion:reduce) {
  body.mk::before, body.mk::after,
  .mk-mock-status .live-dot, .mk-mock-kpi, .mk-stat-ring, .mk-scroll-cue i::after { animation:none; }
}

.mk a { color:inherit; text-decoration:none; }
.mk img { max-width:100%; display:block; }
.mk section { padding:88px 0; position:relative; }
/* width:100% is load-bearing, not decoration: .mk-panel is a flex container, so
   .wrap is a flex item and sizes to its *content* rather than filling the
   panel. The spotlight's wrap came out 960px instead of 1160, which squeezed
   its text column to 362px — narrow enough that a heading line measured during
   the split no longer fitted afterwards and broke to an orphan word. */
.mk .wrap { width:100%; max-width:1160px; margin:0 auto; padding:0 24px; position:relative; }
/* Everything sets in regular. Tracking stays at a mild -0.02em rather than
   anything tighter: a 400 at display size goes cramped where an 800 read as
   solid, so the weight coming out has to be paid back in letter-spacing. */
.mk h1, .mk h2, .mk h3 {
  color:var(--g8); font-family:var(--font-display); font-weight:400;
  letter-spacing:-0.02em; margin:0;
}
.mk .accent { color:var(--accent); }
/* A short accent rule and letterspaced caps — no pill, no border, no fill.
   The bordered badge read as generic; this is the same information as a
   typographic mark instead of a component. */
.mk .eyebrow {
  display:inline-flex; align-items:center; gap:12px;
  font-size:10.5px; font-weight:500; letter-spacing:0.24em; text-transform:uppercase;
  color:var(--g5);
  border:0; background:none; padding:0; border-radius:0;
}
.mk .eyebrow::before {
  content:""; width:24px; height:1px; flex-shrink:0;
  background:var(--accent); opacity:0.75;
}
.mk .lead { font-size:15px; color:var(--g6); line-height:1.65; }

/* ---------- Header ---------- */
.mk-header {
  position:sticky; top:0; z-index:50;
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; padding:0 28px; height:60px;
  /* Bare at the top of the page, glass only once there is something to sit over.
     Carrying the fill from the start drew a 60px band across the hero: with
     nothing scrolled under it there is nothing for the blur to do, so all that
     showed was rgba(14,19,27,0.62) over near-black — measured at (11,15,22)
     against the page's (6,9,14), twice as bright, with a lit hairline along its
     edge. The .scrolled class was already being toggled; it just wasn't being
     used for anything but the shadow. */
  background:transparent; box-shadow:none;
  transition:box-shadow 400ms ease, background 400ms ease, backdrop-filter 400ms ease;
}
.mk-header.scrolled {
  background:var(--glass-bg); backdrop-filter:blur(16px); -webkit-backdrop-filter:blur(16px);
  /* One hairline only. A border-bottom *and* a 1px box-shadow in the same
     colour stacked into a 2px rule once .scrolled recoloured both. */
  box-shadow:0 1px 0 rgba(134,182,232,0.18), 0 12px 30px rgba(0,0,0,0.35);
}
.mk-brand { display:flex; align-items:center; gap:9px; font-family:var(--font-display); font-weight:500; letter-spacing:0.14em; font-size:13px; color:var(--g8); }
.mk-brand .material-symbols-outlined { font-size:20px !important; color:var(--accent); }
.mk-logo { height:13.5px; width:auto; display:block; filter:drop-shadow(0 0 10px rgba(134,182,232,0.35)); }
.mk-header .mk-logo { height:10.1px; }
/* Four links now, not three. The gap gives way first — at 30px the row ran
   into the actions on a 840px screen, which is exactly where the nav appears. */
.mk-nav { display:none; align-items:center; gap:clamp(18px, 2.2vw, 30px); font-size:12.5px; color:var(--g6); font-weight:500; white-space:nowrap; }
.mk-nav a {
  position:relative; padding:4px 0; transition:color 250ms ease;
  letter-spacing:0.01em;
}
.mk-nav a::after {
  content:""; position:absolute; left:0; right:100%; bottom:0; height:1px;
  background:var(--accent); transition:right 280ms cubic-bezier(.2,.8,.2,1);
}
.mk-nav a:hover { color:var(--g8); }
.mk-nav a:hover::after { right:0; }
@media (min-width:840px) { .mk-nav { display:flex; } }
.mk-actions { display:flex; align-items:center; gap:16px; }
.mk-actions::before {
  content:""; width:1px; height:20px; background:var(--glass-border);
}
.mk-actions .btn:not(.primary) {
  border-color:transparent; background:transparent; color:var(--g6); padding-left:4px; padding-right:4px;
}
.mk-actions .btn:not(.primary):hover { color:var(--g8); background:transparent; }
.mk .btn-lg { padding:9px 18px; font-size:12.5px; border-radius:7px; }

/* Base .btn is inherited from shared.css and now resolves against the
   dark tokens above — .btn.primary becomes a bright pill (near-white bg,
   near-black text), matching the reference's high-contrast CTA pattern. */
/* A button that carries an icon has to lay it out: the base .btn is an inline
   box, so an icon glyph lands on the text baseline at its own size and rides
   above the label with nothing between them. inline-flex centres the two and
   the gap spaces them; the glyph is sized here so no call site needs an
   inline override. */
.mk .btn {
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  border-radius:8px; transition:background 220ms ease, transform 220ms ease, box-shadow 220ms ease;
}
.mk .btn .material-symbols-outlined { font-size:16px !important; line-height:1; }
.mk .btn.primary { box-shadow:0 0 0 0 rgba(134,182,232,0); }
.mk .btn.primary:hover { box-shadow:0 8px 24px rgba(134,182,232,0.25); transform:translateY(-1px); }

/* ---------- Hero panels ----------
   Two full-viewport panels the visitor scrolls through one at a time: panel 1
   is Alfred (orb + headline + CTAs), panel 2 is the product (statement +
   mockup). `section.` is load-bearing here — it out-specifies
   `.mk section { padding:88px 0 }`, which would otherwise pad each panel past
   the viewport and knock its content off centre. */
/* The 60px sticky header sits over the top of whichever panel is at rest, so
   every panel reserves it. Border-box keeps that inside the 100vh rather than
   adding to it. The hero is excluded: it already subtracts the header from its
   own min-height. */
.mk section.mk-panel { padding:0; }
.mk section.mk-panel:not(.mk-hero) { padding-top:60px; }
/* `clip`, not `hidden`: hidden makes the section its own scroll container, and
   a position:sticky child inside one has nothing left to stick against. clip
   crops identically without creating that container. */
.mk-panel {
  min-height:100vh; display:flex; align-items:center;
  position:relative; overflow:clip;
}

/* A stepped chapter is several viewports tall and holds its content still
   while they scroll past — that span is the room the reel needs to advance.
   The height itself is set from data-steps in landing-motion.js so the two
   can't drift apart. */
.mk-panel[data-steps] { display:block; align-items:stretch; }
/* A stepped chapter must NOT carry the header reserve the other panels get.
   The padding pushes the hold's natural position 60px down, and a sticky box
   only pins once it would otherwise travel above its `top` — so on arrival the
   hold sits at y=60 and only snaps to y=0 once you have scrolled 60px more.
   Measured: at the chapter's first stop every element sat exactly 60px lower
   than at the four stops after it, which is the lurch you feel on the very
   first gesture into the section.
   The reserve isn't lost, it just moves: the hold is a full 100vh pinned at
   the top, and the header floats over it exactly as it does at every other
   step — the content clears it on the wrap's own padding.
   Specificity is deliberate: it has to outrank `.mk section.mk-panel:not(.mk-hero)`. */
.mk section.mk-panel[data-steps]:not(.mk-hero) { padding-top:0; }
.mk-panel[data-steps] .mk-hold {
  position:sticky; top:0; height:100vh; display:flex; align-items:center;
}
/* Content chapters use the panel geometry but still need breathing room; the
   global border-box keeps the padding inside the 100vh rather than adding to it. */
/* Scaled to the viewport, not fixed: 144px of padding is a fifth of a short
   laptop screen, and it was enough on its own to push a chapter past 100vh —
   which makes the panel taller than the viewport and stands the chapter-by-
   chapter wheel navigation down inside it. */
.mk-chapter .wrap { padding-top:clamp(44px, 8vh, 72px); padding-bottom:clamp(44px, 8vh, 72px); }
/* Panel 1 sits under the 60px sticky header, so it gets that much less height
   — otherwise the first screenful would overflow by exactly the header. */
.mk-hero.mk-panel { min-height:calc(100vh - 60px); }
.mk-hero .wrap { z-index:1; }

/* Panel 1 — centred on the orb behind it. */
.mk-hero-copy {
  position:relative; z-index:1;
  display:flex; flex-direction:column; align-items:center; text-align:center;
  max-width:900px; margin:0 auto;
}

/* Panel 2 — statement beside the product mockup. */
.mk-hero-2 .wrap { display:grid; grid-template-columns:1fr; gap:44px; align-items:center; padding-top:80px; padding-bottom:80px; }
@media (min-width:960px) { .mk-hero-2 .wrap { grid-template-columns:1.05fr 1fr; gap:56px; } }
.mk-hero-2-copy h2 {
  font-size:clamp(1.6rem, 1.15rem + 1.9vw, 2.7rem);
  line-height:1.22; margin-bottom:26px;
}

.mk-scroll-cue {
  position:absolute; left:50%; bottom:14px; transform:translateX(-50%); z-index:1;
  display:flex; flex-direction:column; align-items:center; gap:10px;
  font-size:10.5px; letter-spacing:0.16em; text-transform:uppercase; color:var(--g5);
}
/* Masked, so the travelling highlight fades out with the line instead of
   outliving it. The line's own gradient is already transparent near the
   bottom, so the drip kept showing up there as a detached dash — it read as a
   rendering fault rather than as light falling. */
.mk-scroll-cue i {
  display:block; width:1px; height:38px; background:linear-gradient(var(--accent), transparent);
  position:relative; overflow:hidden;
  -webkit-mask-image:linear-gradient(to bottom, #000 0%, #000 40%, transparent 92%);
  mask-image:linear-gradient(to bottom, #000 0%, #000 40%, transparent 92%);
}
.mk-scroll-cue i::after {
  content:""; position:absolute; left:0; top:-100%; width:100%; height:100%;
  background:linear-gradient(var(--g8), transparent); animation:cue-drip 1.8s ease-in-out infinite;
}
@keyframes cue-drip { 0% { top:-100%; } 100% { top:100%; } }

/* Oversized, near-invisible wordmark bleeding off the section — the same
   "giant ghost type" move the reference collage uses for scale/drama. */
/* Sits fully inside the panel: the panel clips its overflow, so any bleed off
   the edge reads as a sliced wordmark rather than a deliberate crop. */
.mk-hero-watermark {
  position:absolute; left:50%; transform:translateX(-50%); bottom:0;
  width:min(86%, 1000px);
  opacity:0.05; pointer-events:none; z-index:0; filter:blur(0.3px);
}
@media (max-width:960px) { .mk-hero-watermark { display:none; } }
.mk-hero h1 {
  font-size:clamp(2.4rem, 1.5rem + 4vw, 4.5rem);
  line-height:1.06; margin:22px 0 34px;
}
.mk-hero-ctas { display:flex; flex-wrap:wrap; gap:10px; justify-content:center; }
.mk-trust { display:flex; align-items:center; gap:10px; font-size:12px; color:var(--g5); }
.mk-trust .dots { display:flex; }
/* Three of the things Alfred moves, stacked the way a set of avatars would be.
   30px rather than 24: below that a photograph in a circle is just a coloured
   dot, and the point is that you can tell a car from a helicopter from a boat. */
.mk-trust .dots span {
  position:relative; overflow:hidden;
  width:30px; height:30px; border-radius:50%; background:var(--g2); border:2px solid var(--g0);
  margin-left:-9px; display:inline-block;
}
.mk-trust .dots span:first-child { margin-left:0; }
.mk-trust .dots img {
  width:100%; height:100%; object-fit:cover; display:block;
  filter:grayscale(1) brightness(0.74) contrast(1.18);
}
/* The same blue cast the band, the reel, the flow stage and the app cards carry,
   so three stock photographs this small still read as part of the system. */
.mk-trust .dots span::after {
  content:""; position:absolute; inset:0; border-radius:50%;
  background:linear-gradient(160deg, rgba(60,100,150,0.34), rgba(5,7,11,0.48));
  mix-blend-mode:multiply;
}

/* ---- Alfred's WebGL presence (ai-orb.js) — the one true "hero moment" ----
   Given real room above the mockup card instead of hiding behind it, so the
   particle sphere and its glow are the first thing that reads as "alive". */
.mk-hero-visual { position:relative; perspective:1400px; min-height:520px; display:flex; align-items:center; }
/* Square and centred on the panel: the shader renders a true sphere only when
   the canvas is square. `inset:0; margin:auto` (not a translate) keeps the
   element's transform free for the scroll hand-off tween. */
.mk-orb-canvas {
  position:absolute; inset:0; margin:auto; z-index:0; pointer-events:none;
  /* The orb's diffuse halo is still a level or two above the page's black when
     it reaches the canvas edge, and being cut there is what draws a 900px
     square around Alfred. Measured: (5,7,11) outside, (5,8,12) one pixel in.
     Only the outermost 12% of the radius is faded — the halo's tail and
     nothing else. The sphere lives inside the middle half of the canvas, so it
     is not touched at all; masking further in is what dimmed its rim and read
     as a glow. closest-side pins the falloff to the canvas's half-width, so it
     reaches zero exactly at each edge rather than still being partly opaque
     there, which is where the square would otherwise survive. */
  -webkit-mask-image:radial-gradient(circle closest-side, #000 88%, transparent 100%);
  mask-image:radial-gradient(circle closest-side, #000 88%, transparent 100%);
  /* The third term is what keeps the mask above honest. The panel clips at
     its own edge (overflow:clip), and a 900px canvas centred in a hero that is
     only 100vh-60 tall hangs ~70px past the bottom on a laptop — where the
     mask is still fully opaque, because its falloff is measured from the
     canvas's half-width, not from what survives the clip. The halo was
     therefore cut off mid-brightness in a dead-straight horizontal line: the
     black band across the foot of the hero.
     Bounded by the viewport's height as well, the canvas always fits inside
     the panel that clips it, so the fade reaches zero on its own terms and
     there is no edge left to see. 84vh clears the hero's 100vh-60 at any
     window over ~375px tall. */
  width:min(900px, 92vw, 84vh); height:min(900px, 92vw, 84vh);
}
.mk-beam { position:absolute; top:-6%; left:-8%; width:110%; height:110%; z-index:1; pointer-events:none; }
.mk-orb-mini { width:34px; height:34px; flex-shrink:0; border-radius:50%; }
.mk-mock-greet-row { display:flex; align-items:center; gap:10px; }

/* Floating glass stat card with a circular progress ring — the same
   "data card hovering over the dramatic image" move as the reference. */
.mk-stat-ring {
  /* Clear of the panel's top-right corner rather than sitting on it: at 4%/2%
     the card covered the window's own "Live · trip …" status line. */
  position:absolute; top:-3%; right:-2%; z-index:3;
  display:flex; align-items:center; gap:10px;
  background:var(--glass-bg-strong); border:1px solid var(--glass-border); border-radius:14px;
  padding:12px 16px; backdrop-filter:blur(16px); box-shadow:0 20px 50px rgba(0,0,0,0.45);
  animation:chip-float 6s ease-in-out infinite;
}
.mk-stat-ring .ring-svg { width:44px; height:44px; transform:rotate(-90deg); flex-shrink:0; }
.mk-stat-ring .ring-track { fill:none; stroke:rgba(255,255,255,0.08); stroke-width:5; }
.mk-stat-ring .ring-val {
  fill:none; stroke:var(--accent); stroke-width:5; stroke-linecap:round;
  stroke-dasharray:188.5; stroke-dashoffset:calc(188.5 - (188.5 * 0.96));
  filter:drop-shadow(0 0 5px var(--accent));
}
.mk-stat-ring .ring-txt { display:flex; flex-direction:column; line-height:1.15; }
.mk-stat-ring .ring-txt b { font-family:var(--font-display); font-size:16px; font-weight:500; color:var(--g8); }
.mk-stat-ring .ring-txt span { font-size:10px; color:var(--g5); text-transform:uppercase; letter-spacing:0.08em; }
@keyframes chip-float { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-7px); } }

/* ---- hero product mockup panel (replaces the old stock photo) ---- */
.mk-hero-mock {
  position:relative; z-index:2; border-radius:16px; overflow:hidden; width:100%;
  background:linear-gradient(180deg, rgba(20,27,38,0.82), rgba(8,11,16,0.9));
  border:1px solid var(--glass-border);
  box-shadow:0 40px 90px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.02) inset;
  transform:rotateX(4deg) rotateY(-6deg);
  transform-style:preserve-3d; will-change:transform;
  transition:transform 500ms cubic-bezier(.2,.8,.2,1);
}
.mk-mock-top {
  display:flex; align-items:center; gap:8px; padding:12px 16px;
  border-bottom:1px solid var(--glass-border); background:rgba(255,255,255,0.02);
}
.mk-mock-dot { width:8px; height:8px; border-radius:50%; background:var(--g4); }
.mk-mock-status {
  margin-left:auto; display:flex; align-items:center; gap:6px;
  font-size:10.5px; color:var(--g5); letter-spacing:0.02em;
}
.mk-mock-status .live-dot {
  width:6px; height:6px; border-radius:50%; background:var(--accent); box-shadow:0 0 8px var(--accent);
  animation:live-pulse 1.8s ease-in-out infinite;
}
@keyframes live-pulse { 0%,100% { opacity:1; transform:scale(1); } 50% { opacity:0.45; transform:scale(0.75); } }
.mk-mock-body { display:grid; grid-template-columns:64px 1fr; min-height:300px; }
@media (max-height:820px) {
  .mk-hero-2 .wrap { padding-top:48px; padding-bottom:48px; }
  .mk-hero-visual { min-height:440px; }
  .mk-mock-body { min-height:260px; }
}
.mk-mock-rail { border-right:1px solid var(--glass-border); padding:16px 0; display:flex; flex-direction:column; align-items:center; gap:14px; }
.mk-mock-rail .r-ic { width:30px; height:30px; border-radius:8px; display:grid; place-items:center; color:var(--g5); background:transparent; }
.mk-mock-rail .r-ic.on { background:var(--accent-dim); color:var(--accent); }
.mk-mock-rail .r-ic .material-symbols-outlined { font-size:16px !important; }
.mk-mock-main { padding:20px; display:flex; flex-direction:column; gap:14px; }
.mk-mock-greet { font-family:var(--font-display); font-size:15px; font-weight:500; color:var(--g8); }
/* The greeting's own line-height already leaves a gap, but -8px closed it past
   the point of reading as two lines — the two ran together. */
.mk-mock-sub { font-size:11.5px; color:var(--g5); margin-top:1px; }
.mk-mock-chat { display:flex; flex-direction:column; gap:8px; margin-top:4px; }
.mk-mock-chat .bubble { background:var(--g2); border:1px solid var(--glass-border); padding:9px 12px; border-radius:10px; font-size:12px; color:var(--g7); max-width:82%; }
.mk-mock-chat .bubble.me { background:var(--accent-dim); border-color:var(--accent-line); color:var(--g8); margin-left:auto; }
.mk-mock-kpis { display:grid; grid-template-columns:repeat(3,1fr); gap:8px; margin-top:auto; }
.mk-mock-kpi {
  background:rgba(255,255,255,0.03); border:1px solid var(--glass-border); border-radius:10px;
  padding:10px 12px; backdrop-filter:blur(6px);
}
.mk-mock-kpi .k { font-size:9.5px; letter-spacing:0.1em; text-transform:uppercase; color:var(--g5); }
.mk-mock-kpi .v { font-size:15px; font-weight:500; color:var(--g8); font-variant-numeric:tabular-nums; margin-top:2px; }
.mk-mock-kpi .v.up { color:var(--accent); }
.mk-mock-kpi { animation:kpi-float 5s ease-in-out infinite; }
.mk-mock-kpi:nth-child(2) { animation-delay:-1.6s; }
.mk-mock-kpi:nth-child(3) { animation-delay:-3.2s; }
@keyframes kpi-float { 0%,100% { transform:translateY(0); } 50% { transform:translateY(-4px); } }
@media (max-width:640px) { .mk-hero-mock { transform:none; } .mk-mock-body { grid-template-columns:1fr; } .mk-mock-rail { display:none; } }


/* ---------- "What that means" explainer ---------- */
.mk-explainer p {
  max-width:820px; margin:20px 0 0;
  font-family:var(--font-display); font-weight:400;
  font-size:clamp(1.05rem, 0.85rem + 0.85vw, 1.5rem); line-height:1.38;
  letter-spacing:-0.02em; color:var(--g7);
}

/* ---------- Motion primitives ---------- */
/* Type is split at runtime — headlines down to words, body copy down to lines —
   and each piece resolves out of a blur as it rises into place. Deliberately
   NOT an overflow:hidden line mask any more: a mask crops the blur halo at the
   line box, which is exactly the soft edge the reveal is made of.
   Both states here are the *resting* ones; the entrance/exit values only ever
   exist as inline styles written by landing-motion.js, so with JS or GSAP
   missing the type simply reads. */
.mk-line { display:block; }
.mk-line > span { display:block; }
.mk-word { display:inline-block; }


/* ---------- Metric reel (inside the explainer chapter) ----------
   Editorial rather than a carousel of equal tiles: the active shot dominates
   the screen and the one coming next sits beside it as a short card aligned to
   the top, so the hierarchy is carried by size, not by dimming. The track's x
   is driven from scroll in JS. */
/* Reserving the tallest state up front is what stops the rest of the chapter
   from moving: the hold centres its content vertically, so letting the active
   shot grow from 25vh to 47vh re-centred everything above it and the eyebrow
   and paragraph visibly jumped at every step. */
.mk-reel { --shot-max:47vh; margin-top:26px; overflow:clip; min-height:calc(var(--shot-max) + 98px); }
.mk-reel-track { display:flex; gap:26px; align-items:flex-start; will-change:transform; }
.mk-reel-item {
  flex:0 0 68%; opacity:0.6;
  transition:opacity 560ms cubic-bezier(.2,.8,.2,1);
}
.mk-reel-item.is-active { opacity:1; }
.mk-reel-item .shot {
  position:relative; height:25vh; border-radius:16px; overflow:hidden;
  border:1px solid var(--glass-border); background:var(--g1);
  transition:height 620ms cubic-bezier(.2,.8,.2,1);
}
.mk-reel-item.is-active .shot { height:var(--shot-max); }
/* Same duotone as the band photo, so stock imagery reads as part of the system. */
.mk-reel-item .shot img {
  width:100%; height:100%; object-fit:cover;
  filter:grayscale(1) brightness(0.6) contrast(1.14);
}
.mk-reel-item .shot::after {
  content:""; position:absolute; inset:0;
  background:linear-gradient(160deg, rgba(60,100,150,0.4), rgba(5,7,11,0.72));
  mix-blend-mode:multiply;
}

.mk-reel-item .val {
  font-family:var(--font-display); font-size:1.3rem; font-weight:400;
  letter-spacing:-0.03em; color:var(--g8); margin-top:16px; line-height:1;
  transition:font-size 560ms cubic-bezier(.2,.8,.2,1);
}
.mk-reel-item.is-active .val { font-size:clamp(1.9rem, 1.3rem + 1.6vw, 2.9rem); }
.mk-reel-item .lbl {
  font-size:10.5px; letter-spacing:0.22em; text-transform:uppercase;
  color:var(--g5); margin-top:8px;
}
.mk-explainer .wrap { padding-top:52px; padding-bottom:52px; }
@media (max-width:840px) { .mk-reel-item { flex:0 0 82%; } }
/* ---------- Feature grid ----------
   Portrait cards led by a luminous abstract visual rather than a small icon in
   a box. Each visual is built from gradients, not imagery: it renders at any
   size, weighs nothing, and stays exactly on palette. */
/* The head needs real air under it before a grid starts — at 20px the lead read
   as a caption for the first card rather than for the section. Scaled to the
   viewport so a short screen doesn't spend it all here. */
.mk-section-head { max-width:560px; margin-bottom:clamp(28px, 4.5vh, 52px); }
.mk-section-head h2 { font-size:clamp(1.6rem, 1.2rem + 1.2vw, 2.3rem); margin:14px 0 10px; letter-spacing:-0.02em; }
/* The Alfred wordmark's gradient — white to #b0d6ff, raked about 11° down to
   the right — carried onto the section headings so the type and the mark are
   lit from the same source. The ramp is applied per word in landing-motion.js
   (see tintGradient) rather than as a `background-clip: text` gradient on the
   heading: that technique paints from the *heading*, and a word's own opacity,
   blur and transform are then ignored by the clip — the whole reveal stops
   working on exactly these headings. These two custom properties are the ends
   of the ramp; without JS the heading just stays --grad-a. */
.mk-section-head h2,
.mk-hero h1,
.mk-hero-2-copy h2,
.mk-explainer p,
.mk-cta h2 { --grad-a:#ffffff; --grad-b:#b0d6ff; }
.mk-features { display:grid; grid-template-columns:1fr; gap:26px; }
@media (min-width:640px) { .mk-features { grid-template-columns:repeat(2, 1fr); gap:44px; } }
@media (min-width:1024px) { .mk-features { grid-template-columns:repeat(3, 1fr); column-gap:44px; row-gap:0; } }

/* No card chrome: columns separated by hairlines, the way an editorial grid
   sets a page. Four identical boxes in a row was the part that read as basic.
   Separation comes from the gap rather than per-column padding — padding had to
   be dropped on the outer two to keep them flush with the headline, which left
   those columns wider than the middle pair. */
.mk-feature {
  position:relative; display:flex; flex-direction:column;
  transition:transform 340ms cubic-bezier(.2,.8,.2,1);
}
@media (min-width:640px) {
  .mk-feature + .mk-feature::before {
    content:""; position:absolute; left:-22px; top:0; bottom:0; width:1px;
    background:var(--glass-border);
  }
}
.mk-feature:hover { transform:translateY(-5px); }

/* The luminous panel. Layered radial/linear gradients over a dark base, with
   the icon riding on top at low opacity the way a maker's mark would. */
/* Staggered size and drop: each column's visual is shorter than the one before
   and starts lower, so the row reads as a descending stair instead of a bar. */
.viz {
  position:relative; flex-shrink:0; border-radius:12px;
  background-color:#04070c; overflow:hidden;
  height:clamp(150px, 24vh, 250px); margin-top:16px;
}
/* Width steps down alongside height: in the reference the images narrow as
   well as shorten, and that second axis is most of what keeps the row from
   reading as one uniform band. */
/* Three columns now, not four. The fourth feature has a screen of its own
   (.mk-spotlight), and the height it used to cost goes back to the trio — the
   row was reading as cramped precisely because a fourth item had to fit under
   it.
   The visuals are not given a height: they take whatever vertical room the
   viewport has left. Sizing them in vh meant guessing, because the copy above
   them doesn't scale with the screen — the same coefficient that filled a short
   laptop left a dead band under a tall monitor, and the one that filled the
   tall monitor overflowed the laptop. Stretching the wrap and letting the
   visuals absorb the remainder makes the row fill the screen at every height.
   The staircase survives it: each visual still starts lower than the one
   before, so taking the same remaining space leaves each one shorter. */
@media (min-width:1024px) {
  #features .wrap { align-self:stretch; display:flex; flex-direction:column; justify-content:center; }
  /* Each visual is sized outright, in a fixed 1 : 0.8 : 0.63 ratio. Letting
     them all `flex:1` into the stretched row made them end on one line, so the
     only thing separating them was where they started — three near-identical
     slabs, which is the opposite of the descending trio this is meant to be.
     Width steps down alongside height, because that second axis is most of
     what keeps the row from reading as one uniform band. */
  #features .mk-features { flex:0 0 auto; align-items:start; }
  .mk-feature:nth-child(1) .viz { height:clamp(180px, 37vh, 400px); margin-top:16px; }
  .mk-feature:nth-child(2) .viz { height:clamp(146px, 29.5vh, 320px); margin-top:clamp(20px, 4vh, 46px); width:88%; }
  .mk-feature:nth-child(3) .viz { height:clamp(118px, 23vh, 252px); margin-top:clamp(36px, 8vh, 90px); width:74%; }
}
.viz .material-symbols-outlined {
  position:absolute; top:14px; left:16px; z-index:2;
  font-size:20px !important; color:rgba(255,255,255,0.62);
}
.viz::before, .viz::after {
  content:""; position:absolute; inset:0; pointer-events:none;
}

/* A hard light source throwing a cone across the panel. The point itself has
   to sit just inside the edge, not past it — clipped off-canvas the cone lost
   its origin and read as a smudge. */
.viz-beam::before {
  background:
    radial-gradient(ellipse 130% 26% at 3% 60%,
      rgba(255,255,255,1), rgba(206,230,255,0.82) 7%, rgba(134,182,232,0.5) 20%,
      rgba(58,108,180,0.22) 44%, transparent 72%);
}
.viz-beam::after {
  background:radial-gradient(circle at 3% 60%, rgba(255,255,255,1), rgba(210,232,255,0.7) 2.2%, transparent 7%);
}

/* Alfred's own presence: a soft sphere of light. */
.viz-orb::before {
  background:
    radial-gradient(circle at 52% 56%,
      rgba(255,255,255,0.98), rgba(206,231,255,0.66) 9%, rgba(120,168,228,0.36) 23%,
      rgba(46,84,146,0.2) 40%, transparent 62%);
}
.viz-orb::after {
  background:radial-gradient(circle at 52% 56%, rgba(255,255,255,0.9), transparent 4%);
}

/* A lit plane crossed by a single thin rule — structure and scale. */
.viz-grid::before {
  background:linear-gradient(144deg, rgba(208,230,255,0.76), rgba(74,118,186,0.4) 40%, transparent 76%);
}
.viz-grid::after {
  background:
    linear-gradient(to right, transparent calc(50% - 0.5px), rgba(255,255,255,0.42) 50%, transparent calc(50% + 0.5px)),
    linear-gradient(to bottom, transparent calc(46% - 0.5px), rgba(255,255,255,0.3) 46%, transparent calc(46% + 0.5px));
  transform:rotate(-13deg) scale(1.5);
}

/* A sheet of light raking in from the top edge. */
.viz-sheet::before {
  background:
    linear-gradient(164deg, rgba(238,247,255,0.82), rgba(140,183,240,0.4) 30%, transparent 66%),
    radial-gradient(ellipse 62% 42% at 86% 4%, rgba(226,241,255,0.66), transparent 58%);
}
/* Only a light foot, so the card's own background takes over cleanly. */
.viz-sheet::after {
  background:linear-gradient(to bottom, transparent 72%, rgba(4,7,12,0.6));
}

/* Text leads, visual follows — the order the reference sets. */
.mk-feature h3 { order:-2; font-size:15px; font-weight:500; letter-spacing:-0.01em; margin:0 0 8px; }
.mk-feature p { order:-1; font-size:12.5px; color:var(--g5); line-height:1.62; margin:0; }

/* ---------- Feature spotlight (the fourth, on its own screen) ----------
   Built out of .mk-section-head rather than a bespoke block, so it inherits the
   page's type scale and — more to the point — the word-by-word reveal every
   other section heading gets, with no special case in landing-motion.js. */
.mk-spotlight .wrap { display:grid; grid-template-columns:1fr; gap:32px; align-items:center; }
@media (min-width:960px) {
  .mk-spotlight .wrap { grid-template-columns:minmax(0, 0.85fr) minmax(0, 1.15fr); gap:60px; }
}
.mk-spotlight .mk-section-head { margin-bottom:0; }
.mk-spotlight .viz { height:clamp(230px, 42vh, 430px); border-radius:16px; }

/* At card size the sheet of light was enough on its own. Blown up to a
   spotlight it said nothing about billing, and the ruled gradients that stood
   in for a document read as a rendering fault — a band of noise under a blur.
   So the light now falls across a real one: .viz-doc is ordinary markup, set in
   ink on the lit half of the panel and faded out where the panel goes dark, so
   there is something legible under the sheet instead of a texture. */
.viz-doc {
  position:absolute; left:8%; top:19%; width:58%;
  font-size:clamp(8.5px, 0.86vw, 11.5px); line-height:1.5;
  color:rgba(6,11,20,0.72); letter-spacing:-0.005em;
  /* The sheet darkens towards the bottom-right; the ink has to go with it or
     it floats over the shadow as a bright rectangle. */
  -webkit-mask-image:linear-gradient(158deg, #000 58%, rgba(0,0,0,0.62) 84%, rgba(0,0,0,0.12) 104%);
  mask-image:linear-gradient(158deg, #000 58%, rgba(0,0,0,0.62) 84%, rgba(0,0,0,0.12) 104%);
}
.viz-doc .vd-head {
  display:flex; flex-direction:column; gap:2px;
  padding-bottom:0.9em; margin-bottom:0.7em;
  border-bottom:1px solid rgba(6,11,20,0.3);
}
.viz-doc .vd-head b { font-size:1.28em; font-weight:600; color:rgba(6,11,20,0.88); letter-spacing:-0.015em; }
.viz-doc .vd-head span { font-size:0.92em; color:rgba(6,11,20,0.6); }
.viz-doc .vd-rows { list-style:none; margin:0; padding:0; }
.viz-doc .vd-rows li {
  display:flex; align-items:baseline; justify-content:space-between; gap:1.4em;
  padding:0.52em 0; border-bottom:1px solid rgba(6,11,20,0.16);
}
.viz-doc .vd-rows i { font-style:normal; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.viz-doc .vd-rows em { font-style:normal; font-variant-numeric:tabular-nums; color:rgba(6,11,20,0.8); }
.viz-doc .vd-total {
  display:flex; align-items:baseline; justify-content:space-between;
  margin-top:0.85em; padding-top:0.7em; border-top:1.5px solid rgba(6,11,20,0.4);
}
.viz-doc .vd-total span { font-size:0.94em; color:rgba(6,11,20,0.64); }
.viz-doc .vd-total b { font-size:1.18em; font-weight:600; color:rgba(6,11,20,0.88); font-variant-numeric:tabular-nums; }

/* The card-sized sheet runs out of light two thirds of the way down, which at
   spotlight size is exactly where the last rows of the document sit. An extra
   pool over the left half carries the lit surface far enough to hold them. */
.mk-spotlight .viz-sheet::before {
  background:
    radial-gradient(ellipse 58% 62% at 24% 34%, rgba(226,239,255,0.6), rgba(150,186,236,0.24) 54%, transparent 82%),
    linear-gradient(164deg, rgba(238,247,255,0.82), rgba(140,183,240,0.44) 38%, transparent 78%),
    radial-gradient(ellipse 62% 42% at 86% 4%, rgba(226,241,255,0.66), transparent 58%);
}

/* Now purely the gloss over the document: a bloom where the light enters and a
   foot that hands the bottom edge back to the panel's own background. */
.mk-spotlight .viz-sheet::after {
  background:
    radial-gradient(ellipse 54% 40% at 84% 6%, rgba(255,255,255,0.5), transparent 62%),
    linear-gradient(to bottom, transparent 78%, rgba(4,7,12,0.4));
}
/* Ink, not light: this icon sits on the brightest part of the sheet, where a
   white mark at 62% just smears. */
.mk-spotlight .viz .material-symbols-outlined { color:rgba(6,11,20,0.44); z-index:3; }

/* Under the spotlight's two-column breakpoint the panel is much shorter and
   full-width — the document has to shrink with it rather than overrun. */
@media (max-width:959px) {
  .viz-doc { width:64%; top:22%; }
  .viz-doc .vd-rows li:nth-child(n+4) { display:none; }
}

/* `text-wrap:balance` is recent enough to be worth a floor under it. Narrowing
   this one head is that floor: without balance the line still breaks after
   "dispatchers" rather than orphaning the last word. Scoped to this section —
   "How it works" has a shorter headline that fits on one line at 820px, and
   narrowing it would break a line that currently doesn't need to. */
/* The head had been squeezed to 3vh back when the rail's column was driving
   the chapter's height and everything was fighting for room. That is fixed
   (the notes reserve nothing now), so the title gets its air back — at 3vh it
   sat right on top of the first screenshot. */
#platform .mk-flow-head { max-width:700px; margin-bottom:clamp(26px, 6vh, 64px); }

/* ---------- The product itself (the flow component, retuned) ----------
   Same rail / stage / note as "How it works". Two things have to change,
   and both come from these being interfaces rather than photographs:

   1. Proportion. The flow's three equal-ish columns leave the stage about
      470px wide and nearly square — a dashboard cropped into that is
      unreadable. Here the rail and its note share the left column so the stage
      can take the width, and the stage holds the screenshots' own aspect
      instead of a fixed height, so nothing is cut.
   2. Treatment. The duotone that makes stock photography belong to the page
      turns a UI into grey mush, so the screens keep their colour. They are
      still held back — two of the four are light-on-white and would otherwise
      be the brightest thing on a black page — and the veil clears as a screen
      becomes the active one. */
@media (min-width:960px) {
  .mk-flow.mk-screens {
    grid-template-columns:minmax(180px, 0.58fr) minmax(0, 1.7fr);
    /* `auto 1fr`, not `auto auto`: the stage spans both rows, and grid hands a
       spanning item's extra height to its auto rows in equal shares — which
       pushed the rail halfway down its own column and left the note a sliver.
       The rail hugs its content; the note takes whatever the stage leaves. */
    grid-template-rows:auto 1fr;
    align-items:start; gap:26px 46px;
  }
  .mk-screens .mk-flow-nav { grid-column:1; grid-row:1; }
    /* Deliberately reserving NO height. The flow's note column reserves 168px so
     that swapping notes can't resize the row — but here the notes share a
     column with the rail while the stage spans both rows, so any height they
     reserve is added to a column that is already the shorter of the two. At
     200px it overtook the stage and made the chapter taller than its own
     one-viewport hold, which clips the headline off the top and leaves every
     step landing somewhere slightly different.
     With nothing reserved, the chapter's height is the stage's height alone —
     identical at every step, which is what stops the jumping. The notes are
     absolutely positioned inside the row either way, so they still can't move
     anything when they swap. */
  .mk-screens .mk-flow-notes { grid-column:1; grid-row:2; min-height:0; }
  .mk-screens .mk-flow-note { position:absolute; inset:0 0 auto; }
  .mk-screens .mk-flow-note p { max-width:none; font-size:12.5px; }
  /* The rail gives up its air before the stage does: it is the one part of the
     column whose spacing costs height without carrying any content. */
  .mk-screens .mk-flow-nav { gap:clamp(8px, 1.5vh, 14px); }
  .mk-screens .mk-flow-stage { grid-column:2; grid-row:1 / span 2; align-self:center; }
}

/* Height comes from the screenshots' own ratio, capped in vh so the stage can
   never outgrow the chapter's one-viewport hold. Where the cap bites, the crop
   takes the bottom of the screen — empty table rows — and never the chrome. */
.mk-screens .mk-flow-stage {
  width:100%; height:auto; aspect-ratio:1.865; max-height:52vh;
  border-radius:16px;
}
.mk-screens .mk-flow-shot img {
  object-position:center top;
  filter:brightness(0.9) saturate(0.97);
}
.mk-screens .mk-flow-shot::after {
  background:linear-gradient(200deg, rgba(40,72,116,0.26), rgba(5,7,11,0.5));
  mix-blend-mode:normal;
  transition:opacity 760ms cubic-bezier(.2,.8,.2,1);
}
.mk-screens .mk-flow-shot.is-active::after { opacity:0.22; }
/* A hairline of light on the top edge, so the stage reads as a pane of glass
   rather than a pasted rectangle. The flow's own ::before is a broad wash that
   would sit over the interface. */
.mk-screens .mk-flow-shot::before {
  background:linear-gradient(to bottom, rgba(255,255,255,0.09), transparent 6%);
}

/* A step of "How it works" that shows a real Alfred screen takes the same
   exemption the product chapter's shots take: the page's duotone is for
   photography, and it turns an interface into grey mush.
   Centred rather than top-anchored, unlike the dashboards next door — this
   screen's content sits in the middle of its frame, not along the top. */
.mk-flow-shot.is-ui img { object-position:center; filter:brightness(0.88) saturate(0.95); }
.mk-flow-shot.is-ui::after {
  background:linear-gradient(200deg, rgba(40,72,116,0.24), rgba(5,7,11,0.46));
  mix-blend-mode:normal;
  transition:opacity 760ms cubic-bezier(.2,.8,.2,1);
}
.mk-flow-shot.is-ui.is-active::after { opacity:0.2; }
.mk-flow-shot.is-ui::before {
  background:linear-gradient(to bottom, rgba(255,255,255,0.08), transparent 7%);
}

/* ---------- Live route — the show panel ----------
   The canvas is full-bleed and everything else floats over it. The copy sits
   top-left rather than centred, so the scene keeps the middle of the frame. */
.mk-route { position:relative; overflow:clip; }
.mk-route .wrap { z-index:2; align-self:flex-start; }
/* Narrower than the page default: the destination label lands near the top
   of the frame and the copy has to stay clear of it. */
.mk-route .mk-section-head { max-width:440px; }
/* The panel is flex-centred like every other; this pins the copy to the top
   band and lets the terrain own everything below it. */
.mk-route { align-items:flex-start; }
.mk-route .wrap { padding-top:clamp(18px, 4vh, 48px); }

.mk-route-canvas {
  position:absolute; inset:0; width:100%; height:100%; z-index:0;
  pointer-events:none; opacity:0;
  transition:opacity 1600ms cubic-bezier(.2,.8,.2,1);
}
.mk-route.is-live .mk-route-canvas { opacity:1; }
/* A horizon wash under the scene, so the terrain sits in the page rather than
   on it — and so the panel still reads if WebGL never starts. */
.mk-route::before {
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:
    radial-gradient(120% 62% at 50% 96%, rgba(26,58,100,0.42), transparent 62%),
    radial-gradient(70% 40% at 50% 42%, rgba(134,182,232,0.07), transparent 70%);
}
/* Fine noise over the whole panel: at this size the gradients would otherwise
   band into visible steps on a dark screen. */
.mk-route-grain {
  position:absolute; inset:0; z-index:1; pointer-events:none; opacity:0.4;
  background-image:radial-gradient(rgba(255,255,255,0.045) 0.5px, transparent 0.5px);
  background-size:3px 3px;
}

/* ---- the HUD over the scene ---- */
.mk-route-hud { position:absolute; inset:0; z-index:2; pointer-events:none; }
.mk-route-hud .pin, .mk-route-chip {
  position:absolute; padding:9px 14px; border-radius:12px;
  border:1px solid var(--glass-border); background:var(--glass-bg-strong);
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  box-shadow:0 18px 44px rgba(0,0,0,0.45);
  opacity:0;
  transition:opacity 900ms ease, transform 1100ms cubic-bezier(.2,.8,.2,1);
}
.mk-route.is-live .pin { opacity:1; }
.mk-route.is-live .mk-route-chip { opacity:1; }
.mk-route.is-live .pin-a { transition-delay:520ms; }
.mk-route.is-live .mk-route-chip { transition-delay:860ms; }

.mk-route-hud b { display:block; font-size:12.5px; font-weight:500; color:var(--g8); letter-spacing:-0.01em; }
.mk-route-hud span { display:block; font-size:11px; color:var(--g5); margin-top:2px; }

/* No markers on the ends: the route's own stroke already arrives at both, and
   a dot on top of it was one mark too many. The leader below is what ties each
   label to its point. */
/* The leader: a hairline from the label back to the point on the route. */
.mk-route-hud .pin::before {
  content:""; position:absolute; left:-17px; top:50%; width:17px; height:1px;
  background:linear-gradient(to right, var(--accent-line), var(--glass-border));
}

/* The anchor is driven from the scene each frame (route-map.js hands back the
   projected screen position of each end of the route); the pin only offsets
   itself from that point. Splitting the two transforms is deliberate — one
   element cannot carry both a per-frame position and a reveal. */
.mk-route-hud .anchor {
  position:absolute; left:0; top:0; will-change:transform;
  opacity:0; transition:opacity 500ms ease;
}
.mk-route.is-live .anchor { opacity:1; }
/* Offset from the point it names, with the leader bridging the gap. */
.mk-route-hud .pin { position:absolute; left:17px; top:0; transform:translateY(-50%); white-space:nowrap; }
/* The destination sits high in the frame, so its label hangs below-right
   rather than running into the copy above it. */
.mk-route-hud .pin-b { top:14px; }
/* Offset from the point on the route it names, exactly like the two pins —
   the anchor around it carries the per-frame position, this carries only the
   offset and the reveal. It used to sit at left:5%/bottom:8% of the panel,
   which is measured off the section rather than off the terrain: on a short or
   wide viewport the point field simply isn't down there, and the card ended up
   parked on bare background below the map. */
.mk-route-chip {
  left:19px; top:0; transform:translateY(-50%);
  display:flex; align-items:center; gap:10px; white-space:nowrap;
}
/* Same hairline leader the pins use, tying the card back to its point. */
.mk-route-chip::before {
  content:""; position:absolute; left:-19px; top:50%; width:19px; height:1px;
  background:linear-gradient(to right, var(--accent-line), var(--glass-border));
}
.mk-route-chip .live {
  width:7px; height:7px; flex:0 0 7px; border-radius:50%; background:#67e8a6;
  box-shadow:0 0 10px rgba(103,232,166,0.8); animation:route-live 2.4s ease-in-out infinite;
}
@keyframes route-live { 0%,100% { opacity:1; } 50% { opacity:0.35; } }

/* No mobile override any more: the chip is placed off the terrain itself, and
   route-map.js clamps that point to keep the whole card inside the frame. */
@media (prefers-reduced-motion: reduce) {
  .mk-route-chip .live { animation:none; }
}

/* ---------- How it works — stepped flow ----------
   Three steps sharing one screenful. Left: the rail, which marks where you are.
   Middle: the stage, one shot at a time. Right: the note for the step you're on.
   Scroll drives the step (landing-motion.js), and the stage scales up from 0.88
   as the chapter travels into place, so arriving at the section is itself part
   of the motion rather than a cut to a finished layout. */
.mk-flow-head { text-align:center; max-width:820px; margin:0 auto 38px; }
/* Balanced, not ragged: a centred headline this wide lands its last word alone
   on a second line ("…actually work / in."). `text-wrap:balance` has to be in
   force before landing-motion.js splits the heading — the splitter groups words
   by the offsetTop they already have, so whatever the browser decided at that
   moment is what gets frozen into the line masks. It runs after the webfont
   swap, which is exactly when the balanced measurement is the real one. */
.mk-flow-head h2 {
  font-size:clamp(1.5rem, 1.1rem + 1.7vw, 2.5rem); line-height:1.2;
  text-wrap:balance;
}

.mk-flow { display:grid; grid-template-columns:1fr; gap:28px; align-items:center; }
@media (min-width:960px) {
  .mk-flow { grid-template-columns:minmax(170px,0.85fr) minmax(0,1.5fr) minmax(0,0.9fr); gap:44px; }
}

/* The rail. Inactive steps stay legible but recede; only the one you're on
   carries full contrast and a lit dot. */
.mk-flow-nav { display:flex; flex-direction:column; gap:14px; }
.mk-flow-nav button {
  display:flex; align-items:center; gap:11px;
  background:none; border:0; padding:0; cursor:pointer; text-align:left;
  font-family:var(--font-display); font-weight:400;
  font-size:clamp(0.95rem, 0.84rem + 0.42vw, 1.22rem); letter-spacing:-0.02em;
  color:var(--g4); transition:color 460ms cubic-bezier(.2,.8,.2,1);
}
.mk-flow-nav button:hover { color:var(--g6); }
/* The dot keeps its 6px of room at every state, so the labels never shift
   sideways as the active step moves down the rail. */
.mk-flow-nav button i {
  width:6px; height:6px; flex:0 0 6px; border-radius:50%;
  background:var(--accent); opacity:0; transform:scale(0.35);
  transition:opacity 460ms ease, transform 460ms cubic-bezier(.2,.8,.2,1);
}
.mk-flow-nav button.is-active { color:var(--g8); }
.mk-flow-nav button.is-active i { opacity:1; transform:none; box-shadow:0 0 10px var(--accent); }

/* A fixed height rather than an aspect ratio: the chapter's hold is exactly one
   viewport, so the stage has to be sized against the viewport to stay inside it. */
.mk-flow-stage {
  position:relative; width:100%; height:clamp(260px, 46vh, 480px);
  border-radius:18px; overflow:hidden; border:1px solid var(--glass-border);
  background:var(--g1); transform-origin:50% 55%; will-change:transform;
}
/* The shot resolves the same way the type does — out of a blur, not a plain
   crossfade — so changing step speaks the same language as arriving at the
   section. */
.mk-flow-shot {
  position:absolute; inset:0; margin:0; opacity:0; transform:scale(1.06);
  transition:opacity 760ms cubic-bezier(.2,.8,.2,1), transform 1100ms cubic-bezier(.2,.8,.2,1);
}
.mk-flow-shot.is-active { opacity:1; transform:none; }
/* Same duotone as the rest of the page's photography. */
.mk-flow-shot img { width:100%; height:100%; object-fit:cover; filter:grayscale(1) brightness(0.58) contrast(1.15); }
.mk-flow-shot::after {
  content:""; position:absolute; inset:0;
  background:linear-gradient(160deg, rgba(60,100,150,0.42), rgba(5,7,11,0.74));
  mix-blend-mode:multiply;
}
.mk-flow-shot::before {
  content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(200deg, rgba(134,182,232,0.2), transparent 55%);
}

/* Notes are stacked on top of each other so swapping one for the next can't
   move anything around it. */
.mk-flow-notes { position:relative; min-height:168px; }
.mk-flow-note {
  position:absolute; inset:0; opacity:0; pointer-events:none;
  transition:opacity 540ms ease;
}
.mk-flow-note.is-active { opacity:1; pointer-events:auto; }
.mk-flow-note p { font-size:13px; color:var(--g6); line-height:1.68; max-width:330px; }

/* Inside the note, the copy resolves line by line and the tag follows — the
   same reveal the sections use on arrival, driven by a class swap instead of
   scroll. The container fade above is what carries this when the lines don't
   exist (no JS, no GSAP, reduced motion): these rules simply match nothing. */
.mk-flow-note .mk-line, .mk-flow-note .mk-flow-tag {
  opacity:0; transform:translateY(14px); filter:blur(8px);
  transition:opacity 620ms ease, transform 820ms cubic-bezier(.2,.8,.2,1),
             filter 820ms cubic-bezier(.2,.8,.2,1);
}
.mk-flow-note.is-active .mk-line,
.mk-flow-note.is-active .mk-flow-tag { opacity:1; transform:none; filter:none; }
.mk-flow-note.is-active .mk-line:nth-child(2) { transition-delay:80ms; }
.mk-flow-note.is-active .mk-line:nth-child(3) { transition-delay:160ms; }
.mk-flow-note.is-active .mk-line:nth-child(4) { transition-delay:240ms; }
.mk-flow-note.is-active .mk-line:nth-child(n+5) { transition-delay:320ms; }
.mk-flow-note.is-active .mk-flow-tag { transition-delay:400ms; }
.mk-flow-tag {
  display:inline-flex; align-items:center; margin-top:24px; padding:11px 20px;
  border-radius:11px; border:1px solid var(--glass-border); background:var(--glass-bg);
  backdrop-filter:blur(8px); color:var(--g7); font-size:12.5px; letter-spacing:-0.005em;
}

/* With motion reduced the scroll never advances the step, so the notes simply
   stack and every step reads at once. */
@media (prefers-reduced-motion: reduce) {
  .mk-flow-notes { min-height:0; display:flex; flex-direction:column; gap:18px; }
  .mk-flow-note { position:static; opacity:1; transform:none; pointer-events:auto; }
  .mk-flow-note .mk-line, .mk-flow-note .mk-flow-tag { opacity:1; transform:none; filter:none; }
  .mk-flow-note p { max-width:none; }
  .mk-flow-tag { margin-top:10px; }
  .mk-flow-nav button { color:var(--g8); }
  .mk-flow-nav button i { opacity:1; transform:none; }
}

@media (max-width:959px) {
  .mk-flow-nav { flex-direction:row; flex-wrap:wrap; gap:12px 20px; }
  .mk-flow-notes { min-height:0; }
  .mk-flow-note { position:static; display:none; transform:none; }
  .mk-flow-note.is-active { display:block; }
}

/* ---------- Apps section ----------
   Photography-led, like every other section: the shot carries the card and the
   copy sits under it, rather than a glass box with an icon in a tile — which
   was the one module still built out of chrome instead of image. The two apps
   are peers, so they get the same size; the second is simply dropped, the way
   the reference staggers a row of equals. */
.mk-apps { display:grid; grid-template-columns:1fr; gap:34px; }
@media (min-width:720px) {
  .mk-apps { grid-template-columns:1fr 1fr; gap:46px; align-items:start; }
  .mk-app-card:nth-child(2) { margin-top:clamp(16px, 3.5vh, 44px); }
}
.mk-app-card {
  display:flex; flex-direction:column;
  transition:transform 340ms cubic-bezier(.2,.8,.2,1);
}
.mk-app-card:hover { transform:translateY(-4px); }

.mk-app-card .shot {
  position:relative; margin:0 0 18px; overflow:hidden;
  border-radius:16px; border:1px solid var(--glass-border); background:var(--g1);
  height:clamp(155px, 25vh, 300px);
}
/* The same duotone the band, the reel and the flow stage use, so a stock photo
   reads as part of this system rather than as marketing imagery. */
.mk-app-card .shot img {
  width:100%; height:100%; object-fit:cover; display:block;
  filter:grayscale(1) brightness(0.58) contrast(1.15);
  transition:transform 800ms cubic-bezier(.2,.8,.2,1), filter 600ms ease;
}
.mk-app-card:hover .shot img { transform:scale(1.045); filter:grayscale(1) brightness(0.7) contrast(1.12); }
.mk-app-card .shot::after {
  content:""; position:absolute; inset:0;
  background:linear-gradient(160deg, rgba(60,100,150,0.42), rgba(5,7,11,0.74));
  mix-blend-mode:multiply;
}
.mk-app-card .shot::before {
  content:""; position:absolute; inset:0; z-index:1;
  background:linear-gradient(200deg, rgba(134,182,232,0.2), transparent 55%);
}
/* Riding on the image at low opacity, the way the feature visuals carry theirs
   — a maker's mark, not a button. */
.mk-app-card .shot .material-symbols-outlined {
  position:absolute; top:14px; left:16px; z-index:2;
  font-size:20px !important; color:rgba(255,255,255,0.62);
}

.mk-app-card h3 { font-size:15px; font-weight:500; letter-spacing:-0.01em; margin:0 0 8px; }
.mk-app-card p { font-size:12.5px; color:var(--g5); line-height:1.62; margin:0 0 16px; max-width:42ch; }
.mk-app-card .go {
  display:inline-flex; align-items:center; gap:8px; font-size:12.5px; color:var(--g7);
  transition:color 250ms ease;
}
.mk-app-card .go i { font-style:normal; transition:transform 320ms cubic-bezier(.2,.8,.2,1); }
.mk-app-card:hover .go { color:var(--accent); }
.mk-app-card:hover .go i { transform:translateX(5px); }

/* ---------- Final CTA band ---------- */
.mk-cta { text-align:center; position:relative; overflow:hidden; }
.mk-cta::before {
  content:""; position:absolute; left:50%; top:0; width:900px; height:900px;
  transform:translate(-50%,-60%); border-radius:50%; pointer-events:none;
  background:radial-gradient(closest-side, rgba(134,182,232,0.22), transparent 70%);
}
/* Alfred closes the page the way he opened it, at a fraction of the size —
   a presence behind the ask rather than the subject of the screen. */
/* No bespoke sizing for the closing orb: it uses the very same .mk-orb-canvas
   box as the hero. That is the whole reason the hero never shows a square —
   the canvas is larger than its section, so the panel's overflow:clip cuts its
   edges off. Shrinking the canvas until it fitted was what put a visible box
   around Alfred here, and the radial mask added to hide that box is what read
   as a blue halo. The orb is made smaller by its mount scale instead. */
.mk-cta .wrap { z-index:1; }
.mk-cta h2 { font-size:clamp(1.7rem, 1.2rem + 1.6vw, 2.6rem); margin-bottom:12px; }
.mk-cta p { color:var(--g6); margin-bottom:28px; }
.mk-cta .btn { padding:12px 24px; border-radius:9px; }

/* ---------- Footer ---------- */
/* The closing screen is the CTA and the footer together, one viewport tall.
   The CTA gives up its own 100vh and takes whatever the footer leaves, so the
   footer's real height — which changes when its row wraps on a narrow window —
   never has to be guessed at. */
.mk-closing { min-height:100vh; display:flex; flex-direction:column; }
.mk-closing .mk-cta.mk-panel { min-height:0; flex:1 1 auto; }

.mk-footer { border-top:1px solid var(--glass-border); padding:36px 0; }
.mk-footer .wrap { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:16px; }
.mk-footer-links { display:flex; flex-wrap:wrap; gap:22px; font-size:12.5px; color:var(--g6); }
.mk-footer-links a { transition:color 200ms ease; }
.mk-footer-links a:hover { color:var(--g8); }
.mk-footer .copy { font-size:11.5px; color:var(--g5); }
.mk-footer .status { display:flex; align-items:center; gap:6px; font-size:11px; color:var(--g5); }
.mk-footer .status .d { width:6px; height:6px; border-radius:50%; background:var(--accent); box-shadow:0 0 6px var(--accent); }


/* ============================================================
   ALFRED — public support widget (every unauthenticated page)

   Its own chat language rather than the app shell's .a-chat/.a-msg/.a-input:
   those are tuned for a 280px dock in a dense dashboard — 12.5px type, square
   corners, a hard 1px border on every bubble — and dropped next to a hero set
   in clamp(2.4rem…4.5rem) they read as the wireframe this page grew out of.
   Here the panel is sized and lit like the rest of the marketing surface:
   soft radii, an opaque dark surface lit from within by a directional blue
   pool, 13.5px/1.62 body, and one pill for the input instead of a field plus
   a rectangular button.
   ============================================================ */
.pa-widget { position:fixed; right:24px; bottom:24px; z-index:200; }
.pa-bubble {
  position:relative; overflow:hidden;
  width:62px; height:62px; border-radius:50%; border:0; cursor:pointer;
  background:var(--g1); color:var(--g8); display:grid; place-items:center;
  /* No outline and no ring — the orb reads as light, and a stroke around it
     turned it back into a button. Depth comes from the drop shadow alone. */
  box-shadow:0 16px 40px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.04);
  transition:background 200ms ease, box-shadow 320ms ease, transform 320ms cubic-bezier(.2,.8,.2,1);
}
.pa-bubble:hover { transform:translateY(-2px); box-shadow:0 22px 54px rgba(0,0,0,0.6), 0 0 34px rgba(134,182,232,0.18), 0 0 0 1px rgba(134,182,232,0.16); }
.pa-bubble:focus-visible { outline:none; box-shadow:0 16px 40px rgba(0,0,0,0.55), 0 0 0 2px var(--accent-line); }
.pa-orb {
  position:absolute; inset:-25%; width:150%; height:150%; z-index:0;
  mix-blend-mode:screen; pointer-events:none; transition:opacity 180ms ease;
}
.pa-bubble.open .pa-orb { opacity:0; }
.pa-bubble .material-symbols-outlined {
  position:relative; z-index:1; font-size:20px !important;
  opacity:0; transform:rotate(-90deg); transition:opacity 200ms ease, transform 320ms cubic-bezier(.2,.8,.2,1);
}
.pa-bubble.open .material-symbols-outlined { opacity:1; transform:rotate(0); }
/* Micro-label to the left of the bubble. Same eyebrow treatment the page uses
   elsewhere (uppercase, wide tracking), two steps quieter — it should register
   on hover, not compete with the orb. */
.pa-hint {
  position:absolute; right:74px; bottom:0; height:62px;
  display:flex; align-items:center; pointer-events:none;
  font-size:9.5px; letter-spacing:0.24em; text-transform:uppercase;
  color:var(--g6); white-space:nowrap;
  opacity:0; transform:translateX(6px);
  transition:opacity 260ms ease, transform 260ms cubic-bezier(.2,.8,.2,1);
}
.pa-bubble:hover ~ .pa-hint,
.pa-bubble:focus-visible ~ .pa-hint { opacity:0.75; transform:translateX(0); }
/* Nothing to invite once the panel is open. */
.pa-bubble.open ~ .pa-hint { opacity:0 !important; }
@media (prefers-reduced-motion: reduce) { .pa-hint { transition:none; transform:none; } }

/* ---- panel ---- */
.pa-panel {
  position:absolute; right:0; bottom:80px;
  width:396px; max-width:calc(100vw - 48px);
  max-height:min(76vh, 620px);
  /* Fully opaque, and no backdrop-filter. Blur is not opacity: a 28px blur
     still passes the page through, so the hero copy scrolling behind the panel
     stayed legible through it — headlines running straight across the
     transcript. The panel is a surface over the page, not a window onto it, so
     it gets a flat fill one step above the page black (#05070b) — enough to
     read as lifted, without the page ever showing through. Its depth now comes
     from the shadow, the hairline and the internal glow, which is where it
     should have come from anyway. */
  background:#0b1017;
  border:1px solid rgba(255,255,255,0.09); border-radius:24px;
  /* Three layers doing three jobs: the cast shadow that lifts it off the page,
     a faint accent bloom so it belongs to the same light as the orb, and a
     top inner hairline — the highlight that gives the edge its material. */
  box-shadow:
    0 40px 100px rgba(0,0,0,0.66),
    0 0 60px rgba(134,182,232,0.07),
    inset 0 1px 0 rgba(255,255,255,0.07);
  display:flex; flex-direction:column; overflow:hidden; isolation:isolate;
  opacity:0; pointer-events:none;
  transform:translateY(16px) scale(0.97);
  transform-origin:100% 100%;
  filter:blur(6px);
  transition:opacity 320ms ease, transform 420ms cubic-bezier(.2,.8,.2,1), filter 320ms ease;
}
/* The same blur-resolve the landing sections use, so opening Alfred belongs to
   the page's motion vocabulary rather than being a widget that pops.
   `filter:none`, not `blur(0)`: a filter that merely resolves to zero still
   pins the panel in its own filter layer for as long as it is open, and Chrome
   re-rasterises that layer whenever the transcript grows or scrolls — every
   message landing softened the whole panel for a frame or two. `none` lets the
   layer go once the entrance is done, and still interpolates on the way in
   (a `none` filter animates as an equivalent list of zero-value functions). */
.pa-panel.open { opacity:1; pointer-events:auto; transform:translateY(0) scale(1); filter:none; }
@media (prefers-reduced-motion: reduce) {
  .pa-panel { transition:opacity 200ms ease; transform:none; filter:none; }
  .pa-panel.open { transform:none; }
}
/* Directional light inside the glass: a cool pool behind the header and a
   brighter one rising under the input, the way the reference lifts its
   "ask anything" pill off a gradient floor. */
.pa-glow {
  position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:
    radial-gradient(440px 240px at 86% -16%, rgba(134,182,232,0.34), transparent 72%),
    radial-gradient(620px 360px at 50% 124%, rgba(86,142,224,0.46), transparent 74%),
    linear-gradient(180deg, rgba(255,255,255,0.02), transparent 34%);
}

/* ---- header ---- */
.pa-head {
  display:flex; align-items:center; gap:12px; padding:16px 18px 14px;
  border-bottom:1px solid rgba(255,255,255,0.06); flex-shrink:0;
}
.pa-avatar {
  position:relative; width:36px; height:36px; border-radius:50%; flex-shrink:0;
  background:var(--g1); overflow:hidden;
  box-shadow:0 0 0 1px rgba(255,255,255,0.07), 0 0 18px rgba(134,182,232,0.16);
}
.pa-avatar canvas { position:absolute; inset:-28%; width:156%; height:156%; mix-blend-mode:screen; }
.pa-who { flex:1; min-width:0; }
.pa-name { font-size:14px; font-weight:500; color:var(--g8); letter-spacing:-0.01em; line-height:1.25; }
.pa-status {
  display:flex; align-items:center; gap:6px; margin-top:3px;
  font-size:9.5px; letter-spacing:0.2em; text-transform:uppercase; color:var(--g5);
}
.pa-status i {
  width:5px; height:5px; border-radius:50%; background:var(--accent);
  box-shadow:0 0 8px var(--accent); animation:pa-pulse 2.6s ease-in-out infinite;
}
@keyframes pa-pulse { 0%,100% { opacity:0.45; } 50% { opacity:1; } }
@media (prefers-reduced-motion: reduce) { .pa-status i { animation:none; } }
.pa-close {
  width:30px; height:30px; border-radius:50%; flex-shrink:0;
  background:transparent; border:1px solid transparent; color:var(--g5);
  display:grid; place-items:center; cursor:pointer; padding:0;
  transition:background 200ms ease, color 200ms ease, border-color 200ms ease;
}
.pa-close .material-symbols-outlined { font-size:17px !important; }
.pa-close:hover { background:rgba(255,255,255,0.06); border-color:rgba(255,255,255,0.08); color:var(--g8); }

/* ---- transcript ---- */
.pa-chat {
  flex:1; min-height:196px; overflow-y:auto;
  padding:22px 18px 10px; display:flex; flex-direction:column; gap:14px;
  scrollbar-width:thin; scrollbar-color:rgba(255,255,255,0.12) transparent;
}
/* Conversations grow from the floor, not the ceiling: with one message in the
   panel, top-aligning it left a third of the panel empty above it — which is
   most of what made the opened widget look unfinished. A zero-height spacer
   eats the free space and collapses to nothing once the transcript overflows,
   so (unlike justify-content:flex-end) the scrolled-off top stays reachable. */
.pa-chat::before { content:""; margin-top:auto; }
.pa-chat::-webkit-scrollbar { width:6px; }
.pa-chat::-webkit-scrollbar-thumb { background:rgba(255,255,255,0.12); border-radius:3px; }
.pa-chat::-webkit-scrollbar-track { background:transparent; }
/* The name label is gone from the visual design (see alfred-public.js), so it
   is carried here for anyone listening to the panel instead of looking at it. */
.pa-chat .sr-only {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.pa-msg { display:flex; }
.pa-msg.me { justify-content:flex-end; }
/* No avatar, no name label on every turn: at two participants the alignment
   and the fill already say who spoke, and the labels were half the reason the
   panel read as a form. */
.pa-bubble-txt {
  max-width:88%; padding:12px 15px;
  font-size:13.5px; line-height:1.62; color:var(--g7);
  border-radius:18px 18px 18px 6px;
  background:linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.025));
  border:1px solid rgba(255,255,255,0.07);
  /* `backwards`, not `both`: `both` would keep the final keyframe pinned on the
     element forever, leaving every message in the transcript in its own
     composited layer. `backwards` holds the start state until it begins and
     then hands back to the element's own computed style — which is already
     what the animation ends on. */
  animation:pa-rise 420ms cubic-bezier(.2,.8,.2,1) backwards;
}
.pa-msg.me .pa-bubble-txt {
  color:var(--g8); border-radius:18px 18px 6px 18px;
  background:linear-gradient(180deg, rgba(134,182,232,0.20), rgba(134,182,232,0.10));
  border-color:var(--accent-line);
}
/* Alfred's opening line is the panel's headline, not a chat turn — set like
   the hero it answers to (the reference's "How can I help you today?"), so the
   first thing you see at 396px wide is display type rather than a 13px bubble
   floating in an empty box. It becomes an ordinary bubble the moment the
   conversation has anything else in it. */
.pa-msg--intro .pa-bubble-txt {
  max-width:100%; padding:2px 2px 6px; border:0; background:none; border-radius:0;
  font-size:19px; line-height:1.42; letter-spacing:-0.015em; font-weight:300; color:var(--g7);
}
.pa-msg--intro .pa-bubble-txt strong { font-weight:400; color:var(--g8); }
/* Once there's a real exchange the headline steps back to a caption so the
   answers carry the panel. Deliberately not transitioned: font-size is not a
   compositable property, so tweening it relaid out the whole column on every
   frame — for 320ms the incoming bubbles were being moved by layout at the
   same time as their own entrance transform, and the two fought. It switches
   in one frame instead, before anything is appended, and because the
   transcript is anchored to the floor the change only shortens the block from
   the top: nothing below the intro moves at all. */
.pa-chat.has-reply .pa-msg--intro .pa-bubble-txt {
  font-size:13px; line-height:1.6; color:var(--g5); letter-spacing:0;
}
.pa-bubble-txt a { color:var(--accent); text-decoration:none; border-bottom:1px solid var(--accent-line); transition:border-color 180ms ease; }
.pa-bubble-txt a:hover { border-bottom-color:var(--accent); }
/* Opacity and transform only — deliberately no blur on the way in, unlike the
   panel's own entrance. Both of these run on the compositor and a blur does
   not: it is a filter, so every arriving message would put its bubble in a
   layer of its own to be re-rasterised. At 5px over 420ms on a bubble this
   size it was worth almost nothing visually, and without it the entrance is
   free on whatever hardware the visitor brought. (The panel's own open
   animation keeps its blur-resolve: that happens once, not per message.) */
@keyframes pa-rise {
  from { opacity:0; transform:translateY(8px); }
  to   { opacity:1; transform:translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .pa-bubble-txt { animation:none; } }

/* ---- suggestions ---- */
.pa-suggest { padding:8px 18px 12px; flex-shrink:0; }
.pa-suggest-label {
  font-size:9px; letter-spacing:0.24em; text-transform:uppercase; color:var(--g5);
  margin-bottom:9px;
}
.pa-chips { display:flex; flex-wrap:wrap; gap:7px; }
.pa-chip {
  border:1px solid rgba(255,255,255,0.09); border-radius:999px;
  background:rgba(255,255,255,0.035); padding:7px 14px;
  font-family:inherit; font-size:11.5px; letter-spacing:0.01em; color:var(--g6); cursor:pointer;
  transition:border-color 200ms ease, color 200ms ease, background 200ms ease, transform 260ms cubic-bezier(.2,.8,.2,1), box-shadow 260ms ease;
}
.pa-chip:hover {
  border-color:var(--accent-line); color:var(--g8); background:rgba(134,182,232,0.10);
  transform:translateY(-1px); box-shadow:0 6px 18px rgba(0,0,0,0.35);
}
.pa-chip:focus-visible { outline:none; border-color:var(--accent); color:var(--g8); }
/* The press itself, and then a single accent flush as the question leaves for
   the transcript — the chip hands the question over rather than just sitting
   there while a bubble appears elsewhere in the panel. */
.pa-chip:active { transform:translateY(0) scale(0.95); transition-duration:90ms; }
.pa-chip.sent { animation:pa-chip-send 460ms cubic-bezier(.2,.8,.2,1); }
@keyframes pa-chip-send {
  0%   { transform:scale(0.95); background:rgba(134,182,232,0.26); border-color:var(--accent); color:var(--g8); }
  45%  { transform:scale(1.02); background:rgba(134,182,232,0.14); border-color:var(--accent-line); color:var(--g8); }
  100% { transform:scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .pa-chip, .pa-chip:active { transition:none; transform:none; }
  .pa-chip.sent { animation:none; }
}

/* ---- input ---- */
.pa-input { padding:0 14px 14px; flex-shrink:0; }
/* One pill, button inside it — the reference's "Ask anything…" bar. A bordered
   rectangle beside a rectangular Send button was the last piece of the
   dashboard's form language left in here. */
.pa-field {
  display:flex; align-items:center; gap:8px;
  padding:6px 6px 6px 18px; border-radius:999px;
  background:rgba(255,255,255,0.05); border:1px solid rgba(255,255,255,0.10);
  transition:border-color 220ms ease, background 220ms ease, box-shadow 220ms ease;
}
.pa-field:focus-within {
  border-color:var(--accent-line); background:rgba(255,255,255,0.07);
  box-shadow:0 0 0 4px rgba(134,182,232,0.09);
}
.pa-input input {
  flex:1; min-width:0; border:0; background:transparent; padding:9px 0;
  font-family:inherit; font-size:13.5px; color:var(--g7); outline:none;
}
.pa-input input::placeholder { color:var(--g5); }
.pa-send {
  width:36px; height:36px; border-radius:50%; flex-shrink:0; border:0; padding:0;
  background:var(--g8); color:var(--g0); display:grid; place-items:center; cursor:pointer;
  transition:transform 260ms cubic-bezier(.2,.8,.2,1), box-shadow 260ms ease, background 200ms ease;
}
.pa-send .material-symbols-outlined { font-size:19px !important; }
.pa-send:hover { transform:translateY(-1px); box-shadow:0 8px 22px rgba(134,182,232,0.28); }
.pa-send:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

@media (max-width:480px) {
  .pa-widget { right:16px; bottom:16px; }
  .pa-hint { display:none; }
  .pa-panel { width:calc(100vw - 32px); bottom:76px; max-height:min(72vh, 560px); }
}

/* ============================================================
   SIGNUP WIZARD (/register)
   ============================================================ */
.wz-shell { min-height:100vh; display:flex; flex-direction:column; background:var(--g0); }
/* Header, progress bar and action bar all ride the same column the content
   does — 640px of measure inside 24px gutters — so the step counter, the
   track and the buttons line up on one left and one right edge. None of them
   carry a rule or a panel fill: on a screen with a single task, every band of
   chrome reads as one more division to cross. */
.wz-header {
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  width:100%; max-width:688px; margin:0 auto; padding:22px 24px 0;
}
.wz-back { display:flex; align-items:center; gap:6px; color:var(--g6); font-size:12.5px; transition:color 180ms ease; }
.wz-back:hover { color:var(--g8); }
.wz-back .material-symbols-outlined { font-size:16px !important; }
/* The wizard's mark, centred between the back link and the step counter.
   Off past 940px — the brand panel carries the logo there. */
.wz-back-logo { height:13.5px; width:auto; margin:0 auto; filter:drop-shadow(0 0 10px rgba(134,182,232,0.35)); }
.wz-step-count { font-size:11.5px; color:var(--g5); }

.wz-progress { padding:16px 24px 0; max-width:688px; margin:0 auto; width:100%; }
.wz-track { height:3px; background:var(--g3); border-radius:2px; position:relative; overflow:hidden; }
.wz-fill { height:100%; background:linear-gradient(90deg, var(--accent), var(--accent-2)); box-shadow:0 0 12px var(--accent); transition:width 500ms cubic-bezier(.4,0,.2,1); }

.wz-body { flex:1; display:flex; justify-content:center; padding:30px 24px 140px; }
.wz-inner { width:100%; max-width:640px; }
.wz-step { display:none; }
.wz-step.active { display:block; animation:wz-in 520ms cubic-bezier(.16,1,.3,1); }
@keyframes wz-in { from { opacity:0; transform:translateY(14px); } to { opacity:1; transform:translateY(0); } }
.wz-step-head { margin-bottom:26px; }
.wz-step-head .eyebrow { margin-bottom:14px; }
.wz-step-head h1 { font-size:clamp(1.5rem, 1.1rem + 1.2vw, 2rem); letter-spacing:-0.02em; margin-bottom:10px; }
.wz-step-head p { font-size:13.5px; color:var(--g6); line-height:1.6; }

.wz-tiles { display:grid; grid-template-columns:1fr; gap:10px; }
@media (min-width:520px) { .wz-tiles { grid-template-columns:1fr 1fr; } }
.wz-tile {
  display:flex; align-items:center; gap:14px; text-align:left;
  border:1px solid var(--glass-border); border-radius:12px; background:var(--g1); padding:13px; cursor:pointer;
  position:relative; transition:border-color 200ms ease, background 200ms ease;
}
.wz-tile:hover { border-color:var(--accent-line); }
/* A photograph of the operation, not a pictogram of it — 72px is the size at
   which a tarmac still reads as a tarmac. Unpicked tiles hold their photo back
   (muted and dimmed) so the four squares don't shout over the question; the
   one you're on, or hovering, comes up to full colour. Files and credits live
   in src/public/img/register/. */
.wz-tile .ph {
  width:72px; height:72px; flex-shrink:0; border-radius:10px;
  object-fit:cover; display:block; background:var(--g2);
  border:1px solid var(--glass-border);
  filter:saturate(0.7) brightness(0.78);
  transition:filter 300ms ease, border-color 200ms ease;
}
.wz-tile:hover .ph { filter:saturate(0.95) brightness(0.95); }
/* The check circle owns the top-right corner — keep the copy out from under it. */
.wz-tile .txt { min-width:0; padding-right:16px; }
.wz-tile .tt { font-size:13.5px; font-weight:500; color:var(--g8); margin-bottom:3px; }
.wz-tile .ds { font-size:11.5px; color:var(--g5); line-height:1.45; }
.wz-tile.selected { border-color:var(--accent-line); background:var(--accent-dim); box-shadow:0 0 0 1px var(--accent-line); }
.wz-tile.selected .ph { filter:none; border-color:var(--accent-line); }
.wz-tile-check {
  position:absolute; top:12px; right:12px; width:18px; height:18px; border-radius:50%;
  border:1px solid var(--glass-border); display:grid; place-items:center;
}
.wz-tile.selected .wz-tile-check { background:var(--accent); border-color:var(--accent); }
.wz-tile.selected .wz-tile-check::after {
  content:""; width:8px; height:8px;
  background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2305070b' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E") center/contain no-repeat;
}

.wz-plans { display:grid; grid-template-columns:1fr; gap:12px; }
@media (min-width:640px) { .wz-plans { grid-template-columns:repeat(3, 1fr); } }
.wz-plan {
  border:1px solid var(--glass-border); border-radius:14px; background:var(--g1); padding:20px; cursor:pointer; position:relative;
  display:flex; flex-direction:column; gap:10px; transition:border-color 200ms ease, transform 200ms ease;
}
.wz-plan:hover { transform:translateY(-2px); }
.wz-plan.selected { border-color:var(--accent-line); box-shadow:0 0 0 1px var(--accent-line), 0 12px 30px rgba(134,182,232,0.14); }
.wz-plan .nm { font-size:11px; font-weight:500; letter-spacing:0.1em; text-transform:uppercase; color:var(--g5); }
.wz-plan .price { font-size:24px; font-weight:500; color:var(--g8); font-family:var(--font-display); }
.wz-plan .price small { font-size:12px; font-weight:400; color:var(--g5); font-family:var(--font-body); }
.wz-plan .ds { font-size:12px; color:var(--g6); line-height:1.5; }
.wz-plan .feats { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
.wz-plan .feats li { font-size:12px; color:var(--g6); display:flex; gap:6px; align-items:flex-start; }
.wz-plan .feats .material-symbols-outlined { font-size:14px !important; color:var(--accent); margin-top:1px; }
.wz-plan-badge {
  position:absolute; top:-9px; left:16px; background:var(--accent); color:var(--g0);
  font-size:10px; font-weight:600; letter-spacing:0.06em; text-transform:uppercase; padding:3px 9px; border-radius:20px;
}

.wz-field { margin-bottom:16px; }
.wz-field label { display:block; font-size:11.5px; color:var(--g6); margin-bottom:5px; }
.wz-field .hint { font-size:11px; color:var(--g5); margin-top:5px; }
/* The wizard's inputs are the same control /login uses — 13px value text, a
   --g5 placeholder so the prompt sits on the palette instead of on whatever
   grey the browser picks, and the same hover/focus response. They had drifted
   a point larger with no placeholder rule at all, which is what made this step
   read coarser than the rest of signup. */
.wz-field input {
  width:100%; border:1px solid var(--glass-border); border-radius:9px;
  padding:11px 12px; background:var(--g1); color:var(--g7); font-size:13px; line-height:1.4;
  transition:border-color 180ms ease, box-shadow 180ms ease, background 180ms ease;
}
.wz-field input::placeholder { color:var(--g5); }
.wz-field input:hover { border-color:var(--g4); }
.wz-field input:focus {
  outline:none; border-color:var(--accent-line); background:var(--g2);
  box-shadow:0 0 0 3px rgba(134,182,232,0.10);
}
/* The first field of the step gets a little more presence — a step up, not a
   headline. */
.wz-field input.hero { font-size:15px; font-weight:500; padding:12px 13px; }
.wz-row2 { display:grid; grid-template-columns:1fr; gap:14px; }
@media (min-width:520px) { .wz-row2 { grid-template-columns:1fr 1fr; } }

/* Phone — country-code (flag + dial code) picker + local number */
.wz-field-phone { position:relative; }
.wz-phone { display:flex; border:1px solid var(--glass-border); border-radius:9px; background:var(--g1); overflow:hidden; }
.wz-phone-cc {
  display:flex; align-items:center; gap:6px; flex-shrink:0;
  padding:0 10px; border:none; border-right:1px solid var(--glass-border);
  background:var(--g2); color:var(--g7); font-size:12.5px; cursor:pointer;
}
.wz-phone-cc:hover { background:var(--g3); }
.wz-phone-cc .flag { font-size:14px; line-height:1; }
.wz-phone-cc .material-symbols-outlined { font-size:16px !important; color:var(--g5); }
.wz-phone input {
  flex:1; min-width:0; border:none; outline:none; background:transparent;
  padding:11px 12px; font-size:13px; line-height:1.4; color:var(--g7);
}
.wz-phone input::placeholder { color:var(--g5); }
.wz-phone:focus-within { border-color:var(--accent-line); box-shadow:0 0 0 3px rgba(134,182,232,0.10); }
.wz-phone-menu {
  position:absolute; top:100%; left:0; margin-top:4px; z-index:30;
  width:min(320px, 100%); border-radius:12px; overflow:hidden;
  background:var(--glass-bg-strong); border:1px solid var(--glass-border); backdrop-filter:blur(18px);
  box-shadow:0 20px 44px rgba(0,0,0,0.5);
}
.wz-phone-search {
  display:block; width:100%; box-sizing:border-box;
  border:none; border-bottom:1px solid var(--glass-border); outline:none; padding:10px 12px;
  font-size:12.5px; background:transparent; color:var(--g7);
}
.wz-phone-list { max-height:230px; overflow-y:auto; }
.wz-phone-item {
  display:flex; align-items:center; gap:8px; width:100%; text-align:left;
  border:none; background:transparent; padding:8px 12px; font-size:12.5px; color:var(--g7); cursor:pointer;
}
.wz-phone-item:hover { background:rgba(255,255,255,0.05); }
.wz-phone-item .flag { font-size:15px; }
.wz-phone-item .dial { margin-left:auto; color:var(--g5); font-variant-numeric:tabular-nums; }
.wz-phone-empty { padding:14px 12px; font-size:12px; color:var(--g5); }

.wz-summary { border:1px solid var(--glass-border); border-radius:12px; background:var(--g1); overflow:hidden; }
.wz-summary .row-inline { padding:12px 16px; }
.wz-terms { display:flex; align-items:flex-start; gap:10px; font-size:12.5px; color:var(--g6); margin-top:18px; flex-wrap:wrap; }
.wz-terms input { margin-top:2px; accent-color:var(--accent); }

/* ---- invalid fields — /login and /register alike ----
   The browser's own validation bubble is an OS control: white card, orange
   badge, its own type, and no CSS reaches any of it. Both forms carry
   `novalidate` and /js/form-validate.js reports failures here instead — the
   field goes red and says what is wrong underneath, in the type the rest of
   the screen uses. Same look wherever a field lives, so signing in and signing
   up fail the same way. */
.au-field.invalid input,
.wz-field.invalid input,
.wz-field.invalid .wz-phone { border-color:rgba(255,138,118,0.55); }
.au-field.invalid input:focus,
.wz-field.invalid input:focus,
.wz-field.invalid .wz-phone:focus-within {
  border-color:var(--danger); box-shadow:0 0 0 3px rgba(255,138,118,0.12);
}
/* The leading icon goes with it — it is the field's own mark. */
.au-field.invalid .fi { color:var(--danger); }
.fld-msg {
  display:flex; align-items:flex-start; gap:5px; width:100%;
  margin-top:6px; font-size:11px; line-height:1.45; color:var(--danger);
}
.fld-msg::before {
  content:"error"; font-family:'Material Symbols Outlined'; font-size:13px;
  line-height:1.25; flex-shrink:0;
}
/* A live hint switches to the same red when it is a problem rather than a
   note (the wizard's password-match check). */
.au-field .hint.bad, .wz-field .hint.bad { color:var(--danger); }
.wz-terms.invalid { color:var(--danger); }
.wz-terms.invalid input { accent-color:var(--danger); outline:1px solid rgba(255,138,118,0.55); outline-offset:2px; }

/* On a phone this is an overlay the content scrolls under, so it keeps its
   blur and a hairline to sit on. Past 940px it stops floating and the rule
   goes with it — see .au-split-wide. */
.wz-footer {
  position:fixed; left:0; right:0; bottom:0; z-index:20;
  background:var(--glass-bg-strong); backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px);
  border-top:1px solid var(--glass-border); padding:14px 24px;
  display:flex; justify-content:center;
}
.wz-footer-inner { width:100%; max-width:640px; display:flex; justify-content:space-between; gap:10px; }
.wz-err {
  display:flex; align-items:flex-start; gap:8px; margin-bottom:22px;
  font-size:12.5px; line-height:1.5; color:var(--danger);
  background:rgba(255,138,118,0.08); border:1px solid rgba(255,138,118,0.25);
  border-radius:9px; padding:11px 13px;
}
.wz-err::before {
  content:"error"; font-family:'Material Symbols Outlined'; font-size:16px;
  line-height:1.2; flex-shrink:0;
}


/* ============================================================
   AUTH SPLIT SHELL — /login and /choose-role
   Structure lifted from the prototype's client-creation signup shell
   (brand panel left, form panel right, form header on top of the right
   column), rendered in this file's dark "precision instrument" language
   instead of the prototype's light navy/orange one. Nothing here changes
   what the forms submit — it is layout and skin only.
   ============================================================ */
.au-split { position:relative; min-height:100vh; display:grid; grid-template-columns:1fr; }

/* ---- ambient light ----
   body.mk's page glow is aimed at a long scrolling landing and barely
   registers on a single screen that is mostly empty panel, which is why these
   read as flat black. Two fixed layers behind both columns: cool halos bleeding
   in from the corners, and a fine grain over them so the gradients have a
   surface instead of banding across a 700px-wide empty panel. */
.au-split::before, .au-split::after {
  content:""; position:fixed; inset:0; z-index:-1; pointer-events:none;
}
.au-split::before {
  background:
    /* Corner halos. These are deliberately faint: at roughly double these
       values the four of them met in the middle and the whole screen read as
       gradient rather than as a dark room with light coming in at the edges.
       They should be felt at the corners and unnoticed anywhere else. */
    radial-gradient(760px 520px at 2% -4%,   rgba(134,182,232,0.15), transparent 60%),
    radial-gradient(640px 500px at 99% 103%, rgba(134,182,232,0.09), transparent 56%),
    radial-gradient(900px 640px at 103% -8%, rgba(92,132,188,0.09), transparent 58%),
    radial-gradient(720px 540px at -6% 105%, rgba(92,132,188,0.06), transparent 56%),
    /* and a very low wash so the middle isn't flat black between them — deep
       navy, not a fifth gradient competing with the four above */
    radial-gradient(1500px 1000px at 50% 50%, rgba(70,104,156,0.045), transparent 72%);
}
.au-split::after {
  opacity:0.045;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* The seam between the columns catches the light instead of being a flat
   hairline the full height of the screen. */
@media (min-width:940px) {
  .au-brand {
    border-right:0;
    box-shadow:1px 0 0 rgba(255,255,255,0.05), 24px 0 60px -40px rgba(134,182,232,0.18);
  }
}
/* One column until there is genuinely room for two: below this the brand
   panel would squeeze the form rather than frame it, so it steps aside and
   the right column carries a compact logo of its own. */
@media (min-width:940px) { .au-split { grid-template-columns:1.02fr 1fr; } }

/* ---- left: brand panel ---- */
.au-brand {
  display:none; position:relative; overflow:hidden; isolation:isolate;
  flex-direction:column; justify-content:space-between; gap:32px;
  padding:40px 44px; border-right:1px solid var(--glass-border);
}
@media (min-width:940px) {
  /* Sticky, not just tall: /choose-role can run well past one screenful with a
     full roster, and a brand panel that scrolls away leaves the roster
     floating against nothing. The signup shell overrides this — there each
     column is its own scroller (see .au-split-wide). */
  .au-brand { display:flex; position:sticky; top:0; height:100vh; }
}
/* The logo sits with the copy, not in the corner — it takes the eyebrow's
   place above the headline, at the landing's own .mk-logo size. */
.au-brand-logo { display:block; margin-bottom:22px; }
.au-brand-hero { max-width:430px; margin:auto 0; }
.au-brand-hero .eyebrow { margin-bottom:18px; }
.au-brand-hero h2 {
  font-family:var(--font-display); font-weight:400;
  font-size:clamp(1.45rem, 1.05rem + 1.1vw, 2.05rem); line-height:1.2;
  letter-spacing:-0.022em; color:var(--g8); margin-bottom:22px;
}
.au-brand-hero h2 .accent { color:var(--accent); }
.au-brand-foot { font-size:11px; color:var(--g5); }
/* Every "go back" on an auth screen ends up in the same place: top-left of the
   brand panel on desktop, top-left of the form column on a phone. See
   .au-back / .wz-back below — one element, repositioned. */

/* ---- right: form panel ---- */
.au-panel { display:flex; flex-direction:column; min-height:100vh; }
.au-panel-top {
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:20px 24px; min-height:62px;
}
.au-back { display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--g5); transition:color 180ms ease; }
.au-back:hover { color:var(--g8); }
.au-back .material-symbols-outlined { font-size:16px !important; }
@media (min-width:940px) {
  .au-split .au-back { position:absolute; top:40px; left:44px; z-index:5; }
}
.au-panel-top .au-mini-logo { height:13px; width:auto; opacity:0.9; }
@media (min-width:940px) { .au-panel-top .au-mini-logo { display:none; } }
.au-panel-body { flex:1; display:flex; align-items:center; justify-content:center; padding:12px 24px 48px; }
.au-panel-inner { width:100%; max-width:392px; animation:wz-in 520ms cubic-bezier(.16,1,.3,1); }

.au-head { margin-bottom:24px; }
.au-head .eyebrow { margin-bottom:14px; }
.au-head h1 {
  font-family:var(--font-display); font-weight:500; color:var(--g8);
  font-size:clamp(1.35rem, 1.1rem + 0.7vw, 1.75rem); letter-spacing:-0.022em; margin-bottom:8px;
}
.au-head p { font-size:13px; color:var(--g6); line-height:1.6; }

/* ---- fields ----
   Same metrics as the wizard's .wz-field so a visitor moving from /login to
   /register never sees the input style change under them. */
.au-field { margin-bottom:14px; position:relative; }
.au-field label { display:block; font-size:11.5px; color:var(--g6); margin-bottom:6px; }
.au-field input {
  width:100%; border:1px solid var(--glass-border); border-radius:9px;
  padding:11px 12px; background:var(--g1); color:var(--g7); font-size:13px;
  transition:border-color 180ms ease, box-shadow 180ms ease, background 180ms ease;
}
.au-field input::placeholder { color:var(--g5); }
.au-field input:hover { border-color:var(--g4); }
.au-field input:focus {
  outline:none; border-color:var(--accent-line); background:var(--g2);
  box-shadow:0 0 0 3px rgba(134,182,232,0.10);
}
/* Leading icon, the prototype's .input-with-icon — it makes a stack of
   identical inputs scannable at a glance. */
.au-field.with-icon input { padding-left:38px; }
.in-wrap { position:relative; display:block; }
.au-field .fi {
  position:absolute; left:12px; top:50%; transform:translateY(-50%);
  font-size:17px !important; color:var(--g5); pointer-events:none; transition:color 180ms ease;
}
.au-field input:focus ~ .fi { color:var(--accent); }
.au-field .hint { font-size:11px; color:var(--g5); margin-top:6px; }
/* Browser autofill — Chrome/Safari paint their own pale background and black
   text over a filled field, which on this dark shell reads as a white slab the
   moment credentials land. The background can't be overridden directly, so it
   gets covered by an inset shadow the size of the field, and the glyphs are
   repainted with -webkit-text-fill-color (autofill ignores `color`). The
   transition delay keeps the pale flash from showing on the way in. */
.au-field input:-webkit-autofill,
.au-field input:-webkit-autofill:hover,
.au-field input:-webkit-autofill:focus,
.wz-field input:-webkit-autofill,
.wz-field input:-webkit-autofill:hover,
.wz-field input:-webkit-autofill:focus {
  -webkit-text-fill-color:var(--g7);
  -webkit-box-shadow:0 0 0 1000px var(--g1) inset;
  box-shadow:0 0 0 1000px var(--g1) inset;
  caret-color:var(--g7);
  transition:background-color 600000s 0s, color 600000s 0s;
}
.au-field input:-webkit-autofill:focus,
.wz-field input:-webkit-autofill:focus {
  -webkit-box-shadow:0 0 0 1000px var(--g2) inset, 0 0 0 3px rgba(134,182,232,0.10);
  box-shadow:0 0 0 1000px var(--g2) inset, 0 0 0 3px rgba(134,182,232,0.10);
}


.au-submit { width:100%; margin-top:8px; justify-content:center; }
.au-err {
  display:flex; align-items:flex-start; gap:8px; margin-bottom:16px;
  font-size:12px; line-height:1.5; color:var(--danger);
  background:rgba(255,138,118,0.08); border:1px solid rgba(255,138,118,0.25);
  border-radius:9px; padding:10px 12px;
}
.au-err .material-symbols-outlined { font-size:16px !important; flex-shrink:0; }

/* Demo credentials — a callout, not a footnote: on a demo build this is the
   single most useful thing on the page, and as grey small print it was being
   read as legal boilerplate and skipped. */
.au-demo {
  margin-top:20px; border:1px dashed var(--glass-border); border-radius:10px;
  padding:12px 13px; background:var(--glass-bg); font-size:11.5px; color:var(--g6); line-height:1.7;
}
.au-demo .t {
  display:block; font-size:10px; letter-spacing:0.16em; text-transform:uppercase;
  color:var(--g5); margin-bottom:6px;
}
.au-demo b { color:var(--g8); font-weight:500; }
.au-alt { margin-top:18px; font-size:12px; color:var(--g5); text-align:center; }
.au-alt a { color:var(--accent); }

/* ---- choose-role cards ----
   A full roster is 8+ people (staff, drivers, inspector, client logins), which
   as one stacked column ran past the fold and turned a *pick one* screen into a
   scroll. It flows into as many columns as the panel can hold instead, so the
   whole roster is visible at once and the choice is a glance, not a hunt. */
.au-roles {
  display:grid; gap:9px;
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
}
.au-role {
  width:100%; min-width:0; text-align:left; cursor:pointer;
  display:flex; align-items:center; gap:11px;
  background:var(--glass-bg); border:1px solid var(--glass-border); border-radius:12px;
  padding:11px 12px; transition:border-color 200ms ease, background 200ms ease, transform 200ms ease;
}
.au-role:hover { border-color:var(--accent-line); background:var(--accent-dim); transform:translateY(-1px); }
.au-role .mark {
  width:34px; height:34px; flex-shrink:0; border-radius:10px; display:grid; place-items:center;
  background:var(--accent-dim); color:var(--accent); border:1px solid var(--accent-line);
  font-size:11.5px; font-weight:500; letter-spacing:0.02em;
}
/* Name first, role as the quiet line under it: in a narrow column the name is
   what's being chosen, so it leads. Everything truncates rather than wrapping —
   a two-line email would make neighbouring cards different heights. */
.au-role > div:nth-child(2) { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; }
.au-role .nm,
.au-role .rl,
.au-role .em { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:block; }
.au-role .nm { font-size:13px; font-weight:500; color:var(--g8); margin-bottom:2px; order:-1; }
.au-role .rl { font-size:9.5px; letter-spacing:0.12em; text-transform:uppercase; color:var(--g5); margin-bottom:0; }
.au-role .em { font-size:11px; color:var(--g6); margin-top:1px; }
.au-role .go { flex-shrink:0; color:var(--g4); transition:color 200ms ease, transform 200ms ease; }
.au-role:hover .go { color:var(--accent); transform:translateX(2px); }
.au-role .go .material-symbols-outlined { font-size:17px !important; }
.au-empty {
  padding:22px; background:var(--glass-bg); border:1px solid var(--glass-border);
  border-radius:12px; color:var(--g5); font-size:12.5px; line-height:1.6;
}
@media (prefers-reduced-motion: reduce) {
  .au-panel-inner { animation:none; }
  .au-role:hover { transform:none; }
}

/* ---- the signup wizard inside the split shell ----
   The wizard markup, IDs and script are untouched; it simply becomes the
   right-hand column. Mirrors the prototype's client-creation behaviour: past
   the breakpoint each column scrolls on its own and the action bar stops
   being viewport-fixed (it would otherwise run under the brand panel) and
   sits in flow at the bottom of the form column instead. */
.au-split-wide { grid-template-columns:1fr; }
@media (min-width:940px) {
  .au-split-wide { grid-template-columns:minmax(360px, 440px) 1fr; }
  .au-split-wide .au-brand-hero h2 { font-size:clamp(1.3rem, 0.95rem + 0.9vw, 1.7rem); }
  /* The wizard's own back link is the one that travels here, so its header is
     left with just the step counter — space-between would swing that to the
     left edge. The lockup drops its logo (the brand panel already shows one)
     and picks up the label the other auth screens use. */
  .au-split-wide .wz-header { justify-content:flex-end; }
  .au-split-wide .wz-back-logo { display:none; }
  /* Two panes, two scrollbars — the brand panel stays put while the form moves. */
  .au-split-wide, .au-split-wide > .wz-shell { height:100vh; overflow:hidden; }
  .au-split-wide > .wz-shell { min-height:0; }
  .au-split-wide .wz-body { overflow-y:auto; padding-bottom:32px; }
  /* In place, not pinned: nothing scrolls under it, so it needs neither the
     blur nor the rule — just the same gutters the form above it uses. */
  .au-split-wide .wz-footer {
    position:relative; left:auto; right:auto; bottom:auto;
    flex-shrink:0; backdrop-filter:none; -webkit-backdrop-filter:none;
    background:transparent; border-top:0; padding:6px 24px 30px;
  }
}

/* ---- password visibility toggle ----
   A type swap on the input, nothing else: the field keeps its name, its value
   and its place in the form, so what gets submitted is identical either way. */
.pw-toggle {
  position:absolute; right:6px; top:50%; transform:translateY(-50%); z-index:2;
  display:grid; place-items:center; width:30px; height:30px;
  border:0; border-radius:7px; background:transparent; color:var(--g5);
  cursor:pointer; transition:color 160ms ease, background 160ms ease;
}
.pw-toggle:hover { color:var(--g7); background:rgba(255,255,255,0.05); }
.pw-toggle:focus-visible { outline:1px solid var(--accent-line); outline-offset:1px; color:var(--g7); }
.pw-toggle .material-symbols-outlined { font-size:18px !important; }
/* Room for the button so a long value never slides under it. */
.in-wrap.has-toggle input { padding-right:42px; }

/* ---- vertical stepper in the signup brand panel ----
   The right column already has the progress bar; this gives the five steps
   names, so "Step 3 of 5" means something before you get there. Display only —
   it reflects the wizard, it doesn't drive it. */
.au-steps { list-style:none; margin:26px 0 0; padding:0; display:flex; flex-direction:column; gap:0; }
.au-steps li {
  position:relative; display:flex; align-items:center; gap:12px;
  padding:9px 0; font-size:12.5px; color:var(--g5);
  transition:color 260ms ease;
}
/* The rail, drawn per item so it stops cleanly at the last one. */
.au-steps li:not(:last-child)::before {
  content:""; position:absolute; left:8px; top:26px; bottom:-4px;
  width:1px; background:var(--g3); transition:background 300ms ease;
}
.au-steps li.done::before { background:var(--accent-line); }
.au-steps .d {
  position:relative; z-index:1; flex-shrink:0; width:17px; height:17px; border-radius:50%;
  border:1px solid var(--g4); background:var(--g0);
  display:grid; place-items:center; font-size:9.5px; color:var(--g5);
  transition:border-color 260ms ease, background 260ms ease, color 260ms ease, box-shadow 260ms ease;
}
.au-steps li.current { color:var(--g8); }
.au-steps li.current .d {
  border-color:var(--accent); color:var(--g0); background:var(--accent);
  box-shadow:0 0 0 4px var(--accent-dim);
}
.au-steps li.done { color:var(--g6); }
.au-steps li.done .d { border-color:var(--accent-line); background:var(--accent-dim); color:var(--accent); }
.au-steps li.done .d::after {
  content:"check"; font-family:'Material Symbols Outlined'; font-size:11px; line-height:1;
}
.au-steps li.done .n { display:none; }
@media (prefers-reduced-motion: reduce) { .au-steps li, .au-steps .d, .au-steps li::before { transition:none; } }
