/* ============================================================================
   Reloy — "for business" onboarding site.
   Art direction: "Ink & Ember". A near-black editorial ground so the product's
   own surfaces (a paper stamp card, a phone, the owner dashboard) read as lit
   physical objects, with one warm accent borrowed from a café's own light.

   Everything is single-origin: fonts are self-hosted woff2 (Google Fonts is
   blocked in Iran), there is no CDN and no animation library — the CSP is
   `script-src 'self'` with no 'unsafe-inline' (backend/pb_hooks/headers.pb.js),
   so every effect below is CSS or the Web Animations API.

   Sections alternate two bands:
     .band--ink    near-black, the default
     .band--paper  warm off-white, used where the page shows the real product
   Both bands redefine the same semantic tokens, so every component below is
   written once and inherits the right colours from its band.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts --- */
@font-face { font-family: "Fraunces";   src: url("fonts/fraunces-var.woff2") format("woff2");      font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Archivo";    src: url("fonts/archivo-var.woff2") format("woff2");       font-weight: 100 900; font-style: normal; font-display: swap; }
@font-face { font-family: "JetBrains";  src: url("fonts/jetbrainsmono-var.woff2") format("woff2"); font-weight: 100 800; font-style: normal; font-display: swap; }

/* Persian body/heading swap — see html[dir="rtl"] below. Static weights,
   not a variable font, so only the weights actually used are declared. */
@font-face { font-family: "Vazirmatn"; font-style: normal; font-weight: 400; font-display: swap; src: url("fonts/vazirmatn-400.woff2") format("woff2"); }
@font-face { font-family: "Vazirmatn"; font-style: normal; font-weight: 500; font-display: swap; src: url("fonts/vazirmatn-500.woff2") format("woff2"); }
@font-face { font-family: "Vazirmatn"; font-style: normal; font-weight: 600; font-display: swap; src: url("fonts/vazirmatn-600.woff2") format("woff2"); }
@font-face { font-family: "Vazirmatn"; font-style: normal; font-weight: 700; font-display: swap; src: url("fonts/vazirmatn-700.woff2") format("woff2"); }
@font-face { font-family: "Vazirmatn"; font-style: normal; font-weight: 800; font-display: swap; src: url("fonts/vazirmatn-800.woff2") format("woff2"); }

/* --------------------------------------------------------------- tokens --- */
:root {
  /* ---- raw palette ---- */
  --ink-900: #08080a;
  --ink-850: #0b0b0e;
  --ink-800: #101014;
  --ink-750: #16161c;
  --ink-700: #1d1d25;

  --paper:   #f1efea;
  --paper-2: #e7e4dd;
  --paper-3: #d9d5cc;

  --ember:      #f0a94a;   /* accent on ink   */
  --ember-lift: #ffc87e;
  --ember-deep: #9a5a0a;   /* accent on paper (4.5:1 on --paper) */

  /* data hues — only ever used inside the dashboard section, so their
     appearance always means "this is your café's own data" */
  --data-loyal: #2f3f4f;
  --data-new:   #b3803f;

  /* ---- semantic tokens: ink band (the default) ---- */
  --bg:        var(--ink-900);
  --bg-2:      var(--ink-800);
  --fg:        #f4f3f0;
  --fg-dim:    #a5a19a;
  --fg-faint:  #8b8780;   /* 5.6:1 on --ink-900 */
  --line:      rgba(255,255,255,0.10);
  --line-2:    rgba(255,255,255,0.20);
  --surface:   rgba(255,255,255,0.045);
  --surface-2: rgba(255,255,255,0.075);
  --accent:    var(--ember);
  --accent-fg: #0a0a0c;
  --shadow:    0 40px 90px -40px rgba(0,0,0,0.9);

  /* ---- type ---- */
  --f-display: "Fraunces", "Iowan Old Style", Georgia, serif;
  --f-ui:      "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --f-mono:    "JetBrains", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ---- motion ---- */
  --e-out:    cubic-bezier(0.22, 1, 0.36, 1);
  --e-spring: cubic-bezier(0.34, 1.4, 0.5, 1);

  /* ---- metrics ---- */
  --wrap: 1200px;
  --pad: 26px;
  --radius: 22px;
  --radius-lg: 30px;
}

/* Persian pass: one font swap covers every heading/body/mono rule on the
   page, since they all read from --f-display/--f-ui/--f-mono rather than
   naming a family directly (see the @font-face block above for the
   Vazirmatn files). --f-mono has no real Persian monospace equivalent, so
   eyebrows/badges/labels get Vazirmatn too instead of falling back past
   JetBrains (Latin-only) to whatever generic font the OS picks. */
html[dir="rtl"] {
  --f-display: "Vazirmatn", system-ui, sans-serif;
  --f-ui:      "Vazirmatn", system-ui, sans-serif;
  --f-mono:    "Vazirmatn", system-ui, sans-serif;
}

.band--paper {
  --bg:        var(--paper);
  --bg-2:      var(--paper-2);
  --fg:        #17161a;
  --fg-dim:    #56534d;
  --fg-faint:  #6b675f;   /* 4.8:1 on --paper  */
  --line:      rgba(20,18,14,0.12);
  --line-2:    rgba(20,18,14,0.22);
  --surface:   rgba(255,255,255,0.62);
  --surface-2: rgba(255,255,255,0.85);
  --accent:    var(--ember-deep);
  --accent-fg: #ffffff;
  --shadow:    0 34px 70px -34px rgba(60,48,30,0.42);
}

/* ------------------------------------------------------------------ base -- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
html:focus-within { scroll-behavior: smooth; }
html, body { margin: 0; min-height: 100%; }

body {
  background: var(--ink-900);
  color: var(--fg);
  font-family: var(--f-ui);
  font-size: 17px;
  line-height: 1.62;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;   /* not `hidden`: that would make body a scroll container
                         and silently break every position:sticky descendant */
}

img, svg { max-width: 100%; }
a { color: inherit; }
::selection { background: var(--ember); color: #0a0a0c; }

/* one focus style for the whole page, always visible, never removed */
:focus-visible {
  outline: 2px solid var(--ember);
  outline-offset: 3px;
  border-radius: 6px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
  position: fixed; top: 10px; left: 50%; z-index: 400;
  transform: translate(-50%, -180%);
  background: var(--ember); color: #0a0a0c; font-weight: 600;
  padding: 10px 18px; border-radius: 999px; text-decoration: none;
  transition: transform 0.2s var(--e-out);
}
.skip:focus { transform: translate(-50%, 0); }

/* the sprite that carries the traced Reloy logo; never rendered directly */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ------------------------------------------------------------ typography -- */
h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 600;
  font-variation-settings: "SOFT" 30, "WONK" 1;
  letter-spacing: -0.018em;
  line-height: 1.06;
  margin: 0;
  text-wrap: balance;
}
h2 { font-size: clamp(30px, 4.4vw, 54px); }
h3 { font-size: clamp(19px, 1.5vw, 22px); line-height: 1.22; letter-spacing: -0.01em; }
p  { margin: 0; text-wrap: pretty; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--f-mono);
  font-size: 11.5px; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fg-dim);
  margin: 0 0 20px;
}
.eyebrow::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 18%, transparent);
  animation: pulseDot 3.4s ease-in-out infinite;
}
@keyframes pulseDot {
  0%, 100% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
  50%      { box-shadow: 0 0 0 8px color-mix(in srgb, var(--accent) 0%, transparent); }
}

.lede {
  font-size: clamp(16.5px, 1.35vw, 19px);
  color: var(--fg-dim);
  max-width: 60ch;
  margin-top: 20px;
}

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--pad); }

section { position: relative; background: var(--bg); color: var(--fg); }
.band { padding-block: clamp(70px, 8vw, 118px); }
.sec-head { max-width: 780px; margin-bottom: clamp(36px, 4.4vw, 64px); }
.sec-head .lede { max-width: 56ch; }

/* a hairline that runs the width of a band and doubles as a section divider */
.rule { height: 1px; background: linear-gradient(90deg, transparent, var(--line-2), transparent); border: 0; margin: 0; }

/* ------------------------------------------------------- fixed backdrop --- */
.fx { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
.fx__orb { position: absolute; border-radius: 50%; filter: blur(90px); will-change: transform; }
.fx__orb--a {
  width: 60vmax; height: 60vmax; top: -26vmax; right: -18vmax;
  background: radial-gradient(circle at 50% 50%, rgba(240,169,74,0.30), transparent 62%);
  animation: drift1 38s ease-in-out infinite;
}
.fx__orb--b {
  width: 52vmax; height: 52vmax; bottom: -22vmax; left: -16vmax;
  background: radial-gradient(circle at 50% 50%, rgba(120,140,190,0.22), transparent 64%);
  animation: drift2 46s ease-in-out infinite;
}
@keyframes drift1 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(-8vmax,7vmax) scale(1.16); } }
@keyframes drift2 { 0%,100% { transform: translate(0,0) scale(1.06); } 50% { transform: translate(7vmax,-6vmax) scale(0.9); } }

.fx__grain {
  position: absolute; inset: -50%; opacity: 0.42; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* a soft light that follows the pointer; --mx/--my are set by for-business.js */
.fx__spot {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity 0.6s ease;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%),
              rgba(240,169,74,0.13), transparent 70%);
}
.js.has-pointer .fx__spot { opacity: 1; }

.page { position: relative; z-index: 1; }

/* ------------------------------------------------------------- reveal ----- */
/* Until for-business.js adds `.js` to <html>, every .rv is fully visible, so a
   no-JS visitor (or a crawler) gets the whole page. */
.js .rv {
  opacity: 0;
  transform: translateY(20px);
  filter: blur(7px);
  transition:
    opacity   0.62s var(--e-out) var(--rv-delay, 0ms),
    transform 0.72s var(--e-out) var(--rv-delay, 0ms),
    filter    0.62s var(--e-out) var(--rv-delay, 0ms);
}
.js .rv.is-in { opacity: 1; transform: none; filter: none; }

/* ------------------------------------------------------------ buttons ----- */
.btn {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 10px;
  min-height: 52px; padding: 0 24px;
  border-radius: 999px; border: 1px solid transparent;
  font-family: var(--f-ui); font-size: 16px; font-weight: 600; letter-spacing: -0.005em;
  text-decoration: none; cursor: pointer;
  transition: transform 0.28s var(--e-spring), box-shadow 0.28s var(--e-out),
              background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
  will-change: transform;
}
.btn__arrow { transition: transform 0.32s var(--e-spring); }
.btn:hover .btn__arrow { transform: translateX(4px); }
.btn:active { transform: translateY(1px) scale(0.985); }

/* a quick "launched" feedback on click, on top of the hover/active states
   above — the arrow darts off and reappears, the button gives a squeeze */
.btn.is-launch { animation: btnLaunch 0.42s var(--e-out); }
.btn.is-launch .btn__arrow { animation: btnArrowLaunch 0.42s var(--e-out); }
@keyframes btnLaunch {
  0%   { transform: scale(1); }
  35%  { transform: scale(0.93); }
  100% { transform: scale(1); }
}
@keyframes btnArrowLaunch {
  0%   { transform: translateX(0); opacity: 1; }
  45%  { transform: translateX(12px); opacity: 0; }
  46%  { transform: translateX(-8px); }
  100% { transform: translateX(0); opacity: 1; }
}

.btn--primary {
  background: var(--ember); color: #0a0a0c;
  box-shadow: 0 14px 34px -14px rgba(240,169,74,0.75), inset 0 1px 0 rgba(255,255,255,0.45);
}
.btn--primary:hover {
  background: var(--ember-lift);
  box-shadow: 0 20px 46px -16px rgba(240,169,74,0.9), inset 0 1px 0 rgba(255,255,255,0.55);
  transform: translateY(-2px);
}
/* a light sweeps across the button on hover */
.btn--primary::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,0.55) 48%, transparent 66%);
  transform: translateX(-120%);
  transition: transform 0.75s var(--e-out);
}
.btn--primary:hover::after { transform: translateX(120%); }

.btn--ghost {
  background: var(--surface); color: var(--fg);
  border-color: var(--line-2);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.btn--ghost:hover { background: var(--surface-2); border-color: var(--fg-faint); transform: translateY(-2px); }

.btn--lg { min-height: 58px; padding: 0 30px; font-size: 17px; }

/* ---------------------------------------------------------------- logo ---- */
.logo { display: inline-flex; align-items: center; gap: 11px; text-decoration: none; color: inherit; }
.logo__mark { width: 26px; height: 29.5px; fill: currentColor; overflow: visible; }
.logo__word { width: 74px; height: 27.3px; fill: currentColor; }
.logo__mark .spark {
  fill: none; stroke: var(--ember); stroke-width: 7.23; stroke-linecap: round;
  opacity: 0; transition: opacity 0.3s ease;
}
.logo:hover .logo__mark .spark { opacity: 1; animation: orbit 1.5s linear infinite; }
@keyframes orbit { from { stroke-dashoffset: 0; } to { stroke-dashoffset: -100; } }

/* a little hop on click — feedback that "home" is where you just landed */
.logo.is-jump { animation: logoJump 0.5s var(--e-out); }
.logo.is-jump .logo__mark .spark { opacity: 1; animation: orbit 0.5s linear 1; }
@keyframes logoJump {
  0%   { transform: translateY(0) scale(1); }
  35%  { transform: translateY(-4px) scale(1.08); }
  65%  { transform: translateY(1px) scale(0.97); }
  100% { transform: translateY(0) scale(1); }
}

/* ----------------------------------------------------------------- nav ---- */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 200;
  transition: background-color 0.3s ease, box-shadow 0.3s ease, backdrop-filter 0.3s ease;
}
.nav.is-stuck {
  background: rgba(8,8,10,0.72);
  backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
  box-shadow: 0 1px 0 rgba(255,255,255,0.08), 0 20px 40px -30px rgba(0,0,0,0.9);
}
.nav__in {
  display: flex; align-items: center; gap: 24px;
  height: 74px;
  color: #f4f3f0;
}
/* .nav__mobile wraps .nav__links + .nav__cta so the two can be relocated
   into a single dropdown panel on mobile. On desktop it stays "contents" —
   invisible as a box — so its children lay out exactly as direct flex
   items of .nav__in; the explicit `order` values below fix their visual
   position regardless of the wrapper. */
.nav__mobile { display: contents; }
.nav__links { display: flex; gap: 4px; margin-inline-start: auto; order: 1; }
.nav__links a {
  position: relative;
  padding: 9px 14px; border-radius: 999px;
  font-size: 14.5px; font-weight: 500; color: rgba(244,243,240,0.72);
  text-decoration: none; white-space: nowrap;
  transition: color 0.2s ease, background-color 0.2s ease;
}
.nav__links a::after {
  content: ""; position: absolute; left: 14px; right: 14px; bottom: 5px; height: 1.5px;
  background: var(--ember); border-radius: 2px;
  transform: scaleX(0); transform-origin: left; transition: transform 0.34s var(--e-out);
}
.nav__links a:hover { color: #fff; }
.nav__links a:hover::after, .nav__links a[aria-current="true"]::after { transform: scaleX(1); }
.nav__links a[aria-current="true"] { color: #fff; }
/* The customer's way out of a page written for café owners. order:2 puts it
   between the section links and the owner CTA; ghost keeps it clearly the
   second-choice action, since this page's job is still selling to cafés.
   The nav sits on near-black, so the inherited .btn--ghost surface is
   overridden here rather than globally. */
.nav__app {
  /* order 3/4 put the two actions side by side after the language switch
     (which is order 2), so the ghost reads as the quieter sibling of the
     owner CTA rather than being split off from it */
  order: 3; margin-inline-start: 6px; min-height: 44px; padding: 0 15px;
  font-size: 14.5px; gap: 7px; white-space: nowrap;
  color: #f4f3f0; background: rgba(244,243,240,0.07); border-color: rgba(244,243,240,0.2);
}
.nav__app:hover { background: rgba(244,243,240,0.14); border-color: rgba(244,243,240,0.4); color: #fff; }
.nav__app svg { flex: none; opacity: 0.85; }
/* below 860px the toolbar collapses into the burger panel; the customer link
   should not be the thing hiding behind a menu, so it stays out on its own */
.nav__cta { margin-inline-start: 6px; min-height: 44px; padding: 0 18px; font-size: 14.5px; order: 4; }
.nav__links + .nav__cta { margin-inline-start: 6px; }
.nav__links:not(:last-child) { margin-inline-end: 0; }

/* --------------------------------------------------------- lang switch --- */
.lang-switch {
  display: flex; align-items: center; gap: 2px;
  margin-inline-start: 6px; padding: 3px;
  border-radius: 999px; border: 1px solid var(--line-2, rgba(244,243,240,0.14));
  background: rgba(244,243,240,0.05);
  order: 2;
}
.lang-switch__btn {
  font-family: var(--f-ui); font-size: 12.5px; font-weight: 600;
  padding: 6px 12px; border: none; border-radius: 999px; cursor: pointer;
  background: none; color: rgba(244,243,240,0.6);
  transition: background-color 0.2s ease, color 0.2s ease;
}
.lang-switch__btn.is-on { background: var(--ember); color: #0a0a0c; }
.lang-switch__btn:hover:not(.is-on) { color: #fff; }
@media (max-width: 860px) {
  .lang-switch { margin-inline-start: auto; }
}

/* ------------------------------------------------------------- burger --- */
/* Hidden on desktop; toggles .nav__mobile below 860px (see media query
   further down and mobileNav() in for-business.js). */
.nav__burger {
  display: none; order: 4;
  align-items: center; justify-content: center; flex-direction: column; gap: 4px;
  width: 40px; height: 40px; padding: 0; margin-inline-start: 6px;
  border: 1px solid rgba(244,243,240,0.14); border-radius: 10px;
  background: rgba(244,243,240,0.05); color: inherit; cursor: pointer;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}
.nav__burger:hover { background: rgba(244,243,240,0.1); border-color: rgba(244,243,240,0.24); }
.nav__burger span {
  display: block; width: 18px; height: 1.5px; border-radius: 2px;
  background: currentColor;
  transition: transform 0.25s var(--e-out), opacity 0.2s ease;
}
.nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.nav__burger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* scroll progress hairline pinned under the bar */
.nav__prog {
  position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  transform: scaleX(var(--p, 0)); transform-origin: left;
  background: linear-gradient(90deg, var(--ember), var(--ember-lift));
  box-shadow: 0 0 14px rgba(240,169,74,0.7);
}

@media (max-width: 860px) {
  .nav__in { height: 66px; }
  .logo__mark { width: 30px; height: 34px; }
  .logo__word { width: 84px; height: 31px; }

  .nav__mobile {
    display: flex;
    position: absolute; top: 100%; left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 4px;
    padding: 10px 20px 22px;
    background: rgba(8,8,10,0.97);
    backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
    border-top: 1px solid rgba(244,243,240,0.1);
    box-shadow: 0 20px 40px -30px rgba(0,0,0,0.9);
    /* closed state: faded, lifted slightly, and pulled out of the tab
       order / a11y tree once the fade has actually finished (the delayed
       visibility transition), rather than snapping away mid-animation */
    opacity: 0; visibility: hidden; pointer-events: none;
    transform: translateY(-6px) scaleY(0.97); transform-origin: top;
    transition: opacity 0.22s var(--e-out), transform 0.22s var(--e-out), visibility 0s linear 0.22s;
  }
  .nav__mobile.is-open {
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: translateY(0) scaleY(1);
    transition: opacity 0.22s var(--e-out), transform 0.22s var(--e-out), visibility 0s linear 0s;
  }
  .nav__mobile .nav__links { display: flex; flex-direction: column; gap: 2px; margin-inline-start: 0; }
  .nav__mobile .nav__links a { padding: 12px 10px; }
  .nav__mobile .nav__cta { margin-inline-start: 0; margin-top: 8px; width: 100%; justify-content: center; }

  /* In the dropdown it is a full-width row like the owner CTA below it, and
     order 3 keeps it above that one — the quieter action read first, then the
     one this page is actually selling. */
  .nav__mobile .nav__app {
    margin-inline-start: 0; margin-top: 10px; width: 100%; justify-content: center;
    min-height: 46px; font-size: 15px;
  }
  .nav__mobile .nav__app svg { opacity: 1; }

  .nav__burger { display: inline-flex; }
}


/* ============================================================================
   HERO
   ========================================================================== */
.hero {
  position: relative;
  padding: clamp(104px, 13vh, 158px) 0 clamp(48px, 6vw, 80px);
  overflow: hidden;
}
/* an ember pool behind the headline, and a hairline horizon under the fold */
.hero__glow {
  position: absolute; inset: -20% -10% auto -30%; height: 120%;
  background: radial-gradient(58% 52% at 22% 34%, rgba(240,169,74,0.20), transparent 68%);
  pointer-events: none;
}
.hero__ghost {
  position: absolute; left: -8vw; top: 4vh;
  width: min(58vw, 720px); height: auto;
  fill: #ffffff; opacity: 0.035; pointer-events: none;
  animation: ghostSpin 90s linear infinite;
}
@keyframes ghostSpin { to { transform: rotate(360deg); } }

.hero__grid {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  gap: clamp(40px, 5vw, 76px); align-items: center;
}

.hero__title {
  font-size: clamp(38px, 5.3vw, 74px);
  font-weight: 600;
  font-variation-settings: "SOFT" 24, "WONK" 1, "opsz" 96;
  line-height: 1.0;
  letter-spacing: -0.032em;
  margin: 0;
}
.hero__title .line { display: block; overflow: hidden; padding-bottom: 0.06em; }
.hero__title .w { display: inline-block; will-change: transform; }
.js .hero__title .w { transform: translateY(112%) rotate(3deg); opacity: 0; }
.js .hero__title.is-in .w {
  transform: none; opacity: 1;
  transition: transform 0.95s var(--e-out) var(--d, 0ms), opacity 0.7s ease var(--d, 0ms);
}

/* the promise word: ember, with a rule that draws itself underneath */
.hero__accent { position: relative; color: var(--ember); white-space: nowrap; }
.hero__accent svg {
  position: absolute; left: 0; right: 0; bottom: -0.03em; width: 100%; height: 0.2em;
  overflow: visible;
}
.hero__accent path {
  fill: none; stroke: var(--ember); stroke-width: 5; stroke-linecap: round;
  stroke-dasharray: 210; stroke-dashoffset: 210;
  filter: drop-shadow(0 2px 10px rgba(240,169,74,0.55));
}
.js .hero__title.is-in .hero__accent path {
  animation: draw 0.9s var(--e-out) 1.05s forwards;
}
@keyframes draw { to { stroke-dashoffset: 0; } }

.hero__lede { font-size: clamp(17px, 1.5vw, 20px); color: var(--fg-dim); max-width: 46ch; margin-top: 26px; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 36px; }
.hero__notes { display: flex; flex-direction: column; gap: 10px; margin-top: 26px; }
/* start, not center: centring puts the tick in the gutter between lines as
   soon as a note wraps to two, instead of beside the sentence it marks. The
   nudge re-centres it on that first line. */
.hero__note {
  display: flex; align-items: flex-start; gap: 10px;
  font-size: 14.5px; color: var(--fg-faint);
}
.hero__note svg { color: var(--ember); flex: none; margin-top: 3px; }

/* ---------------------------------------------------------- the live card - */
/* This is the wallet's own card component (styles.css/app.js buildCard),
   reproduced here 1:1 — same markup shape, same glass/notch/stamp styling —
   just hardcoded with four sample cafés instead of built from live data. */
.demo { position: relative; display: flex; flex-direction: column; align-items: center; gap: 20px; }
/* the stack, plus the two things that bracket it: the tag it starts with and
   the reward it ends with, both floating free of the card stack */
.demo__scene { position: relative; width: 100%; max-width: 400px; }
.demo__stack { position: relative; width: 100%; }
.demo__caption {
  font-family: var(--f-mono); font-size: 11.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--fg-faint); text-align: center; margin: 0;
}

/* The stack deals itself in on load, and fans open under the pointer the way
   it does under a thumb in the app. `backwards` (not `both`) so the finished
   animation hands the transform back to the hover rules below. */
.wcard {
  position: relative;
  animation: dealIn 0.9s var(--e-spring) backwards;
  transition: transform 0.45s var(--e-out);
}
.wcard:not(:first-child) { margin-top: calc(-100% + 68px); }
.wcard:nth-child(1) { z-index: 1; filter: brightness(0.97) saturate(0.98); animation-delay: 0.25s; }
.wcard:nth-child(2) { z-index: 2; filter: brightness(0.97) saturate(0.98); animation-delay: 0.40s; }
.wcard:nth-child(3) { z-index: 3; filter: brightness(0.97) saturate(0.98); animation-delay: 0.55s; }
.wcard:nth-child(4) { z-index: 4; animation-delay: 0.70s; }
@keyframes dealIn {
  from { opacity: 0; transform: translateY(46px) scale(0.93); }
  to   { opacity: 1; transform: none; }
}
/* The cards behind rise; the front card holds its place, so the fan never
   pushes into the caption below it. */
.demo__stack:hover .wcard:nth-child(1) { transform: translateY(-30px); }
.demo__stack:hover .wcard:nth-child(2) { transform: translateY(-20px); }
.demo__stack:hover .wcard:nth-child(3) { transform: translateY(-10px); }

.wcard .card { position: relative; width: 100%; aspect-ratio: 1 / 1; }
.wcard .face {
  position: absolute; inset: 0; border-radius: 24px; padding: 18px 20px;
  display: flex; flex-direction: column;
  background:
    linear-gradient(158deg, rgba(255,255,255,0.16), rgba(255,255,255,0.02)),
    linear-gradient(158deg, var(--paper), var(--paper-2));
  border: 1px solid rgba(255,255,255,0.7);
  box-shadow:
    0 20px 38px -16px rgba(0,0,0,0.55),
    inset 0 1px 0 rgba(255,255,255,0.9),
    inset 0 0 0 1px rgba(255,255,255,0.22);
  overflow: hidden;
}
.wcard .face::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(130% 60% at 20% -10%, rgba(255,255,255,0.28), transparent 52%),
    linear-gradient(125deg, transparent 30%, rgba(255,255,255,0.08) 46%, transparent 60%);
}

/* ticket notches, the same detail the real wallet card has */
.notch {
  position: absolute; top: 50%; width: 20px; height: 20px; border-radius: 50%;
  background: var(--ink-900);
  box-shadow: inset 0 2px 6px rgba(0,0,0,0.5);
  transform: translateY(-50%); z-index: 2;
}
.notch--l { left: -10px; }
.notch--r { right: -10px; }

.oram-sheen {
  position: absolute; top: 0; left: 0; width: 55%; height: 100%;
  background: linear-gradient(105deg, transparent 20%, rgba(255,255,255,0.28) 50%, transparent 80%);
  transform: translateX(-170%) skewX(-12deg);
  pointer-events: none; z-index: 3;
  animation: oramSheen 7s ease-in-out infinite;
}
.wcard:nth-child(2) .oram-sheen { animation-delay: 1.4s; }
.wcard:nth-child(3) .oram-sheen { animation-delay: 2.8s; }
.wcard:nth-child(4) .oram-sheen { animation-delay: 4.2s; }
@keyframes oramSheen {
  0%   { transform: translateX(-170%) skewX(-12deg); }
  16%  { transform: translateX(340%) skewX(-12deg); }
  100% { transform: translateX(340%) skewX(-12deg); }
}

/* text-align:left, explicitly — harmless in the hero stack (already the
   default there), but this component also gets embedded inside .scene
   further down this file, which sets text-align:center for its own
   headings; without this the café name inherits that and centers too. */
.oram-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; position: relative; z-index: 1; text-align: left; }
.oram-head__main { display: flex; align-items: center; gap: 12px; min-width: 0; }
.oram-head__id { min-width: 0; display: flex; flex-direction: column; justify-content: center; }
.oram-mini { flex: 0 0 auto; display: flex; align-items: baseline; gap: 2px; font-family: var(--f-ui); line-height: 1; }
.oram-mini .count { font-weight: 700; font-size: 22px; color: #fff; }
.oram-mini__sep { font-size: 16px; color: rgba(255,255,255,0.5); }
.oram-mini > span:last-child { font-size: 16px; color: rgba(255,255,255,0.56); font-weight: 700; }
.oram-name {
  font-family: var(--f-display); font-weight: 600; font-size: 20px; letter-spacing: 0.02em;
  margin: 0; color: #fff; text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.oram-sub { margin: 3px 0 0; font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(255,255,255,0.72); }

.grid { flex: 1; display: grid; grid-template-columns: repeat(var(--cols, 4), 1fr); grid-template-rows: repeat(2, 1fr); gap: 7px 6px; position: relative; z-index: 1; }
.slot {
  position: relative; border-radius: 50%; display: grid; place-items: center;
  aspect-ratio: 1; align-self: center; justify-self: center; width: 100%; max-width: 84px;
  background: rgba(255,255,255,0.3);
  border: 1.5px dashed rgba(255,255,255,0.68);
}
.slot__num { position: absolute; font-size: 9px; font-weight: 700; bottom: 2px; left: 50%; transform: translateX(-50%); color: rgba(255,255,255,0.56); }
.slot .stamp { position: absolute; inset: 0; display: grid; place-items: center; opacity: 0; z-index: 1; }
.slot .stamp img { width: 90%; height: 90%; object-fit: contain; display: block; }
.slot.is-on { border-color: rgba(0,0,0,0.08); }
.slot.is-on .stamp { opacity: 1; }
.slot.is-on .slot__num { opacity: 0; }

/* ---------------------------------------------------------------------------
   The front card fills itself: the stamp lands, then all eight circles ripple
   in one after another — a customer's whole loyalty card, played back in
   fast-forward. Same 12s duration as the stamp above; each circle's own
   animation-delay is T0 + 2.52s (the instant of impact) plus a 110ms ripple
   per --i, so circle 1 lands the moment the rubber hits and circle 8 follows
   0.77s later.
   --------------------------------------------------------------------------- */
.wcard:nth-child(4) .slot.is-on,
.wcard:nth-child(4) .slot.is-on::after,
.wcard:nth-child(4) .slot.is-on .stamp,
.wcard:nth-child(4) .slot.is-on .slot__num {
  animation-duration: 12s;
  animation-iteration-count: infinite;
  animation-delay: calc(4.42s + var(--i) * 0.11s);
  /* `backwards` so the card sits empty through the delay — without it the
     stamps show at full strength until their first cycle starts, and the card
     contradicts the counter and the stamp tool for the first few seconds
     after load. */
  animation-fill-mode: backwards;
}
.wcard:nth-child(4) .slot.is-on          { animation-name: slotFill;  animation-timing-function: linear; }
.wcard:nth-child(4) .slot.is-on .stamp   { animation-name: stampDrop; animation-timing-function: var(--e-out); }
.wcard:nth-child(4) .slot.is-on .slot__num { animation-name: numOut;  animation-timing-function: linear; }
/* the shockwave ring the app pulses the moment a stamp lands */
.wcard:nth-child(4) .slot.is-on::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; pointer-events: none;
  animation-name: stampRing; animation-timing-function: ease-out;
}

@keyframes stampDrop {
  0%        { opacity: 0; transform: scale(1.65) rotate(-16deg); }
  3%        { opacity: 1; transform: scale(0.9) rotate(2deg); }
  6%, 70%   { opacity: 1; transform: scale(1) rotate(0deg); }
  74%, 100% { opacity: 0; transform: scale(0.82) rotate(0deg); }
}
@keyframes slotFill {
  0%, 2%    { border-color: rgba(255,255,255,0.68); }
  4%, 70%   { border-color: rgba(0,0,0,0.08); }
  74%, 100% { border-color: rgba(255,255,255,0.68); }
}
@keyframes numOut {
  0%, 2%    { opacity: 1; }
  4%, 70%   { opacity: 0; }
  74%, 100% { opacity: 1; }
}
@keyframes stampRing {
  0%, 2%    { opacity: 0.85; box-shadow: 0 0 0 0 rgba(255,255,255,0.5); }
  11%, 100% { opacity: 0;    box-shadow: 0 0 0 20px rgba(255,255,255,0); }
}

/* The counter is a strip of digits behind a one-digit window, stepped to the
   stamp's own T0 so it counts 0→8 exactly as the ripple lands, then resets
   just before the stamp comes down again next cycle. */
.wcard:nth-child(4) .oram-mini { align-items: flex-end; }
.wcard:nth-child(4) .count { display: inline-block; height: 1em; line-height: 1; overflow: hidden; }
.count__roll {
  display: block;
  animation: countRoll 12s steps(1) 1.9s infinite backwards;
}
.count__roll i { display: block; height: 1em; line-height: 1; font-style: normal; }
@keyframes countRoll {
  0%        { transform: translateY(0); }      /* empty card */
  21%       { transform: translateY(-1em); }    /* the stamp lands — each circle steps the count up */
  21.9%     { transform: translateY(-2em); }
  22.83%    { transform: translateY(-3em); }
  23.75%    { transform: translateY(-4em); }
  24.67%    { transform: translateY(-5em); }
  25.58%    { transform: translateY(-6em); }
  26.5%     { transform: translateY(-7em); }
  27.42%    { transform: translateY(-8em); }    /* full — holds through the reward */
  90%       { transform: translateY(-8em); }
  91.25%    { transform: translateY(-7em); }    /* resets in the same quick beat the circles clear in */
  92.5%     { transform: translateY(-6em); }
  93.75%    { transform: translateY(-5em); }
  95%       { transform: translateY(-4em); }
  96.25%    { transform: translateY(-3em); }
  97.5%     { transform: translateY(-2em); }
  98.75%    { transform: translateY(-1em); }
  100%      { transform: translateY(0); }
}

/* The whole face flashes gold the instant the eighth circle lands — the
   "card complete" beat that used to only live in the (now-removed) win
   screen. Same T0, timed just after the ripple finishes. */
.face__complete {
  position: absolute; inset: 0; z-index: 5; pointer-events: none; border-radius: inherit;
  background: radial-gradient(120% 120% at 50% 38%, rgba(255,214,145,0.95), rgba(255,177,68,0.3) 55%, transparent 78%);
  opacity: 0;
  animation: cardComplete 12s ease-out 1.9s infinite backwards;
}
@keyframes cardComplete {
  0%, 27%   { opacity: 0; }
  29.5%     { opacity: 0.95; }
  37%       { opacity: 0; }
  100%      { opacity: 0; }
}

/* ============================================================================
   ONE STORY, ONE CLOCK
   Everything in the scene — the stamp, the eight circles, the counter, the
   card's gold flash, the reward ticket — shares a single 12s animation-
   duration. CSS keeps animations with the same duration in lockstep forever,
   no matter when the page loaded, so giving every piece here the same 12s
   duration (and offsetting each with animation-delay instead of a duration
   of its own) is what makes the tap, the fill and the reward read as one
   continuous beat instead of three loops that happen to overlap.
   T0 = 1.9s (when the stamp's pop-in entrance finishes) is the shared zero
   for the stamp, the counter, the card's flash and the ticket. The eight
   circles land on their own staggered delays starting at T0 + 2.52s, the
   instant the rubber hits — see "the front card fills itself" below.
   ========================================================================== */

/* ========================================================= the stamp tool ==
   The rubber stamp from the wallet's own redeem animation (index.html
   #stamper): cocked, dropped, squashed on impact, bounced off — once per
   12s cycle, right as the visitor's eye lands on the scene.
   ========================================================================== */
.seal {
  position: absolute; z-index: 6; top: -14px; left: -92px;
  width: 90px; height: 112px;
  transform-origin: 50% 92%; /* pivots near its own base, like a hand holding it */
  filter: drop-shadow(0 16px 18px rgba(0,0,0,0.55));
  animation: sealPress 12s ease-in-out 1.9s infinite,
             popIn 0.8s var(--e-spring) 1.1s backwards;
}
@keyframes popIn {
  from { opacity: 0; transform: scale(0.6) translateY(16px) rotate(-6deg); }
  to   { opacity: 1; transform: scale(1) translateY(0) rotate(-6deg); }
}
@keyframes sealPress {
  0%, 15% { transform: translateY(0) rotate(-6deg); }          /* resting, poised over the counter */
  18%     { transform: translateY(-16px) rotate(-17deg); }     /* cocked back */
  21%     { transform: translateY(11px) rotate(3deg); }        /* slams down — this instant is T0+2.52s, when the first circle lands */
  23%     { transform: translateY(8px) rotate(3deg) scale(1, 0.93); } /* squashes on impact */
  27%     { transform: translateY(-3px) rotate(-5deg); }       /* bounces off */
  30%, 100% { transform: translateY(0) rotate(-6deg); }        /* its part of the story is over; it just rests through the reveal */
}
.seal__tool { position: relative; z-index: 2; display: block; width: 100%; height: 100%; }
/* engraved into the rubber face, the same warm tone the app's star imprint used */
.seal__mark {
  font-family: var(--f-mono); font-size: 15px; font-weight: 700;
  letter-spacing: 0.1em; fill: rgba(190,120,70,0.55);
}
/* the ink hitting the counter, timed to the moment the rubber lands */
.seal__impact {
  position: absolute; left: 50%; bottom: 4px; z-index: 1;
  width: 60px; height: 18px; margin-left: -30px; border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(240,169,74,0.6), transparent 72%);
  opacity: 0;
  animation: sealImpact 12s ease-out 1.9s infinite backwards;
}
@keyframes sealImpact {
  0%, 19%   { opacity: 0; transform: scale(0.4); }
  21%       { opacity: 0.9; transform: scale(1); }
  30%, 100% { opacity: 0; transform: scale(1.7); }
}

/* ========================================================== the reward =====
   The app's own ticket — scalloped edges, dashed rules, the discount in the
   page's ember. It sits over the bottom-right of the stack, where the circles
   the demo never fills are, so it hides nothing that moves.
   ========================================================================== */
.prize {
  position: absolute; z-index: 6; right: -110px; bottom: 6px;
  width: 210px;
  /* hidden until the card completes, springs in, holds through the read,
     clears out before the stamp comes down again — same T0 as the stamp
     and the counter, so it always arrives right as the eighth circle lands.
     `backwards` matters here: without it, the ticket sits fully visible for
     the entire 1.9s delay instead of hidden, since nothing else declares its
     resting opacity/transform. */
  animation: prizeCycle 12s ease-in-out 1.9s infinite backwards;
}
@keyframes prizeCycle {
  0%, 28%  { opacity: 0; transform: scale(0.7) translateY(30px) rotate(11deg); }
  34%      { opacity: 1; transform: scale(1.06) translateY(-6px) rotate(1deg); } /* arrives with a bounce, right on the card's gold flash */
  40%      { transform: scale(1) translateY(0) rotate(4deg); }                  /* settles */
  50%      { transform: scale(1) translateY(-8px) rotate(1.5deg); }             /* a slow held breath while it's read */
  60%      { transform: scale(1) translateY(0) rotate(4deg); }
  70%      { transform: scale(1) translateY(-6px) rotate(2deg); }
  77%      { opacity: 1; transform: scale(1) translateY(0) rotate(4deg); }
  82%, 100%{ opacity: 0; transform: scale(0.72) translateY(28px) rotate(10deg); } /* clears out before the next tap */
}
.prize__glow {
  position: absolute; inset: -20% -16%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(240,169,74,0.32), transparent 70%);
  filter: blur(12px); pointer-events: none;
}
.prize__card {
  position: relative; overflow: hidden;
  padding: 12px 16px 11px; text-align: center;
  border-radius: 15px;
  background: linear-gradient(158deg, #fbfaf7 0%, #f2efe9 55%, #e6e2da 100%);
  color: #17161a;
  /* the ticket's punched edge, the same mask the app's ticket uses */
  --sc: 8px;
  -webkit-mask:
    radial-gradient(var(--sc) at 50% 0, #0000 98%, #000 100%) 50% 0 / calc(var(--sc) * 2) 51% repeat-x,
    radial-gradient(var(--sc) at 50% 100%, #0000 98%, #000 100%) 50% 100% / calc(var(--sc) * 2) 51% repeat-x;
          mask:
    radial-gradient(var(--sc) at 50% 0, #0000 98%, #000 100%) 50% 0 / calc(var(--sc) * 2) 51% repeat-x,
    radial-gradient(var(--sc) at 50% 100%, #0000 98%, #000 100%) 50% 100% / calc(var(--sc) * 2) 51% repeat-x;
}
.prize__sweep {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: linear-gradient(112deg, transparent 36%, rgba(255,255,255,0.85) 50%, transparent 64%);
  transform: translateX(-130%);
  /* one crossing per cycle, right as the ticket settles in — same T0 */
  animation: prizeSweep 12s ease-in-out 1.9s infinite backwards;
}
@keyframes prizeSweep {
  0%, 39%   { transform: translateX(-130%); }
  47%, 100% { transform: translateX(130%); }
}
/* 🎉 and the title share a line — the ticket has to stay short enough to clear
   the top row of circles, which is the row the demo actually fills */
.prize__head { display: flex; align-items: center; justify-content: center; gap: 7px; margin: 0; }
.prize__pop { font-size: 16px; line-height: 1; }
.prize__title {
  font-family: var(--f-display); font-size: 14.5px; font-weight: 600;
  background: linear-gradient(100deg, #17161a 0%, #17161a 38%, #b9954f 50%, #17161a 62%, #17161a 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text; color: transparent;
  animation: prizeShine 12s ease-in-out 1.9s infinite backwards;
}
@keyframes prizeShine {
  0%, 39%   { background-position: 200% 0; }
  47%, 100% { background-position: -60% 0; }
}
.prize__rule { display: block; border-top: 2px dashed rgba(22,21,26,0.18); margin: 7px 0; }
.prize__pct {
  margin: 0; font-family: var(--f-display); font-size: 34px; font-weight: 600; line-height: 1;
  font-variation-settings: "SOFT" 40, "WONK" 1;
  background: linear-gradient(96deg, #c8761c, #ee9f34 45%, #ffc271);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.prize__off { margin: 5px 0 0; font-size: 12.5px; font-weight: 600; color: #4b4740; }
.prize__code {
  display: inline-block; margin: 0;
  font-family: var(--f-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.16em;
  color: #17161a; background: rgba(22,21,26,0.06);
  border: 1px dashed rgba(22,21,26,0.28); border-radius: 8px; padding: 5px 10px;
}
.prize__hint {
  margin: 7px 0 0; font-family: var(--f-mono); font-size: 9px;
  letter-spacing: 0.14em; text-transform: uppercase; color: #6f6b63;
}

/* the floats tuck in as the margin beside the wrap runs out */
@media (max-width: 1340px) {
  .seal { left: -60px; }
  .prize { right: -62px; }
}
@media (max-width: 940px) {
  .hero__grid { grid-template-columns: 1fr; }
  /* stacked single-column, .hero__copy and .demo now share the section's
     full width/height instead of sitting side by side — centering the
     ghost logo and letting it run a bit bigger reads as one watermark
     behind the whole section rather than a sliver pinned to an edge.
     margin-inline:auto rather than transform:translateX(-50%) to center
     it, since .hero__ghost's own `transform` is already spoken for by
     the ghostSpin rotation — a second, static transform on the same
     property would just get overwritten by the animation every frame. */
  .hero__ghost { left: 0; right: 0; top: 6vh; width: min(88vw, 480px); margin-inline: auto; }
  /* no margin to hover in at this width, so the stamp clears the stack
     entirely rather than sitting on the top card's name — the only thing a
     peeked card has to show. The extra margin above buys it that room. */
  .demo { margin-top: 64px; }
  .seal { top: -78px; left: auto; right: 8px; width: 64px; height: 80px; }
  .prize { right: -10px; width: 178px; }
  .prize__pct { font-size: 36px; }
}

/* ============================================================================
   SHARED SURFACE
   ========================================================================== */
.glass {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: var(--shadow);
}

/* ============================================================================
   HOW IT WORKS
   ========================================================================== */
/* two moments, one switcher — a plain pill toggle, ember on the active side */
.how__tabs {
  display: inline-flex; gap: 4px; padding: 4px;
  margin-bottom: clamp(28px, 4vw, 44px);
  background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
}
.how__tab {
  border: 0; border-radius: 999px; cursor: pointer;
  padding: 11px 20px; min-height: 44px;
  font-family: var(--f-ui); font-size: 14px; font-weight: 600;
  background: transparent; color: var(--fg-dim);
  transition: background-color 0.25s ease, color 0.25s ease;
}
.how__tab:hover { color: var(--fg); }
.how__tab.is-on { background: var(--ember); color: var(--accent-fg); }
.how__grid {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  gap: clamp(30px, 5vw, 70px); align-items: start;
}
/* belt-and-braces: .how__grid's own `display: grid` and the UA `[hidden]`
   rule are equal specificity, so without this the later one in the cascade
   (ours) can win and leave a "hidden" panel visible. */
.how__grid[hidden] { display: none; }
.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.step {
  position: relative; width: 100%; text-align: start; cursor: pointer;
  display: grid; grid-template-columns: auto 1fr; gap: 18px;
  padding: 20px 22px; border-radius: 20px;
  background: transparent; border: 1px solid transparent;
  color: inherit; font: inherit;
  transition: background-color 0.35s ease, border-color 0.35s ease, transform 0.35s var(--e-out);
}
.step:hover { background: var(--surface); }
.step.is-on { background: var(--surface); border-color: var(--line-2); }
.step__n {
  font-family: var(--f-mono); font-size: 12px; font-weight: 600; letter-spacing: 0.1em;
  color: var(--fg-faint); padding-top: 4px;
  transition: color 0.3s ease;
}
/* steps 2 and 3 only ever happen on a customer's first tap — nudged right
   and labelled so that reads at a glance instead of looking like every step
   in the list carries equal weight on every visit. */
.steps__group { padding-inline-start: 42px; }
.steps__grouplabel {
  margin: 0 0 8px; font-family: var(--f-mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ember);
}
.steps__sub { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
/* per-step stand-in for .steps__grouplabel, used only once the carousel
   below 940px turns each step into its own slide — .steps__grouplabel
   itself is hidden there since a header shared by two slides that are
   never on screen together stops making sense. */
.step__kicker {
  display: none; margin: 0 0 6px; font-family: var(--f-mono); font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ember);
}
.step.is-on .step__n { color: var(--ember); }
.step h3 { font-size: 19px; margin-bottom: 6px; }
.step p {
  font-size: 15px; color: var(--fg-dim);
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height 0.5s var(--e-out), opacity 0.35s ease, margin-top 0.4s var(--e-out);
}
.step.is-on p { max-height: 160px; opacity: 1; }
.step__bar {
  position: absolute; inset-inline-start: 0; top: 14px; bottom: 14px; width: 2px;
  background: var(--line); border-radius: 2px; overflow: hidden;
}
.step__bar span {
  display: block; width: 100%; height: 100%; background: var(--ember);
  transform: scaleY(0); transform-origin: top;
}
.step.is-on .step__bar span { animation: fill 6.4s linear forwards; }
.is-manual .step.is-on .step__bar span { animation: none; transform: scaleY(1); }
@keyframes fill { to { transform: scaleY(1); } }

/* prev/next + "N / total" — only meaningful once .steps below 940px
   shows one step at a time (see that media query), so hidden above it
   where every step already sits in the list and is its own click target. */
.steps__nav, .steps__hint { display: none; }
@media (max-width: 940px) {
  /* Swipe turns the page (see stepSwipe in for-business.js); these are the
     position indicator, and a tap target for anyone who would rather jump. The
     active one stretches to a pill so the current step is legible at a glance
     without a "3 / 5" readout to parse. */
  /* ltr even in Persian: swipe-left advances (see stepSwipe), so progress runs
     left to right and the first step's dot has to be the leftmost one. */
  .steps__nav { display: flex; direction: ltr; align-items: center; justify-content: center; gap: 8px; margin-top: 18px; }
  .steps__dot {
    width: 8px; height: 8px; padding: 0; border: 0; border-radius: 999px;
    background: rgba(255,255,255,0.22); cursor: pointer;
    transition: background-color 0.35s var(--e-out), width 0.35s var(--e-spring);
  }
  .steps__dot:hover { background: rgba(255,255,255,0.4); }
  .steps__dot.is-on { width: 24px; background: var(--ember); }
  /* Swipe is invisible until someone tries it, so say so — and animate the
     arrow in the direction the gesture actually goes. It retires itself the
     first time the visitor drives the carousel by any means (see stepSwipe). */
  .steps__hint {
    /* between the phone and the cards (see .phone's order below): it explains
       the gesture at the moment the eye crosses from one to the other. Tight
       margins so it reads as a caption on the gap, not a band of its own. */
    order: -1; display: flex; align-items: center; justify-content: center; gap: 7px;
    margin: 0 0 4px; font-size: 12.5px; color: var(--fg-faint);
    transition: opacity 0.5s var(--e-out), height 0.5s var(--e-out), margin 0.5s var(--e-out);
  }
  .steps__hint svg { flex: none; animation: swipeHint 1.9s ease-in-out infinite; }
  @keyframes swipeHint {
    0%, 100% { transform: translateX(3px); opacity: 0.45; }
    50%      { transform: translateX(-4px); opacity: 1; }
  }
  .steps__hint.is-done { opacity: 0; height: 0; margin: 0; pointer-events: none; }
  @media (prefers-reduced-motion: reduce) { .steps__hint svg { animation: none; } }

  /* horizontal gestures belong to the carousel, vertical ones to the page */
  .steps { touch-action: pan-y; }
}

/* the phone that keeps pace with the step list */
.phone {
  position: sticky; top: 84px;
  justify-self: center;
  /* 1170:2532 is the frame's aspect ratio (a real iPhone's, so app
     screenshots drop in uncropped), and 0.462 is its inverse — the third term
     is "as wide as it can be while the whole phone still fits the viewport it
     is pinned to". The 140px it subtracts is the 84px it sits below the nav
     plus room to breathe underneath — raise `top` and this has to come down
     with it, or tall phones get clipped at the bottom of short viewports.
     The trailing "- 4.62px" trims 10px off the phone's rendered height —
     4.62 = 10 * (1170/2532), the width cut that the aspect-ratio carries
     through to exactly a 10px shorter frame, at any of the three widths
     above, instead of distorting it by touching height directly. */
  width: calc(min(330px, 78vw, calc((100vh - 140px) * 0.462)) - 4.62px);
}
.phone__frame {
  position: relative; aspect-ratio: 1170 / 2532;
  border-radius: 44px; padding: 11px;
  background: linear-gradient(160deg, #2a2a33, #101014 60%);
  box-shadow: 0 50px 90px -40px rgba(0,0,0,0.95), inset 0 1px 0 rgba(255,255,255,0.16);
}
.phone__screen {
  position: relative; height: 100%; overflow: hidden;
  border-radius: 34px;
  background: linear-gradient(168deg, #f7f6f3, #e6e3dc);
  color: #17161a;
}
.phone__notch {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  width: 84px; height: 20px; border-radius: 999px; background: #101014; z-index: 4;
}
.scene {
  position: absolute; inset: 0;
  display: grid; place-items: center; text-align: center; padding: 30px 24px;
  /* opaque, and the incoming one sits on top: with a full-bleed screenshot in
     the deck, two transparent scenes crossfading let the photo ghost straight
     through whatever follows it */
  background: linear-gradient(168deg, #f7f6f3, #e6e3dc);
  /* default rest state: not reached yet — parked just off to the right,
     waiting to slide in. The step order plays out left-to-right across the
     screen instead of everything just rising in from below in place. */
  opacity: 0; transform: translateX(28px) scale(0.98);
  transition: opacity 0.45s ease, transform 0.6s var(--e-out);
  pointer-events: none;
}
/* a scene with a later `.is-on` sibling has already had its turn — instead
   of falling back to the same "waiting on the right" rest state, park it to
   the left, so the active scene always arrives from the right and leaves
   toward the left as the steps advance. */
.scene:has(~ .scene.is-on) { transform: translateX(-28px) scale(0.98); }
.scene.is-on { opacity: 1; transform: none; z-index: 2; }
.scene h4 { font-size: 19px; margin-bottom: 6px; }
.scene p { font-size: 13.5px; color: #6e6a63; }

/* a real screenshot fills the screen edge to edge instead of sitting in the
   132px art slot, where a whole phone screen would be illegible. The caption
   moves onto a scrim at the bottom rather than being dropped. */
.scene--shot { display: block; padding: 0; }
.scene__shot { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
/* a frosted bar rather than a gradient fade — over a screenshot that already
   has UI in it, a fade just reads as a smudge, where a bar reads as chrome */
.scene__cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 16px 20px 20px; text-align: center;
  background: rgba(244,242,238,0.9);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-top: 1px solid rgba(22,21,26,0.08);
}
.scene__art { height: 132px; display: grid; place-items: center; margin-bottom: 22px; }
/* The non-shot scenes wrap their art + copy in a bare <div> so .scene's
   place-items:center can center it as one block (see for-business.html).
   That div has no width of its own, though, so it (and everything inside
   it, including .req-card below) sizes to its content's natural width —
   fine on the original ~330px phone, but the mobile carousel's phone can
   render much narrower than that, and content wider than the frame just
   overflows past it uncontained. Stretching this div to the scene's own
   (padded) width gives every fixed-size mock inside it something real to
   scale against via width:min(...,100%) / max-width:100%. */
.scene > div { width: 100%; }

.tag {
  position: relative; width: 76px; max-width: 100%; aspect-ratio: 1; border-radius: 50%;
  display: grid; place-items: center; color: #f6f5f2;
  background: linear-gradient(150deg, #23212a, #101014);
  box-shadow: 0 14px 26px -12px rgba(0,0,0,0.7);
}
.tag::before, .tag::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid rgba(240,169,74,0.7);
  animation: ripple 2.4s ease-out infinite;
}
.tag::after { animation-delay: 1.2s; }
@keyframes ripple {
  0%   { transform: scale(1);   opacity: 0.85; }
  100% { transform: scale(1.9); opacity: 0; }
}

.mini-field {
  width: 168px; margin: 0 auto 10px; padding: 11px 14px; border-radius: 12px;
  background: rgba(255,255,255,0.85); border: 1px solid rgba(22,21,26,0.14);
  font-size: 14px; text-align: left; color: #17161a;
}
.caret { display: inline-block; width: 1.5px; height: 15px; margin-left: 2px; background: var(--ember-deep); vertical-align: -2px; animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

.home-icon {
  width: 78px; max-width: 100%; aspect-ratio: 1; border-radius: 20px; display: grid; place-items: center;
  background: #17161a; color: #f6f5f2;
  box-shadow: 0 16px 28px -14px rgba(0,0,0,0.8);
  animation: pop 3.2s var(--e-spring) infinite;
}
.home-icon svg { width: 46px; height: auto; fill: currentColor; }
@keyframes pop { 0%,72%,100% { transform: scale(1); } 82% { transform: scale(1.08); } }

/* .scene centers its content by default (place-items:center); this one
   scene nudges left instead so the card doesn't sit dead-centre. */
.scene__body--left { transform: translateX(-14px); }

/* the customer's own card mid-fill — not a redrawn stand-in but the exact
   same .wcard/.oram-head/.grid/.slot component the hero demo uses (and the
   real knot-stamp.png), just held at its natural ~220px size and shrunk with
   a CSS transform instead of being resized directly — resizing directly
   would leave every fixed-px value (the 20px name, the 20px notches, the
   9px slot numbers) at the wrong size relative to the card, which is exactly
   what a transform scale avoids. The app's reward-teaser box sits underneath
   it at native size, since that one only has to stay legible, not to scale. */
.scene__art--card { display: flex; flex-direction: column; align-items: center; gap: 10px; height: auto; }
.mini-wcard-frame { position: relative; width: 200px; height: 200px; }
.mini-wcard-frame .wcard {
  /* 260px, not 200px — .oram-sub has no nowrap, so a tagline this long needs
     that much room to stay on one line the way it does on the real card. */
  position: absolute; top: 0; left: 0; width: 260px; height: 260px;
  transform: scale(0.769); transform-origin: top left;
  /* neither applies to a single static replica the way it does in the live
     hero stack — without turning them off, the hero's entrance animation
     plays this card at full size for its first ~0.9s before snapping down. */
  animation: none; transition: none; filter: none;
}

.mini-teaser {
  display: flex; align-items: center; gap: 8px; width: 220px; text-align: left;
  padding: 9px 12px; border-radius: 12px;
  background: linear-gradient(120deg, var(--paper-3), var(--paper-2));
  border: 1px solid rgba(255,255,255,0.16);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.14), 0 10px 20px -12px rgba(0,0,0,0.5);
  font-size: 11px; line-height: 1.35; color: #fff;
}
.mini-teaser b { font-weight: 800; }
.mini-teaser__gift { flex: none; font-size: 16px; line-height: 1; animation: giftBob 2.4s ease-in-out infinite; }
@keyframes giftBob { 0%, 100% { transform: translateY(0) rotate(-6deg); } 50% { transform: translateY(-3px) rotate(6deg); } }

/* the staff panel's own incoming-request card, shrunk to fit the phone mockup.
   min(...,100%) rather than a bare 168px so it scales down instead of
   overflowing on the mobile carousel's narrower phone (see ".scene > div"
   above) — its own padding stays fixed, so at the smallest phone sizes
   the padding-to-content ratio just gets a little more generous instead
   of the card breaking layout. */
.req-card {
  width: min(168px, 100%); padding: 14px 16px; border-radius: 16px; text-align: left;
  background: rgba(255,255,255,0.9); border: 1px solid rgba(22,21,26,0.12);
  box-shadow: 0 14px 26px -14px rgba(0,0,0,0.3);
  animation: pop 3.2s var(--e-spring) infinite;
}
.req-card__name { margin: 0; font-size: 14px; font-weight: 700; color: #17161a; }
.req-card__sub { margin: 2px 0 12px; font-size: 11.5px; color: #8a8680; }
.req-card__btns { display: flex; gap: 8px; }
.req-card__btn { flex: 1; height: 34px; border-radius: 10px; display: grid; place-items: center; }
.req-card__btn--ok { background: #17161a; color: #f6f5f2; }
.req-card__btn--no { background: transparent; border: 1px solid rgba(22,21,26,0.2); color: #6e6a63; }

/* ---- scene art for "using a reward" ---- */
/* max-width:100% on these (qr-mini/scan-frame below, same reasoning as
   .req-card above) is a safety net more than an active fix at today's
   sizes — they're already narrower than the phone gets on typical
   viewports — but the mobile phone's width scales with viewport height
   (52vh), so a short-enough screen could still shrink it past their
   fixed 92px. */
.qr-mini {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
  width: 92px; max-width: 100%; aspect-ratio: 1; padding: 10px; border-radius: 14px;
  background: #ffffff; box-shadow: 0 14px 26px -12px rgba(0,0,0,0.35);
}
.qr-mini i { border-radius: 2px; background: rgba(23,22,26,0.12); }
.qr-mini i:nth-child(1), .qr-mini i:nth-child(2), .qr-mini i:nth-child(4),
.qr-mini i:nth-child(5), .qr-mini i:nth-child(7), .qr-mini i:nth-child(9),
.qr-mini i:nth-child(10), .qr-mini i:nth-child(12), .qr-mini i:nth-child(13),
.qr-mini i:nth-child(15), .qr-mini i:nth-child(16) { background: #17161a; }

.scan-frame { position: relative; width: 92px; max-width: 100%; aspect-ratio: 1; }
.scan-frame .c { position: absolute; width: 20px; height: 20px; border: 2.5px solid #17161a; }
.scan-frame .tl { top: 0; left: 0; border-right: 0; border-bottom: 0; border-radius: 6px 0 0 0; }
.scan-frame .tr { top: 0; right: 0; border-left: 0; border-bottom: 0; border-radius: 0 6px 0 0; }
.scan-frame .bl { bottom: 0; left: 0; border-right: 0; border-top: 0; border-radius: 0 0 0 6px; }
.scan-frame .br { bottom: 0; right: 0; border-left: 0; border-top: 0; border-radius: 0 0 6px 0; }
.scan-frame .scan-line {
  position: absolute; left: 6px; right: 6px; height: 2px; top: 8px;
  background: var(--ember-deep);
  box-shadow: 0 0 8px rgba(154,90,10,0.5);
  animation: scanSweep 2s ease-in-out infinite;
}
@keyframes scanSweep {
  0%, 100% { top: 8px; opacity: 0.9; }
  50%      { top: 78px; opacity: 0.9; }
}

.check-ring {
  position: relative; width: 84px; max-width: 100%; aspect-ratio: 1; border-radius: 50%;
  background: conic-gradient(var(--ember-deep) 0 25%, rgba(22,21,26,0.12) 25% 100%);
  animation: checkSpin 1.1s linear infinite;
}
.check-ring::after { content: ""; position: absolute; inset: 10px; border-radius: 50%; background: #f3f1ec; }
@keyframes checkSpin { to { transform: rotate(360deg); } }

.redeem-badge {
  width: 84px; max-width: 100%; aspect-ratio: 1; border-radius: 50%; display: grid; place-items: center;
  background: #17161a; color: #f6f5f2;
  box-shadow: 0 16px 28px -14px rgba(0,0,0,0.8);
  animation: pop 3.2s var(--e-spring) infinite;
}

.used-stamp {
  display: flex; align-items: center; justify-content: center;
  width: 84px; max-width: 100%; aspect-ratio: 1; border-radius: 50%;
  border: 4px solid rgba(23,22,26,0.55);
  box-shadow: inset 0 0 0 2px rgba(23,22,26,0.18);
  color: rgba(23,22,26,0.6);
  font-family: var(--f-display); font-weight: 800; font-size: 15px; letter-spacing: 1.5px; text-transform: uppercase;
  transform: rotate(-14deg);
}

/* The tab switcher sits above the grid, so by default the phone starts a full
   tab-row lower than it. Pulling it back up by exactly that row (the pill's
   own 54px plus the margin under it) lines the top of the phone up with the
   top of the Gaining/Using buttons. Two columns only — stacked, the phone sits
   above the steps and this would drag it into the tabs. */
@media (min-width: 941px) {
  .phone { margin-top: calc(-54px - clamp(28px, 4vw, 44px)); }
}
@media (max-width: 940px) {
  /* stacked, the rows sit much closer than the two desktop columns need to —
     30px between a phone, a one-line caption and the card reads as three
     detached blocks rather than one control. */
  .how__grid { grid-template-columns: 1fr; row-gap: 14px; }
  /* the base .phone width is sized for the desktop sticky column, where
     fitting the viewport just means not overflowing it — stacked here
     above the step card instead of beside it, that leaves the card
     pushed halfway off-screen. Scaling by vh (with the same *0.462
     aspect-ratio trick, just against viewport height instead of 100vh
     minus the header) keeps the phone in proportion while shrinking it
     enough that the current step still shows on the same screen. */
  /* Sized so the phone and the full step card below it share one screen
     without the eyebrow/heading/tab switcher above needing to as well —
     48vh leaves enough room (measured against the tallest step's card
     across both panels, once for-business.js's syncHeight() was actually
     measuring that height correctly) for the gap to .steps, the card
     itself, the gap to .steps__nav, and .steps__nav, inside a 100vh-66px
     (fixed header) budget, with a small margin held back. */
  .phone { position: relative; top: 0; order: -2; margin-bottom: 0; width: min(90vw, calc(64vh * 0.462)); }
  /* stacked under the phone, the pill reads better centered than pinned to
     the start edge — .how__tabs is inline-flex (shrink-to-fit) so switching
     to flex + a fit-content width lets margin-inline:auto center the box */
  .how__tabs { display: flex; width: fit-content; margin-inline: auto; }

  /* One step at a time, side by side rather than stacked — the same idea
     as the phone's own scenes. Every step stays position:absolute all the
     time now (toggling absolute<->relative on .is-on was the earlier cut
     at this — it changes .steps's height in one instant frame, no matter
     how long the opacity/transform transition runs, which is what read as
     "sudden"). Height instead comes from for-business.js measuring the
     active step's scrollHeight and writing it to .steps, so it's a real
     transition too. Works even though steps 2/3 live two levels deeper
     inside the "first time only" group, because absolute positioning
     resolves against the nearest *positioned* ancestor (.steps) regardless
     of how many plain wrapper elements sit in between. */
  .steps { position: relative; overflow: hidden; transition: height 0.5s var(--e-out); }
  .steps__group { padding-inline-start: 0; }
  .steps__grouplabel { display: none; }
  .step__kicker { display: block; }
  .step {
    position: absolute; inset: 0; width: 100%;
    opacity: 0; pointer-events: none;
    transform: translateX(28px);
    transition: opacity 0.45s ease, transform 0.6s var(--e-out),
                background-color 0.35s ease, border-color 0.35s ease;
    /* one card on screen at a time has the room the old always-visible
       list didn't, so it gets more breathing room than the base 20px/22px
       rather than just inheriting it */
    padding: 26px 24px; gap: 20px;
  }
  .step.is-before { transform: translateX(-28px); }
  .step.is-on { opacity: 1; pointer-events: auto; transform: none; z-index: 1; }
  .step h3 { font-size: 20px; margin-bottom: 8px; }
}

/* ============================================================================
   WHY
   ========================================================================== */
/* Cards, not a seven-row list. At this length a list is a wall — the eye has
   nowhere to land and every row reads at the same weight. As cards, the ember
   stat becomes the hook you scan ("~2 seconds", "Zero print cost") and the
   claim underneath is what you read once the stat has caught you. */
.why__list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
.why__row {
  /* the icon and the stat share the top line, the words sit under both —
     laid out by area so the existing markup order needs no rewriting */
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: "ico stat" "text text";
  gap: 16px 14px; align-content: start;
  padding: 24px 22px 26px; border-radius: 22px;
  background: var(--surface); border: 1px solid var(--line);
  transition: transform 0.4s var(--e-out), background-color 0.3s ease,
              border-color 0.3s ease, box-shadow 0.4s var(--e-out);
}
.why__row:hover {
  transform: translateY(-4px);
  background: var(--surface-2);
  border-color: rgba(240,169,74,0.42);
  box-shadow: 0 30px 60px -34px rgba(0,0,0,0.9), 0 0 60px -32px rgba(240,169,74,0.5);
}
.why__row > div { grid-area: text; }
.why__ico {
  grid-area: ico;
  width: 46px; height: 46px; border-radius: 14px; display: grid; place-items: center; flex: none;
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--ember);
  transition: transform 0.45s var(--e-spring), background-color 0.3s ease;
}
.why__row:hover .why__ico { transform: translateY(-3px) rotate(-6deg); }
.why__row h3 { font-size: 19px; margin-bottom: 7px; }
.why__row p { font-size: 14.5px; line-height: 1.55; color: var(--fg-dim); }
.why__stat {
  grid-area: stat; justify-self: end; align-self: center;
  font-family: var(--f-display); font-size: 21px; font-weight: 600;
  letter-spacing: -0.02em; color: var(--ember); white-space: nowrap;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
/* the last one closes the section, so it runs the full width and lays out
   sideways instead — a full stop rather than a seventh identical tile */
.why__row:last-child {
  grid-column: 1 / -1;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "ico text stat";
  align-items: center; gap: 22px;
  padding: 26px 26px 28px;
}
.why__row:last-child .why__stat { font-size: 28px; }
.why__row:last-child p { max-width: 62ch; }

@media (max-width: 1080px) { .why__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px) {
  .why__list { grid-template-columns: 1fr; gap: 26px; }
  /* stacked, the wide closer has no width to lay out sideways in */
  .why__row:last-child {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "ico stat" "text text";
    align-items: start; gap: 16px 14px;
  }
  .why__row:last-child .why__stat { font-size: 21px; }

  /* One column only, so each card can park under the header and let the
     next one scroll up to cover it — a deck, not a list. Same `top` for
     every card is what makes the stacking read as "the new one lands on
     the last one" rather than seven cards each pinning at a different
     height. z-index climbs down the deck so a later card always paints
     over the one(s) still parked behind it. */
  .why__row {
    position: sticky; top: 78px;
    /* .why__row's usual background is a ~5%-opacity white wash — fine
       layered once over the page, but see-through once cards start
       stacking on top of each other. Needs to be opaque here so a card
       fully hides whatever is parked behind it. */
    background-color: var(--ink-800);
    box-shadow: 0 24px 46px -26px rgba(0,0,0,0.75);
  }
  .why__row:nth-child(1) { z-index: 1; }
  .why__row:nth-child(2) { z-index: 2; }
  .why__row:nth-child(3) { z-index: 3; }
  .why__row:nth-child(4) { z-index: 4; }
  .why__row:nth-child(5) { z-index: 5; }
  .why__row:nth-child(6) { z-index: 6; }
  .why__row:nth-child(7) { z-index: 7; }
  /* keep the opaque backdrop if a touch device fires :hover on tap-hold —
     the translucent hover wash would otherwise let the covered card show through */
  .why__row:hover { background-color: var(--ink-800); }
}
/* A viewer who asked for less motion still gets the cards — just laid out
   as a plain scrolling list instead of a deck that pins and covers. */
@media (max-width: 760px) and (prefers-reduced-motion: reduce) {
  .why__row { position: static; box-shadow: none; }
}

/* ============================================================================
   INSIGHTS — the paper band. This is the only place colour appears, and it is
   always the café's own data, never chrome.
   ========================================================================== */
/* Its own ground. Insights and Pricing are both paper bands, so back to back
   they read as one long light stretch; this section gets a deeper, warmer
   base with an ember bloom in one corner and a cool one in the other (the
   two data hues), which separates the two and gives the white cards
   something to lift off. */
#insights {
  background:
    radial-gradient(56% 42% at 10% 6%, rgba(240,169,74,0.20), transparent 62%),
    radial-gradient(48% 40% at 94% 86%, rgba(47,63,79,0.12), transparent 66%),
    linear-gradient(168deg, #efebe3 0%, #e8e3d9 58%, #e3ded2 100%);
}

/* Grouped rows, not a tile mosaic. Eight small tiles meant eight small
   captions, and the thing an owner actually needs — what to DO with the
   number — was set in 13px under a chart. Here the chart sits beside the
   words, the words are readable, and each group ends in a decision. */
.insights__rows { display: grid; gap: clamp(24px, 3vw, 40px); counter-reset: ins; }

.ins {
  position: relative; overflow: hidden; counter-increment: ins;
  /* The panels run on ink inside the paper band. The thing they are showing is
     a dark dashboard, so an inverted card reads as a screen lifted off the
     page rather than another block of page. Redeclaring the ink tokens here
     (the same trick .band--paper plays) means every child below keeps
     inheriting the right colours from its own container — only the handful of
     chart colours hardcoded for paper need re-pointing, further down. */
  --fg:        #f4f3f0;
  --fg-dim:    #a5a19a;
  --fg-faint:  #8b8780;
  --line:      rgba(255,255,255,0.10);
  --line-2:    rgba(255,255,255,0.20);
  --surface:   rgba(255,255,255,0.045);
  --surface-2: rgba(255,255,255,0.075);
  --accent:    var(--ember);
  /* the two data hues lifted off the dark ground — #2f3f4f disappears on ink */
  --data-loyal: #7fa6cb;
  --data-new:   #d9a45f;
  display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  gap: clamp(28px, 4vw, 56px); align-items: center;
  padding: clamp(26px, 2.8vw, 38px); border-radius: 26px;
  color: var(--fg);
  background: linear-gradient(168deg, #17171d 0%, #0d0d11 100%);
  border: 1px solid rgba(255,255,255,0.09);
  box-shadow:
    0 34px 66px -34px rgba(38,28,12,0.55),
    inset 0 1px 0 rgba(255,255,255,0.07);
  transition: transform 0.45s var(--e-out), box-shadow 0.45s var(--e-out);
}
.ins:hover {
  transform: translateY(-5px);
  box-shadow:
    0 52px 92px -38px rgba(38,28,12,0.62),
    inset 0 1px 0 rgba(255,255,255,0.1);
}
/* an ember rail that draws itself along the top edge on hover */
.ins::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: linear-gradient(90deg, var(--ember), var(--ember-deep));
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.65s var(--e-out);
}
.ins--flip::before { transform-origin: right; }
.ins:hover::before { transform: scaleX(1); }
/* alternate which side the chart lands on, so three rows do not read as a list */
.ins--flip .ins__viz { order: 2; }
.ins__viz { min-width: 0; }
/* numbered from the row's own counter, so the three groups read as a
   sequence the way the how-it-works steps do — no extra markup */
.ins__eyebrow {
  margin: 0 0 14px; display: flex; align-items: center; gap: 11px;
  font-family: var(--f-mono); font-size: 11px; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ember-deep);
}
.ins__eyebrow::before {
  content: counter(ins, decimal-leading-zero);
  flex: none; width: 32px; height: 32px; border-radius: 50%;
  /* line-height:1 so the grid centres the digits themselves — with the
     inherited body leading the line box is taller than the glyphs and they
     sit visibly low in the circle. Same for the FAQ and initials badges. */
  display: grid; place-items: center; line-height: 1;
  font-family: var(--f-display); font-size: 15px; font-weight: 600;
  letter-spacing: 0; font-variation-settings: "SOFT" 40, "WONK" 1;
  color: var(--ember-deep);
  background: rgba(240,169,74,0.18);
  border: 1px solid rgba(240,169,74,0.42);
}
.ins__eyebrow::after {
  content: ""; flex: 1 1 auto; height: 1px;
  background: linear-gradient(90deg, rgba(240,169,74,0.4), transparent);
}
.ins__say h3 { font-size: clamp(21px, 2vw, 27px); line-height: 1.2; margin-bottom: 12px; }
.ins__body { font-size: 16px; line-height: 1.62; color: var(--fg-dim); }
/* the payoff line — what this number changes about your week */
.ins__use {
  margin-top: 18px; padding-top: 16px; border-top: 1px dashed rgba(20,18,14,0.2);
  font-size: 16px; line-height: 1.6; color: var(--fg);
}
.ins__use-tag {
  display: inline-block; margin-right: 8px;
  font-family: var(--f-mono); font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ember-deep);
  background: rgba(240,169,74,0.16); border: 1px solid rgba(240,169,74,0.34);
  border-radius: 999px; padding: 4px 10px; vertical-align: 2px;
}
/* two stats sharing one side of a row */
/* the two figures stack down one column — side by side they read as a pair to
   compare, stacked they read as two separate answers to the question on the left */
.ins__pair { display: grid; grid-template-columns: 1fr; gap: 26px; }
.ins__stat { min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.ins__stat-big {
  margin: 0; font-family: var(--f-display); font-size: clamp(34px, 4vw, 50px);
  font-weight: 600; line-height: 1; letter-spacing: -0.03em;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
.ins__stat-big i { font-style: normal; }
.ins__stat-big em { font-style: normal; font-family: var(--f-ui); font-size: 16px; font-weight: 600; color: var(--fg-dim); }
/* reserve two lines so a one-line caption beside a two-line one does not
   shove its own big number out of alignment */
/* no min-height now the figures are stacked — that was only there to level two
   captions sitting side by side */
.ins__stat-cap { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--fg-dim); }
.ins__stat-cap b { color: var(--fg); font-weight: 700; }

@media (max-width: 900px) {
  .ins { grid-template-columns: 1fr; }
  .ins--flip .ins__viz { order: 0; }
}
/* ---- the few paper-tuned colours inside a panel, re-pointed for ink ---- */
/* --ember-deep is the accent for the paper band and goes muddy on the dark
   card, so inside a panel the accent is plain ember */
.ins .ins__eyebrow,
.ins .ins__eyebrow::before,
.ins .ins__use-tag { color: var(--ember); }
.ins .ins__use { border-top-color: rgba(255,255,255,0.16); }
.ins .heat__c[data-peak] { box-shadow: 0 0 0 2px var(--ember); }
/* low-traffic cells mixed toward white would go BRIGHTER than busy ones on a
   dark ground, so the quiet end mixes toward the card instead */
.ins .heat__c {
  /* mixing toward a translucent white puts the variation in the alpha and
     flattens the ramp — mixing toward the card's own ink keeps it in the
     colour, so a quiet hour sinks into the card and a busy one lifts off it */
  background: color-mix(in srgb, var(--data-loyal) calc(var(--v) * 96%), #191920);
}
.ins .donut .track { stroke: rgba(255,255,255,0.11); }
.ins .rhythm i { background: rgba(255,255,255,0.11); }

.insights__grid {
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 18px;
}
.tile {
  padding: 22px 22px 24px; border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 12px;
  transition: transform 0.4s var(--e-out), box-shadow 0.4s var(--e-out);
}
.tile:hover { transform: translateY(-4px); }
.tile--wide  { grid-column: span 4; }
.tile--half  { grid-column: span 3; }
.tile--third { grid-column: span 2; }
.tile__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.tile__label {
  font-family: var(--f-mono); font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--fg-faint); margin: 0;
}
.tile__delta {
  font-family: var(--f-mono); font-size: 11px; font-weight: 600;
  color: var(--ember-deep); background: rgba(154,90,10,0.10);
  padding: 3px 9px; border-radius: 999px; white-space: nowrap;
}
.tile__big {
  font-family: var(--f-display); font-size: clamp(34px, 4vw, 46px); font-weight: 600;
  letter-spacing: -0.03em; line-height: 1; margin: 4px 0 0;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
.tile__foot { font-size: 13.5px; color: var(--fg-dim); margin-top: auto; }
.tile__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: auto; }
.tile__pair > div { border-top: 1px solid var(--line); padding-top: 8px; }
.tile__pair span { display: block; font-family: var(--f-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--fg-faint); }
.tile__pair b { font-size: 19px; }

/* heatmap */
.heat { display: grid; grid-template-columns: 30px repeat(7, 1fr); gap: 5px; }
.heat__d, .heat__h {
  font-family: var(--f-mono); font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--fg-faint); display: grid; place-items: center;
}
.heat__h { justify-content: start; }
.heat__c {
  aspect-ratio: 1.5; border-radius: 6px;
  display: grid; place-items: center;
  font-family: var(--f-mono); font-size: 10px; font-weight: 600; color: #fff;
  background: color-mix(in srgb, var(--data-loyal) calc(var(--v) * 92%), #ffffff);
  transform: scale(0.2); opacity: 0;
  transition: transform 0.5s var(--e-spring) calc(var(--d) * 11ms), opacity 0.4s ease calc(var(--d) * 11ms);
}
.is-in .heat__c { transform: none; opacity: 1; }
.heat__c[data-peak] { box-shadow: 0 0 0 2px var(--ember-deep); }
.heat__legend { display: flex; align-items: center; gap: 5px; font-family: var(--f-mono); font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); }
.heat__legend i { width: 14px; height: 10px; border-radius: 3px; }

/* donut */
.donut { display: flex; align-items: center; gap: 18px; }
.donut svg { width: 92px; height: 92px; flex: none; transform: rotate(-90deg); }
.donut .track { fill: none; stroke: rgba(20,18,14,0.09); stroke-width: 6.5; }
.donut .val {
  fill: none; stroke: var(--data-loyal); stroke-width: 6.5; stroke-linecap: round;
  stroke-dasharray: 100 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset 1.3s var(--e-out) 0.15s;
}
.is-in .donut .val { stroke-dashoffset: calc(100 - var(--pct, 71)); }

/* new vs loyal split — new grows in from one end, loyal from the other,
   so they visibly close together toward the middle rather than both just
   expanding from the same start edge. */
.split { position: relative; height: 44px; border-radius: 12px; overflow: hidden; }
.split i {
  position: absolute; inset-block: 0;
  display: grid; place-items: center; font-family: var(--f-mono); font-size: 12px; font-weight: 700; color: #fff;
  width: 0; transition: width 1.1s var(--e-out) 0.1s;
}
.split .new   { background: var(--data-new);   inset-inline-start: 0; }
.split .loyal { background: var(--data-loyal); inset-inline-end: 0; }
.is-in .split .new   { width: var(--w-new); }
.is-in .split .loyal { width: var(--w-loyal); }
.key { display: flex; gap: 16px; font-size: 12.5px; color: var(--fg-dim); }
.key span { display: inline-flex; align-items: center; gap: 7px; }
.key i { width: 10px; height: 10px; border-radius: 3px; }

/* return rhythm bars */
.rhythm { display: flex; align-items: flex-end; gap: 6px; height: 74px; }
.rhythm i {
  flex: 1; border-radius: 5px 5px 2px 2px; background: rgba(47,63,79,0.32);
  height: 0; transition: height 0.7s var(--e-out) calc(var(--d) * 60ms);
}
.rhythm i.pk { background: var(--data-loyal); }
.is-in .rhythm i { height: var(--h); }

/* per-reward usage bars */
.dealbars { display: flex; flex-direction: column; gap: 15px; }
.dealbar__top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-bottom: 6px; }
.dealbar__label { font-family: var(--f-ui); font-weight: 700; font-size: 14px; color: var(--fg); }
.dealbar__val { flex: none; font-family: var(--f-mono); font-size: 12px; font-weight: 600; color: var(--fg-dim); }
.dealbar__track { position: relative; height: 10px; border-radius: 6px; background: rgba(255,255,255,0.08); overflow: hidden; }
.dealbar__fill {
  position: absolute; inset-block: 0; inset-inline-start: 0; height: 100%; border-radius: 6px;
  background: var(--data-loyal); width: 0; transition: width 0.9s var(--e-out) 0.15s;
}
.is-in .dealbar__fill { width: var(--w); }

/* weekly activity line */
.spark { width: 100%; height: 96px; overflow: visible; }
.spark .area { fill: url(#sparkFill); opacity: 0; transition: opacity 0.7s ease 0.5s; }
.spark .line {
  fill: none; stroke: var(--data-loyal); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 400; stroke-dashoffset: 400;
  transition: stroke-dashoffset 1.5s var(--e-out);
}
.spark .knob { fill: var(--ember-deep); r: 0; transition: r 0.4s var(--e-spring) 1.2s; }
.is-in .spark .line { stroke-dashoffset: 0; }
.is-in .spark .area { opacity: 1; }
.is-in .spark .knob { r: 4.5; }

/* ---- what is being built next ----
   Deliberately not a tile: dashed rather than solid, dimmer than the data
   above it, and captioned "In development". These two are not shipped, and
   the page says so plainly — on a page whose About section opens with "we are
   early, and we are not hiding it", a visible roadmap is worth more than a
   feature list that quietly overstates. */
.next {
  position: relative; overflow: hidden;
  margin-top: 18px; padding: 26px 26px 28px; border-radius: var(--radius);
  border: 1px dashed rgba(20,18,14,0.22);
  background: rgba(255,255,255,0.4);
}
/* a slow ember pass, like something still being worked on */
.next__sweep {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(240,169,74,0.16) 50%, transparent 62%);
  transform: translateX(-130%);
  animation: nextSweep 7s ease-in-out infinite;
}
@keyframes nextSweep {
  0%       { transform: translateX(-130%); }
  38%,100% { transform: translateX(130%); }
}
.next__line {
  position: relative; display: flex; align-items: center; gap: 10px; margin: 0;
  font-size: 14.5px; line-height: 1.5; color: var(--fg-dim);
}
.next__dot {
  flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--ember-deep);
  animation: nextPulse 2s ease-in-out infinite;
}
@keyframes nextPulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(154,90,10,0.5); opacity: 1; }
  60%      { box-shadow: 0 0 0 7px rgba(154,90,10,0); opacity: 0.65; }
}


@media (max-width: 1000px) {
  .insights__grid { grid-template-columns: repeat(4, 1fr); }
  .tile--wide { grid-column: span 4; }
  .tile--half { grid-column: span 4; }
  .tile--third { grid-column: span 2; }
}
@media (max-width: 620px) {
  .insights__grid { grid-template-columns: repeat(2, 1fr); }
  .tile--wide, .tile--half { grid-column: span 2; }
  .tile--third { grid-column: span 2; }
  .heat { grid-template-columns: 24px repeat(7, 1fr); gap: 3px; }
  .heat__c { font-size: 8px; }
}

/* ============================================================================
   PRICING
   ========================================================================== */
/* the price and what it buys, side by side — `stretch` so neither card is
   left hanging shorter than the other */
/* A lit stage, not another paper band. The ground darkens toward the edges
   and an ember spotlight falls where the price card stands, so the dark card
   reads as the thing being lit rather than a dark box dropped on cream. */
#pricing {
  --plan-ring: 0deg;
  background:
    radial-gradient(64% 46% at 50% -6%, rgba(240,169,74,0.15), transparent 68%),
    linear-gradient(180deg, #15141b 0%, #0e0d12 54%, #111016 100%);
}
/* the ember wash under the cards, breathing slowly so the stage is never
   quite still — wide and centred, so it reads the same in either direction */
#pricing::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(52% 44% at 50% 58%, rgba(240,169,74,0.11), transparent 72%);
  animation: stageBreathe 11s ease-in-out infinite;
}
@keyframes stageBreathe {
  0%, 100% { opacity: 0.5; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.05); }
}
/* a hairline of ember where the two paper sections meet, so the seam is a
   deliberate edge instead of a smudge */
#pricing::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(240,169,74,0.55), transparent);
}

.pricing__grid { display: grid; grid-template-columns: 1.02fr 0.98fr; gap: 22px; align-items: stretch; }
.plan { position: relative; padding: 34px; display: flex; flex-direction: column; gap: 22px; }
/* The one thing on this page that has to be clicked, so it is the one thing
   lit from within: an ember hairline, an ember bloom under it, and a lift on
   hover. Everything stays inside the Ink & Ember palette — no new hues. */
.plan--hero {
  background: linear-gradient(158deg, #17161a, #23212a);
  color: #f4f3f0; border-color: transparent;
  overflow: hidden;
  box-shadow:
    0 0 0 1px rgba(240,169,74,0.30),
    0 46px 90px -40px rgba(0,0,0,0.95),
    0 0 90px -34px rgba(240,169,74,0.45);
  transition: transform 0.4s var(--e-out), box-shadow 0.4s var(--e-out);
}
.plan--hero:hover {
  transform: translateY(-4px);
  box-shadow:
    0 0 0 1px rgba(240,169,74,0.55),
    0 60px 110px -44px rgba(0,0,0,0.95),
    0 0 120px -30px rgba(240,169,74,0.6);
}
.plan--hero::before {
  content: ""; position: absolute; inset: -40% -10% auto -10%; height: 90%;
  background: radial-gradient(50% 60% at 70% 20%, rgba(240,169,74,0.28), transparent 70%);
  pointer-events: none;
}
.plan--hero .plan__list span { color: rgba(244,243,240,0.82); }

/* An ember travelling once around the card's own edge. Drawn on a child
   rather than a pseudo-element because both of .plan--hero's are already
   spoken for (bloom above, sheen below), and masked to a 1px rim so the
   conic gradient shows only as a border, never as a fill. */
@property --plan-ring { syntax: "<angle>"; initial-value: 0deg; inherits: false; }
.plan__ring {
  position: absolute; inset: 0; border-radius: inherit; padding: 1px;
  pointer-events: none; z-index: 1;
  background: conic-gradient(from var(--plan-ring),
    transparent 0%, rgba(240,169,74,0.85) 8%, rgba(255,200,126,0.95) 12%,
    rgba(240,169,74,0.85) 16%, transparent 26%, transparent 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: planRing 7s linear infinite;
}
@keyframes planRing { to { --plan-ring: 360deg; } }

/* the card follows the pointer with a soft ember pool, so it feels lit from
   wherever the visitor is looking. --mx/--my are set in for-business.js; the
   50%/40% fallback keeps it centred before the first pointer move. */
.plan__spot {
  position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 1;
  background: radial-gradient(34% 40% at var(--mx, 50%) var(--my, 40%), rgba(255,200,126,0.16), transparent 68%);
  opacity: 0; transition: opacity 0.45s var(--e-out);
}
.plan--hero:hover .plan__spot { opacity: 1; }

/* everything that is actual content sits above the two light layers */
.plan--hero > *:not(.plan__ring):not(.plan__spot) { position: relative; z-index: 2; }

/* a light runs across the button the moment it is worth pressing */
.plan__cta { position: relative; overflow: hidden; }
.plan__cta::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 40%, rgba(255,255,255,0.42) 50%, transparent 60%);
  transform: translateX(-120%);
}
.plan__cta:hover::after { animation: ctaShine 0.85s var(--e-out); }
@keyframes ctaShine { to { transform: translateX(120%); } }

/* the reassurance line arrives a beat after the price, one chip at a time */
.js .plan__trust li {
  opacity: 0; transform: translateY(7px);
  transition: opacity 0.45s var(--e-out) calc(var(--d, 0) * 90ms + 260ms),
              transform 0.45s var(--e-out) calc(var(--d, 0) * 90ms + 260ms);
}
.js .plan--hero.is-in .plan__trust li { opacity: 1; transform: none; }
.plan__trust li:nth-child(1) { --d: 1; }
.plan__trust li:nth-child(2) { --d: 2; }
.plan__trust li:nth-child(3) { --d: 3; }

@media (prefers-reduced-motion: reduce) {
  .plan__ring, #pricing::after { animation: none; }
  .plan__cta:hover::after { animation: none; }
  .js .plan__trust li { opacity: 1; transform: none; transition: none; }
  .js .plan--features .plan__list svg path { stroke-dashoffset: 0; transition: none; }
}
.plan--hero .plan__list svg { color: var(--ember); }
/* a slow light sweep across the card face — the same trick a jeweller uses on
   a turntable. Long and shallow, so it reads as polish, not as a banner ad. */
.plan--hero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(104deg,
    transparent 38%,
    rgba(255,255,255,0.055) 47%,
    rgba(255,200,126,0.10) 50%,
    rgba(255,255,255,0.055) 53%,
    transparent 62%);
  transform: translateX(-120%);
  animation: planSheen 7s ease-in-out 1.4s infinite;
}
@keyframes planSheen {
  0%       { transform: translateX(-120%); }
  38%, 100% { transform: translateX(120%); }
}
@media (prefers-reduced-motion: reduce) { .plan--hero::after { animation: none; opacity: 0; } }
.trial-badge {
  align-self: flex-start; position: relative;
  font-family: var(--f-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.16em; text-transform: uppercase;
  padding: 7px 14px; border-radius: 999px;
  background: rgba(240,169,74,0.16); color: var(--ember); border: 1px solid rgba(240,169,74,0.4);
}
.plan__price { position: relative; }
.plan__amount {
  display: block; font-family: var(--f-display); font-size: clamp(38px, 5vw, 58px); font-weight: 600;
  letter-spacing: -0.03em; line-height: 1.02;
  font-variation-settings: "SOFT" 40, "WONK" 1;
}
.plan__per { display: block; font-size: 14.5px; color: rgba(244,243,240,0.6); margin-top: 8px; }
.plan__list { list-style: none; margin: 0; padding: 0; display: grid; gap: 13px; position: relative; }
.plan__list li { display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: start; font-size: 15.5px; }
.plan__list svg { margin-top: 4px; color: var(--accent); flex: none; }
.plan__list span { color: var(--fg-dim); }
.plan__list strong { color: var(--fg); font-weight: 600; }
.plan h3 { margin-bottom: 4px; }
.plan__note { font-size: 14px; color: var(--fg-faint); border-top: 1px solid var(--line); padding-top: 16px; margin-top: auto; }

/* ---- billing switch (monthly/yearly) ---- */
.plan__top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 14px; }
.billing {
  position: relative; display: inline-flex; gap: 2px; padding: 3px;
  border-radius: 999px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
}
.billing__glider {
  position: absolute; top: 3px; left: 3px; bottom: 3px;
  border-radius: 999px; background: var(--ember);
  transition: transform 0.32s var(--e-spring), width 0.32s var(--e-spring);
}
.billing__btn {
  position: relative; z-index: 1; display: flex; align-items: center; gap: 6px;
  border: 0; background: none; cursor: pointer; white-space: nowrap;
  padding: 8px 14px; min-height: 34px;
  font-family: var(--f-ui); font-size: 13px; font-weight: 600; color: rgba(244,243,240,0.7);
  transition: color 0.25s ease;
}
.billing__btn.is-on { color: var(--accent-fg); }
.billing__save {
  font-family: var(--f-mono); font-size: 9.5px; font-weight: 700; letter-spacing: 0.04em;
  padding: 2px 7px; border-radius: 999px;
  background: rgba(23,22,26,0.14); color: inherit;
}
.billing__btn:not(.is-on) .billing__save { background: rgba(240,169,74,0.16); color: var(--ember-lift); }

/* ---- the figure itself ---- */
.plan__figure { display: flex; align-items: baseline; gap: 9px; transition: opacity 0.16s ease, transform 0.16s ease; }
.plan__figure.is-swapping { opacity: 0; transform: translateY(6px); }
.plan__unit { font-family: var(--f-ui); font-size: 15px; font-weight: 600; color: rgba(244,243,240,0.55); }
.plan__equiv {
  margin: 10px 0 0; font-size: 13px; color: rgba(244,243,240,0.65);
  padding-top: 10px; border-top: 1px dashed rgba(255,255,255,0.16);
}
.plan__equiv b { color: var(--ember-lift); font-weight: 700; }
.plan__equiv[hidden] { display: none; }

/* ---- price hierarchy: the anchor, the figure, the per-day reframe ---- */
/* what twelve monthly payments cost, struck through, shown only on yearly */
.plan__was {
  display: block; margin-bottom: 2px;
  font-family: var(--f-ui); font-size: 16px; font-weight: 600;
  color: rgba(244,243,240,0.38); text-decoration: line-through;
  text-decoration-color: rgba(240,169,74,0.7); text-decoration-thickness: 2px;
}
.plan__was[hidden] { display: none; }
/* 6,000,000 a month is an invoice; 200,000 a day is a decision a café owner
   can make from behind the counter */
.plan__day {
  margin: 9px 0 0; font-family: var(--f-mono); font-size: 12px; letter-spacing: 0.06em;
  color: var(--ember-lift);
}
/* ---- the companion card: what the price buys ---- */
/* the companion card lifts off the darker ground the same way the insight
   rows do, so the two light sections share one card language */
.plan--features {
  gap: 16px;
  background: linear-gradient(168deg, rgba(255,255,255,0.055), rgba(255,255,255,0.018));
  border-color: rgba(255,255,255,0.10);
  box-shadow:
    0 30px 60px -40px rgba(0,0,0,0.9),
    inset 0 1px 0 rgba(255,255,255,0.07);
  transition: transform 0.4s var(--e-out), box-shadow 0.4s var(--e-out), border-color 0.4s var(--e-out);
}
.plan--features:hover {
  transform: translateY(-4px);
  border-color: rgba(240,169,74,0.26);
  box-shadow:
    0 46px 84px -40px rgba(0,0,0,0.95),
    inset 0 1px 0 rgba(255,255,255,0.09);
}
/* on the dark stage the companion card inherits the page's own text tones */
.plan--features h3,
.plan--features .plan__list strong { color: var(--fg); }
.plan--features .plan__sub,
.plan--features .plan__list span { color: rgba(244,243,240,0.72); }
.plan--features .plan__note { color: rgba(244,243,240,0.5); border-top-color: rgba(255,255,255,0.10); }
.plan--features .plan__sub { border-bottom-color: rgba(255,255,255,0.10); }
.plan--features h3 { margin-bottom: 0; }
/* each tick gets its own ember disc — eight bare checkmarks read as fine
   print, eight discs read as a list of things you are being handed */
.plan--features .plan__list { gap: 14px; }
.plan--features .plan__list li { grid-template-columns: 24px 1fr; gap: 13px; }
.plan--features .plan__list svg {
  width: 24px; height: 24px; margin-top: -1px; padding: 4px;
  border-radius: 50%; color: var(--ember);
  background: rgba(240,169,74,0.14); border: 1px solid rgba(240,169,74,0.38);
}
/* each tick draws itself rather than just fading in — the stroke runs on as
   its own line arrives, so the list reads as being handed over item by item */
.js .plan--features .plan__list svg path {
  stroke-dasharray: 26; stroke-dashoffset: 26;
  transition: stroke-dashoffset 0.5s var(--e-out) calc(var(--d, 0) * 65ms + 180ms);
}
.js .plan--features.is-in .plan__list svg path { stroke-dashoffset: 0; }
/* the list is handed over one item at a time rather than appearing as a wall
   of ticks — each disc springs in just behind its own line */
.js .plan--features .plan__list li {
  opacity: 0; transform: translateY(9px);
  transition: opacity 0.5s var(--e-out) calc(var(--d, 0) * 65ms),
              transform 0.5s var(--e-out) calc(var(--d, 0) * 65ms);
}
.js .plan--features .plan__list svg {
  transform: scale(0.55);
  transition: transform 0.55s var(--e-spring) calc(var(--d, 0) * 65ms + 70ms);
}
.js .plan--features.is-in .plan__list li { opacity: 1; transform: none; }
.js .plan--features.is-in .plan__list svg { transform: none; }
.plan--features .plan__list li:nth-child(1) { --d: 1; }
.plan--features .plan__list li:nth-child(2) { --d: 2; }
.plan--features .plan__list li:nth-child(3) { --d: 3; }
.plan--features .plan__list li:nth-child(4) { --d: 4; }
.plan--features .plan__list li:nth-child(5) { --d: 5; }
.plan--features .plan__list li:nth-child(6) { --d: 6; }
.plan--features .plan__list li:nth-child(7) { --d: 7; }
.plan--features .plan__list li:nth-child(8) { --d: 8; }
.plan--features .plan__list li:nth-child(n+9) { --d: 9; }
.plan__sub {
  margin: 0 0 4px; font-size: 14.5px; line-height: 1.5; color: var(--fg-dim);
  padding-bottom: 16px; border-bottom: 1px solid var(--line);
}
/* the list is the body of this card, so let it take the slack and keep the
   closing note pinned to the bottom edge */
.plan--features .plan__list { margin-bottom: auto; }

/* the other billing option, priced out and clickable. It fills the space a
   two-line price block leaves in a card as tall as the feature list beside
   it, and it answers the question the toggle silently poses. */
/* block, not flex: an inline <b> inside a flex button becomes its own flex
   item and the sentence breaks into columns */
/* the two clauses each stay whole, so the line break falls between them rather
   than splitting a figure from its unit */
.plan__nudge .nb { white-space: nowrap; }
.plan__nudge {
  display: block; position: relative; width: 100%;
  margin-top: -6px; padding: 14px 34px 14px 14px;
  font: inherit; font-size: 14px; line-height: 1.5; text-align: start;
  color: rgba(244,243,240,0.72);
  background: rgba(240,169,74,0.07);
  border: 1px dashed rgba(240,169,74,0.32); border-radius: 14px;
  cursor: pointer;
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}
.plan__nudge::after {
  content: "→"; position: absolute; right: 16px; top: 50%;
  color: var(--ember); font-size: 16px; line-height: 1;
  transform: translateY(-50%);
  transition: transform 0.25s var(--e-out);
}
.plan__nudge:hover {
  background: rgba(240,169,74,0.13);
  border-color: rgba(240,169,74,0.6);
  color: #f4f3f0;
}
.plan__nudge:hover::after { transform: translate(4px, -50%); }
.plan__nudge b { color: var(--ember-lift); font-weight: 700; }
.plan__nudge[hidden] { display: none; }

/* ---- the call to action ---- */
.plan__cta {
  align-self: stretch; justify-content: center;
  min-height: 60px; font-size: 17px;
  /* takes up the slack left when this card is shorter than the one beside it,
     so the price sits at the top and the button lands on the bottom edge
     rather than both bunching up with dead space underneath */
  margin-top: auto;
  animation: ctaBreathe 3.6s ease-in-out infinite;
}
/* a slow, shallow swell — enough to catch the eye in peripheral vision,
   not enough to nag */
@keyframes ctaBreathe {
  0%, 100% { box-shadow: 0 14px 34px -14px rgba(240,169,74,0.75), inset 0 1px 0 rgba(255,255,255,0.45); }
  50%      { box-shadow: 0 18px 50px -14px rgba(240,169,74,0.95), inset 0 1px 0 rgba(255,255,255,0.55); }
}
.plan__cta:hover { animation: none; }
/* the risk-reversal row — the three objections that stop a click, answered
   where the click happens */
/* pulled tighter than the card's 22px flex gap — these belong to the button */
.plan__trust {
  list-style: none; margin: -8px 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 8px 18px; justify-content: center;
}
.plan__trust li {
  display: flex; align-items: center; gap: 6px;
  font-size: 13px; color: rgba(244,243,240,0.62);
}
.plan__trust svg { color: var(--ember); flex: none; }
/* 0.58, not 0.42 — at 0.42 this lands near 3.8:1 on the card's ground, under
   the 4.5:1 floor for body text */
.plan__foot {
  margin: -12px 0 0; text-align: center; font-size: 12.5px; line-height: 1.5;
  color: rgba(244,243,240,0.58);
}
.plan__foot a { color: rgba(244,243,240,0.82); text-decoration: underline; text-underline-offset: 2px; }
.plan__foot a:hover { color: #f4f3f0; }

/* two cards side by side need the room; below this they stack, price first */
@media (max-width: 860px) { .pricing__grid { grid-template-columns: 1fr; } }

/* ============================================================================
   ABOUT
   ========================================================================== */
/* Capped and centred: at the full 1200px wrap the prose ran ~110 characters
   to the line, which reads as a wall. This keeps it near 70 — the measure the
   rest of the page already holds itself to (see .hero__lede's 46ch). */
.about {
  display: grid; gap: 24px;
  padding: clamp(28px, 4vw, 46px);
  max-width: 740px; margin-inline: auto;
}
/* The seal sits above the prose rather than in a side column, so the text runs
   flush to the card's start edge instead of being indented past it. Narrower
   card than the two-column version to hold the same measure. */
/* eyebrow at the reading-start, mark at the reading-end — so Persian puts the
   words right and the logo left, and English mirrors it without a second rule */
.about__head { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.about__head .eyebrow { margin: 0; }
.about__seal {
  width: 86px; height: 86px; border-radius: 50%; display: grid; place-items: center; flex: none;
  background: var(--surface-2); border: 1px solid var(--line-2); color: var(--ember);
}
.about__seal svg { width: 46px; height: auto; fill: currentColor; }
.about p { color: var(--fg-dim); font-size: 16.5px; }
.about p + p { margin-top: 16px; }
/* The two headings that split the section into "who we are" and "where we
   are" — the second one follows the team rule, which already carries its own
   spacing, so only the first needs a gap above it. */
.about__h { margin-bottom: 14px; }
.about p + .about__h { margin-top: 26px; }

/* "a direct line to the person building it" is a claim until it has a name
   against it. Two named people, ruled off from the prose so they read as a
   signature at the bottom of a letter rather than as more copy. */
/* One column, not two: inside the capped card each of the two columns fell to
   ~250px, which broke a one-sentence bio into four ragged lines. Stacked, each
   bio gets the full measure and the pair reads as two signatures, not a grid. */
.about__team {
  margin: 22px 0; padding: 20px 0; border-block: 1px solid var(--line);
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px;
}
/* start, not center: one description runs to two lines and the other to one,
   and centring each row independently leaves the two names off the same line */
.about__person { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 14px; align-items: start; }
.about__ini {
  width: 46px; height: 46px; border-radius: 50%; flex: none;
  display: grid; place-items: center; line-height: 1;
  font-family: var(--f-mono); font-size: 14px; font-weight: 700; letter-spacing: 0.04em;
  color: var(--ember);
  background: rgba(240,169,74,0.12); border: 1px solid rgba(240,169,74,0.38);
}
.about__person b { display: block; font-size: 15.5px; font-weight: 600; color: var(--fg); }
/* > div, so this does not also swallow .about__ini — which is a span too */
.about__person > div span { display: block; margin-top: 3px; font-size: 13.5px; line-height: 1.5; color: var(--fg-faint); }

/* real-person contact details — eNamad and any visitor both want these easy
   to find, so they sit ruled-off like the team block above, not buried in
   the footer's small print */
.about__contact { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--line); }
.about__contact-h { margin: 0 0 12px; font-family: var(--f-mono); font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--fg-faint); }
.about__contact-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.about__contact-list li { display: flex; align-items: center; gap: 11px; font-size: 14.5px; color: var(--fg-dim); }
.about__contact-list svg { flex: none; color: var(--ember); }
.about__contact-list a { color: inherit; text-decoration: none; }
.about__contact-list a:hover { color: var(--fg); text-decoration: underline; }


/* ============================================================================
   FAQ
   ========================================================================== */
.faq__list { display: grid; gap: 0; max-width: 900px; counter-reset: faqcat; }

/* Chapter markers. A category has to out-rank the questions inside it, so it
   is set in display type and the accent, where a question is white and a
   little smaller — the same numbered-badge-and-rule vocabulary as the insight
   rows, which is what makes the page read as one system. */
.faq__cat {
  counter-increment: faqcat;
  display: flex; align-items: center; gap: 15px;
  margin: 0; padding: 54px 4px 18px;
}
.faq__cat:first-child { padding-top: 0; }
.faq__cat::before {
  content: counter(faqcat, decimal-leading-zero);
  flex: none; width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center; line-height: 1;
  font-family: var(--f-display); font-size: 16px; font-weight: 600;
  letter-spacing: 0; font-variation-settings: "SOFT" 40, "WONK" 1;
  color: var(--accent);
  background: rgba(240,169,74,0.14);
  border: 1px solid rgba(240,169,74,0.42);
  box-shadow: 0 0 0 6px rgba(240,169,74,0.045), 0 14px 30px -16px rgba(240,169,74,0.85);
}
.faq__cat h3 {
  flex: 0 1 auto; margin: 0;
  font-size: clamp(21px, 2.1vw, 27px);
  color: var(--accent);
}
/* the rule draws itself out of the title as the row arrives */
.faq__cat-rule {
  flex: 1 1 auto; min-width: 24px; height: 1px;
  background: linear-gradient(90deg, rgba(240,169,74,0.5), transparent);
}
.js .faq__cat-rule {
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.9s var(--e-out) 0.14s;
}
.js .faq__cat.is-in .faq__cat-rule { transform: scaleX(1); }
/* a two-line title leaves the rule as a stub floating beside it, so on a
   phone the badge and the title carry the marker on their own */
@media (max-width: 620px) {
  .faq__cat { gap: 12px; padding: 42px 4px 14px; }
  .faq__cat::before { width: 32px; height: 32px; font-size: 14px; }
  .faq__cat-rule { display: none; }
}
.qa { border-top: 1px solid var(--line); }
.qa:last-child { border-bottom: 1px solid var(--line); }
.qa:has(+ .faq__cat) { border-bottom: 1px solid var(--line); }
.qa > summary {
  list-style: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 22px;
  padding: 24px 4px;
  font-family: var(--f-display); font-size: clamp(17px, 1.6vw, 21px); font-weight: 600;
  letter-spacing: -0.012em;
  transition: color 0.2s ease;
}
.qa > summary::-webkit-details-marker { display: none; }
.qa > summary:hover { color: var(--accent); }
.qa__sign { position: relative; width: 20px; height: 20px; flex: none; }
.qa__sign::before, .qa__sign::after {
  content: ""; position: absolute; left: 50%; top: 50%; background: currentColor; border-radius: 2px;
  transform: translate(-50%, -50%);
  transition: transform 0.35s var(--e-out), opacity 0.25s ease;
}
.qa__sign::before { width: 15px; height: 2px; }
.qa__sign::after  { width: 2px; height: 15px; }
.qa[open] .qa__sign::after { transform: translate(-50%, -50%) rotate(90deg); opacity: 0; }
.qa__body { overflow: hidden; }
.qa__body p { padding: 0 60px 26px 4px; color: var(--fg-dim); font-size: 15.5px; max-width: 74ch; }

/* ============================================================================
   FOOTER
   ========================================================================== */
.foot {
  position: relative; overflow: hidden;
  background: var(--ink-850);
  padding-block: clamp(48px, 6vw, 76px) 0;
}
/* an ember hairline where the last section ends, brightest in the middle —
   the same edge the pricing band gets, so the page closes the way it turns */
.foot::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, transparent, rgba(240,169,74,0.5), transparent);
}
.foot__glow {
  position: absolute; inset: auto auto -30% -10%; width: 70%; height: 130%;
  background: radial-gradient(50% 50% at 40% 60%, rgba(240,169,74,0.10), transparent 70%);
  pointer-events: none;
}
/* sunk low enough that the link columns sit clear of the letterforms — it
   should register as texture at the bottom edge, not as something to read */
.foot__watermark {
  position: absolute; left: 50%; bottom: -30%; width: min(1120px, 104%);
  transform: translateX(-50%);
  fill: #ffffff; opacity: 0.02; pointer-events: none;
}
@media (max-width: 860px) { .foot__watermark { bottom: -18%; } }

.foot__in {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.35fr);
  gap: clamp(34px, 5vw, 76px); align-items: start;
}
.foot__brand { display: grid; justify-items: start; gap: 18px; }
.foot__pitch { margin: 0; max-width: 36ch; font-size: 14.5px; line-height: 1.62; color: var(--fg-dim); }

/* ---- social ---- */
.foot__social { list-style: none; margin: 2px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.foot__social li[hidden] { display: none; }
.social {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 9px 16px 9px 13px; border-radius: 999px;
  font-size: 13.5px; font-weight: 500; text-decoration: none;
  color: var(--fg-dim);
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--line-2);
  transition: color 0.25s ease, border-color 0.25s ease,
              background 0.25s ease, transform 0.25s var(--e-out);
}
.social svg { flex: none; color: var(--fg-faint); transition: color 0.25s ease; }
.social:hover {
  color: #f4f3f0; transform: translateY(-2px);
  background: rgba(240,169,74,0.10);
  border-color: rgba(240,169,74,0.55);
}
.social:hover svg { color: var(--ember); }

/* ---- link columns ---- */
.foot__nav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px 22px; }
.foot__h {
  margin: 0 0 15px; font-family: var(--f-mono); font-size: 11px; font-weight: 600;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ember);
}
.foot__col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 11px; }
/* the underline grows from the left instead of appearing all at once */
.foot__col a {
  position: relative; display: inline-block;
  font-size: 14.5px; color: var(--fg-dim); text-decoration: none;
  transition: color 0.2s ease;
}
.foot__col a::after {
  content: ""; position: absolute; left: 0; right: 100%; bottom: -3px; height: 1px;
  background: var(--ember); transition: right 0.32s var(--e-out);
}
.foot__col a:hover { color: #f4f3f0; }
.foot__col a:hover::after { right: 0; }
/* plain postal address, not a link — same size as the links around it, just
   without the hover underline treatment that implies something's clickable */
.foot__addr { font-size: 14.5px; line-height: 1.5; color: var(--fg-dim); }

/* ---- the bottom bar ---- */
.foot__bar {
  position: relative; z-index: 1;
  /* padding-block, not the shorthand — .wrap owns the inline padding and a
     `padding: 22px 0 30px` here would zero it out */
  margin-top: clamp(38px, 5vw, 60px); padding-block: 22px 30px;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 28px;
}
.foot__legal { margin: 0; font-size: 13px; line-height: 1.5; color: var(--fg-faint); }
/* the Enamad trust seal sits between the legal line and "back to top". The white
   plate is deliberate: Enamad's badge is drawn for light backgrounds and goes
   muddy straight onto the dark footer. Scaled proportionally and never hidden —
   both are conditions of keeping the seal. */
.foot__seal { display: inline-flex; line-height: 0; }
/* the white plate goes on the IMG, never on the link. Enamad's host refuses
   connections from outside Iran, so for a visitor abroad this image always
   fails — and a failed <img alt=""> collapses to 0x0, taking the plate with it.
   Styling the link instead left an 8px white splinter in the footer for every
   non-Iranian visitor. */
.foot__seal img {
  display: block; height: 62px; width: auto;
  background: #fff; border-radius: 10px;
}

.foot__top {
  margin-inline-start: auto; display: inline-flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--fg-dim); text-decoration: none;
  padding: 8px 14px; border-radius: 999px; border: 1px solid var(--line-2);
  transition: color 0.25s ease, border-color 0.25s ease, background 0.25s ease;
}
.foot__top svg { transition: transform 0.3s var(--e-out); }
.foot__top:hover { color: #f4f3f0; border-color: rgba(240,169,74,0.55); background: rgba(240,169,74,0.10); }
.foot__top:hover svg { transform: translateY(-3px); }
.foot__top.is-bump svg { animation: footTopBump 0.55s var(--e-out); }
@keyframes footTopBump {
  0%   { transform: translateY(0); }
  35%  { transform: translateY(-8px); }
  62%  { transform: translateY(2px); }
  100% { transform: translateY(0); }
}

@media (max-width: 860px) {
  .foot__in { grid-template-columns: 1fr; gap: 40px; }
  .foot__top { margin-left: 0; }
}
@media (max-width: 480px) {
  .foot__nav { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ============================================================================
   REDUCED MOTION — the page must read identically with everything switched off
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html:focus-within { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js .rv, .js .hero__title .w { opacity: 1 !important; transform: none !important; filter: none !important; }
  .hero__accent path { stroke-dashoffset: 0; }
  .js .fx__spot { display: none; }
  .heat__c, .split i, .rhythm i { opacity: 1; transform: none; }
  .spark .line { stroke-dashoffset: 0; }
  .spark .area { opacity: 1; }
  .spark .knob { r: 4.5; }
  .step p { max-height: 160px; opacity: 1; }
  /* the whole scene rests on its finished state: card complete, reward out —
     the payoff of the story, held still instead of replayed */
  .wcard, .oram-sheen, .face__complete,
  .wcard .slot.is-on, .wcard .slot.is-on::after,
  .wcard .slot.is-on .stamp, .wcard .slot.is-on .slot__num { animation: none; }
  .wcard { opacity: 1; transform: none; }
  .count__roll { animation: none; transform: translateY(-8em); }
  .seal, .seal__impact, .prize__sweep, .prize__title { animation: none; }
  .seal { opacity: 1; transform: rotate(-6deg); }
  .seal__impact { display: none; }
  .prize { animation: none; opacity: 1; transform: scale(1) translateY(0) rotate(4deg); }
  .prize__sweep { display: none; }
}

/* ============================================================================
   BAND VARIANTS + LATE FIXES
   ========================================================================== */
/* a second ink tone so consecutive dark sections still read as separate slabs */
.alt { --bg: var(--ink-850); }
.band--paper { box-shadow: inset 0 1px 0 rgba(255,255,255,0.6); }

/* anchors land below the fixed bar */
section[id] { scroll-margin-top: 92px; }

/* ============================================================================
   RTL (Persian)
   The rest of the page mirrors on its own once html[dir="rtl"] flips the
   document's `direction` — flex/grid main axes and default text alignment
   follow it natively. What is fixed below are the handful of spots that use
   a *physical* left/right (an absolutely-positioned icon, a fixed padding
   side) which direction:rtl does not touch by itself.
   ========================================================================== */
html[dir="rtl"] .plan__nudge { padding: 14px 14px 14px 34px; }
html[dir="rtl"] .plan__nudge::after { content: "←"; right: auto; left: 16px; }
html[dir="rtl"] .plan__nudge:hover::after { transform: translate(-4px, -50%); }

/* the spinning background wordmark sits behind the card demo, which mirrors
   to the right side of the hero in RTL — move the ghost logo to match. */
/* 1.06 is tuned for Fraunces, whose Latin caps sit in a shallow band. Vazirmatn
   carries taller ascenders and real descenders, so the same ratio jams two
   Persian lines into each other — most visible on any heading that wraps. */
html[dir="rtl"] h1,
html[dir="rtl"] h2,
html[dir="rtl"] h3,
html[dir="rtl"] h4 { line-height: 1.24; }

html[dir="rtl"] .hero__ghost { left: auto; right: -8vw; }
/* The stacked mobile layout centres the watermark (see the max-width:860px
   block), but that never takes effect in Persian: this RTL section is both
   more specific and later in the file, so the rule above wins. Rather than
   leave the mobile rule quietly dead, place the mark deliberately for the
   narrow layout — a little further right, off the stacked copy. */
@media (max-width: 860px) {
  html[dir="rtl"] .hero__ghost { right: -14vw; }
  /* English gets the mirror of it: the copy stacks left-aligned here, so the
     mark sits left too. left/right and margin-inline all need overriding,
     because the centring rule sets all three. */
  html[dir="ltr"] .hero__ghost { left: -14vw; right: auto; margin-inline: 0; }
}

/* the ember glow pools are placed with a physical inset + an off-centre
   radial-gradient position, so direction:rtl doesn't move them on its own —
   mirror both the box and the gradient centre to the right in RTL. */
html[dir="rtl"] .hero__glow {
  inset: -20% -30% auto -10%;
  background: radial-gradient(58% 52% at 78% 34%, rgba(240,169,74,0.20), transparent 68%);
}
html[dir="rtl"] .foot__glow {
  inset: auto -10% -30% auto;
  background: radial-gradient(50% 50% at 60% 60%, rgba(240,169,74,0.10), transparent 70%);
}

/* same story for the two paper bands: each corner wash is baked into the
   section background rather than a positioned child, so mirror the corners
   the gradients sit in along with everything else in RTL. */
html[dir="rtl"] #insights {
  background:
    radial-gradient(56% 42% at 90% 6%, rgba(240,169,74,0.20), transparent 62%),
    radial-gradient(48% 40% at 6% 86%, rgba(47,63,79,0.12), transparent 66%),
    linear-gradient(168deg, #efebe3 0%, #e8e3d9 58%, #e3ded2 100%);
}
/* the mini loyalty card is a literal replica of the real app's UI (see the
   comment above .wcard's shared use), not page copy — it stays laid out
   exactly as in the English version regardless of site language. */
html[dir="rtl"] .wcard { direction: ltr; }

/* the eyebrow/category rules draw themselves out of the label towards the
   far edge, so their gradient (opaque near the label, fading away) and their
   scale-reveal origin both need to point the other way once the flex row
   reverses and the label ends up on the right in RTL. */
html[dir="rtl"] .faq__cat-rule {
  background: linear-gradient(270deg, rgba(240,169,74,0.5), transparent);
}
html[dir="rtl"] .js .faq__cat-rule { transform-origin: right; }
/* line-height:1 centres the line box, but Vazirmatn's descent is 0.54em against
   Fraunces' 0.26em, so its baseline sits lower and the digits ride ~0.11em above
   the middle of a round badge. Padding shifts grid-centred content down by half
   its value, so 0.22em lands them dead centre. Measured from the two fonts' own
   metrics, and Latin needs no correction at all — hence RTL only. */
html[dir="rtl"] .ins__eyebrow::before,
html[dir="rtl"] .faq__cat::before,
html[dir="rtl"] .about__ini { padding-top: 0.22em; }

html[dir="rtl"] .ins__eyebrow::after {
  background: linear-gradient(270deg, rgba(240,169,74,0.4), transparent);
}

/* every other underline/reveal animation on the page grows from the left by
   default (scaleX from transform-origin:left, or a right-inset shrinking
   toward 0) — mirror each one so it grows from the reading-start side in
   RTL instead. */
html[dir="rtl"] .nav__links a::after { transform-origin: right; }
html[dir="rtl"] .nav__prog { transform-origin: right; }
html[dir="rtl"] .foot__col a::after {
  left: 100%; right: 0; transition: left 0.32s var(--e-out);
}
html[dir="rtl"] .foot__col a:hover::after { left: 0; }

/* the CTA buttons' arrow is a baked-in right-pointing SVG path, not a glyph
   direction:rtl can flip on its own — mirror it, and negate the hover/launch
   translateX distances so it still darts toward the reading-forward side
   (left in RTL) instead of drifting into the button label. */
html[dir="rtl"] .btn__arrow { transform: scaleX(-1); }
html[dir="rtl"] .btn:hover .btn__arrow { transform: scaleX(-1) translateX(4px); }
html[dir="rtl"] .btn.is-launch .btn__arrow { animation: btnArrowLaunchRtl 0.42s var(--e-out); }
@keyframes btnArrowLaunchRtl {
  0%   { transform: scaleX(-1) translateX(0); opacity: 1; }
  45%  { transform: scaleX(-1) translateX(12px); opacity: 0; }
  46%  { transform: scaleX(-1) translateX(-8px); }
  100% { transform: scaleX(-1) translateX(0); opacity: 1; }
}

/* ============================================================================
   MOBILE PERFORMANCE
   ========================================================================== */
/* Scrolling on iOS was janky, and the causes are all decorative:
   - .fx__orb is a ~500px fixed element carrying blur(90px) while animating
     scale(). A blur cannot be cached across a scale change, so the whole
     blurred gradient re-renders every frame, and being fixed it does that for
     the entire length of the page. Freezing the animation lets it composite
     once and stay put.
   - .nav's backdrop-filter re-blurs whatever passes beneath it, every frame,
     for the whole scroll. A near-opaque bar reads the same and costs nothing.
   - the ring, sheen, stage and sweep loops repaint large areas continuously.
   - .rv blurs 44 elements until they reveal.
   None of it carries meaning, so a phone keeps the layout and loses the cost. */
@media (max-width: 940px) {
  /* Safari re-rasterises a blurred layer during momentum scroll even when it
     is not animating, and both of these sit on radial gradients that are
     already soft — the blur buys almost nothing and costs the most. */
  .fx__orb { animation: none; will-change: auto; filter: none; }
  .prize__glow { filter: none; }
  /* will-change pins a composited layer for the life of the page. Across every
     button and every word of the hero headline that is a lot of layers for a
     phone to hold, in exchange for hover lifts a touch device never sees. */
  .btn, .hero__title .w { will-change: auto; }
  /* wide shadow blurs repaint over long stretches of scroll */
  .phone__frame {
    box-shadow: 0 22px 38px -26px rgba(0,0,0,0.9), inset 0 1px 0 rgba(255,255,255,0.16);
  }
  .ins {
    box-shadow: 0 18px 34px -24px rgba(38,28,12,0.5), inset 0 1px 0 rgba(255,255,255,0.07);
  }
  .nav.is-stuck {
    background: rgba(8,8,10,0.94);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .glass, .btn--ghost, .nav__mobile, .scene__cap {
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
  .plan__ring,
  .plan--hero::after,
  #pricing::after,
  .next__sweep { animation: none; }
  /* keep the reveal, drop the part of it that costs the most */
  .js .rv { filter: none; }
  .js .rv {
    transition:
      opacity   0.5s var(--e-out) var(--rv-delay, 0ms),
      transform 0.55s var(--e-out) var(--rv-delay, 0ms);
  }
}

/* An animation off screen still costs frames to composite — the hero alone runs
   ~39 loops, and they were all still running three sections later. pauseOffscreen()
   in for-business.js marks a region while it is out of view. Nothing visible
   changes, because by definition none of it is being looked at. */
.is-paused, .is-paused * { animation-play-state: paused; }


/* A quiet line under the hero buttons for the visitor who is already a Reloy
   customer: reloy.ir shows this page to anyone not signed in, and it is the
   domain printed on the NFC tags, so they arrive here by default. Deliberately
   understated — it must not compete with the two CTAs above it. */
.hero__app { margin: 14px 0 0; font-size: 14.5px; }
.hero__app a {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--fg-dim, rgba(244,243,240,0.72)); text-decoration: none;
  border-bottom: 1px solid transparent; padding-bottom: 1px;
  transition: color 0.2s ease, border-color 0.2s ease, gap 0.2s ease;
}
.hero__app a:hover { color: #fff; border-bottom-color: rgba(244,243,240,0.5); gap: 9px; }
.hero__app svg { flex: none; }
@media (max-width: 860px) { .hero__app { font-size: 13.5px; } }
