/* ==========================================================================
   RustChin Official Website: Design System
   Sleek frosted-glass panels, adaptive dark/light theming, crisp hairlines,
   and authentic Iranian typography mirroring the extension popup.
   No em dashes used anywhere.
   ========================================================================== */

/* ── Bundled Variable Fonts ── */
@font-face {
  font-family: 'Vazirmatn';
  src: url('../fonts/Vazirmatn-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F;
}

@font-face {
  font-family: 'Estedad';
  src: url('../fonts/Estedad-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F;
}

@font-face {
  font-family: 'Sahel';
  src: url('../fonts/Sahel-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F;
}

@font-face {
  font-family: 'Arad';
  src: url('../fonts/Arad-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F;
}

@font-face {
  font-family: 'Mikhak';
  src: url('../fonts/Mikhak-Variable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200F;
}

/* ── Theme Tokens (Directly Aligned with Extension Popup) ── */
:root {
  --rc-font: "Vazirmatn", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-display: "Estedad", var(--rc-font), sans-serif;
  --font-body: "Vazirmatn", var(--rc-font), sans-serif;
  /* Monospace for code, key hints and the chrome:// mock. Consumers already
     referenced this token; it was never declared, so `font-family: var(...)`
     resolved to the guaranteed-invalid value and they inherited the Persian
     body face instead of a monospace one. */
  --font-code: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;

  /* Brand Accent: RustChin Blue */
  --accent: #2188ca;
  --accent-hover: #196fa8;
  --accent-soft: rgba(33, 136, 202, 0.14);
  --accent-glow: 0 0 24px -4px rgba(33, 136, 202, 0.45);
  /* Fill behind white text: --accent itself only reaches 3.86:1 with white,
     so buttons and badges paint with this deeper shade instead (4.77:1). */
  --accent-solid: #1a78b5;
  --accent-solid-deep: #16689c;
  /* Accent drawn AS text. It has to clear 4.5:1 twice over: once on the page
     background and once on the --accent-soft tint, which is the lighter of
     the two and therefore the binding case. */
  --accent-ink: #4aa8e0;
  /* Control outlines. A glass hairline is decorative; a field boundary is
     load-bearing, so it gets its own token that clears 3:1 (WCAG 1.4.11). */
  --field-border: #6e7683;

  /* Emerald Status Color */
  --emerald: #1f9d55;
  --emerald-light: #22c55e;
  --emerald-soft: rgba(34, 197, 94, 0.14);
  /* Foreground-safe emerald for text and icons (retunes per theme) */
  --emerald-ink: #22c55e;

  /* Dark Theme Tokens (Default) */
  --bg-ambient-1: #020617;
  --bg-ambient-2: #0f172a;
  --bg-ambient-3: #1e293b;
  --panel: rgba(30, 41, 59, 0.82);
  --panel-solid: #1e293b;
  --panel-card: rgba(15, 23, 42, 0.72);
  --panel-card-solid: #0b1225;
  --panel-card-hover: rgba(30, 41, 59, 0.9);
  --panel-border: rgba(255, 255, 255, 0.08);
  --panel-border-hover: rgba(33, 136, 202, 0.45);
  --hairline: rgba(255, 255, 255, 0.08);
  --text-1: #f8fafc;
  --text-2: #cbd5e1;
  --text-3: #94a3b8;
  --text-muted: #64748b;

  --shadow-panel: 0 16px 40px -10px rgba(0, 0, 0, 0.65), 0 0 0 1px var(--panel-border), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  --shadow-card: 0 6px 18px -4px rgba(0, 0, 0, 0.45), 0 0 0 1px var(--panel-border);
  --toggle-off: #334155;
  --toggle-on: var(--accent);

  --radius-window: 24px;
  --radius-panel: 18px;
  --radius-card: 14px;
  --radius-pill: 100px;

  --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-bounce: cubic-bezier(0.16, 1, 0.3, 1);

  /* Colour tokens for Lens-Drift effects */
  --ink: currentColor;
  --ink-2: #38bdf8;
  --ink-3: #4ff8ff;
}

/* 07 Lens-Drift: letters slip in and out of focus, one after another */
.fx-focus {
  color: var(--ink);
  overflow: visible;
}

.fx-focus b,
.fx-focus .lens-drift-word {
  font-weight: inherit;
  display: inline-block;
  animation: fx-focus 2.6s ease-in-out infinite;
  animation-delay: calc(var(--i, 0) * 0.18s);
  will-change: filter, opacity;
  overflow: visible;
}

@keyframes fx-focus {
  0%, 100% { filter: blur(0);   opacity: 1; }
  50%      { filter: blur(7px); opacity: .35; }
}

/* Light Theme Tokens */
:root[data-theme="light"] {
  --bg-ambient-1: #f8fafc;
  --bg-ambient-2: #e2e8f0;
  --bg-ambient-3: #cbd5e1;
  --panel: rgba(255, 255, 255, 0.88);
  --panel-solid: #ffffff;
  --panel-card: rgba(255, 255, 255, 0.82);
  --panel-card-solid: #ffffff;
  --panel-card-hover: #ffffff;
  --panel-border: rgba(0, 0, 0, 0.08);
  --panel-border-hover: rgba(33, 136, 202, 0.5);
  --hairline: rgba(0, 0, 0, 0.07);
  --text-1: #0f172a;
  --text-2: #334155;
  --text-3: #475569;
  --text-muted: #64748b;

  --shadow-panel: 0 16px 40px -10px rgba(15, 23, 42, 0.08), 0 0 0 1px var(--panel-border), inset 0 1px 0 rgba(255, 255, 255, 0.9);
  --shadow-card: 0 4px 14px -3px rgba(15, 23, 42, 0.05), 0 0 0 1px var(--panel-border);
  --toggle-off: #cbd5e1;
  --toggle-on: var(--accent);
  --accent-ink: #16689c;
  --field-border: #848e9c;
  --emerald-ink: #147534;

}

@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]):not([data-theme="light"]) {
    --bg-ambient-1: #f8fafc;
    --bg-ambient-2: #e2e8f0;
    --bg-ambient-3: #cbd5e1;
    --panel: rgba(255, 255, 255, 0.88);
    --panel-solid: #ffffff;
    --panel-card: rgba(255, 255, 255, 0.82);
    --panel-card-solid: #ffffff;
    --panel-card-hover: #ffffff;
    --panel-border: rgba(0, 0, 0, 0.08);
    --panel-border-hover: rgba(33, 136, 202, 0.5);
    --hairline: rgba(0, 0, 0, 0.07);
    --text-1: #0f172a;
    --text-2: #334155;
    --text-3: #475569;
    --text-muted: #64748b;

    --shadow-panel: 0 16px 40px -10px rgba(15, 23, 42, 0.08), 0 0 0 1px var(--panel-border), inset 0 1px 0 rgba(255, 255, 255, 0.9);
    --shadow-card: 0 4px 14px -3px rgba(15, 23, 42, 0.05), 0 0 0 1px var(--panel-border);
    --toggle-off: #cbd5e1;
    --toggle-on: var(--accent);
    --accent-ink: #16689c;
    --field-border: #848e9c;
    --emerald-ink: #147534;

      }
}

/* ── Global Reset ── */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ── Form controls take the page face ──
   Buttons, inputs, selects and textareas do not inherit font-family. The user
   agent gives them their own, which on Windows resolves to Arial, and that
   silently beat the stack on <body> for every control the site did not set a
   family on itself. Only .audio-btn did, so it was the one button rendering in
   Vazirmatn while "ارسال بازخورد" on the feedback form, the copy buttons on
   feedback and goodbye, and the platform dropdown all rendered in Arial: wrong
   letterforms, wrong metrics, and a label that would not sit where every other
   label on the page sits. Stated for the whole element set rather than per
   component so the next control added gets it too.
   font-family only, not the font shorthand: the shorthand would also reset
   font-size and line-height and quietly override the component rules below. */
button,
input,
select,
textarea {
  font-family: inherit;
}

/* Explicit heading rhythm. Never inherit `normal`: Persian descenders
   (ی، ج، چ) crowd and clip against tight line boxes in the display faces. */
h1, h2, h3, h4, h5, h6 {
  line-height: 1.45;
}

html {
  scroll-behavior: smooth;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ── Skip Link: lets keyboard users bypass the header controls ── */
.skip-link {
  position: fixed;
  inset-inline-start: 16px;
  top: -80px;
  z-index: 200;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  background: var(--accent-solid);
  color: #ffffff;
  font-size: 14px;
  font-weight: 700;
  text-decoration: none;
  box-shadow: 0 8px 24px -6px rgba(33, 136, 202, 0.6);
  transition: top 0.18s var(--ease-standard);
}

.skip-link:focus {
  top: 12px;
}

body {
  font-family: var(--font-body);
  color: var(--text-1);
  background:
    radial-gradient(120% 80% at 50% -10%, var(--bg-ambient-2) 0%, transparent 60%),
    var(--bg-ambient-1);
  background-attachment: fixed;
  direction: rtl;
  text-align: right;
  min-height: 100vh;
  overflow-x: hidden;
  position: relative;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color 0.3s ease, color 0.3s ease;
}

/* ── Precision Structural Backdrop ── */
.backdrop-grid {
  display: none;
}

/* ── Container Layout ── */
.site-wrap {
  width: 100%;
  max-width: 1160px;
  margin: 0 auto;
  padding: 0 24px;
  position: relative;
  z-index: 1;
}

/* ── Header & Navigation (Popup Style Pill) ──
   The bar is not one pill. It is two copies of the pill, each clipped to half of
   itself, meeting at dead centre. At rest they tile into a single seamless bar,
   because the clip only ever cuts them at that join: each copy draws its border,
   radius and shadow from the full pill box, so the composite carries one outline
   and no seam.

   Dividing is each copy's inner edge sweeping outward to its own end while its
   corners round from square into a cap. The material is drawn back into the two
   ends. Nothing fades, nothing appears, nothing is replaced, and the middle of
   the bar is not hidden, it simply stops being painted. That is the whole point:
   the first version cross dissolved one pill into two, which is the cheapest
   transition there is, and it read as cheap because nothing persisted.

   The padding lives in custom properties so the two ends can be derived from the
   header's own box.

   --hdr-dur is longer while splitting than while merging, because the navigation
   has to be back under the pointer the instant someone reaches up for it. Both
   directions read the variable at the moment the class flips, so the two speeds
   never have to be written twice. */
.site-header {
  --hdr-pad-block: 12px;
  --hdr-pad-inline: 20px;
  /* The return trip is the short one: a hand reaching back for the navigation
     has already decided, so the merge plays at 360ms while the split is given
     long enough to be read as a movement. */
  --hdr-dur: 360ms;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: var(--hdr-pad-block) var(--hdr-pad-inline);
  margin-top: 18px;
  position: sticky;
  top: 18px;
  z-index: 100;
}

/* The resting surface is one pill on the bar itself. It is the bar's own
   pseudo rather than an element, so nothing has to be added to five pages of
   markup to draw it. */
.site-header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--panel);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-panel);
  transition: opacity var(--hdr-dur) var(--ease-standard);
}

/* Once the bar divides, that one pill dissolves and two of its own ends take
   its place, each drawn tight around its contents. The two surfaces are never
   both solid at the same strength, so the bar is never a pill with two extra
   pills inside it. */
.site-header.is-split::before {
  opacity: 0;
}

/* Stretched, not centred, so both ends are as tall as the row rather than as
   tall as whatever is inside them. The brand is a 34px mark inside a 44px row
   and the controls are 40px, and a pill hugging each one at its natural height
   would have given two surfaces of two different heights sitting at the same y.
   Positioned, so each end's own inset:0 below resolves against that end and not
   against the bar. */
.brand-badge,
.header-actions {
  position: relative;
  z-index: 1;
  align-self: stretch;
}

.brand-badge::before,
.header-actions::before {
  content: "";
  position: absolute;
  /* Each island is the bar's own box, not a box drawn tight around its
     contents. This is the whole of the refinement the user asked for after
     seeing the first version: "the pills after splitting need refinement".

     The first version was `inset: 0 -12px`, which made each island exactly as
     tall as the row it held (36px) instead of as tall as the bar (52px). So
     splitting did not just open a gap in the middle, it also shrank both ends
     by 16px, and the two smaller surfaces read as a different, tinnier control
     that had replaced the one the user liked rather than as that one divided.
     Worse, the 12 was only ever there to keep the logo mark's square corners
     inside a 36px pill's 18px round cap, and at 36px the mark cleared the top
     and bottom edges by 4px, so the whole island read as a tight skin.

     Derived from the bar's own padding, the two islands occupy exactly the rest
     pill's box: same height, same outer edges, same caps. The outer silhouette
     is therefore identical before and after, and the only thing the transition
     does is open the middle. At the scrolled padding of 8px block and 18px
     inline the mark has 12px of clearance top and bottom and 18px at its outer
     end, both clear of the 26px cap radius.

     Split needs y > 160 and the bar has already collapsed at y > 40, so the
     pad variables are settled by the time this is ever visible. */
  inset: calc(-1 * var(--hdr-pad-block)) calc(-1 * var(--hdr-pad-inline));
  z-index: -1;
  pointer-events: none;
  background: var(--panel);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-pill);
  /* Exactly the bar's own shadow, never a variant of it. This used to take a
     deeper drop shadow while scrolled, declared on its own, and a box-shadow is
     replaced wholesale rather than merged: the hairline ring and the
     `inset 0 1px 0` top highlight went with it. That one-pixel lit edge is what
     makes the acrylic read as glass, and losing it is what made the two
     islands look cheaper than the pill they came out of. The deeper shadow was
     also hardcoded black, which was wrong in light mode. One surface, one
     style, both themes, both states. */
  box-shadow: var(--shadow-panel);
  opacity: 0;
  transition: opacity var(--hdr-dur) var(--ease-standard);
}

.site-header.is-split .brand-badge::before,
.site-header.is-split .header-actions::before {
  opacity: 1;
}

/* The navigation is what sits between the two ends, so no width has to be
   animated to open the gap. It keeps its place in the flow while invisible,
   which is what holds the brand and the actions at the two ends of the row for
   the whole transition. Nothing reflows, so nothing can jitter. */
.nav-container {
  transition:
    opacity var(--hdr-dur) var(--ease-standard),
    transform var(--hdr-dur) var(--ease-standard),
    visibility 0s linear 0s;
}

.brand-badge {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text-1);
  transition:
    transform var(--hdr-dur) var(--ease-standard),
    opacity 0.18s ease;
}

.header-actions {
  transition: transform var(--hdr-dur) var(--ease-standard);
}

/* The logo was the only header target with no feedback at all. The dim sits on
   the contents rather than on the badge, because the badge now owns the island
   surface and fading that would look like the glass failing. */
.brand-badge:hover .brand-icon-box,
.brand-badge:hover .brand-text-col {
  opacity: 0.82;
}

/* The store icon already carries its own rounded square, so the box around it
   draws nothing: a second radius inside the first read as a circle sitting in a
   square, which is the one shape the brand mark never uses. */
.brand-icon-box {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.brand-icon-box img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.28));
}

.brand-text-col {
  display: flex;
  flex-direction: column;
}

.brand-name-fa {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 15.5px;
  letter-spacing: -0.2px;
  line-height: 1.15;
}

.brand-name-en {
  font-size: 9.5px;
  color: var(--text-3);
  font-weight: 500;
  direction: ltr;
  text-align: right;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 22px;
  list-style: none;
}

/* The list items are the flex children, so they, not the links, carry the
   height that has to stay inside the pill. */
.nav-links > li {
  display: flex;
  /* Each link slides toward whichever island is nearer while the bar divides,
     so the middle visibly comes apart in the direction the two ends are going
     instead of simply dimming. */
  transition: transform var(--hdr-dur) var(--ease-standard);
}

/* Standalone links count as targets for WCAG 2.5.8. They used to carry a 24px
   box, which only just cleared the 24x24 floor while sitting 22px apart, so a
   near miss on either side opened the neighbouring page. The box is now a full
   44px tall and the negative margin keeps the visual gap at 22px, which is the
   header's own content height, so the bar does not grow. */
.nav-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 6px;
  margin: 0 -6px;
  color: var(--text-2);
  text-decoration: none;
  font-size: 13.5px;
  font-weight: 600;
  transition: color 0.18s ease;
}

.nav-link:hover {
  color: var(--accent);
}

/* The current page. This used to be an inline style on the link itself, which
   is why only two of the four sub-pages had it. */
.nav-link[aria-current="page"] {
  color: var(--text-1);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* ── Segmented Control (Popup Style) ── */
.seg,
.theme-segmented {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  background: var(--hairline);
  border-radius: 10px;
}

.seg-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 26px;
  border: none;
  background: transparent;
  color: var(--text-3);
  border-radius: 7px;
  cursor: pointer;
  transition: background 0.15s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.seg-btn svg {
  width: 15px;
  height: 15px;
  display: block;
}

.seg-btn:hover {
  color: var(--text-1);
}

.seg-btn.active {
  background: var(--panel-solid);
  color: var(--accent);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.18);
}

.audio-btn {
  /* No font-family here: the global form-control reset covers it, and it was
     the only control on the site that had one, which is how the rest ended up
     in Arial without anyone noticing. */
  display: flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--panel-border);
  background: var(--panel-card);
  color: var(--text-2);
  border-radius: var(--radius-pill);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease, border-color 0.18s ease;
}

.audio-icon-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
}

.audio-btn:hover {
  background: var(--panel-solid);
  color: var(--text-1);
  border-color: var(--panel-border-hover);
}

/* Buttons use a fixed height so icon and text-only variants match exactly. */
.btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 42px;
  background-color: var(--accent-solid);
  background-image: linear-gradient(135deg, var(--accent-solid) 0%, var(--accent-solid-deep) 100%);
  color: #ffffff;
  text-decoration: none;
  padding: 0 20px;
  border-radius: var(--radius-pill);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1;
  border: none;
  box-shadow: 0 4px 14px -2px rgba(33, 136, 202, 0.5);
  cursor: pointer;
  transition: transform 0.18s var(--ease-standard), box-shadow 0.18s var(--ease-standard);
}

.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 20px -2px rgba(33, 136, 202, 0.65);
}

.btn-primary:active {
  transform: translateY(1px);
}

/* ── Tactile Ripple Feedback ── */
.ripple-btn {
  position: relative;
  overflow: hidden;
}

.ripple {
  position: absolute;
  border-radius: 50%;
  background: rgba(33, 150, 243, 0.4);
  transform: scale(0);
  animation: ripple-out 0.6s ease-out;
  pointer-events: none;
}

@keyframes ripple-out {
  to { transform: scale(3); opacity: 0; }
}

/* ── Blue Fade-In Reveal Animation & Atmospheric Light ── */
.hero-glow-beam {
  position: absolute;
  top: -100px;
  left: 50%;
  transform: translateX(-50%);
  width: min(850px, 90vw);
  height: 320px;
  background: radial-gradient(ellipse at center, rgba(56, 189, 248, 0.24) 0%, rgba(14, 165, 233, 0.1) 40%, rgba(2, 132, 199, 0.02) 65%, transparent 80%);
  filter: blur(52px);
  pointer-events: none;
  z-index: 0;
  animation: blueAuraPulse 7s ease-in-out infinite alternate;
}

@keyframes blueAuraPulse {
  0% {
    opacity: 0.75;
    transform: translateX(-50%) scale(0.96);
  }
  100% {
    opacity: 1;
    transform: translateX(-50%) scale(1.04);
  }
}

@keyframes titleLensDrift {
  0% {
    filter: blur(7px);
    opacity: 0;
  }
  100% {
    filter: blur(0);
    opacity: 1;
  }
}

@keyframes blueFadeInUp {
  0% {
    opacity: 0;
    transform: translateY(24px);
    filter: blur(6px);
    text-shadow: 0 0 35px rgba(56, 189, 248, 0.75);
  }
  50% {
    filter: blur(1px);
    text-shadow: 0 0 20px rgba(56, 189, 248, 0.45);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
    text-shadow: 0 0 0px transparent;
  }
}

@keyframes blueShimmer {
  0% { background-position: 0% center; }
  50% { background-position: 100% center; }
  100% { background-position: 0% center; }
}

/* ── Hero Section (Window to the Extension) ──
   The whole pitch and the whole demo window are meant to be one screen. The
   window is 820px of fixed height, so on any normal laptop it ran off the bottom
   and the visitor met a cropped mock of a chat window as their first impression
   of the product. The section is now exactly one viewport tall, the block above
   the window tightens on short screens instead of holding its size, and the
   window takes whatever is left. The floor on the window is what stops that from
   producing a slot too small to read: below it the section grows and the page
   scrolls again, which is the honest fallback. */
/* The headline and nothing else. It used to be a full viewport tall with the
   demo window filling whatever was left, which meant the window and the pitch
   competed for the same screen and the window lost: on a laptop it came out
   under 480px tall and cut the transcript off part way down. The window has its
   own section below now, so this one is simply as tall as its contents. */
.hero-section {
  position: relative;
  /* Ruled for a hero that still had the demo window inside it, where every
     vertical pixel the headline spent was a pixel the window could not have
     (22/24 padding, 18px gap). The window left for its own section and the hero
     became a thin band above it: 268px on a 772px viewport, which the user read
     correctly as "much more compact and not free". Nothing above it competes
     for the height now, so it gets its breathing room back. Measured on a
     772px viewport this lands at 330px, and the demo still shows its top 350px
     above the fold. */
  padding: clamp(44px, 7.5vh, 88px) 0 clamp(40px, 6.6vh, 80px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 3vh, 34px);
}

/* ── The demo section ──
   One viewport tall, and the ordinary content column wide.

   Tall, because the window's contents need about 865px and a shorter section is
   a section with something cut off. The user asked for exactly this and nothing
   else: "I just wanted the height of the mockup to fit my screen".

   Column width, because the window is a window: it has a 260px sidebar and a
   768px conversation column that are fixed in the real client, so a wider
   window does not show more app, it just parks the two of them further apart
   and leaves black between them. It was taken full bleed once and that is
   exactly what happened, which is what the user was looking at when they said
   "why did you leave all this space empty just to widen the mockup".

   A definite height rather than one that follows the window, because
   initWindowFit() measures this box to work out the scale, and a box sized by
   its own contents is a circular measurement. */
.demo-section {
  height: calc(100svh - 76px);
  min-height: 520px;
  display: flex;
  justify-content: center;
}

.hero-headline-wrap {
  position: relative;
  z-index: 1;
  text-align: center;
  max-width: 840px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(18px, 2.6vh, 28px);
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 900;
  /* The vh term was there to hand the demo window its height back on a short
     screen, which no longer applies, but it still keeps the headline from
     filling a short viewport. At 850px tall it never binds and the headline is
     the 48px it always was. */
  font-size: clamp(28px, min(4.8vw, 5.8vh), 48px);
  /* Deep Persian descenders in the display faces need more air than the
     1.25 this used to carry, or خط and ی collide with the next line. */
  line-height: 1.42;
  letter-spacing: -0.5px;
  color: var(--text-1);
  overflow: visible;
  padding-bottom: 8px;
}

.hero-title:not(.has-lens-drift) {
  animation: titleLensDrift 1.2s ease-in-out both;
}

.hero-title.has-lens-drift .lens-drift-word {
  display: inline-block;
  animation: titleLensDrift 1.2s ease-in-out both;
  animation-delay: calc(var(--i, 0) * 0.18s);
  will-change: filter, opacity;
  overflow: visible;
  padding-bottom: 0.22em;
  margin-bottom: -0.22em;
  padding-top: 0.06em;
  margin-top: -0.06em;
  vertical-align: baseline;
}

.hero-title .accent-text,
.accent-text {
  background: linear-gradient(135deg, #38bdf8 0%, #0ea5e9 45%, #60a5fa 75%, #38bdf8 100%);
  background-size: 200% auto;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: blueShimmer 4s ease-in-out infinite;
  display: inline-block;
  text-shadow: none;
  filter: drop-shadow(0 0 24px rgba(56, 189, 248, 0.4));
  padding-bottom: 0.22em;
  margin-bottom: -0.22em;
  padding-top: 0.06em;
  margin-top: -0.06em;
  line-height: inherit;
  overflow: visible;
  vertical-align: baseline;
}

.hero-desc {
  font-size: clamp(15.5px, 2.1vw, 18px);
  line-height: 1.75;
  color: var(--text-2);
  max-width: 680px;
  animation: blueFadeInUp 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.15s both;
}

.hero-cta-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 12px;
  animation: blueFadeInUp 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.3s both;
}

.hero-reassurance-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 18px;
  font-size: 13px;
  color: var(--text-2);
  animation: blueFadeInUp 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.45s both;
}

@media (prefers-reduced-motion: reduce) {
  .hero-glow-beam,
  .hero-title,
  .lens-drift-word,
  .fx-focus b,
  .hero-title .accent-text,
  .hero-desc,
  .hero-cta-row,
  .hero-reassurance-row {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }

  /* The logo marquee is decorative and loops forever. WCAG 2.2.2 wants a
     pause control; hover-to-pause does not exist for keyboard or touch, so
     reduced motion stops it outright and the first set reads as a static row. */
  .marquee-track {
    animation: none !important;
  }

  /* The local-processing dial is a static ring; nothing to disable there. */

  /* The header still shrinks and still divides on scroll, it just arrives
     there in one frame instead of easing into it. */
  .site-header,
  .site-header::before,
  .brand-badge,
  .brand-badge::before,
  .brand-icon-box,
  .brand-text-col,
  .brand-name-fa,
  .header-actions,
  .header-actions::before,
  .nav-container,
  .nav-links > li,
  .nav-link,
  .seg-btn,
  .audio-btn,
  .site-header .btn-primary {
    transition: none !important;
  }
}

/* Fallback for engines without background-clip: text, so the accent word
   stays legible even when the shimmer gradient does not paint.
   The .hero-title size and leading live in one block above; a second
   declaration here used to win with line-height 1.25, which is tight enough
   that Persian descenders on one line collide with the next. */
.hero-title .accent-text {
  color: var(--accent);
}

.hero-desc {
  font-size: clamp(15px, 2vw, 17.5px);
  line-height: 1.75;
  color: var(--text-2);
  max-width: 660px;
}

.hero-cta-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 6px;
}

.hero-reassurance-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 14px;
  font-size: 13px;
  color: var(--text-2);
}

.reassurance-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.reassurance-sep {
  opacity: 0.35;
}

.kbd-hint {
  font-family: var(--font-code);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--text-2);
  background: var(--hairline);
  border: 1px solid var(--panel-border);
  padding: 3px 8px;
  border-radius: 6px;
  display: inline-flex;
  align-items: center;
  line-height: 1.2;
}

@media (max-width: 640px) {
  .kbd-hint {
    display: none;
  }
}

.btn-cta-large {
  height: 52px;
  padding: 0 30px;
  font-size: 15.5px;
  border-radius: var(--radius-pill);
}

.btn-secondary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 42px;
  padding: 0 22px;
  background: var(--panel-card);
  color: var(--text-1);
  /* The frosted fill barely separates this button from the panel it sits on,
     so the outline is what identifies it. --panel-border is a decorative
     hairline near 1.2:1; --field-border is the token this system reserves for
     anything a visitor has to aim at, and it clears the 3:1 of WCAG 1.4.11. */
  border: 1px solid var(--field-border);
  border-radius: var(--radius-pill);
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  transition: background 0.18s ease, border-color 0.18s ease;
}

.btn-secondary:hover {
  background: var(--panel-solid);
  border-color: var(--panel-border-hover);
}

/* Keep the hero and CTA-banner pairs optically matched to their large sibling */
.hero-cta-row .btn-secondary,
.cta-banner-glass .btn-secondary {
  height: 52px;
  padding: 0 26px;
  font-size: 15px;
}

/* ── Hero Window Panel (Popup Style Glass Frame) ── */
/* The demo window is a picture of an app, so it is sized like one: drawn once at
   a fixed 1112x560 and then scaled down whole until it fits the space under the
   headline. Letting it reflow to that space instead made it shorter without
   making it smaller, which pushed the window's own bottom edge off-screen and
   cut the transcript through a line of code. A uniform scale keeps every
   proportion, every measurement taken off the real client, and the whole window
   in view at once. initWindowFit() measures the space and writes --win-scale. */
.glass-window-panel {
  width: 100%;
  height: 100%;
  max-width: 1112px;
  flex: 0 0 auto;
  position: relative;
  background: var(--panel);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-window);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
  /* hidden, then clip: clip does not make the panel a scroll container, so a
     stray focus or wheel event can never slide the stage inside its own frame.
     Browsers without clip keep the line above. */
  overflow: clip;
  transition: background-color 0.3s ease, border-color 0.3s ease;
}

/* Absolute, and pinned with physical edges rather than logical ones. The stage
   is wider than the panel by design, and the page is RTL, so in flow it would
   overflow to the LEFT: the panel would then clip the stage's left edge, which
   is the sidebar, and scale it about a corner that is no longer on screen.
   Taking it out of flow and pinning left:0 top:0 puts the stage's top-left
   corner exactly on the panel's, in either direction. */
.window-stage {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  /* Auto, so the stage is exactly as tall as the conversation it holds and the
     scale initWindowFit() works out is measured against the real thing rather
     than a number kept in two places. */
  height: auto;
  display: flex;
  flex-direction: column;
  transform: scale(var(--win-scale, 1));
  transform-origin: 0 0;
}

/* Window Titlebar */
.window-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  /* 10 rather than 12 so the taller switch below does not make the strip taller
     than it was. The control grows; the strip does not. */
  padding: 10px 18px;
  border-bottom: 1px solid var(--hairline);
  background: var(--panel-card);
}

.window-master-row {
  display: flex;
  align-items: center;
  gap: 12px;
}

.master-meta {
  display: flex;
  align-items: center;
}

.master-label {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
  line-height: 1.2;
}

/* ── Toggle Switch (macOS Tactile Spring-Bounce Style) ── */
.toggle {
  position: relative;
  display: inline-block;
  width: 54px;
  height: 30px;
  flex-shrink: 0;
  cursor: pointer;
  direction: ltr;
  user-select: none;
  -webkit-user-select: none;
  vertical-align: middle;
}

.toggle input {
  opacity: 0;
  width: 0;
  height: 0;
  position: absolute;
  pointer-events: none;
  margin: 0;
}

.toggle .slider {
  position: absolute;
  inset: 0;
  background: var(--toggle-off);
  border-radius: var(--radius-pill);
  transition: background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s ease;
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
}

.toggle .slider::before {
  content: "";
  position: absolute;
  width: 26px;
  height: 26px;
  left: 2px;
  top: 2px;
  background: #ffffff;
  border-radius: 50%;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.25), 0 1px 2px rgba(0, 0, 0, 0.15);
  transition: transform 0.24s cubic-bezier(0.2, 0.9, 0.3, 1), width 0.18s ease, box-shadow 0.24s ease;
  will-change: transform, width;
}

.toggle:hover .slider::before {
  box-shadow: 0 3px 8px rgba(0, 0, 0, 0.3);
}

.toggle:active .slider::before {
  width: 31px;
}

.toggle input:checked + .slider {
  background: var(--emerald);
  box-shadow: 0 0 12px rgba(34, 197, 94, 0.35);
}

.toggle input:checked + .slider::before {
  transform: translate3d(24px, 0, 0);
}

.toggle input:checked:active + .slider::before {
  transform: translate3d(15px, 0, 0);
  width: 25px;
}

/* Compact Toggle for Site Cards */
.site-row .toggle {
  width: 36px;
  height: 20px;
}

.site-row .toggle .slider::before {
  width: 16px;
  height: 16px;
  left: 2px;
  top: 2px;
  transition: transform 0.24s cubic-bezier(0.2, 0.9, 0.3, 1), width 0.18s ease;
  will-change: transform, width;
}

.site-row .toggle:active .slider::before {
  width: 19px;
}

.site-row .toggle input:checked + .slider::before {
  transform: translate3d(16px, 0, 0);
}

.site-row .toggle input:checked:active + .slider::before {
  transform: translate3d(13px, 0, 0);
  width: 19px;
}


/* ══════════════════════════════════════════════════════════════════════════════
   CHAT ARENA: the real app window, measured rather than imagined
   ══════════════════════════════════════════════════════════════════════════════
   Everything inside .app-frame comes from chatgpt.com at a 1112px window on
   2026-09-24: a 260px #000000 sidebar, a 52px transparent top bar whose only
   controls sit on the far right, a 768px thread column, a #173E76 user bubble
   with a uniform 22px radius and 10px 16px inside it, flat #212121 raised
   surfaces, and a 28px-radius composer with 36px circular controls.

   The chrome stays LTR in both site themes and in both toggle states, because
   that is what the real app does. A Persian conversation does not turn the app
   around, and pretending otherwise is the mistake this rewrite exists to fix. */

.app-frame {
  /* The window keeps the app's own appearance whatever the site theme is, so
     the light theme's chat tokens are overridden for this subtree only. */
  --app-canvas: #000000;
  --app-sidebar: #000000;
  --app-surface: #212121;
  --app-text: #ffffff;
  --app-dim: #afafaf;
  --app-chip: #424242;
  --app-accent: #63a8f8;
  --app-voice: #2c67c5;
  --app-hover: rgba(255, 255, 255, 0.09);
  --app-hairline: rgba(255, 255, 255, 0.08);
  --app-system-font: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;
  /* Surfaces whose colour is not a flat token in the app either: the user
     message bubble, the code card, and the inline chips each carry their own
     value, so they need their own name to be able to follow the theme. */
  --app-bubble: #173e76;
  --app-bubble-text: #f6fafe;
  --app-code-bg: #212121;
  --app-code-fg: #ffffff;
  --app-code-hairline: rgba(255, 255, 255, 0.05);
  --app-run-border: rgba(255, 255, 255, 0.32);
  --app-inline-code-fg: #ffffff;
  --code-kw: #c678dd;
  --code-fn: #61afef;
  --code-num: #d19a66;

  display: flex;
  direction: ltr;
  text-align: left;
  /* Auto, so the frame is exactly as tall as the conversation. It used to take
     the leftover room in a fixed-height window, which trimmed the transcript
     wherever the arithmetic landed: a table row cut in half at the bottom edge
     is what made the whole hero look broken. Nothing is trimmed now, because
     there is nothing to trim it against. */
  height: auto;
  background: var(--app-canvas);
  font-family: var(--app-system-font);
  color: var(--app-text);
}

/* The app's own light palette, read off chatgpt.com with the app in light mode:
   surface #fcfcfc, composer #ffffff, text #0d0d0d, the user bubble a pale blue
   #e8f3fe that carries a navy #0c274a. The window has to move with the site
   theme, because a black rectangle sitting in a white page reads as a picture
   of an app rather than a copy of one. The syntax colours change with it: the
   dark palette is unreadable on a light card. */
:root[data-theme="light"] .app-frame {
  --app-canvas: #fcfcfc;
  --app-sidebar: #fcfcfc;
  --app-surface: #ffffff;
  --app-text: #0d0d0d;
  --app-dim: #8f8f8f;
  --app-chip: #ececec;
  --app-accent: #0d0d0d;
  --app-voice: #3a83f7;
  --app-hover: rgba(0, 0, 0, 0.05);
  --app-hairline: rgba(0, 0, 0, 0.09);
  --app-bubble: #e8f3fe;
  --app-bubble-text: #0c274a;
  --app-code-bg: #f9f9f9;
  --app-code-fg: #0d0d0d;
  --app-code-hairline: rgba(0, 0, 0, 0.08);
  --app-run-border: rgba(0, 0, 0, 0.32);
  --app-inline-code-fg: #0d0d0d;
  --code-kw: #a626a4;
  --code-fn: #4078f2;
  --code-num: #986801;
}

/* ── Sidebar ── */
.app-sidebar {
  flex: 0 0 260px;
  width: 260px;
  background: var(--app-sidebar);
  display: flex;
  flex-direction: column;
  padding: 8px 0 12px;
  overflow: hidden;
}

/* The account row belongs on the floor of the column, the way it is in the real
   client. The sidebar stretches to the frame's height, and the frame is now as
   tall as the main column's conversation, so without this the row would sit
   wherever the last conversation left off with dead space beneath it. */
.app-side-foot {
  margin-top: auto;
}

.app-side-head {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 36px;
  padding: 0 8px 0 16px;
  margin-bottom: 10px;
}

.app-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  color: var(--app-text);
  flex-shrink: 0;
}

.app-model {
  font-size: 18px;
  font-weight: 600;
  color: var(--app-text);
  line-height: 1;
  flex: 1;
}

.app-side-search {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  color: var(--app-dim);
  flex-shrink: 0;
}

.app-side-nav {
  display: flex;
  flex-direction: column;
  padding: 0 12px;
}

.app-nav-item,
.app-chat-item {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 10px;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 400;
  color: var(--app-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.app-nav-item {
  direction: ltr;
}

.app-nav-item > svg {
  flex-shrink: 0;
}

/* The app marks conversation titles with dir="auto", so a Persian title reads
   from the right while the labels around it stay on the left. */
.app-chat-item {
  direction: rtl;
  text-align: right;
  margin: 0 12px;
  width: calc(100% - 24px);
}

.app-chat-item--current {
  background: var(--app-hover);
}

.app-side-section {
  padding: 6px 16px;
  font-size: 16px;
  color: var(--app-dim);
  line-height: 20px;
  margin-top: 12px;
}

.app-side-foot {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px 0;
}

.app-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 99999px;
  background: var(--app-voice);
  color: #ffffff;
  font-size: 12px;
  font-weight: 600;
  flex-shrink: 0;
}

/* The name sits against the avatar rather than being stretched across the row.
   As a full-width flex item with right-aligned text it left the avatar stranded
   at the far end of a 137px gap, and any name long enough to fill the row ran
   into the Upgrade label beside it. */
.app-user {
  font-size: 14px;
  color: var(--app-text);
  direction: rtl;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The trailing action is pushed to the far end of the row, which is where the
   app puts it. */
.app-upgrade {
  font-size: 13px;
  font-weight: 500;
  color: var(--app-accent);
  flex-shrink: 0;
  margin-inline-start: auto;
}

/* ── Main column ── */
.app-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.app-topbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  height: 52px;
  padding: 8px;
  flex-shrink: 0;
}

.app-topbar-btn {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 12px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 500;
  color: var(--app-text);
}

.app-topbar-btn--accent {
  color: var(--app-accent);
}

.app-topbar-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  color: var(--app-text);
}

/* ── Thread column ── */
.app-thread {
  flex: 1;
  min-height: 0;
  padding: 0 16px 8px;
  /* No fade. It existed to soften a transcript that ran past a fixed-height
     frame; the frame is now exactly as tall as the transcript, so there is
     nothing running past anything and the fade would only dim the last row. */
}

.chat-row {
  display: flex;
  width: 100%;
  max-width: 768px;
  margin-inline: auto;
}

.chat-row + .chat-row {
  margin-top: 24px;
}

.user-row {
  justify-content: flex-end;
}

/* ── User Bubble ──
   Measured off the live app: a solid #173E76 fill in dark mode and #E8F3FE in
   light, a uniform 22px radius (the real one is NOT tailed on the corner),
   10px 16px of padding, 16px text on a 24px line. */
.chat-bubble-user {
  max-width: 75%;
  padding: 10px 16px;
  background: var(--app-bubble);
  border-radius: 22px;
  font-size: 16px;
  line-height: 24px;
  color: var(--app-bubble-text);
  font-family: var(--font-body);
  direction: rtl;
  text-align: right;
  text-wrap: pretty;
}

/* ── AI Row (No Avatar, Full Width, Text Only) ── */
.ai-row {
  justify-content: flex-start;
}

.ai-response-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── The toggle, rendered as the difference it actually is ──
   Both states carry the SAME Persian content. What changes is the layout
   the browser produces with and without the extension, and every value below
   is the one the real page computes.

   OFF is the app's own CSS. The assistant list is hardcoded LTR, so a Persian
   bullet list hangs off the wrong edge with its markers on the left while the
   paragraphs above it read from the right. Latin chips lose their isolation and
   drag the punctuation around them to the wrong end of the sentence. The font
   falls back to whatever the operating system has for Persian.

   ON is what RustChin injects: the list turns around, the markers move to the
   right gutter, the chips are isolated so they stay where the sentence puts
   them, and the text is set in Vazirmatn at the size and leading the extension
   defaults to. */

/* Shared type for both states, so only the differences show. */
.ai-response-title {
  margin: 0;
  font-weight: 600;
  /* The app runs its markdown through dir="auto", so a Persian heading already
     reads from the right before RustChin exists. Leaving this to inherit the
     window's LTR would put the one line that is most obviously a heading on the
     wrong edge in both states. */
  direction: rtl;
  text-align: right;
}

.ai-prose p {
  margin: 0;
}

/* Deliberately no list-style-type here. The app numbers an ordered list 1. 2. 3.
   and only the ON state replaces those markers with Persian numerals, so
   forcing a marker in the base rule would hide half the difference. */
.ai-list {
  margin: 0;
}

/* ══════════════════════════════════════════════════════════════════════════════
   TABLE: the one block whose direction is decided per cell
   ══════════════════════════════════════════════════════════════════════════════
   The app gives a whole table one direction. RustChin detects it from the
   table's own text and then walks every th and td, so a Persian header, a Latin
   identifier and a formula can each sit the right way round inside the same
   grid. */

.ai-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
  line-height: 1.6;
  margin: 2px 0;
}

.ai-table th,
.ai-table td {
  padding: 8px 12px;
  border: 1px solid var(--app-hairline);
  text-align: start;
  vertical-align: top;
}

.ai-table th {
  color: var(--app-dim);
  font-weight: 500;
}

.ai-table td {
  color: var(--app-text);
}

.ai-table code {
  font-family: var(--font-code);
  font-size: 0.85em;
  background: var(--app-chip);
  color: var(--app-inline-code-fg);
  border-radius: 4px;
  padding: 0.1em 0.3em;
}

/* ── OFF: the bare browser ── */
.app-frame.mode-broken .ai-response-title,
.app-frame.mode-broken .ai-prose,
.app-frame.mode-broken .ai-list li {
  font-family: var(--app-system-font);
  font-size: 16px;
  line-height: 26px;
}

/* The user's own message is the app's text too, so it carries the app's font
   until the extension replaces it. */
.app-frame.mode-broken .chat-bubble-user {
  font-family: var(--app-system-font);
  font-size: 16px;
  line-height: 24px;
}

/* The prompt box is where a Persian sentence is most likely to be typed, and
   where the app is least ready for one: it stays LTR and left-aligned until
   RustChin notices the text is Persian. */
.app-frame.mode-broken .composer-draft {
  direction: ltr;
  text-align: left;
  font-family: var(--app-system-font);
  font-size: 16px;
  line-height: 24px;
}

/* One direction for the whole grid, so a Persian table starts from the left and
   every cell follows it. */
.app-frame.mode-broken .ai-table {
  direction: ltr;
  font-family: var(--app-system-font);
}

.app-frame.mode-broken .ai-table th,
.app-frame.mode-broken .ai-table td {
  direction: ltr;
  text-align: left;
}

.app-frame.mode-broken .ai-table code {
  direction: ltr;
  unicode-bidi: normal;
}

/* Sidebar conversation titles are a plain nav row, so a Persian title sits
   against the left edge in the system font. */
.app-frame.mode-broken .app-chat-item {
  direction: ltr;
  text-align: left;
  font-family: var(--app-system-font);
}

.app-frame.mode-broken .ai-prose p {
  /* The app marks its paragraphs dir="auto", so Persian still reads from the
     right. That is exactly why the left-hanging list below it looks wrong. */
  direction: rtl;
  text-align: right;
}

.app-frame.mode-broken .ai-list {
  direction: ltr;
  text-align: left;
  padding: 0 0 0 26px;
}

.app-frame.mode-broken .ai-list li {
  direction: ltr;
  text-align: left;
  padding: 0 0 0 6px;
}

.app-frame.mode-broken .ai-list li::marker {
  color: var(--app-text);
}

.app-frame.mode-broken .ai-prose code,
.app-frame.mode-broken .ai-list code {
  direction: ltr;
  unicode-bidi: normal;
}

/* ── ON: RustChin ── */
.app-frame.mode-active .ai-response-title,
.app-frame.mode-active .ai-prose,
.app-frame.mode-active .ai-list li {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.8;
}

/* Headings get their own scale in the extension, because 15px body text would
   otherwise flatten the hierarchy the app had at 16px. h3 is the level this
   demo shows: 15 x 1.2. */
.app-frame.mode-active .ai-response-title {
  font-size: 18px;
  line-height: 1.45;
}

.app-frame.mode-active .chat-bubble-user {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.8;
}

.app-frame.mode-active .composer-draft {
  direction: rtl;
  text-align: right;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.8;
}

.app-frame.mode-active .app-chat-item {
  direction: rtl;
  text-align: right;
  font-family: var(--font-body);
}

/* Direction is detected from the table's own text, so a mostly-Persian table
   turns around while an English or code table is left alone. */
.app-frame.mode-active .ai-table {
  direction: rtl;
}

.app-frame.mode-active .ai-table th,
.app-frame.mode-active .ai-table td {
  direction: rtl;
  text-align: right;
  font-family: var(--font-body);
}

/* ...except a cell whose own content is Latin. The engine runs its direction
   test on each cell separately, so a formula or an identifier is set back to
   LTR and left-aligned inside the RTL table rather than being pushed to the
   right edge with the prose. */
.app-frame.mode-active .ai-table [dir="ltr"] {
  direction: ltr;
  text-align: left;
}

.app-frame.mode-active .ai-table code {
  direction: ltr;
  unicode-bidi: isolate;
}

.app-frame.mode-active .ai-prose p {
  direction: rtl;
  text-align: right;
}

.app-frame.mode-active .ai-list {
  direction: rtl;
  text-align: right;
  padding: 0 26px 0 0;
}

/* Persian numerals for RTL ordered lists. The app numbers them 1. 2. 3. even
   when the list itself is Persian. */
.app-frame.mode-active ol.ai-list {
  list-style-type: persian;
}

.app-frame.mode-active .ai-list li {
  direction: rtl;
  text-align: right;
  padding: 0 0.375em 0 0;
}

.app-frame.mode-active .ai-list li::marker {
  color: var(--app-text);
}

.app-frame.mode-active .ai-prose code,
.app-frame.mode-active .ai-list code {
  direction: ltr;
  unicode-bidi: isolate;
}

/* ── Inline code chips ──
   Measured: #424242 at a 4px radius with 0.15em / 0.3em of padding, set one
   step below the surrounding text. */
.app-frame .ai-prose code,
.app-frame .ai-list code {
  font-family: var(--font-code);
  font-size: 0.875em;
  background: var(--app-chip);
  color: var(--app-inline-code-fg);
  border-radius: 4px;
  padding: 0.15em 0.3em;
  line-height: 1.4;
}

/* ══════════════════════════════════════════════════════════════════════════════
   CODE BLOCK: Exact ChatGPT Code Card Style
   ══════════════════════════════════════════════════════════════════════════════ */

.ai-code-card {
  background: var(--app-code-bg);
  border: 1px solid var(--app-code-hairline);
  border-radius: 16px;
  overflow: hidden;
  direction: ltr;
  text-align: left;
  margin: 6px 0;
  flex-shrink: 0;
}

/* One flat surface: the real card paints the header and the body the same
   colour and separates them with nothing at all. The label is set in the app's
   UI font, not its monospace face. */
.code-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 8px 8px 16px;
  background: var(--app-code-bg);
  font-family: var(--app-system-font);
  font-size: 13.5px;
  font-weight: 500;
  color: var(--app-dim);
}

.code-lang {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

.code-card-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.code-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 99999px;
  background: transparent;
  border: none;
  color: var(--app-dim);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.code-action-btn:hover {
  background: var(--app-hover);
  color: var(--app-code-fg);
}

/* The copy control acknowledges the press by turning into a tick. */
.code-action-btn .icon-check {
  display: none;
}

.code-action-btn.copied .icon-copy {
  display: none;
}

.code-action-btn.copied .icon-check {
  display: block;
  color: #22c55e;
}

.code-block {
  direction: ltr;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 16px 20px;
  font-family: var(--font-code);
  font-size: 14px;
  line-height: 24px;
  color: var(--app-code-fg);
  overflow-x: auto;
  box-shadow: none;
  margin: 0;
  white-space: pre;
}

.code-keyword { color: var(--code-kw); font-weight: 600; }
.code-func { color: var(--code-fn); font-weight: 500; }
.code-num { color: var(--code-num); }

/* "Run" pill, as it appears on runnable snippets in the real app: outlined,
   pill-shaped, no fill. It is a span because this mock executes nothing.
   The outline is lifted past the app's own 0.15 alpha so it clears 3:1 against
   the card instead of vanishing. */
.code-run-btn {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 12px;
  border: 1px solid var(--app-run-border);
  border-radius: 99999px;
  color: var(--app-code-fg);
  font-size: 14px;
  font-weight: 500;
  line-height: 1;
  user-select: none;
  -webkit-user-select: none;
}

/* ══════════════════════════════════════════════════════════════════════════════
   AI ACTION BAR: Exact ChatGPT Button Row
   ══════════════════════════════════════════════════════════════════════════════ */

.ai-actions-bar {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-top: 2px;
}

.ai-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: transparent;
  border: none;
  color: var(--app-dim);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.ai-action-btn:hover {
  background: var(--app-hover);
  color: var(--app-text);
}

.ai-action-btn.active {
  color: var(--app-accent);
  background: color-mix(in srgb, var(--app-accent) 14%, transparent);
}

/* ══════════════════════════════════════════════════════════════════════════════
   COMPOSER: the 52px surface the app keeps at the bottom of the thread
   ══════════════════════════════════════════════════════════════════════════════
   Measured: 768px wide, 52px tall, #212121 at a 28px radius, 8px of padding,
   a 36px circular "+" on the left, a 90px "Think" pill near the right end, a
   36px dictation button, and a 36px voice disc filled #2C67C5. */

.app-composer {
  flex-shrink: 0;
  padding: 8px 16px 16px;
}

.composer-surface {
  display: flex;
  align-items: center;
  gap: 4px;
  height: 52px;
  max-width: 768px;
  margin-inline: auto;
  padding: 0 8px;
  background: var(--app-surface);
  border-radius: 28px;
}

.composer-plus,
.composer-icon,
.composer-voice {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 99999px;
  flex-shrink: 0;
}

.composer-plus,
.composer-icon {
  color: var(--app-text);
}

.composer-voice {
  background: var(--app-voice);
  color: #ffffff;
}

.composer-placeholder {
  flex: 1;
  min-width: 0;
  padding: 0 6px;
  font-size: 16px;
  line-height: 24px;
  color: var(--app-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A draft rather than the empty box's placeholder, because the prompt box is
   one of the surfaces the extension changes and a placeholder cannot show it. */
.composer-draft {
  flex: 1;
  min-width: 0;
  padding: 0 6px;
  color: var(--app-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.composer-think {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 36px;
  padding: 0 12px;
  border-radius: 99999px;
  font-size: 14px;
  color: var(--app-dim);
  flex-shrink: 0;
}

/* ── Marquee Section ── */
.marquee-section {
  /* Tile geometry lives in tokens because the loop distance is derived from it. */
  --marquee-gap: 20px;
  --marquee-tile: 76px;
  /* Tiles per identical set. The track repeats this set THREE times, and the
     third copy is load bearing rather than belt and braces. The loop translates
     by exactly one set, so the track has to be at least one set wider than the
     window it scrolls through, or the track's trailing edge slides into view and
     the row simply runs out of tiles.
     The numbers: one set is 8 x 76 + 8 x 20 = 768px, the window is up to
     1040px, so 1808px is the minimum and two sets only reach 1516px. That 292px
     shortfall is a gap that opened on the left for the last third of every
     cycle, right where the row ends, which reads as an icon that failed to
     load. A third set takes the track to 2284px and the gap cannot happen. */
  --marquee-set: calc(8 * var(--marquee-tile) + 8 * var(--marquee-gap));
  width: 100%;
  max-width: 1040px;
  margin-top: 24px;
  overflow: hidden;
  position: relative;
  mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%);
  -webkit-mask-image: linear-gradient(to right, transparent 0%, black 12%, black 88%, transparent 100%);
}

.marquee-title-wrap {
  text-align: center;
  margin-bottom: 18px;
}

.marquee-heading {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-2);
  letter-spacing: -0.2px;
  text-align: center;
  display: inline-block;
}

.marquee-wrapper {
  display: flex;
  overflow: hidden;
  user-select: none;
  padding: 16px 0 34px;
}

.marquee-track {
  display: flex;
  align-items: center;
  gap: var(--marquee-gap);
  animation: marquee-scroll 36s linear infinite;
  width: max-content;
  /* Own compositor layer, so the loop is a transform replay rather than a
     repaint of sixteen tiles every frame. */
  will-change: transform;
  backface-visibility: hidden;
}

.marquee-wrapper:hover .marquee-track {
  animation-play-state: paused;
}

/* Scroll by exactly ONE set, expressed in tiles rather than as a percentage.
   A percentage of the track cannot be right: the track holds 24 tiles and
   therefore only 23 gaps, so a third of its width is 8 tiles + 7.66 gaps while
   one set is 8 tiles + 8 gaps, and the row snaps back by 7px at the top of
   every loop. */
@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(var(--marquee-set)); }
}

/* No backdrop-filter here on purpose. Sixteen blurred tiles animating inside a
   masked, transformed track is a compositor cliff: Chrome has to re-sample every
   backdrop each frame, and while it catches up the tiles on the far side of the
   loop come up blank, which is why the row looked like it was still loading.
   The backdrop behind these tiles is a flat gradient, so the blur bought almost
   nothing visually. An opaque surface reads the same and paints instantly. */
.marquee-brand-item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--marquee-tile);
  height: var(--marquee-tile);
  background: var(--panel-card-solid);
  border: 1px solid var(--panel-border);
  border-radius: 22px;
  transition: transform 0.24s cubic-bezier(0.2, 0, 0, 1), border-color 0.2s ease, box-shadow 0.24s ease, background 0.2s ease;
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04);
  flex-shrink: 0;
}

.marquee-brand-item:hover {
  transform: translateY(-4px);
  border-color: var(--brand-color, var(--accent));
  box-shadow: 0 10px 24px -2px var(--brand-glow, rgba(56, 189, 248, 0.35)), inset 0 1px 0 rgba(255, 255, 255, 0.12);
  background: var(--panel-solid);
  z-index: 5;
}

.marquee-brand-item svg,
.marquee-brand-item img {
  width: 40px;
  height: 40px;
  object-fit: contain;
  transform: scale(var(--logo-scale, 1));
  transition: transform 0.24s ease;
}

.marquee-brand-item:hover svg,
.marquee-brand-item:hover img {
  transform: scale(calc(var(--logo-scale, 1) * 1.06));
}

/* ── Optical size normalisation for the platform marquee ──
   Every logo is drawn in its own viewBox with its own internal padding, so a
   uniform 40px box renders eight different visual weights (NotebookLM's ink
   fills 63% of its 512 grid, Claude's fills 100% of a 24 grid). Each factor
   below equalises ink AREA, not bounding box, which is what the eye reads.
   Measured from getBBox() on the live SVGs; re-measure if a logo is swapped. */
.marquee-brand-item[title="ChatGPT"]     { --logo-scale: 0.94; }
.marquee-brand-item[title="Claude"]      { --logo-scale: 0.94; }
.marquee-brand-item[title="Google Gemini"] { --logo-scale: 1.02; }
.marquee-brand-item[title="NotebookLM"]  { --logo-scale: 1.22; }
.marquee-brand-item[title="DeepSeek"]    { --logo-scale: 1.09; }
.marquee-brand-item[title="Copilot"]     { --logo-scale: 0.98; }
.marquee-brand-item[title="Perplexity"]  { --logo-scale: 1.01; }
.marquee-brand-item[title="Poe"]         { --logo-scale: 1.07; }

.marquee-tooltip {
  position: absolute;
  bottom: -28px;
  left: 50%;
  transform: translateX(-50%) translateY(4px);
  background: var(--panel-solid);
  border: 1px solid var(--panel-border);
  color: var(--text-1);
  padding: 3px 9px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
  z-index: 10;
}

.marquee-brand-item:hover .marquee-tooltip {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

@media (max-width: 640px) {
  .marquee-brand-item {
    width: 62px;
    height: 62px;
    border-radius: 18px;
  }
  .marquee-brand-item svg,
  .marquee-brand-item img {
    width: 32px;
    height: 32px;
  }
}

.site-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 12px;
  background: var(--hairline);
  border: 1px solid var(--panel-border);
  border-radius: 11px;
  transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}

.site-row:hover {
  background: var(--panel-solid);
  border-color: var(--panel-border-hover);
}

.site-info {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  overflow: hidden;
}

.site-icon {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  flex-shrink: 0;
}

.site-meta {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.site-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--text-1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.site-domain {
  font-size: 9px;
  color: var(--text-3);
  direction: ltr;
  text-align: right;
}

.site-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--site-color, var(--accent));
  box-shadow: 0 0 5px var(--site-color, var(--accent));
  flex-shrink: 0;
}

/* Signature Platform Switch Colors */
.site-row[data-host="chatgpt.com"] .toggle input:checked + .slider {
  background: #111111;
}
:root[data-theme="dark"] .site-row[data-host="chatgpt.com"] .toggle input:checked + .slider {
  background: #ffffff;
}
:root[data-theme="dark"] .site-row[data-host="chatgpt.com"] .toggle input:checked + .slider::before {
  background: #111111;
}

.site-row[data-host="gemini.google.com"] .toggle input:checked + .slider {
  background: linear-gradient(135deg, #1BA1E2 0%, #4285F4 25%, #9B72CF 65%, #D96570 100%);
}

.site-row[data-host="claude.ai"] .toggle input:checked + .slider {
  background: #d97706;
}

.site-row[data-host="deepseek.com"] .toggle input:checked + .slider {
  background: #2563eb;
}

/* ── Section Structure ── */
/* Vertical rhythm: every top-level band sits 80px below the previous one
   (24px of bottom padding + 56px of top padding), so the page reads as one
   steady cadence instead of the 50/60/70/90 drift it had before. */
.section-wrap {
  padding: 56px 0 24px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.section-head {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  max-width: 680px;
  margin: 0 auto;
}

.section-tag {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  color: var(--accent-ink);
  margin-bottom: 2px;
}

.section-title {
  font-family: var(--font-display);
  font-size: clamp(24px, 3.5vw, 34px);
  font-weight: 800;
  color: var(--text-1);
}

.section-desc {
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-2);
}

/* ── Feature rows ──
   Three full-width rows separated by whitespace and a single hairline. No
   cards, no fills, no shadows: the strongest sites in this category present a
   small set of claims as rows, and reserve bordered boxes for things a visitor
   can actually click. The rule above each row starts after the icon rail so it
   never cuts through the icons. */
.feature-rows {
  display: flex;
  flex-direction: column;
  /* 730px is not a round number on purpose. The three columns need 705px to sit
     on one line, and the block is only as wide as the widest row plus about
     twenty five pixels of slack. Centred, that slack is the entire difference
     between the left and right margins, so the row reads as balanced against
     the centred heading above it. At 900px the descriptions stopped well short
     of the left edge and the content read as right aligned under a centred
     title: two layouts stacked rather than one section. Widen this and the
     imbalance comes straight back. */
  max-width: 730px;
  margin-inline: auto;
  width: 100%;
}

/* One line per claim. Icon, title and description each get their own column and
   all three sit on the same baseline, so the whole section is three rules and
   three sentences rather than three blocks of text. The copy is kept to roughly
   sixty characters precisely so it stays on one line at this width; if a
   description ever needs a second line, the description is too long, not the
   column too narrow. */
.feature-row {
  position: relative;
  display: grid;
  grid-template-columns: 40px minmax(0, 212px) minmax(0, 1fr);
  align-items: center;
  gap: 22px;
  padding: 17px 0;
}

/* The rule spans the block edge to edge, which is what ties the three rows into
   one centred table. It used to stop 62px short on the icon side, and that one
   sided gap was most of why the block looked pinned to the right even after the
   block itself was centred: the longest line in the section was a rule that
   only ran three quarters of the way across. The rules sit at each row's top
   edge, well clear of the icons, so there is nothing to cut through. */
.feature-row + .feature-row::before {
  content: "";
  position: absolute;
  top: 0;
  inset-inline: 0;
  height: 1px;
  background: var(--hairline);
}

.feature-row-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  color: var(--accent-ink);
}

.feature-row-title {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  line-height: 1.5;
  color: var(--text-1);
  margin: 0;
}

.feature-row-desc {
  font-size: 14px;
  line-height: 1.75;
  color: var(--text-2);
  margin: 0;
  text-wrap: pretty;
}

/* Below this width the row cannot hold three columns without the description
   wrapping, so it splits into two: the icon keeps its own rail and the title
   and description stack in the text column. */
@media (max-width: 860px) {
  .feature-row {
    grid-template-columns: 36px minmax(0, 1fr);
    align-items: start;
    gap: 4px 14px;
    padding: 16px 0;
  }

  .feature-row-icon {
    grid-row: 1 / span 2;
    width: 36px;
    height: 36px;
    margin-top: 1px;
  }

  .feature-row-title {
    grid-column: 2;
    font-size: 16px;
  }

  .feature-row-desc {
    grid-column: 2;
    font-size: 13.5px;
  }

  /* The rule still spans the row here. It is drawn on a two column layout, so
     insetting it would leave a stub hanging off the icon rail. */
}

/* ── FAQ ──
   An accordion that animates its own height without measuring anything. The
   wrapper is a one-column grid and the panel moves between `0fr` and `1fr`;
   grid interpolates that, so there is no scrollHeight read, no reflow on every
   frame, and no jump when the answer turns out to be a different length than
   the last one. Opening does not close the others: a visitor comparing two
   answers should not have to reopen the first. */
.faq-list {
  display: flex;
  flex-direction: column;
}

.faq-item {
  position: relative;
}

.faq-item + .faq-item::before {
  content: "";
  position: absolute;
  top: 0;
  inset-inline: 0;
  height: 1px;
  background: var(--hairline);
}

.faq-head {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}

.faq-q {
  display: flex;
  align-items: center;
  gap: 20px;
  width: 100%;
  padding: 20px 0;
  background: none;
  border: 0;
  font-family: var(--font-display);
  font-size: 16.5px;
  font-weight: 700;
  line-height: 1.6;
  color: var(--text-1);
  text-align: start;
  cursor: pointer;
  transition: color 0.18s var(--ease-standard);
}

.faq-q:hover {
  color: var(--accent-ink);
}

/* The plus spins half a turn and drops its vertical bar on the way, so the
   open state is a minus the eye arrives at rather than one that snaps in. */
.faq-mark {
  position: relative;
  display: block;
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  /* Leading edge, not trailing. The feature rows put their icons on the right
     and the setup steps put their numerals there, so the control that opens a
     row belongs on the same side, where the eye already is. order:-1 places it
     visually first while the DOM keeps the question ahead of it, which is what
     a screen reader should hear. */
  order: -1;
  border: 1px solid var(--field-border);
  border-radius: 99999px;
  color: var(--text-2);
  transition: transform 0.42s var(--ease-standard), border-color 0.24s ease,
              background 0.24s ease, color 0.24s ease;
}

.faq-mark i {
  position: absolute;
  top: 50%;
  left: 25%;
  width: 50%;
  height: 1.8px;
  border-radius: 2px;
  background: currentColor;
  transform: translateY(-50%);
}

.faq-mark i:nth-child(2) {
  transform: translateY(-50%) rotate(90deg);
  transition: transform 0.42s var(--ease-standard);
}

.faq-item.is-open .faq-mark {
  transform: rotate(180deg);
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.faq-item.is-open .faq-mark i:nth-child(2) {
  transform: translateY(-50%) rotate(90deg) scaleX(0);
}

.faq-a {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  /* visibility rather than aria-hidden: it keeps the collapsed text out of the
     accessibility tree, which grid-template-rows alone does not, and the delay
     holds it visible for the length of the collapse so the animation is seen. */
  visibility: hidden;
  transition: grid-template-rows 0.42s var(--ease-standard), visibility 0s linear 0.42s;
}

.faq-item.is-open .faq-a {
  grid-template-rows: 1fr;
  visibility: visible;
  transition: grid-template-rows 0.42s var(--ease-standard), visibility 0s linear 0s;
}

/* The grid item, not the container, is what has to collapse. A grid track of
   0fr still sizes to the item's automatic minimum, and that minimum includes
   padding, so leaving the gap here reserved ~18px of phantom height under every
   closed question. overflow:hidden drops the automatic minimum to zero and the
   spacing moved onto the last child, where it is clipped with everything else. */
.faq-a-inner {
  min-height: 0;
  overflow: hidden;
  padding-inline-start: 54px;
  opacity: 0;
  transform: translateY(-5px);
  transition: opacity 0.3s ease 0.08s, transform 0.3s var(--ease-standard) 0.08s;
}

.faq-item.is-open .faq-a-inner {
  opacity: 1;
  transform: none;
}

.faq-a-inner > :last-child {
  margin-bottom: 22px;
}

.faq-a-inner p {
  font-size: 14.5px;
  line-height: 1.95;
  color: var(--text-2);
  margin: 0;
  /* Deliberately no max-width. Every answer is written to land on a single
     line; a measure cap here only produced a ragged three line block in the
     middle of an otherwise empty row. */
}

@media (max-width: 640px) {
  .faq-q {
    gap: 14px;
    font-size: 15.5px;
    padding: 18px 0;
  }

  .faq-mark {
    width: 30px;
    height: 30px;
  }

  .faq-a-inner {
    padding-inline-start: 44px;
  }

  .faq-a-inner > :last-child {
    margin-bottom: 18px;
  }
}

/* Nothing here needs to move to be understood, so under reduced motion the
   accordion simply changes state. Transitioning only the duration would have
   left the visibility delay in place and held a collapsed answer visible. */
@media (prefers-reduced-motion: reduce) {
  .faq-mark,
  .faq-mark i,
  .faq-a,
  .faq-a-inner {
    transition: none;
  }

  .metric-slider::-webkit-slider-thumb {
    transition: none;
  }

  .stepper-val.tick {
    animation: none;
  }
}

/* ── Interactive Font Playground (Popup Font-Row Style) ── */
.font-playground-card {
  background: var(--panel);
  backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-panel);
  padding: 24px;
  box-shadow: var(--shadow-panel);
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Two rows rather than one wrapping row. With the sliders at a usable width the
   bar no longer fits on one line, and space-between broke it into a pills row
   plus two metric rows hugging the right edge, which read as three unrelated
   things. Pills on top, the two metrics paired underneath. */
.font-controls-bar {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--hairline);
}

.font-metrics-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px 28px;
}

/* Centred, on the panel's own axis. The five faces are one choice, and the two
   metrics below them are spread to the two edges, so a pills row parked on the
   right edge read as a third column rather than as the top of the same control
   bar. Centred, the pills answer to the heading and the metrics frame them. */
.font-tabs-wrap {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex-wrap: wrap;
}

/* An opaque chip, not an 8% wash: the fill is what makes the control
   findable, and a translucent fill on a glass panel has almost no colour of
   its own to read against. */
.font-tab-pill {
  padding: 8px 14px;
  min-height: 36px;
  background: var(--panel-solid);
  border: 1px solid var(--field-border);
  color: var(--text-2);
  border-radius: 9px;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: all 0.18s ease;
}

.font-tab-pill:hover {
  color: var(--text-1);
  background: var(--panel-solid);
}

.font-tab-pill.active {
  background: var(--accent-solid);
  color: #ffffff;
  border-color: var(--accent-solid);
  box-shadow: 0 2px 8px rgba(33, 136, 202, 0.4);
}

/* Stepper Control from Popup */
.font-stepper-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
}

.font-metric-label {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--text-2);
  min-width: 64px;
  text-align: end;
}

/* ── Metric sliders ──
   One style for both metrics, so size and leading read as a pair rather than as
   two unrelated widgets, and drawn from nothing rather than left as the browser
   default with an accent-color on top. A native range input is the only control
   on this page that looked like it belonged to Chrome instead of the product.

   The filled portion is a gradient painted on the track, because a range input
   has no element between its two ends to give a background to. `--fill` is the
   percentage main.js writes on every change and once at init.

   RTL is load bearing here. Chrome flips a range input for the inherited
   direction, so the minimum sits on the right, and the gradient therefore has
   to grow towards the left or the fill would run away from the thumb. Firefox
   paints its own progress element instead, so it skips the gradient. */
.metric-slider {
  --fill: 0%;
  --slider-groove: color-mix(in srgb, var(--text-3) 36%, transparent);
  width: 148px;
  height: 32px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.metric-slider::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 99999px;
  background: linear-gradient(
    to left,
    var(--accent-solid) 0 var(--fill),
    var(--slider-groove) var(--fill) 100%
  );
}

.metric-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  margin-top: -6px;
  border: 2px solid var(--accent-solid);
  border-radius: 99999px;
  background: var(--panel-solid);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.32);
  transition: transform 0.18s var(--ease-standard), box-shadow 0.18s var(--ease-standard);
}

.metric-slider:hover::-webkit-slider-thumb {
  transform: scale(1.12);
}

/* The halo is the press feedback. It reads at a glance on a control this small
   without growing the thumb enough to jump under the pointer. */
.metric-slider:active::-webkit-slider-thumb {
  transform: scale(1.18);
  box-shadow: 0 0 0 8px var(--accent-soft);
}

.metric-slider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 99999px;
}

.metric-slider:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 5px var(--accent-soft);
}

.metric-slider::-moz-range-track {
  height: 6px;
  border-radius: 99999px;
  background: var(--slider-groove);
}

.metric-slider::-moz-range-progress {
  height: 6px;
  border-radius: 99999px;
  background: var(--accent-solid);
}

.metric-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border: 2px solid var(--accent-solid);
  border-radius: 99999px;
  background: var(--panel-solid);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.32);
}

.font-size-stepper {
  display: inline-flex;
  align-items: center;
  background: var(--hairline);
  border: 1px solid var(--panel-border);
  border-radius: 9px;
  padding: 2px;
  gap: 2px;
  height: 32px;
}

.stepper-btn {
  width: 28px;
  height: 26px;
  border: none;
  background: transparent;
  color: var(--text-2);
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}

.stepper-btn:hover {
  background: var(--panel-solid);
  color: var(--text-1);
}

.stepper-val {
  display: inline-block;
  font-size: 12.5px;
  font-weight: 700;
  padding: 0 8px;
  color: var(--text-1);
  min-width: 48px;
  text-align: center;
  /* The readout changes on every pixel of a drag. Tabular figures are what stop
     the whole control twitching sideways as a 1 becomes a 4. */
  font-variant-numeric: tabular-nums;
}

/* Restarted by main.js on every change, so it tracks the drag like a dial
   rather than firing once. */
.stepper-val.tick {
  animation: stepper-tick 0.22s var(--ease-standard);
}

@keyframes stepper-tick {
  0% { transform: scale(1); }
  45% { transform: scale(1.14); }
  100% { transform: scale(1); }
}

.font-preview-area {
  padding: 22px;
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-card);
  font-size: 15px;
  line-height: 1.8;
  color: var(--text-1);
  min-height: 120px;
  transition: font-size 0.15s ease, line-height 0.15s ease;
}

/* ── Security & Privacy Section ──
   Three statements and a link. There is no server, no account and no
   telemetry, so a percentage dial and a badge would only be restating the
   sentences next to them. */
.privacy-strip {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  background: var(--panel);
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
}

.privacy-item {
  padding: 26px 28px;
}

/* The divider sits on the inline-start edge, which is the right-hand side in
   this layout, so the first item keeps a clean outer edge. */
.privacy-item + .privacy-item {
  border-inline-start: 1px solid var(--hairline);
}

.privacy-item b {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
  margin-bottom: 6px;
}

.privacy-item span {
  display: block;
  font-size: 13.5px;
  line-height: 1.65;
  color: var(--text-2);
}

.privacy-more {
  /* .section-wrap is a column flex container, so without this the link would
     stretch to the full width and make the whole row clickable. The padding is
     there to lift the 22px line box over the 24px target floor. */
  align-self: center;
  display: inline-block;
  margin-top: 4px;
  padding: 3px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--accent-ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.18s ease;
}

.privacy-more:hover {
  border-bottom-color: currentColor;
}

@media (max-width: 760px) {
  .privacy-strip {
    grid-template-columns: 1fr;
  }

  .privacy-item {
    padding: 20px 22px;
  }

  .privacy-item + .privacy-item {
    border-inline-start: none;
    border-top: 1px solid var(--hairline);
  }
}

/* ── Install CTA Banner ── */
.cta-banner-glass {
  background: var(--panel);
  backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-panel);
  padding: 42px 32px;
  box-shadow: var(--shadow-panel);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  margin: 56px 0 0;
}

.cta-banner-glass .cta-title {
  font-family: var(--font-display);
  font-size: clamp(24px, 3.5vw, 32px);
  font-weight: 800;
}

/* Sized so the copy holds a single line on desktop rather than breaking
   after four words; it still wraps naturally on narrow viewports. */
.cta-banner-glass .cta-desc {
  max-width: 720px;
  font-size: 15.5px;
  color: var(--text-2);
  line-height: 1.7;
  text-wrap: pretty;
}

/* ── Footer ── */
.site-footer {
  margin-top: 80px;
  padding: 24px 0 36px;
  border-top: 1px solid var(--hairline);
}

.footer-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 16px;
}

.footer-shield-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-2);
}

.footer-links {
  display: flex;
  align-items: center;
  gap: 18px;
  list-style: none;
}

.footer-link {
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  color: var(--text-2);
  text-decoration: none;
  font-size: 12.5px;
  transition: color 0.15s ease;
}

.footer-link:hover {
  color: var(--text-1);
}

/* ── Responsive Breakpoints ── */
@media (max-width: 900px) {
  .features-bento-grid {
    grid-template-columns: 1fr;
  }
  .window-platforms-dock,
  .platforms-grid-full {
    grid-template-columns: repeat(2, 1fr);
  }
  .nav-links {
    display: none;
  }
}

@media (max-width: 640px) {
  .site-wrap {
    padding: 0 14px;
  }
  .site-header {
    --hdr-pad-block: 10px;
    --hdr-pad-inline: 14px;
    gap: 8px;
    margin-top: 10px;
    top: 10px;
  }
  .brand-badge {
    gap: 8px;
  }
  .brand-icon-box {
    width: 30px;
    height: 30px;
  }
  .brand-name-fa {
    font-size: 14.5px;
  }
  .brand-name-en {
    display: none;
  }
  .header-actions {
    gap: 6px;
    flex-wrap: nowrap;
  }
  .seg,
  .theme-segmented {
    padding: 2px;
  }
  .seg-btn {
    width: 28px;
    height: 28px;
  }
  .seg-btn svg {
    width: 14px;
    height: 14px;
  }
  .audio-btn {
    padding: 0 8px;
    height: 30px;
  }
  .audio-btn .audio-label {
    display: none;
  }
  /* On a phone the transcript column is already narrow, so the 75% cap broke
     the opening user turn across five short lines. The real client widens its
     bubble on small screens for the same reason. */
  .chat-bubble-user {
    max-width: 86%;
  }
  /* Horizontal only. Vertical padding here used to fight the optical
     correction at the end of the sheet, and the fixed height makes it
     unnecessary. */
  .btn-primary,
  .btn-secondary {
    padding-inline: 12px;
    font-size: 12px;
    white-space: nowrap;
  }
  .subpage-container {
    margin: 20px auto 60px;
    gap: 20px;
  }
  .subpage-card-glass {
    padding: 22px 18px;
    gap: 18px;
  }
  .browser-bar-mock {
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
  }
  .browser-bar-tools {
    justify-content: flex-end;
  }
  .survey-option-glass {
    padding: 12px 14px;
    font-size: 13.5px;
    gap: 10px;
  }
}

@media (max-width: 580px) {
  .window-platforms-dock,
  .platforms-grid-full {
    grid-template-columns: 1fr;
  }
  .window-titlebar {
    flex-direction: column;
    align-items: stretch;
  }
  .window-master-row {
    justify-content: space-between;
  }
}

/* ==========================================================================
   Subpages Shared Acrylic System (Welcome, Feedback, Goodbye, Privacy)
   ========================================================================== */
.subpage-container {
  max-width: 840px;
  margin: 32px auto 80px;
  display: flex;
  flex-direction: column;
  gap: 28px;
}

.subpage-header-wrap {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.subpage-header-wrap .hero-title:not(.has-lens-drift) {
  animation: titleLensDrift 1.2s ease-in-out both;
}

.subpage-header-wrap .hero-desc {
  animation: blueFadeInUp 0.9s cubic-bezier(0.16, 1, 0.3, 1) 0.2s both;
}

.subpage-card-glass {
  background: var(--panel);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-window);
  box-shadow: var(--shadow-panel);
  padding: 36px 32px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* ── Welcome Guide (Pinning Steps) ── */
.browser-bar-mock {
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-card);
  padding: 12px 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  direction: ltr;
}

.browser-bar-url {
  font-family: var(--font-code);
  font-size: 13px;
  color: var(--text-2);
  display: flex;
  align-items: center;
  gap: 8px;
}

.browser-bar-tools {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Toolbar icons as Chrome actually draws them: bare glyphs, no pill, no
   caption, and nothing behind the pinned one. This carried a soft accent well
   and an inset ring until it was checked against a real toolbar, and Chrome
   draws neither; a reader sent looking for a blue ring on their own browser
   would not find one. The pinned mark is the circle, which is what
   updateActionIcon paints while the extension is on. */
.toolbar-glyph {
  position: relative;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
}

.toolbar-glyph.is-muted {
  color: var(--text-3);
}

.toolbar-glyph.is-pinned img {
  width: 17px;
  height: 17px;
  border-radius: 5px;
}

/* ── Welcome: the three setup steps ──
   Rows inside the one panel, separated by hairlines, not three nested cards.
   Three equal cards with a badge on top is the shape every generated
   onboarding page has, and the cards also carried a hover lift on something
   that is not pressable, which promises an interaction that never arrives. */
.step-heading {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 800;
  color: var(--text-1);
  margin: 4px 0 2px;
}

.step-list {
  list-style: none;
  display: flex;
  flex-direction: column;
}

.step-item {
  position: relative;
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  padding: 15px 0;
}

.step-item + .step-item::before {
  content: "";
  position: absolute;
  top: 0;
  inset-inline: 44px 0;
  height: 1px;
  background: var(--hairline);
}

.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 99999px;
  border: 1px solid var(--field-border);
  color: var(--text-2);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
}

.step-text b {
  display: block;
  font-family: var(--font-display);
  font-size: 15.5px;
  font-weight: 700;
  line-height: 1.6;
  color: var(--text-1);
}

.step-text span {
  display: block;
  margin-top: 3px;
  font-size: 13.5px;
  line-height: 1.8;
  color: var(--text-3);
  text-wrap: pretty;
}

/* ── Welcome: the language note and the two fallback boxes ──
   The note explains once why the steps carry two labels for every button
   (the reader's Chrome may be in English or in Persian and they cannot be
   expected to know which), which is what lets the steps themselves stay one
   line of prose each instead of three. The two boxes answer the two ways the
   steps can fail, and sit side by side because a reader who needs one should
   not have to scroll past the other. */
.guide-note {
  margin: -4px 0 2px;
  font-size: 12.5px;
  line-height: 1.85;
  color: var(--text-3);
  text-wrap: pretty;
}

.guide-asides {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin-top: 2px;
}

.guide-aside {
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-card);
  padding: 14px 16px;
}

.guide-aside b {
  display: block;
  font-family: var(--font-display);
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.6;
  color: var(--text-2);
}

.guide-aside span {
  display: block;
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.85;
  color: var(--text-3);
  text-wrap: pretty;
}

/* The escape hatch was 100x21, a text link with no padding, which is under
   every touch-target floor there is. Padded to a real control and given the
   panel treatment on hover so it reads as pressable, which a bare underlined
   phrase never did. */
.step-skip {
  align-self: center;
  margin-top: 10px;
  padding: 12px 20px;
  border-radius: 99999px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-3);
  text-decoration: none;
  transition:
    color 0.18s var(--ease-standard),
    background-color 0.18s var(--ease-standard);
}

.step-skip:hover,
.step-skip:focus-visible {
  color: var(--text-1);
  background: var(--panel-card);
}

/* ── Welcome: platform launchpad ──
   The platform name lives in a tooltip instead of the button label, so the
   row is four recognisable marks rather than four sentences. */
.launchpad {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  margin-top: 8px;
}

.launchpad-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-2);
}

.launchpad-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
}

.launch-tile {
  position: relative;
  width: 68px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--panel-solid);
  border: 1px solid var(--field-border);
  border-radius: 20px;
  transition: transform 0.2s var(--ease-standard), border-color 0.2s ease, box-shadow 0.2s ease;
}

.launch-tile img {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  transition: transform 0.2s var(--ease-standard);
}

.launch-tile:hover,
.launch-tile:focus-visible {
  transform: translateY(-4px);
  border-color: var(--accent-ink);
  box-shadow: 0 12px 26px -10px var(--tile-glow, rgba(33, 136, 202, 0.6));
}

.launch-tile:hover img,
.launch-tile:focus-visible img {
  transform: scale(1.08);
}

.launch-tip {
  position: absolute;
  bottom: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%) translateY(3px);
  padding: 5px 11px;
  border-radius: 8px;
  background: var(--panel-solid);
  border: 1px solid var(--field-border);
  color: var(--text-1);
  font-size: 12px;
  font-weight: 700;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  box-shadow: 0 8px 20px -8px rgba(0, 0, 0, 0.45);
  transition: opacity 0.16s ease, transform 0.16s ease;
  z-index: 6;
}

.launch-tile:hover .launch-tip,
.launch-tile:focus-visible .launch-tip {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ── Welcome: quick AI platform launcher ──
   Exactly four fixed items, so explicit column counts replace auto-fit.
   auto-fit/minmax drops to 3 columns across a wide band of viewports and
   strands the fourth button on a row of its own; 4 / 2 / 1 always divides
   evenly. Below 620px a single column is the only width that keeps the
   Persian label on one line inside the fixed 42px button height. */
/* ── Feedback & Survey Forms ── */
.form-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: right;
}

.form-label {
  font-size: 14px;
  font-weight: 700;
  color: var(--text-1);
}

.form-input,
.form-textarea,
.form-select {
  width: 100%;
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  padding: 12px 16px;
  font-family: var(--font-body);
  font-size: 14.5px;
  color: var(--text-1);
  outline: none;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.form-input::placeholder,
.form-textarea::placeholder {
  color: var(--text-3);
  opacity: 1;
}

.form-input:focus,
.form-textarea:focus,
.form-select:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.form-textarea {
  min-height: 120px;
  resize: vertical;
  line-height: 1.75;
}

.category-pills-wrap {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.cat-pill-glass {
  /* Inline-flex, not the block default. These pills carry an icon and a label on
     one line, and a block box lays the inline SVG out on the text baseline, so
     the icon rode 3.8px above the pill centre while the label sat 0.5px off.
     Flex centres both against the same line. */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  padding: 8px 16px;
  border-radius: 10px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text-2);
  cursor: pointer;
  transition: all 0.18s ease;
  user-select: none;
}

.cat-pill-glass:hover {
  background: var(--panel-solid);
  color: var(--text-1);
}

.cat-pill-glass:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.cat-pill-glass.active {
  background: var(--accent-solid);
  color: #ffffff;
  border-color: var(--accent-solid);
  box-shadow: 0 2px 10px rgba(33, 136, 202, 0.4);
}

/* ── Form delivery panel ──
   Shown after a submit on the two free-text forms. It is informational, not a
   success state: the site stores nothing, so a green "recorded" receipt would
   be a lie. Accent colours, and an explicit next step. */
.delivery-panel {
  display: none;
  background: var(--accent-soft);
  border: 1px solid var(--accent-ink);
  border-radius: var(--radius-card);
  padding: 24px;
  color: var(--text-1);
  text-align: center;
}

.delivery-panel-icon {
  display: flex;
  justify-content: center;
  margin-bottom: 10px;
  color: var(--accent-ink);
}

.delivery-panel-title {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  margin-bottom: 8px;
}

.delivery-panel-body {
  font-size: 14.5px;
  line-height: 1.85;
  color: var(--text-2);
  max-width: 56ch;
  margin: 0 auto;
}

.delivery-panel-note {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-ink);
  margin-top: 10px;
}

.delivery-panel-note:empty {
  display: none;
}

.delivery-panel-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 18px;
}

/* ── Goodbye Survey Options ── */
.survey-option-glass {
  background: var(--panel-card);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-card);
  padding: 16px 20px;
  display: flex;
  align-items: center;
  gap: 14px;
  cursor: pointer;
  font-size: 15px;
  font-weight: 700;
  color: var(--text-1);
  transition: all 0.18s ease;
  user-select: none;
}

.survey-option-glass:hover {
  border-color: var(--accent);
  transform: translateX(-3px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.survey-option-glass:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-color: var(--accent);
}

.survey-option-glass.selected {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent);
}

.survey-radio-circle {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--text-3);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 0.15s ease;
}

/* The white centre dot sits on this fill, so it uses the deeper accent that
   clears 3:1 against white rather than the decorative one. */
.survey-option-glass.selected .survey-radio-circle {
  border-color: var(--accent-solid);
  background: var(--accent-solid);
}

.survey-radio-circle::after {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #ffffff;
  display: none;
}

.survey-option-glass.selected .survey-radio-circle::after {
  display: block;
}

/* ── Privacy Policy Documentation Styling ── */
.privacy-section-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: right;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 22px;
}

.privacy-section-block:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.privacy-section-block h2 {
  font-family: var(--font-display);
  font-size: 18px;
  font-weight: 800;
  color: var(--text-1);
}

.privacy-section-block p,
.privacy-section-block ul {
  font-size: 14.5px;
  line-height: 1.8;
  color: var(--text-2);
}

.privacy-section-block ul {
  padding-right: 20px;
}

.privacy-section-block li {
  margin-bottom: 6px;
}

/* ── Control Boundaries ──
   Glass hairlines (--panel-border) read at roughly 1.2:1, which is right for a
   decorative card edge but wrong for a field: it is the only thing telling the
   eye where an input begins. Every control that a user must aim at gets
   --field-border instead, which clears the 3:1 bar of WCAG 1.4.11.
   Declared last so it wins over the base rules above without touching the
   panel and card borders those rules share. */
.form-input,
.form-textarea,
.form-select,
.audio-btn,
.font-tab-pill,
.cat-pill-glass,
.survey-option-glass,
.theme-segmented {
  border-color: var(--field-border);
}

.form-input:hover,
.form-textarea:hover,
.form-select:hover,
.audio-btn:hover,
.font-tab-pill:hover,
.cat-pill-glass:hover,
.survey-option-glass:hover {
  border-color: var(--accent-ink);
}

/* The active pill is a filled control: its border must match the fill, not
   the outline grey, or it reads as a stray ring. */
.font-tab-pill.active,
.cat-pill-glass.active {
  border-color: var(--accent-solid);
}

/* ── Header on phones ──
   Below ~430px the logo, the theme switcher, the sound toggle and the install
   button no longer fit the pill together: the CTA was pushed past the header's
   left edge and clipped mid-pill. The sound toggle is the one to drop, because
   on a phone it is a decorative nicety (and mobile browsers often refuse to
   play it anyway) while the theme switcher is a real preference and the CTA is
   the conversion. Dropping it keeps every remaining control at thumb size
   instead of shrinking all four to fit. These live at the end of the sheet so
   no wider media block declared later can undo them. */
@media (max-width: 430px) {
  .audio-btn {
    display: none;
  }
}

/* Narrower still: the wordmark is the last thing that can go, and the mark
   alone still identifies the product. */
@media (max-width: 380px) {
  .brand-text-col {
    display: none;
  }
  .brand-badge {
    gap: 0;
  }
}

/* ── Welcome toolbar mock, stacked, at narrow widths ──
   `.browser-bar-mock` is meant to go full width below 640 and drop its icons
   to the right end of the bar, which is where Chrome keeps them. Only half of
   that was happening. The rule that says so lives in the 640px block up at
   line 3088, but the component itself is defined at 3228, and a later
   declaration of the same property at equal specificity wins: the base rule's
   `align-items: center` beat the media block's `stretch`, so the icon row
   shrink-wrapped to 70px and drifted to the middle of the mock while the
   `justify-content: flex-end` beside it had a content-sized box to act on and
   did nothing. Restated here, after the definition, where it can win. The
   companion overrides in that early block, the card padding and gap, are dead
   the same way and are left for a deliberate call rather than changed here. */
@media (max-width: 640px) {
  .browser-bar-mock {
    align-items: stretch;
  }
}

/* ── Welcome fallback boxes at narrow widths ──
   Two columns of wrapped Persian at 12.5px need roughly 290px each to keep a
   sane measure, and the card is down to about 420px of content by 720. Below
   that they stack, which also puts the more likely of the two first. */
@media (max-width: 720px) {
  .guide-asides {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ── Welcome steps at narrow widths ──
   The rows already run badge-beside-text at every width, so there is nothing
   to re-flow here beyond tightening the gaps. The old three-column rule and
   its badge-above-label fallback both died with the cards. */
@media (max-width: 560px) {
  .step-item {
    grid-template-columns: 24px minmax(0, 1fr);
    gap: 12px;
    padding: 13px 0;
  }

  .step-item + .step-item::before {
    inset-inline: 36px 0;
  }

  .step-num {
    width: 24px;
    height: 24px;
    font-size: 12px;
  }

  .step-text b {
    font-size: 14.5px;
  }

  .step-text span {
    font-size: 13px;
  }
}

/* ── Typography demo controls on phones ──
   One metric row needs 290px (label 64 + stepper 110 + slider 96 + gaps) while
   the bar only has about 230px at 320 wide. The row overflowed its card and, in
   RTL, pushed 8px past the left edge of the page, which gave the whole document
   a horizontal scrollbar. Below 430px the label takes its own line and the
   slider absorbs whatever width is left, so nothing overflows at any width. */
@media (max-width: 430px) {
  .font-stepper-wrap {
    flex-wrap: wrap;
    row-gap: 8px;
  }
  .font-metric-label {
    flex: 0 0 100%;
    min-width: 0;
    text-align: start;
  }
  .metric-slider {
    flex: 1 1 auto;
    width: auto;
    min-width: 0;
  }
}

/* ── Optical centring of the pill button labels ──
   Measured off the rendered pixels at 2.7x, against the true centre of each
   pill and not against the line box. Before this rule the icons were already
   right (every one of them within half a pixel of the pill centre) and only the
   labels were off, every one of them low:

     hero CTA label +3.7px   header CTA +2.4px   hero secondary +1.7px
     feedback submit +1.5px  category pills +0.9px and +0.6px

   The cause is the face, not a stray padding. Vazirmatn's ascent is deeper than
   its descent, so the ink of a line sits low inside the content area flex is
   centring: the label's ink centre measured 1.5px to 4.1px below that box's
   centre in every control, in proportion to nothing but which letters the word
   happens to contain. Centring the line box, which is all align-items can do,
   therefore centres the wrong thing.

   0.16em is the average of what the four worst labels needed, so nothing is
   overcorrected by more than about a pixel. It is applied as bottom padding
   rather than a transform because these buttons have a fixed height: the
   padding eats into the content box, and align-items then centres what is left,
   which lifts the label without touching the layout outside the pill. The icon
   is pushed straight back down by the other half so it keeps the centre it
   already had, and so stays level with the label's visual mass.

   The classes are doubled for specificity, which is load-bearing. The hero and
   banner pairs set padding as a shorthand at (0,2,0), and a shorthand zeroes a
   bottom padding declared by a plainer selector however late in the sheet it
   sits, so the hero secondary button kept its label exactly where it was while
   its icon, which is a separate declaration, moved on its own. Doubling ties
   with those rules and wins on source order. */
.btn-primary.btn-primary,
.btn-secondary.btn-secondary {
  padding-bottom: 0.32em;
}

.btn-primary > svg,
.btn-secondary > svg {
  transform: translateY(0.16em);
}

/* ── Anchor landing ──
   Clicking a nav item used to park the section title underneath the sticky
   header, so "پلتفرم‌ها" landed with the pill sitting on the marquee. The
   offset clears the header in its scrolled state plus a little air. */
#demo,
#features,
#platforms,
#fonts,
#security,
#faq {
  scroll-margin-top: 104px;
}

/* ── Sticky header, shrunk on scroll ──
   At rest the bar is an 86px pill sitting 18px down. Once the page moves it
   compresses to 60px and tightens everything inside it, which hands the
   viewport back about 26px and stops a full-size bar from floating over the
   content it is meant to sit above. A single 0.35s transition list covers the
   whole change so it reads as one movement rather than five independent ones. */
.site-header {
  transition:
    padding 0.35s var(--ease-standard),
    margin-top 0.35s var(--ease-standard),
    /* Reads --hdr-dur at the moment the class flips, so the bar's box opens at
       the split's 440ms and closes at the merge's 360ms, in step with the
       surfaces fading over the same box. Declared here and not on .is-split,
       because a transition list is replaced wholesale: put it on the split state
       and the merge would snap the margins back with no transition at all. */
    margin-inline var(--hdr-dur) var(--ease-standard),
    top 0.35s var(--ease-standard);
}

/* A transition declaration is replaced wholesale, not merged, so each child's
   own hover transition is repeated here. Without them the header would swap
   size smoothly while its hover colour snapped. */
.brand-icon-box,
.brand-text-col,
.brand-name-fa,
.nav-link,
.seg-btn,
.audio-btn,
.site-header .btn-primary {
  transition:
    width 0.35s var(--ease-standard),
    height 0.35s var(--ease-standard),
    min-height 0.35s var(--ease-standard),
    padding 0.35s var(--ease-standard),
    font-size 0.35s var(--ease-standard),
    transform 0.18s var(--ease-standard),
    opacity 0.18s ease,
    color 0.18s ease,
    background 0.15s ease,
    border-color 0.18s ease,
    box-shadow 0.2s ease;
}

/* The islands take no scrolled override. Two things used to hang off this
   selector and both are gone. Swapping the hairline to --panel-border-hover,
   a translucent blue, is what the user reported as "a blue border which ruins
   the aesthetics" once the page moved. And the deeper drop shadow is what cost
   the islands their lit top edge, see the note in the island block above.
   Whatever the scrolled state needs, it does not need the surface to change. */

/* The compact numbers only apply where the bar has room to give. On phones it
   is already at its floor, so scrolling there only deepens the shadow. */
@media (min-width: 641px) {
  .site-header.is-scrolled {
    --hdr-pad-block: 8px;
    --hdr-pad-inline: 18px;
    margin-top: 8px;
    top: 8px;
  }

  .site-header.is-scrolled .brand-icon-box {
    width: 28px;
    height: 28px;
  }

  .site-header.is-scrolled .brand-name-fa {
    font-size: 14.5px;
  }

  .site-header.is-scrolled .nav-link {
    min-height: 36px;
    font-size: 12.5px;
  }

  .site-header.is-scrolled .seg-btn {
    width: 26px;
    height: 24px;
  }

  .site-header.is-scrolled .seg-btn svg {
    width: 13px;
    height: 13px;
  }

  .site-header.is-scrolled .audio-btn {
    height: 28px;
    padding: 0 10px;
    font-size: 11.5px;
  }

  /* padding-inline, not padding: the button carries a bottom padding that
     optically centres its Persian label, and a shorthand would wipe it. */
  .site-header.is-scrolled .btn-primary {
    height: 36px;
    padding-inline: 16px;
    font-size: 12.5px;
  }
}

/* ── The split bar ──
   Reading is the one activity a sticky bar is allowed to get in the way of, so
   once the visitor is committed to it the bar divides into its two ends and the
   middle of the top edge goes back to the page. Scrolling up merges it again,
   which is why no menu button and no hidden dock are needed here: the
   navigation is never more than one gesture away, and that gesture is the one
   someone reaching for a nav link makes anyway.

   Only above 901px, where the navigation exists at all. Below that the bar is
   already a brand and a set of controls with the links dropped, and there is
   nothing in the middle to clear. */
@media (min-width: 901px) {
  .site-header.is-split {
    /* The two ends go out to the sides of the screen, and this is the second
       attempt at it. The first version kept them inside the content column and
       the user photographed it with an arrow drawn out of each island toward the
       page edge: "would it be okay if you move the islands more to the sides??
       there are empty spaces on the right and left!"

       An even earlier version DID travel outward and was rejected, and the
       difference between that one and this one is the whole reason this works.
       That one moved the islands by transform while the bar's own pill stayed
       1120px wide, so at 1536 they stopped halfway across a 212px gutter
       floating with nothing to line up with on either side. This widens the
       bar's box instead, so the islands arrive at the page gutter and line up
       with the screen edge itself.

       The arithmetic: the header fills its containing block, so its 50% is half
       the content column, and `50vw - 50%` is therefore the gutter between the
       bar's edge and the viewport edge. Take the 28px target off that and what
       is left is the outward travel. `max(0px, ...)` is what keeps it from ever
       pulling the bar inward: below about 1200px there is no gutter left to
       claim and the islands simply hold their places. `50vw` counts the
       scrollbar, so they land at about 20px rather than 28; measured 20.5px from
       the visible edge at 1920, 1440 and 1200, and 0 travel at 1024.

       Widening the box rather than moving the islands also means the ends never
       leave the surface they belong to: the island's outer edge and the resting
       pill's outer edge are both the header's box edge, so the two travel
       together and stay flush with each other for the entire transition. */
    --hdr-bleed: max(0px, calc(50vw - 50% - 28px));
    margin-inline: calc(-1 * var(--hdr-bleed));
    --hdr-dur: 440ms;
    /* The row is now two objects with empty space between them. Without this
       the invisible strip across the middle would keep swallowing clicks meant
       for whatever is scrolling underneath it. */
    pointer-events: none;
  }

  .site-header.is-split .brand-badge,
  .site-header.is-split .header-actions {
    pointer-events: auto;
  }

  /* The links fade where they stand. They used to slide outward toward the end
     they belonged to, which only made sense while the ends were travelling;
     with the ends still, a drift would read as the links escaping rather than
     as the bar opening. */
  .site-header.is-split .nav-container {
    opacity: 0;
    transform: translateY(-8px);
    visibility: hidden;
    pointer-events: none;
    /* The links have to leave the tab order when the bar divides, but only
       after they have finished fading, or a keyboard visitor six tabs deep
       would lose their place mid-fade. */
    transition:
      opacity var(--hdr-dur) var(--ease-standard),
      transform var(--hdr-dur) var(--ease-standard),
      visibility 0s linear var(--hdr-dur);
  }
}

/* ── The demo window on a narrow screen ──
   The real app drops its sidebar once the window cannot hold both columns, so
   the mock does the same. With one column left the frame stops being a cropped
   window and simply grows to fit the conversation. */
@media (max-width: 900px) {
  /* One column means the window is no longer a cropped picture of an app, so
     there is nothing to scale: it just grows to fit the conversation, and the
     section around it goes back to being as tall as its contents. */
  .demo-section {
    height: auto;
    min-height: 0;
  }

  .glass-window-panel,
  .window-stage {
    width: 100%;
    height: auto;
    position: static;
  }

  .window-stage {
    transform: none;
  }

  .glass-window-panel {
    max-width: 1112px;
  }

  .app-sidebar {
    display: none;
  }

  .app-frame {
    height: auto;
  }

  .app-thread {
    overflow: visible;
    mask-image: none;
    -webkit-mask-image: none;
  }
}

@media (max-width: 560px) {
  .app-thread {
    padding: 0 12px 8px;
  }

  .app-composer {
    padding: 8px 12px 12px;
  }

  .composer-think {
    display: none;
  }
}
