/* ---- tokens.css ---- */
/* ==========================================================================
   Design Tokens
   Single source of truth for colour, type, spacing, motion and elevation.
   Change a value here and it propagates across the entire site.
   ========================================================================== */

:root {
  /* --- Brand accents ------------------------------------------------------
     Deep gunmetal navy, clay orange and a warm yellow. The same three hues
     drive both themes; only their lightness is re-tuned per mode. */
  --brand-navy:    #1b2430;
  --brand-navy-2:  #293546;
  --brand-clay:    #c15f3c;
  --brand-clay-lo: #a8471f;
  --brand-yellow:  #e3a53a;

  /* --- Type families ------------------------------------------------------
     No webfont is required for the site to look correct. Drop Vazirmatn /
     Inter files into assets/fonts and they are picked up automatically
     (see assets/fonts/README.md). Otherwise a tuned system stack is used. */
  --font-sans: "Inter", "InterVariable", system-ui, -apple-system, "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-fa:   "Vazirmatn", "IRANSansX", "IRANSans", "Sahel", "Segoe UI",
               Tahoma, system-ui, sans-serif;
  --font-mono: "JetBrains Mono", "IBM Plex Mono", ui-monospace, SFMono-Regular,
               "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
  --font-body: var(--font-sans);

  /* --- Fluid type scale --------------------------------------------------- */
  --step--2: clamp(0.69rem, 0.67rem + 0.10vw, 0.75rem);
  --step--1: clamp(0.83rem, 0.79rem + 0.18vw, 0.94rem);
  --step-0:  clamp(0.95rem, 0.91rem + 0.20vw, 1.06rem);
  --step-1:  clamp(1.13rem, 1.05rem + 0.38vw, 1.36rem);
  --step-2:  clamp(1.35rem, 1.21rem + 0.68vw, 1.76rem);
  --step-3:  clamp(1.60rem, 1.38rem + 1.10vw, 2.27rem);
  --step-4:  clamp(1.91rem, 1.56rem + 1.72vw, 2.94rem);
  --step-5:  clamp(2.27rem, 1.75rem + 2.60vw, 3.80rem);
  --step-6:  clamp(2.70rem, 1.93rem + 3.85vw, 4.91rem);

  /* --- Spacing scale ------------------------------------------------------ */
  --sp-1:  0.25rem;  --sp-2: 0.5rem;   --sp-3: 0.75rem;  --sp-4: 1rem;
  --sp-5:  1.5rem;   --sp-6: 2rem;     --sp-7: 3rem;     --sp-8: 4rem;
  --sp-9:  6rem;     --sp-10: 8rem;

  --section-y: clamp(3.5rem, 2rem + 7vw, 7.5rem);
  --gutter:    clamp(1.15rem, 0.6rem + 2.4vw, 2.5rem);
  --maxw:      1280px;
  --maxw-prose: 68ch;

  /* --- Radii, borders, motion --------------------------------------------- */
  --r-sm: 4px;  --r-md: 8px;  --r-lg: 14px;  --r-xl: 22px;  --r-pill: 999px;
  --bw: 1px;

  --ease:      cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
  --dur-1: 140ms;  --dur-2: 260ms;  --dur-3: 420ms;  --dur-4: 700ms;

  --z-base: 1; --z-sticky: 100; --z-nav: 200; --z-overlay: 300; --z-modal: 400;
}

/* ==========================================================================
   COLD MODE  (data-theme="dark")
   --------------------------------------------------------------------------
   Deep gunmetal navy — the studio's primary identity, shown by default.
   Internally the value stays "dark" so it lines up with color-scheme and
   prefers-color-scheme; the name shown to visitors is "Cold".
   ========================================================================== */
:root,
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:           #10161e;
  --bg-elev:      #141b24;
  --surface:      #18202b;
  --surface-2:    #1e2734;
  --surface-3:    #273241;

  --text:         #e7eaef;
  --text-strong:  #ffffff;
  --text-muted:   #a5aebd;
  /* Dimmest tier still clears WCAG AA (4.5:1) on every surface above. */
  --text-faint:   #909aaa;

  --line:         rgba(165, 174, 189, 0.16);
  --line-strong:  rgba(165, 174, 189, 0.30);
  --line-accent:  rgba(224, 128, 80, 0.38);

  --accent:        #e08050;   /* clay orange, lifted for dark backgrounds */
  --accent-ink:    #1a0f07;   /* text placed on top of --accent */
  --accent-soft:   rgba(224, 128, 80, 0.14);
  --accent-2:      #e8b44a;   /* warm yellow */
  --accent-2-ink:  #1c1405;   /* text placed on top of --accent-2 */
  --accent-2-soft: rgba(232, 180, 74, 0.14);
  --accent-3:      #8ba3bd;   /* cool steel, for the occasional cold note */

  --heart: #ff6b6b;

  --ok:    #5cbf8a;
  /* A solid fill for the privacy shield, deeper than --ok so the white tick
     inside it stays legible. --ok itself is tuned to be readable AS text on a
     dark background, which makes it too light to put white type on. */
  --ok-solid: #2f9e63;
  --ok-ink:   #ffffff;
  --warn:  #e8b44a;
  --info:  #8ba3bd;

  --glow:        0 0 0 1px rgba(224, 128, 80, 0.28), 0 0 28px -8px rgba(224, 128, 80, 0.30);
  --shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.5);
  --shadow-md:   0 10px 30px -12px rgba(0, 0, 0, 0.72);
  --shadow-lg:   0 30px 70px -28px rgba(0, 0, 0, 0.82);

  --grid-line:   rgba(165, 174, 189, 0.05);
  --hero-wash-1: rgba(224, 128, 80, 0.15);
  --hero-wash-2: rgba(232, 180, 74, 0.10);
  --hero-wash-3: rgba(139, 163, 189, 0.09);
  --scrim:       rgba(10, 14, 20, 0.72);
  --img-placeholder: linear-gradient(135deg, #182029 0%, #212c3a 50%, #171f28 100%);
}

/* ==========================================================================
   WARM MODE  (data-theme="light")
   --------------------------------------------------------------------------
   Not white. The light surfaces are mixed 40% toward a warm grey so the page
   reads as soft paper and does not glare during long reading. Text is a deep
   navy, so both modes share one family of hues.

   Note: at this background depth a genuinely faint text tier cannot also meet
   AA, so --text-muted and --text-faint sit close together on purpose. Warm
   Mode separates hierarchy by size and weight rather than by lightness.
   ========================================================================== */
:root[data-theme="light"] {
  color-scheme: light;

  --bg:           #cdc5b9;
  --bg-elev:      #d1cac0;
  --surface:      #d1cac0;
  --surface-2:    #d0c6b9;
  --surface-3:    #cdc2b1;

  --text:         #26303c;
  --text-strong:  #141c26;
  --text-muted:   #584f42;
  /* Dimmest tier still clears WCAG AA (4.5:1) on every surface above. */
  --text-faint:   #574f42;

  --line:         rgba(38, 48, 60, 0.18);
  --line-strong:  rgba(38, 48, 60, 0.32);
  --line-accent:  rgba(134, 56, 25, 0.45);

  --accent:        #863819;   /* clay orange, deepened for warm paper */
  --accent-ink:    #fff8f2;
  --accent-soft:   rgba(134, 56, 25, 0.12);
  --accent-2:      #674b0c;   /* ochre — the yellow, taken to a readable depth */
  --accent-2-ink:  #fff8ec;   /* text placed on top of --accent-2 */
  --accent-2-soft: rgba(103, 75, 12, 0.14);
  --accent-3:      #38536c;

  --heart: #a32a22;

  --ok:    #175b3f;
  --ok-solid: #167046;
  --ok-ink:   #ffffff;
  --warn:  #674b0c;
  --info:  #38536c;

  --glow:        0 0 0 1px rgba(134, 56, 25, 0.28), 0 0 24px -8px rgba(134, 56, 25, 0.28);
  --shadow-sm:   0 1px 2px rgba(48, 38, 26, 0.10);
  --shadow-md:   0 12px 28px -16px rgba(48, 38, 26, 0.34);
  --shadow-lg:   0 30px 60px -30px rgba(48, 38, 26, 0.38);

  --grid-line:   rgba(38, 48, 60, 0.06);
  --hero-wash-1: rgba(134, 56, 25, 0.14);
  --hero-wash-2: rgba(103, 75, 12, 0.12);
  --hero-wash-3: rgba(56, 83, 108, 0.09);
  --scrim:       rgba(209, 202, 192, 0.86);
  --img-placeholder: linear-gradient(135deg, #d0c6b9 0%, #cdc2b1 50%, #d3cabe 100%);
}

:root[lang="fa"] { --font-body: var(--font-fa); }

/* ==========================================================================
   PERSIAN SMALL TEXT
   --------------------------------------------------------------------------
   The type scale above was tuned for Latin. Persian is not simply "the same
   letters in another direction": its letterforms carry dots above and below,
   and at 0.69rem those dots merge into the strokes — so «۱۲۵٬۰۰۰» under a
   field, a tool count, a caption, all read as grey smudges while the same
   size in English is perfectly legible. Every one of the smallest labels on
   the Persian site is affected, which is why this is one value rather than a
   patch on each of them.

   TO REVERT
       Replace the value below with the Latin original and the Persian site
       returns exactly to how it looked. Nothing else depends on this line.

       Latin original:  clamp(0.69rem, 0.67rem + 0.10vw, 0.75rem)

   IF REVERTED, THE TARGETED ALTERNATIVE is to raise only the readouts that
   sit under a control, leaving captions and card text alone:
       .field__hint  .tool__hint  .tool__result-note  .tool__result-exact
       .preview__meta  .stat__label  .search__count
   ========================================================================== */
:root[lang="fa"] {
  --step--2: clamp(0.78rem, 0.75rem + 0.14vw, 0.85rem);
}


/* ---- fonts.css ---- */
/* ==========================================================================
   Self-hosted webfonts
   --------------------------------------------------------------------------
   The site still works with no webfonts at all: css/tokens.css lists a tuned
   system stack in both languages, so anything not loaded here simply falls
   back and nothing breaks. Nothing is fetched from a font CDN either — the
   files are served from this origin, which is also why the CSP can keep
   font-src at 'self'.

   Persian is self-hosted because its fallbacks are not dependable. The stack
   asks for Vazirmatn, IRANSansX, IRANSans and Sahel, and none of those ship
   with Android or iOS — so on a phone, which is where most of the traffic is,
   Persian dropped all the way to the platform's default face. Latin needs no
   such rescue: system-ui is already a close match, so Inter stays optional.

   TO ADD A FONT
     1. Put the file in assets/fonts/ — see assets/fonts/README.md
     2. Delete the comment markers around the matching block below

   Nothing else needs changing: both families are already first in --font-sans
   and --font-fa, so they take effect the moment they load.
   ========================================================================== */


/* --- Persian: Vazirmatn ---------------------------------------------------
   Vazirmatn v33.003, variable weight 100–900, SIL Open Font License.
   Source: https://github.com/rastikerdar/vazirmatn

   _headers caches /assets/fonts/* for a year as immutable, so replacing the
   font means renaming the file — editing it in place would leave the old one
   in visitors' caches.
   ------------------------------------------------------------------------- */

@font-face {
  font-family: "Vazirmatn";
  src: url("../assets/fonts/Vazirmatn-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}


/* --- Latin: Inter — not installed -----------------------------------------
   Download the variable woff2 from:
   https://github.com/rsms/inter/releases

@font-face {
  font-family: "Inter";
  src: url("../assets/fonts/Inter-Variable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

--------------------------------------------------------------------------- */


/* ---- base.css ---- */
/* ==========================================================================
   Reset, typography, RTL foundations, accessibility
   ========================================================================== */

/* Optional self-hosted webfonts are declared in css/fonts.css, which ships
   empty so the site makes zero font requests by default. See that file and
   assets/fonts/README.md to enable them. */

/* --- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: 6rem;
  -moz-text-size-adjust: 100%;
}

body {
  min-height: 100svh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  transition: background-color var(--dur-2) var(--ease),
              color var(--dur-2) var(--ease);
}

img, svg, video, canvas, picture {
  display: block;
  max-width: 100%;
  height: auto;
}

input, button, textarea, select { font: inherit; color: inherit; }

button { background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; }

table { border-collapse: collapse; }

:target { scroll-margin-block-start: 7rem; }

/* --- Headings -------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
  color: var(--text-strong);
  font-weight: 650;
  line-height: 1.12;
  letter-spacing: -0.022em;
  text-wrap: balance;
}
h1 { font-size: var(--step-5); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); line-height: 1.22; }
h4 { font-size: var(--step-1); line-height: 1.3; letter-spacing: -0.01em; }

p { text-wrap: pretty; }

/* Persian needs more line-height and no negative tracking. */
:root[lang="fa"] h1,
:root[lang="fa"] h2,
:root[lang="fa"] h3,
:root[lang="fa"] h4,
:root[lang="fa"] h5,
:root[lang="fa"] h6 { letter-spacing: 0; line-height: 1.4; }
:root[lang="fa"] body { line-height: 1.95; }
/* A monospace face has no real Persian glyphs, so these labels were falling
   back to something the rest of the page never uses — the reason they read as
   less legible than everything around them. Persian gets the body face. */
:root[lang="fa"] .u-mono {
  font-family: var(--font-body);
  letter-spacing: 0;
  font-weight: 700;
  text-transform: none;
}

/* --- Selection & scrollbar -------------------------------------------------- */
::selection { background: var(--accent); color: var(--accent-ink); }

* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--line-strong);
  border-radius: var(--r-pill);
  border: 3px solid var(--bg);
}
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

/* --- Focus ---------------------------------------------------------------- */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}

/* --- Accessibility helpers -------------------------------------------------- */
.u-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  inset-block-start: -100%;
  inset-inline-start: var(--gutter);
  z-index: calc(var(--z-modal) + 10);
  padding: 0.7rem 1.1rem;
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  border-radius: 0 0 var(--r-md) var(--r-md);
  transition: inset-block-start var(--dur-2) var(--ease);
}
.skip-link:focus-visible { inset-block-start: 0; outline-offset: -3px; }

/* --- Utility classes ------------------------------------------------------- */
/* The small accent labels — eyebrows, category tags, meta lines. They were
   set at the smallest step in a light weight, which read as decoration rather
   than text. One step up and a heavier weight makes them legible; the wide
   tracking is pulled in at the same time, because letter-spacing that helps a
   tiny label starts hurting once the label is big enough to actually read. */
/* The small accent labels — section eyebrows and meta lines.
   Sized up to sit clearly above a glance: they introduce the heading below
   them, so they stop one step short of it rather than competing with it. */
.u-mono {
  font-family: var(--font-mono);
  font-size: var(--step-1);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 650;
}
.u-muted   { color: var(--text-muted); }
.u-faint   { color: var(--text-faint); }
.u-accent  { color: var(--accent); }
.u-prose   { max-width: var(--maxw-prose); }
.u-center  { text-align: center; }
.u-nowrap  { white-space: nowrap; }
.is-hidden { display: none !important; }

/* Latin text (brand name, emails, codes) stays LTR even inside Persian copy. */
.u-ltr { direction: ltr; unicode-bidi: isolate; display: inline-block; }

/* --- Motion preferences ------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1 !important; transform: none !important; }
}

/* --- Print ------------------------------------------------------------------- */
@media print {
  .site-header, .site-footer, .hero__bg, .noise { display: none !important; }
  body { background: #fff; color: #000; }
}


/* ---- layout.css ---- */
/* ==========================================================================
   Layout primitives
   Container, sections, grids, page headers, scroll reveal.
   All logical properties, so RTL is handled without any mirrored CSS.
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: 920px; }
.container--wide   { max-width: 1500px; }

.section {
  padding-block: var(--section-y);
  position: relative;
}
.section--tight { padding-block: calc(var(--section-y) * 0.6); }
.section--alt   { background: var(--bg-elev); }
.section + .section { border-block-start: var(--bw) solid var(--line); }

/* --- Section heading block --------------------------------------------------- */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  justify-content: space-between;
  gap: var(--sp-4) var(--sp-6);
  margin-block-end: var(--sp-7);
}
.section-head__text { max-width: 60ch; }
.section-head__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--accent);
  margin-block-end: var(--sp-3);
}
.section-head__eyebrow::before {
  content: "";
  inline-size: 26px;
  block-size: 1px;
  background: currentColor;
  opacity: 0.75;
}
.section-head__title { font-size: var(--step-4); }
.section-head__lede {
  margin-block-start: var(--sp-4);
  color: var(--text-muted);
  font-size: var(--step-1);
  line-height: 1.6;
}

/* --- Page header (inner pages) ------------------------------------------------ */
.page-head {
  position: relative;
  padding-block: clamp(6.5rem, 4rem + 9vw, 9.5rem) var(--sp-7);
  border-block-end: var(--bw) solid var(--line);
  overflow: hidden;
  isolation: isolate;
}
.page-head::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(60% 120% at 12% 0%, var(--hero-wash-1), transparent 62%),
    radial-gradient(46% 100% at 92% 8%, var(--hero-wash-2), transparent 66%);
  opacity: 0.85;
}
.page-head::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background-image:
    linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
  background-size: 68px 68px;
  mask-image: linear-gradient(to bottom, #000 0%, transparent 92%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 92%);
}
.page-head__title {
  font-size: var(--step-5);
  margin-block-start: var(--sp-3);
}
.page-head__lede {
  margin-block-start: var(--sp-4);
  max-width: 62ch;
  color: var(--text-muted);
  font-size: var(--step-1);
}
/* A quieter line under the lede, for a note about the page rather than a
   continuation of it. */
.page-head__note {
  margin-block-start: var(--sp-3);
  max-width: 62ch;
  color: var(--text-faint);
  font-size: var(--step--1);
}

/* --- Grids --------------------------------------------------------------------- */
.grid { display: grid; gap: var(--sp-5); }

/* Auto-fit grids: never need a media query, never overflow on mobile. */
.grid--cards    { grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr)); }
.grid--products { grid-template-columns: repeat(auto-fill, minmax(min(100%, 270px), 1fr)); }
.grid--services { grid-template-columns: repeat(auto-fill, minmax(min(100%, 250px), 1fr)); }
.grid--2        { grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr)); }
.grid--gallery  { grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr)); }

.stack { display: grid; gap: var(--sp-4); align-content: start; }
.stack--lg { gap: var(--sp-6); }

.cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
}
.cluster--center { justify-content: center; }

/* Split layout used by About / Contact: content + sidebar */
.split {
  display: grid;
  gap: clamp(2rem, 1rem + 4vw, 4.5rem);
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  align-items: start;
}
@media (max-width: 900px) {
  .split { grid-template-columns: 1fr; }
}

/* --- Divider ------------------------------------------------------------------- */
.rule {
  block-size: 1px;
  border: 0;
  background: linear-gradient(to right, transparent, var(--line-strong), transparent);
  margin-block: var(--sp-6);
}

/* --- Scroll reveal --------------------------------------------------------------
   Elements get .reveal in markup; JS adds .is-in when they enter the viewport.
   If JS never runs, `.reveal` without `.js-on` stays fully visible.
   --------------------------------------------------------------------------------- */
.js-on .reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--dur-4) var(--ease-out),
              transform var(--dur-4) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js-on .reveal.is-in {
  opacity: 1;
  transform: none;
  will-change: auto;
}


/* ---- components.css ---- */
/* ==========================================================================
   Components
   Header, nav, buttons, cards, badges, footer, forms, modal, lightbox.
   ========================================================================== */

/* ==========================================================================
   HEADER / NAV
   ========================================================================== */
.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-nav);
  transition: background-color var(--dur-2) var(--ease),
              border-color var(--dur-2) var(--ease),
              backdrop-filter var(--dur-2) var(--ease);
  border-block-end: var(--bw) solid transparent;
}
.site-header.is-stuck {
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(150%) blur(14px);
  -webkit-backdrop-filter: saturate(150%) blur(14px);
  border-block-end-color: var(--line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  block-size: 72px;
}
@media (min-width: 768px) { .site-header__inner { block-size: 84px; } }

/* --- Brand / logo ---------------------------------------------------------- */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.65rem;
  flex-shrink: 0;
  padding: 0.4rem 0.6rem;
  margin: -0.4rem -0.6rem;
  border-radius: var(--r-md);
  transition: background-color var(--dur-1) var(--ease);
}
.brand:hover { background: var(--surface-2); }
.brand__img { block-size: 34px; inline-size: auto; }
/* Below about 34px a detailed mark stops reading. */
@media (min-width: 768px) { .brand__img { block-size: 44px; } }

/* Two logo variants: the right one for the active theme is shown. */
.brand__img--light { display: none; }
:root[data-theme="light"] .brand__img--dark  { display: none; }
:root[data-theme="light"] .brand__img--light { display: block; }

/* Wordmark fallback shown until the official logo file is configured. */
.brand__mark {
  display: grid;
  place-items: center;
  inline-size: 36px;
  block-size: 36px;
  border: var(--bw) solid var(--line-accent);
  border-radius: var(--r-md);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  background: var(--accent-soft);
  transition: box-shadow var(--dur-2) var(--ease);
}
.brand:hover .brand__mark { box-shadow: var(--glow); }
.brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
  direction: ltr;
}
.brand__name {
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: 0.01em;
  color: var(--text-strong);
}
.brand__sub {
  font-family: var(--font-mono);
  font-size: 0.56rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-block-start: 3px;
}
@media (max-width: 400px) { .brand__sub { display: none; } }

/* Phones: the bar keeps the brand, the language switch and the menu button.
   With the heart and the mode toggle as well it needs ~430px, and at 375px
   the menu button was pushed 39px off the screen — measured. Both are one
   tap away in the menu's own footer. A long name shortens with an ellipsis
   rather than pushing the controls out. */
@media (max-width: 479px) {
  .site-header .support-link,
  .site-header .theme-toggle { display: none; }
  .site-header .brand { flex-shrink: 1; min-inline-size: 0; }
  .site-header .brand__text { min-inline-size: 0; }
  .site-header .brand__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* --- Desktop nav ------------------------------------------------------------ */
/* The bar carries eight links. Eight English labels plus the wordmark and the
   controls need about 1150px, so at the old 1024px breakpoint they were being
   squeezed to within a few pixels of collapsing even at the smaller type size
   — measured, not guessed. Below this width the menu button takes over, which
   is what it is there for. */
.nav { display: none; }
@media (min-width: 1200px) {
  .nav {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
  }
}
.nav__link {
  position: relative;
  padding: 0.6rem 0.95rem;
  border-radius: var(--r-md);
  color: var(--text-muted);
  /* Body size rather than a step below it. The bar is the one piece of the
     site every visitor uses, and it was reading as fine print next to the
     content it leads to. */
  font-size: var(--step-0);
  font-weight: 500;
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.nav__link:hover { color: var(--text-strong); background: var(--surface-2); }
.nav__link[aria-current="page"] { color: var(--text-strong); }
.nav__link[aria-current="page"]::after {
  content: "";
  position: absolute;
  inset-block-end: 2px;
  inset-inline: 0.95rem;
  block-size: 2px;
  background: var(--accent);
  border-radius: var(--r-pill);
  box-shadow: 0 0 10px -1px var(--accent);
}

/* --- Header actions --------------------------------------------------------- */
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* --- The header mirrors with the language, like the rest of the page --------
   It used to be pinned to one physical arrangement: `direction: ltr` was
   forced onto the bar so the brand and the controls never swapped sides. That
   came from a real complaint — the theme and language buttons moved out from
   under the cursor at the exact moment they were being used — but the cure
   was aimed too wide. Switching language is something a visitor might do once
   ever; reading direction is every second they are on the page, and leaving
   the bar in Latin order put the menu at the end of the line for a Persian
   reader instead of the start.

   So it mirrors now, the way every Persian site does: in Persian the brand
   and navigation sit on the right and the controls on the left, and the whole
   thing reverses in English. Nothing here forces a direction any more —
   removing the override is the entire change. */

.icon-btn {
  display: grid;
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  color: var(--text-muted);
  background: color-mix(in srgb, var(--surface) 60%, transparent);
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease);
}
.icon-btn:hover {
  color: var(--accent);
  border-color: var(--line-accent);
  background: var(--accent-soft);
}
.icon-btn:active { transform: scale(0.94); }
.icon-btn svg { inline-size: 18px; block-size: 18px; }

/* Language switcher: compact segmented control */
.lang-switch {
  display: flex;
  align-items: center;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  background: color-mix(in srgb, var(--surface) 60%, transparent);
}
.lang-switch__btn {
  padding: 0.5rem 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
  min-block-size: 40px;
}
.lang-switch__btn:hover { color: var(--text); }
.lang-switch__btn[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
}

/* Theme toggle icon swap */
.theme-toggle .icon-sun  { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun  { display: block; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: none; }

/* --- Mobile menu ------------------------------------------------------------- */
.nav-toggle { display: grid; }
/* Moves with the .nav breakpoint above — if these three ever disagree there
   is a window with no navigation at all. */
@media (min-width: 1200px) { .nav-toggle { display: none; } }

.nav-toggle__bars { position: relative; inline-size: 18px; block-size: 12px; }
.nav-toggle__bars span {
  position: absolute;
  inset-inline: 0;
  block-size: 1.75px;
  background: currentColor;
  border-radius: 2px;
  transition: transform var(--dur-2) var(--ease), opacity var(--dur-1) var(--ease);
}
.nav-toggle__bars span:nth-child(1) { inset-block-start: 0; }
.nav-toggle__bars span:nth-child(2) { inset-block-start: 5px; }
.nav-toggle__bars span:nth-child(3) { inset-block-start: 10px; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

.mobile-nav {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: var(--bg);
  /* Only opacity animates. `visibility` flips instantly on open and is delayed
     until after the fade on close, so the menu is never focusable while
     invisible — and opening never depends on a transition actually running. */
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease), visibility 0s linear var(--dur-2);
}
.mobile-nav.is-open {
  visibility: visible;
  opacity: 1;
  transition: opacity var(--dur-2) var(--ease), visibility 0s linear 0s;
}
@media (min-width: 1200px) { .mobile-nav { display: none; } }

.mobile-nav__head { block-size: 72px; display: flex; align-items: center; justify-content: space-between; }
.mobile-nav__body {
  overflow-y: auto;
  padding-block: var(--sp-5) var(--sp-7);
  -webkit-overflow-scrolling: touch;
}
.mobile-nav__list { display: grid; gap: var(--sp-1); }
.mobile-nav__link {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  padding: 0.85rem 0;
  border-block-end: var(--bw) solid var(--line);
  font-size: var(--step-2);
  font-weight: 600;
  color: var(--text-muted);
  transition: color var(--dur-1) var(--ease), padding-inline-start var(--dur-2) var(--ease);
}
.mobile-nav__link:hover,
.mobile-nav__link[aria-current="page"] { color: var(--text-strong); padding-inline-start: 0.5rem; }
.mobile-nav__link[aria-current="page"] { border-block-end-color: var(--accent); }
/* `.mobile-nav__num` used to live here, styling the 01–04 beside each menu
   entry. The numbers were replaced by icons; the rule is gone with them,
   rather than left behind to be puzzled over later. See .mobile-nav__icon in
   tools.css for what took its place. */
.mobile-nav__foot {
  padding-block: var(--sp-5);
  border-block-start: var(--bw) solid var(--line);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
body.is-locked { overflow: hidden; }

/* ==========================================================================
   BUTTONS
   ========================================================================== */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--text-strong);
  --btn-bd: var(--line-strong);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.95rem 1.75rem;
  min-block-size: 54px;
  border: var(--bw) solid var(--btn-bd);
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: var(--step-0);
  font-weight: 650;
  letter-spacing: 0.005em;
  text-align: center;
  cursor: pointer;
  overflow: hidden;
  transition: transform var(--dur-1) var(--ease), box-shadow var(--dur-2) var(--ease),
              background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease),
              color var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.btn:active { transform: translateY(1px); }
.btn svg { inline-size: 18px; block-size: 18px; flex-shrink: 0; }

/* Arrow icons point the right way in RTL automatically. */
:root[dir="rtl"] .btn .icon-flip,
:root[dir="rtl"] .link-arrow .icon-flip { transform: scaleX(-1); }

.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: var(--accent-ink);
  --btn-bd: var(--accent);
  font-weight: 700;
}
.btn--primary:hover { box-shadow: var(--glow); }

/* A neutral border read as "barely a button" next to the colored controls
   around it — "امروز"/"پاک کردن"/"کپی" all but disappeared into the page.
   The accent tint makes it a secondary action, not an invisible one. */
.btn--ghost { --btn-bg: transparent; --btn-fg: var(--accent); --btn-bd: color-mix(in srgb, var(--accent) 45%, transparent); }
.btn--ghost:hover { --btn-bd: var(--accent); --btn-fg: var(--accent); background: var(--accent-soft); }

.btn--solid { --btn-bg: var(--surface-2); --btn-bd: var(--line); }
.btn--solid:hover { --btn-bd: var(--line-strong); background: var(--surface-3); }

.btn--accent2 { --btn-bg: var(--accent-2); --btn-fg: var(--accent-2-ink); --btn-bd: var(--accent-2); font-weight: 700; }
.btn--accent2:hover { filter: brightness(1.08); }

.btn--sm { padding: 0.62rem 1.15rem; min-block-size: 46px; font-size: var(--step--1); }
.btn--lg { padding: 1.15rem 2.3rem; min-block-size: 64px; font-size: var(--step-1); }
.btn--block { inline-size: 100%; }

/* Touch devices get the 44px minimum target recommended by the platform
   accessibility guidelines. Mouse pointers keep the tighter sizing. */
@media (pointer: coarse) {
  .btn--sm       { min-block-size: 48px; }
  .icon-btn      { inline-size: 44px; block-size: 44px; }
  .lang-switch__btn { min-block-size: 44px; padding-inline: 0.75rem; }
  .filter-btn    { min-block-size: 44px; }
  .social-link   { inline-size: 44px; block-size: 44px; }
}

.btn[disabled], .btn[aria-disabled="true"],
.icon-btn[disabled], .icon-btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* Inline text link with a sliding arrow */
.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--accent);
  font-weight: 600;
  font-size: var(--step--1);
  border-radius: var(--r-sm);
}
.link-arrow svg { inline-size: 15px; block-size: 15px; transition: transform var(--dur-2) var(--ease); }
.link-arrow:hover svg { transform: translateX(4px); }
:root[dir="rtl"] .link-arrow:hover svg { transform: translateX(-4px) scaleX(-1); }

/* Underlined inline link inside prose */
.link-inline {
  color: var(--accent);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  transition: text-decoration-color var(--dur-1) var(--ease);
}
.link-inline:hover { text-decoration-color: var(--accent); }

/* ==========================================================================
   BADGES / CHIPS / TAGS
   ========================================================================== */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.28rem 0.6rem;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 650;
  white-space: nowrap;
}
.badge__dot {
  inline-size: 6px;
  block-size: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}
.badge--accent { color: var(--accent); border-color: var(--line-accent); background: var(--accent-soft); }
.badge--ok     { color: var(--ok);   border-color: color-mix(in srgb, var(--ok) 40%, transparent);   background: color-mix(in srgb, var(--ok) 10%, transparent); }
.badge--warn   { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 40%, transparent); background: color-mix(in srgb, var(--warn) 10%, transparent); }
.badge--muted  { color: var(--text-faint); }

.chip {
  display: inline-flex;
  align-items: center;
  padding: 0.42rem 0.85rem;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--step--2);
  font-weight: 500;
  white-space: nowrap;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease);
}
.chip:hover { color: var(--text-strong); border-color: var(--line-strong); }

/* Filter buttons on the projects page */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-block-end: var(--sp-6);
  padding-block-end: var(--sp-5);
  border-block-end: var(--bw) solid var(--line);
}
.filter-btn {
  padding: 0.5rem 1rem;
  min-block-size: 40px;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-size: var(--step--1);
  font-weight: 500;
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease);
}
.filter-btn:hover { color: var(--text-strong); border-color: var(--line-strong); }
.filter-btn[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
  font-weight: 650;
}

/* ==========================================================================
   CARD — shared shell with HUD corner brackets
   ========================================================================== */
.card {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  isolation: isolate;
  transition: border-color var(--dur-2) var(--ease), transform var(--dur-3) var(--ease),
              box-shadow var(--dur-3) var(--ease), background-color var(--dur-2) var(--ease);
}
.card:hover {
  border-color: var(--line-accent);
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}
.card:focus-within { border-color: var(--accent); }

/* Two corner brackets that light up on hover — the HUD accent. */
.card::before, .card::after {
  content: "";
  position: absolute;
  inline-size: 14px;
  block-size: 14px;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-2) var(--ease);
  border-color: var(--accent);
}
.card::before {
  inset-block-start: 8px; inset-inline-start: 8px;
  border-block-start: 1.5px solid; border-inline-start: 1.5px solid;
  border-start-start-radius: 3px;
}
.card::after {
  inset-block-end: 8px; inset-inline-end: 8px;
  border-block-end: 1.5px solid; border-inline-end: 1.5px solid;
  border-end-end-radius: 3px;
}
.card:hover::before, .card:hover::after,
.card:focus-within::before, .card:focus-within::after { opacity: 1; }

.card__media {
  position: relative;
  aspect-ratio: 16 / 10;
  background: var(--img-placeholder);
  overflow: hidden;
}
/* width/height rather than inline-size/block-size. They are the same thing in
   a horizontal writing mode, Persian included — but PageSpeed's image-size
   check only reads the physical names, and reported these as unsized. */
.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-4) var(--ease-out);
}
.card:hover .card__media img { transform: scale(1.045); }

/* Placeholder shown when a project/product has no cover image yet. */
.card__media--empty {
  display: grid;
  place-items: center;
}
.card__media--empty::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, var(--grid-line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--grid-line) 1px, transparent 1px);
  background-size: 26px 26px;
}
.card__media-mark {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--text-faint);
  border: var(--bw) solid var(--line);
  padding: 0.45rem 0.8rem;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--bg) 55%, transparent);
}

.card__media-badges {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-start: var(--sp-3);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  z-index: 2;
}
.card__media-badges .badge {
  background: color-mix(in srgb, var(--bg) 78%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.card__body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  flex: 1;
}
.card__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  color: var(--accent);
}
.card__title {
  font-size: var(--step-1);
  font-weight: 650;
  line-height: 1.25;
}
.card__title a { display: block; }
/* Stretched link: on .card--link the whole card is clickable while remaining
   a single focusable element. Cards with their own buttons opt out. */
.card--link .card__title a::after { content: ""; position: absolute; inset: 0; z-index: 2; }
/* Anything interactive inside a stretched card must sit above the overlay. */
.card__foot a, .card__foot button, .card__actions { position: relative; z-index: 3; }
.card:hover .card__title { color: var(--accent); }
.card__desc {
  color: var(--text-muted);
  font-size: var(--step--1);
  line-height: 1.62;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-block-start: auto;
  padding-block-start: var(--sp-4);
  border-block-start: var(--bw) solid var(--line);
  color: var(--text-faint);
  font-size: var(--step--2);
}
.card__tags { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* --- Product card variant ---------------------------------------------------- */
.card--product .card__media { aspect-ratio: 4 / 3; }
.card__price {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--font-mono);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--text-strong);
  direction: ltr;
}
.card__price--free { color: var(--ok); }
.card__price-note { font-size: var(--step--2); color: var(--text-faint); letter-spacing: 0.06em; }
.card--product .card__foot { border: 0; padding-block-start: 0; }
.card--product .card__actions { display: grid; gap: var(--sp-2); inline-size: 100%; }

/* --- Service card ------------------------------------------------------------- */
.service-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  background: var(--surface);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: border-color var(--dur-2) var(--ease), transform var(--dur-3) var(--ease),
              background-color var(--dur-2) var(--ease);
}
.service-card::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 2px;
  background: linear-gradient(to right, var(--accent), transparent 70%);
  transform: scaleX(0);
  transform-origin: inline-start;
  transition: transform var(--dur-3) var(--ease);
}
.service-card:hover { border-color: var(--line-accent); transform: translateY(-3px); }
.service-card:hover::before { transform: scaleX(1); }
.service-card__icon {
  display: grid;
  place-items: center;
  inline-size: 42px;
  block-size: 42px;
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line);
  background: var(--surface-2);
  color: var(--accent);
  flex-shrink: 0;
  transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.service-card:hover .service-card__icon { border-color: var(--line-accent); background: var(--accent-soft); }
.service-card__icon svg { inline-size: 20px; block-size: 20px; }
.service-card__title { font-size: var(--step-0); font-weight: 650; }
.service-card__desc { color: var(--text-muted); font-size: var(--step--1); line-height: 1.6; }
.service-card__index {
  position: absolute;
  inset-block-start: var(--sp-4);
  inset-inline-end: var(--sp-4);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--text-faint);
  opacity: 0.6;
}

/* ==========================================================================
   EMPTY STATE
   ========================================================================== */
.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--sp-4);
  padding: clamp(3rem, 2rem + 5vw, 6rem) var(--sp-5);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  text-align: center;
}
.empty-state__icon {
  display: grid;
  place-items: center;
  inline-size: 54px;
  block-size: 54px;
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line);
  color: var(--accent);
  background: var(--accent-soft);
}
.empty-state__icon svg { inline-size: 24px; block-size: 24px; }
.empty-state__title { font-size: var(--step-2); }
.empty-state__text { color: var(--text-muted); max-width: 46ch; }

/* ==========================================================================
   FOOTER
   ========================================================================== */
.site-footer {
  position: relative;
  border-block-start: var(--bw) solid var(--line);
  background: var(--bg-elev);
  padding-block: var(--sp-8) var(--sp-5);
  margin-block-start: auto;
}
.site-footer__top {
  display: grid;
  gap: var(--sp-7);
  grid-template-columns: minmax(0, 1.6fr) repeat(auto-fit, minmax(150px, 1fr));
  padding-block-end: var(--sp-7);
  border-block-end: var(--bw) solid var(--line);
}
@media (max-width: 720px) { .site-footer__top { grid-template-columns: 1fr 1fr; } }
@media (max-width: 460px) { .site-footer__top { grid-template-columns: 1fr; gap: var(--sp-6); } }

.site-footer__about { max-width: 42ch; }
.site-footer__tagline { margin-block-start: var(--sp-4); color: var(--text-muted); font-size: var(--step--1); }
.footer-col__title {
  font-family: var(--font-mono);
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  margin-block-end: var(--sp-4);
}
.footer-col__list { display: grid; gap: var(--sp-3); }
.footer-col__list a {
  color: var(--text-muted);
  font-size: var(--step--1);
  transition: color var(--dur-1) var(--ease);
  border-radius: var(--r-sm);
}
.footer-col__list a:hover { color: var(--accent); }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block-start: var(--sp-5);
  color: var(--text-faint);
  font-size: var(--step--2);
}
.social-list { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.social-link {
  display: grid;
  place-items: center;
  inline-size: 38px;
  block-size: 38px;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  color: var(--text-muted);
  transition: color var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease);
}
.social-link:hover { color: var(--accent); border-color: var(--line-accent); background: var(--accent-soft); }
.social-link svg { inline-size: 17px; block-size: 17px; }

/* ==========================================================================
   FORMS
   ========================================================================== */
.field { display: grid; gap: var(--sp-2); }
.field__label {
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text);
}
.field__req { color: var(--accent-2); margin-inline-start: 2px; }
.field__hint { font-size: var(--step--2); color: var(--text-faint); }

.input, .textarea, .select {
  inline-size: 100%;
  padding: 0.75rem 0.9rem;
  min-block-size: 46px;
  background: var(--surface);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  color: var(--text);
  font-size: var(--step--1);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--text-faint); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--line-strong); }
.input:focus-visible, .textarea:focus-visible, .select:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.textarea { min-block-size: 140px; resize: vertical; line-height: 1.65; }
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--accent-2); }
.field__error { font-size: var(--step--2); color: var(--accent-2); }

.form-note {
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: var(--bw) solid var(--line);
  border-inline-start: 2px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text-muted);
  font-size: var(--step--2);
  line-height: 1.6;
}

.form-status {
  padding: var(--sp-4);
  border-radius: var(--r-md);
  font-size: var(--step--1);
  border: var(--bw) solid var(--line);
}
.form-status--ok    { color: var(--ok);       border-color: color-mix(in srgb, var(--ok) 40%, transparent);       background: color-mix(in srgb, var(--ok) 8%, transparent); }
.form-status--error { color: var(--accent-2); border-color: color-mix(in srgb, var(--accent-2) 40%, transparent); background: var(--accent-2-soft); }

/* ==========================================================================
   INFO ROWS (project / product detail specs)
   ========================================================================== */
.spec-list {
  display: grid;
  gap: 0;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface);
}
.spec-row {
  display: grid;
  grid-template-columns: minmax(90px, 34%) 1fr;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  font-size: var(--step--1);
  align-items: baseline;
}
.spec-row + .spec-row { border-block-start: var(--bw) solid var(--line); }
.spec-row__key {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.spec-row__val { color: var(--text); }

/* ==========================================================================
   MODAL / LIGHTBOX
   ========================================================================== */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: var(--gutter);
  background: var(--scrim);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 0;
  inline-size: 100%;
  block-size: 100%;
  max-inline-size: none;
  max-block-size: none;
  overflow: hidden;
}
.lightbox::backdrop { background: transparent; }
.lightbox:not([open]) { display: none; }
.lightbox__figure { max-inline-size: min(1200px, 100%); max-block-size: 100%; display: grid; gap: var(--sp-3); }
.lightbox__img {
  max-block-size: 78svh;
  inline-size: auto;
  margin-inline: auto;
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line-strong);
  background: var(--surface);
}
.lightbox__cap { text-align: center; color: var(--text-muted); font-size: var(--step--1); }
.lightbox__close {
  position: absolute;
  inset-block-start: var(--gutter);
  inset-inline-end: var(--gutter);
  z-index: 2;
}
.lightbox__nav {
  position: absolute;
  inset-block-start: 50%;
  transform: translateY(-50%);
  background: color-mix(in srgb, var(--surface) 85%, transparent);
}
.lightbox__nav--prev { inset-inline-start: var(--gutter); }
.lightbox__nav--next { inset-inline-end: var(--gutter); }

/* Gallery thumbnails */
.thumb {
  position: relative;
  aspect-ratio: 16 / 10;
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--img-placeholder);
  cursor: zoom-in;
  transition: border-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
  inline-size: 100%;
}
.thumb:hover { border-color: var(--line-accent); transform: translateY(-2px); }
.thumb img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* Responsive video embeds */
.embed {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: var(--r-lg);
  overflow: hidden;
  border: var(--bw) solid var(--line);
  background: var(--img-placeholder);
}
.embed iframe, .embed video {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  border: 0;
}

/* ==========================================================================
   MISC
   ========================================================================== */
.prose { max-width: var(--maxw-prose); display: grid; gap: var(--sp-4); }
.prose p { color: var(--text-muted); font-size: var(--step-0); line-height: 1.78; }
.prose h2 { font-size: var(--step-3); margin-block-start: var(--sp-4); }
.prose h3 { font-size: var(--step-1); margin-block-start: var(--sp-3); }
.prose ul { display: grid; gap: var(--sp-2); }
.prose li {
  position: relative;
  padding-inline-start: 1.35rem;
  color: var(--text-muted);
}
.prose li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.72em;
  inline-size: 6px;
  block-size: 1.5px;
  background: var(--accent);
}

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  color: var(--text-faint);
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.breadcrumb a { transition: color var(--dur-1) var(--ease); border-radius: var(--r-sm); }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb__sep { opacity: 0.5; }
:root[dir="rtl"] .breadcrumb__sep { transform: scaleX(-1); display: inline-block; }

/* Skeleton shown while data modules load */
.skeleton {
  border-radius: var(--r-lg);
  background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 37%, var(--surface) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  min-block-size: 260px;
}
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }


/* --- Legal documents ------------------------------------------------------- */
.legal-doc { display: grid; gap: var(--sp-7); max-inline-size: var(--maxw-prose); }

.legal-section__title {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  font-size: var(--step-2);
  margin-block-end: var(--sp-4);
}
.legal-section__num {
  color: var(--accent);
  font-size: var(--step-0);
  font-weight: 700;
  flex-shrink: 0;
}
.legal-section .prose li { color: var(--text-muted); }

.legal-updated {
  font-size: var(--step--1);
  margin-block: var(--sp-3) var(--sp-4);
}

/* The current document in the switcher. */
.chip--active {
  color: var(--accent-ink);
  background: var(--accent);
  border-color: var(--accent);
}
.chip--active:hover { color: var(--accent-ink); border-color: var(--accent); }

.footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  font-size: var(--step--1);
}
.footer-legal a { color: var(--text-faint); }
.footer-legal a:hover { color: var(--accent); }


/* --- Support / crypto wallets --------------------------------------------- */
.callout {
  border: var(--bw) solid var(--line);
  border-inline-start: 3px solid var(--accent-2);
  border-radius: var(--r-md);
  background: var(--surface-2);
  padding: var(--sp-5);
  margin-block-end: var(--sp-6);
  max-inline-size: var(--maxw-prose);
}
.callout--warn { border-inline-start-color: var(--warn); }
.callout__title { font-size: var(--step-0); margin-block-end: var(--sp-2); }
.callout p { color: var(--text-muted); font-size: var(--step--1); line-height: 1.8; }

.wallet-grid { display: grid; gap: var(--sp-2); max-inline-size: var(--maxw-prose); }

/* One row per address. The whole row is the copy control, so it needs a real
   button's affordances — hover, focus ring and a pressed state. */
.wallet-row {
  display: grid;
  grid-template-columns: auto 1fr auto auto;
  align-items: center;
  gap: var(--sp-3);
  inline-size: 100%;
  min-block-size: 56px;
  padding: var(--sp-3) var(--sp-4);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: start;
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.wallet-row:hover { border-color: var(--line-strong); background: var(--surface-2); }
.wallet-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wallet-row:active { transform: translateY(1px); }

.wallet-row__asset {
  font-size: var(--step-0);
  font-weight: 750;
  color: var(--text-strong);
  min-inline-size: 3.5rem;
}
.wallet-row__net {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 650;
  color: var(--accent);
  letter-spacing: 0.02em;
}
.wallet-row__addr {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--text-faint);
  white-space: nowrap;
}
.wallet-row__copy {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: var(--step--1);
  font-weight: 600;
  color: var(--text-muted);
}
.wallet-row.is-copied { border-color: var(--ok); }
.wallet-row.is-copied .wallet-row__copy { color: var(--ok); }

.wallet-row--unset { cursor: default; opacity: 0.75; }
.wallet-row--unset .wallet-row__pending { font-size: var(--step--1); }

/* On a narrow screen the shortened address is the first thing worth dropping:
   the asset and its network are what a sender actually needs to match. */
@media (max-width: 560px) {
  .wallet-row { grid-template-columns: auto 1fr auto; gap: var(--sp-2); }
  .wallet-row__addr { display: none; }
  .wallet-row__copy span[data-i18n] { display: none; }
}

.donate-foot {
  max-inline-size: var(--maxw-prose);
  font-size: var(--step--1);
  line-height: 1.8;
  margin-block-start: var(--sp-6);
}


/* --- Support link in the header -------------------------------------------
   The one spot of red in the palette, so it reads as an invitation rather
   than another control. It fills in on hover. */
.support-link { color: var(--heart); }
.support-link:hover,
.support-link:focus-visible { color: var(--heart); border-color: var(--heart); }
.support-link svg { transition: transform var(--dur-2) var(--ease), fill var(--dur-2) var(--ease); fill: transparent; }
.support-link:hover svg { fill: var(--heart); transform: scale(1.12); }

@media (prefers-reduced-motion: reduce) {
  .support-link:hover svg { transform: none; }
}

.donate-message, .donate-message__lede { max-inline-size: var(--maxw-prose); }
.donate-message__lede { margin-block-end: var(--sp-5); }



/* ---- tools.css ---- */
/* ==========================================================================
   Tools hub — page layouts and tool-specific components.
   Built on the tokens and form/button/card components already in the system.
   Everything uses logical properties so RTL needs no separate stylesheet.
   ========================================================================== */

/* The browser's own `[hidden] { display: none }` is a low-specificity rule in
   the UA stylesheet, so any `display` we set on a component silently defeats
   it — and an element told to hide simply stays on screen. This one line is
   what keeps `el.hidden = true` meaning what it says, everywhere. */
[hidden] { display: none !important; }

/* --- Page scaffolding ----------------------------------------------------- */

/* The site header is position:fixed and transparent until it sticks, so
   without this every page begins underneath it and the first line of content
   shows through the bar. The clearance is deliberately generous: the header
   grows with the visitor's font size and browser zoom, and a tight margin
   here clips the breadcrumb on exactly the setups we cannot test. */
/* The end padding is the other half: without it the last card on a page sits
   directly on the footer's top border, which reads as the card having a
   double bottom edge rather than as the page having ended. */
main { padding-block: 6.5rem var(--sp-7); }
@media (min-width: 720px) { main { padding-block: 7.5rem var(--sp-8); } }

/* Persian is a joined script: letter-spacing pulls the letters of a word
   apart and «ابزارها» renders as «ا ب ز ا ر ه ا». The design system applies
   tracking to small caps-style labels (breadcrumbs, footer headings, badges),
   which is right for Latin and wrong for Persian — so it is switched off for
   the whole Persian tree rather than patched class by class. */
:root[lang="fa"] * { letter-spacing: normal; }

.head { padding-block: var(--sp-5) var(--sp-5); }
.head__eyebrow {
  font-size: var(--step--2);
  color: var(--text-faint);
  letter-spacing: 0.04em;
  margin-block-end: var(--sp-2);
}
.head h1 { font-size: var(--step-4); margin-block-end: var(--sp-3); }
.head__lede {
  color: var(--text-muted);
  max-inline-size: var(--maxw-prose);
  font-size: var(--step-1);
}

/* The design system styles the breadcrumb as a Latin micro-label: monospaced,
   uppercased, 0.66rem. None of that survives translation — Persian has no
   upper case, the mono stack has no Persian glyphs so the browser silently
   substitutes another font mid-line, and 0.66rem of a joined script is a
   smudge. It becomes ordinary small text in the page's own font. */
.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  font-family: inherit;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-faint);
  margin-block-end: var(--sp-4);
}
.breadcrumb a { color: var(--text-muted); }
.breadcrumb a:hover { color: var(--accent); }

/* build.py already writes the chevron that points the way the trail runs —
   ‹ in Persian, › in English. The inherited rule then mirrored it in RTL and
   turned the correct character back into the wrong one, so the flip is
   switched off here rather than in the file it came from. */
.breadcrumb__sep { opacity: 0.5; }
:root[dir="rtl"] .breadcrumb__sep { transform: none; }

/* --- Search --------------------------------------------------------------- */

.search {
  position: relative;
  max-inline-size: 640px;
}
.search .input { padding-inline-start: 3.2rem; min-block-size: 52px; }

/* A real submit button, not an ornament: people click the magnifier. */
.search__btn {
  position: absolute;
  inset-inline-start: 0.4rem;
  inset-block-start: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  border-radius: var(--r-md);
  color: var(--text-muted);
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.search__btn:hover { color: var(--accent); background: var(--accent-soft); }
.search__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.search__btn svg { inline-size: 20px; block-size: 20px; }

.search__count {
  margin-block-start: var(--sp-3);
  font-size: var(--step--2);
  color: var(--text-faint);
}

/* --- Suggestions ----------------------------------------------------------- */

.search__list {
  position: absolute;
  inset-inline: 0;
  inset-block-start: calc(100% + 0.4rem);
  z-index: var(--z-nav);
  margin: 0;
  padding: var(--sp-2);
  display: grid;
  gap: 2px;
  background: var(--bg-elev);
  border: var(--bw) solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
  max-block-size: min(60vh, 420px);
  overflow-y: auto;
}
.search__item a, .search__more a {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: 0.6rem 0.75rem;
  border-radius: var(--r-sm);
  min-block-size: 44px;
}
.search__item a:hover, .search__item.is-active a,
.search__more a:hover, .search__more.is-active a {
  background: var(--accent-soft);
  color: var(--accent);
}
.search__name { font-weight: 600; color: var(--text-strong); }
.search__item.is-active .search__name, .search__item a:hover .search__name { color: var(--accent); }
.search__cat { font-size: var(--step--2); color: var(--text-faint); flex: none; }
.search__more {
  border-block-start: var(--bw) solid var(--line);
  margin-block-start: var(--sp-1);
  padding-block-start: var(--sp-1);
  font-size: var(--step--2);
  color: var(--text-muted);
}

/* Link under a section's cards, for "there are more where these came from". */
.section-more { margin-block-start: var(--sp-5); }

/* --- Tool grid ------------------------------------------------------------ */

.tool-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}

.tool-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
  background: var(--surface);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  transition: border-color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease),
              background-color var(--dur-1) var(--ease);
}
.tool-card:hover {
  border-color: var(--line-accent);
  background: var(--surface-2);
  transform: translateY(-2px);
}
.tool-card:focus-within { border-color: var(--accent); }
.tool-card__icon {
  inline-size: 38px;
  block-size: 38px;
  display: grid;
  place-items: center;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--accent);
  margin-block-end: var(--sp-1);
}
.tool-card__icon svg { inline-size: 20px; block-size: 20px; }
.tool-card__title { font-size: var(--step-0); font-weight: 650; color: var(--text-strong); }
.tool-card__title a::after { content: ''; position: absolute; inset: 0; }
.tool-card { position: relative; }
.tool-card__desc {
  font-size: var(--step--2);
  color: var(--text-muted);
  line-height: 1.6;
}
.tool-card__cat { font-size: var(--step--2); color: var(--text-faint); }

/* --- Category grid -------------------------------------------------------- */

.cat-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.cat-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.cat-card:hover { border-color: var(--line-accent); background: var(--surface-2); }
.cat-card__title { font-size: var(--step-1); font-weight: 650; color: var(--text-strong); }
.cat-card__title a::after { content: ''; position: absolute; inset: 0; }
.cat-card__desc { font-size: var(--step--2); color: var(--text-muted); }
.cat-card__count { font-size: var(--step--2); color: var(--text-faint); margin-block-start: auto; }

/* --- The tool itself ------------------------------------------------------ */

.tool {
  display: grid;
  gap: var(--sp-5);
  padding: var(--sp-5);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
@media (min-width: 640px) { .tool { padding: var(--sp-6); } }

/* The unit converter's fields are a single stacked column (value, from
   unit, swap, to unit, result) rather than a side-by-side grid, so on a
   wide page they stretched edge to edge — a two-word unit name sitting in
   a box built for a paragraph. Capped and centred, they read as one
   compact control instead of a form spanning the whole card. */
[data-tool="unit-converter"] .tool__panel { max-inline-size: 420px; margin-inline: auto; inline-size: 100%; }

.tool__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding-block-end: var(--sp-4);
  border-block-end: var(--bw) solid var(--line);
}

/* The search box, the filter row, the count and the cards were four things
   touching. The chips need air above them or they read as part of the search
   field; the count keeps close company with the grid it describes; and the
   gap above the count is what separates the controls from the results. */
.search + .tool__tabs { margin-block-start: var(--sp-6); }
.tool__tabs + .search__count { margin-block-start: var(--sp-5); }
.search__count + .tool-grid { margin-block-start: var(--sp-3); }
.tool__tab {
  padding: 0.5rem 0.9rem;
  border-radius: var(--r-pill);
  border: var(--bw) solid var(--line);
  color: var(--text-muted);
  font-size: var(--step--1);
  min-block-size: 40px;
  transition: all var(--dur-1) var(--ease);
}
.tool__tab:hover { border-color: var(--line-strong); color: var(--text); }
.tool__tab[aria-selected="true"] {
  background: var(--accent-soft);
  border-color: var(--line-accent);
  color: var(--accent);
  font-weight: 600;
}

.tool__panel { display: grid; gap: var(--sp-4); }
.tool__panel[hidden] { display: none; }

/* --- "Today is …" strip ----------------------------------------------------- */

/* A statement, not a control — so it is deliberately unlike everything around
   it: no border box that could be mistaken for a field, no button styling, a
   single accent rule down its leading edge to mark it as the page speaking
   rather than the tool answering. It has to be legible at a glance and must
   not be confused with the result panel below, which is why it is quiet in
   weight but sits on its own line with clear air on both sides. */
.today-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  /* Sized to its words, not to the card: stretched edge to edge it reads as
     another field. `.tool` is a grid, so its own gap does the spacing below —
     a margin here would be added on top of it. */
  justify-self: start;
  max-inline-size: 100%;
  padding: var(--sp-3) var(--sp-4);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  font-size: var(--step--1);
  color: var(--text);
}
.today-strip__label { font-weight: 650; color: var(--accent); }
.today-strip__date { unicode-bidi: isolate; }
.today-strip__sep { color: var(--text-faint); }

/* A wrapper inside a tool has to carry the spacing itself: the gap on .tool
   only separates ITS children, so everything inside one wrapper div was
   stacking with no breathing room and labels sat on the box above them. */
.tool__stack { display: grid; gap: var(--sp-5); }

/* A .field is a grid; inside a stretched grid cell its rows would stretch too,
   so a field with a hint under it made its neighbour's control taller than
   itself. Rows start at the top and keep their natural height instead. */
.field { align-content: start; }

/* The browser draws a select's arrow hard against the edge of the box, and in
   RTL that edge is the left one, where it looked glued on. Replacing it with
   our own chevron is the only way to control that spacing. */
.select {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: 2.7rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a5aebd' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 16px 16px;
}
:root[dir="rtl"] .select { background-position: left 0.95rem center; }
:root[dir="ltr"] .select { background-position: right 0.95rem center; }
.select::-ms-expand { display: none; }

.tool__grid { display: grid; gap: var(--sp-4); }
@media (min-width: 560px) {
  .tool__grid--2 { grid-template-columns: 1fr 1fr; }
  .tool__grid--3 { grid-template-columns: repeat(3, 1fr); }
}

/* --- Two fields that convert into each other ------------------------------- */

/* Side by side with a normal gap, the two boxes read as two separate
   questions. The arrows in the middle say the two are one thing, and the
   wider gap gives them room to say it. Stacked on a phone the same pair of
   arrows turns to point up and down, because that is where the other field
   now is. */
.swap-pair {
  display: grid;
  gap: var(--sp-4);
  align-items: start;
  justify-items: stretch;
}
@media (min-width: 560px) {
  .swap-pair {
    grid-template-columns: 1fr auto 1fr;
    gap: var(--sp-6);
  }
}
.swap-pair__arrows {
  display: grid;
  gap: var(--sp-2);
  justify-items: center;
  color: var(--text-muted);
}
/* The box is as tall as an input (46px) and as wide as the art. A viewBox
   scales to fit and centres what is left over, so the arrows land on the same
   line as the two values instead of on the top edge of the boxes. */
.swap-pair__arrows svg {
  inline-size: 38px;
  block-size: 46px;
  align-self: start;
}
/* Stacked: the other field is above and below, so the arrows turn with it,
   and the stand-in label line has nothing left to stand in for. */
@media (max-width: 559px) {
  .swap-pair__arrows { justify-self: center; }
  .swap-pair__arrows svg { transform: rotate(90deg); block-size: 34px; }
  .swap-pair__spacer { display: none; }
}

/* The theme control inside the phone menu reads as a setting with a name,
   not a bare icon floating at the bottom of the screen. */
.mobile-nav__setting { color: var(--text-muted); font-size: var(--step--1); }

/* The icon beside each menu entry, in the same soft-square treatment the tool
   cards use — so the menu looks like it belongs to the site rather than like
   a list borrowed from somewhere else. It is decoration: the label sits right
   beside it and carries the meaning on its own. */
.mobile-nav__icon {
  display: grid;
  place-items: center;
  inline-size: 36px;
  block-size: 36px;
  flex: none;
  border-radius: var(--r-md);
  background: var(--accent-soft);
  color: var(--accent);
  transition: background-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.mobile-nav__icon svg { inline-size: 19px; block-size: 19px; }

/* The row already slides on press; the icon lifts with it. */
.mobile-nav__link:active .mobile-nav__icon { transform: scale(0.94); }
.mobile-nav__link[aria-current="page"] .mobile-nav__icon {
  background: var(--accent);
  color: var(--accent-ink);
}
@media (prefers-reduced-motion: reduce) {
  .mobile-nav__icon { transition: background-color var(--dur-2) var(--ease); }
  .mobile-nav__link:active .mobile-nav__icon { transform: none; }
}

/* The entries line up on their icons, not on their baselines — with a 36px
   square in the row, baseline alignment left the icon hanging low. */
.mobile-nav__link { align-items: center; }

/* --- Things a finger has to hit -------------------------------------------- */

/* The menu button was shrink-wrapped around its three bars: an 18×12px target
   for the single most important control on a phone. Everything else in the
   header is already 40px; this brings it into line. The icon does not change
   size — only the area around it that counts as a press. */
.nav-toggle {
  place-items: center;
  inline-size: 40px;
  block-size: 40px;
  border-radius: var(--r-md);
  color: var(--text-muted);
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.nav-toggle:hover { color: var(--accent); background: var(--accent-soft); }

/* Footer and breadcrumb links sit at around 21px — perfectly clickable with a
   mouse, a coin toss with a thumb. So they grow only where the pointer is
   actually a finger. A desktop with a mouse keeps the spacing it already had;
   nothing below moves an approved layout.
   (The menu button above is the exception: 18×12px is too small for any
   pointer, and it is hidden on desktop anyway.) */
@media (pointer: coarse) {
  .nav-toggle { inline-size: 44px; block-size: 44px; }

  /* The links grow, so the gap between them shrinks by about as much and the
     footer column ends up close to the height it always had. */
  .footer-col__list { gap: var(--sp-1); }
  .footer-col__list a {
    display: inline-flex;
    align-items: center;
    min-block-size: 44px;
  }
  .breadcrumb a { display: inline-flex; align-items: center; min-block-size: 36px; }
}

/* --- The privacy shield ---------------------------------------------------- */

/* Filled, not outlined — see SHIELD_OK in build.py for why this one icon
   breaks the pattern. The tick is drawn in a colour that stays readable on
   the fill in both themes rather than a hard-coded white. */
.shield-ok { flex: none; margin-block-start: 1px; }
.shield-ok__body { fill: var(--ok-solid); }
.shield-ok__tick { stroke: var(--ok-ink); }

/* --- Contact --------------------------------------------------------------- */

.contact-mail {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  margin-block: var(--sp-5) var(--sp-2);
  padding: var(--sp-4);
  border: var(--bw) solid var(--line);
  border-inline-start: 3px solid var(--accent);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.contact-mail__label { font-weight: 650; color: var(--text-strong); }
.contact-mail__value { font-size: var(--step-0); }
.contact-hint, .contact-soon { color: var(--text-faint); font-size: var(--step--1); }
.contact-soon { margin-block-start: var(--sp-5); }

.tool__hint { font-size: var(--step--2); color: var(--text-faint); }

.tool__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
}
/* A lone button between two things it acts on — e.g. a swap button sitting
   between the two fields it exchanges — reads better centred than pinned
   to the start edge. */
.tool__actions--center { justify-content: center; }

.tool__result {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border-radius: var(--r-md);
  background: var(--surface-2);
  border: var(--bw) solid var(--line);
}
.tool__result[hidden] { display: none; }
.tool__result-label { font-size: var(--step--1); color: var(--text-muted); }
/* Actions need air: pressed against the result they read as part of it. */
.tool__result .tool__actions { margin-block-start: var(--sp-3); }
.tool__result-value {
  font-size: var(--step-3);
  font-weight: 650;
  color: var(--text-strong);
  line-height: 1.25;
  word-break: break-word;
  /* Numbers and Latin output inside a Persian page must not be reordered. */
  unicode-bidi: isolate;
}
.tool__result-value--sm { font-size: var(--step-1); }
.tool__result-note { font-size: var(--step--2); color: var(--text-muted); }

/* The rounded figure stays big; the precise one sits beside it, quieter. */
.tool__result-line {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
}
.tool__result-exact {
  font-size: var(--step--2);
  color: var(--text-faint);
  unicode-bidi: isolate;
}

.tool__error {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--accent-2-soft);
  border: var(--bw) solid color-mix(in srgb, var(--accent-2) 40%, transparent);
  color: var(--accent-2);
  font-size: var(--step--1);
}
.tool__error[hidden] { display: none; }

/* A hint that has just refused a keystroke: brief, and skipped entirely for
   anyone who has asked their system for less motion. */
@keyframes hint-flash {
  0%, 100% { color: var(--text-faint); }
  30%      { color: var(--accent-2); }
}
.field__hint.is-flash { animation: hint-flash 600ms var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .field__hint.is-flash { animation: none; color: var(--accent-2); }
}

/* Output that is text the visitor will take away: monospaced, selectable. */
.tool__output {
  inline-size: 100%;
  min-block-size: 120px;
  padding: var(--sp-4);
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line);
  background: var(--bg-elev);
  color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  line-height: 1.7;
  unicode-bidi: isolate;
  white-space: pre-wrap;
  word-break: break-word;
}

/* The same box in the page's own typeface, for output that is PROSE.
   There is no Persian glyph in any monospace font, so the browser swaps font
   mid-line and the answer comes out looking like a different language from
   the text that produced it. A password or a code still wants the mono
   above; a sentence never does. */
.tool__output--text {
  font-family: inherit;
  font-size: var(--step-0);
  line-height: 1.8;
}

/* The name of a GROUP of fields — «تاریخ اول» above a year/month/day row.
   It used to carry the plain field-label style and sat directly above the
   year field's OWN label, so the two read as two labels for one box. */
.tool__group-title {
  font-weight: 700;
  color: var(--text-strong);
  padding-block-end: var(--sp-2);
  margin-block-end: var(--sp-3);
  border-block-end: var(--bw) solid var(--line);
}

/* A "today" button under a titled group sits inside that group's own div,
   not as a direct child of .tool__stack, so the stack's gap never reaches
   it — it landed pressed against the grid above with nothing to say
   otherwise. */
.tool__group-title ~ .tool__actions { margin-block-start: var(--sp-3); }

/* A count field whose unit isn't obvious from the number alone — the word
   updates live beside it instead of sitting in the input's own text, so the
   field stays plain and parseable. */
.field-unit { display: flex; align-items: center; gap: var(--sp-2); }
.field-unit .input { flex: 1; }
.field-unit__word { flex: none; color: var(--text-faint); font-size: var(--step--1); white-space: nowrap; }
.field-unit__word[hidden] { display: none; }

/* --- Stats row (counters) -------------------------------------------------- */

.stats {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
}
/* Money figures run longer than a word/character count ever does, so a
   two-column box on a phone leaves too little room and the auto-shrink
   in fitStatValues() would have to go smaller than is comfortable to
   read. Opt-in, so counters elsewhere (word count, SMS parts, age in
   days) keep their compact two-column layout. */
@media (max-width: 559px) {
  .stats--wide { grid-template-columns: 1fr; }
}
.stat {
  padding: var(--sp-4);
  border-radius: var(--r-md);
  background: var(--surface-2);
  border: var(--bw) solid var(--line);
  text-align: center;
  /* A grid item's automatic minimum width is its content's min-content
     size, so an unbroken run of digits (no spaces to wrap at) was forcing
     the box wider than its own track and spilling past the card's edge
     on a narrow phone. Zeroing it lets the number wrap instead. */
  min-inline-size: 0;
}
.stat__value {
  display: block;
  font-size: var(--step-2);
  font-weight: 650;
  color: var(--text-strong);
  unicode-bidi: isolate;
  overflow-wrap: break-word;
}
.stat__label {
  display: block;
  font-size: var(--step--2);
  color: var(--text-faint);
  margin-block-start: var(--sp-1);
}
/* A pass/fail stat value (the colour-contrast checker's WCAG rows) instead of
   a number — same box, a semantic colour and an inline icon instead. */
.stat__value--ok   { color: var(--ok); }
.stat__value--fail { color: var(--accent-2); }
.stat__value--ok svg, .stat__value--fail svg { vertical-align: -3px; margin-inline-end: 4px; }

/* --- Colour picker pairs (colour-contrast checker) -------------------------- */

/* The swatch reuses .input[type="color"] from components.css (the same
   sizing image-resizer/image-compressor/image-format-converter already use
   for their background-colour picker) rather than a second, near-identical
   rule set. */
.color-field { display: flex; align-items: flex-end; gap: var(--sp-3); }
.color-field .field { flex: 1; min-inline-size: 0; }
/* A HEX code is typed/read left-to-right everywhere, including on the fa
   (RTL) site. Without this, an editable input's own bidi resolution (unlike
   a plain paragraph's) anchors a leading "#" to the page's base direction
   rather than to the letters after it — "#ffffff" was rendering as
   "ffffff#" whenever the code contained an a-f letter (a plain digit-only
   code like "#767676" happened to look fine, which is what made this easy
   to miss). Forcing the field's own direction, as `.color-row__value`
   already does elsewhere for the same reason, fixes both cases at once. */
.color-field__hex {
  font-family: var(--font-mono);
  direction: ltr;
  unicode-bidi: isolate;
  text-align: left;
}
.color-field__rgb {
  font-size: var(--step--2);
  color: var(--text-faint);
  margin-block-start: calc(-1 * var(--sp-2));
  unicode-bidi: isolate;
}

.contrast-preview {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line);
  text-align: center;
  overflow-wrap: break-word;
  /* Set inline by JS to the chosen pair; these are only a pre-JS fallback. */
  color: var(--text);
  background: var(--surface-2);
}
.contrast-preview__large  { font-size: 24px; font-weight: 700; }
.contrast-preview__normal { font-size: 16px; }

/* --- Switches and ranges --------------------------------------------------- */

.switch {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  cursor: pointer;
  font-size: var(--step--1);
  color: var(--text);
  min-block-size: 40px;
}
.switch input { inline-size: 20px; block-size: 20px; accent-color: var(--accent); cursor: pointer; }

.range { display: grid; gap: var(--sp-2); }
.range__head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-3); }
.range__value { font-weight: 650; color: var(--text-strong); unicode-bidi: isolate; }
.range input[type="range"] { inline-size: 100%; accent-color: var(--accent); block-size: 28px; }

/* --- Dropzone (file tools) -------------------------------------------------- */

.dropzone {
  display: grid;
  place-items: center;
  gap: var(--sp-2);
  padding: var(--sp-7) var(--sp-4);
  border: 2px dashed var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.dropzone:hover, .dropzone.is-over {
  border-color: var(--accent);
  background: var(--accent-soft);
}
.dropzone:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }
.dropzone__icon { color: var(--text-faint); }
.dropzone__icon svg { inline-size: 34px; block-size: 34px; }
.dropzone__title { font-weight: 650; color: var(--text-strong); }
.dropzone__hint { font-size: var(--step--2); color: var(--text-faint); }
.dropzone input[type="file"] { display: none; }

.preview {
  display: grid;
  gap: var(--sp-4);
  justify-items: center;
  padding: var(--sp-4);
  border-radius: var(--r-md);
  background: var(--bg-elev);
  border: var(--bw) solid var(--line);
}
.preview img {
  max-block-size: 320px;
  border-radius: var(--r-sm);
  background-image: linear-gradient(45deg, var(--surface-3) 25%, transparent 25%),
                    linear-gradient(-45deg, var(--surface-3) 25%, transparent 25%),
                    linear-gradient(45deg, transparent 75%, var(--surface-3) 75%),
                    linear-gradient(-45deg, transparent 75%, var(--surface-3) 75%);
  background-size: 16px 16px;
  background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}
.preview__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  justify-content: center;
  font-size: var(--step--2);
  color: var(--text-muted);
}
/* A value like "1564 × 1319" or "649 KB" is a left-to-right run. Dropped into
   a right-to-left line it is re-ordered and reads "1319 × 1564" — the width
   and height swap places without anything looking broken. Isolation alone is
   not enough; the run needs its own direction. */
.ltr, .preview__meta b {
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
}
.preview__meta b { color: var(--text-strong); }

/* The QR code's own SVG carries its own white background and black modules
   (see qr-code-generator.js) — contrast there is a scanning requirement, not
   a theme, so nothing here recolours it. Only size and framing are ours. */
.qr-preview__box {
  inline-size: 220px;
  block-size: 220px;
  padding: var(--sp-2);
  border-radius: var(--r-sm);
  background: #fff;
}
.qr-preview__box svg { display: block; inline-size: 100%; block-size: 100%; }

.busy { opacity: 0.55; pointer-events: none; }

/* --- Multi-file list (image-to-pdf and, later, any tool that orders several
   files rather than editing one) -------------------------------------------- */

.pdf-list { display: grid; gap: var(--sp-3); list-style: none; padding: 0; margin: 0; }
.pdf-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.pdf-item__thumb {
  inline-size: 48px;
  block-size: 48px;
  object-fit: cover;
  border-radius: var(--r-sm);
  flex: none;
  background: var(--surface-3);
}
.pdf-item__name {
  flex: 1 1 auto;
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 600;
  color: var(--text-strong);
}
.pdf-item__meta {
  flex: none;
  font-size: var(--step--2);
  color: var(--text-faint);
  direction: ltr;
  unicode-bidi: isolate;
  display: inline-block;
}
.pdf-item__actions { display: flex; gap: var(--sp-2); flex: none; }
.pdf-item__actions .icon-btn { inline-size: 36px; block-size: 36px; }
.pdf-item__actions .icon-btn svg { inline-size: 16px; block-size: 16px; }

/* --- GPA calculator's course rows ------------------------------------------- */

.gpa-list { display: grid; gap: var(--sp-3); list-style: none; padding: 0; margin: 0; }
.gpa-item {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.gpa-item__label {
  flex: 0 0 100%;
  font-weight: 600;
  color: var(--text-strong);
  font-size: var(--step--1);
}
.gpa-item .field { flex: 1 1 120px; min-inline-size: 0; }
.gpa-item .icon-btn { flex: none; }

/* Flex-wrap decides lines by each item's flex-basis, before any shrinking
   is applied — so on a narrow phone the two 120px field baselines alone
   already fill the row, and the remove button is pushed to a line of its
   own below them. A smaller basis here only changes that line-fitting
   decision on narrow screens; the desktop width is unaffected because
   equal flex-grow still splits the real available space evenly between
   the two fields either way. */
@media (max-width: 559px) {
  .gpa-item .field { flex-basis: 84px; }
}

/* --- Tool page content sections -------------------------------------------- */

.tool-page { display: grid; gap: var(--sp-6); }

.prose-section { max-inline-size: var(--maxw-prose); }
.prose-section h2 {
  font-size: var(--step-2);
  margin-block-end: var(--sp-3);
}
.prose-section p { color: var(--text-muted); margin-block-end: var(--sp-3); }
.prose-section ol, .prose-section ul {
  display: grid;
  gap: var(--sp-2);
  color: var(--text-muted);
  padding-inline-start: var(--sp-5);
}
.prose-section ol { list-style: decimal; }
.prose-section ul { list-style: disc; }

/* The step numbers beside «راهنمای استفاده» are drawn by the browser, not
   written in the content, so they came out as 1. 2. 3. in the middle of a
   Persian page — the one set of digits the content pass could not reach.
   `persian` is a counter style the browser already knows; it needs no
   webfont and no markup. Any numbered list added later gets it for free. */
:root[lang="fa"] .prose-section ol { list-style-type: persian; }

.privacy-note {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--ok) 8%, transparent);
  border: var(--bw) solid color-mix(in srgb, var(--ok) 35%, transparent);
  color: var(--text-muted);
  font-size: var(--step--1);
}
.privacy-note svg { color: var(--ok); flex: none; margin-block-start: 2px; }

.faq { display: grid; gap: var(--sp-3); max-inline-size: var(--maxw-prose); }
.faq__item {
  border: var(--bw) solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.faq__q {
  padding: var(--sp-4);
  font-weight: 650;
  color: var(--text-strong);
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  align-items: center;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: '+';
  color: var(--accent);
  font-size: var(--step-2);
  line-height: 1;
  flex: none;
}
.faq__item[open] .faq__q::after { content: '–'; }
.faq__a {
  padding: 0 var(--sp-4) var(--sp-4);
  color: var(--text-muted);
  font-size: var(--step--1);
  line-height: 1.75;
}

.limits-note {
  padding: var(--sp-4);
  border-radius: var(--r-md);
  border: var(--bw) solid var(--line);
  border-inline-start: 2px solid var(--accent-2);
  background: var(--surface-2);
}
.limits-note h2 { font-size: var(--step-0); margin-block-end: var(--sp-2); }
.limits-note ul { padding-inline-start: var(--sp-5); list-style: disc; color: var(--text-muted); font-size: var(--step--2); display: grid; gap: var(--sp-1); }

/* --- Home ------------------------------------------------------------------ */

.hero {
  padding-block: var(--sp-6) var(--sp-7);
  display: grid;
  gap: var(--sp-5);
  justify-items: start;
}
.hero h1 { font-size: var(--step-4); max-inline-size: 20ch; }
.hero__lede {
  font-size: var(--step-1);
  color: var(--text-muted);
  max-inline-size: 52ch;
}
.hero .search { inline-size: 100%; }

.why-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.why {
  padding: var(--sp-5);
  border-radius: var(--r-lg);
  border: var(--bw) solid var(--line);
  background: var(--surface);
}
.why h3 { font-size: var(--step-0); margin-block-end: var(--sp-2); }
.why p { font-size: var(--step--2); color: var(--text-muted); }

/* --- Section headers ------------------------------------------------------- */

.sec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-block-end: var(--sp-4);
}
.sec-head h2 { font-size: var(--step-2); }
.sec-head a { font-size: var(--step--1); color: var(--accent); }

/* --- Language switch as links ---------------------------------------------- */

.lang-switch__btn[aria-current="true"] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 650;
}

/* Both labels one step up together, so the balance between them is kept.
   Enlarging the text is what broke the alignment: the buttons were sized by
   their own padding plus line-height, so a bigger font made the language
   switch taller than the theme button sitting next to it. Both are pinned to
   the same 40px instead, and the labels centre inside that — the height no
   longer depends on the type size, in either language. */
.lang-switch, .site-header .icon-btn { block-size: 40px; }
.lang-switch__btn {
  display: grid;
  place-items: center;
  block-size: 100%;
  padding-block: 0;
  font-size: var(--step--1);
  line-height: 1;
}
@media (pointer: coarse) {
  .lang-switch, .site-header .icon-btn { block-size: 44px; }
  .lang-switch__btn { min-block-size: 0; }
}

/* --- Links inside body text ------------------------------------------------ */

/* A link buried in a paragraph of Persian had nothing but a slightly
   different grey to mark it, and people were reading straight past it.
   Colour, a proper underline set away from the descenders, and a small lift
   on hover so it answers the pointer. */
.tlink {
  color: var(--accent);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--r-sm);
  display: inline-block;
  transition: color var(--dur-1) var(--ease),
              text-decoration-color var(--dur-1) var(--ease),
              transform var(--dur-1) var(--ease);
}
/* `--accent-ink` is the colour that goes ON TOP of accent — near-black in the
   dark theme — so it is not a hover colour. The link keeps its own colour and
   gains weight instead: a solid underline, a faint wash behind it, a lift. */
.tlink:hover {
  text-decoration-color: currentColor;
  background: var(--accent-soft);
  transform: scale(1.04);
}
.tlink:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
  .tlink { transition: color var(--dur-1) var(--ease); }
  .tlink:hover { transform: none; }
}

/* --- Hints that echo a number ---------------------------------------------- */

/* The grouped echo under a money field is a left-to-right run, so `.ltr`
   gives it its own direction — and that direction then pushed the whole box
   to the left of a right-to-left field, under nothing. `justify-self` is
   resolved against the FIELD's direction, not the span's, so the box lands
   under the start of the input while the digits inside it stay in order. */
.field__hint.ltr {
  justify-self: start;
  inline-size: fit-content;
}

/* A colour input is a swatch, not a text field: at full width it looked like
   a broken input. Sized to the swatch it is, with the value beside it. */
.input[type="color"] {
  inline-size: 68px;
  block-size: 44px;
  min-block-size: 0;
  padding: 4px;
  cursor: pointer;
}
.color-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.color-row__value {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--text-muted);
  direction: ltr;
  unicode-bidi: isolate;
}

/* --- Ad / affiliate slot: reserved so nothing shifts ------------------------ */

.promo-slot {
  min-block-size: 0;
  margin-block: var(--sp-5);
}
.promo-slot:empty { display: none; }
