/* ===========================================================================
   RunArcOS landing page. LIVE STYLESHEET, since 2026-09-18.
   The review copies under /koyeb/ carry their own version of this file plus the
   preview picker; this one is the production cut.
   Structure, spacing, component shapes and colour discipline ripped from
   koyeb.com (see DESIGN-NOTES.md). Identity is ArcOS: Spectral display serif,
   Inter Tight body, JetBrains Mono labels, mint #5cfa9d as the only accent.

   Mobile first: base styles are the 375px view, media queries add width.
   Self-contained on purpose. This file never touches ../assets/lp.css, and
   the page reuses ../assets/lp.js and ../assets/ads/ rather than copying them.

   Koyeb elements implemented here, in order of how much they carry the look:
     1. alternating bone-light / near-black full-bleed bands
     2. hairline engineering grid + noise grain on every band (inline, no fetch)
     3. huge tight-leading ALL CAPS headings (Spectral, not Anton)
     4. uppercase monospace for every small label
     5. mono badge pills, corner crop marks, the bottom fade to paper
     6. translucent cards, alpha hairline borders, backdrop blur, radius 8/16
     7. the wireframe globe behind the hero and the closing band
   NOT taken: Koyeb's mono keycap buttons. The live page's green pill button is
   the ArcOS button and it is used here unchanged (section 3).
   =========================================================================== */

*{margin:0;padding:0;box-sizing:border-box}

:root{
  /* ── ArcOS identity (arcos/branding/system/tokens.css) ── */
  --paper:#faf6e9;             /* the warmer beige, variant 03 of backgrounds.html */
  --paper-soft:#f2ecdb; --white:#fff;
  --ink:#0a0a0a; --ink-soft:#2a2a2a; --muted:#5d615c;
  --dark:#141312;              /* the band colour, warmed to sit on paper */
  --dark-soft:#1e1c1a;
  --card:rgba(255,255,255,.94);
  --field:#fff; --field-ink:#0a0a0a;
  --orbit-line:150,143,124;    /* light warm grey, rgb triplet for the canvas */
  --mint:#5cfa9d; --mint-soft:#7dfbb1; --mint-deep:#1fb866; --mint-ink:#0a4023;
  /* Ink that sits ON mint. It is the same on both themes BY DEFINITION: the
     mint never changes, so anything printed on it must not follow --ink. In
     dark theme --ink is pale, and the green button measured 1.21:1 before this
     existed, which is unreadable. Every mint surface uses this token. */
  --on-mint:#0a0a0a;
  /* Text that sits ON the dark band or the dark badge. Constant on both themes
     for the same reason as --on-mint: --dark barely moves between themes, so
     anything printed on it must not follow --ink or --paper. In dark theme
     --paper is #1a1918, so every `color:var(--paper)` on a dark surface was
     rendering dark grey on near black: the badge pills, the proof band, the
     guarantee block and the footer all went invisible. */
  --on-dark:#f6f3ec;

  /* ── Koyeb's alpha-border discipline: never a solid grey rule ── */
  --line:rgba(10,10,10,.17);
  --line-soft:rgba(10,10,10,.09);
  --line-dark:rgba(247,245,238,.13);
  --line-dark-soft:rgba(247,245,238,.07);
  --fill-card:rgba(255,255,255,.55);
  --fill-card-dark:rgba(247,245,238,.06);

  --font-display:'Spectral',Georgia,serif;
  --font-ui:'Inter Tight',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,monospace;

  /* Koyeb's 4px lattice */
  --gutter:16px;
  --max:1180px;

  --ease:cubic-bezier(.4,0,.2,1);
  --grain-opacity:.30;
  --tile:64px;

  /* ── background system, overridden per bg-* body class (see section 1b) ── */
  --grain-opacity:.05;
  --grain-scale:256px;
  --grain-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='256' height='256'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.35' numOctaves='3' stitchTiles='stitch' seed='0'/%3E%3C/filter%3E%3Cmask id='m'%3E%3Crect width='100%25' height='100%25' fill='%23fff' filter='url(%23n)'/%3E%3C/mask%3E%3Crect width='100%25' height='100%25' fill='%238a8778' mask='url(%23m)'/%3E%3C/svg%3E");

  /* ── the PAPER MOTTLE, 2026-09-18. Angus: the later sections should not all be
        grain, he wants "a similar dark colour but a paper look". This is the
        gallery's own recipe, koyeb/backgrounds-2.html v09 (.k-paper): low
        frequency turbulence on a 600px tile drawn at 720px, so it reads as the
        paper's own unevenness, a completely different frequency to --grain-img
        and with no motion at all.
        THE LIGHT OPACITY IS .16, NOT the gallery's .30, and that is a
        measurement, not taste: at .30 the tan takes the mono panel labels on the
        tint band to 4.05:1 and the footer disclaimer to 4.07:1, both under AA.
        At .16 the same two read 4.63:1 and 5.16:1. Dark theme keeps the
        gallery's .85, where the worst pair measures 5.24:1. ── */
  --mottle-op:.16;
  --mottle-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.006' numOctaves='4' stitchTiles='stitch' seed='11'/%3E%3C/filter%3E%3Cmask id='k'%3E%3Crect width='100%25' height='100%25' fill='%23fff' filter='url(%23m)'/%3E%3C/mask%3E%3Crect width='100%25' height='100%25' fill='%239a8a68' mask='url(%23k)'/%3E%3C/svg%3E");
}

html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{background:var(--paper);color:var(--ink);font-family:var(--font-ui);
  font-size:16px;line-height:1.5;-webkit-font-smoothing:antialiased;overflow-x:hidden}
img,video{max-width:100%;display:block}
a{color:inherit;text-decoration:none}

/* ===========================================================================
   1. BANDS. Koyeb alternates light and dark full-bleed sections, both grainy,
   most of them carrying a hairline grid. Everything below is CSS-only so the
   page fetches nothing but fonts and the ad images.
   =========================================================================== */
.k-band{position:relative;padding:40px 0;isolation:isolate}
.k-band > *{position:relative;z-index:2}

/* ===========================================================================
   GRAIN. The tv-fast recipe Angus picked out of backgrounds.html, variant 03.

   WHERE IT SITS: z-index 0, BEHIND everything. That is the whole point. Text
   never has grain composited over it, so every contrast ratio on this page is
   exact ink on paper rather than an estimate, and the strength slider can go to
   0.6 without touching legibility. Paint order back to front is body colour,
   grain, orbit map, section content. Opaque bands (the dark proof band, the
   tint bands) simply cover it, which is correct: the texture is the paper.

   WHERE THE TILE COMES FROM: /koyeb/grain.js draws it into a canvas at DEVICE
   resolution and hands it over as --grain-img, with --tile as its CSS size.
   An SVG feTurbulence tile is resolution independent and goes blobby at 2x,
   which is why it is not used. One noise particle lands on exactly `size`
   device pixels at any dpr, and `size` is the slider, 1 to 4.

   THE MOTION: one oversized layer jumps between 10 offsets of the same noise
   field on a steps() timing, ten swaps a second. The tile repeats, so any
   translation that is not a whole tile shows a different phase of the same
   field, which is what makes it boil like an old TV instead of slide. Nothing
   re-rasterises per frame and only `transform` animates.
   =========================================================================== */
.k-grain{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  opacity:var(--grain-opacity,.30)}
.k-grain span{position:absolute;top:-30%;left:-30%;width:160%;height:160%;
  display:block;background-repeat:repeat;
  background-size:var(--tile,64px) var(--tile,64px);
  background-image:var(--grain-img);
  will-change:transform;backface-visibility:hidden;
  animation:k-boil10 1s steps(1,end) infinite}
@keyframes k-boil10{
  0%  {transform:translate3d(0,0,0)}
  10% {transform:translate3d(-11px,5px,0)}
  20% {transform:translate3d(7px,-13px,0)}
  30% {transform:translate3d(-17px,-3px,0)}
  40% {transform:translate3d(13px,9px,0)}
  50% {transform:translate3d(-3px,-19px,0)}
  60% {transform:translate3d(19px,-7px,0)}
  70% {transform:translate3d(-7px,15px,0)}
  80% {transform:translate3d(5px,-9px,0)}
  90% {transform:translate3d(-15px,11px,0)}
  100%{transform:translate3d(0,0,0)}
}
@media(prefers-reduced-motion:reduce){.k-grain span{animation:none}}

/* the engineering grid */
.k-grid::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background-image:
    repeating-linear-gradient(to right,var(--line-soft) 0 1px,transparent 1px 64px),
    repeating-linear-gradient(to bottom,var(--line-soft) 0 1px,transparent 1px 64px);
  -webkit-mask-image:linear-gradient(to bottom,transparent,#000 12%,#000 74%,transparent);
  mask-image:linear-gradient(to bottom,transparent,#000 12%,#000 74%,transparent)}

/* dark band: near-black, light type, inverted hairlines */
.k-band--dark{background:var(--dark);color:var(--on-dark)}
.k-band--dark.k-grid::after{
  background-image:
    repeating-linear-gradient(to right,var(--line-dark-soft) 0 1px,transparent 1px 64px),
    repeating-linear-gradient(to bottom,var(--line-dark-soft) 0 1px,transparent 1px 64px)}
.k-band--dark p{color:#cfccc2}
.k-band--dark h1,.k-band--dark h2,.k-band--dark h3{color:var(--on-dark)}

/* tint band: the sunken paper Koyeb uses between two white ones */
.k-band--tint{background:var(--paper-soft);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line)}

/* Koyeb's bottom fade: the grid dissolves into the canvas instead of stopping */
.k-fade::after{-webkit-mask-image:linear-gradient(to bottom,transparent,#000 12%,#000 62%,transparent);
  mask-image:linear-gradient(to bottom,transparent,#000 12%,#000 62%,transparent)}

/* Any band that paints something oversized clips it here. body{overflow-x:hidden}
   does NOT clip an overflowing child: with html visible the overflow propagates
   to the viewport and the page gains a sideways scroll. Measured at 901px when
   the globe lived here: scrollWidth 1150 against a 901 viewport. */
.k-band{overflow:hidden}

.k-wrap{max-width:var(--max);margin:0 auto;padding:0 var(--gutter)}
.k-narrow{max-width:720px;margin-left:auto;margin-right:auto}

/* ===========================================================================
   2. TYPE. Koyeb's rule is a huge all-caps display face at line-height 1, a
   plain sans body on tight leading, and uppercase mono for everything small.
   ArcOS flair: the display face is Spectral, and the highlight is mint.
   =========================================================================== */
/* ONE display face (Spectral), ONE weight (600), ONE line-height (1.12), and a
   three-step size scale. Nothing on this page overrides the face, the weight,
   the line-height or the tracking of a heading. Every heading is
   letter-spacing:normal on purpose: no tracking tricks.
     step 1  h1               the page heading
     step 2  h2               section headings
     step 3  .k-display-sm    card titles (the magnet title, FAQ questions)
   Uppercase is a SECTION rule, not a face rule: h1 and h2 set uppercase because
   they are display slabs; step 3 sits inside a card as a sentence and does not. */
h1,h2,.k-display-sm{line-height:1.12}
h1{font-size:28px}
h2{font-size:24px}
.k-display-sm{font-size:20px;text-transform:none}

/* ---------------------------------------------------------------------------
   THREE HEADER TREATMENTS, for side by side comparison. The treatment is a
   class on <body> and NOTHING else on the page changes with it.
     body.h-serif   index.html    ArcOS display serif, mixed case   (default)
     body.h-caps    h-caps.html   Koyeb-style all caps sans, tracked out
     body.h-sans    h-sans.html   mixed case heavy sans
   Within a treatment the face, weight, line-height and tracking are identical
   across h1, h2 and the card step: one system each, never a mix.
   --------------------------------------------------------------------------- */
body.h-serif h1,body.h-serif h2,body.h-serif .k-display-sm{
  font-family:var(--font-display);font-weight:600;letter-spacing:normal;
  text-transform:none}

body.h-caps h1,body.h-caps h2,body.h-caps .k-display-sm{
  font-family:var(--font-ui);font-weight:800;letter-spacing:.02em;
  text-transform:uppercase}
body.h-caps .k-display-sm{text-transform:none;letter-spacing:normal}
body.h-caps h1{font-size:22px}
body.h-caps h2{font-size:20px}

body.h-sans h1,body.h-sans h2,body.h-sans .k-display-sm{
  font-family:var(--font-ui);font-weight:800;letter-spacing:normal;
  text-transform:none}
body.h-sans h1{font-size:26px}
body.h-sans h2{font-size:22px}
/* h3 is the UI face, as it is on the live page. Still no tracking trick. */
h3{font-family:var(--font-ui);font-weight:600;font-size:19px;line-height:1.25;
  letter-spacing:normal}
p{font-size:16px;line-height:1.55;color:var(--ink-soft)}
p + p{margin-top:12px}
strong{font-weight:600;color:var(--ink)}

/* HIGHLIGHT. The one piece of colour in a heading, and the thing that was
   messy before. Two separate causes, both fixed here:

   1. A plain `background` on an inline element paints the FONT'S CONTENT BOX,
      not the line box. Measured on Spectral 600 at 32px: content box 49px tall
      against a 35.84px line box, so the slab bled about 6.5px into the line
      above AND the line below. That is the "rolls over onto other lines" bug,
      and no amount of padding fixes it because padding only makes it taller.
      The fix is to draw the slab as a background IMAGE with an em-sized height,
      so the slab is the size we say it is and always sits inside the line box.
      Line-height is not touched: the heading keeps its 1.12.
   2. The highlighted phrase is "$11m", four characters, so it cannot wrap at
      any width. box-decoration-break:clone stays anyway, so that if the phrase
      is ever changed to something longer each wrapped line still gets its own
      slab instead of one box stretched across the break.

   Verified at 375, 768 and 1280: one client rect, slab height inside the line
   box, no spill onto the leading edge of the next line. */
.k-em{color:var(--on-mint);padding:0 .16em;
  background-image:linear-gradient(var(--mint),var(--mint));
  background-repeat:no-repeat;
  background-size:100% 1.04em;
  background-position:0 .2em;
  box-decoration-break:clone;-webkit-box-decoration-break:clone}
.k-lede{font-size:17px;line-height:1.5;color:var(--ink-soft)}
.k-fine{font-size:12.5px;line-height:1.5;color:var(--muted)}

/* uppercase mono label: Koyeb puts one of these above every section head */
.k-eyebrow{font-family:var(--font-mono);font-size:10.5px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink);display:inline-flex;align-items:center;gap:8px;
  line-height:1}
.k-eyebrow::before{content:"";width:8px;height:8px;flex:0 0 8px;background:var(--mint);
  border-radius:2px;box-shadow:inset 0 0 0 1px rgba(10,10,10,.22)}
.k-band--dark .k-eyebrow{color:var(--on-dark)}
.k-band--dark .k-eyebrow::before{box-shadow:none}

.k-sechead{margin-bottom:24px}
.k-sechead .k-eyebrow{margin-bottom:14px}
.k-sechead h2{margin-bottom:0}
.k-sechead .k-lede{margin-top:14px}
.k-center{text-align:center}
.k-center .k-eyebrow{justify-content:center}

/* ===========================================================================
   3. BUTTONS. NOT Koyeb's. These are the live page's green buttons, copied
   property for property from ../assets/lp.css (.btn / .btn-primary /
   .btn-ghost / .btn-primary .arrow, plus the 600px width and padding change).
   Koyeb's mono keycap with the inset bottom edge was built here and removed on
   Angus's call: the green pill is the ArcOS button and it stays the ArcOS
   button. Hover matches the live page, which changes nothing on hover and
   lightens to mint-soft on :active only.
   =========================================================================== */
.k-btn{font-family:var(--font-ui);font-size:16px;font-weight:600;border-radius:99px;
  cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:9px;
  text-decoration:none;border:none;text-align:center;line-height:1.25}
.k-btn--primary{background:var(--mint);color:var(--on-mint);padding:16px 26px;
  box-shadow:0 6px 18px rgba(31,184,102,0.28);width:100%}
.k-btn--primary:active{background:var(--mint-soft)}
.k-btn--primary .arrow{font-family:var(--font-display);font-style:italic;font-size:18px}
.k-btn--ghost{background:transparent;color:var(--ink);padding:9px 16px;
  border:1px solid var(--ink);font-weight:500;font-size:13px;border-radius:99px}
.k-band--dark .k-btn--ghost{color:var(--on-dark);border-color:var(--on-dark)}

.k-cta{margin-top:26px}
.k-cta .k-fine{margin-top:12px;text-align:center}

/* ===========================================================================
   4. BADGE PILL. Koyeb's signature small component: dark rounded rect, a 20px
   green square tile, then uppercase mono. Used on feature visuals and cards.
   =========================================================================== */
.k-badge{display:inline-flex;align-items:center;gap:8px;background:var(--dark);
  color:var(--on-dark);border-radius:6px;padding:6px 10px 6px 6px;
  font-family:var(--font-mono);font-size:10px;font-weight:500;letter-spacing:.12em;
  text-transform:uppercase;line-height:1}
.k-badge .k-tile{width:18px;height:18px;flex:0 0 18px;border-radius:2px;background:var(--mint);
  color:var(--on-mint);display:inline-flex;align-items:center;justify-content:center;
  font-family:var(--font-display);font-size:11px;font-weight:600;line-height:1}
.k-band--dark .k-badge{background:rgba(247,245,238,.1);border:1px solid var(--line-dark)}

/* ===========================================================================
   5. NAV. Koyeb keeps it transparent over the grid, mark hard left, keycap
   right. The centre link pill is deliberately empty here: this page carries no
   menu on purpose, so there is nothing to bleed a click off the page.
   =========================================================================== */
.k-nav{position:sticky;top:0;z-index:40;background:rgba(247,245,238,.82);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line-soft)}
.k-nav .k-row{display:flex;align-items:center;justify-content:space-between;gap:16px;
  max-width:var(--max);margin:0 auto;padding:10px var(--gutter)}
/* wordmark, not a heading: the tracking here is logotype spacing, exactly as
   ../assets/lp.css sets it on .mark. Same display face and weight as headings. */
.k-mark{display:inline-flex;align-items:center;gap:9px;font-family:var(--font-display);
  font-weight:600;font-size:18px;letter-spacing:.1em;line-height:1}
.k-mark .k-tile{width:22px;height:22px;flex:0 0 22px;border-radius:4px;background:var(--mint);
  color:var(--on-mint);display:inline-flex;align-items:center;justify-content:center;
  font-size:13px;letter-spacing:0;box-shadow:inset 0 -3px 0 rgba(31,184,102,.55)}

/* ===========================================================================
   6. HERO. Koyeb's composition: centred, globe behind, corner crop marks
   bracketing the content box, heading block, one sub line, then the artefact
   (their terminal mockup). Here the artefact is the lead magnet card, because
   the form has to stay in the fold.
   =========================================================================== */
.k-hero{padding:32px 0 44px;text-align:center}
.k-hero h1{margin-bottom:16px}
.k-hero .k-sub{font-size:16.5px;line-height:1.55;color:var(--ink-soft);
  max-width:620px;margin:0 auto}

/* print-style registration marks around the fold */
.k-crop{position:relative;padding:14px 0}
.k-crop::before,.k-crop::after,
.k-crop-foot::before,.k-crop-foot::after{content:"";position:absolute;width:13px;height:13px;
  border:1px solid rgba(10,10,10,.3);pointer-events:none}
.k-crop::before{top:0;left:-4px;border-right:0;border-bottom:0}
.k-crop::after{top:0;right:-4px;border-left:0;border-bottom:0}
.k-crop-foot{position:absolute;inset:0;pointer-events:none}
.k-crop-foot::before{bottom:0;left:-4px;border-right:0;border-top:0}
.k-crop-foot::after{bottom:0;right:-4px;border-left:0;border-top:0}

/* ===========================================================================
   7. LEAD MAGNET CARD. Shaped like Koyeb's terminal mockup: dark header strip
   with traffic lights and a mono label, body below, one tinted glow. The glow
   is the only drop shadow allowed on the page (Koyeb uses exactly one too).
   =========================================================================== */
/* NO backdrop-filter ON THE FORM CARD since 2026-09-19. The card is opaque now, so the blur
   did nothing on Chrome and did damage on Safari: WebKit paints a backdrop-filtered box with
   a rounded corner as a light square halo outside the radius, which is the "white around the
   form" Angus saw on the iPad and no Chromium render ever showed. The glow shadow is neutral
   and dark, not mint, for the same reason: on a light card a tinted halo reads as an edge. */
.k-magnet{margin-top:28px;text-align:left;background:var(--card);
  border:1px solid var(--line);border-radius:14px;overflow:hidden;
  box-shadow:0 25px 50px -12px rgba(0,0,0,.35)}
.k-magnet-bar{display:flex;align-items:center;gap:10px;background:var(--dark);
  padding:11px 14px;border-bottom:1px solid rgba(0,0,0,.4)}
/* WINDOW CONTROLS, 2026-09-18 (Angus: "add the little X and the maximise button to
   the form, I like that detail"). Three dots, 11px so a glyph inside one still reads:
   a close cross on the mint dot, a maximise square on the third, the middle plain.
   background-COLOR plus background-IMAGE, never the `background` shorthand, which
   would reset the repeat, position and size the glyph needs. The third dot's ground
   is lifted to .55 alpha because a near black glyph on a .25 dot is invisible.
   Decoration only: the markup is aria-hidden and carries nothing focusable. */
.k-magnet-bar .k-dots{display:inline-flex;gap:6px;flex:0 0 auto}
.k-magnet-bar .k-dots i{width:11px;height:11px;border-radius:50%;
  background-color:rgba(247,245,238,.25);background-repeat:no-repeat;
  background-position:center;background-size:100% 100%}
.k-magnet-bar .k-dots i:first-child{background-color:var(--mint);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M3 3l4 4M7 3l-4 4' fill='none' stroke='%230a0a0a' stroke-width='1.3' stroke-linecap='round'/%3E%3C/svg%3E")}
.k-magnet-bar .k-dots i:last-child{background-color:rgba(247,245,238,.55);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Crect x='3' y='3' width='4' height='4' fill='none' stroke='%230a0a0a' stroke-width='1.3'/%3E%3C/svg%3E")}
.k-magnet-bar .k-lf-eye{font-family:var(--font-mono);font-size:9.5px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--mint);line-height:1.4}
.k-magnet-body{padding:20px 16px 22px}
/* the magnet title is display step 3 (.k-display-sm). Nothing here touches the
   face, weight, line-height or tracking: colour only. */
.k-magnet .k-lf-title{color:var(--ink)}
.k-magnet .k-lf-sub{font-size:14.5px;line-height:1.5;color:var(--ink-soft);margin-top:10px}
/* CARD BODY REWRITE, 2026-09-19 (Angus, verbatim copy): the sub paragraph is replaced
   by a lead line, a four-item list and a closing line. Same face, size (14.5px) and
   colour as the old sub throughout, so the swap reads as one voice, not three.
   The list marker reuses the small mint square the eyebrow dot and the fit-list ticks
   already use on this page, just smaller (6px), rather than inventing a new shape.
   Hanging indent (padding-left on the li, marker at left:0) so a wrapped second line
   sits under the text, never under the marker. */
.k-magnet .k-lf-lead,
.k-magnet .k-lf-list li,
.k-magnet .k-lf-close{font-size:14.5px;line-height:1.5;color:var(--ink-soft)}
.k-magnet .k-lf-lead{margin-top:10px}
.k-magnet .k-lf-list{list-style:none;margin-top:8px}
.k-magnet .k-lf-list li{position:relative;padding-left:16px;margin-top:7px}
.k-magnet .k-lf-list li:first-child{margin-top:0}
.k-magnet .k-lf-list li::before{content:"";position:absolute;left:0;top:.55em;
  width:6px;height:6px;border-radius:1px;background:var(--mint)}
.k-magnet .k-lf-close{margin-top:10px}

/* form: Koyeb's 6px radius and alpha borders, ArcOS mint focus ring */
.k-form{margin-top:18px}
.k-field{margin-top:14px}
.k-form label{display:block;font-family:var(--font-mono);font-size:9.5px;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
.k-form input,.k-form select{width:100%;background:var(--white);border:1px solid var(--line);
  border-radius:6px;padding:13px 12px;color:var(--ink);font-family:var(--font-ui);
  font-size:16px;line-height:1.3;outline:none;appearance:none;-webkit-appearance:none;
  transition:border-color .15s var(--ease),box-shadow .15s var(--ease)}
.k-form select{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%237a7e7a' stroke-width='1.6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 13px center;background-size:12px 8px;
  padding-right:34px}
.k-form input:focus,.k-form select:focus{border-color:var(--mint-deep);
  box-shadow:0 0 0 3px rgba(31,184,102,.22)}
.k-form .k-btn--primary{margin-top:18px}
/* the website gate (lp.js): red hairline and the hint, only while the field is refused */
.k-err{display:none;margin-top:6px;font-size:12px;line-height:1.45;color:#b3261e}
.k-field.bad input{border-color:#b3261e;box-shadow:0 0 0 3px rgba(179,38,30,.16)}
.k-field.bad .k-err{display:block}
.k-form .k-lf-fine{font-family:var(--font-mono);font-size:10px;line-height:1.6;
  color:var(--muted);margin-top:12px;text-align:center}

/* ===========================================================================
   8. PROOF BAND. Koyeb's stat row: dark band, display-face numbers, hairline
   verticals, caption directly under. ArcOS flair: the numbers are mint.
   =========================================================================== */
.k-stats{padding:0}
.k-stats .k-row{display:grid;grid-template-columns:1fr 1fr;max-width:var(--max);margin:0 auto}
.k-stat{padding:20px 14px;text-align:center;border-right:1px solid var(--line-dark);
  border-bottom:1px solid var(--line-dark)}
.k-stat:nth-child(2n){border-right:0}
.k-stat:nth-last-child(-n+2){border-bottom:0}
.k-stat .k-n{font-family:var(--font-display);font-weight:600;font-size:31px;line-height:1.12;
  letter-spacing:normal;color:var(--mint)}
.k-stat .k-d{font-size:12.5px;line-height:1.4;color:#c4c1b8;margin-top:9px}

/* ===========================================================================
   9. CARDS. One shape, used for the four locks, the steps, the fit columns and
   the objection handlers: translucent fill, alpha hairline, radius 16, blur.
   Hover lifts the alphas, never the geometry (Koyeb never transforms a card).
   =========================================================================== */
.k-cards{display:grid;gap:14px;margin-top:24px}
.k-card{background:var(--fill-card);border:1px solid var(--line);border-radius:16px;
  padding:20px 18px;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  transition:background-color .15s var(--ease),border-color .15s var(--ease)}
.k-card:hover{background:rgba(255,255,255,.8);border-color:rgba(10,10,10,.3)}
.k-card .k-badge{margin-bottom:14px}
.k-card h3{margin-bottom:8px}
.k-card p{font-size:15px;line-height:1.5}
.k-band--dark .k-card{background:var(--fill-card-dark);border-color:var(--line-dark)}
.k-band--dark .k-card:hover{background:rgba(247,245,238,.1);border-color:rgba(247,245,238,.22)}

/* bullets: Koyeb's green square tile, never a bare tick glyph */
.k-ticks{list-style:none}
.k-ticks li{position:relative;padding-left:30px;margin-top:12px;font-size:16px;
  line-height:1.45;color:var(--ink)}
.k-ticks li:first-child{margin-top:0}
.k-ticks li::before{content:"";position:absolute;left:0;top:2px;width:18px;height:18px;
  border-radius:3px;background:var(--mint);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M2.6 7.4l2.8 2.8 6-6' fill='none' stroke='%230a0a0a' stroke-width='1.9' stroke-linecap='square'/%3E%3C/svg%3E");
  background-size:14px 14px;background-position:center;background-repeat:no-repeat}
.k-band--dark .k-ticks li{color:var(--on-dark)}
.k-crosses{list-style:none}
.k-crosses li{position:relative;padding-left:30px;margin-top:12px;font-size:16px;
  line-height:1.45;color:var(--ink-soft)}
.k-crosses li:first-child{margin-top:0}
.k-crosses li::before{content:"";position:absolute;left:0;top:2px;width:18px;height:18px;
  border-radius:3px;background:rgba(10,10,10,.08);border:1px solid var(--line);
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M4 4l6 6M10 4l-6 6' fill='none' stroke='%237a7e7a' stroke-width='1.7' stroke-linecap='square'/%3E%3C/svg%3E");
  background-size:14px 14px;background-position:center;background-repeat:no-repeat}

/* ===========================================================================
   10. VISUAL PANEL. Koyeb's feature cards sit on a bordered panel with the grid
   showing through and a badge pinned top-left. Used here for the video and
   portal placeholders so the empty slots still read as designed, not broken.
   =========================================================================== */
.k-panel{position:relative;border:1px solid var(--line);border-radius:10px;overflow:hidden;
  background:rgba(255,255,255,.4);
  background-image:
    repeating-linear-gradient(to right,var(--line-soft) 0 1px,transparent 1px 34px),
    repeating-linear-gradient(to bottom,var(--line-soft) 0 1px,transparent 1px 34px);
  display:flex;align-items:center;justify-content:center;text-align:center;padding:20px 16px;
  font-family:var(--font-mono);font-size:9.5px;line-height:2;letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);overflow-wrap:anywhere}
.k-panel--video{aspect-ratio:16/9;margin-top:24px}
.k-panel--shot{aspect-ratio:9/16;max-height:300px;margin-top:16px}
/* THE PHONE RECORDINGS, 2026-09-19. A <video> in the slot the labelled tile held:
   same hairline, radius and height budget as .k-panel--shot, no padding and no
   grid because the frame is the content. 390x844 CSS pixels recorded at 2x, so
   the aspect is the phone's, not 9:16. Muted, looping, autoplay, playsinline. */
.k-shot{display:block;width:auto;max-width:100%;max-height:300px;aspect-ratio:390/844;margin-top:16px;
  border:1px solid var(--line);border-radius:10px;background:#faf8f2;object-fit:cover}

/* ===========================================================================
   11. AD GRID. Koyeb's app-card grid: bordered translucent tile, content, then
   a hairline-separated uppercase mono meta strip at the bottom.
   contain, never cover: cropping an ad cuts its headline off.
   =========================================================================== */
.k-adgrid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:24px}
.k-ad{background:rgba(255,255,255,.6);border:1px solid var(--line);border-radius:10px;
  overflow:hidden;transition:border-color .15s var(--ease),background-color .15s var(--ease)}
.k-ad:hover{border-color:rgba(10,10,10,.32);background:rgba(255,255,255,.85)}
/* SQUARE since 2026-09-18 (Angus: "we are only running square images here"). Every
   file under /assets/ads/*-sq.webp is 540x540, so `contain` has nothing left to
   letterbox: it stays because cropping an ad would cut its headline off. */
.k-ad img{width:100%;height:auto;aspect-ratio:1/1;object-fit:contain;background:var(--white)}
.k-ad .k-cap{font-family:var(--font-mono);font-size:8.5px;letter-spacing:.07em;
  color:var(--muted);padding:9px 10px;line-height:1.5;text-transform:uppercase;
  border-top:1px solid var(--line-soft)}

/* ===========================================================================
   12. ANSWER BLOCK. The embedded objection handlers, given Koyeb's compact
   card treatment with the green tile pulled out to the left.
   =========================================================================== */
.k-answer{display:flex;gap:14px;align-items:flex-start;background:var(--fill-card);
  border:1px solid var(--line);border-radius:12px;padding:18px 16px;margin-top:26px;
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}
.k-answer .k-tile{width:22px;height:22px;flex:0 0 22px;border-radius:3px;background:var(--mint);
  margin-top:2px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M2.6 7.4l2.8 2.8 6-6' fill='none' stroke='%230a0a0a' stroke-width='1.9' stroke-linecap='square'/%3E%3C/svg%3E");
  background-size:16px 16px;background-position:center;background-repeat:no-repeat}
.k-answer h3{margin-bottom:8px}
.k-answer p{font-size:15px}

/* ===========================================================================
   13. STEPS + FIT
   =========================================================================== */
.k-steps{display:grid;gap:14px;margin-top:24px}
.k-fit{display:grid;gap:14px;margin-top:24px}
.k-fit h3{font-family:var(--font-mono);font-weight:500;font-size:10.5px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);margin-bottom:16px}

/* ===========================================================================
   14. FAQ. Hairline rows, Spectral question, a green square tile carrying the
   plus. Koyeb turns its accordion markers into the same tile as its bullets.
   =========================================================================== */
.k-faq{margin-top:24px;border-top:1px solid var(--line)}
.k-faq details{border-bottom:1px solid var(--line)}
/* the FAQ question is display step 3 too: same face, weight, line-height, tracking */
.k-faq summary{cursor:pointer;list-style:none;padding:17px 0;color:var(--ink);display:flex;
  justify-content:space-between;align-items:flex-start;gap:16px}
.k-faq summary::-webkit-details-marker{display:none}
.k-faq summary .k-plus{flex:0 0 22px;width:22px;height:22px;border-radius:3px;
  background:var(--mint);color:var(--on-mint);font-family:var(--font-ui);font-weight:600;
  font-size:15px;line-height:22px;text-align:center;margin-top:1px;
  transition:transform .15s var(--ease)}
.k-faq details[open] summary .k-plus{transform:rotate(45deg)}
:root[data-theme="dark"] .k-faq{border-top-color:var(--line)}
:root[data-theme="dark"] .k-faq details{border-bottom-color:var(--line)}
:root[data-theme="dark"] .k-faq details[open]{background:rgba(246,243,236,.03)}
:root[data-theme="dark"] .k-faq details[open] summary{color:var(--ink)}
.k-faq .k-ans{padding:0 0 20px;font-size:15.5px;line-height:1.6;color:var(--ink-soft);
  max-width:62ch}

/* ===========================================================================
   15. OFFER BAND. Koyeb's closing CTA: full-bleed dark, grid and globe behind,
   centred, uppercase heading, a mono capacity pill, then the keycap.
   =========================================================================== */
.k-offer{padding:48px 0 52px;text-align:center}
.k-offer h2{margin:16px 0 20px}
.k-offer .k-ticks{text-align:left;max-width:560px;margin:0 auto}
.k-capacity{display:inline-flex;align-items:center;gap:9px;margin-top:26px;
  border:1px solid var(--line-dark);border-radius:6px;padding:9px 14px;
  background:rgba(247,245,238,.06);
  font-family:var(--font-mono);font-size:10.5px;font-weight:500;letter-spacing:.13em;
  text-transform:uppercase;color:var(--mint);line-height:1.4}
.k-capacity::before{content:"";width:7px;height:7px;flex:0 0 7px;border-radius:50%;
  background:var(--mint)}
.k-offer .k-cta{max-width:420px;margin-left:auto;margin-right:auto}
.k-offer .k-fine{color:#a9a69d}

/* the guarantee, same dark card treatment inside a tint band. Its internal
   lattice moved from ::before onto the element's own background in section 28,
   so ::before could carry the grain UNDER the copy rather than over it. */
.k-risk{color:var(--on-dark);border:1px solid rgba(0,0,0,.5);
  border-radius:16px;padding:24px 20px;position:relative;overflow:hidden}
.k-risk > *{position:relative}
.k-risk .k-eyebrow{color:var(--on-dark)}
.k-risk h2{color:var(--on-dark);margin:14px 0 12px}
.k-risk p{color:#cfccc2;font-size:15.5px}

/* ===========================================================================
   16. FOOTER. Koyeb closes dark. Mark, uppercase mono colophon, a hairline,
   then the provenance block at the smallest size on the page.
   =========================================================================== */
.k-footer{background:var(--dark);color:var(--on-dark);padding:34px 0 40px;
  border-top:1px solid rgba(0,0,0,.4)}
.k-footer .k-mark{margin-bottom:16px}
.k-footer .k-mark .k-tile{box-shadow:inset 0 -3px 0 rgba(31,184,102,.55)}
.k-footer .k-colo{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.11em;
  text-transform:uppercase;color:#a9a69d;line-height:2}
.k-footer .k-colo a{color:var(--mint);text-decoration:none}
.k-footer .k-colo a:hover{text-decoration:underline}
/* #8d8a82 measured 4.50:1 once the grain of section 28 sits on the footer's own
   ground (worst case particle, both themes): AA by a rounding error. #9a978f
   clears it at 5.32 and stays a clear step dimmer than the colophon's #a9a69d,
   which is the whole point of the tone. Same class of change as --muted going
   #7a7e7a to #5d615c in round 4: a token moved because a measurement said so. */
.k-footer .k-disc{margin-top:20px;padding-top:16px;border-top:1px solid var(--line-dark);
  font-size:11.5px;color:#9a978f;line-height:1.65}

/* ===========================================================================
   17. WIDER SCREENS. Koyeb's only real breakpoints are 768 and 1280: the
   heading scale roughly doubles and the feature grid goes to two columns.
   =========================================================================== */
@media(min-width:600px){
  :root{--gutter:28px}
  h1{font-size:46px}
  h2{font-size:34px}
  .k-display-sm{font-size:24px}
  body.h-caps h1{font-size:38px}  body.h-caps h2{font-size:30px}
  body.h-sans h1{font-size:43px}  body.h-sans h2{font-size:32px}
  h3{font-size:21px}
  .k-band{padding:56px 0}
  .k-hero{padding:48px 0 60px}
  .k-btn--primary{width:auto;padding:17px 30px}
  .k-magnet-body{padding:26px 24px 28px}
  .k-stats .k-row{grid-template-columns:repeat(4,1fr)}
  .k-stat{border-bottom:0;padding:28px 18px}
  .k-stat:nth-child(2n){border-right:1px solid var(--line-dark)}
  .k-stat:last-child{border-right:0}
  .k-stat .k-n{font-size:38px}
  .k-cards{grid-template-columns:1fr 1fr;gap:16px}
  .k-steps{grid-template-columns:repeat(3,1fr);gap:16px}
  .k-fit{grid-template-columns:1fr 1fr;gap:16px}
  .k-adgrid{grid-template-columns:repeat(3,1fr);gap:16px}
  .k-panel--shot{aspect-ratio:9/16;max-height:340px}
  .k-shot{max-height:340px}
  .k-answer{padding:22px 22px}
  .k-cta .k-fine{text-align:center}
  .k-crop::before,.k-crop::after,.k-crop-foot::before,.k-crop-foot::after{width:18px;height:18px}
  .k-crop::before,.k-crop-foot::before{left:-14px}
  .k-crop::after,.k-crop-foot::after{right:-14px}
  .k-offer{padding:64px 0 68px}
  .k-risk{padding:34px 32px}
}

@media(min-width:1000px){
  :root{--gutter:40px}
  h1{font-size:46px}
  h2{font-size:40px}
  .k-display-sm{font-size:26px}
  body.h-caps h1{font-size:47px}  body.h-caps h2{font-size:35px}
  body.h-sans h1{font-size:54px}  body.h-sans h2{font-size:38px}
  .k-band{padding:72px 0}
  .k-hero{padding:64px 0 76px}
  .k-hero .k-sub{font-size:18px}
  .k-stat .k-n{font-size:44px}
  .k-band--dark.k-grid::after,.k-grid::after{background-size:auto}
  .k-offer{padding:88px 0 92px}
  .k-offer h2{max-width:900px;margin-left:auto;margin-right:auto}
}

/* ===========================================================================
   18. ACCESSIBILITY / MOTION
   =========================================================================== */
:focus-visible{outline:2px solid var(--mint-deep);outline-offset:2px}
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{transition-duration:.01ms !important;animation-duration:.01ms !important}
}


/* ===========================================================================
   19. THEMES. data-theme on <html>, not on <body>, so the picker can set it
   before the first paint without waiting for the body to exist.
     light  warm beige paper, ink type, green pill buttons     (default)
     dark   deep warm charcoal, pale type, the same green pills
   Everything else on the page reads tokens, so nothing else knows which theme
   it is on. The four bg-paper / bg-stone / bg-grid / bg-dusk variants from the
   previous round are gone: Angus picked variant 03 out of backgrounds.html, so
   there is one canvas now and a real dark theme instead of a proto one.
   =========================================================================== */
:root[data-theme="dark"]{
  --paper:#1a1918;
  --paper-soft:#232120;
  --white:#2b2827;
  --ink:#f6f3ec;
  --ink-soft:#dcd7cb;
  --muted:#a6a298;
  --dark:#100f0e;              /* bands still separate from the page ground */
  --dark-soft:#191817;
  --line:rgba(246,243,236,.16);
  --line-soft:rgba(246,243,236,.09);
  --line-dark:rgba(246,243,236,.13);
  --line-dark-soft:rgba(246,243,236,.07);
  --fill-card:rgba(246,243,236,.05);
  --fill-card-dark:rgba(246,243,236,.05);
  --card:#262322;              /* a lifted card, not a hole */
  --field:#141312; --field-ink:#f6f3ec;
  --orbit-line:214,208,192;
  --grain-opacity:.22;         /* lighter particles need less of them */
  --mottle-op:.85;             /* the gallery's dark value; worst pair measures 5.24:1 */
  --mottle-img:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='600'%3E%3Cfilter id='m'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.006' numOctaves='4' stitchTiles='stitch' seed='11'/%3E%3C/filter%3E%3Cmask id='k'%3E%3Crect width='100%25' height='100%25' fill='%23fff' filter='url(%23m)'/%3E%3C/mask%3E%3Crect width='100%25' height='100%25' fill='%232e2823' mask='url(%23k)'/%3E%3C/svg%3E");
}

/* pieces that were written against the light theme and need saying twice */
:root[data-theme="dark"] body{background:var(--paper);color:var(--ink)}
:root[data-theme="dark"] .k-nav{background:rgba(26,25,24,.86);
  border-bottom-color:var(--line-soft)}
:root[data-theme="dark"] .k-band--tint{background:var(--paper-soft)}
/* THE FORM CARD IS LIGHT ON THE DARK THEME, 2026-09-19 (Angus: "change the background
   of the form to white or a light colour, give it some contrast so it's the clearest,
   easiest next step"). The card used to be a lifted charcoal (#262322) on a charcoal
   page, so the one thing the page exists for was the same colour as everything around
   it. Now it is the light theme's card, on both themes: the ink, muted, line, field and
   white tokens are re-scoped INSIDE the card to their light values, so every rule
   written against those tokens (title, list, labels, inputs, the select chevron) renders
   as it does in the light theme without a second copy of any of it. The dark input and
   select overrides that used to live here are gone for the same reason: with the
   tokens re-scoped, the base rules already produce white fields with dark type. The
   header strip stays dark (var(--dark) is not re-scoped), so the window controls and
   the mint button read exactly as before. */
:root[data-theme="dark"] .k-magnet{
  --ink:#0a0a0a;--ink-soft:#2a2a2a;--muted:#5d615c;
  --line:rgba(10,10,10,.17);--line-soft:rgba(10,10,10,.09);
  --white:#fff;--field:#fff;--field-ink:#0a0a0a;
  color:var(--ink);background:#fbf9f3;border-color:rgba(0,0,0,.55)}
:root[data-theme="dark"] .k-ad{background:rgba(246,243,236,.05);
  border-color:rgba(246,243,236,.16)}
/* the eight ads are light artwork. On a dark ground they need a mat, or they
   read as eight glaring white rectangles. A warm off white tile plus a hairline
   and a little inset padding makes each one look mounted rather than pasted. */
:root[data-theme="dark"] .k-ad img{background:#efece3;
  border-bottom:1px solid rgba(246,243,236,.14)}
:root[data-theme="dark"] .k-ad .k-cap{border-top:0}
:root[data-theme="dark"] .k-ad:hover{background:rgba(246,243,236,.09);
  border-color:rgba(246,243,236,.3)}
/* THE PANEL LABEL IS #b5b1a7, NOT #9c9a92, and that is a measurement (2026-09-18).
   #9c9a92 measured 3.94:1 against the worst pixel under its own glyphs BEFORE the
   paper pass, and 3.77:1 after, both under AA; the paper film is worth 0.17 of
   that, so the colour was always the problem. #b5b1a7 (the same value
   backgrounds-2.html carries as --plate-muted on dark) reads 4.97:1 on the same
   worst pixel. Same class of change as the footer disclaimer going #8d8a82 to
   #9a978f: a token moved because a measurement said so. */
:root[data-theme="dark"] .k-panel{background:rgba(246,243,236,.035);
  border-color:rgba(246,243,236,.16);color:#b5b1a7;
  background-image:
    repeating-linear-gradient(to right,rgba(246,243,236,.05) 0 1px,transparent 1px 34px),
    repeating-linear-gradient(to bottom,rgba(246,243,236,.05) 0 1px,transparent 1px 34px)}
/* CARD HOVER ON THE DARK THEME, 2026-09-19 (Angus: "the cards on hover look cool but
   all the text gets hidden", on the steps, the Andromeda locks and the fit lists). The
   base .k-card:hover is a light theme value, rgba(255,255,255,.8): on the dark theme
   the type is pale (#f6f3ec), so the card went near white under pale text and the text
   vanished. Same lift as the dark .k-ad:hover, a few points of alpha, never a colour
   flip. Every card on the page shares .k-card, so this one rule covers all of them. */
:root[data-theme="dark"] .k-card:hover{background:rgba(246,243,236,.09);
  border-color:rgba(246,243,236,.3)}
:root[data-theme="dark"] .k-btn--ghost{color:var(--ink);border-color:var(--ink)}
:root[data-theme="dark"] .k-crosses li::before{background:rgba(246,243,236,.08)}
:root[data-theme="dark"] .k-risk{border-color:rgba(246,243,236,.12)}
:root[data-theme="dark"] .k-footer{border-top-color:rgba(246,243,236,.1)}
/* the mint pill button is identical on both themes, on purpose: it is the one
   thing on the page that must not change when the lights go out */

/* ===========================================================================
   20. THE HERO IS CENTRED, AND THE ORBIT MAP SITS BEHIND IT.
   The two column hero and the globe are both gone. One centred column: H1 and
   sub to 720px, the form card to 560px, CTAs and section headings centred.
   =========================================================================== */
.k-hero{text-align:center}
.k-hero .k-hero-col{max-width:720px;margin:0 auto;min-width:0}
.k-hero h1{max-width:720px;margin-left:auto;margin-right:auto}
.k-hero .k-sub{max-width:640px;margin-left:auto;margin-right:auto}
.k-magnet{max-width:560px;margin-left:auto;margin-right:auto;text-align:left}

/* centred section headings and centred CTAs, everywhere */
.k-sechead{text-align:center}
.k-sechead .k-eyebrow{justify-content:center}
.k-sechead .k-lede{margin-left:auto;margin-right:auto;max-width:62ch}
.k-cta{text-align:center}
.k-cta .k-fine{text-align:center}
.k-risk{text-align:center}
.k-risk .k-eyebrow{justify-content:center}
.k-risk p{max-width:62ch;margin-left:auto;margin-right:auto}
.k-how,.k-cards,.k-steps,.k-fit{text-align:left}

/* ---- the orbit zone: the hero plus the section under it ---- */
.k-orbit-zone{position:relative;z-index:0}
.k-orbit{position:absolute;inset:0;z-index:0;pointer-events:none;
  width:100%;height:100%;display:block}
:root[data-orbit="off"] .k-orbit{display:none}

/* ===========================================================================
   21. VARIANT B. Angus's own headline is 125 characters against variant A's 66,
   so it gets its own step on the same scale: same face, same weight, same
   line-height, only the size changes.
   =========================================================================== */
body.v-b h1{font-size:23px}
@media(min-width:600px){body.v-b h1{font-size:32px}}
@media(min-width:1000px){body.v-b h1{font-size:36px}}

/* the three step block on variant B. Numbered, one card each, mint numeral. */
.k-how{display:grid;gap:14px;margin-top:22px}
.k-how .k-card{position:relative}
.k-how .k-num{display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:7px;background:var(--mint);color:var(--on-mint);
  font-family:var(--font-display);font-weight:600;font-size:16px;line-height:1;
  margin-bottom:12px}
@media(min-width:900px){.k-how{grid-template-columns:repeat(3,1fr);gap:16px}}

/* ===========================================================================
   The preview picker's styles are deliberately NOT in this file. They live in
   koyeb/koyeb.css, which the review pages under /koyeb/ load instead. A
   production page has no control widget, only the silent query overrides read
   by assets/grain.js.
   =========================================================================== */

/* ===========================================================================
   23. VIDEO. Used by index-vsl.html and by the VSL 2 slot on thanks.html. The
   markup and the ids are the ones /assets/lp.js already binds, unchanged from
   the previous design, so the player behaviour is not being rewritten with the
   paint job: autoplay muted, tap to unmute and restart, end card, replay.
   =========================================================================== */
.k-video{margin:22px auto 0;max-width:560px}
.k-video .video{background:#000;border-radius:10px;aspect-ratio:16/9;position:relative;
  overflow:hidden;border:1px solid var(--line);box-shadow:0 25px 50px -12px rgba(0,0,0,.45)}
.k-video video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;background:#000}
.vsl-ov{position:absolute;inset:0;z-index:3;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:12px;cursor:pointer;text-align:center;
  padding:18px;background:linear-gradient(180deg,rgba(10,10,10,.14),rgba(10,10,10,.46));
  transition:opacity .4s ease}
.vsl-ov.hide{opacity:0;pointer-events:none}
.vsl-ov .ring{width:66px;height:66px;border-radius:50%;background:var(--mint);
  color:var(--on-mint);display:flex;align-items:center;justify-content:center;position:relative;
  box-shadow:0 8px 24px rgba(31,184,102,.45)}
.vsl-ov .ring::after{content:"";position:absolute;inset:-6px;border-radius:50%;
  border:2px solid var(--mint);opacity:.5;animation:vslpulse 2.2s ease-out infinite}
@keyframes vslpulse{0%{transform:scale(1);opacity:.5}70%{transform:scale(1.32);opacity:0}100%{opacity:0}}
.vsl-ov .ring svg{width:28px;height:28px}
.vsl-ov .eye{font-family:var(--font-mono);font-size:10px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--mint);display:inline-flex;align-items:center;gap:7px}
.vsl-ov .eye .blip{width:6px;height:6px;border-radius:50%;background:var(--mint)}
.vsl-ov .main{font-family:var(--font-display);font-weight:600;font-size:20px;color:#fff;line-height:1.12}
.vsl-ov .sub{font-size:12.5px;color:#dcdcd4}
.vsl-end{position:absolute;inset:0;z-index:4;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:14px;text-align:center;padding:20px;
  background:rgba(10,10,10,.86);opacity:0;pointer-events:none;transition:opacity .4s ease}
.vsl-end.show{opacity:1;pointer-events:auto}
.vsl-end .main{font-family:var(--font-display);font-weight:600;font-size:21px;color:#fff;
  line-height:1.15;max-width:20ch}
.vsl-end .k-btn--primary{width:auto}
.vsl-end .replay{background:none;border:none;color:#c8c8c0;font-family:var(--font-mono);
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;cursor:pointer}
@media(prefers-reduced-motion:reduce){.vsl-ov .ring::after{animation:none}}

/* ===========================================================================
   24. THANKS PAGE. Step 2 of 2: the calendar, and nothing competing with it.
   =========================================================================== */
.k-cal{margin-top:22px;border:1px solid var(--line);border-radius:14px;
  background:var(--card);min-height:540px;overflow:hidden}
#cal-inline{min-height:540px}
.k-cal-fallback{padding:50px 18px;text-align:center;color:var(--muted);font-size:15px}
.k-cal-fallback a{color:var(--mint);text-decoration:underline}
.k-step{font-family:var(--font-mono);font-size:10.5px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--muted);line-height:1}

/* ===========================================================================
   25. THE 2026-09-18 COPY. Three new pieces in the fold.

   .k-h1sub   the H2 directly under the H1. Same face, same weight, same
              line-height as every other heading, one step smaller than the H1
              and one larger than a card title, so it reads as the headline's
              second line rather than as a competing heading. Muted a shade
              so the H1 still wins the eye.
   .k-aster   the one line guarantee under the form card. Smallest type on the
              fold, because it is a reassurance, not a claim to read first.
   .k-row--3  the proof band at three tiles instead of four. Two up on a phone
              leaves an orphan, so it is one up until there is room for three.
   =========================================================================== */
/* Sizes tightened 2026-09-18 to hold the fold rule. His fold runs to about 470
   characters above the card (103 in the H1, 85 in the H2, 285 in the sub), so at
   375px the type alone is roughly 380px tall. Rather than drop any of it, the
   three display steps each came down a notch and the gaps closed. Measured
   result is in DESIGN-NOTES. */
.k-h1sub{font-size:17px;color:var(--ink-soft);margin-top:10px;
  max-width:640px;margin-left:auto;margin-right:auto}
.k-hero .k-sub{margin-top:12px;font-size:15.5px;line-height:1.45}
.k-hero h1{font-size:26px}
.k-hero .k-magnet{margin-top:20px}
.k-aster{font-size:11.5px;line-height:1.55;color:var(--muted);margin-top:14px;
  max-width:560px;margin-left:auto;margin-right:auto;text-align:left}

.k-stats .k-row--3{grid-template-columns:1fr}
.k-stats .k-row--3 .k-stat{border-right:0;border-bottom:1px solid var(--line-dark)}
.k-stats .k-row--3 .k-stat:last-child{border-bottom:0}

@media(min-width:600px){
  .k-h1sub{font-size:21px;margin-top:12px}
  .k-hero h1{font-size:42px}
  .k-hero .k-sub{font-size:16.5px;line-height:1.5}
  .k-aster{font-size:12px}
  .k-stats .k-row--3{grid-template-columns:repeat(3,1fr)}
  .k-stats .k-row--3 .k-stat{border-bottom:0;
    border-right:1px solid var(--line-dark)}
  .k-stats .k-row--3 .k-stat:last-child{border-right:0}
}
@media(min-width:1000px){
  .k-h1sub{font-size:24px}
  .k-hero h1{font-size:46px}
  .k-hero .k-sub{font-size:17.5px}
}

/* ===========================================================================
   26. HERO WIDTH AND THE SUPPORT LINE. 2026-09-18, from Angus's annotated
   screenshot at about 1900px: the H1 was wrapping to four lines and the H2 to
   three inside a 720px column, with dead space either side, and the H2 was
   close enough in size to the H1 to read as a second headline.

   THE H1 BOX IS SIZED TO THE SENTENCE, not to a grid. His H1 is two sentences
   of roughly 50 characters each ("We use AI to scale the s#! out of your
   Facebook ads." and "Or you get your money back, no questions asked."), so
   the natural setting is one sentence per line. At 52px in the serif that
   needs about 1300px of measure, which is why the hero gets its own wider wrap
   past 1600 instead of inheriting the 1180px page container.
       1000 to 1599   H1 box 1100px at 52px  ->  three lines
       1600 and up    H1 box 1340px at 52px  ->  two lines, one sentence each
   Sub and support line stay at 820px, because a 1300px measure is unreadable
   for body copy. The form card does not move: 560px at every width.

   THE SUPPORT LINE drops a full step below the H1 and loses the display
   weight. This is the one deliberate exception to the "one face, one weight"
   rule in section 2: a 600 weight serif at 24px next to a 600 weight serif at
   52px still reads as a second headline, and dropping to 400 plus a muted
   colour is what makes it read as support. Colour is checked, not guessed:
   6.55:1 on dark and 8.26:1 on light, both clear of AA.
   =========================================================================== */
:root{--h1sub-ink:#4f524c}
:root[data-theme="dark"]{--h1sub-ink:#c0bab0}

/* the treatment rule `body.h-serif h1,h2,.k-display-sm` sets weight 600 and
   out-specifies a bare class, so the support line has to match its specificity
   to drop to 400. Measured before this line: the H2 was still rendering at 600. */
body.h-serif h2.k-h1sub{font-weight:400}
.k-h1sub{color:var(--h1sub-ink);font-size:20px}

@media(min-width:600px){
  .k-h1sub{font-size:21px}
}

@media(min-width:1000px){
  .k-hero .k-hero-col{max-width:1100px}
  .k-hero h1{font-size:52px;max-width:1100px}
  .k-h1sub{font-size:24px;max-width:820px}
  .k-hero .k-sub{max-width:820px}
  .k-magnet,.k-aster{max-width:560px}
}

@media(min-width:1600px){
  /* the hero, and only the hero, breaks out of the 1180px page container so the
     headline can hold one sentence per line */
  .k-hero .k-wrap{max-width:1420px}
  .k-hero .k-hero-col{max-width:1340px}
  .k-hero h1{max-width:1340px}
  .k-h1sub,.k-hero .k-sub{max-width:860px}
}

/* "Or you" is held together only where the headline sets as one sentence per
   line. Below 1600 the break can fall wherever it fits: binding it there cost a
   whole line at 375 and pushed the form card 29px further down the fold. */
@media(min-width:1600px){ .k-nb{white-space:nowrap} }

/* ===========================================================================
   27. THE 2026-09-18 WALKTHROUGH. Angus's 20 points against the live page.
   Five pieces need CSS; the other fifteen were copy or order.

     .k-btn--nav        point 2. The nav action is the mint pill now, at nav
                        size. It was the only CTA on the page that did not look
                        like a CTA. Same fill, same --on-mint ink, so it needs
                        no theme rule: the mint never changes.
     (the .k-magnet--bare rule of points 3 and 5 is deleted: the chrome strip
                        came back on 2026-09-18, so the card body no longer owns the top
                        of the card and needs none of that padding.)
     .k-row--4          point 7. The proof band at four tiles, one wide over
                        three. Stacked on a phone: THREE UP DOES NOT FIT at 375.
                        Measured, not guessed, see the note on .k-n--long.
     .k-marquee         point 8, as re-cut on 2026-09-18: a continuous full bleed
                        marquee on one keyframe. It replaced the scroll-snap
                        scroller and its two arrow buttons, and with them the only
                        script this page owned.
     .k-ticks--lead     point 11. The three bullets folded into the week section
                        between the two video slots.
   =========================================================================== */

/* --- point 2: the nav pill ------------------------------------------------ */
.k-btn--nav{background:var(--mint);color:var(--on-mint);padding:9px 13px;
  font-size:12.5px;font-weight:600;border-radius:99px;
  box-shadow:0 4px 12px rgba(31,184,102,.24)}
.k-btn--nav:active{background:var(--mint-soft)}
@media(min-width:600px){.k-btn--nav{font-size:14px;padding:10px 18px}}

/* --- point 7: four proof tiles, one wide over three ----------------------- */
/* MOBILE IS STACKED, and that is a measured call, not a preference. Three up at
   375 gives each tile 86px of content box. Tile 4's number, "$0 to $135k a
   year", is 18 characters: it needs 176px at the smallest size the band's type
   scale allows, so a 3-up row either wraps the number or clips it, and his rule
   is that the numbers do not wrap. Both layouts were built and measured before
   this one shipped. */
.k-stats .k-row--4{grid-template-columns:1fr}
.k-stats .k-row--4 .k-stat{border-right:0;border-bottom:1px solid var(--line-dark)}
.k-stats .k-row--4 .k-stat:last-child{border-bottom:0}
/* the long number gets its own step on the same scale: same face, same weight,
   same line-height, size only, so it sets on one line at every width */
.k-stat .k-n--long{font-size:25px}
/* tile 4 names one brand rather than the account, so its caption sits in the
   muted grey. 7.87:1 on the dark band, the same value the footer colophon uses. */
.k-stat .k-d--muted{color:#a9a69d}
@media(min-width:600px){
  .k-stats .k-row--4{grid-template-columns:repeat(3,1fr)}
  .k-stats .k-row--4 .k-stat{border-bottom:0;border-right:1px solid var(--line-dark)}
  .k-stats .k-row--4 .k-stat:last-child{border-right:0}
  /* the wide tile spans the row and keeps the hairline under it. This rule sits
     AFTER the generic one on purpose: the generic one zeroes the bottom border. */
  .k-stats .k-row--4 .k-stat--wide{grid-column:1 / -1;border-right:0;
    border-bottom:1px solid var(--line-dark)}
  .k-stat .k-n--long{font-size:28px}
}
@media(min-width:1000px){
  .k-stat .k-n--long{font-size:34px}
}

/* --- point 8, SECOND PASS: the ads marquee -------------------------------- */
/* Angus, 2026-09-18: "make it auto-scroll across the full width of the screen, not
   this arrow bullshit, a continuous, infinite scroll." So the scroll-snap track and
   its two buttons are gone and this is one keyframe, no script at all.

   HOW THE SEAM IS INVISIBLE. The track holds the eight cards TWICE and translates
   from 0 to -50%, so at the end of a pass the second copy sits exactly where the
   first began. That only holds if half the track is exactly one copy's advance,
   which is why the gap is a MARGIN ON EACH CARD and not `gap` on the flex track:
   with `gap`, sixteen cards carry fifteen gaps, half of that is 7.5 gaps, and
   the loop jumps half a gutter every pass.

   SPEED. One pass moves eight cards plus eight gutters, so at 48s a card slot passes
   a fixed point every 6.0s on desktop. A phone card is narrower, so the same
   duration reads slower there, which is what a small screen wants.

   WIDTH. The strip is a block child of the band, so it is already exactly as wide
   as the viewport and no 100vw breakout is needed (100vw would include the
   scrollbar and put a sideways scroll on the page). It clips its own overflow, and
   .k-band clips again.
   Pause on hover only where a pointer can hover. Under reduced motion the animation
   stops and the strip becomes an ordinary scrollable row. */
.k-marquee{position:relative;margin-top:24px;overflow:hidden}
.k-marquee-track{display:flex;width:max-content;
  animation:k-marquee 48s linear infinite;
  will-change:transform;backface-visibility:hidden}
/* EACH CARD IS ITS OWN COMPOSITING LAYER, 2026-09-19. Angus, on the iPad: "when one image
   has to disappear, part of the next image also disappears". The track is one ~4,700px
   layer under a transform animation, and WebKit tiles a layer that size: a tile whose
   edge falls mid-card gets culled at the clip while the card is still half on screen, so
   the card leaving takes a slice of the page with it. Promoting each card gives WebKit a
   280px layer per card, well inside one tile, so nothing is culled mid-image. Chromium
   was never affected; this costs it nothing. */
.k-marquee-track > .k-ad{flex:0 0 66vw;margin-right:12px;min-width:0;transform:translateZ(0)}
@keyframes k-marquee{
  from{transform:translate3d(0,0,0)}
  to  {transform:translate3d(-50%,0,0)}
}
@media(hover:hover){
  .k-marquee:hover .k-marquee-track{animation-play-state:paused}
}
@media(min-width:600px){
  .k-marquee-track > .k-ad{flex-basis:280px;margin-right:16px}
}
@media(prefers-reduced-motion:reduce){
  .k-marquee{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .k-marquee::-webkit-scrollbar{display:none}
  .k-marquee-track{animation:none}
}

/* --- point 11: the three bullets, folded into the week section ------------ */
.k-ticks--lead{margin-top:24px}
.k-ticks--lead + .k-panel--video{margin-top:24px}

/* ===========================================================================
   28. GRAIN ON EVERY BAND, 2026-09-18. Angus: the hero is right, but the
   sections below it sit on a flat grey ground with the grid and no grain and
   no motion, and they read as panels. The target is how
   /koyeb/backgrounds-2.html paints a section.

   THE PROBLEM, precisely. The page grain is ONE fixed film at z-index 0,
   BEHIND everything (section 1). A transparent band lets it through and looks
   alive. A band that paints its own background covers it dead: the dark proof
   band, the two tint bands, the guarantee card, the offer band and the footer.
   Six flat panels on a grainy page.

   THE FIX. Every opaque surface gets its own grain layer, painted ABOVE its
   own background and BELOW its copy, which is the paint order backgrounds-2
   uses per section:

       z 0   the band's own background colour
       z 1   the grain, and above it the hairline grid (.k-grid::after)
       z 2   the copy

   Only surfaces that paint a background get one. A transparent band already
   shows the fixed film, and giving it a second layer would double the grain.

   THREE THINGS THAT MATTER AND ARE NOT OBVIOUS:

   1. TEXT IS NEVER UNDER THE FILM. The band layer is a ::before, so it paints
      before the band's children, and .k-band > * is z-index 2 anyway. What
      changes is what the type SITS ON, not what covers it, so the contrast
      table was recomputed against the composited ground (band colour at 90%
      plus the grain particle at 10%), not against the flat band colour.
   2. THE OVERSIZE IS 32px, NOT 160%. The fixed film is 160% because it covers
      the viewport and the k-boil10 offsets are relative to a huge layer. A
      band layer only has to cover its own box plus the largest jump in the
      keyframes, which is 19px. 32px of bleed on each edge costs a band sized
      layer instead of a page sized one, and .k-band{overflow:hidden} clips it.
   3. THE GRID STAYS EXACTLY AS IT WAS. Same alpha, same 64px pitch, same mask.
      .k-grid::after is still z-index 1 and still paints after ::before, so the
      lattice sits on top of the speckle. backgrounds-2 puts the ground under
      the grain and notes the two orders are indistinguishable at 0.10.
   =========================================================================== */

/* TWO SURFACES, NOT ONE, since 2026-09-18. Angus reviewed the all grain page and
   split it three ways: the proof band takes NO texture ("get rid of the grain
   there"), the tint bands, the guarantee card and the footer take a PAPER look
   ("similar dark colour but a paper look"), and the closing offer band keeps the
   grain, so the page ends the way the hero starts. The band GROUNDS do not move:
   only what is painted on them does.

   1. THE GRAIN, unchanged, on the dark bands. .k-offer is the one that still
      wears it; .k-stats opts out on the next rule. */
.k-band--dark::before{
  content:"";position:absolute;top:-32px;right:-32px;bottom:-32px;left:-32px;
  z-index:1;pointer-events:none;
  background-repeat:repeat;
  background-size:var(--tile,64px) var(--tile,64px);
  background-image:var(--grain-img);
  opacity:var(--grain-opacity,.10);
  backface-visibility:hidden;
  animation:k-boil10 1s steps(1,end) infinite}
@media(prefers-reduced-motion:reduce){
  .k-band--dark::before{animation:none}
}

/* 2. THE PROOF BAND: flat dark, the way it was before the grain pass. The four
      tiles are untouched. `content:none` rather than `display:none`, so the
      pseudo element is never generated at all. */
.k-stats::before{content:none}

/* 3. THE PAPER SURFACE, on the two tint bands, the guarantee card and the footer.
      Same z-index, same inset, same pointer-events as the grain layer, so the
      paint order is identical (band colour, texture, hairline grid, copy) and the
      copy still sits above it. NO ANIMATION: paper does not boil. */
.k-band--tint::before,
.k-footer::before,
.k-risk::before{
  content:"";position:absolute;top:-32px;right:-32px;bottom:-32px;left:-32px;
  z-index:1;pointer-events:none;
  background-repeat:repeat;
  background-size:720px 720px;
  background-image:var(--mottle-img);
  opacity:var(--mottle-op)}

/* THE FOOTER is not a .k-band, so it carries none of that machinery: it needs
   its own stacking context, its own clip, and its content lifted above the
   film. */
.k-footer{position:relative;isolation:isolate;overflow:hidden}
.k-footer > .k-wrap{position:relative;z-index:2}

/* THE GUARANTEE CARD used ::before for its internal 48px lattice, and ::after
   would have painted the grain OVER the copy, because an ::after is the last
   child in the positioned layer. So the lattice moved onto the element's own
   background, where it belongs (it is part of the card's ground, not an
   overlay), and ::before is now free to carry the grain under the text.
   background-COLOR plus background-IMAGE, never the `background` shorthand:
   the shorthand would reset repeat, position and size. */
.k-risk{background-color:var(--dark);
  background-image:
    repeating-linear-gradient(to right,var(--line-dark-soft) 0 1px,transparent 1px 48px),
    repeating-linear-gradient(to bottom,var(--line-dark-soft) 0 1px,transparent 1px 48px);
  isolation:isolate}
.k-risk > *{z-index:2}

/* Surfaces that deliberately stay ABOVE the film, unchanged from the hero:
   the form card and its fields, because they are the one thing people type
   into, and the ad cards, because noise over a client's creative is noise on
   the product. They are opaque children at z-index 2, so they already sit on
   top; this is a note, not a rule that needs writing. */

/* ===========================================================================
   29. ABOUT THE OPERATOR, 2026-09-19 (Angus: "we had an about Angus section on the
   old landing page with a photo of me, find that and implement it, showing a face
   adds something"). The block is the archived RunArcOS GEO page's About section
   (Creazionz/AIOS-Personal, projects/ArcOS/RunArcOS/site/lp-build/geo/index.html):
   photo with a mono caption, eyebrow, heading, two stats, three paragraphs. Same
   photo file (720x900). Restyled to this page: the card hairline and radius on the
   photo, the two stats as .k-card tiles, everything left aligned inside the narrow
   column because the photo sits beside the copy from 600px up.
   =========================================================================== */
.k-about{display:grid;gap:22px;margin-top:24px;align-items:start;max-width:960px;margin-left:auto;margin-right:auto}
.k-about-photo{max-width:300px}
.k-about-photo img{width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;display:block;
  border-radius:14px;border:1px solid var(--line);background:#efece3}
.k-about-cap{font-family:var(--font-mono);font-size:9.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--muted);margin-top:10px;line-height:1.6}
.k-about-copy h2{margin-top:14px}
.k-about-copy p{font-size:16px;line-height:1.6;color:var(--ink-soft);max-width:58ch;margin-top:14px}
.k-about-copy p b{color:var(--ink);font-weight:600}
.k-about-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-top:20px}
.k-about-stats .k-card{padding:16px 16px 14px}
.k-about-stats .k-n{font-family:var(--font-display);font-weight:600;font-size:26px;line-height:1.1;overflow-wrap:anywhere;
  letter-spacing:normal;color:var(--ink)}
.k-about-stats .k-d{font-size:12.5px;line-height:1.4;color:var(--muted);margin-top:6px}
/* Two columns only from 760px. MEASURED 2026-09-19 on Angus's iPad: at 600 to 744px the copy
   column kept a 371px minimum (two nowrap stat numbers side by side) and ran off the right
   edge of the screen. Below 760 the photo sits above the copy; the columns are minmax(0, ...)
   so nothing inside can ever widen them again. */
@media(min-width:760px){
  .k-about{grid-template-columns:minmax(0,300px) minmax(0,1fr);gap:44px}
  .k-about-stats .k-n{font-size:30px}
}
