/* ============================================================================
   site.css — component layer for the oomny website design system.
   Consumes the tokens from tokens.css + colours from theme.css. Because every
   rule reads CSS custom properties, dark mode is automatic: theme.css remaps
   the tokens under prefers-color-scheme / [data-theme] and this file follows.

   Two audiences of selectors:
     1. NEW design-system components (.hero-grid, .float-chip, .proof, …)
        — the professionalized hero/proof/manifesto compositions.
     2. EXISTING content-page classes (.steps, .benefit, .compare, .faq,
        .trust-list, .packet, .prose-page) used by the already-translated
        features/faq/support/privacy/terms pages — restyled in place so the
        whole multi-locale site is professionalized without rewriting content.

   Duplicated per site by design (monorepo rule forbids shared app code);
   sync-check.sh enforces these three CSS files stay identical to the template.

   2026-06 refinement pass: typography now reads the leading (--lh-*) and
   display-tracking tokens; the device frame got a refined titanium bezel, a
   floating camera pill, a glass reflection and the unified --shadow-3; chips
   are quieter (--shadow-2); cards/pricing read --radius-lg + --shadow-* so a
   brand's squareness/elevation cascades; motion reads --dur-* and adds a
   motion-safe "phone settles upright on hover". No colour added here (C4).
   ============================================================================ */

*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{margin:0;font-family:var(--sans);font-size:var(--step-0);line-height:var(--lh-body);
  color:var(--text);background:var(--bg);
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  transition:background var(--dur-2) var(--ease), color var(--dur-2) var(--ease)}
h1,h2,h3{font-family:var(--display);line-height:var(--lh-tight);letter-spacing:var(--display-tracking);margin:0;text-wrap:balance}
h1{font-size:var(--step-4);font-weight:680}
h2{font-size:var(--step-3);font-weight:660}
h3{font-size:var(--step-1);font-weight:640;letter-spacing:calc(var(--display-tracking) + .006em)}
p{margin:0}
a{color:inherit}
img{max-width:100%;height:auto}
:is(a,button,summary,input):focus-visible{outline:2px solid var(--accent-text);outline-offset:3px;border-radius:4px}

/* ---- shared shell: nav, wrap, buttons ---- */
.wrap{max-width:72rem;margin-inline:auto;padding-inline:clamp(1.1rem,4vw,2.5rem)}
main{max-width:72rem;margin-inline:auto;padding-inline:clamp(1.1rem,4vw,2.5rem)}

.nav{display:flex;align-items:center;gap:1.4rem;padding-block:1.3rem;flex-wrap:wrap}
.nav .brand,.brand{display:inline-flex;align-items:center;gap:.55rem;font-weight:700;
  font-size:var(--step-1);font-family:var(--display);letter-spacing:-.02em;text-decoration:none;color:var(--text)}
.brand>img{width:30px;height:30px;border-radius:7px;object-fit:cover}
.brand .dot{width:1.6rem;height:1.6rem;border-radius:7px;background:var(--accent);display:grid;place-items:center;
  color:var(--accent-ink);font:800 .9rem/1 var(--sans)}
.nav nav{margin-left:auto;display:flex;gap:1.3rem;align-items:center;flex-wrap:wrap;
  font-size:var(--step--1);font-weight:560}
.nav nav a{text-decoration:none;color:var(--text-soft);transition:color .15s}
.nav nav a:hover{color:var(--text)}

/* ---- informational app-language flag row (the hero {{appLanguages}} block) ----
   Markup the build emits (appLanguagesRow in build.mjs):
     <div class="app-langs" role="group" aria-label="App languages">
       <span class="app-langs-lead">Speaks your language</span>
       <span class="app-langs-flags">
         <span class="lang-flag" role="img" lang="en" aria-label="English" title="English"><svg…flag…></svg></span> …
       </span>
       <span class="app-langs-note">Available in 6 languages on the App Store</span>
     </div>
   The marketing site is English-only (owner decision 2026-06-26); this row is INFORMATION, not
   navigation — it tells the visitor which languages the APP speaks (its in-app UI + App Store
   listing, read from app.toml [app] locales). Nothing is clickable: bare flags (role="img"), a quiet
   lead line, and the count note. Lives in the hero copy column under the price note. The flags are
   static (no hover/active state — there is nothing to select); the row stacks lead → flags → note. */
.app-langs{margin-top:1.1rem;display:flex;flex-direction:column;gap:.4rem;align-items:flex-start}
.app-langs-lead{font-size:var(--step--1);font-weight:620;color:var(--text);letter-spacing:-.01em}
.app-langs-flags{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.lang-flag{display:block;width:22px;height:15px;border-radius:3px;overflow:hidden;line-height:0;
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--text) 16%,transparent)}
.lang-flag svg,.lang-flag img{width:100%;height:100%;display:block;object-fit:cover}
.app-langs-note{font-size:var(--step--1);color:var(--text-soft)}

.btn,.btn.primary{display:inline-flex;align-items:center;gap:.5rem;font:650 var(--step-0)/1 var(--sans);
  padding:.9rem 1.5rem;border-radius:var(--radius-pill);text-decoration:none;cursor:pointer;border:1px solid transparent;
  transition:transform var(--dur-1) var(--ease-out), box-shadow var(--dur-2), background var(--dur-2), border-color var(--dur-2)}
.btn-primary,.btn.primary{background:var(--accent);color:var(--accent-ink);box-shadow:0 6px 22px -10px var(--accent)}
.btn-primary:hover,.btn.primary:hover{transform:translateY(-2px);box-shadow:0 12px 30px -12px var(--accent)}
.btn-ghost,.btn:not(.primary){background:transparent;color:var(--text);border-color:var(--line)}
.btn-ghost:hover,.btn:not(.primary):hover{border-color:var(--accent-text);color:var(--accent-text)}

/* ---- hero (asymmetric: copy left, device/showcase right, halo atmosphere) ---- */
.hero{position:relative;padding:var(--sp-5) 0 var(--sp-6)}
.hero::before{content:"";position:absolute;inset:0;background:var(--halo);pointer-events:none;z-index:0}
.hero-grid{position:relative;z-index:1;display:grid;grid-template-columns:1.05fr .95fr;gap:var(--sp-5);align-items:center}
.eyebrow{display:inline-flex;align-items:center;gap:.5rem;font-size:var(--step--1);font-weight:680;
  letter-spacing:.06em;text-transform:uppercase;color:var(--accent-text);margin-bottom:var(--sp-3)}
.eyebrow::before{content:"";width:1.6rem;height:1px;background:var(--accent-text)}
.hero h1{margin-bottom:var(--sp-3);max-width:14ch}
.hero .lede,.hero .sub{font-size:var(--step-1);color:var(--text-soft);max-width:36ch;line-height:var(--lh-snug);margin-bottom:var(--sp-4)}
.hero-actions,.cta-row{display:flex;gap:.8rem;flex-wrap:wrap;align-items:center;position:relative;z-index:1}
.note,.hero .fine{font-size:var(--step--1);color:var(--text-soft);margin-top:var(--sp-3)}
.note b,.hero .fine b{color:var(--text);font-weight:650}

/* ---- device frame (real screenshot inside a true-to-spec iPhone/iPad shell) ----
   Geometry adapted from devices.css (picturepan2, MIT) — the proven box-model where the
   FRAME and the SCREEN come from ONE source, so the screen can never drift from the bezel:
   the screen is the frame minus a uniform padding (iPhone 428×868 frame, 19px pad → 390×830
   screen; iPad 560×778 frame, 27px pad → 506×724 screen). Earlier hand-tuned percentage
   bezels (the 2026-06-22 failures) are RETIRED — there is nothing to "align" here, the
   padding does it. The whole device is built at devices.css native px and scaled to its slot
   with one transform:scale(var(--dv-scale)); every pixel — bezel, island, corner, screenshot
   — scales together, so the bezel stays uniform at any size. Colours read theme tokens
   (--frame-rail / --frame-edge) so light + dark track the palette; the screenshot fills
   .device-screen, whose window ratio is DERIVED FROM THE SCREENSHOT so cover never crops. The
   screenshots are GENUINE simulator captures (the in-app marketing host holds a real screen;
   `xcrun simctl io screenshot` grabs the true iOS render — real status bar, Dynamic Island,
   safe area), so they already carry the island and nothing lands under it or in the rounded
   corners. The frame therefore draws NO island of its own (the capture has one) — only the
   titanium rail + side buttons.

   THE NO-CROP INVARIANT (the one rule that makes this robust): the screen-window's height is
   computed from the screenshot's real pixel size (--shot-w / --shot-h, the asset's true WxH in
   px), NOT a hard-coded constant. The window WIDTH is fixed per device (390px iPhone, 506px iPad
   — that sets the frame size); the window HEIGHT follows as width × (shot-h / shot-w), and the
   frame height follows as window height + 2×bezel. When window-ratio === screenshot-ratio,
   object-fit:cover === contain: zero crop, zero letterbox. A page that omits --shot-w/--shot-h
   falls back to the device's native screen ratio. (Two separate unitless numbers, not a division
   token, so every calc() below is unambiguously valid.)

   HTML:  <div class="device dv-iphone" style="--shot-w:1176;--shot-h:2555">
            <div class="device-frame"><img class="device-screen" src="…" alt="…"></div>
            <i class="device-btns"></i><i class="device-power"></i>
          </div>
   (.dv-ipad uses the iPad geometry, no buttons row. A .device may carry .dv-landscape. Always
   pass --shot-w/--shot-h as the asset's real px WxH — the screenshot-ratio-gate enforces the
   served PNG matches the device's native ratio so the frame stays true.) */
.showcase{display:grid;place-items:center;position:relative}

/* scale wrapper: the .device is a footprint box sized to the SCALED frame (calc on width/height
   below); an inner .device-frame is the native-px frame, scaled to fill the box with a single
   transform anchored top-left so it never overflows. Rotation/hover live on .device (one
   transform layer, no nesting conflicts). One source of truth for size: --dv-scale. */
.device{position:relative;display:block;transform:rotate(var(--rot));transition:transform .45s var(--ease)}
.device .device-frame{position:absolute;top:0;left:0;box-sizing:border-box;
  transform:scale(var(--dv-scale,1));transform-origin:top left}
.device .device-screen{display:block;background:#000;object-fit:cover;object-position:top center;
  width:100%;height:100%}

/* iPhone 14 Pro — 19px bezel, 390px screen WIDTH. The screen HEIGHT is derived from the
   screenshot px size (--shot-w/--shot-h), so the frame stretches to the capture and never crops
   it. The native-ratio fallback (--shot-w/--shot-h = 390/830 → ratio 0.4699) gives an 830px
   window, 868px frame. --dv-sw/--dv-sh are the screen window's px size; the frame adds 2×bezel;
   the .device footprint matches the frame. The screenshot is a REAL device capture (simctl io)
   carrying the iOS status bar + Dynamic Island, so the frame draws NO island of its own. */
.dv-iphone{--dv-bezel:19px;--dv-bezel-v:28px;--dv-sw:390;--shot-w:390;--shot-h:830;
  --dv-sh:calc(var(--dv-sw) * var(--shot-h) / var(--shot-w));
  --dv-fw:calc((var(--dv-sw) * 1px) + 2 * var(--dv-bezel) + 2px);
  --dv-fh:calc((var(--dv-sh) * 1px) + 2 * var(--dv-bezel-v) + 2px);
  width:calc(var(--dv-fw) * var(--dv-scale,1));height:calc(var(--dv-fh) * var(--dv-scale,1))}
/* Bezels are CALIBRATED to the real iPhone 16 Pro Max at the 390px screen-width scale
   (5.524 px/mm): 3.5mm side bezels = 19px, 5.15mm top/bottom = 28px, device corner 14mm =
   77px, screen corner 10mm = 55px. A uniform 19px bezel made the phone visibly stubbier
   than hardware (frame 0.483 vs the real 0.476) — the owner read it as "screen size not in
   line with the content". The +2px in --dv-fw/--dv-fh is the 1px frame border on each side,
   so the screen window stays EXACTLY --dv-sw wide and object-fit:cover crops zero pixels. */
.dv-iphone .device-frame{width:var(--dv-fw);height:var(--dv-fh);
  padding:var(--dv-bezel-v) var(--dv-bezel);border-radius:77px;
  background:var(--frame-edge,#0a0a0c);border:1px solid var(--frame-rim,#1b1721);
  box-shadow:0 0 0 1px var(--frame-halo,transparent), var(--shadow-3), inset 0 0 4px 2px var(--frame-rail,#c0b7cd), inset 0 0 0 6px var(--frame-edge2,#342c3f)}
.dv-iphone .device-screen{border-radius:55px}
/* film-in-frame: an OPTIONAL <video class="device-screen-video"> AFTER the img plays a
   walkthrough/act clip inside the same screen window (build.mjs injectShotVars already
   recognises the video and skips the drawn status bar — the film is a real screen recording
   carrying its own). The img beneath stays as the ratio measurement source and the
   reduced-motion/loading fallback. Absolute over the frame's content box (offset = the bezel
   padding), sized to the derived screen window, same radius as .device-screen. iPhone-only
   until a tablet film lane exists. */
.dv-iphone .device-screen-video{position:absolute;top:var(--dv-bezel-v);left:var(--dv-bezel);
  width:calc(var(--dv-sw) * 1px);height:calc(var(--dv-sh) * 1px);
  object-fit:cover;object-position:top center;background:#000;display:block;border-radius:55px}

/* Status-bar + Dynamic Island, DRAWN by the frame — only on .dv-bare (an asset captured WITHOUT
   an iOS status bar; build.mjs detects this by decoding the asset's top strip and tags the block
   .dv-bare). An asset that ALREADY carries a 9:41 bar + island (most hero captures) is NOT .dv-bare,
   so the frame draws nothing and there is no double bar. The .device-chrome lives INSIDE
   .device-frame (which has the bezel as padding), positioned to the screen content-box top-left
   (inset = the bezel padding) and scaled with the frame via the .device transform, so 9:41, the
   pill and the signal/wifi/battery glyphs always sit where iOS puts them — the buyer never sees a
   blank top strip. Colours adapt: a light screenshot gets dark glyphs, dark gets light (build.mjs's
   darkBg sample sets .dv-bare-dark to flip the glyph colour). */
.dv-iphone.dv-bare .device-chrome{position:absolute;left:var(--dv-bezel);right:var(--dv-bezel);
  top:var(--dv-bezel-v);height:54px;z-index:3;pointer-events:none;
  border-radius:55px 55px 0 0;
  font:600 17px/1 var(--font-sans,-apple-system,system-ui,sans-serif);color:var(--dv-chrome-ink,#000)}
.dv-iphone.dv-bare.dv-bare-dark .device-chrome{--dv-chrome-ink:#fff}
.dv-iphone.dv-bare .device-chrome .dv-time{position:absolute;left:34px;top:16px;letter-spacing:.5px;font-weight:600}
.dv-iphone.dv-bare .device-chrome .dv-island{position:absolute;left:50%;top:14px;transform:translateX(-50%);
  width:125px;height:37px;border-radius:19px;background:#000}
.dv-iphone.dv-bare .device-chrome .dv-stat{position:absolute;right:30px;top:19px;display:flex;align-items:center;gap:6px}
/* real Apple-shaped status glyphs (Bootstrap Icons, MIT) as inline SVG; currentColor follows
   --dv-chrome-ink. viewBox is 0 0 16 16 on all three; height sets the on-screen size, width:auto
   keeps each glyph's own aspect (signal/wifi ~square, battery wider). */
.dv-iphone.dv-bare .device-chrome .dv-ic{display:block;height:12px;width:auto;color:inherit}
.dv-iphone.dv-bare .device-chrome .dv-signal{height:11px}
.dv-iphone.dv-bare .device-chrome .dv-wifi{height:12px}
.dv-iphone.dv-bare .device-chrome .dv-batt{height:13px}
/* THE SIDE BUTTONS SCALE BY HAND — they are SIBLINGS of .device-frame, so the frame's
   transform:scale() never reaches them (a transform only scales descendants). Every native-px
   value below is therefore multiplied by --dv-scale explicitly. Leaving one unscaled is not a
   rounding error: at --dv-scale:.19 (aida's sync-section iPhone) the unscaled power button ran
   from 116% to 174% of the device height — a black bar hanging BELOW the phone, which is the
   2026-08-10 owner report. The rail geometry itself is unchanged: at scale 1 these calcs are
   identical to the constants they replace. */
.dv-iphone .device-btns{position:absolute;left:calc(-2px * var(--dv-scale,1));
  top:calc(115px * var(--dv-scale,1));
  width:calc(3px * var(--dv-scale,1));height:calc(32px * var(--dv-scale,1));border-radius:2px;
  background:var(--frame-rim,#1b1721);z-index:2;
  box-shadow:0 calc(45px * var(--dv-scale,1)) var(--frame-rim,#1b1721),
             0 calc(125px * var(--dv-scale,1)) var(--frame-rim,#1b1721) /* vol-up, vol-down */}
.dv-iphone .device-power{position:absolute;right:calc(-2px * var(--dv-scale,1));
  top:calc(200px * var(--dv-scale,1));
  width:calc(3px * var(--dv-scale,1));height:calc(100px * var(--dv-scale,1));border-radius:2px;
  background:var(--frame-rim,#1b1721);z-index:2}

/* iPad Pro — 27px bezel, 506px screen WIDTH; screen HEIGHT from --shot-w/--shot-h (native
   fallback 506/724 → a 724px window, 778px frame). No island (camera dot). */
.dv-ipad{--dv-bezel:20px;--dv-bezel-v:16px;--dv-sw:506;--shot-w:506;--shot-h:724;
  --dv-sh:calc(var(--dv-sw) * var(--shot-h) / var(--shot-w));
  --dv-fw:calc((var(--dv-sw) * 1px) + 2 * var(--dv-bezel));
  --dv-fh:calc((var(--dv-sh) * 1px) + 2 * var(--dv-bezel-v));
  width:calc(var(--dv-fw) * var(--dv-scale,1));height:calc(var(--dv-fh) * var(--dv-scale,1))}
/* Calibrated to the real iPad Pro 13 at the 506px screen-width scale (1.9116 px/mm):
   10.5mm side bezels = 20px, 8.5mm top/bottom = 16px — the old uniform 27px squared the
   whole device off (frame 1.292 vs the real 1.326 in landscape, the owner's "aspect ratio
   klopt niet"). Landscape swaps the two below. */
.dv-ipad .device-frame{width:var(--dv-fw);height:var(--dv-fh);
  padding:var(--dv-bezel-v) var(--dv-bezel);border-radius:36px;
  background:var(--frame-edge,#0d0d0d);
  box-shadow:0 0 0 1px var(--frame-halo,transparent), var(--shadow-3), inset 0 0 0 1px var(--frame-rail,#babdbf), inset 0 0 1px 3px var(--frame-edge2,#e2e3e4)}
.dv-ipad .device-screen{border-radius:11px;border:2px solid #121212}
.dv-ipad .device-power{position:absolute;right:calc(-2px * var(--dv-scale,1));
  top:calc(63px * var(--dv-scale,1));
  width:calc(2px * var(--dv-scale,1));height:calc(32px * var(--dv-scale,1));border-radius:2px;
  background:var(--frame-rail,#babdbf);z-index:2}

/* landscape: a real LYING-DOWN device — the screen WIDTH becomes the long edge (830px iPhone,
   724px iPad) and the HEIGHT is derived from --shot-ar (a true landscape capture has ar>1, e.g.
   2555/1176=2.172, so height = 830/2.172 = 382px → a wide window, no crop). The border-radii/
   island/buttons move to a landscape position. The screenshot is ALREADY a true landscape capture
   (the --screenshot-landscape pipeline writes an upright wide PNG), so it drops straight into the
   wide screen with object-fit:cover — NO rotation transform anywhere (an earlier rotate()
   double-rotated the already-correct capture). The capture's own status bar + island sit on the
   left edge (landscapeRight); the frame just swaps its button/power positions. */
.dv-landscape.dv-iphone{--dv-sw:830;--shot-w:830;--shot-h:390;--dv-bezel:28px;--dv-bezel-v:19px}
.dv-landscape.dv-iphone .device-btns{left:calc(115px * var(--dv-scale,1));
  top:calc(-2px * var(--dv-scale,1));
  width:calc(32px * var(--dv-scale,1));height:calc(3px * var(--dv-scale,1));
  box-shadow:calc(45px * var(--dv-scale,1)) 0 var(--frame-rim,#1b1721),
             calc(125px * var(--dv-scale,1)) 0 var(--frame-rim,#1b1721)}
.dv-landscape.dv-iphone .device-power{top:calc(-2px * var(--dv-scale,1));
  left:calc(200px * var(--dv-scale,1));right:auto;
  width:calc(100px * var(--dv-scale,1));height:calc(3px * var(--dv-scale,1))}
.dv-landscape.dv-ipad{--dv-sw:724;--shot-w:724;--shot-h:506;--dv-bezel:16px;--dv-bezel-v:20px}
.dv-landscape.dv-ipad .device-power{top:calc(-2px * var(--dv-scale,1));
  left:calc(63px * var(--dv-scale,1));right:auto;
  width:calc(32px * var(--dv-scale,1));height:calc(2px * var(--dv-scale,1))}

/* Mac — the WINDOW is the device. The asset is a genuine `screencapture -o -l <windowNumber>`
   window capture (real AppKit title bar, traffic lights, toolbar, alpha-rounded corners, no
   baked shadow), so this frame adds NO bezel of its own: any drawn chassis would fake chrome
   the asset already carries for real. All the frame contributes is the floating-window shadow
   macOS itself draws — filter:drop-shadow, never box-shadow, so the shadow follows the alpha
   corners instead of a rectangle. Screen WIDTH 720px at native scale; HEIGHT derived from the
   asset (--shot-w/--shot-h; 16:10 fallback 720/450), same no-crop invariant as the others.
   No .device-btns / .device-power siblings — a window has neither. */
.dv-mac{--dv-bezel:0px;--dv-bezel-v:0px;--dv-sw:720;--shot-w:720;--shot-h:450;
  --dv-sh:calc(var(--dv-sw) * var(--shot-h) / var(--shot-w));
  --dv-fw:calc(var(--dv-sw) * 1px);
  --dv-fh:calc(var(--dv-sh) * 1px);
  width:calc(var(--dv-fw) * var(--dv-scale,1));height:calc(var(--dv-fh) * var(--dv-scale,1))}
.dv-mac .device-frame{width:var(--dv-fw);height:var(--dv-fh);padding:0;border-radius:10px;
  background:transparent;
  filter:drop-shadow(0 24px 48px rgba(15,12,10,.26)) drop-shadow(0 6px 14px rgba(15,12,10,.14))}
.dv-mac .device-screen{background:transparent;object-fit:cover;border-radius:10px}

/* theme tokens for the frame metal — neutral titanium that reads on paper + on dark.
   --frame-edge is the device body; in dark mode it must NOT collapse into a near-black section
   background, so it's a visible dark titanium (#16161a) with the inset rail highlight carrying
   the metal sheen. --frame-halo is a faint outer ring that lifts the body off any background. */
:root{--frame-rail:#cfd2d8;--frame-edge:#0c0c0f;--frame-edge2:#2a2a30;--frame-rim:#3a3a42;--frame-halo:rgba(0,0,0,.10)}
[data-theme="dark"]{--frame-rail:#7a7e88;--frame-edge:#16161a;--frame-edge2:#26262e;--frame-rim:#34343c;--frame-halo:rgba(255,255,255,.14)}
@media (prefers-color-scheme:dark){:root:not([data-theme]){--frame-rail:#7a7e88;--frame-edge:#16161a;--frame-edge2:#26262e;--frame-rim:#34343c;--frame-halo:rgba(255,255,255,.14)}}

.float-chip{position:absolute;background:var(--chip-bg);border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:.7rem .9rem;display:flex;gap:.6rem;align-items:center;font-size:var(--step--1);font-weight:600;
  box-shadow:var(--shadow-2), inset 0 1px 0 rgba(255,255,255,.05);white-space:nowrap;z-index:3}
.float-chip .ic{width:1.9rem;height:1.9rem;border-radius:8px;display:grid;place-items:center;flex:none;
  background:color-mix(in srgb,var(--accent) 16%,transparent);color:var(--accent-text);font-size:1rem}
.chip-a{top:-3%;left:-7%} .chip-b{bottom:8%;right:-8%}
.chip-b .ic{background:color-mix(in srgb,var(--verified,var(--accent)) 18%,transparent);color:var(--verified,var(--accent-text))}

/* dark mode: dim the (light) screenshot so it doesn't glare, and ring it */
[data-theme="dark"] .phone .scrim{background:linear-gradient(180deg,#0000 55%, #00000026);box-shadow:inset 0 0 0 1px #ffffff1f}
[data-theme="dark"] .phone img{filter:brightness(.95) contrast(.98)}
@media (prefers-color-scheme:dark){
  :root:not([data-theme]) .phone .scrim{background:linear-gradient(180deg,#0000 55%, #00000026);box-shadow:inset 0 0 0 1px #ffffff1f}
  :root:not([data-theme]) .phone img{filter:brightness(.95) contrast(.98)}
}

/* ---- hero atmosphere: an optional full-bleed lifestyle photo behind the hero ----
   Opt-in per site: add class `has-atmos` to the .hero and set --atmos to a url() in
   the page (or layout) — the template ships NO image, so a site without one renders
   exactly the old clean halo hero. The photo sits behind everything at low opacity,
   masked to fade out where the copy lives, and a paper-toned scrim keeps text AA.
   Token-driven (bg/accent), so each brand tints its own atmosphere; reduced-motion
   and dark mode handled below. */
.hero.has-atmos::before{
  background:
    linear-gradient(105deg, var(--bg) 0%, color-mix(in srgb,var(--bg) 88%,transparent) 38%, color-mix(in srgb,var(--bg) 30%,transparent) 70%, transparent 100%),
    var(--halo),
    var(--atmos, none);
  background-size:cover, auto, cover;
  background-position:center, center, center right;
  opacity:1;
}
/* a soft accent wash at the top edge ties the photo to the brand */
.hero.has-atmos::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(180deg, color-mix(in srgb,var(--bg) 55%,transparent) 0%, transparent 22%, transparent 78%, color-mix(in srgb,var(--bg) 92%,transparent) 100%)}
[data-theme="dark"] .hero.has-atmos::before{
  background:
    linear-gradient(105deg, var(--bg) 0%, color-mix(in srgb,var(--bg) 90%,transparent) 40%, color-mix(in srgb,var(--bg) 46%,transparent) 72%, color-mix(in srgb,var(--bg) 22%,transparent) 100%),
    var(--halo),
    var(--atmos, none);
  background-size:cover, auto, cover; background-position:center, center, center right}
@media (prefers-color-scheme:dark){
  :root:not([data-theme]) .hero.has-atmos::before{
    background:
      linear-gradient(105deg, var(--bg) 0%, color-mix(in srgb,var(--bg) 90%,transparent) 40%, color-mix(in srgb,var(--bg) 46%,transparent) 72%, color-mix(in srgb,var(--bg) 22%,transparent) 100%),
      var(--halo),
      var(--atmos, none);
    background-size:cover, auto, cover; background-position:center, center, center right}
}

/* ---- per-slot scale: the .device is native px, --dv-scale shrinks it to its slot ---- */
/* hero: a single iPhone at ~0.66 of native (428px → ~282px wide) */
.showcase>.device.dv-iphone{--dv-scale:.66}
.showcase>.device.dv-ipad{--dv-scale:.62}

/* ---- device duo: an iPad behind/beside the iPhone, to show a universal app ----
   Drop a .device-duo inside the .showcase. The iPad (.dv-ipad) sits larger and behind,
   the iPhone (.dv-iphone) overlaps front-right with its tilt — one glance says "iPhone AND
   iPad." Both are devices.css-spec frames, so the duo inherits the same correct geometry. */
.device-duo{position:relative;width:min(460px,100%);margin-inline:auto;
  display:grid;grid-template-columns:1fr;place-items:center;padding-block:var(--sp-3)}
.device-duo .dv-ipad{--dv-scale:.58;justify-self:start;transform:rotate(calc(var(--rot) * -1.2)) scale(var(--dv-scale))}
.device-duo .dv-iphone{--dv-scale:.42;position:absolute;right:0;bottom:-6%;z-index:2;
  transform:rotate(calc(var(--rot) * 1.6)) scale(var(--dv-scale))}
.device-duo .float-chip{z-index:4}
@media (prefers-reduced-motion:no-preference){
  .device-duo .dv-ipad,.device-duo .dv-iphone{transition:transform .5s var(--ease)}
  /* hover lift only — NO scale() here: the base .device-duo .dv-* rules already carry
     scale(var(--dv-scale)); re-applying it on hover double-scaled the frame and read as the
     shot SHRINKING. The lift (translateY) + rotate stay; the size is owned by the base state. */
  .showcase:hover .device-duo .dv-ipad{transform:rotate(calc(var(--rot) * -1.2)) translateY(-4px)}
  .showcase:hover .device-duo .dv-iphone{transform:rotate(calc(var(--rot) * 1.6)) translateY(-7px)}
}
@media (max-width:560px){
  .device-duo{width:100%}
  .device-duo .dv-ipad{--dv-scale:.5;justify-self:center;transform:rotate(0deg) scale(var(--dv-scale))}
  .device-duo .dv-iphone{--dv-scale:.34;right:2%}
}

/* ---- atmosphere band: a full-bleed lifestyle photo strip between sections ----
   <section class="atmos-band" style="--atmos:url(/assets/atmos.jpg)"> with an optional
   <blockquote> inside .wrap for a one-line mood caption. Pure decoration → it carries
   role/alt nothing; keep meaningful copy in real sections. Scrim keeps any caption AA.
   No --atmos set → it collapses to a tinted gradient strip (still fine, never broken). */
.atmos-band{position:relative;isolation:isolate;
  min-height:clamp(240px,38vw,440px);display:grid;place-items:center;
  background:var(--atmos, linear-gradient(120deg,var(--bg-2),var(--surface))) center/cover no-repeat;
  border-block:1px solid var(--line)}
.atmos-band::before{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(90deg, color-mix(in srgb,var(--bg) 78%,transparent) 0%, color-mix(in srgb,var(--bg) 30%,transparent) 50%, color-mix(in srgb,var(--bg) 70%,transparent) 100%),
             radial-gradient(120% 80% at 50% 120%, color-mix(in srgb,var(--accent) 12%,transparent), transparent 60%)}
.atmos-band .wrap{position:relative;z-index:2;text-align:center}
.atmos-band blockquote{margin:0 auto;max-width:24ch;font-family:var(--display);
  font-size:var(--step-2);line-height:var(--lh-snug);color:var(--text);
  text-wrap:balance;text-shadow:0 1px 14px color-mix(in srgb,var(--bg) 70%,transparent)}
.atmos-band blockquote cite{display:block;margin-top:var(--sp-2);font-family:var(--sans);
  font-style:normal;font-size:var(--step--1);font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;color:var(--accent-text)}
@media (prefers-reduced-motion:no-preference){
  /* gentle parallax: the band's photo drifts slower than the page on scroll */
  @supports (background-attachment:fixed){
    .atmos-band{background-attachment:fixed}
  }
}

/* ---- trust strip ---- */
.strip{border-block:1px solid var(--line);background:var(--bg-2)}
.strip .wrap{display:flex;gap:var(--sp-4);flex-wrap:wrap;justify-content:space-between;padding-block:var(--sp-3)}
.strip .item{display:flex;gap:.6rem;align-items:center;font-size:var(--step--1);font-weight:600;color:var(--text-soft)}
.strip .item b{color:var(--text);font-weight:700}
.strip .item .ic{color:var(--accent-text)}

/* ---- sections + alternating feature rows ---- */
.section{padding:var(--sp-6) 0}
.section-head{max-width:var(--measure);margin-bottom:var(--sp-5)}
.section-head .eyebrow{margin-bottom:var(--sp-2)}
.section-head p{color:var(--text-soft);font-size:var(--step-1);line-height:1.45;margin-top:var(--sp-2)}
.frow{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-5);align-items:center;padding:var(--sp-4) 0}
.frow:nth-child(even) .fmedia{order:2}
.fbody h3{margin-bottom:var(--sp-2)}
.fbody p{color:var(--text-soft);margin-bottom:var(--sp-2)}
.fbody .more{color:var(--accent-text);font-weight:650;text-decoration:none;font-size:var(--step--1)}
.fmedia{aspect-ratio:4/3;border-radius:var(--radius);border:1px solid var(--line);
  background:linear-gradient(150deg,var(--surface),var(--bg-2));display:grid;place-items:center;position:relative;overflow:hidden}
.fmedia::before{content:"";position:absolute;inset:0;background:var(--halo);opacity:.7}
.fmedia .glyph{font-size:3.2rem;color:var(--accent-text);opacity:.85;position:relative}
/* feature row can hold a real product image instead of a glyph. SCOPED with :not(.device-screen)
   so it never hijacks a framed screenshot — the device-screen img is positioned by the frame. */
.fmedia img:not(.device-screen){position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center}
.fmedia.contain{background:linear-gradient(150deg,var(--surface),var(--bg-2))}
.fmedia.contain img{object-fit:contain;padding:clamp(1rem,4%,2rem)}
/* a feature row can hold a real device frame instead of a flat card: wrap the screenshot in
   a .device.dv-iphone INSIDE .fmedia.dv-device. The cell drops its card chrome and sizes itself
   to the SCALED frame (no fixed min-height — the .device footprint sets the height, so the full
   bezel is always visible, top and bottom). */
.fmedia.dv-device{aspect-ratio:auto;border:0;background:none;overflow:visible;
  display:flex;align-items:center;justify-content:center;padding:var(--sp-3) 0}
.fmedia.dv-device::before{display:none}
.fmedia.dv-device .device{--dv-scale:.5}
.fmedia.dv-device .dv-ipad{--dv-scale:.5}

/* ---- feature gallery: a row of product screenshots to highlight features ----
   App pages carry several screenshots; each is wrapped in a .device.dv-iphone shell with a
   caption. 3-up on desktop, a swipeable scroll-snap row on small screens. The frame reads
   theme tokens → dark mode + the correct geometry come for free. */
/* auto-fit, not a fixed 3: a gallery carries three OR four plates (Aida added photo-to-pattern)
   and both must sit on one row on desktop; the mobile branch below is a carousel either way. */
.gallery{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:var(--sp-5);align-items:start;padding:var(--sp-4) 0}
.gallery figure{margin:0;display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--sp-3)}
.gallery .device{--dv-scale:.5}
.gallery figcaption{max-width:26ch}
.gallery figcaption b{display:block;font-family:var(--display);font-size:var(--step-0);font-weight:660;
  letter-spacing:var(--display-tracking);margin-bottom:.25rem;color:var(--text)}
.gallery figcaption span{font-size:var(--step--1);color:var(--text-soft);line-height:1.5}
/* hover lift only — NO scale(): the gallery's base .device carries its size on the inner
   .device-frame (scale(var(--dv-scale))), so adding scale() here stacked a second scale and
   read as the shot SHRINKING on hover. rotate + lift stay; size is owned by the base. */
@media (prefers-reduced-motion:no-preference){.gallery figure:hover .device{transform:rotate(var(--rot)) translateY(-6px)}}
@media (max-width:820px){
  .gallery{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:min(74%,260px);
    overflow-x:auto;scroll-snap-type:x mandatory;scroll-padding:var(--sp-3);
    margin-inline:calc(-1*clamp(1.1rem,4vw,2.5rem));padding-inline:clamp(1.1rem,4vw,2.5rem);
    -webkit-overflow-scrolling:touch}
  .gallery figure{scroll-snap-align:center}
  .gallery .device{--dv-scale:.56}
}

/* ---- existing content-page sections, restyled in place ---- */
/* how-it-works steps */
.how-it-works{padding:var(--sp-6) 0}
.how-it-works h2{margin-bottom:var(--sp-4)}
.steps{list-style:none;padding:0;margin:0;display:grid;gap:var(--sp-3)}
.steps li{display:flex;gap:var(--sp-2);align-items:flex-start;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);padding:var(--sp-3)}
.steps .step-num,.step-num{flex:none;width:2.1rem;height:2.1rem;border-radius:50%;display:grid;place-items:center;
  background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--accent-text);font-weight:750;margin-top:.1rem}
.steps strong,.steps li div strong{display:block;font-size:var(--step-1);font-family:var(--display);margin-bottom:.2rem}
.steps p,.steps li div p{color:var(--text-soft);margin:0}

/* benefit + compare + contrast cards */
.benefits,.compare-grid,.contrast-grid{display:grid;gap:var(--sp-3);padding:var(--sp-4) 0}
.benefits{grid-template-columns:repeat(3,1fr)}
.compare-grid{grid-template-columns:repeat(3,1fr)}
.contrast-grid{grid-template-columns:1fr 1fr}
.benefit,.compare-card,.contrast-card{background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:var(--sp-4)}
.benefit h2,.compare-card h3,.contrast-card h3{font-size:var(--step-1);margin-bottom:var(--sp-1);margin-top:0}
.benefit p,.compare-card p,.contrast-card p{color:var(--text-soft)}
.compare-card.us,.contrast-card.us{border-color:color-mix(in srgb,var(--accent) 42%,var(--line));
  background:linear-gradient(160deg,var(--surface),color-mix(in srgb,var(--accent) 5%,var(--surface)))}
.compare-card.them,.contrast-card.them{opacity:.82}
.compare-card.them h3,.contrast-card.them h3{color:var(--text-soft)}
.compare h2,.contrast h2,.packet h2,.trust h2,.pricing h2,.proof h2{margin-bottom:var(--sp-3)}

/* claim-packet demo (Kestrove content page) */
.packet-demo{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--surface);margin:var(--sp-3) 0}
.packet-row{display:grid;grid-template-columns:1.4fr 1fr .7fr 1.1fr;gap:.6rem;padding:.7rem var(--sp-3);
  border-bottom:1px dashed var(--line);font-size:var(--step--1)}
.packet-row:last-child{border-bottom:0}
.packet-head{background:var(--bg-2);font-weight:700;text-transform:uppercase;letter-spacing:.04em;font-size:11px}

/* trust list */
.trust-list{list-style:none;padding:0;margin:0;display:grid;gap:var(--sp-2)}
.trust-list li{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-sm);padding:var(--sp-3);color:var(--text-soft)}
.trust-list strong{color:var(--text)}

/* verified card (Kestrove hero adjunct) */
.verified-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:var(--sp-4)}
.verified-card .check{color:var(--verified);font-weight:750;font-size:var(--step-1);margin-bottom:var(--sp-1)}
.verified-card p{color:var(--text-soft)}

/* ---- manifesto block (oomny umbrella; replaces fake testimonials) ---- */
.manifesto{background:var(--bg-2);border-block:1px solid var(--line)}
.manifesto .wrap,.manifesto.legacy{padding-block:var(--sp-6)}
.manifesto .lead{font-family:var(--display);font-size:var(--step-2);line-height:1.3;max-width:26ch;
  letter-spacing:-.01em;margin-bottom:var(--sp-5)}
.manifesto .lead b{color:var(--accent-text);font-weight:inherit}
.principles{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4)}
.principles>div{border-top:2px solid var(--accent);padding-top:var(--sp-2)}
.principles h4{font-family:var(--display);font-size:var(--step-1);margin:0 0 .4rem;font-weight:640;letter-spacing:-.01em}
.principles p{font-size:var(--step--1);color:var(--text-soft);line-height:1.55}

/* ---- proof bands (own-vs-rent / claim-packet) ---- */
.proof{background:var(--bg-2);border-block:1px solid var(--line)}
.proof .wrap{padding-block:var(--sp-6)}
.proof .lead{font-family:var(--display);font-size:var(--step-2);line-height:1.3;max-width:24ch;
  letter-spacing:-.01em;margin-bottom:var(--sp-4)}
.proof .lead b{color:var(--accent-text);font-weight:inherit}
.compare{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)}
.compare .col{border:1px solid var(--line);border-radius:var(--radius);padding:var(--sp-4);background:var(--surface)}
.compare .col .lbl{font-size:11px;font-weight:750;letter-spacing:.06em;text-transform:uppercase;display:block;margin-bottom:.6rem}
.compare .own{border-color:color-mix(in srgb,var(--accent) 42%,var(--line))}
.compare .own .lbl{color:var(--accent-text)}
.compare .own .amt{font-family:var(--display);font-size:var(--step-2);font-weight:700;letter-spacing:-.01em;margin-bottom:.4rem}
.compare .rent{opacity:.72}
.compare .rent .lbl{color:var(--text-soft)}
.compare .rent .amt{font-size:var(--step-2);font-weight:700;margin-bottom:.4rem;color:var(--text-soft)}
.compare p{font-size:var(--step--1);color:var(--text-soft);line-height:1.55}

/* claim-packet sample + calm states */
.kgrid{display:grid;grid-template-columns:1.15fr .85fr;gap:var(--sp-4);align-items:center}
.packet{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:var(--sp-4);
  box-shadow:var(--shadow-3)}
.packet .doc-h{display:flex;justify-content:space-between;align-items:baseline;border-bottom:1px solid var(--line);
  padding-bottom:.7rem;margin-bottom:.9rem}
.packet .doc-h b{font-size:var(--step-0);font-weight:700}
.packet .doc-h span{font-size:11px;color:var(--text-soft);font-weight:650;letter-spacing:.04em;text-transform:uppercase}
.packet .line{display:flex;justify-content:space-between;gap:1rem;font-size:var(--step--1);color:var(--text-soft);
  padding:.4rem 0;border-bottom:1px dashed var(--line)}
.packet .line b{color:var(--text);font-weight:600}
.packet .tot{display:flex;justify-content:space-between;margin-top:.9rem;font-weight:700;font-size:var(--step-0)}
.states{display:grid;gap:.7rem;align-content:center}
.state{display:inline-flex;align-items:center;gap:.55rem;font-size:var(--step-0);font-weight:600;color:var(--text);
  padding:.55rem .85rem;border-radius:10px;border:1px solid var(--line);background:var(--surface);width:fit-content}
.state i{font-style:normal;font-size:1.05em;line-height:1}
.state.amber i{color:var(--caution-text)}
.state.green i{color:var(--verified)}
.states .note{font-size:var(--step--1);color:var(--text-soft);margin-top:.3rem;max-width:34ch}

/* ---- pricing card ---- */
.price-wrap,.pricing{display:grid;place-items:center;padding:var(--sp-6) 0}
.price,.price-card{width:min(30rem,100%);background:var(--surface);border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:var(--sp-5);text-align:center;position:relative;overflow:hidden;box-shadow:var(--shadow-3)}
.price::before{content:"";position:absolute;inset:0;background:var(--halo)}
.price>*{position:relative}
.price .amt{font-family:var(--display);font-size:var(--step-4);font-weight:700;letter-spacing:-.02em;margin:.3rem 0}
.price-card .price{font-family:var(--display);font-size:var(--step-3);font-weight:700;margin:.3rem 0;box-shadow:none;border:0;padding:0;width:auto;background:none}
.price-card .once{font-size:var(--step-1);color:var(--text-soft);font-weight:600}
.price .once{display:inline-block;font-size:11px;font-weight:750;letter-spacing:.08em;text-transform:uppercase;
  color:var(--accent-text);background:color-mix(in srgb,var(--accent) 14%,transparent);padding:.3rem .7rem;border-radius:99px}
.price ul{list-style:none;padding:0;margin:var(--sp-3) 0;display:grid;gap:.55rem;text-align:left}
.price li{display:flex;gap:.6rem;align-items:center;font-size:var(--step--1);color:var(--text-soft)}
.price li::before{content:"✓";color:var(--verified,var(--accent-text));font-weight:800}
.price-card p{color:var(--text-soft);margin-top:var(--sp-2)}

/* ---- FAQ (details/summary) ---- */
.faq{padding:var(--sp-6) 0;max-width:var(--measure);margin-inline:auto}
.faq h2{margin-bottom:var(--sp-3)}
.faq details{border:1px solid var(--line);border-radius:var(--radius-sm);background:var(--surface);
  padding:var(--sp-2) var(--sp-3);margin-bottom:var(--sp-2)}
.faq summary{cursor:pointer;font-weight:650;font-size:var(--step-0);list-style:none}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";float:right;color:var(--accent-text);font-weight:700}
.faq details[open] summary::after{content:"–"}
.faq details p{color:var(--text-soft);margin-top:var(--sp-2)}

/* ---- prose pages (privacy / terms / support) ---- */
.prose-page,.prose{max-width:var(--measure);margin-inline:auto;padding:var(--sp-5) 0 var(--sp-6)}
.prose-page h1,.prose h1{font-size:var(--step-3);margin-bottom:var(--sp-3)}
.prose-page h2,.prose h2{font-size:var(--step-1);margin-top:var(--sp-4);margin-bottom:var(--sp-1)}
.prose-page p,.prose-page li,.prose p,.prose li{color:var(--text-soft)}
.prose-page p:first-of-type,.prose p:first-of-type{color:var(--text)}
.prose-page ul,.prose-page ol,.prose ul,.prose ol{padding-left:1.4rem}
.prose-page li,.prose li{margin-bottom:.35rem}
.prose-page a,.prose a{color:var(--accent-text);text-underline-offset:3px}
.prose.center{text-align:center}
.fine{color:var(--text-soft);font-size:var(--step--1)}

/* ---- legal-identity (colofon / informatieplicht block, shared partial) ---- */
.legal-id{display:grid;grid-template-columns:max-content 1fr;gap:.3rem 1.2rem;
  margin:var(--sp-3) 0;font-size:var(--step--1)}
.legal-id dt{color:var(--text-soft);font-weight:600}
.legal-id dd{margin:0;color:var(--text)}
@media (max-width:520px){.legal-id{grid-template-columns:1fr;gap:.1rem .8rem}
  .legal-id dd{margin-bottom:.5rem}}

/* ---- app cards (umbrella portfolio grid) ---- */
.apps{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4)}
.app-card{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:var(--surface);
  display:grid;grid-template-columns:auto 1fr;transition:transform .2s var(--ease), box-shadow .2s}
.app-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-3)}
.app-card .shot{width:156px;background:var(--bg-2);display:grid;place-items:center;padding:1.2rem;border-right:1px solid var(--line)}
.app-card .shot img{width:100%;border-radius:14px;box-shadow:0 14px 30px -18px #0008}
[data-theme="dark"] .app-card .shot img{filter:brightness(.95)}
@media (prefers-color-scheme:dark){:root:not([data-theme]) .app-card .shot img{filter:brightness(.95)}}
.app-card .meta{padding:var(--sp-4)}
.app-card .tag{font-size:10px;font-weight:750;letter-spacing:.06em;text-transform:uppercase;color:var(--accent-text)}
.app-card h3{margin:.4rem 0 .55rem;font-family:var(--display)}
.app-card p{font-size:var(--step--1);color:var(--text-soft);line-height:1.5;margin-bottom:.9rem}
.app-card .go{font-weight:650;color:var(--accent-text);text-decoration:none;font-size:var(--step--1)}

/* ---- footer ---- */
footer{border-top:1px solid var(--line);background:var(--bg-2);margin-top:var(--sp-5);
  padding:var(--sp-4) clamp(1.1rem,4vw,2.5rem)}
footer .inner{max-width:72rem;margin-inline:auto;
  display:flex;gap:var(--sp-3);flex-wrap:wrap;align-items:center;font-size:var(--step--1);color:var(--text-soft)}
footer .links{margin-left:auto;display:flex;gap:1.2rem}
.foot-brand{display:inline-flex;align-items:center;gap:.5rem;flex-wrap:wrap}
footer a{text-decoration:none;color:var(--text-soft)}
footer a:hover{color:var(--text)}
footer .fine{font-size:var(--step--1)}
.footer-mark,.oomny-link img{vertical-align:-3px;border-radius:4px}

/* ---- App Store badge slot ---- */
.badge-slot{text-align:center;padding:var(--sp-4) 0}
.store-badge{display:inline-block;padding:.75rem 1.5rem;border-radius:12px;background:var(--text);color:var(--bg);
  font-weight:650;text-decoration:none;border:none}
.store-badge.hidden{display:none}

/* ---- staggered load reveal (motion-safe) ---- */
@media (prefers-reduced-motion:no-preference){
  .reveal{opacity:0;transform:translateY(14px);animation:rise var(--dur-3) var(--ease) forwards}
  .reveal:nth-child(2){animation-delay:.08s}.reveal:nth-child(3){animation-delay:.16s}
  .d1{animation-delay:.05s}.d2{animation-delay:.13s}.d3{animation-delay:.21s}.d4{animation-delay:.29s}
  @keyframes rise{to{opacity:1;transform:none}}
  .btn{transition:transform var(--dur-1) var(--ease-out), box-shadow var(--dur-2), background var(--dur-2), border-color var(--dur-2)}
  .showcase:hover .phone{transform:rotate(calc(var(--rot) * .25))}
}

/* ---- Waitlist form (umbrella). Token-based; inherits the site's form look. ---- */
.waitlist{display:flex;flex-direction:column;gap:1rem;max-width:30rem;margin:1.5rem auto 0}
.waitlist .wl-field{display:flex;flex-direction:column;gap:.35rem;font-size:var(--step--1)}
.waitlist input,.waitlist select{padding:.7rem .8rem;border:1px solid var(--line);border-radius:8px;
  background:var(--surface,#fff);color:var(--text);font:inherit}
.waitlist .wl-consent{display:flex;gap:.6rem;align-items:flex-start;font-size:var(--step--1);
  color:var(--text-soft);line-height:1.45}
.waitlist .wl-consent input{margin-top:.2rem}
.waitlist .waitlist-msg{margin:.2rem 0 0;font-size:var(--step--1);color:var(--accent-text)}

/* ---- responsive ---- */
@media (max-width:820px){
  .hero-grid,.frow,.apps,.compare,.kgrid,.principles,.benefits,.compare-grid,.contrast-grid,.packet-row{grid-template-columns:1fr}
  .frow:nth-child(even) .fmedia{order:0}
  .showcase{margin-top:var(--sp-3)}
  .packet-head{display:none}
}

/* ---- narrow-viewport frame + chip steps ----
   A .device footprint is FIXED px (--dv-fw × --dv-scale: 430px iPhone, 546px iPad) while the
   .wrap around it is fluid, so below ~430px the per-slot scales above stop fitting and the page
   gains a horizontal scrollbar. Measured 2026-07-26 across every template site: each home page
   pinned scrollWidth at 341px on a 320px phone, so mobile Safari rendered it pinched-out. The
   .gallery already had this treatment at 820px; .showcase and the float-chips were the gap.

   Values are measured against the container, not guessed. With the frames collapsed the stacked
   .wrap content box is `vw − 85px`, and a frame must clear it INCLUDING the 2px .device-power
   nub, which sits OUTSIDE .device's own box and is what actually reached 341.7px. Budget per
   band = its TIGHTEST width, minus 4px for that nub and subpixel rounding:
     381px → 292px usable → iPhone ≤.67, iPad ≤.53
     320px → 231px usable → iPhone ≤.53, iPad ≤.42   (320px is the floor — iPhone SE 1st gen)
   .chip-b hangs 8% outside the showcase by design; at 320px there is no gutter left to hang
   into, so both chips tuck flush instead.

   The price card is the third cause, and the one the frames were masking: `padding:var(--sp-5)`
   is 4.5rem = 72px a side, so on a 320px phone the card spends 144px of a 284.8px content box
   on padding and its remaining min-content still pushed the .wrap 9–19px past the viewport on
   every template site. --sp-4 (44px) is still a generous card inset and gives the content 56px
   back, which clears the widest of them with room to spare. */

   A site that RAISES one of these scales in its own theme.css (a `[data-brand=…] .fmedia`/
   `.gallery` bump) beats these rules on specificity and needs its own steps — those live in
   that site's theme.css, next to the bump they correct. */
@media (max-width:428px){
  .showcase>.device.dv-ipad{--dv-scale:.52}
}
@media (max-width:380px){
  .showcase>.device.dv-iphone{--dv-scale:.53}
  .showcase>.device.dv-ipad{--dv-scale:.41}
  .chip-a{left:0}
  .chip-b{right:0}
  .price,.price-card{padding:var(--sp-4)}
}
