/*
Theme Name: Beterekoonesh
Description: Public site theme for BETEREKOONESH — landing page, login/register (locked in docs/07_PHASE_06_UI_IMPLEMENTATION_BLUEPRINT.md and docs/12_PHASE_08_WORDPRESS_LANDING_LOGIN.md). Dark theme only, Persian/RTL.
Version: 0.1.0
*/

/* Morabba — the project's font (docs/07_PHASE_06_UI_IMPLEMENTATION_BLUEPRINT.md Typography,
   revised 2026-08-26). Self-hosted (licensed via fontiran, not on Google Fonts); files copied
   from docs/moraba/webfont/fonts/ into assets/fonts/morabba/. Used everywhere — headlines and
   body text alike, per the owner's explicit standing instruction. */
/* Weights 200/300/850/900 were removed 2026-10-01. Nothing on the site requests them.
   The 900 file was still being downloaded because Tailwind's preflight gives <b>/<strong>
   `font-weight: bolder`, which against a 700 parent resolves to 900 - so a 21KB file was
   being fetched for a weight no one had asked for. They now resolve to 800. If a design
   ever genuinely needs one of these, add the face back rather than leaving it declared
   'just in case': a declared face that something accidentally matches is a real download. */
@font-face { font-family: Morabba; font-style: normal; font-weight: 400; font-display: swap; src: url('assets/fonts/morabba/Morabba-Regular.woff2?v=hhea2') format('woff2'), url('assets/fonts/morabba/Morabba-Regular.woff?v=hhea2') format('woff'); }
@font-face { font-family: Morabba; font-style: normal; font-weight: 500; font-display: swap; src: url('assets/fonts/morabba/Morabba-Medium.woff2?v=hhea2') format('woff2'), url('assets/fonts/morabba/Morabba-Medium.woff?v=hhea2') format('woff'); }
@font-face { font-family: Morabba; font-style: normal; font-weight: 600; font-display: swap; src: url('assets/fonts/morabba/Morabba-SemiBold.woff2?v=hhea2') format('woff2'), url('assets/fonts/morabba/Morabba-SemiBold.woff?v=hhea2') format('woff'); }
@font-face { font-family: Morabba; font-style: normal; font-weight: 700; font-display: swap; src: url('assets/fonts/morabba/Morabba-Bold.woff2?v=hhea2') format('woff2'), url('assets/fonts/morabba/Morabba-Bold.woff?v=hhea2') format('woff'); }
@font-face { font-family: Morabba; font-style: normal; font-weight: 800; font-display: swap; src: url('assets/fonts/morabba/Morabba-ExtraBold.woff2?v=hhea2') format('woff2'), url('assets/fonts/morabba/Morabba-ExtraBold.woff?v=hhea2') format('woff'); }

/* Material Symbols Outlined — self-hosted (Phase 08.5), was loaded from fonts.googleapis.com.
   That Google-hosted webfont intermittently failed on the owner's connection and rendered raw
   ligature text ("light_mode", "check", …) instead of icons — a real, reported bug. This file
   is a SUBSET: the 166 icons the theme and all three panel plugins actually use, instanced at
   wght 300 / opsz 24 to match what Google was serving. Regenerate with tools/subset-icons.py
   when new icons are added — see docs/16_PHASE_08_5_BUILD_PIPELINE.md. font-display:block (not
   swap) is deliberate: a brief blank is better than a flash of the ligature text.
   Size note (2026-09-15): this file shipped at 268KB carrying all 3,555 Material Symbols icons
   until the subsetter's layout closure was turned off — see tools/subset-icons.py. It is now
   15.6KB. */
@font-face {
  font-family: 'Material Symbols Outlined';
  font-style: normal;
  font-weight: 300;
  font-display: block;
  src: url('assets/fonts/material-symbols/material-symbols-outlined.woff2?v=53bb5cf95c') format('woff2');
}

/* Vazirmatn — the documented fallback face shown for the brief moment Morabba is loading
   (font stack: Morabba, Vazirmatn, Tahoma). Self-hosted (Phase 08.5), was loaded from
   fonts.googleapis.com — the last Google Fonts dependency; the preconnect and <link> in
   front-page.php are gone with it. Variable weight 100–900, split by unicode-range exactly
   as @fontsource-variable/vazirmatn ships it (arabic subset carries the Persian glyphs). */
/* Only the ARABIC subset of Vazirmatn is kept. The latin and latin-ext subsets were
   removed 2026-10-01: Vazirmatn exists here purely as the brief fallback while Morabba
   loads, and the latin file alone was 33.7KB - the largest single file on the page - to
   cover Latin strings like "rosa.shop" that Morabba has no glyphs for. Those now fall
   through to the system face at the end of the stack. No Persian text is affected. */
@font-face {
  font-family: Vazirmatn;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/vazirmatn/vazirmatn-arabic-wght-normal.woff2') format('woff2');
  unicode-range: U+0600-06FF,U+0750-077F,U+0870-088E,U+0890-0891,U+0897-08E1,U+08E3-08FF,U+200C-200E,U+2010-2011,U+204F,U+2E41,U+FB50-FDFF,U+FE70-FE74,U+FE76-FEFC;
}

/* Theme tokens — light/dark toggle (added 2026-08-26, docs/07_PHASE_06...Section 2 updated).
   Accent colors (blue/cyan/green/red/amber) are identical in both themes per the owner's
   explicit "exact same colors, just a light version" instruction — only the neutral
   bg/surface/border/text/muted tokens differ. Every other rule in this file that needs a
   "faint tint of the page's own surface/text color" (glass panels, hairline borders, grid
   lines, dots) uses color-mix() against these variables instead of hardcoded rgba(255,...)
   values, so it adapts to both themes automatically without a second copy of each rule. */
:root {
  --c-bg: #0A0F1E;
  --c-surface: #121A2C;
  --c-surface2: #1A2338;
  --c-border: #232E47;
  --c-text: #F1F5F9;
  --c-muted: #8B96AC;
}
[data-theme="light"] {
  --c-bg: #F6F8FC;
  --c-surface: #FFFFFF;
  --c-surface2: #EEF2F9;
  --c-border: #DCE3F0;
  --c-text: #0B1220;
  --c-muted: #5B6478;
}

body {
  margin: 0;
  background-color: var(--c-bg);
  color: var(--c-text);
  transition: background-color 0.25s ease, color 0.25s ease;
}

/* Visible keyboard focus on every interactive element (web-design-guidelines rule:
   never remove focus without a focus-visible replacement). */
a:focus-visible,
button:focus-visible,
summary:focus-visible {
  outline: 2px solid #22d3ee;
  outline-offset: 2px;
}

/* ---- Landing page v2 (Phase 08 Task 3 redesign) ---- */

html {
  scroll-behavior: smooth;
}

/* The `.material-symbols-outlined` base rule (font-family, ligatures, `display: inline-block`,
   etc. — formerly injected by Google's <link>, brought in-repo in Phase 08.5) now lives in
   src/tailwind.css under @layer base, so Tailwind's `hidden` / `md:hidden` display utilities
   always win over it. It must NOT be redeclared here as a plain rule — this file loads after
   the compiled tailwind.css and an unlayered rule would defeat those utilities again. */

/* Glass surfaces: our navy tokens tinted + blurred, not generic white glassmorphism,
   so panels still read as "our app" rather than a stock frosted-glass template. */
.glass {
  background: color-mix(in srgb, var(--c-surface) 55%, transparent);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
}
.glass-strong {
  background: color-mix(in srgb, var(--c-bg) 75%, transparent);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}

.hover-lift {
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
/* hover:hover excludes touchscreens — without it, a tap can leave the lift/glow "stuck"
   until the user taps elsewhere, which reads as a bug rather than a hover affordance. */
@media (hover: hover) {
  .hover-lift:hover {
    transform: translateY(-4px);
    border-color: rgba(34, 211, 238, 0.4);
    box-shadow: 0 24px 48px -24px rgba(34, 211, 238, 0.35);
  }
  /* Red variant for the "problem" card (Problem vs. Solution section) — mirrors the default
     cyan glow so the card's own accent color intensifies on hover instead of switching to
     a mismatched cyan glow on a card whose resting state is red-themed. */
  .hover-lift-red:hover {
    border-color: rgba(239, 68, 68, 0.4);
    box-shadow: 0 24px 48px -24px rgba(239, 68, 68, 0.35);
  }
}
.hover-lift:active {
  transform: translateY(-1px);
  border-color: rgba(34, 211, 238, 0.3);
}
.hover-lift-red:active {
  border-color: rgba(239, 68, 68, 0.3);
}

.float {
  animation: btk-float 6s ease-in-out infinite;
}
@keyframes btk-float {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-28px) rotate(-1.5deg); }
}

/* =====================================================================================
   HERO v3 + site-wide background — redesigned 2026-10-01 (owner's "redesign pass").

   What replaced what, and why:
   - The 3-phone carousel is gone. Three devices read as clutter at 390px and forced every
     screenshot to be tiny; ONE device large enough to actually read is the stronger frame,
     and it is the same device on desktop and mobile so there is one silhouette to get right.
   - The device no longer shows a static .webp screenshot. It runs a LIVE, looping mini-UI of
     our own product doing its job (scan -> score climbs 58->84 -> findings land). That is the
     owner's own brief for this whole landing: show the work as motion, don't describe it in
     prose. It also drops ~250KB of hero imagery off the critical path.
   - The loop is 100% CSS keyframes on ONE shared 10s timeline, no JS and no GSAP. Every step
     below is a percentage of that same 10s, which is why the ring, the number and the copy
     can never drift out of sync with each other the way a JS-driven counter would.
   - Compositor-only properties (opacity/transform/stroke-dashoffset). Nothing here animates a
     layout property, so the whole hero stays off the layout path on a mid-range phone.
   ===================================================================================== */

/* ---- Site-wide background layer (printed once, right after <body>) ------------------
   Fixed, behind everything, so it reads as one continuous atmosphere the page scrolls over
   instead of a per-section gradient that visibly restarts at each seam. Three parts:
   an aurora bloom in our own accent colors, a hairline grid, and a rising "growth curve".
   The curve rises toward the INLINE-START edge on purpose: in RTL, right is where the eye
   begins, so left is "later / higher" — the same direction our customers' growth should go. */
.btk-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;
}
.btk-bg-aurora {
  position: absolute;
  inset: -25%;
  background:
    radial-gradient(34% 28% at 76% 10%, rgba(34, 211, 238, 0.26), transparent 70%),
    radial-gradient(40% 32% at 14% 72%, rgba(59, 130, 246, 0.24), transparent 70%),
    radial-gradient(30% 26% at 54% 44%, rgba(16, 185, 129, 0.10), transparent 70%);
  animation: btk-aurora 30s ease-in-out infinite alternate;
  will-change: transform;
}
/* The light theme uses the exact same blooms at lower strength rather than a second palette,
   per the locked "same colors, just a light version" rule for this project's tokens. */
[data-theme="light"] .btk-bg-aurora { opacity: 0.5; }
.btk-bg-grid {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(color-mix(in srgb, var(--c-text) 4%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--c-text) 4%, transparent) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: radial-gradient(ellipse 75% 55% at 50% 0%, black, transparent 78%);
  mask-image: radial-gradient(ellipse 75% 55% at 50% 0%, black, transparent 78%);
}
.btk-bg-curve {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  width: 100%;
  height: 62%;
  opacity: 0.55;
}
[data-theme="light"] .btk-bg-curve { opacity: 0.42; }
@keyframes btk-aurora {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(2%, -2.5%, 0) scale(1.08); }
}

/* A pulse of light travelling up the growth curve — the background's one moving idea, and the
   one that actually means something here: data moving through an analysis. It is a dash
   pattern sliding along the SAME path the curve is drawn with, so it costs one animated
   property on one element and can never drift away from the line it rides on.
   `pathLength="100"` in the markup normalises the path so "6 94" is literally "6% lit,
   94% dark" regardless of how long the real path is at the current viewport size. */
.btk-bg-pulse {
  animation: btk-bg-pulse 11s linear infinite;
}
.btk-bg-pulse-2 {
  animation-duration: 16s;
  animation-delay: -6s;
}
@keyframes btk-bg-pulse {
  from { stroke-dashoffset: 100; }
  to   { stroke-dashoffset: 0; }
}

/* Post tiles drifting far behind everything — the shape of an Instagram grid, at the opacity
   of a watermark. They are the subject of the whole product, so the page's own atmosphere is
   made of them rather than of abstract blobs. */
.btk-bg-tile {
  position: absolute;
  border-radius: 24%;
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  background: linear-gradient(140deg,
    color-mix(in srgb, var(--c-text) 4%, transparent),
    transparent 70%);
  animation: btk-bg-tile 34s ease-in-out infinite alternate;
}
.btk-bg-tile:nth-child(1) { width: 92px;  height: 92px;  top: 14%; inset-inline-end: 8%;  animation-delay: -2s;  }
.btk-bg-tile:nth-child(2) { width: 58px;  height: 58px;  top: 32%; inset-inline-end: 21%; animation-delay: -11s; }
.btk-bg-tile:nth-child(3) { width: 120px; height: 120px; top: 58%; inset-inline-start: 6%; animation-delay: -19s; }
.btk-bg-tile:nth-child(4) { width: 70px;  height: 70px;  top: 74%; inset-inline-end: 14%; animation-delay: -27s; }
.btk-bg-tile:nth-child(5) { width: 46px;  height: 46px;  top: 8%;  inset-inline-start: 18%; animation-delay: -7s; }
@keyframes btk-bg-tile {
  0%   { transform: translate3d(0, 0, 0) rotate(-4deg); }
  100% { transform: translate3d(0, -38px, 0) rotate(4deg); }
}
/* Below `md` the tiles are decoration the visitor has no room for, and every one of them is a
   composited layer on a device that has the least to spare. */
@media (max-width: 767px) {
  .btk-bg-tile { display: none; }
}

/* ---- Hero stage: the device and the glass cards that orbit it ---------------------- */
.hero-stage {
  display: flex;
  justify-content: center;
}
/* The cards are positioned against THIS, not against .hero-stage. The stage is a full grid
   column and is much wider than the device, so anchoring the cards to it left them floating
   off in open space instead of overlapping the screen they are pointing at. */
.hero-anchor {
  position: relative;
  width: max-content;
}
/* One device, one silhouette. `container-type: inline-size` is what lets every dimension of
   the device chrome AND of the live UI inside it be written in `cqw` (1% of the phone's own
   width) — so the desktop phone and the smaller mobile one are the same design at two sizes,
   never a set of px values tuned for one and broken on the other. */
.hero-phone {
  position: relative;
  /* Set per layout branch as an inline custom property rather than an inline `width`, so the
     media query below can still shrink it — an inline width would beat any stylesheet rule
     short of !important. */
  width: var(--phone-w, 300px);
  container-type: inline-size;
}
.hero-phone::before {
  content: '';
  position: absolute;
  inset: -12% -24%;
  z-index: -1;
  background: radial-gradient(closest-side, rgba(34, 211, 238, 0.3), transparent 72%);
}
.hero-card {
  position: absolute;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.8rem;
  border-radius: 16px;
  white-space: nowrap;
  font-size: 12.5px;
  line-height: 1.5;
  /* Real glass: a tinted pane of OUR surface color, blurred, with a bright top hairline for
     the lit edge and a long soft shadow for the lift. Not a generic white frosted panel. */
  background: color-mix(in srgb, var(--c-surface) 58%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(150%);
  backdrop-filter: blur(18px) saturate(150%);
  border: 1px solid color-mix(in srgb, var(--c-text) 11%, transparent);
  box-shadow:
    0 20px 44px -24px rgba(2, 6, 18, 0.85),
    inset 0 1px 0 color-mix(in srgb, var(--c-text) 12%, transparent);
}
.hero-card b { font-weight: 700; }
.hero-card small { display: block; font-size: 10.5px; color: var(--c-muted); }
.hero-card-icon {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  flex: none;
  border-radius: 9px;
  font-size: 15px;
}
/* The cards are on the SAME 15s clock as the screen, and each one appears only during the
   moment of the story it belongs to: the problem card as the scan finishes and the findings
   land, the score card as the number settles on 84. That is not just narrative tidiness — it
   is what fixed a real defect. Parked permanently, a card sat on top of live data (the stats
   row, then the findings) for the whole loop, and there is no position on a phone-shaped
   screen where a callout that size covers nothing. Appearing and leaving, it covers its own
   scene for a few seconds and the rest of the time the screen is unobstructed. */
.hero-card-a {
  top: 13%;
  inset-inline-end: -30%;
  animation: btk-card-a 15s ease-in-out infinite;
}
.hero-card-b {
  bottom: 15%;
  inset-inline-start: -28%;
  animation: btk-card-b 15s ease-in-out infinite;
}
@keyframes btk-card-a {
  0%, 25%   { opacity: 0; transform: translateY(14px) scale(0.96); }
  31%, 45%  { opacity: 1; transform: translateY(0) scale(1); }
  50%, 100% { opacity: 0; transform: translateY(-10px) scale(0.98); }
}
@keyframes btk-card-b {
  0%, 51%   { opacity: 0; transform: translateY(14px) scale(0.96); }
  57%, 73%  { opacity: 1; transform: translateY(0) scale(1); }
  78%, 100% { opacity: 0; transform: translateY(-10px) scale(0.98); }
}

/* Between lg and xl the two-column hero exists but is genuinely narrow: at 1024 the stage
   column is ~464px, so a 320px device plus a callout hanging 96px past it put that callout's
   outer edge at -8px and the section's own overflow:hidden quietly sliced it off. Verified in
   the browser at 1024, not assumed. The device gives back a little width and both callouts
   tuck closer in; from 1280 up there is room and they return to the fuller offsets. */
@media (min-width: 1024px) and (max-width: 1279px) {
  .hero-phone { --phone-w: 286px; }
  .hero-card-a { inset-inline-end: -13%; }
  .hero-card-b { inset-inline-start: -11%; }
  .hero-card { font-size: 12px; padding: 0.55rem 0.7rem; }
}

/* ---- The live product loop inside the screen --------------------------------------
   Rebuilt 2026-10-01 after the owner's review of v3: "the screen has to be FULL of realistic
   data, and things have to keep happening the whole time someone is looking at the hero."

   So this is no longer one layout with a few animated parts. It is THREE full screens of our
   own product — تحلیل صفحه / امتیاز و مشکل‌ها / نقشهٔ راه هفته — that replace each other on one
   shared 15s clock, each with its own data moving inside it. There is never a moment where
   nothing is happening, and every number on screen is the kind of number our product really
   prints.

   Second change from the same review: the screen is now THEMED. It was hard-coded dark, which
   meant a light-mode visitor saw a dark phone on a light page. Every color below is a --c-*
   token, so the phone is light in light mode and dark in dark mode — which is also honest,
   because the real customer panel has both.

   Everything animated here is opacity / transform / stroke-dashoffset. The bars grow with
   scaleX/scaleY against a fixed box rather than by animating width or height, so not one frame
   of this loop touches layout. */
.pscreen {
  --ps-surface: var(--c-surface);
  --ps-line: color-mix(in srgb, var(--c-text) 9%, transparent);
  --ps-track: color-mix(in srgb, var(--c-text) 10%, transparent);
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  padding: 0 4cqw 3.4cqw;
  background: var(--c-bg);
  color: var(--c-text);
  font-size: 3.4cqw;
  text-align: start;
}

/* iOS status bar. Persian is RTL and iOS mirrors it, so the clock sits at the start edge.
   The side padding clears the Dynamic Island sitting between the two groups. */
.ps-status {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 12cqw;
  padding: 0 4cqw;
  font-size: 3.1cqw;
  font-weight: 700;
}
.ps-sys { display: flex; align-items: flex-end; gap: 0.8cqw; }
.ps-sys i { width: 1cqw; background: currentColor; border-radius: 0.4cqw; }
.ps-sys i:nth-child(1) { height: 1.6cqw; }
.ps-sys i:nth-child(2) { height: 2.3cqw; }
.ps-sys i:nth-child(3) { height: 3cqw; }
.ps-sys i:nth-child(4) { width: 4.8cqw; height: 2.5cqw; border-radius: 0.8cqw; }

/* ---- Scene machinery --------------------------------------------------------------
   One stage, three screens stacked on it. `space-between` is doing real work: it spreads
   whatever vertical slack a given scene has between its own blocks, so all three fill the
   same tall screen without any of them needing hand-tuned heights. */
.ps-body { position: relative; flex: 1; min-height: 0; }
.ps-scene {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  opacity: 0;
  animation: btk-sc1 15s ease-in-out infinite;
}
.ps-scene-2 { animation-name: btk-sc2; }
.ps-scene-3 { animation-name: btk-sc3; }
@keyframes btk-sc1 {
  0%, 1%    { opacity: 0; transform: translateY(5%); }
  5%, 29%   { opacity: 1; transform: translateY(0); }
  33%, 100% { opacity: 0; transform: translateY(-5%); }
}
@keyframes btk-sc2 {
  0%, 31%   { opacity: 0; transform: translateY(5%); }
  37%, 62%  { opacity: 1; transform: translateY(0); }
  66%, 100% { opacity: 0; transform: translateY(-5%); }
}
@keyframes btk-sc3 {
  0%, 64%   { opacity: 0; transform: translateY(5%); }
  70%, 95%  { opacity: 1; transform: translateY(0); }
  99%, 100% { opacity: 0; transform: translateY(-5%); }
}

/* ---- Shared pieces ---------------------------------------------------------------- */
.ps-head { display: flex; align-items: center; gap: 2.4cqw; }
/* The avatar ring is the one piece of Instagram's own visual language on the page — it is
   what makes the screen legible as "this is your page" in a quarter of a second. */
.ps-av {
  position: relative;
  width: 10.5cqw;
  height: 10.5cqw;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(from 200deg, #22d3ee, #3b82f6, #a855f7, #f59e0b, #22d3ee);
}
.ps-av::after {
  content: '';
  position: absolute;
  inset: 1cqw;
  border-radius: 50%;
  background: var(--c-bg);
}
/* min-width:0 so a long Persian line shrinks instead of pushing the row wider than the screen;
   the spans must be blocks or the name and the sub-line share one line. */
.ps-headtext { display: block; min-width: 0; }
.ps-name { display: block; font-weight: 700; font-size: 3.7cqw; }
.ps-sub { display: block; font-size: 3cqw; color: var(--c-muted); white-space: nowrap; }
.ps-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-weight: 700;
  font-size: 3.6cqw;
}
.ps-title span { font-weight: 400; font-size: 2.9cqw; color: var(--c-muted); }
.ps-card {
  padding: 3.4cqw;
  border-radius: 3cqw;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
}

/* ---- Scene 1: تحلیل صفحه ----------------------------------------------------------- */
.ps-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.6cqw; }
.ps-stat {
  padding: 2.8cqw 1.6cqw;
  border-radius: 2.6cqw;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  text-align: center;
}
.ps-stat b { display: block; font-size: 4cqw; font-weight: 800; }
.ps-stat i { display: block; font-style: normal; font-size: 2.7cqw; color: var(--c-muted); }
.ps-stat-up b { color: #10b981; }

.ps-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.3cqw;
  border-radius: 2.4cqw;
  overflow: hidden;
}
/* The tiles used to be one tinted surface gradient repeated twelve times. In dark mode that
   read as a placeholder, and in light mode it went nearly white — the owner spotted exactly
   that: "some of it has no image". A real Instagram grid is twelve DIFFERENT photographs, so
   the tiles now cycle six muted colour recipes with a soft top-left highlight, which is what a
   photograph looks like at 90px. They are deliberately desaturated: bright stock-photo colours
   would pull the eye off the score and the findings, which are the point of the screen.
   These are CSS gradients, not photographs — see the note in front-page.php about what it
   would take to put real imagery here. */
.ps-grid i {
  aspect-ratio: 1;
  border-radius: 1.4cqw;
  background-image:
    radial-gradient(120% 90% at 22% 12%, rgba(255, 255, 255, 0.22), transparent 62%),
    linear-gradient(140deg, var(--t1), var(--t2));
}
.ps-grid i:nth-child(6n + 1) { --t1: #5B6B8C; --t2: #36425A; }
.ps-grid i:nth-child(6n + 2) { --t1: #8C6F5B; --t2: #57412F; }
.ps-grid i:nth-child(6n + 3) { --t1: #4F7C6E; --t2: #2C4941; }
.ps-grid i:nth-child(6n + 4) { --t1: #7B5B8C; --t2: #473353; }
.ps-grid i:nth-child(6n + 5) { --t1: #8C8257; --t2: #565033; }
.ps-grid i:nth-child(6n + 6) { --t1: #5B7A8C; --t2: #364C5A; }
/* A couple of tiles carry a reel marker, so the grid reads as a real feed rather than a
   placeholder pattern of identical squares. */
.ps-grid i.ps-reel::after {
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  margin: 1.6cqw 0 0 1.6cqw;
  border-style: solid;
  border-width: 1.3cqw 0 1.3cqw 2.1cqw;
  border-color: transparent transparent transparent color-mix(in srgb, var(--c-text) 55%, transparent);
}
.ps-sweep {
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 26%;
  background: linear-gradient(180deg, transparent, rgba(34, 211, 238, 0.5), transparent);
  animation: btk-ps-sweep 15s ease-in-out infinite;
}
@keyframes btk-ps-sweep {
  0%        { transform: translateY(-120%); opacity: 0; }
  3%        { opacity: 1; }
  24%       { transform: translateY(400%); opacity: 1; }
  27%, 100% { transform: translateY(400%); opacity: 0; }
}

.ps-prog { display: grid; gap: 1.6cqw; }
.ps-prog-row { display: flex; justify-content: space-between; font-size: 3cqw; color: var(--c-muted); }
.ps-bar { height: 1.6cqw; border-radius: 9999px; background: var(--ps-track); overflow: hidden; }
/* transform-origin is the INLINE start, so every bar in this UI grows from the right in RTL —
   the direction a Persian reader expects a value to fill. */
.ps-bar > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #3b82f6, #22d3ee);
  transform: scaleX(0);
  transform-origin: right center;
}
.ps-prog .ps-bar > i { animation: btk-ps-prog 15s ease-out infinite; }
@keyframes btk-ps-prog {
  0%, 3%    { transform: scaleX(0.04); }
  26%, 33%  { transform: scaleX(1); }
  34%, 100% { transform: scaleX(0.04); }
}

/* ---- Scene 2: امتیاز و مشکل‌ها ------------------------------------------------------ */
.ps-score { display: flex; align-items: center; gap: 3.2cqw; }
.ps-scorecard { display: block; }
.ps-ring { width: 24cqw; height: 24cqw; flex: none; transform: rotate(-90deg); }
.ps-ring-bg { fill: none; stroke: var(--ps-track); stroke-width: 11; }
/* r=52 -> circumference 326.7. The keyframes drive dashoffset from "empty" (327) to the 84%
   mark (52.3), which is the score the copy and the glass card both quote. */
.ps-ring-fg {
  fill: none;
  stroke: url(#btkRingGrad);
  stroke-width: 11;
  stroke-linecap: round;
  stroke-dasharray: 327;
  stroke-dashoffset: 327;
  animation: btk-ps-ring 15s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
@keyframes btk-ps-ring {
  0%, 35%   { stroke-dashoffset: 327; }
  52%, 64%  { stroke-dashoffset: 52; }
  67%, 100% { stroke-dashoffset: 327; }
}
.ps-cap { display: block; font-size: 2.9cqw; color: var(--c-muted); }
.ps-nums { position: relative; display: block; height: 10cqw; }
.ps-nums b {
  position: absolute;
  inset-inline-start: 0;
  top: 0;
  font-size: 9.4cqw;
  font-weight: 800;
  line-height: 1;
  opacity: 0;
  animation: btk-ps-n1 15s ease-in-out infinite;
}
.ps-nums b:nth-child(2) { color: #22d3ee; animation-name: btk-ps-n2; }
.ps-nums b:nth-child(3) { color: #10b981; animation-name: btk-ps-n3; }
@keyframes btk-ps-n1 { 0%, 34% { opacity: 0; } 38%, 42% { opacity: 1; } 46%, 100% { opacity: 0; } }
@keyframes btk-ps-n2 { 0%, 43% { opacity: 0; } 47%, 50% { opacity: 1; } 54%, 100% { opacity: 0; } }
@keyframes btk-ps-n3 { 0%, 51% { opacity: 0; } 55%, 64% { opacity: 1; } 67%, 100% { opacity: 0; } }
.ps-delta {
  display: inline-flex;
  align-items: center;
  gap: 0.8cqw;
  margin-top: 0.6cqw;
  padding: 0.6cqw 1.6cqw;
  border-radius: 9999px;
  font-size: 2.5cqw;
  font-weight: 700;
  color: #10b981;
  background: color-mix(in srgb, #10b981 16%, transparent);
}

.ps-subs { display: grid; gap: 2.8cqw; }
.ps-subrow { display: grid; gap: 1.2cqw; }
/* :first-child is load-bearing. Written as `.ps-subrow > span` this also hit the .ps-bar span
   next to the label, turning the track itself into a flex container — its fill child then had
   `width: auto` against no content, so it measured zero and scaleX had nothing to scale. The
   four sub-score bars rendered as empty lines. */
.ps-subrow > span:first-child { display: flex; justify-content: space-between; font-size: 3cqw; color: var(--c-muted); }
.ps-subrow > span:first-child b { color: var(--c-text); font-weight: 700; }

/* Section captions. The score screen was one unbroken column of controls; these give it the
   two headings a real settings-style screen has, and close most of its vertical slack. */
.ps-seclabel { font-size: 3cqw; font-weight: 600; color: var(--c-muted); }

/* Four-week score history inside the score card — the same series F3 already builds for the
   real panel, shown here as the smallest possible sparkline. Static on purpose: the ring, the
   number and the four sub-scores are already moving in this scene, and one more animated thing
   would be noise rather than information. */
.ps-spark {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2cqw;
  margin-top: 3cqw;
  padding-top: 3cqw;
  border-top: 1px solid var(--ps-line);
}
.ps-spark-cap { font-size: 2.8cqw; color: var(--c-muted); white-space: nowrap; }
.ps-spark-bars { display: flex; align-items: flex-end; gap: 1.3cqw; height: 8cqw; }
.ps-spark-bars i {
  width: 3.2cqw;
  height: 100%;
  border-radius: 0.9cqw;
  background: var(--ps-track);
  transform: scaleY(var(--h));
  transform-origin: bottom;
}
.ps-spark-bars i:nth-child(1) { --h: 0.4; }
.ps-spark-bars i:nth-child(2) { --h: 0.55; }
.ps-spark-bars i:nth-child(3) { --h: 0.72; }
.ps-spark-bars i:nth-child(4) { --h: 1; background: linear-gradient(180deg, #34d399, #10b981); }

/* Weekday initials under the 7-day chart. In RTL the first item is the rightmost, so ش lands
   on the right and ج on the left — the same direction the columns already climb. */
.ps-days { display: flex; justify-content: space-between; gap: 1.4cqw; margin-top: 2cqw; font-size: 2.5cqw; color: var(--c-muted); }
.ps-days span { flex: 1; text-align: center; }
.ps-subs .ps-bar > i { animation: btk-ps-sub 15s cubic-bezier(0.22, 1, 0.36, 1) infinite; }
/* Each sub-score stops at its own value, so the four bars land at four different lengths the
   way a real score breakdown does, instead of four identical full bars. */
.ps-subs .ps-subrow:nth-child(1) .ps-bar > i { --v: 0.82; animation-delay: 0.15s; background: linear-gradient(90deg, #3b82f6, #22d3ee); }
.ps-subs .ps-subrow:nth-child(2) .ps-bar > i { --v: 0.71; animation-delay: 0.3s;  background: linear-gradient(90deg, #6366f1, #3b82f6); }
.ps-subs .ps-subrow:nth-child(3) .ps-bar > i { --v: 0.64; animation-delay: 0.45s; background: linear-gradient(90deg, #f59e0b, #fbbf24); }
.ps-subs .ps-subrow:nth-child(4) .ps-bar > i { --v: 0.9;  animation-delay: 0.6s;  background: linear-gradient(90deg, #10b981, #34d399); }
@keyframes btk-ps-sub {
  0%, 36%   { transform: scaleX(0); }
  54%, 64%  { transform: scaleX(var(--v)); }
  67%, 100% { transform: scaleX(0); }
}

.ps-find { list-style: none; margin: 0; padding: 0; display: grid; gap: 2cqw; }
.ps-find li {
  display: flex;
  align-items: center;
  gap: 2.2cqw;
  padding: 3.1cqw 3cqw;
  border-radius: 3cqw;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  font-size: 3.2cqw;
  opacity: 0;
  animation: btk-ps-item 15s ease-out infinite;
}
.ps-find li:nth-child(2) { animation-delay: 0.35s; }
.ps-find li:nth-child(3) { animation-delay: 0.7s; }
.ps-find i { width: 2cqw; height: 2cqw; flex: none; border-radius: 50%; }
@keyframes btk-ps-item {
  0%, 44%   { opacity: 0; transform: translateY(20%); }
  52%, 63%  { opacity: 1; transform: translateY(0); }
  66%, 100% { opacity: 0; transform: translateY(20%); }
}
.ps-d-red   { background: #ef4444; }
.ps-d-amber { background: #f59e0b; }
.ps-d-green { background: #10b981; }

/* ---- Scene 3: نقشهٔ راه این هفته ----------------------------------------------------- */
.ps-todo { list-style: none; margin: 0; padding: 0; display: grid; gap: 2cqw; }
.ps-todo li {
  display: flex;
  align-items: center;
  gap: 2.2cqw;
  padding: 3.2cqw 3cqw;
  border-radius: 3cqw;
  background: var(--ps-surface);
  border: 1px solid var(--ps-line);
  font-size: 3.2cqw;
}
.ps-check {
  position: relative;
  width: 4cqw;
  height: 4cqw;
  flex: none;
  border-radius: 1.2cqw;
  border: 0.6cqw solid var(--ps-track);
}
.ps-check::after {
  content: '';
  position: absolute;
  inset: -0.6cqw;
  border-radius: 1.2cqw;
  background: #10b981;
  opacity: 0;
}
/* The first task ticks itself off while the visitor watches — the one moment in the whole loop
   that shows the product being USED, not just read. */
.ps-todo li:first-child .ps-check::after { animation: btk-ps-check 15s ease-out infinite; }
.ps-todo li:first-child .ps-label { animation: btk-ps-done 15s ease-out infinite; }
@keyframes btk-ps-check {
  0%, 77%   { opacity: 0; transform: scale(0.4); }
  82%, 95%  { opacity: 1; transform: scale(1); }
  99%, 100% { opacity: 0; transform: scale(0.4); }
}
@keyframes btk-ps-done {
  0%, 79%   { opacity: 1; }
  84%, 95%  { opacity: 0.45; }
  99%, 100% { opacity: 1; }
}

.ps-chart { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.4cqw; height: 30cqw; }
/* height:100% is load-bearing, not tidiness: the columns are scaled with transform, and a flex
   child with no height is zero tall, so scaling it produced nothing at all — the chart rendered
   as an empty card. The bar's height comes from the track, the VALUE comes from scaleY. */
.ps-chart i {
  flex: 1;
  height: 100%;
  border-radius: 1cqw 1cqw 0.4cqw 0.4cqw;
  background: linear-gradient(180deg, #22d3ee, #3b82f6);
  transform: scaleY(0);
  transform-origin: bottom;
  animation: btk-ps-col 15s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
.ps-chart i:nth-child(1) { --h: 0.34; animation-delay: 0.05s; }
.ps-chart i:nth-child(2) { --h: 0.46; animation-delay: 0.12s; }
.ps-chart i:nth-child(3) { --h: 0.41; animation-delay: 0.19s; }
.ps-chart i:nth-child(4) { --h: 0.62; animation-delay: 0.26s; }
.ps-chart i:nth-child(5) { --h: 0.71; animation-delay: 0.33s; }
.ps-chart i:nth-child(6) { --h: 0.88; animation-delay: 0.4s; }
.ps-chart i:nth-child(7) { --h: 1;    animation-delay: 0.47s; background: linear-gradient(180deg, #34d399, #10b981); }
@keyframes btk-ps-col {
  0%, 69%   { transform: scaleY(0.06); }
  80%, 95%  { transform: scaleY(var(--h)); }
  99%, 100% { transform: scaleY(0.06); }
}

.ps-reelcard { display: flex; align-items: center; gap: 2.4cqw; }
.ps-reelthumb {
  position: relative;
  width: 11cqw;
  height: 11cqw;
  flex: none;
  border-radius: 2cqw;
  background: linear-gradient(140deg, #6366f1, #22d3ee);
}
.ps-reelthumb::after {
  content: '';
  position: absolute;
  inset-inline-start: 3.4cqw;
  top: 2.6cqw;
  border-style: solid;
  border-width: 1.9cqw 0 1.9cqw 3cqw;
  border-color: transparent transparent transparent #fff;
}

/* ---- Chrome: tab bar and home indicator -------------------------------------------- */
.ps-tabs {
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding-top: 2.6cqw;
  margin-top: 2.6cqw;
  border-top: 1px solid var(--ps-line);
}
.ps-tabs i {
  width: 4.2cqw;
  height: 4.2cqw;
  border-radius: 1.2cqw;
  background: var(--ps-track);
}
/* The active tab moves with the scene, so the three screens read as three places inside one
   app rather than three unrelated pictures. */
.ps-tabs i:nth-child(1) { animation: btk-ps-tab1 15s step-end infinite; }
.ps-tabs i:nth-child(2) { animation: btk-ps-tab2 15s step-end infinite; }
.ps-tabs i:nth-child(3) { animation: btk-ps-tab3 15s step-end infinite; }
@keyframes btk-ps-tab1 { 0%, 32% { background: #22d3ee; } 33%, 100% { background: var(--ps-track); } }
@keyframes btk-ps-tab2 { 0%, 32% { background: var(--ps-track); } 33%, 65% { background: #22d3ee; } 66%, 100% { background: var(--ps-track); } }
@keyframes btk-ps-tab3 { 0%, 65% { background: var(--ps-track); } 66%, 100% { background: #22d3ee; } }
.ps-home {
  width: 30cqw;
  height: 1cqw;
  margin: 2.4cqw auto 0;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--c-text) 32%, transparent);
}

/* The "Apple glass" read: one fixed specular sheet over the whole screen, so the display looks
   like it is under glass rather than printed on the front of the device. */
.ps-gloss {
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  background: linear-gradient(118deg,
    rgba(255, 255, 255, 0.13) 0%,
    rgba(255, 255, 255, 0.05) 16%,
    transparent 38%,
    transparent 100%);
}
[data-theme="light"] .ps-gloss {
  background: linear-gradient(118deg,
    rgba(255, 255, 255, 0.5) 0%,
    rgba(255, 255, 255, 0.16) 16%,
    transparent 38%,
    transparent 100%);
}

/* The reel markers in scene 1 are ::after on the tiles, so the tiles have to be the
   positioning context for them. */
.ps-grid i { position: relative; }
.ps-label { flex: 1; }
.ps-card > .ps-title { margin-bottom: 2.4cqw; }

/* The live percentage beside the scan progress bar. Same trick as the score number: three
   fixed values stacked on top of each other, each fading in at its own point on the shared
   clock, so a counter can never disagree with the bar it is labelling. */
.ps-pct { position: relative; display: inline-block; width: 8cqw; height: 3.4cqw; }
.ps-pct b {
  position: absolute;
  inset-inline-end: 0;
  top: 0;
  font-weight: 700;
  color: var(--c-text);
  opacity: 0;
  animation: btk-ps-p1 15s ease-in-out infinite;
}
.ps-pct b:nth-child(2) { animation-name: btk-ps-p2; }
.ps-pct b:nth-child(3) { animation-name: btk-ps-p3; color: #10b981; }
@keyframes btk-ps-p1 { 0%, 3% { opacity: 0; } 6%, 12% { opacity: 1; } 15%, 100% { opacity: 0; } }
@keyframes btk-ps-p2 { 0%, 13% { opacity: 0; } 16%, 21% { opacity: 1; } 24%, 100% { opacity: 0; } }
@keyframes btk-ps-p3 { 0%, 22% { opacity: 0; } 25%, 30% { opacity: 1; } 33%, 100% { opacity: 0; } }

/* ---- The three proof points under the desktop CTAs --------------------------------
   These were three lines of small grey text with green ticks beside them — the owner's call
   was that it read as a leftover checklist, not as design. One glass bar with hairline
   dividers makes the same three facts look deliberate, and matches the glass cards already
   orbiting the device rather than introducing a fourth surface treatment. */
.hero-points {
  display: inline-flex;
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: 16px;
  overflow: hidden;
  background: color-mix(in srgb, var(--c-surface) 50%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  border: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--c-text) 8%, transparent);
}
.hero-points li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.05rem;
  font-size: 12.5px;
  white-space: nowrap;
}
/* In RTL, inline-start is the right edge, so this draws the divider between each item and the
   one before it — which is what a physical border-left would get backwards here. */
.hero-points li + li {
  border-inline-start: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
}
.hero-points svg { flex: none; color: #22d3ee; }

/* Nobody is looking at the hero once it has scrolled away — a paused loop costs the
   compositor nothing, and this is the difference between a landing page that drains a
   phone battery while the visitor reads the pricing table and one that doesn't. */
.hero-stage.is-offscreen .pscreen *,
.hero-stage.is-offscreen .hero-card {
  animation-play-state: paused;
}

/* Header: flat and transparent at the very top of the page (blends with the hero), then
   on scroll gains a genuine layered-glass panel and pulls in from the screen edges into a
   rounded "boxed" bar — see #site-header's scroll listener near the bottom of front-page.php,
   which toggles .is-scrolled.

   IMPORTANT (real bug fixed here, not just style): the first version of this transitioned
   `max-width: none -> 960px`, `background: transparent -> a gradient`, `backdrop-filter:
   none -> blur(...)`, and `margin: 0 -> auto`. None of those pairs are actually interpolable
   by the CSS transitions spec (`none`/`auto`/a plain color -> a gradient aren't numeric value
   types), so every one of them silently snapped instead of animating — the glass/shrink
   effect looked instant no matter what duration was set. Fixed by: (1) using `width` with two
   real length values instead of `max-width: none -> <length>`; (2) moving the
   background/blur/shadow into a `::before` layer that is *always* fully "on" and only fades
   via `opacity` (0 -> 1 is properly interpolable); (3) centering via `left:50% + transform`
   instead of `margin: auto`, so nothing here ever depends on an auto value transitioning. */
/* Owner's call (2026-09-15): the header keeps ONE shape. It used to start edge-to-edge and
   transparent, then animate into this boxed glass bar on scroll — but that transition animated
   `width` and `margin-top`, i.e. layout, on a fixed element whose glass layer carries
   `backdrop-filter: blur(22px)`. Every frame of it forced the blurred backdrop behind the header
   to be recomputed, which is one of the most expensive things a phone browser can be asked to do
   while it is also scrolling. Folding the scrolled values in as the permanent state removes the
   transition, the scroll listener and the repeated backdrop work in one go, and the owner
   preferred the boxed look at the top of the page anyway. */
.header-glass {
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  width: min(960px, calc(100% - 24px));
  margin-top: 10px;
  border-radius: 20px;
}
/* The bar keeps the compact height the scrolled state used to animate to (72px -> 58px). */
#header-row {
  height: 58px;
}
.header-glass::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: linear-gradient(180deg, color-mix(in srgb, var(--c-surface) 70%, transparent), color-mix(in srgb, var(--c-bg) 55%, transparent));
  backdrop-filter: blur(22px) saturate(160%);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--c-text) 8%, transparent),
    0 1px 0 rgba(34, 211, 238, 0.12),
    0 20px 40px -30px rgba(0, 0, 0, 0.35);
  opacity: 1;
}
/* Mobile used to reset back to a plain edge-to-edge bar here — the owner asked for the same
   inset/boxed feel as desktop instead ("یه ذره حالت مربع وسط صفحه داشته باشه"), which turns
   out to need no separate rule at all: `width: min(960px, calc(100% - 24px))` above already
   resolves to `calc(100% - 24px)` (a comfortable 12px inset each side) on any phone-width
   viewport, since 960px is never the smaller operand there. Removing the old override was
   the whole fix. */

/* No box around the header nav links — owner's call (2026-08-27): the bordered grey pill
   "made it ugly". The links now float in the header bar, in both the top and scrolled states;
   their hover state is the only chrome. Spacing between them comes from the markup (gap + the
   per-link padding). */
.nav-pill {
  background: transparent;
  border: 0;
  padding: 0;
}
.nav-pill a {
  border-radius: 8px;
  transition: color 0.25s ease, background-color 0.25s ease;
}
.nav-pill a:hover {
  background: rgba(34, 211, 238, 0.12);
  color: #22d3ee;
}

.btn-gradient {
  background-image: linear-gradient(95deg, #3b82f6, #22d3ee, #3b82f6);
  background-size: 220% 100%;
  background-position: 0% 0%;
  box-shadow: 0 0 24px -6px rgba(34, 211, 238, 0.6);
  transition: background-position 0.5s ease, transform 0.25s ease, box-shadow 0.25s ease;
  /* NOTE (2026-09-15): a `translate: 0 1.5px` used to sit here, meant to correct Morabba's
     Persian ink sitting high inside the button. It was wrong and was removed: `translate` on
     the button moves the whole element — box, border, background AND text together — so the
     text never moved relative to its own button, while the button itself ended up 1.5px lower
     than everything aligned beside it. That misalignment is what the owner then reported
     seeing on iPhone. Measured in real WebKit at 8x zoom, the residual ink offset is ~1px at
     14px text — below the threshold worth a hack, and any real fix has to shift the text
     inside the box (padding/line-height), never the box itself. */
}
.btn-gradient:hover {
  background-position: 100% 0%;
  transform: translateY(-2px) scale(1.02);
  box-shadow: 0 0 32px -4px rgba(34, 211, 238, 0.85);
}
.btn-gradient:active {
  transform: translateY(0) scale(0.98);
}

/* Ghost buttons (theme toggle, ورود, hamburger) carry NO resting border/box — owner's call
   (2026-08-27): the grey outline around the cluster of header buttons "made it ugly". They
   sit as bare icons/text separated by whitespace, in both the top and scrolled header states.
   The border is kept declared as transparent so the cyan hover outline still transitions in
   smoothly rather than snapping. */
.btn-ghost {
  border: 1px solid transparent;
  transition: border-color 0.25s ease, background-color 0.25s ease, color 0.25s ease;
  /* (Removed 2026-09-15 along with .btn-gradient's — see the note in that rule.) */
}
.btn-ghost:hover {
  border-color: rgba(34, 211, 238, 0.5);
  background: rgba(34, 211, 238, 0.08);
  color: #22d3ee;
}

/* Mobile nav — a full-screen glass overlay (not a plain dropdown list), starting below
   the fixed header so the header (and its now-"close" toggle button) stays reachable on
   top of it. Links fade/slide in with a per-item stagger via the --d custom property. */
/* REAL BUG FIXED HERE: this used to be `top: 72px` — a hardcoded number matching the
   header's original fixed height. Once the header gained a scroll-condense state (72px ->
   58px, see .header-glass above), opening the menu while scrolled left a real ~14px gap
   between the (now shorter) header and where this overlay's background started, through
   which the hero content behind it showed — exactly the "menu looks broken, content bleeds
   through" the owner reported. Fixed structurally rather than chasing the exact number: this
   now covers the full viewport (`top: 0`) and simply relies on the header's own higher
   z-index (50 vs. this panel's 45) to stay visually on top of it, so no height figure here
   ever needs to track the header's again. Also switched from color-mix()-based translucency
   to a plain, fully solid background — this is the one full-screen panel on the page where
   there's zero reason to allow ANY bleed-through, intentional or not. */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 45;
  display: flex;
  flex-direction: column;
  background: var(--c-bg);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0s linear 0.3s;
}
.mobile-menu.is-open {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.3s ease, visibility 0s;
}
.mobile-menu-close-row {
  display: flex;
  height: 72px;
  flex-shrink: 0;
  align-items: center;
  justify-content: flex-end;
  padding: 0 20px;
}
.mobile-menu-links {
  display: flex;
  flex: 1;
  width: 100%;
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  gap: 8px;
  padding: 0 28px 40px;
}
.mobile-menu-link {
  display: flex;
  min-height: 56px;
  align-items: center;
  gap: 14px;
  border-radius: 12px;
  border: 1px solid transparent;
  padding: 0 18px;
  font-size: 18px;
  font-weight: 700;
  color: var(--c-text);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.4s ease, transform 0.4s ease, background-color 0.2s ease,
    border-color 0.2s ease, color 0.2s ease;
  transition-delay: calc(var(--d, 0) * 55ms);
  /* (Removed 2026-09-15 along with .btn-gradient's — see the note in that rule.) */
}
.mobile-menu.is-open .mobile-menu-link {
  opacity: 1;
  transform: translateY(0);
}
.mobile-menu-link:not(.btn-gradient) {
  background: color-mix(in srgb, var(--c-text) 4%, transparent);
  border-color: color-mix(in srgb, var(--c-text) 8%, transparent);
}
.mobile-menu-link:hover,
.mobile-menu-link:focus-visible {
  border-color: rgba(34, 211, 238, 0.4);
  color: #22d3ee;
}
.mobile-menu-link .material-symbols-outlined {
  font-size: 20px;
  opacity: 0.8;
}
#mobile-menu-toggle .material-symbols-outlined {
  transition: transform 0.3s ease;
}
#mobile-menu-toggle[aria-expanded="true"] .material-symbols-outlined {
  transform: rotate(90deg);
}

/* Hero phone mockup shaped like an actual device (Dynamic-Island notch + side buttons),
   not a generic rounded card, per reference. Side buttons are physical left/right —
   deliberately NOT logical inset-inline-*, because a device silhouette shouldn't mirror
   with page text direction. */
.iphone-frame {
  position: relative;
  width: 100%;
  /* iPhone Pro Max silhouette: a slim uniform titanium bezel (the padding IS the bezel), a
     metal edge band (gradient + bright inset hairline), a thin black inner ring, and a
     floating Dynamic Island. aspect-ratio tuned so the inner screen matches the 720×1538
     hero screenshots with no crop. Every length here is `cqw` (see .hero-phone) so the
     whole device scales as one piece from the desktop cluster down to the mobile one. */
  /* Proportions taken from the real device, not eyeballed (owner: "must be an exact iPhone
     17 Pro Max mockup"). Body 78.0 x 163.4 mm -> 780/1634. Corner radius ~14.5% of the body
     width. Bezel on a Pro Max is a hair over 1.5% of the width and uniform on all four sides —
     the old 17.5cqw radius + 1.7cqw pad read as a generic rounded slab, which is what made the
     previous mockups look "not like a phone". */
  aspect-ratio: 780 / 1634;
  border-radius: 14.5cqw;
  padding: 1.55cqw;
  /* Titanium rail: a cold metal gradient across the band, a bright specular hairline on the
     lit edge, then a black inner ring where the glass meets the frame. */
  background: linear-gradient(148deg, #6d6d73 0%, #34343a 15%, #17171a 50%, #2a2a30 80%, #4a4a51 100%);
  box-shadow:
    inset 0 0 0 0.9px rgba(255, 255, 255, 0.28),
    inset 0 0 0 2.4px #050506,
    0 12px 24px -12px rgba(2, 6, 18, 0.9),
    0 48px 110px -34px rgba(34, 211, 238, 0.45);
}
/* Dynamic Island — a slim black pill centred in the screenshot's own (empty-centre) status
   bar. Sized/positioned in cqw so it stays a small pill at every phone size instead of
   ballooning into a blob that covers the time and battery, which is what the old fixed
   24px / 80px-min-width values did on mobile. */
.iphone-frame::before {
  content: '';
  position: absolute;
  /* The real island is a wide pill, ~28% of the body width and ~7.6% tall — not the thin slit
     the old 25 x 4.3cqw values drew. The camera dot inside it is what sells it at this size. */
  top: 3.2cqw;
  left: 50%;
  transform: translateX(-50%);
  width: 27.5cqw;
  height: 7.4cqw;
  /* The camera is a small dark lens INSIDE the pill, drawn as one background-gradient stop.
     The first version drew it with a spread box-shadow, which bled a grey wedge out past the
     pill's right end instead of staying a dot — the owner caught it on the mockup. Background
     percentages are physical, not logical, so 79% is the right-hand side on an RTL page too,
     which is where the real lens sits. */
  /* The radius has to be an explicit length. Written as bare percentages the gradient sized
     itself to farthest-corner and painted a grey wedge across the whole right half of the pill
     instead of a lens — the owner caught exactly that on the v3 mockup. */
  background:
    radial-gradient(circle 1.35cqw at 79% 50%, #232E47 0 62%, #0A0D14 66%, transparent 70%),
    #000;
  border-radius: 9999px;
  z-index: 8;
}
.iphone-screen {
  position: relative;
  width: 100%;
  height: 100%;
  /* Outer body radius (14.5cqw) minus the bezel (1.55cqw) — so the glass follows the frame
     instead of sitting inside it as a differently-shaped rectangle. */
  border-radius: 12.95cqw;
  /* clip-path (not overflow:hidden + border-radius): overflow clipping is unreliable under a
     transformed ancestor, and the whole hero stage is transformed. clip-path is honoured. */
  clip-path: inset(0 round 12.95cqw);
  transform: translateZ(0);
  isolation: isolate;
  background: #0A0F1E;
}
/* Physical side buttons — positioned as % so they stay in the right place on all 3 phone
   sizes (px would drift on the smaller back phones). Left: mute switch, volume up, volume
   down. Right: the side/power button. They protrude ~2px past the steel band. */
.iphone-side-btn {
  position: absolute;
  background: linear-gradient(90deg, #101012, #37373b 60%, #26262a);
  border-radius: 2px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.45);
}
.iphone-btn-power { background: linear-gradient(-90deg, #101012, #37373b 60%, #26262a); }
.iphone-btn-mute      { width: 3px; height: 2%;   left: -2px;  top: 8.5%;  }
.iphone-btn-vol-up    { width: 3px; height: 4.6%; left: -2px;  top: 13%;   }
.iphone-btn-vol-down  { width: 3px; height: 4.6%; left: -2px;  top: 18.5%; }
.iphone-btn-power     { width: 3px; height: 7.4%; right: -2px; top: 13%;   }

/* Problem->Solution connector: a pulsing halo behind the arrow badge plus a marching
   dashed line, so the "this becomes that" relationship reads as an active process. */
.connector-pulse::before {
  content: '';
  position: absolute;
  inset: -10px;
  border-radius: 9999px;
  background: radial-gradient(circle, rgba(34, 211, 238, 0.35), transparent 70%);
  animation: btk-pulse-ring 2.4s ease-in-out infinite;
}
@keyframes btk-pulse-ring {
  0%, 100% { transform: scale(0.85); opacity: 0.6; }
  50% { transform: scale(1.25); opacity: 1; }
}
.connector-pulse .material-symbols-outlined {
  animation: btk-arrow-nudge 1.6s ease-in-out infinite;
}
@keyframes btk-arrow-nudge {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(-5px); }
}
/* Icon tiles used across steps/features/demo cards: distinct gradient per card via the
   --tile accent custom property, with a glow + scale + tilt on hover so the grid feels
   less like one uniform stamped-out pattern. */
.icon-tile {
  --tile: #3b82f6;
  background: linear-gradient(150deg, color-mix(in srgb, var(--tile) 22%, transparent), color-mix(in srgb, var(--tile) 6%, transparent));
  border: 1px solid color-mix(in srgb, var(--tile) 35%, transparent);
  color: var(--tile);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
@media (hover: hover) {
  .group:hover .icon-tile {
    transform: scale(1.12) rotate(-6deg);
    box-shadow: 0 0 24px -4px var(--tile);
  }
}

/* "How it works" step nodes: dimmed by default, lighting up individually in sequence
   as each one scrolls into view (toggled via .is-active — see script), rather than all
   four rendering in their final bright state with nothing to notice while scrolling. */
.step-node {
  transition: background-color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease, color 0.35s ease;
  /* (Removed 2026-09-15 along with .btn-gradient's — see the note in that rule. This one was
     the worst of the set: .step-node draws its own circle, so translating it pushed the whole
     circle off the connecting line it sits on.) */
}
/* .step-node-dimmed is only added by script right before it starts managing the
   scroll-triggered light-up — so if that script never runs (GSAP CDN failure), nodes
   just render in their normal lit state instead of being stuck permanently dim. */
.step-node.step-node-dimmed:not(.is-active) {
  background: color-mix(in srgb, var(--c-text) 4%, transparent);
  border-color: color-mix(in srgb, var(--c-text) 10%, transparent);
  color: color-mix(in srgb, var(--c-text) 40%, transparent);
  box-shadow: none;
}
.step-node.is-active {
  box-shadow: 0 0 22px -2px var(--tile);
}

/* Compact horizontal footer link row (mobile footer only). */
.footer-pill {
  display: inline-flex;
  min-height: 36px;
  align-items: center;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  padding: 0 14px;
  font-size: 12px;
  color: var(--c-muted);
  transition: border-color 0.2s ease, color 0.2s ease;
}
.footer-pill:hover,
.footer-pill:focus-visible {
  border-color: rgba(34, 211, 238, 0.4);
  color: #22d3ee;
}

/* Pricing cards: a diagonal light sweep on hover, on top of the shared hover-lift. */
/* The pricing cards moved to .plan-card (see the pricing block at the end of this file)
   when the section was rebuilt on 2026-10-02; the shimmer motif moved with them. */

/* JS-driven FAQ accordion panel (see script at bottom of front-page.php). No default
   height:0 here on purpose — if JS never runs, answers stay visible/readable rather
   than being locked shut; the init script collapses them via inline style once it runs. */
.faq-panel {
  overflow: hidden;
}

/* The three blurred .orb divs the hero used to carry were removed 2026-10-01: each one was a
   ~400px element under a 90px blur repainting on its own timer, and the new .btk-bg-aurora
   does the same job once, for the whole site, as a single composited layer. */
.bg-grid-fade {
  background-image:
    linear-gradient(color-mix(in srgb, var(--c-text) 4%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--c-text) 4%, transparent) 1px, transparent 1px);
  background-size: 48px 48px;
  -webkit-mask-image: radial-gradient(ellipse 60% 60% at 50% 20%, black, transparent);
  mask-image: radial-gradient(ellipse 60% 60% at 50% 20%, black, transparent);
}

/* =====================================================================================
   Story tray (the strip under the hero) — added 2026-10-01.

   Replaces the "trusted by top brands" marquee of five invented geometric logos. Two reasons,
   and the first one is not a design reason: we have no such brands, so the line was a claim we
   cannot stand behind on a page that also carries an Enamad seal. What replaced it answers the
   question a visitor actually has three seconds after the hero — "is this for a page like
   mine?" — and it answers it in Instagram's own visual language: a row of story rings.

   The rail is ONE element carrying the animation with two identical tracks inside it, and it
   translates exactly -50%, which is exactly one track width, so the loop is seamless by
   construction rather than by tuning a number until it looks right.
   ===================================================================================== */
/* `direction: ltr` has to sit on the VIEWPORT, not on the rail. An over-wide child is anchored
   by its parent's direction, so inside an RTL section the rail hung off to the LEFT with its
   right edge pinned to the container's right edge — and translating it leftwards then emptied
   the right-hand side, which is exactly the gap the owner saw. (Putting `direction: ltr` on the
   rail itself does nothing about this: it governs the rail's own children, not where the rail
   is anchored.) Anchored from the left instead, leftward motion always has strip behind it. */
.story-viewport { direction: ltr; }
.story-rail {
  display: flex;
  width: max-content;
  animation: btk-story-scroll 60s linear infinite;
}
/* FOUR copies of the set, and the loop translates exactly -25% — one copy. The rail is only
   ever gapless if (copies x set width) >= viewport + set width; with two copies the set was
   990px against a 1440px viewport, so roughly 450px of the strip ran empty on every pass. Four
   copies hold a gapless strip out to a ~2970px viewport. The translate is a percentage of the
   rail's own width, so this stays exact at any breakpoint even though the items resize. */
@keyframes btk-story-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-25%); }
}
.story-track { display: flex; gap: 26px; padding-inline-end: 26px; }
/* Stop the rail while someone is reading it. A strip that keeps sliding out from under the
   label you are trying to read is the one real usability failure of this pattern. */
@media (hover: hover) {
  .story-strip:hover .story-rail,
  .story-strip:focus-within .story-rail { animation-play-state: paused; }
}

.story {
  direction: rtl;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 84px;
  flex: none;
  transition: transform 0.25s cubic-bezier(0.22, 1, 0.36, 1);
}
.story-ring {
  position: relative;
  width: 68px;
  height: 68px;
  border-radius: 50%;
}
/* The gradient is on a pseudo-element, not on .story-ring itself, so the hover spin turns the
   ring without also turning the icon inside it. */
.story-ring::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: conic-gradient(from 210deg, #22d3ee, #3b82f6, #a855f7, #f59e0b, #22d3ee);
}
/* ring -> gap -> disc -> icon, the same three layers Instagram's own unread story ring has. */
.story-face {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--c-bg);
  display: grid;
  place-items: center;
}
.story-face::before {
  content: '';
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background: var(--c-surface);
}
.story-face svg { position: relative; z-index: 1; color: #22d3ee; }
.story-label {
  font-size: 12.5px;
  line-height: 1.4;
  text-align: center;
  color: var(--c-muted);
}
@media (hover: hover) {
  .story:hover { transform: translateY(-5px); }
  .story:hover .story-ring::before { animation: btk-story-spin 1.1s cubic-bezier(0.4, 0, 0.2, 1); }
  .story:hover .story-face svg { color: #34d399; }
}
@keyframes btk-story-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@media (max-width: 767px) {
  .story { width: 74px; gap: 8px; }
  .story-ring { width: 60px; height: 60px; }
  .story-track { gap: 18px; padding-inline-end: 18px; }
  .story-label { font-size: 11.5px; }
}
/* With motion off the rail cannot scroll, and a non-scrolling rail would simply cut half the
   categories off the side of the page. It becomes a centred, wrapping row instead, and the
   duplicate track (which exists only to make the loop seamless) is dropped. */
@media (prefers-reduced-motion: reduce) {
  .story-rail { animation: none; width: 100%; flex-wrap: wrap; justify-content: center; }
  .story-track { flex-wrap: wrap; justify-content: center; row-gap: 22px; padding-inline-end: 0; }
  .story-track-dup { display: none; }
  .story { transition: none; }
}

.marquee-track {
  animation: btk-marquee 14s linear infinite;
}
@keyframes btk-marquee {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}
/* Marks fade in/out at the strip's own edges instead of hard-cutting off mid-shape as they
   scroll past — a cheap CSS mask does this without any JS "show 4-5, hide, show next" logic. */
.marquee-fade-edges {
  -webkit-mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 8%, black 92%, transparent);
}

/* FAQ disclosure affordance: rotating chevron driven by the JS-toggled aria-expanded
   state (see .js-faq-toggle in front-page.php) rather than native <details>, so the
   panel can animate open/close smoothly instead of snapping. */
.chevron {
  transition: transform 0.25s ease;
}
.js-faq-toggle[aria-expanded="true"] .chevron {
  transform: rotate(180deg);
}
/* The two rules that used to live here tinted the FAQ's card border and its leading
   help-icon on open. The FAQ has had neither a card nor an icon since 2026-10-02 — see the
   .qa block at the end of this file, which carries the open state on the answer instead. */

/* ---- Dedicated mobile pass (Phase 08 Task 3, round 2) ---- */

/* Persistent bottom CTA — a mobile-only pattern desktop doesn't need, since desktop
   always has the header CTA in view. Hidden until JS reveals it past the hero (see
   #mobile-sticky-cta in front-page.php); starts translated off-screen so it never
   flashes visible before JS runs. */
.mobile-sticky-cta {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 40;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, color-mix(in srgb, var(--c-bg) 0%, transparent), color-mix(in srgb, var(--c-bg) 85%, transparent) 35%, color-mix(in srgb, var(--c-bg) 97%, transparent));
  transform: translateY(100%);
  transition: transform 0.3s ease;
}
.mobile-sticky-cta.is-visible {
  transform: translateY(0);
}

/* Horizontal snap carousels (features / pricing on mobile) — CSS-only swipe, no JS
   library. flex-basis < 100% deliberately leaves the next card peeking in, so a static
   screenshot still reads "there's more, swipe" without needing the dots to be seen. */
.scroll-snap-row {
  display: flex;
  gap: 12px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 4px 4px 8px;
}
.scroll-snap-row::-webkit-scrollbar {
  display: none;
}
.snap-card {
  scroll-snap-align: center;
  flex: 0 0 86%;
}
.snap-card-wide {
  scroll-snap-align: center;
  flex: 0 0 88%;
}
.scroll-dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 4px;
}
.scroll-dot {
  height: 6px;
  width: 6px;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--c-text) 18%, transparent);
  transition: background-color 0.25s ease, width 0.25s ease;
}
.scroll-dot.is-active {
  width: 20px;
  background: #22d3ee;
}


@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .float, .marquee-track,
  .connector-pulse::before, .connector-pulse .material-symbols-outlined,
  .btk-bg-aurora, .btk-bg-pulse, .btk-bg-tile, .hero-card,
  .ps-sweep, .ps-nums b, .ps-find li, .ps-scene, .ps-bar > i,
  .ps-chart i, .ps-tabs i, .ps-check::after, .ps-label {
    animation: none !important;
  }
  /* A reduced-motion visitor must still see the hero's POINT, not an empty phone. The loop is
     switched off at the ONE scene that carries the whole message — the score screen, at its
     finished state — rather than at frame 0, which is the "0%" of every keyframe above and
     would leave the screen blank. */
  .ps-scene { opacity: 0 !important; transform: none !important; }
  .ps-scene-2 { opacity: 1 !important; }
  .ps-ring-fg { animation: none !important; stroke-dashoffset: 52; }
  .ps-nums b:nth-child(3), .ps-find li { opacity: 1 !important; transform: none !important; }
  .ps-nums b:nth-child(1), .ps-nums b:nth-child(2) { opacity: 0 !important; }
  .ps-subs .ps-bar > i { transform: scaleX(var(--v)) !important; }
  .ps-tabs i:nth-child(2) { background: #22d3ee; }
  .hero-card { opacity: 1 !important; transform: none !important; }
  .ps-sweep { opacity: 0; }
  .plan-card::after { display: none; }
  .mobile-sticky-cta { transition: none; }
  .mobile-menu-link { transition-delay: 0s !important; }
  .step-node { transition: none; }
}



/* =====================================================================================
   "چرا اکثر پیج‌ها رشد نمی‌کنند؟" — rebuilt 2026-10-01.

   It used to be two cards of bullet text: five problems on one side, five solutions on the
   other. The owner's call was that a list of sentences never actually shows what the product
   does, and that this section should work the way the hero does — a thing happening, in glass.

   The idea the whole section turns on: the difference we sell is not "more followers", it is
   that you can SEE. So both sides render the EXACT SAME five views of a page, and the only
   difference between them is that the "بدون بترکونش" side is behind frosted glass — blurred,
   desaturated, with a question mark where the answer would be — while the "با بترکونش" side is
   sharp and annotated. One component, two states. That is honest (we are not promising
   results, we are promising sight), it is cheap (five views, not ten), and it reads in about a
   second with no text at all.

   Five beats run on one shared 20s clock, the same single-timeline technique as the hero, so
   the caption, both cards, the tags and the progress dots can never drift apart. Clicking a dot
   pins a beat: `.is-manual` kills every animation and `[data-beat]` decides what shows.
   ===================================================================================== */
/* ---- Scroll-driven mode ------------------------------------------------------------
   Added 2026-10-01 at the owner's request: scrolling through the section should advance the
   five beats, rather than a timer advancing them whether or not anyone is watching. With a
   timer, a visitor who scrolls quickly could see one beat out of five.

   `position: sticky` over a tall spacer — deliberately NOT wheel interception. The page never
   fights the visitor: a flick still moves at the speed they flicked. Both classes below are
   added by script, so the section is an ordinary block for anyone whose JS did not run.

   Five beats share the travel, so the spacer is one screen plus five steps. The step is what
   sets the pace: at 70vh a beat takes a comfortable half-flick on a trackpad. */
.vs-scroller { --vs-step: 70vh; }
.vs-scroller.is-pinned { height: calc(100vh + 5 * var(--vs-step)); }
.vs-scroller.is-pinned .vs-sticky {
  position: sticky;
  top: 0;
  display: flex;
  flex-direction: column;
  /* Third and final answer to the gap above this section. The history is worth keeping because
     two reasonable-looking options are both wrong:
       - centring inside the full 100vh puts the heading underneath the fixed header;
       - centring inside a box that reserves the header still leaves HALF the slack above the
         heading, and the content is much shorter than a screen, so that half is ~150px. That
         is the band the owner kept seeing.
     The content is top-aligned instead, offset by just enough to clear the header. The gap
     above is then exactly that offset and nothing more, and the leftover sits below the
     progress bars where the next section is about to arrive anyway. */
  /* Centred with NO header offset of its own. The offset and the centring slack were stacking:
     with both, the distance from the top of the screen to the heading measured 213px. The
     centring alone already leaves more than the header's height above the content, and the fit
     guard below demands at least 150px of slack so that stays true on the shortest screen that
     still pins. Result, measured: the space above the heading and the space after the progress
     bars are the same size, and each is about what the rest of this page already puts between
     two sections. */
  justify-content: center;
  min-height: 100vh;
}
/* The inner container's own py-12/py-20 would stack on top of that offset and double the gap,
   so it is dropped once pinned — the offset above is the whole spacing budget. It also becomes
   a flex column that fills the screen, purely so the progress bars can be pushed to the bottom
   edge (below). */
.vs-scroller.is-pinned .vs-sticky > * {
  padding-top: 0;
  padding-bottom: 0;
}
/* Deliberately NOT stretched to fill the screen. That was tried and it is worse: with the cards
   forced to the full height, a 150px visual floated in the middle of a 530px card and both
   panels read as empty. The cards keep their natural height and the leftover is split above and
   below the block instead — ordinary page whitespace, which the eye does not read as a gap the
   way a tail hanging off the end of a section does. */
/* Load-bearing. The inner container carries `mx-auto`, and auto inline margins on a flex item
   cancel `align-self: stretch` — the item shrinks to its content and then centres. Pinning the
   section therefore collapsed the two cards from full width to about 540px. */
.vs-scroller.is-pinned .vs-sticky > * { width: 100%; }
@media (max-width: 899px) {
  /* Shorter steps on a phone: the same five beats over less thumb travel, because a flick
     covers far less of the page than a trackpad swipe does. */
  .vs-scroller { --vs-step: 50vh; }
}

.vs-stage { position: relative; }

.vs-cards {
  display: grid;
  gap: 18px;
  align-items: stretch;
}
@media (min-width: 900px) {
  /* The two cards and the seam between them. Below 900px they stack, good side FIRST on
     desktop (RTL: rightmost is the problem, so the eye meets the problem, then the answer). */
  .vs-cards { grid-template-columns: 1fr auto 1fr; gap: 0; }
}

.vs-card {
  position: relative;
  overflow: hidden;
  border-radius: 22px;
  padding: 22px 20px 20px;
  background: color-mix(in srgb, var(--c-surface) 58%, transparent);
  -webkit-backdrop-filter: blur(22px) saturate(150%);
  backdrop-filter: blur(22px) saturate(150%);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
  box-shadow:
    0 30px 60px -40px rgba(2, 6, 18, 0.9),
    inset 0 1px 0 color-mix(in srgb, var(--c-text) 10%, transparent);
}
/* A hairline of the side's own colour along the top edge — the only place either card is
   allowed to shout. Everything else stays the same neutral glass. */
.vs-card::before {
  content: '';
  position: absolute;
  inset-inline: 18%;
  top: 0;
  height: 2px;
  border-radius: 9999px;
}
.vs-card-bad::before  { background: linear-gradient(90deg, transparent, #ef4444, transparent); }
.vs-card-good::before { background: linear-gradient(90deg, transparent, #10b981, transparent); }
@media (min-width: 900px) {
  .vs-card-bad  { border-start-start-radius: 22px; border-end-start-radius: 22px; }
}

.vs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 16px;
}
.vs-title { font-weight: 700; font-size: 15px; }
.vs-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 9999px;
  font-size: 11.5px;
  font-weight: 700;
}
.vs-chip-bad  { color: #ef4444; background: color-mix(in srgb, #ef4444 15%, transparent); }
.vs-chip-good { color: #10b981; background: color-mix(in srgb, #10b981 15%, transparent); }

/* The seam. A thin vertical rule with the beat's own arrow sitting on it, so the two cards
   read as one comparison rather than two unrelated panels. */
.vs-seam { position: relative; display: none; width: 78px; }
@media (min-width: 900px) { .vs-seam { display: block; } }
.vs-seam::before {
  content: '';
  position: absolute;
  inset-block: 14%;
  inset-inline-start: 50%;
  width: 1px;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--c-text) 14%, transparent), transparent);
}
.vs-arrow {
  position: absolute;
  top: 50%;
  inset-inline-start: 50%;
  transform: translate(50%, -50%);
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  color: #22d3ee;
  background: var(--c-bg);
  border: 1px solid color-mix(in srgb, #22d3ee 35%, transparent);
  box-shadow: 0 0 26px -6px rgba(34, 211, 238, 0.55);
}
.vs-arrow::after {
  content: '';
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 1px solid color-mix(in srgb, #22d3ee 30%, transparent);
  animation: btk-vs-ping 2.6s ease-out infinite;
}
@keyframes btk-vs-ping {
  0%       { transform: scale(0.85); opacity: 0.8; }
  70%, 100%{ transform: scale(1.35); opacity: 0; }
}

/* ---- The view stack: five beats, absolutely stacked, one clock ---------------------- */
/* overflow:hidden is a real guard, not tidiness: a beat taller than the stage used to spill
   over the card header above it and the tag below it. Each beat is sized to fit, and this
   stops the next one that is not from silently wrecking the card. */
/* Tied to the viewport, not fixed. A fixed 300px filled a 900px screen nicely and then made
   the whole section too tall to pin on a 700px laptop or a 640px phone, which silently sent
   those back to the timer — the exact regression the guard exists to prevent. clamp() lets
   the stage shrink with the screen so short viewports still qualify. */
.vs-view { position: relative; height: clamp(190px, 30vh, 300px); overflow: hidden; }
.vs-beat {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 12px;
  opacity: 0;
  animation: btk-vs-b1 20s ease-in-out infinite;
}
.vs-beat-2 { animation-name: btk-vs-b2; }
.vs-beat-3 { animation-name: btk-vs-b3; }
.vs-beat-4 { animation-name: btk-vs-b4; }
.vs-beat-5 { animation-name: btk-vs-b5; }
/* Each beat owns 20% of the loop; the 2% overlap at each end is the crossfade. */
@keyframes btk-vs-b1 { 0%,1%{opacity:0;transform:translateY(8px)} 4%,17%{opacity:1;transform:none} 20%,100%{opacity:0;transform:translateY(-8px)} }
@keyframes btk-vs-b2 { 0%,20%{opacity:0;transform:translateY(8px)} 24%,37%{opacity:1;transform:none} 40%,100%{opacity:0;transform:translateY(-8px)} }
@keyframes btk-vs-b3 { 0%,40%{opacity:0;transform:translateY(8px)} 44%,57%{opacity:1;transform:none} 60%,100%{opacity:0;transform:translateY(-8px)} }
@keyframes btk-vs-b4 { 0%,60%{opacity:0;transform:translateY(8px)} 64%,77%{opacity:1;transform:none} 80%,100%{opacity:0;transform:translateY(-8px)} }
@keyframes btk-vs-b5 { 0%,80%{opacity:0;transform:translateY(8px)} 84%,97%{opacity:1;transform:none} 99%,100%{opacity:0;transform:translateY(-8px)} }

/* THE WHOLE POINT: the problem side is the same view, behind glass you cannot read through. */
.vs-viewwrap { position: relative; }
.vs-card-bad .vs-view {
  filter: grayscale(0.95) blur(2.6px) opacity(0.5);
}
.vs-card-bad .vs-note,
.vs-card-bad .vs-mark { display: none; }
/* Sits OUTSIDE .vs-view on purpose — see the note in front-page.php: `filter` applies to the
   whole subtree, so a "؟" inside the blurred element came out blurred as well. */
.vs-card-bad .vs-q { display: grid; }
.vs-q {
  display: none;
  position: absolute;
  inset: 0;
  place-items: center;
  z-index: 2;
  font-size: 52px;
  font-weight: 800;
  color: color-mix(in srgb, var(--c-text) 40%, transparent);
  text-shadow: 0 0 30px color-mix(in srgb, var(--c-bg) 90%, transparent);
}

/* ---- Beat pieces, shared by both sides ---------------------------------------------- */
.vs-line { width: 100%; height: 96px; overflow: visible; }
.vs-line-track { fill: none; stroke: color-mix(in srgb, var(--c-text) 12%, transparent); stroke-width: 2; }
.vs-line-path {
  fill: none;
  stroke: url(#btkVsGrad);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 300;
  stroke-dashoffset: 300;
  animation: btk-vs-draw 20s ease-out infinite;
}
@keyframes btk-vs-draw { 0%,2%{stroke-dashoffset:300} 14%,20%{stroke-dashoffset:0} 22%,100%{stroke-dashoffset:300} }
.vs-notes { display: flex; justify-content: space-around; gap: 8px; }
.vs-note {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 9999px;
  font-size: 11px;
  font-weight: 600;
  color: var(--c-text);
  background: color-mix(in srgb, var(--c-text) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
}
.vs-note i { width: 7px; height: 7px; border-radius: 50%; flex: none; }

/* Capped, not fluid: the tiles are square, so at full card width a 2x3 grid resolved to
   ~218px against a 196px stage and overflowed. 272px keeps two rows of ~85px inside it. */
.vs-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; width: 100%; max-width: 310px; margin-inline: auto; }
.vs-tile {
  position: relative;
  aspect-ratio: 1;
  border-radius: 10px;
  background-image:
    radial-gradient(120% 90% at 22% 12%, rgba(255, 255, 255, 0.2), transparent 62%),
    linear-gradient(140deg, var(--t1), var(--t2));
}
.vs-tile:nth-child(1) { --t1: #5B6B8C; --t2: #36425A; }
.vs-tile:nth-child(2) { --t1: #8C6F5B; --t2: #57412F; }
.vs-tile:nth-child(3) { --t1: #4F7C6E; --t2: #2C4941; }
.vs-tile:nth-child(4) { --t1: #7B5B8C; --t2: #473353; }
.vs-tile:nth-child(5) { --t1: #8C8257; --t2: #565033; }
.vs-tile:nth-child(6) { --t1: #5B7A8C; --t2: #364C5A; }
.vs-mark {
  position: absolute;
  inset-inline-end: 4px;
  bottom: 4px;
  padding: 2px 6px;
  border-radius: 7px;
  font-size: 10px;
  font-weight: 700;
  color: #fff;
  white-space: nowrap;
}
.vs-mark-up   { background: #10b981; }
.vs-mark-down { background: #ef4444; }

.vs-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 5px; align-items: end; height: 108px; }
.vs-day { display: flex; flex-direction: column; justify-content: flex-end; gap: 5px; height: 100%; }
.vs-slot { border-radius: 7px; background: color-mix(in srgb, var(--c-text) 7%, transparent); height: 22px; }
.vs-slot-on {
  background: linear-gradient(180deg, #22d3ee, #3b82f6);
  transform: scaleY(0);
  transform-origin: bottom;
  animation: btk-vs-slot 20s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
.vs-day:nth-child(2) .vs-slot-on { animation-delay: 0.12s; }
.vs-day:nth-child(4) .vs-slot-on { animation-delay: 0.24s; }
.vs-day:nth-child(6) .vs-slot-on { animation-delay: 0.36s; }
@keyframes btk-vs-slot { 0%,42%{transform:scaleY(0)} 50%,57%{transform:scaleY(1)} 60%,100%{transform:scaleY(0)} }
.vs-dayname { text-align: center; font-size: 10.5px; color: var(--c-muted); }

.vs-funnel { display: grid; gap: 9px; }
.vs-frow { display: grid; grid-template-columns: 62px 1fr; align-items: center; gap: 10px; font-size: 11.5px; color: var(--c-muted); }
.vs-fbar { height: 22px; border-radius: 8px; background: color-mix(in srgb, var(--c-text) 8%, transparent); overflow: hidden; }
.vs-fbar > i {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, #3b82f6, #22d3ee);
  transform: scaleX(0);
  transform-origin: right center;
  animation: btk-vs-fill 20s cubic-bezier(0.22, 1, 0.36, 1) infinite;
}
.vs-frow:nth-child(1) .vs-fbar > i { --v: 1; }
.vs-frow:nth-child(2) .vs-fbar > i { --v: 0.42; animation-delay: 0.14s; }
.vs-frow:nth-child(3) .vs-fbar > i { --v: 0.12; animation-delay: 0.28s; background: linear-gradient(90deg, #ef4444, #f59e0b); }
@keyframes btk-vs-fill { 0%,62%{transform:scaleX(0)} 70%,77%{transform:scaleX(var(--v))} 80%,100%{transform:scaleX(0)} }
.vs-leak {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  align-self: start;
  padding: 3px 9px;
  border-radius: 9999px;
  font-size: 11px;
  font-weight: 700;
  color: #ef4444;
  background: color-mix(in srgb, #ef4444 14%, transparent);
}

.vs-todo { display: grid; gap: 9px; }
.vs-trow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 11px;
  border-radius: 12px;
  font-size: 12.5px;
  background: color-mix(in srgb, var(--c-text) 5%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
}
.vs-box {
  position: relative;
  width: 16px;
  height: 16px;
  flex: none;
  border-radius: 5px;
  border: 2px solid color-mix(in srgb, var(--c-text) 22%, transparent);
}
.vs-box::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 5px;
  background: #10b981;
  opacity: 0;
  animation: btk-vs-tick 20s ease-out infinite;
}
.vs-trow:nth-child(2) .vs-box::after { animation-delay: 0.5s; }
.vs-trow:nth-child(3) .vs-box::after { animation-delay: 1s; }
@keyframes btk-vs-tick { 0%,84%{opacity:0;transform:scale(0.4)} 88%,97%{opacity:1;transform:scale(1)} 99%,100%{opacity:0;transform:scale(0.4)} }

/* ---- The per-beat tag at the bottom of each card ------------------------------------ */
.vs-tags { position: relative; height: 50px; margin-top: 18px; }
.vs-tag {
  position: absolute;
  inset-inline: 0;
  top: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 13px;
  border-radius: 13px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
  opacity: 0;
  animation: btk-vs-b1 20s ease-in-out infinite;
}
.vs-tag-2 { animation-name: btk-vs-b2; }
.vs-tag-3 { animation-name: btk-vs-b3; }
.vs-tag-4 { animation-name: btk-vs-b4; }
.vs-tag-5 { animation-name: btk-vs-b5; }
.vs-card-bad  .vs-tag { color: #ef4444; background: color-mix(in srgb, #ef4444 12%, transparent); }
.vs-card-good .vs-tag { color: #10b981; background: color-mix(in srgb, #10b981 12%, transparent); }
.vs-tag svg { flex: none; }

/* ---- Progress dots (also the manual control) ---------------------------------------- */
.vs-dots { display: flex; justify-content: center; gap: 10px; margin-top: 22px; }
.vs-dot {
  position: relative;
  width: 34px;
  height: 4px;
  padding: 0;
  border: 0;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--c-text) 14%, transparent);
  cursor: pointer;
  overflow: hidden;
}
/* The fill is a child so the hit area can stay 34px wide while the bar itself animates. */
.vs-dot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: #22d3ee;
  transform: scaleX(0);
  transform-origin: right center;
  animation: btk-vs-dot1 20s linear infinite;
}
.vs-dot:nth-child(2)::after { animation-name: btk-vs-dot2; }
.vs-dot:nth-child(3)::after { animation-name: btk-vs-dot3; }
.vs-dot:nth-child(4)::after { animation-name: btk-vs-dot4; }
.vs-dot:nth-child(5)::after { animation-name: btk-vs-dot5; }
@keyframes btk-vs-dot1 { 0%{transform:scaleX(0)} 20%{transform:scaleX(1)} 20.01%,100%{transform:scaleX(0)} }
@keyframes btk-vs-dot2 { 0%,20%{transform:scaleX(0)} 40%{transform:scaleX(1)} 40.01%,100%{transform:scaleX(0)} }
@keyframes btk-vs-dot3 { 0%,40%{transform:scaleX(0)} 60%{transform:scaleX(1)} 60.01%,100%{transform:scaleX(0)} }
@keyframes btk-vs-dot4 { 0%,60%{transform:scaleX(0)} 80%{transform:scaleX(1)} 80.01%,100%{transform:scaleX(0)} }
@keyframes btk-vs-dot5 { 0%,80%{transform:scaleX(0)} 100%{transform:scaleX(1)} }
.vs-dot:focus-visible { outline: 2px solid #22d3ee; outline-offset: 4px; }

/* ---- Driven mode: by scroll (.is-scroll) or by a pinned frame (.is-manual) -----------
   The shared 20s keyframe clock is switched off here — position decides what is on screen, not
   time. But the MOTION is not switched off with it: everything that used to move on the clock
   now moves on the CHANGE OF STATE instead. A beat still slides in and the one before it still
   leaves upwards, the line still draws itself, the week still fills in, the funnel still pours
   and the boxes still tick themselves off.

   This is LIGHTER than the timer version, not heavier: a transition runs only when the beat
   actually changes — five times as you pass through the section — where the keyframes ran every
   frame forever. Everything here is opacity/transform, plus one stroke-dashoffset. */
.vs-stage.is-manual .vs-beat, .vs-stage.is-scroll .vs-beat,
.vs-stage.is-manual .vs-tag, .vs-stage.is-scroll .vs-tag,
.vs-stage.is-manual .vs-dot::after, .vs-stage.is-scroll .vs-dot::after,
.vs-stage.is-manual .vs-line-path, .vs-stage.is-scroll .vs-line-path,
.vs-stage.is-manual .vs-slot-on, .vs-stage.is-scroll .vs-slot-on,
.vs-stage.is-manual .vs-fbar > i, .vs-stage.is-scroll .vs-fbar > i,
.vs-stage.is-manual .vs-box::after, .vs-stage.is-scroll .vs-box::after { animation: none !important; }

/* A beat waits below, comes into place, and leaves UPWARDS once it is behind you — so the
   sequence reads as one column moving, not five cards blinking on and off in the same spot. */
.vs-stage.is-manual .vs-beat, .vs-stage.is-scroll .vs-beat {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.5s ease, transform 0.68s cubic-bezier(0.2, 0.7, 0.25, 1);
}
.vs-stage[data-beat="2"] .vs-beat-1,
.vs-stage[data-beat="3"] .vs-beat-1, .vs-stage[data-beat="3"] .vs-beat-2,
.vs-stage[data-beat="4"] .vs-beat-1, .vs-stage[data-beat="4"] .vs-beat-2, .vs-stage[data-beat="4"] .vs-beat-3,
.vs-stage[data-beat="5"] .vs-beat-1, .vs-stage[data-beat="5"] .vs-beat-2,
.vs-stage[data-beat="5"] .vs-beat-3, .vs-stage[data-beat="5"] .vs-beat-4 { transform: translateY(-28px); }
.vs-stage.is-manual .vs-tag, .vs-stage.is-scroll .vs-tag {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.45s ease 0.14s, transform 0.6s cubic-bezier(0.2, 0.7, 0.25, 1) 0.14s;
}

/* Rest state for every moving piece inside a beat, and the transition it animates on. The
   delays are what make a beat arrive in pieces instead of all at once. */
.vs-stage.is-manual .vs-line-path, .vs-stage.is-scroll .vs-line-path {
  stroke-dashoffset: 300;
  transition: stroke-dashoffset 1.25s cubic-bezier(0.3, 0.75, 0.3, 1) 0.18s;
}
.vs-stage.is-manual .vs-slot-on, .vs-stage.is-scroll .vs-slot-on {
  transform: scaleY(0);
  transition: transform 0.62s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.vs-stage.is-manual .vs-day:nth-child(1) .vs-slot-on, .vs-stage.is-scroll .vs-day:nth-child(1) .vs-slot-on { transition-delay: 0.14s; }
.vs-stage.is-manual .vs-day:nth-child(3) .vs-slot-on, .vs-stage.is-scroll .vs-day:nth-child(3) .vs-slot-on { transition-delay: 0.30s; }
.vs-stage.is-manual .vs-day:nth-child(5) .vs-slot-on, .vs-stage.is-scroll .vs-day:nth-child(5) .vs-slot-on { transition-delay: 0.46s; }
.vs-stage.is-manual .vs-day:nth-child(7) .vs-slot-on, .vs-stage.is-scroll .vs-day:nth-child(7) .vs-slot-on { transition-delay: 0.62s; }
.vs-stage.is-manual .vs-fbar > i, .vs-stage.is-scroll .vs-fbar > i {
  transform: scaleX(0);
  transition: transform 0.72s cubic-bezier(0.2, 0.75, 0.25, 1);
}
.vs-stage.is-manual .vs-frow:nth-child(2) .vs-fbar > i, .vs-stage.is-scroll .vs-frow:nth-child(2) .vs-fbar > i { transition-delay: 0.14s; }
.vs-stage.is-manual .vs-frow:nth-child(3) .vs-fbar > i, .vs-stage.is-scroll .vs-frow:nth-child(3) .vs-fbar > i { transition-delay: 0.34s; }
.vs-stage.is-manual .vs-box::after, .vs-stage.is-scroll .vs-box::after {
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.28s ease, transform 0.34s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.vs-stage.is-manual .vs-trow:nth-child(1) .vs-box::after, .vs-stage.is-scroll .vs-trow:nth-child(1) .vs-box::after { transition-delay: 0.30s; }
.vs-stage.is-manual .vs-trow:nth-child(2) .vs-box::after, .vs-stage.is-scroll .vs-trow:nth-child(2) .vs-box::after { transition-delay: 0.46s; }
.vs-stage.is-manual .vs-trow:nth-child(3) .vs-box::after, .vs-stage.is-scroll .vs-trow:nth-child(3) .vs-box::after { transition-delay: 0.62s; }
.vs-stage.is-manual .vs-trow:nth-child(4) .vs-box::after, .vs-stage.is-scroll .vs-trow:nth-child(4) .vs-box::after { transition-delay: 0.78s; }
/* Labels and markers pop in last. Without them beat 2 (the post grid) would be the one beat
   with nothing moving in it at all. */
.vs-stage.is-manual .vs-note, .vs-stage.is-scroll .vs-note,
.vs-stage.is-manual .vs-mark, .vs-stage.is-scroll .vs-mark {
  opacity: 0;
  transform: translateY(7px) scale(0.94);
  transition: opacity 0.3s ease, transform 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.vs-stage.is-manual .vs-notes > :nth-child(1), .vs-stage.is-scroll .vs-notes > :nth-child(1) { transition-delay: 0.42s; }
.vs-stage.is-manual .vs-notes > :nth-child(2), .vs-stage.is-scroll .vs-notes > :nth-child(2) { transition-delay: 0.52s; }
.vs-stage.is-manual .vs-notes > :nth-child(3), .vs-stage.is-scroll .vs-notes > :nth-child(3) { transition-delay: 0.62s; }
.vs-stage.is-manual .vs-leak, .vs-stage.is-scroll .vs-leak { transition-delay: 0.62s; }
.vs-stage.is-manual .vs-tile:nth-child(2) .vs-mark, .vs-stage.is-scroll .vs-tile:nth-child(2) .vs-mark { transition-delay: 0.30s; }
.vs-stage.is-manual .vs-tile:nth-child(4) .vs-mark, .vs-stage.is-scroll .vs-tile:nth-child(4) .vs-mark { transition-delay: 0.46s; }

/* ---- Active state: one attribute decides everything above --------------------------- */
.vs-stage[data-beat="1"] .vs-beat-1, .vs-stage[data-beat="1"] .vs-tag-1,
.vs-stage[data-beat="2"] .vs-beat-2, .vs-stage[data-beat="2"] .vs-tag-2,
.vs-stage[data-beat="3"] .vs-beat-3, .vs-stage[data-beat="3"] .vs-tag-3,
.vs-stage[data-beat="4"] .vs-beat-4, .vs-stage[data-beat="4"] .vs-tag-4,
.vs-stage[data-beat="5"] .vs-beat-5, .vs-stage[data-beat="5"] .vs-tag-5 { opacity: 1; transform: translateY(0); }
.vs-stage[data-beat="1"] .vs-beat-1 .vs-line-path { stroke-dashoffset: 0; }
.vs-stage[data-beat="3"] .vs-beat-3 .vs-slot-on { transform: scaleY(1); }
.vs-stage[data-beat="4"] .vs-beat-4 .vs-fbar > i { transform: scaleX(var(--v)); }
.vs-stage[data-beat="5"] .vs-beat-5 .vs-box::after { opacity: 1; transform: scale(1); }
.vs-stage[data-beat="1"] .vs-beat-1 .vs-note,
.vs-stage[data-beat="2"] .vs-beat-2 .vs-mark,
.vs-stage[data-beat="3"] .vs-beat-3 .vs-note,
.vs-stage[data-beat="4"] .vs-beat-4 .vs-note { opacity: 1; transform: translateY(0) scale(1); }
/* The blurred side never reveals its markers or labels — that IS the comparison. */
.vs-card-bad .vs-note, .vs-card-bad .vs-mark { display: none; }

/* Progress bars: an index in pinned mode, a real readout of scroll position in scroll mode
   (--p is written per bar by the script). */
.vs-stage[data-beat="1"] .vs-dot:nth-child(1)::after,
.vs-stage[data-beat="2"] .vs-dot:nth-child(2)::after,
.vs-stage[data-beat="3"] .vs-dot:nth-child(3)::after,
.vs-stage[data-beat="4"] .vs-dot:nth-child(4)::after,
.vs-stage[data-beat="5"] .vs-dot:nth-child(5)::after { transform: scaleX(1); }
.vs-stage.is-scroll .vs-dot::after {
  transform: scaleX(var(--p, 0)) !important;
  transition: transform 0.12s linear;
}

/* ---- Phones: a genuinely different build, not the desktop one shrunk -----------------
   Stacked, the two cards came to 967px against an 844px screen, so the scroll-driven mode
   measured "does not fit" and fell back to the timer — on the device the owner cares about
   most. Side by side they fit in roughly half that, and the comparison actually reads BETTER
   small: the whole message is blurred-next-to-sharp, and that contrast survives at any size,
   while a stacked pair makes you scroll between the two things you are meant to compare.
   Everything below is the compaction that buys the fit. */
@media (max-width: 899px) {
  .vs-cards { grid-template-columns: 1fr 1fr; gap: 9px; }
  .vs-card { padding: 13px 10px 11px; border-radius: 16px; }
  .vs-head { flex-direction: column; align-items: flex-start; gap: 3px; margin-bottom: 9px; }
  .vs-title { font-size: 12.5px; }
  .vs-chip { font-size: 9.5px; padding: 2px 7px; gap: 4px; }
  .vs-view { height: clamp(148px, 24vh, 236px); }
  .vs-tags { height: 70px; margin-top: 10px; }
  .vs-tag { align-items: flex-start; gap: 6px; padding: 8px 9px; border-radius: 10px; font-size: 10.5px; }
  .vs-q { font-size: 34px; }

  .vs-line { height: 78px; }
  .vs-notes { gap: 4px; }
  .vs-note { font-size: 8.5px; padding: 2px 6px; gap: 3px; }
  .vs-note i { width: 5px; height: 5px; }

  .vs-grid { max-width: 100%; gap: 5px; }
  .vs-mark { font-size: 8px; padding: 1px 4px; border-radius: 5px; inset-inline-end: 3px; bottom: 3px; }

  .vs-week { height: 84px; gap: 3px; }
  .vs-slot { height: 15px; border-radius: 5px; }
  .vs-dayname { font-size: 8.5px; }

  .vs-frow { grid-template-columns: 34px 1fr; gap: 6px; font-size: 9.5px; }
  .vs-fbar { height: 16px; border-radius: 6px; }
  .vs-funnel { gap: 6px; }
  .vs-leak { font-size: 9px; padding: 2px 7px; }

  .vs-todo { gap: 6px; }
  .vs-trow { padding: 7px 8px; font-size: 9.5px; gap: 7px; border-radius: 9px; }
  .vs-box { width: 13px; height: 13px; border-width: 2px; }

  .vs-dots { margin-top: 16px; gap: 8px; }
  .vs-dot { width: 26px; }
}

/* With motion off, the section becomes the first beat, fully drawn and readable. It keeps the
   blurred-vs-sharp contrast, which is the whole message and costs no movement at all; the dots
   still work, so every other beat is one tap away. */
@media (prefers-reduced-motion: reduce) {
  .vs-beat, .vs-tag, .vs-dot::after, .vs-line-path,
  .vs-slot-on, .vs-fbar > i, .vs-box::after, .vs-arrow::after { animation: none !important; }
  .vs-beat, .vs-tag, .vs-line-path, .vs-slot-on, .vs-fbar > i,
  .vs-box::after, .vs-note, .vs-mark { transition: none !important; }
  .vs-beat, .vs-tag { opacity: 0; transform: none; }
  .vs-note, .vs-mark { opacity: 1; transform: none; }
  .vs-beat-1, .vs-tag-1 { opacity: 1; }
  .vs-dot:nth-child(1)::after { transform: scaleX(1); }
  .vs-line-path { stroke-dashoffset: 0; }
  .vs-slot-on { transform: scaleY(1); }
  .vs-fbar > i { transform: scaleX(var(--v)); }
  .vs-box::after { opacity: 1; transform: scale(1); }
}

/* =====================================================================================
   "چطور کار می‌کند؟" as a direct-message thread — rebuilt 2026-10-01.

   The owner's brief for the landing is that no two sections may work the same way, or the eye
   tires. The hero is one device running a loop; the section above this one is two glass panels
   that scroll-swap. So this one is deliberately the opposite SHAPE: messages ACCUMULATE. It
   plays itself out once and then it is simply a conversation you can read, top to bottom, with
   nothing left moving.

   That is also the answer to "don't make the site heavy". There is no loop and no scroll
   handler here at all: one IntersectionObserver fires a single timeline of transitions the
   first time the section is reached, and after roughly five seconds this section's running
   cost is exactly zero. It is the lightest thing on the page.

   The four steps are the chat's own date-separators. A chat app puts a little centred divider
   between stretches of conversation; ours name the step. So the section still answers "how
   does it work" in plain words at a glance, while the thread underneath shows it.

   No phone frame on purpose — the hero already has one, and repeating it is exactly the
   sameness the owner asked us to avoid. This is a bare conversation on the page's own glass.
   ===================================================================================== */
.dm-thread {
  max-width: 600px;
  margin-inline: auto;
  display: grid;
  gap: 14px;
}

/* ---- Step separator: a chat date-divider doing double duty ---------------------------- */
.dm-sep {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 10px 0 2px;
  font-size: 12px;
  font-weight: 700;
  color: var(--c-muted);
}
.dm-sep::before,
.dm-sep::after {
  content: '';
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--c-text) 14%, transparent), transparent);
}
.dm-sep b {
  display: inline-grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-inline-end: 6px;
  border-radius: 50%;
  font-size: 11px;
  color: #22d3ee;
  background: color-mix(in srgb, #22d3ee 16%, transparent);
}
.dm-sep span { display: inline-flex; align-items: center; }

/* ---- Rows. Persian chat apps mirror the usual sides: incoming sits at the inline start
        (the right), outgoing at the inline end (the left). Logical properties do that for
        free, so nothing here needs an RTL override. -------------------------------------- */
.dm-row { display: flex; align-items: flex-end; gap: 9px; }
.dm-row-me { flex-direction: row-reverse; }

.dm-av {
  position: relative;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #061019;
  background: linear-gradient(140deg, #22d3ee, #3b82f6);
  box-shadow: 0 0 18px -6px rgba(34, 211, 238, 0.8);
}

.dm-bub {
  max-width: 78%;
  padding: 10px 13px;
  border-radius: 16px;
  font-size: 13.5px;
  line-height: 1.75;
  background: color-mix(in srgb, var(--c-surface) 72%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
  border-end-start-radius: 5px;
}
.dm-row-me .dm-bub {
  border-end-start-radius: 16px;
  border-end-end-radius: 5px;
  background: color-mix(in srgb, #3b82f6 16%, transparent);
  border-color: color-mix(in srgb, #3b82f6 28%, transparent);
}

/* The attachment chip on the first message: what "connecting your page" actually looks like. */
.dm-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  padding: 7px 10px;
  border-radius: 12px;
  background: color-mix(in srgb, var(--c-text) 7%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
  font-size: 12px;
}
.dm-chip i {
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(from 200deg, #22d3ee, #3b82f6, #a855f7, #f59e0b, #22d3ee);
}
.dm-chip small { display: block; font-size: 10.5px; color: var(--c-muted); }

/* ---- Typing indicator: its own bubble, removed once the real one lands. Chat really does
        reflow when a message arrives, so the shift is honest rather than a glitch. --------- */
.dm-typing { display: flex; gap: 4px; padding: 13px; }
.dm-typing i {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--c-muted);
  animation: btk-dm-dot 1.1s ease-in-out infinite;
}
.dm-typing i:nth-child(2) { animation-delay: 0.16s; }
.dm-typing i:nth-child(3) { animation-delay: 0.32s; }
@keyframes btk-dm-dot {
  0%, 60%, 100% { opacity: 0.3; transform: translateY(0); }
  30%           { opacity: 1;   transform: translateY(-3px); }
}

/* ---- The two rich replies. Deliberately flatter and tighter than the cards in the section
        above, so they read as attachments inside a message, not as another panel. ---------- */
.dm-card {
  margin-top: 9px;
  padding: 11px;
  border-radius: 13px;
  background: color-mix(in srgb, var(--c-bg) 55%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
}
.dm-score { display: flex; align-items: center; gap: 12px; }
.dm-ring { width: 52px; height: 52px; flex: none; transform: rotate(-90deg); }
.dm-ring-bg { fill: none; stroke: color-mix(in srgb, var(--c-text) 11%, transparent); stroke-width: 11; }
.dm-ring-fg {
  fill: none;
  stroke: url(#btkDmGrad);
  stroke-width: 11;
  stroke-linecap: round;
  stroke-dasharray: 327;
  stroke-dashoffset: 327;
  transition: stroke-dashoffset 1.1s cubic-bezier(0.3, 0.75, 0.3, 1) 0.25s;
}
.dm-num { font-size: 22px; font-weight: 800; line-height: 1; }
.dm-cap { font-size: 11px; color: var(--c-muted); }
.dm-list { list-style: none; margin: 9px 0 0; padding: 0; display: grid; gap: 6px; }
.dm-list li { display: flex; align-items: center; gap: 8px; font-size: 12px; }
.dm-list i { width: 6px; height: 6px; flex: none; border-radius: 50%; }
.dm-red { background: #ef4444; }
.dm-amber { background: #f59e0b; }
.dm-green { background: #10b981; }

.dm-task { display: flex; align-items: center; gap: 9px; font-size: 12.5px; padding: 6px 0; }
.dm-task + .dm-task { border-top: 1px solid color-mix(in srgb, var(--c-text) 7%, transparent); }
.dm-box {
  position: relative;
  width: 15px;
  height: 15px;
  flex: none;
  border-radius: 5px;
  border: 2px solid color-mix(in srgb, var(--c-text) 22%, transparent);
}
.dm-box::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 5px;
  background: #10b981;
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.26s ease, transform 0.32s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.dm-delta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  padding: 5px 10px;
  border-radius: 9999px;
  font-size: 11.5px;
  font-weight: 700;
  color: #10b981;
  background: color-mix(in srgb, #10b981 15%, transparent);
}

/* ---- Play-once reveal ---------------------------------------------------------------
   The resting state is VISIBLE. `.is-anim` is added by script only when it is actually going
   to run the timeline, so a visitor with no JS — or with reduced motion — gets the finished
   thread immediately instead of a blank block. */
.dm-thread.is-anim > * {
  opacity: 0;
  transform: translateY(12px);
}
.dm-thread.is-anim > .is-in {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.2, 0.7, 0.25, 1);
}
/* Unconditional, so a visitor with no JS never sees a stray typing bubble sitting there
   forever; the script reveals it by adding .is-typing and takes it away again. */
.dm-hide { display: none; }
.dm-thread.is-anim > .dm-hide.is-typing { display: flex; opacity: 1; transform: none; }
/* Once the score bubble is in, its ring draws and the tasks tick themselves off. */
.dm-thread .is-in .dm-ring-fg { stroke-dashoffset: 52; }
.dm-thread .is-in .dm-task:nth-child(1) .dm-box::after { opacity: 1; transform: scale(1); transition-delay: 0.45s; }
.dm-thread .is-in .dm-task:nth-child(2) .dm-box::after { opacity: 1; transform: scale(1); transition-delay: 0.62s; }
.dm-thread .is-in .dm-task:nth-child(3) .dm-box::after { opacity: 1; transform: scale(1); transition-delay: 0.79s; }

@media (max-width: 767px) {
  .dm-thread { gap: 11px; }
  .dm-bub { max-width: 86%; font-size: 13px; padding: 9px 12px; }
  .dm-sep { font-size: 11.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .dm-typing i { animation: none; opacity: 0.6; }
  .dm-ring-fg, .dm-box::after { transition: none !important; }
}

/* =====================================================================================
   «امکانات کلیدی» as a PRODUCTION LINE — built 2026-10-01.

   Fourth shape on this landing, and it had to be a fourth: the hero is one device running a
   loop, «چرا اکثر پیج‌ها…» is two panels that scroll-swap, «چطور کار می‌کند» accumulates. So
   this one is SPATIAL — a single object moving through a machine, left to right, while the
   capabilities sit along its path as stations. A list of eight features is the hardest thing on
   a landing page to make anyone read; a line they can watch something travel down is not.

   The whole thing is one travelling transform plus eight station lights on the same clock, and
   an IntersectionObserver stops all of it the moment the section leaves the screen. No images,
   no library, nothing per-frame from JavaScript.

   Desktop runs horizontally with the stations zigzagged above and below the belt; the phone
   turns the same line vertical with the stations alternating left and right. Two builds out of
   one set of markup, which is what the project's same-content-two-layouts rule asks for.
   ===================================================================================== */
.pl {
  --pl-loop: 16s;
  position: relative;
}
@media (min-width: 900px) {
  /* The travel track is a full-width box that slides almost its own width to the left, so its
     trailing edge leaves the page and the document grows a horizontal scrollbar. Only the pill
     riding it is ever visible, so clipping the track costs nothing — and the padding is what
     stops the clip from also cutting the glow off the top and bottom rows of tiles (the belt
     and the track both sit at 50% of this same padded box, so they stay aligned). */
  .pl { overflow: hidden; padding-block: 30px; }
}

/* ---- The belt ------------------------------------------------------------------------ */
.pl-belt {
  position: absolute;
  inset-inline: 0;
  top: 50%;
  height: 3px;
  transform: translateY(-50%);
  border-radius: 9999px;
  background: color-mix(in srgb, var(--c-text) 10%, transparent);
  overflow: hidden;
}
/* The moving tread. A repeating-linear-gradient slid by one tile per cycle — one animated
   transform on one element, which is the cheapest way to make a surface look like it is
   running. */
.pl-belt::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 200%;
  background: repeating-linear-gradient(
    90deg,
    color-mix(in srgb, #22d3ee 55%, transparent) 0 10px,
    transparent 10px 24px);
  animation: btk-pl-tread 1.4s linear infinite;
}
@keyframes btk-pl-tread {
  from { transform: translateX(0); }
  to   { transform: translateX(-24px); }
}

/* ---- The workpiece: the customer's page, travelling the line ------------------------- */
/* The travel lives on this, not on the pill. A percentage inside translate() is relative to
   the element being moved, so the element being moved is made exactly as wide as the belt and
   `100%` then means "the length of the line" on every browser. The previous version said the
   same thing in `cqw`, which is correct and silently does NOTHING without container query
   support: the transform becomes invalid, the pill never leaves the entrance, and yet the label
   still flips to "report ready" at the end of the loop. That is exactly what the owner saw, and
   it is not worth a modern-CSS dependency to avoid one wrapper div. */
.pl-piece-track {
  position: absolute;
  inset-inline-start: 0;
  top: 50%;
  width: 100%;
  height: 0;
  z-index: 3;
  animation: btk-pl-travel var(--pl-loop) linear infinite;
}
.pl-piece {
  position: absolute;
  top: 0;
  inset-inline-start: 0;
  font-size: 13px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 11px 7px 8px;
  border-radius: 14px;
  white-space: nowrap;
  font-size: 12px;
  font-weight: 700;
  background: color-mix(in srgb, var(--c-surface) 82%, transparent);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, #22d3ee 34%, transparent);
  box-shadow: 0 0 26px -8px rgba(34, 211, 238, 0.75);
  /* linear, not eased. The station lights are scheduled on a linear reading of the belt, so
     an ease-in-out here made the glow run ahead of the piece through the middle of the line
     and behind it at the ends. A conveyor runs at a constant speed anyway. */
  animation: btk-pl-fade var(--pl-loop) linear infinite;
}
.pl-piece i {
  width: 20px;
  height: 20px;
  flex: none;
  border-radius: 50%;
  background: conic-gradient(from 200deg, #22d3ee, #3b82f6, #a855f7, #f59e0b, #22d3ee);
}
.pl-piece b { font-weight: 700; }
/* The label swaps for the last stretch of the run, so the piece visibly leaves as a finished
   report rather than as the same thing that went in. */
/* One grid cell holding both labels: the pill is then as wide as the LONGER of them, instead
   of being sized by "پیج شما" and letting "گزارش آماده" spill out of its own capsule. */
.pl-piece span { display: grid; }
.pl-piece span em { grid-area: 1 / 1; font-style: normal; white-space: nowrap; }
.pl-piece span em + em { color: #10b981; opacity: 0; }
.pl-piece span em:first-child { animation: btk-pl-label1 var(--pl-loop) step-end infinite; }
.pl-piece span em + em        { animation: btk-pl-label2 var(--pl-loop) step-end infinite; }
@keyframes btk-pl-label1 { 0%, 86% { opacity: 1; } 87%, 100% { opacity: 0; } }
@keyframes btk-pl-label2 { 0%, 86% { opacity: 0; } 87%, 100% { opacity: 1; } }
/* translate % is relative to the piece's own width, so the run is expressed against the belt in
   the markup's own custom property instead. */
@keyframes btk-pl-travel {
  0%, 4%    { transform: translateX(0); }
  92%, 100% { transform: translateX(calc(-100% + 160px)); }
}
/* The pill's own entrance and exit, on the same clock as the travel above. */
@keyframes btk-pl-fade {
  0%        { transform: translateY(-50%) scale(0.92); opacity: 0; }
  4%        { transform: translateY(-50%) scale(1);    opacity: 1; }
  92%       { transform: translateY(-50%) scale(1);    opacity: 1; }
  97%, 100% { transform: translateY(-50%) scale(0.92); opacity: 0; }
}

/* ---- Stations ------------------------------------------------------------------------ */
/* Three rows: stations above, a RESERVED LANE, stations below. The lane is the only thing
   that stops a label from sitting where the pill is about to pass — which it was doing, and
   the pill rode straight over "ساخت سناریو" and the caption under "هوش فروش". Centring the
   stations on the belt and hoping they clear it is not a layout, it is a coincidence. */
.pl-rail {
  position: relative;
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-template-rows: 1fr 76px 1fr;
  min-height: 340px;
}
.pl-station {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 0 4px;
  text-align: center;
}
/* Zigzag: odd stations sit above the belt, even ones below, each with a short stem down or up
   to it. Eight in a row would be unreadably narrow otherwise. */
/* Above the lane, bottom-aligned to it; below the lane, top-aligned to it. Each station is
   therefore pressed against the lane edge and never inside it. */
.pl-station:nth-child(odd)  { grid-row: 1; align-self: end; }
.pl-station:nth-child(even) { grid-row: 3; align-self: start; flex-direction: column-reverse; }
/* The COLUMN has to be explicit too. Pinning only the row let auto-placement pack the four odd
   stations into columns 1-4 of row 1 and the four even ones into columns 1-4 of row 3, so eight
   stations ended up sharing four columns and every station sat at twice its intended distance
   from the entrance — which is why the glow appeared to run two stations ahead of the pill.
   Written out rather than computed: `grid-column: calc(...)` with a custom property is not
   reliably accepted as a grid line. */
.pl-station:nth-child(1) { grid-column: 1; }
.pl-station:nth-child(2) { grid-column: 2; }
.pl-station:nth-child(3) { grid-column: 3; }
.pl-station:nth-child(4) { grid-column: 4; }
.pl-station:nth-child(5) { grid-column: 5; }
.pl-station:nth-child(6) { grid-column: 6; }
.pl-station:nth-child(7) { grid-column: 7; }
.pl-station:nth-child(8) { grid-column: 8; }
@media (max-width: 899px) {
  .pl-station { grid-column: 1 !important; }
}

.pl-stem {
  width: 1px;
  height: 26px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--c-text) 22%, transparent), transparent);
}
.pl-station:nth-child(even) .pl-stem {
  background: linear-gradient(0deg, color-mix(in srgb, var(--c-text) 22%, transparent), transparent);
}

.pl-tile {
  position: relative;
  display: grid;
  place-items: center;
  width: 50px;
  height: 50px;
  flex: none;
  border-radius: 15px;
  color: var(--c-muted);
  background: color-mix(in srgb, var(--c-surface) 70%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
  transition: color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
}
/* Each station lights as the piece reaches it — same 16s clock, one eighth of it each, so the
   light and the piece can never disagree about where on the line the work is happening. */
.pl-station { --pl-i: 0; }
/* The piece is on the belt from 4% to 92% of the loop, so station i is reached at
   4% + 88% x (i + 0.5)/8. The light peaks at 11% of its OWN cycle, so each tile is offset by
   (0.11 - that) — which is what keeps the glow under the piece instead of somewhere else on
   the line. Negative delays are the point: they start the animation part-way in. */
.pl-tile {
  animation: btk-pl-light var(--pl-loop) linear infinite;
  /* delay = t_target - 0.11 x loop, where t_target = loop x (0.04 + 0.88 x (i+0.5)/8).
     Written the other way round first, which made the lights run BACKWARDS down the line
     while the piece went forwards — visible immediately once the two were sampled together
     rather than eyeballed. */
  animation-delay: calc(var(--pl-loop) * (0.11 * var(--pl-i) - 0.015));
}
@keyframes btk-pl-light {
  0%, 4%   { color: var(--c-muted); border-color: color-mix(in srgb, var(--c-text) 10%, transparent); box-shadow: none; transform: none; }
  8%, 14%  { color: #22d3ee; border-color: color-mix(in srgb, #22d3ee 55%, transparent); box-shadow: 0 0 24px -6px rgba(34, 211, 238, 0.8); transform: translateY(-3px); }
  20%, 100%{ color: var(--c-muted); border-color: color-mix(in srgb, var(--c-text) 10%, transparent); box-shadow: none; transform: none; }
}

/* 11px captions were smaller than anything else on this page, which runs 13-16px. The rail
   gets a wider container below to pay for the extra width this needs. */
.pl-name { font-size: 14px; font-weight: 700; line-height: 1.5; }
.pl-desc { font-size: 12px; line-height: 1.65; color: var(--c-muted); }
/* Wider than the page's usual max-w-6xl: eight stations in a row need the room, and a line
   is the one thing on the page that reads better for being long. */
#features { max-width: 1280px; }

/* ---- Phones: the same line, turned vertical ------------------------------------------
   Eight stations across a 390px screen is not a design, it is a row of thumbnails. The belt
   becomes a column and the stations alternate either side of it, which is the same idea and
   reads better in a thumb's natural scroll direction. */
@media (max-width: 899px) {
  /* Fixed row height on purpose. The vertical run below has to be a real length, and `cqh`
     would need `container-type: size`, which needs a definite height — circular. Pinning the
     row height instead makes the rail's height arithmetic the markup already knows: eight rows
     plus seven gaps. */
  /* A DIFFERENT name from the horizontal run, which is set inline on the element in the
     markup — an inline custom property beats anything a media query says, so reusing the
     name meant the phone kept using the desktop value and the piece travelled upwards. */
  .pl { --pl-run-v: calc(8 * 56px + 7 * 14px - 42px); }
  .pl-rail {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    grid-auto-rows: 56px;
    gap: 14px;
    min-height: 0;
    padding-inline-start: 0;
  }
  .pl-belt {
    inset-inline: auto;
    left: 50%;
    top: 0;
    bottom: 0;
    height: auto;
    width: 3px;
    transform: translateX(-50%);
  }
  .pl-belt::after {
    width: 100%;
    height: 200%;
    background: repeating-linear-gradient(
      180deg,
      color-mix(in srgb, #22d3ee 55%, transparent) 0 10px,
      transparent 10px 24px);
    animation-name: btk-pl-tread-v;
  }
  @keyframes btk-pl-tread-v {
    from { transform: translateY(0); }
    to   { transform: translateY(-24px); }
  }
  /* Same reservation, turned sideways: each station stops 58px short of the centre line, so
     the pill (about 111px wide, i.e. 55px either side) has a clear column to travel down. */
  .pl-station {
    align-self: stretch !important;
    grid-row: auto !important;
    flex-direction: row !important;
    align-items: center;
    gap: 8px;
    text-align: start;
    width: calc(50% - 34px);
  }
  /* First station on the inline START — the right, in RTL — so the line begins where a Persian
     reader's eye already is, then alternates. */
  .pl-station:nth-child(odd)  { justify-self: start; padding-inline-start: 0; }
  .pl-station:nth-child(even) { justify-self: end;   flex-direction: row-reverse !important; text-align: end; padding-inline-end: 0; }
  .pl-stem { width: 10px; height: 1px; background: linear-gradient(90deg, color-mix(in srgb, var(--c-text) 22%, transparent), transparent) !important; }
  .pl-tile { width: 34px; height: 34px; border-radius: 11px; }
  .pl-name { font-size: 12.5px; line-height: 1.45; }
  .pl-desc { display: none; }
  /* The track spans the rail's HEIGHT here, so `100%` is the length of the line again. */
  .pl-piece-track {
    inset-inline-start: auto;
    left: 50%;
    top: 0;
    width: 0;
    height: 100%;
    animation-name: btk-pl-travel-v;
  }
  @keyframes btk-pl-travel-v {
    0%, 4%    { transform: translateY(0); }
    92%, 100% { transform: translateY(calc(100% - 42px)); }
  }
  /* PHYSICAL centring on the pill: in RTL `inset-inline-start` resolves to `right`, so a
     translateX(-50%) pushed it a further half-width left and it rode down the page on top of
     the left-hand labels instead of on the belt. */
  .pl-piece { inset-inline-start: auto; left: 0; animation-name: btk-pl-fade-v; }
  /* Turned upright. The pill's layout box is still 111x36, so translateX(-50%) centres it on
     the belt exactly as before and the rotation happens about that same centre — but it now
     occupies ~36px across the line instead of 111px. That reclaimed width is what stops the
     station captions wrapping onto three lines. */
  @keyframes btk-pl-fade-v {
    0%        { transform: translateX(-50%) rotate(90deg) scale(0.92); opacity: 0; }
    4%        { transform: translateX(-50%) rotate(90deg) scale(1);    opacity: 1; }
    92%       { transform: translateX(-50%) rotate(90deg) scale(1);    opacity: 1; }
    97%, 100% { transform: translateX(-50%) rotate(90deg) scale(0.92); opacity: 0; }
  }
}

/* Nothing on a line nobody is looking at needs to keep running. */
.pl.is-idle .pl-belt::after,
.pl.is-idle .pl-piece,
.pl.is-idle .pl-piece-track,
.pl.is-idle .pl-piece span em,
.pl.is-idle .pl-tile { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .pl-belt::after, .pl-piece, .pl-piece-track, .pl-piece span em, .pl-tile { animation: none !important; }
  /* With the line stopped, the piece would sit at the entrance looking stuck. It is taken off
     the belt entirely and every station is shown lit, which is the same information without
     any movement at all. */
  .pl-piece-track { display: none; }
  .pl-tile { color: #22d3ee; border-color: color-mix(in srgb, #22d3ee 45%, transparent); }
}

/* =====================================================================================
   «دمو» — watch it rewrite. Built 2026-10-01, replacing a drag-to-compare wipe.

   The wipe was built first and thrown away after looking at it: a vertical reveal works for
   IMAGES, where both halves carry picture across the full width. Persian text hugs the right
   edge, so cutting down the middle showed the right of one text and the blank left margin of
   the other. The mechanic was fine; it was wrong for the content.

   What replaced it keeps the same goal — show the thing actually CHANGING, not describe it —
   and is still the fifth distinct shape on this page: it is the only section you PRESS, and
   the only one where the content is replaced in front of you rather than revealed, swapped or
   scrolled past. One text on screen at a time, so it is perfectly readable at any width.

   All of it is opacity and transform on a handful of lines; the panel reserves its own height
   up front so nothing on the page below it moves when the text changes.
   ===================================================================================== */
.rw { display: grid; gap: 16px; max-width: 720px; margin-inline: auto; }

.rw-panel {
  border-radius: 20px;
  background: var(--c-surface);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
  box-shadow: 0 26px 54px -38px rgba(2, 6, 18, 0.9);
  overflow: hidden;
  transition: border-color 0.4s ease;
}
.rw-panel[data-state="after"] { border-color: color-mix(in srgb, #10b981 34%, transparent); }

.rw-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 11px 15px;
  border-bottom: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
}
.rw-kind { display: flex; align-items: center; gap: 8px; flex: none; white-space: nowrap; font-size: 12.5px; font-weight: 700; color: var(--c-muted); }
.rw-kind i { width: 6px; height: 6px; border-radius: 50%; background: #22d3ee; flex: none; }
.rw-state {
  padding: 3px 10px;
  border-radius: 9999px;
  font-size: 11px;
  font-weight: 700;
  color: #ef4444;
  background: color-mix(in srgb, #ef4444 14%, transparent);
  transition: color 0.3s ease, background-color 0.3s ease;
}
.rw-panel[data-state="after"] .rw-state { color: #10b981; background: color-mix(in srgb, #10b981 15%, transparent); }

/* The two texts share one cell, so the panel is as tall as the taller of them and the page
   below never shifts when they swap. */
.rw-stage { display: grid; padding: 16px 15px; }
.rw-text { grid-area: 1 / 1; font-size: 14px; line-height: 2; }
.rw-line { display: block; transition: opacity 0.35s ease, transform 0.45s cubic-bezier(0.2, 0.7, 0.25, 1); }

/* Before: present at rest, struck through and gone once the rewrite runs. */
.rw-before .rw-line { opacity: 1; transform: none; }
.rw-panel[data-state="working"] .rw-before .rw-line,
.rw-panel[data-state="after"] .rw-before .rw-line { opacity: 0; transform: translateY(-8px); }
.rw-before .rw-line:nth-child(2) { transition-delay: 0.06s; }
.rw-before .rw-line:nth-child(3) { transition-delay: 0.12s; }
.rw-before .rw-line:nth-child(4) { transition-delay: 0.18s; }

/* After: absent until the rewrite lands, then arrives a line at a time. */
.rw-after { pointer-events: none; }
.rw-after .rw-line { opacity: 0; transform: translateY(10px); }
.rw-panel[data-state="after"] .rw-after { pointer-events: auto; }
.rw-panel[data-state="after"] .rw-after .rw-line { opacity: 1; transform: none; }
.rw-panel[data-state="after"] .rw-after .rw-line:nth-child(1) { transition-delay: 0.10s; }
.rw-panel[data-state="after"] .rw-after .rw-line:nth-child(2) { transition-delay: 0.26s; }
.rw-panel[data-state="after"] .rw-after .rw-line:nth-child(3) { transition-delay: 0.42s; }
.rw-panel[data-state="after"] .rw-after .rw-line:nth-child(4) { transition-delay: 0.58s; }

/* The working state: a scan sweeping the text, the same gesture the hero uses when the product
   is reading a page — so "it is thinking" means the same thing everywhere on this landing. */
.rw-scan {
  grid-area: 1 / 1;
  align-self: stretch;
  opacity: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, #22d3ee 26%, transparent), transparent);
  background-size: 45% 100%;
  background-repeat: no-repeat;
  pointer-events: none;
}
.rw-panel[data-state="working"] .rw-scan { opacity: 1; animation: btk-rw-scan 0.85s linear infinite; }
@keyframes btk-rw-scan {
  from { background-position: 140% 0; }
  to   { background-position: -40% 0; }
}

.rw-notes { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 15px 15px; }
.rw-note {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: 9999px;
  font-size: 11.5px;
  color: var(--c-muted);
  background: color-mix(in srgb, var(--c-text) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.3s ease, transform 0.36s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.rw-panel[data-state="after"] .rw-note { opacity: 1; transform: none; }
.rw-panel[data-state="after"] .rw-note:nth-child(2) { transition-delay: 0.72s; }
.rw-panel[data-state="after"] .rw-note:nth-child(3) { transition-delay: 0.84s; }
.rw-panel[data-state="after"] .rw-note:nth-child(1) { transition-delay: 0.60s; }

.rw-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 0 14px;
  border: 0;
  border-radius: 9999px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  color: #061019;
  background: #22d3ee;
  cursor: pointer;
  white-space: nowrap;
  transition: transform 0.2s ease, background-color 0.3s ease, color 0.3s ease;
}
.rw-btn:active { transform: scale(0.96); }
.rw-btn:focus-visible { outline: 2px solid #22d3ee; outline-offset: 3px; }
.rw-panel[data-state="after"] .rw-btn {
  color: var(--c-muted);
  background: color-mix(in srgb, var(--c-text) 8%, transparent);
}
.rw-panel[data-state="working"] .rw-btn { opacity: 0.6; pointer-events: none; }
.rw-btn svg { flex: none; }

@media (max-width: 767px) {
  .rw { gap: 13px; }
  .rw-text { font-size: 13px; line-height: 1.95; }
  .rw-stage { padding: 14px 13px; }
  .rw-head { padding: 10px 13px; }
  .rw-btn { font-size: 12px; padding: 0 12px; min-height: 32px; }
  .rw-notes { padding: 0 13px 13px; }
}

/* Narrow phones (320-399px): the title, the state pill and the button no longer fit on one row,
   and the title was wrapping onto 2-4 lines. The pill is the least valuable of the three (the
   text on screen already says which state it is in), so it goes first. */
@media (max-width: 399px) {
  .rw-state { display: none; }
  .rw-head { gap: 8px; }
}

@media (prefers-reduced-motion: reduce) {
  .rw-line, .rw-note, .rw-btn, .rw-panel, .rw-state { transition: none !important; }
  .rw-panel[data-state="working"] .rw-scan { animation: none; }
}

/* =====================================================================================
   Trust pages (/terms /privacy /refund /contact) - added 2026-09-30.
   Same tokens as the landing page (--c-*), same accents, same radii. Logical properties
   only (inline-start/-end), so the layout mirrors correctly in RTL without overrides.
   ===================================================================================== */

.legal-main { padding-top: 96px; }            /* clears the fixed 58px glass header + its 10px margin */

/* ---- hero ---- */
.legal-hero {
  position: relative;
  overflow: hidden;
  border-radius: 24px;
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  background:
    radial-gradient(520px 220px at 100% 0%, rgba(34, 211, 238, 0.14), transparent 70%),
    radial-gradient(420px 200px at 0% 100%, rgba(59, 130, 246, 0.12), transparent 70%),
    var(--c-surface);
  padding: 28px 22px;
}
@media (min-width: 768px) { .legal-hero { padding: 40px 44px; } }
.legal-hero__row { display: flex; align-items: flex-start; gap: 16px; }
/* A large, faint echo of the page icon on the empty side of the hero (desktop only). Decorative. */
.legal-hero__mark { display: none; }
@media (min-width: 768px) {
  .legal-hero__mark {
    display: block; position: absolute; inset-inline-end: 36px; top: 50%; transform: translateY(-50%);
    line-height: 1; color: #22d3ee; opacity: 0.07; pointer-events: none; user-select: none;
  }
  .legal-hero__mark .material-symbols-outlined { font-size: 200px; }
}
.legal-hero__row { position: relative; }
.legal-tile {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 56px; height: 56px; border-radius: 16px;
  color: #22d3ee;
  background: color-mix(in srgb, #22d3ee 13%, transparent);
  border: 1px solid rgba(34, 211, 238, 0.28);
  box-shadow: 0 0 28px -8px rgba(34, 211, 238, 0.55);
}
.legal-tile .material-symbols-outlined { font-size: 30px; }
.legal-h1 { margin: 0; font-size: 26px; line-height: 1.5; font-weight: 800; }
@media (min-width: 768px) { .legal-h1 { font-size: 34px; } }
.legal-lead { margin: 10px 0 0; max-width: 46rem; color: var(--c-muted); font-size: 15px; line-height: 2; }
.legal-meta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 18px; }
.legal-chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 12px;
  border-radius: 999px; font-size: 12px; color: var(--c-muted);
  border: 1px solid color-mix(in srgb, var(--c-text) 10%, transparent);
  background: color-mix(in srgb, var(--c-bg) 40%, transparent);
}
.legal-chip .material-symbols-outlined { font-size: 16px; color: #22d3ee; }

/* ---- page switcher ---- */
.legal-switch { display: flex; gap: 8px; margin: 18px 0 0; padding: 2px 0 6px; overflow-x: auto; scrollbar-width: none; }
.legal-switch::-webkit-scrollbar { display: none; }
.legal-switch a {
  flex: none; display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 16px;
  border-radius: 10px; font-size: 14px; font-weight: 600; color: var(--c-muted);
  border: 1px solid var(--c-border); background: var(--c-surface);
  transition: border-color .2s ease, color .2s ease, background-color .2s ease;
}
.legal-switch a:hover { border-color: rgba(34, 211, 238, 0.45); color: #22d3ee; }
.legal-switch a[aria-current="page"] {
  color: var(--c-text); border-color: rgba(34, 211, 238, 0.55);
  background: color-mix(in srgb, #22d3ee 10%, var(--c-surface));
}
.legal-switch .material-symbols-outlined { font-size: 18px; }

/* ---- "in one glance" ---- */
.legal-glance {
  margin-top: 22px; padding: 18px 20px; border-radius: 16px;
  border: 1px solid var(--c-border); border-inline-start: 3px solid #22d3ee;
  background: var(--c-surface);
}
.legal-glance h2 { margin: 0 0 10px; font-size: 15px; font-weight: 800; display: flex; align-items: center; gap: 8px; }
.legal-glance h2 .material-symbols-outlined { font-size: 20px; color: #22d3ee; }
.legal-glance ul { margin: 0; padding: 0; list-style: none; display: grid; gap: 10px; }
.legal-glance li { display: flex; gap: 10px; font-size: 14.5px; line-height: 1.95; }
.legal-glance li .material-symbols-outlined { flex: none; margin-top: 4px; font-size: 18px; color: #22c55e; }

/* ---- refund: three cases ---- */
.legal-cases { display: grid; gap: 12px; margin-top: 22px; }
@media (min-width: 768px) { .legal-cases { grid-template-columns: repeat(3, 1fr); } }
.legal-case {
  padding: 18px; border-radius: 16px; border: 1px solid var(--c-border); background: var(--c-surface);
  border-top: 3px solid var(--tone, #22d3ee);
}
.legal-case .material-symbols-outlined { font-size: 26px; color: var(--tone, #22d3ee); }
.legal-case h3 { margin: 10px 0 6px; font-size: 16px; font-weight: 800; }
.legal-case p { margin: 0; font-size: 14px; line-height: 1.95; color: var(--c-muted); }
.legal-case--green { --tone: #22c55e; }
.legal-case--cyan { --tone: #22d3ee; }
.legal-case--amber { --tone: #f59e0b; }

/* ---- layout: contents rail + article ---- */
.legal-layout { display: grid; gap: 22px; margin-top: 28px; }
@media (min-width: 1024px) { .legal-layout { grid-template-columns: 250px minmax(0, 1fr); gap: 52px; } }

.legal-toc { align-self: start; }
@media (min-width: 1024px) { .legal-toc { position: sticky; top: 96px; max-height: calc(100vh - 120px); overflow-y: auto; } }
.legal-toc__mobile { border: 1px solid var(--c-border); border-radius: 12px; background: var(--c-surface); padding: 0 14px; }
.legal-toc__mobile > summary {
  display: flex; min-height: 48px; align-items: center; justify-content: space-between;
  font-size: 13px; font-weight: 800; color: var(--c-muted); list-style: none; cursor: pointer;
}
.legal-toc__mobile > summary::-webkit-details-marker { display: none; }
.legal-toc__mobile[open] > summary { border-bottom: 1px solid var(--c-border); }
.legal-toc__title-desktop { display: none; font-size: 13px; font-weight: 800; color: var(--c-muted); }
.legal-toc ol { margin: 0; padding: 10px 0; list-style: none; display: grid; gap: 2px; }
.legal-toc ol a {
  display: flex; align-items: baseline; gap: 10px; padding: 8px 12px; border-radius: 10px;
  font-size: 14px; line-height: 1.7; color: var(--c-muted);
  border-inline-start: 2px solid transparent;
  transition: color .2s ease, background-color .2s ease, border-color .2s ease;
}
.legal-toc ol a:hover { color: var(--c-text); background: color-mix(in srgb, var(--c-text) 5%, transparent); }
.legal-toc ol a.is-active { color: #22d3ee; border-inline-start-color: #22d3ee; background: color-mix(in srgb, #22d3ee 9%, transparent); }
.legal-toc ol a span:first-child { flex: none; min-width: 1.4em; font-size: 12px; opacity: .8; }
@media (min-width: 1024px) {
  .legal-toc__mobile { border: 0; background: transparent; padding: 0; }
  .legal-toc__mobile > summary { display: none; }
  .legal-toc__title-desktop { display: block; padding: 0 12px 6px; }
  .legal-toc ol { padding-top: 0; }
}

/* ---- article typography ---- */
.legal-article { max-width: 46rem; }
.legal-article section { padding: 6px 0 26px; scroll-margin-top: 92px; }
.legal-article h2 {
  display: flex; align-items: baseline; gap: 12px; margin: 0 0 12px;
  font-size: 21px; line-height: 1.6; font-weight: 800;
}
.legal-num {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 34px; height: 34px; padding: 0 8px; border-radius: 10px; font-size: 14px; font-weight: 800;
  color: #22d3ee; background: color-mix(in srgb, #22d3ee 11%, transparent); border: 1px solid rgba(34, 211, 238, 0.26);
  transform: translateY(2px);
}
.legal-article p, .legal-article li { font-size: 15.5px; line-height: 2.05; }
.legal-article p { margin: 0 0 12px; }
.legal-article ul { margin: 0 0 12px; padding: 0; list-style: none; display: grid; gap: 8px; }
.legal-article li { position: relative; padding-inline-start: 22px; }
.legal-article li::before {
  content: ''; position: absolute; inset-inline-start: 4px; top: 1.05em;
  width: 7px; height: 7px; border-radius: 50%; background: #22d3ee; box-shadow: 0 0 10px rgba(34, 211, 238, 0.6);
}
.legal-note {
  padding: 12px 16px; border-radius: 12px; font-size: 14px !important;
  background: color-mix(in srgb, #f59e0b 10%, transparent); border: 1px solid rgba(245, 158, 11, 0.3);
}
.legal-info { margin: 14px 0 6px; display: grid; gap: 1px; overflow: hidden; border-radius: 14px; border: 1px solid var(--c-border); background: var(--c-border); }
.legal-info > div { display: grid; gap: 4px; padding: 12px 16px; background: var(--c-surface); }
@media (min-width: 640px) { .legal-info > div { grid-template-columns: 120px 1fr; gap: 16px; } }
.legal-info dt { font-size: 13px; color: var(--c-muted); }
.legal-info dd { margin: 0; font-size: 14.5px; line-height: 1.95; }
/* Raw #22d3ee as TEXT measures 1.32:1 on a light surface — it failed AA on every trust
   page, not only in the footer. --plan-ink-accent is the theme-aware ink. */
.legal-link { color: var(--plan-ink-accent); text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--plan-ink-accent) 45%, transparent); text-underline-offset: 4px; }
.legal-link:hover { text-decoration-color: var(--plan-ink-accent); }

/* ---- contact cards ---- */
.legal-contact { display: grid; gap: 12px; margin-top: 26px; }
@media (min-width: 768px) { .legal-contact { grid-template-columns: repeat(2, 1fr); } }
.legal-card {
  display: flex; flex-direction: column; gap: 6px; padding: 20px; border-radius: 16px;
  border: 1px solid var(--c-border); background: var(--c-surface);
}
.legal-card .legal-tile { width: 44px; height: 44px; border-radius: 12px; margin-bottom: 6px; }
.legal-card .legal-tile .material-symbols-outlined { font-size: 24px; }
.legal-card h2 { margin: 0; font-size: 16px; font-weight: 800; }
.legal-card p { margin: 0; font-size: 14.5px; line-height: 2; color: var(--c-muted); }
.legal-card__value { color: var(--c-text); font-size: 16px; font-weight: 700; word-break: break-word; }
.legal-card--wide { grid-column: 1 / -1; }
.legal-card a.legal-card__cta {
  margin-top: 8px; align-self: flex-start; display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 18px;
  border-radius: 10px; font-size: 14px; font-weight: 700; color: var(--c-text);
  border: 1px solid rgba(34, 211, 238, 0.5); background: color-mix(in srgb, #22d3ee 9%, transparent);
  transition: background-color .2s ease, border-color .2s ease;
}
.legal-card a.legal-card__cta:hover { background: color-mix(in srgb, #22d3ee 18%, transparent); border-color: #22d3ee; }

/* ---- footer trust strip (Enamad seal + seller line) ---- */
/* .trust-strip is defined with the rest of the footer at the end of this file (2026-10-02). */
/* Hidden until the image has really loaded (see the script in template-parts/site-footer.php);
   without JavaScript the seal simply shows, as Enamad expects. */
.trust-seal { display: inline-flex; align-items: center; justify-content: center; flex: none; }
.js-seal .trust-seal:not(.is-ready) { display: none; }
.trust-seal.is-ready {
  padding: 10px; border-radius: 16px; background: #fff;
  border: 1px solid var(--c-border); box-shadow: 0 10px 30px -18px rgba(0, 0, 0, 0.5);
}
.trust-seal img { display: block; }     /* the image itself is never resized or altered (Enamad rule) */

@media (prefers-reduced-motion: reduce) {
  .legal-switch a, .legal-toc ol a, .legal-card a.legal-card__cta { transition: none; }
}


/* =====================================================================================
   تعرفه‌ها + مقایسهٔ کامل پلن‌ها — rebuilt 2026-10-02 (landing redesign pass).

   The data behind this is unchanged; only its presentation is new. Three problems the
   old build had, and what replaced each:

   1. The 4-line "what you get" checklist was reprinted on every card, so three quarters
      of the pricing block said the same thing four times -> stated once in .plan-every,
      derived from the real comparison data rather than a second hand-written list.
   2. Phones got four full cards in an AUTO-ADVANCING carousel, then four more stacked
      cards of 14 rows each (~56 rows of scrolling) to compare them -> a tab strip and one
      card, plus ONE four-column matrix that fits a 320px screen.
   3. Nothing showed how big the difference between plans actually is. Tokens are the only
      thing that varies and they vary ~30x (130 -> 4,000) -> .plan-meter draws every plan
      on one shared linear scale, with the exact number always printed beside the bar.
   ===================================================================================== */

/* Accent INK, as opposed to accent fills. Raw #22d3ee is a fine border or background in
   both themes, but as TEXT on a light surface it measures ~1.5:1 — the section's own
   «پیشنهاد ما» flag, its featured column name and its group titles all failed WCAG AA in
   light mode before these were introduced (measured, not assumed). Fills keep using the
   literal accent; anything that has to be READ uses these. */
:root {
  --plan-ink-accent: #5be3f7;
  --plan-ink-ok: #22c55e;
  --plan-ink-warn: #fbbf24;
  /* The footer's horizon word, as an ELEMENT opacity rather than a translucent colour —
     see .ftr-word for why. Lighter in the light theme. */
  --plan-horizon: 0.12;
}
[data-theme="light"] {
  --plan-ink-accent: #0b6e85;
  --plan-ink-ok: #15803d;
  --plan-ink-warn: #b45309;
  --plan-horizon: 0.08;
}

/* `.btk-num` was already written into this page's markup but had only ever been DEFINED in
   the admin plugin's own stylesheet — on the landing page it was a dead class. Defined
   here so prices and token counts actually get tabular figures and line up in a column. */
.btk-num {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ---- what every plan includes, said once ---- */
.plan-every {
  margin: 0 0 18px;
  padding: 15px 17px;
  border-radius: 16px;
  border: 1px solid color-mix(in srgb, var(--c-text) 7%, transparent);
  background: color-mix(in srgb, var(--c-surface) 45%, transparent);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}
.plan-every__title {
  margin: 0 0 11px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--c-muted);
}
.plan-every__list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}
.plan-every__list li {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  background: color-mix(in srgb, var(--c-text) 4%, transparent);
  font-size: 12px;
  line-height: 1.5;
  color: var(--c-text);
}
.plan-every__list .material-symbols-outlined {
  font-size: 15px;
  color: var(--plan-ink-ok);
  flex: none;
}

/* ---- the phone tab strip ---- */
.plan-tabs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 6px;
  margin-bottom: 13px;
}
.plan-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-height: 56px;
  padding: 8px 3px;
  border-radius: 12px;
  border: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
  background: color-mix(in srgb, var(--c-surface) 50%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  font: inherit;
  color: var(--c-muted);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease, box-shadow 0.2s ease;
}
.plan-tab__name { font-size: 13px; font-weight: 800; white-space: nowrap; }
.plan-tab__price { font-size: 11px; white-space: nowrap; }
.plan-tab[aria-selected="true"] {
  color: var(--c-text);
  border-color: rgba(34, 211, 238, 0.5);
  background: color-mix(in srgb, #22d3ee 13%, var(--c-surface));
  box-shadow: 0 10px 22px -16px rgba(34, 211, 238, 0.95);
}

/* ---- the card ---- */
.plan-grid { display: none; }
.plan-card {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 22px 19px 18px;
  border-radius: 16px;
  border: 1px solid color-mix(in srgb, var(--c-text) 8%, transparent);
  background: color-mix(in srgb, var(--c-surface) 55%, transparent);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
}
/* `display: flex` above out-specifics the browser's own `[hidden] { display: none }`, so
   without this the tab strip would switch the lit column and the aria state while every
   card stayed on screen. The same trap caught this project once before, in the login
   plugin's `.btk-step` — any class that sets `display` has to restore `hidden` itself. */
.plan-card[hidden] { display: none; }

/* Elevation is declared once per card: the plain cards carry a hairline border and no
   shadow; the featured one adds a real shadow with an offset and a soft blur (a colored
   halo at zero offset is decoration, not depth). */
.plan-card.is-featured {
  border-color: rgba(34, 211, 238, 0.38);
  box-shadow: 0 20px 52px -32px rgba(34, 211, 238, 0.75), 0 3px 12px -8px rgba(0, 0, 0, 0.45);
}
/* The diagonal shine-sweep this page already uses on the login card and the old pricing
   cards. Hover-only, and gated behind a real hover device so a tap cannot leave it stuck. */
.plan-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 40%, color-mix(in srgb, var(--c-text) 12%, transparent) 50%, transparent 60%);
  transform: translateX(-120%);
  transition: transform 0.6s ease;
  pointer-events: none;
}
@media (hover: hover) {
  .plan-card:hover::after { transform: translateX(120%); }
}

.plan-card__name {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  min-height: 30px;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.01em;
}
.plan-card__flag {
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, #22d3ee 16%, transparent);
  border: 1px solid rgba(34, 211, 238, 0.35);
  font-size: 10.5px;
  font-weight: 700;
  color: var(--plan-ink-accent);
  white-space: nowrap;
}
/* Two real lines reserved: the longest description runs to two, and a card whose
   description is one line long must not pull its price and button up out of line with
   its neighbours. line-clamp caps it so a longer string can never reintroduce the drift. */
.plan-card__desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 7px 0 0;
  min-height: 40px;
  font-size: 12px;
  line-height: 1.75;
  color: var(--c-muted);
}

/* ---- the meter ---- */
.plan-meter { margin-top: 15px; }
.plan-meter__head {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 0 0 7px;
  font-size: 11.5px;
  color: var(--c-muted);
}
.plan-meter__head strong {
  font-size: 17px;
  font-weight: 800;
  color: var(--c-text);
}
.plan-meter__track {
  position: relative;
  display: block;
  height: 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--c-text) 8%, transparent);
  overflow: hidden;
}
/* Revealed with clip-path rather than scaleX. scaleX squashes the gradient along with the
   box, so every bar ended up carrying the WHOLE cyan->blue ramp inside whatever width it
   happened to have — رایگان's 7px sliver and انفجار's full bar were coloured differently
   for no reason, which is the one thing a set of bars on a shared scale must not do. Here
   the gradient is always painted across the full track and the clip decides how much of it
   shows, so the colour at any point on the track means the same thing on all four cards:
   the further a plan reaches, the further into the blue it gets. The page is always
   dir="rtl", so the reveal is inset from the LEFT and the bar grows right-to-left. */
.plan-meter__fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: linear-gradient(270deg, #22d3ee, #3b82f6);
  clip-path: inset(0 0 0 calc((1 - var(--fill, 1)) * 100%));
}
.plan-meter__ticks {
  position: absolute;
  inset: 0 1px;
  pointer-events: none;
}
.plan-tick {
  position: absolute;
  top: 0;
  bottom: 0;
  inset-inline-start: calc(var(--at, 0) * 100%);
  width: 2px;
  margin-inline-start: -1px;
  border-radius: 1px;
  background: color-mix(in srgb, var(--c-text) 26%, transparent);
}

/* ---- price, value, CTA ---- */
.plan-card__price {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin: 14px 0 0;
}
.plan-card__price strong {
  font-size: 30px;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
}
.plan-card__price span { font-size: 12.5px; color: var(--c-muted); }
.plan-card__value {
  margin: 5px 0 0;
  min-height: 18px;
  font-size: 12px;
  color: var(--c-muted);
}
.plan-card__cta {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 15px;
  min-height: 48px;
  padding: 0 14px;
  border-radius: 10px;
  border: 1px solid var(--c-border);
  font-size: 14.5px;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  color: var(--c-text);
  transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}
/* Dark ink on the cyan->blue gradient, the same pairing the demo section's own button
   uses. The old build put theme-colored text on flat #3B82F6, which measured 3.4:1 —
   under the 4.5:1 this size of text needs. This pairing measures 5.4:1 at the blue end
   and 11:1 at the cyan end. */
.plan-card__cta.is-primary {
  border-color: transparent;
  background-image: linear-gradient(95deg, #3b82f6, #22d3ee, #3b82f6);
  background-size: 220% 100%;
  background-position: 0% 0%;
  color: #061019;
  box-shadow: 0 12px 28px -14px rgba(34, 211, 238, 0.85);
  transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), background-position 0.5s ease, box-shadow 0.25s ease;
}
@media (hover: hover) {
  .plan-card__cta:hover { border-color: #22d3ee; color: #22d3ee; transform: translateY(-1px); }
  .plan-card__cta.is-primary:hover {
    color: #061019;
    background-position: 100% 0%;
    box-shadow: 0 16px 34px -14px rgba(34, 211, 238, 1);
  }
}
.plan-card__cta:active { transform: scale(0.985); }

/* ---- what only this plan adds ---- */
.plan-card__own {
  display: grid;
  gap: 10px;
  margin: 15px 0 0;
  padding: 15px 0 0;
  list-style: none;
  border-top: 1px solid color-mix(in srgb, var(--c-text) 7%, transparent);
}
.plan-card__own li {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  font-size: 12px;
  line-height: 1.7;
  color: var(--c-muted);
}
.plan-card__own .material-symbols-outlined {
  flex: none;
  margin-top: 1px;
  font-size: 16px;
  color: var(--plan-ink-accent);
}
.plan-card__fun {
  margin: 16px 0 0;
  padding-top: 14px;
  font-size: 12px;
  line-height: 1.8;
  text-align: center;
  color: var(--c-muted);
}
.plan-note {
  margin: 17px 0 0;
  text-align: center;
  font-size: 12px;
  color: var(--c-muted);
}

/* ---- the comparison matrix ---- */
.plan-compare { margin-top: 54px; }
.plan-compare__title {
  margin: 0 0 6px;
  text-align: center;
  font-size: 22px;
  font-weight: 700;
}
.plan-compare__hint {
  margin: 0 0 16px;
  text-align: center;
  font-size: 12px;
  color: var(--c-muted);
}
/* No `overflow` anywhere on this wrapper on purpose: an overflow value other than
   `visible` on ANY ancestor turns that element into the sticky containing block, and the
   header row below would silently stop sticking. The rounded corners are therefore drawn
   on the table's own corner cells instead of clipped by the wrapper. */
.plan-matrix-wrap { position: relative; }
.plan-matrix {
  width: 100%;
  table-layout: fixed;
  border-collapse: separate;
  border-spacing: 0;
  border: 1px solid var(--c-border);
  border-radius: 16px;
  background: color-mix(in srgb, var(--c-surface) 40%, transparent);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}
.plan-matrix__corner { width: 40%; }
/* 76px = the fixed header's 10px top margin + its 58px bar + 8px of air. */
.plan-matrix thead th {
  position: sticky;
  top: 76px;
  z-index: 3;
  padding: 0;
  background: var(--c-surface2);
}
.plan-matrix thead th:first-child { border-start-start-radius: 15px; }
.plan-matrix thead th:last-child { border-start-end-radius: 15px; }
.plan-matrix tbody:last-child tr:last-child > *:first-child { border-end-start-radius: 15px; }
.plan-matrix tbody:last-child tr:last-child > *:last-child { border-end-end-radius: 15px; }

.plan-colbtn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  width: 100%;
  min-height: 48px;
  padding: 10px 3px;
  border: 0;
  border-radius: 10px;
  background: none;
  font: inherit;
  cursor: pointer;
}
.plan-colbtn__name { font-size: 13.5px; font-weight: 800; color: var(--c-text); white-space: nowrap; }
.plan-colbtn__name.is-featured { color: var(--plan-ink-accent); }
.plan-colbtn__price { font-size: 11px; color: var(--c-muted); white-space: nowrap; }

.plan-matrix__grouprow > * {
  padding: 13px 13px 7px;
  background: color-mix(in srgb, var(--c-surface2) 70%, transparent);
  border-top: 1px solid var(--c-border);
}
.plan-matrix__group {
  text-align: right;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--plan-ink-accent);
}
.plan-matrix__label,
.plan-matrix__cell {
  border-top: 1px solid color-mix(in srgb, var(--c-text) 6%, transparent);
  vertical-align: middle;
}
.plan-matrix__label {
  padding: 11px 13px;
  text-align: right;
  font-weight: 400;
  font-size: 12.5px;
  line-height: 1.6;
}
.plan-matrix__lab {
  display: flex;
  align-items: center;
  gap: 8px;
}
.plan-matrix__lab .material-symbols-outlined {
  flex: none;
  font-size: 17px;
  color: color-mix(in srgb, var(--plan-ink-accent) 75%, transparent);
}
/* A row all four plans answer the same way stays in the table in full — it is still a
   real capability and the owner's locked call is that each one is individually visible —
   but it is drawn quieter, because what sends someone to a comparison table is where the
   plans DIFFER. Derived per row from the real values, never hand-tagged. */
.plan-matrix tr.is-same .plan-matrix__label { color: var(--c-muted); }
.plan-matrix tr.is-diff .plan-matrix__label { color: var(--c-text); font-weight: 600; }
.plan-matrix__cell { padding: 11px 5px; text-align: center; }
.plan-yes { font-size: 18px; color: var(--plan-ink-ok); }
.plan-no { font-size: 18px; color: var(--c-muted); }
.plan-val { font-size: 11.5px; font-weight: 600; }

/* The selected plan's column, lit from the header all the way to the last row. */
.plan-matrix thead th.is-sel { background: color-mix(in srgb, #22d3ee 15%, var(--c-surface2)); }
.plan-matrix__grouprow > .is-sel { background: color-mix(in srgb, #22d3ee 11%, var(--c-surface2)); }
.plan-matrix__cell.is-sel { background: color-mix(in srgb, #22d3ee 8%, transparent); }

/* ---- the one authored moment: the bars fill as the section arrives ---- */
/* Default (and the no-JavaScript result) is a filled bar. The script adds `has-js`
   synchronously, which empties them, and `is-lit` once the section is actually on screen. */
.plan-pick.has-js .plan-meter__fill {
  clip-path: inset(0 0 0 100%);
  transition: clip-path 0.75s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.plan-pick.has-js.is-lit .plan-meter__fill { clip-path: inset(0 0 0 calc((1 - var(--fill, 1)) * 100%)); }
/* A card revealed by the tab strip has missed the section's own entrance, so it replays
   the fill for itself — set by the script, which clears it one frame later. */
.plan-pick.has-js .plan-card.is-refilling .plan-meter__fill {
  transition: none;
  clip-path: inset(0 0 0 100%);
}

@media (min-width: 768px) {
  .plan-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 17px;
    align-items: stretch;
  }
  .plan-card { padding: 24px 20px 20px; }
  .plan-card__fun { margin-top: auto; }
  .plan-every { margin-bottom: 22px; padding: 17px 20px; }
  .plan-every__list li { font-size: 12.5px; padding: 7px 12px; }
  .plan-compare { margin-top: 84px; }
  .plan-compare__title { font-size: 28px; }
  .plan-matrix__corner { width: 30%; }
  .plan-matrix__label { padding: 14px 18px; font-size: 13.5px; }
  .plan-matrix__cell { padding: 14px 8px; }
  .plan-matrix__grouprow > * { padding: 15px 18px 9px; }
  .plan-val { font-size: 12.5px; }
}

/* Narrow phones: the icon in a row label is decorative (aria-hidden already), and at this
   width its 17px plus the gap is the difference between a one-line and a three-line label. */
@media (max-width: 639px) {
  .plan-matrix__lab .material-symbols-outlined { display: none; }
}
@media (max-width: 399px) {
  .plan-colbtn__price { display: none; }
  .plan-tabs { gap: 5px; }
  .plan-tab__price { font-size: 10.5px; }
  .plan-matrix__label { padding: 10px 9px; font-size: 11.5px; }
  .plan-matrix__cell { padding: 10px 3px; }
  .plan-matrix__grouprow > * { padding: 11px 9px 6px; }
  .plan-val { font-size: 11px; }
  /* the narrowest value column must still hold the «به‌زودی» chip whole */
  .plan-matrix__cell .plan-soon { font-size: 9px; padding: 0 3px; }
}

@media (prefers-reduced-motion: reduce) {
  .plan-pick.has-js .plan-meter__fill,
  .plan-pick.has-js .plan-card.is-refilling .plan-meter__fill {
    transition: none;
    clip-path: inset(0 0 0 calc((1 - var(--fill, 1)) * 100%));
  }
  .plan-tab,
  .plan-card__cta,
  .plan-card__cta.is-primary { transition: none; }
}


/* =====================================================================================
   سوالات متداول — rebuilt 2026-10-02 (landing redesign pass).

   One typographic column. No cards, no glass, no panel — every other section on this page
   is a box, so this one earns its place by being the opposite. The ANSWER is the headline
   and the question is the small line above it, because a visitor this far down already has
   the question in their head and only needs the answer.

   The only motion is the rules drawing themselves as the column arrives, which is why they
   are real elements rather than `border-top`.
   ===================================================================================== */

.qa-row {
  position: relative;
  padding: 22px 0;
}
.qa-rule {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 1px;
  background: color-mix(in srgb, var(--c-text) 13%, transparent);
  transform-origin: right center; /* the page is always dir="rtl" */
}
.qa-row:first-child .qa-rule { display: none; }

/* One grid so the plain rows and the button rows lay out identically — the button must not
   introduce its own spacing just because it is a button. */
.qa-head {
  display: grid;
  gap: 7px;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: right;
}
.qa-head.js-faq-toggle { cursor: pointer; }

.qa-q {
  font-size: 13px;
  font-weight: 400;
  line-height: 1.7;
  color: var(--c-muted);
}
.qa-a {
  font-size: 21px;
  font-weight: 700;
  line-height: 1.55;
  letter-spacing: -0.02em;
  color: var(--c-text);
  text-wrap: balance;
}
.qa-more {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  justify-self: start; /* RTL: the inline start, i.e. under the right edge of the answer */
  margin-top: 1px;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--plan-ink-accent);
}
.qa-more .chevron { font-size: 17px; }
.qa-detail {
  max-width: 62ch;
  margin: 0;
  padding-top: 11px;
  font-size: 14px;
  line-height: 2.05;
  color: var(--c-muted);
}

/* Which row is interactive is shown by its own rule warming up, not by a hover background —
   there is no card here to tint. Gated behind a real hover device so a tap cannot strand it. */
@media (hover: hover) {
  .qa-head.js-faq-toggle:hover + .faq-panel,
  .qa-head.js-faq-toggle:hover .qa-a { color: var(--plan-ink-accent); }
  .qa-row:has(.js-faq-toggle:hover) .qa-rule,
  .qa-row:has(.js-faq-toggle:hover) + .qa-row .qa-rule {
    background: color-mix(in srgb, var(--plan-ink-accent) 55%, transparent);
  }
}
.qa-row:has(.js-faq-toggle[aria-expanded="true"]) .qa-a { color: var(--plan-ink-accent); }
.qa-head.js-faq-toggle:focus-visible {
  outline: 2px solid var(--plan-ink-accent);
  outline-offset: 6px;
  border-radius: 4px;
}

/* ---- the one authored moment: the column rules itself as it arrives ---- */
/* Default (and the no-JavaScript result) is a fully drawn rule. The script adds `has-js`
   synchronously, which collapses them, then `is-ruled` once the column is on screen — the
   same order the pricing meters use, and for the same reason: a visit where the script
   never runs must not be left with a column of invisible rules. */
.qa.has-js .qa-rule {
  transform: scaleX(0);
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.qa.has-js.is-ruled .qa-rule { transform: scaleX(1); }
.qa.has-js .qa-head {
  opacity: 0;
  transform: translateY(7px);
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--i, 0) * 70ms + 90ms);
}
.qa.has-js.is-ruled .qa-head {
  opacity: 1;
  transform: none;
}

@media (min-width: 768px) {
  .qa-row { padding: 26px 0; }
  .qa-q { font-size: 14px; }
  .qa-a { font-size: 27px; line-height: 1.5; }
  .qa-detail { font-size: 15px; padding-top: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .qa.has-js .qa-rule { transition: none; transform: scaleX(1); }
  .qa.has-js .qa-head { transition: none; opacity: 1; transform: none; }
}


/* =====================================================================================
   Footer — rebuilt 2026-10-02 (last section of the landing redesign pass; applied to the
   trust pages too, by the owner's call).

   A first version kept the old four-column link grid and only restyled the top edge. The
   owner's verdict on seeing it was that it felt identical to what was there before, and he
   was right: four equal columns of links ARE the thing that makes a footer look like every
   other footer. So the structure went, not the paint.

   What is here now: the landing's growth curve (`.btk-bg-curve`, rising bottom-right to
   top-left) ARRIVES here and writes the name. The line draws itself right-to-left, a pulse
   runs it, it blooms where it lands, and the brand comes into focus out of a blur as the
   horizon of the page. The links are one compact band above it.
   ===================================================================================== */

.ftr {
  position: relative;
  overflow: hidden; /* the horizon word is deliberately wider than small viewports */
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--c-bg) 60%, transparent));
}
.ftr-inner {
  position: relative;
  margin: 0 auto;
  max-width: 72rem;
  padding: 0 16px;
}

/* ---- the curve ---- */
.ftr-arrive {
  position: relative;
  height: 72px;
  pointer-events: none;
}
.ftr-svg { display: block; width: 100%; height: 100%; }
/* The landing point: viewBox (0, 8) of a 1440x120 box stretched to the element, so 8/120. */
.ftr-bloom {
  position: absolute;
  top: 6.7%;
  inset-inline-end: 100%;      /* RTL: the inline END is the left edge, where the line lands */
  width: 220px;
  height: 220px;
  margin: -110px -110px 0 0;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(16, 185, 129, 0.5), rgba(34, 211, 238, 0.18) 38%, transparent 68%);
  opacity: 0;
}

/* ---- the link band ---- */
.ftr-band {
  display: grid;
  gap: 26px;
  padding: 10px 0 34px;
}
.ftr-brand__link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.ftr-brand__logo { height: 38px; width: auto; }
.ftr-brand__name { font-size: 17px; font-weight: 800; }
.ftr-brand__tag {
  margin: 9px 0 0;
  max-width: 34ch;
  font-size: 13px;
  line-height: 1.9;
  color: var(--c-muted);
}
.ftr-groups {
  display: grid;
  gap: 22px;
}
.ftr-group__title {
  margin: 0 0 10px;
  font-size: 12.5px;
  font-weight: 800;
  color: var(--c-text);
}
.ftr-group__list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.ftr-link {
  font-size: 13px;
  color: var(--c-muted);
  transition: color 0.2s ease;
}
@media (hover: hover) { .ftr-link:hover { color: var(--plan-ink-accent); } }
.ftr-link:focus-visible { color: var(--plan-ink-accent); }

/* ---- the horizon ---- */
.ftr-horizon {
  position: relative;
  display: flex;
  justify-content: center;
  pointer-events: none;
  user-select: none;
  /* It fades out downward so the trust strip can sit over its lower half and stay readable. */
  -webkit-mask-image: linear-gradient(180deg, #000 28%, transparent 88%);
  mask-image: linear-gradient(180deg, #000 28%, transparent 88%);
}
.ftr-word {
  display: block;
  font-size: clamp(66px, 22vw, 236px);
  font-weight: 800;
  line-height: 0.92;
  letter-spacing: -0.035em;
  white-space: nowrap;
  /* Opaque colour plus element opacity, NOT a translucent colour. A translucent fill is
     painted once per glyph, so wherever two joined Persian letters overlap — and in
     «بترکونش» almost every pair does — the overlap is painted twice and shows up as a
     bright diamond at each join. The owner spotted exactly those. Element opacity
     composites the whole word once, so the joins are seamless. */
  color: var(--c-text);
  opacity: var(--plan-horizon);
}
/* Its own band under the horizon — the word's space is the word's. */
.trust-strip {
  position: relative;
  margin-top: 10px;
  border-top: 1px solid color-mix(in srgb, var(--c-text) 9%, transparent);
}
.trust-strip__inner {
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  gap: 16px;
  padding: 20px 0 0;
}
/* The support address as a control, not a sentence: a pill that looks pressable because
   it is. The address itself is LTR inside an RTL line, hence its own dir. */
.ftr-mail {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  padding: 0 16px;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--c-text) 11%, transparent);
  background: color-mix(in srgb, var(--c-surface) 55%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  font-size: 13px;
  color: var(--c-text);
  transition: border-color 0.2s ease, color 0.2s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.ftr-mail .material-symbols-outlined { font-size: 18px; color: var(--plan-ink-accent); }
.ftr-mail__label { color: var(--c-muted); }
.ftr-mail__addr { font-weight: 700; letter-spacing: 0.01em; }
@media (hover: hover) {
  .ftr-mail:hover { border-color: color-mix(in srgb, var(--plan-ink-accent) 55%, transparent); transform: translateY(-1px); }
  .ftr-mail:hover .ftr-mail__addr { color: var(--plan-ink-accent); }
}
.ftr-mail:active { transform: scale(0.985); }
.ftr-copy {
  margin: 0;
  padding: 16px 0 22px;
  text-align: center;
  font-size: 11.5px;
  color: var(--c-muted);
}

/* ---- the arrival ---- */
/* Default, and the result when the script never runs: everything drawn and visible. */
.ftr.has-js .ftr-line {
  /* The dash maths, written out because the SIGN silently reverses the whole gesture and
     still looks like "a line animating". With `stroke-dasharray: 100` on a pathLength="100"
     path, offset d puts the dash at (-d)..(100-d). The path starts at x=1440 — the right,
     where the eye begins in RTL — so d must fall from +100 to 0 for the drawn part to grow
     from the right. Verified by hit-testing the running animation, not by reading the CSS. */
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}
.ftr.has-js .ftr-fill,
.ftr.has-js .ftr-band,
.ftr.has-js .trust-strip { opacity: 0; }
.ftr.has-js .ftr-group { opacity: 0; transform: translateY(8px); }
.ftr.has-js .ftr-word { opacity: 0; transform: translateY(16px); filter: blur(14px); }
@keyframes ftr-focus-word { to { opacity: var(--plan-horizon); transform: none; filter: blur(0); } }

.ftr.has-js.is-landed .ftr-line  { animation: ftr-draw 1s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.ftr.has-js.is-landed .ftr-fill  { animation: ftr-fade 0.9s 0.45s ease-out forwards; }
.ftr.has-js.is-landed .ftr-pulse { animation: ftr-run 1.05s 0.7s cubic-bezier(0.37, 0, 0.3, 1) forwards; }
.ftr.has-js.is-landed .ftr-bloom { animation: ftr-bloom 1.5s 1.5s ease-out forwards; }
.ftr.has-js.is-landed .ftr-band  { animation: ftr-fade 0.6s 0.8s ease-out forwards; }
.ftr.has-js.is-landed .ftr-group {
  animation: ftr-rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  animation-delay: calc(0.85s + var(--i, 0) * 100ms);
}
/* The payoff: the brand comes into focus as the line finishes landing. */
.ftr.has-js.is-landed .ftr-word  { animation: ftr-focus-word 1.1s 1.45s cubic-bezier(0.16, 1, 0.3, 1) forwards; }
.ftr.has-js.is-landed .trust-strip { animation: ftr-fade 0.6s 2s ease-out forwards; }

@keyframes ftr-draw { to { stroke-dashoffset: 0; } }
@keyframes ftr-fade { to { opacity: 1; } }
@keyframes ftr-rise { to { opacity: 1; transform: none; } }
@keyframes ftr-bloom {
  0%   { opacity: 0; transform: scale(0.4); }
  35%  { opacity: 1; }
  100% { opacity: 0; transform: scale(1.15); }
}
/* Same sign trap as the line: the 3-unit dash sits at (-d)..(-d+3), so d must go 0 -> -100
   for the pulse to travel from the path start (right) to its end (left). */
@keyframes ftr-run {
  0%   { stroke-dashoffset: 0; opacity: 0; }
  12%  { opacity: 0.9; }
  78%  { opacity: 0.9; }
  100% { stroke-dashoffset: -100; opacity: 0; }
}
.ftr .ftr-pulse { opacity: 0; }

@media (min-width: 768px) {
  .ftr-arrive { height: 112px; }
  .ftr-band {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.55fr);
    align-items: start;
    gap: 40px;
    padding: 18px 0 34px;
  }
  .ftr-groups { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px; }
  .ftr-group__list { flex-direction: column; gap: 10px; }
  .ftr-brand__logo { height: 44px; }
  .ftr-brand__name { font-size: 19px; }
  .trust-strip { margin-top: 18px; }
  .trust-strip__inner {
    flex-direction: row;
    justify-content: space-between;
    padding: 22px 0 0;
  }
  .ftr-copy { padding: 18px 0 26px; }
}

/* The safety-net reveal: identical to the reduced-motion result. */
.ftr.has-js.is-instant .ftr-line { stroke-dasharray: none; stroke-dashoffset: 0; }
.ftr.has-js.is-instant .ftr-fill,
.ftr.has-js.is-instant .ftr-band,
.ftr.has-js.is-instant .trust-strip { opacity: 1; }
.ftr.has-js.is-instant .ftr-group { opacity: 1; transform: none; }
.ftr.has-js.is-instant .ftr-word { opacity: var(--plan-horizon); transform: none; filter: none; }
.ftr.has-js.is-instant .ftr-line,
.ftr.has-js.is-instant .ftr-fill,
.ftr.has-js.is-instant .ftr-pulse,
.ftr.has-js.is-instant .ftr-bloom,
.ftr.has-js.is-instant .ftr-band,
.ftr.has-js.is-instant .ftr-group,
.ftr.has-js.is-instant .ftr-word,
.ftr.has-js.is-instant .trust-strip { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .ftr.has-js .ftr-line { stroke-dasharray: none; stroke-dashoffset: 0; }
  .ftr.has-js .ftr-fill,
  .ftr.has-js .ftr-band,
  .ftr.has-js .trust-strip { opacity: 1; }
  .ftr.has-js .ftr-group { opacity: 1; transform: none; }
  .ftr.has-js .ftr-word { opacity: var(--plan-horizon); transform: none; filter: none; }
  .ftr.has-js.is-landed .ftr-line,
  .ftr.has-js.is-landed .ftr-fill,
  .ftr.has-js.is-landed .ftr-pulse,
  .ftr.has-js.is-landed .ftr-bloom,
  .ftr.has-js.is-landed .ftr-band,
  .ftr.has-js.is-landed .ftr-group,
  .ftr.has-js.is-landed .ftr-word,
  .ftr.has-js.is-landed .trust-strip { animation: none; }
}

/* ---- T12: «به‌زودی» chip, the plan's one-word position, and the long Pro checklist ----
   The chip is the same shape everywhere it appears (a card line, a table cell) so a visitor learns it once.
   Amber, not cyan: cyan is «included», amber is «not yet» — and the word is on it, never colour alone. */
.plan-soon {
  display: inline-flex;
  align-items: center;
  flex: none;
  padding: 1px 8px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, #f59e0b 45%, transparent);
  background: color-mix(in srgb, #f59e0b 14%, transparent);
  color: var(--plan-ink-warn, #b45309);
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1.7;
  white-space: nowrap;
}
.plan-card__tag {
  padding: 3px 9px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--c-text) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--c-text) 12%, transparent);
  font-size: 10.5px;
  font-weight: 700;
  color: var(--c-muted);
  white-space: nowrap;
}
.plan-card__kicker { display: flex; align-items: center; min-height: 24px; margin: 0 0 7px; }
.plan-card__own-text { min-width: 0; flex: 1 1 auto; }
.plan-card__own li { align-items: flex-start; }
.plan-card__own li .plan-soon { margin-top: 2px; }
.plan-matrix__cell .plan-soon { font-size: 10px; padding: 0 6px; }

/* the narrowest phones (320): the «به‌زودی» chip must sit whole inside a ~43 px value column, and on a tablet a card whose
   «ارزش واقعی» figure wraps to a second line must not push its button out of line with its neighbours */
@media (max-width: 360px) {
  .plan-matrix__cell { padding: 10px 1px; }
  .plan-matrix__cell .plan-soon { font-size: 9.5px; padding: 0 2px; }
}
@media (min-width: 768px) and (max-width: 1100px) {
  .plan-card__value { min-height: 36px; }
}
