/* ═══════════════════════════════════════════════════════════════════════════
   a7 · About («من نحن») — «الرحلة» / The Walk: one spine, five stations.

   Theme-aware via color-mix(in oklch, hsl(var(--token)) N%, transparent).
   Bilingual: shell toggles .t-ar/.t-en on body.font-english; RTL flips keyed on
   body:not(.font-english) — one consistent mechanism, matches other sections.
   ⛔ Every token wrapped in hsl() inside color-mix (no invalid-color trap).

   ───────────────────────────────────────────────────────────────────────────
   BLOCKERS THIS FILE RESOLVES
   ───────────────────────────────────────────────────────────────────────────
   B3 · The first attempt told the integrator to delete about.css:227-239 —
        a range that swallowed `.a7ab-interest-media` and `.a7ab-interest-img`,
        the two rules the same document listed as "KEPT AND REUSED". Following
        it stripped the frame, radius, overflow clip, width:100% and
        object-fit:cover from station 02's images, because the pair layout only
        overrides max-width / margin / aspect-ratio.
        RESOLVED BY CONSTRUCTION: this is a whole file, not a delete range.
        `.a7ab-interest-media` and `.a7ab-interest-img` are present below,
        byte-identical to what ships today, under the «STATION 02» heading.
        What actually went: `.a7ab-interest` (the zig-zag grid), its
        `+ .a7ab-interest` gap, its `:nth-of-type(2n)` flip and that flip's
        640px cancel, and `.a7ab-interest-idx` (the 01/02 badges — the page
        keeps exactly ONE numeral series, `.a7ab-led-idx`).

   B4 · The reduced-motion overrides tie EXACTLY on specificity with their
        normal-motion counterparts (`.a7ab-jr-st.a7ab-reveal.is-in` 0,3,0 vs
        0,3,0; `.a7ab-jr-st.is-in::before` 0,2,1 vs 0,2,1; and so on), and
        @media contributes ZERO specificity — so SOURCE ORDER is the only
        tie-break, and the previous instructions put the block in the wrong
        place. A user who asked for no motion still got the pin pop, the
        station rise, the three ticks, the six-row ledger stagger and the
        directory's 6px hover shift.
        RESOLVED STRUCTURALLY, not by hand: this file now contains exactly ONE
        `@media (prefers-reduced-motion: reduce)` block and it is the LAST
        thing in the file. Every animation and transition it cancels is
        declared above it. ⛔ NOTHING may be appended after that block. If you
        add an animated rule, it goes above it and gets a line inside it.
        ⛔ SOURCE ORDER IS NOT ENOUGH ON ITS OWN — the cancel must also MATCH OR
        BEAT the original selector's specificity. Two escapes were found by a
        specificity-aware re-check after the first pass and are fixed below:
        `.a7ab-crumb` could never cancel `.a7ab-crumb[href]` (0,1,0 vs 0,2,0),
        and the primary CTA's `.a7ab-btn:hover .a7ab-btn-arrow` transform had no
        counterpart at all. Verify this block by specificity, never by matching
        class names — a name-matching pass reports both as covered.

   Also fixed here, beyond the numbered blockers:
   • The station entrance no longer uses a TRANSFORM. `a7ab-pf-rise` translates
     10px, and a transform on `.a7ab-jr-st` becomes the containing block for
     its own `::before` — so each blue pin was dragged 10px BELOW the rail for
     550ms while the rail (drawn on the untransformed `.a7ab-jr::before`) held
     still. That degrades the one device the owner named as beautiful. Stations
     now fade only; the pin's own scale-pop supplies the motion and scales
     about its own centre, so it can never leave the line.
   • `.a7ab-soc` (~39.6px) and `.a7ab-lane` (~43.4px) get min-height:44px.
     `.a7ab-crumb[href]` in this same file already proved the rule was known.
   • The two stale comments that asserted "no percentage is rendered anywhere"
     while the rail renders `pct` are rewritten to describe what ships.
   • `.a7ab-pf.is-in .a7ab-pf-permit` (matched no element — the real class is
     `.a7ab-permit`) is gone. So are `.a7ab-pf-spine`, its 620px override,
     `@keyframes a7ab-pf-node`, `.a7ab-h2`, `.a7ab-sec-head`, `.a7ab-mission*`,
     `.a7ab-quote*`, `.a7ab-offer-grid`, `.a7ab-tile*`, `.a7ab-cta*` and the
     orphaned `.a7ab-decode` container.
   ═══════════════════════════════════════════════════════════════════════════ */
.a7ab-wrap{
  --a7ab-ink: hsl(var(--base-content));
  --a7ab-primary: hsl(var(--primary));
  --a7ab-muted: color-mix(in oklch, hsl(var(--base-content)) 68%, transparent);
  --a7ab-faint: color-mix(in oklch, hsl(var(--base-content)) 42%, transparent);
  --a7ab-line: color-mix(in oklch, hsl(var(--base-content)) 12%, transparent);
  --a7ab-line-strong: color-mix(in oklch, hsl(var(--base-content)) 22%, transparent);
  --a7ab-card: color-mix(in oklch, hsl(var(--base-content)) 4%, transparent);
  --a7ab-card-bd: color-mix(in oklch, hsl(var(--base-content)) 10%, transparent);
  --a7ab-tint: color-mix(in oklch, hsl(var(--primary)) 8%, transparent);
  --a7ab-tint-bd: color-mix(in oklch, hsl(var(--primary)) 22%, transparent);
  /* ── radius scale ──────────────────────────────────────────────────────────
     FOUR steps, and nothing else. Every rounded thing on this page picks one:
       --a7ab-r     card / container surfaces
       --a7ab-r-in  small surfaces (chips, tags, notes) AND anything nested
                    inside a card — radius has to scale with the element, or a
                    constant value reads featherweight on a 300px card and
                    almost pill-like on a 30px chip
       999px        interactive pills — chips, buttons, tags, the rail
       50%          true circles — dots, avatars, step markers
     --a7ab-r-in is not a taste call: a child's radius should be the parent's
     minus the gap between them (14 − ~5), otherwise the two curves run at
     different speeds and the nesting looks wrong even when both "look round".
     ⛔ Do NOT add a fifth value. If something needs one, it belongs to a step
     above and the step is what should move. */
  --a7ab-r: var(--a7-radius-card, 14px);
  --a7ab-r-in: 9px;
  max-width: 880px;
  margin-inline: auto;
  padding-inline: clamp(18px, 5vw, 32px);
  padding-block: clamp(26px, 6vw, 52px) clamp(60px, 11vw, 112px);
  color: var(--a7ab-ink);
  box-sizing: border-box;
}
.a7ab-wrap *, .a7ab-wrap *::before, .a7ab-wrap *::after{ box-sizing: border-box; }
.a7ab-wrap img{ max-width: 100%; }
.a7ab-wrap h1, .a7ab-wrap h2, .a7ab-wrap h3{ text-wrap: balance; }

/* bilingual visibility — Arabic default; English when the shell sets body.font-english */
.a7ab-wrap .t-en{ display: none; }
body.font-english .a7ab-wrap .t-ar{ display: none; }
body.font-english .a7ab-wrap .t-en{ display: inline; }

/* reveal — scroll-triggered by about.js (.is-in). Base rests VISIBLE, so if JS
   never runs / the tab is paused, all content still shows. The 18px lurch is
   for the OFF-SPINE elements only (titlerow, hero); stations override it. */
@keyframes a7ab-rise{ from{ opacity: 0; transform: translateY(18px); } }
.a7ab-reveal.is-in{ animation: a7ab-rise .6s cubic-bezier(.16, 1, .3, 1); }

/* breadcrumb */
.a7ab-crumbs{
  display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
  font-size: .85rem; color: var(--a7ab-muted);
  margin-bottom: clamp(22px, 4vw, 36px);
}
/* ≥44px tap area for the link without changing its visual position */
.a7ab-crumb{ color: inherit; text-decoration: none; border-radius: var(--a7ab-r-in); }
.a7ab-crumb[href]{
  display: inline-flex; align-items: center; min-height: 44px;
  padding-inline: 4px; margin-inline: -4px; transition: color .18s ease;
}
.a7ab-crumb[href]:hover{ color: var(--a7ab-primary); }
.a7ab-crumb-now{ color: var(--a7ab-ink); font-weight: 600; }
.a7ab-crumb-sep{ color: var(--a7ab-faint); display: inline-block; }
body:not(.font-english) .a7ab-crumb-sep{ transform: scaleX(-1); }

/* titlerow — section's own name between two fading hairlines (RTL flips) */
.a7ab-titlerow{ display: flex; align-items: center; gap: clamp(14px, 3vw, 26px); }
.a7ab-hair{ flex: 1 1 0; height: 1px; min-width: 20px; }
.a7ab-hair-s{ background: linear-gradient(to right, transparent, var(--a7ab-line-strong)); }
.a7ab-hair-e{ background: linear-gradient(to right, var(--a7ab-line-strong), transparent); }
body:not(.font-english) .a7ab-hair-s{ background: linear-gradient(to left, transparent, var(--a7ab-line-strong)); }
body:not(.font-english) .a7ab-hair-e{ background: linear-gradient(to left, var(--a7ab-line-strong), transparent); }
.a7ab-title{
  margin: 0; text-align: center; white-space: nowrap;
  font-size: clamp(1.6rem, 4.6vw, 2.4rem); font-weight: 800; line-height: 1.1; letter-spacing: -.01em;
}
.a7ab-title .t-en{ letter-spacing: -.02em; }
/* the subtitle is the page's table of contents — it names all five stations */
.a7ab-subtitle{
  margin: 14px auto 0; max-width: 52ch; text-align: center;
  color: var(--a7ab-muted); font-size: clamp(1rem, 2.4vw, 1.08rem); line-height: 1.6;
}

/* divider — 440px, fade both ends. THE ONLY ONE LEFT OF SIX: it marks
   cover → path and nothing else. RETUNED from clamp(46px,8vw,84px): ~169px of
   combined margin was the measured cause of the "disconnected slabs" reading,
   and at ~96px this is a breath between the cover and the walk, not a wall.
   ⛔ width / height / background are load-bearing — a margin-only rewrite of
   this block renders a 0px element with no gradient, i.e. no divider at all. */
.a7ab-rule{
  width: min(440px, 78%); height: 1px; margin: clamp(38px, 6vw, 58px) auto;
  background: linear-gradient(to right, transparent, var(--a7ab-line-strong), transparent);
}

/* ═══════════════════════════════════════════════════════════════════════════
   THE COVER (off-spine)
   ═══════════════════════════════════════════════════════════════════════════ */
.a7ab-hero{
  display: grid; grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  gap: clamp(24px, 5vw, 60px); align-items: end;   /* he stands on the ground, not centred in the cell */
  margin-top: clamp(34px, 6vw, 62px);
}
/* ── the portrait · a cutout on a stage ──────────────────────────────────────
   The asset is a TRIMMED ALPHA CUTOUT (1347×2234): background removed via Vision,
   margin trimmed, so THE FILE'S BOUNDS ARE THE SUBJECT'S BOUNDS. That is what
   lets the percentage mask stops below map to fixed anatomy at every width. The
   closed rounded box is GONE — radius, card plate, overflow clip, box-shadow and
   the ::after 1px border all deleted. Three layers replace it:
     ::before  z0 · residual light  — the dissolved remnant of the studio floor
     ::after   z1 · THE GROUND      — contact ellipse + pool
     picture   z2 · the silhouette shadow (filter lives HERE, not on the img)
       img          the subject     — the bottom melt (mask lives HERE)

   ⛔ THE FILTER IS ON <picture>, THE MASK IS ON <img>, AND THAT SPLIT IS LOAD-
   BEARING. Within one element CSS renders filter → clip → mask, and mask-clip
   defaults to border-box, so the mask would shave its own drop-shadow flat at the
   file edges — and because the asset is TRIMMED the subject touches all four
   edges, putting that seam on the crown of the ghutra and the widest shoulder.
   On the parent the filter is computed from the already-masked child, is not
   clipped to a border box, and inherits the melt for free: the cast shadow
   dissolves with the body, no shadow stub under the hem.

   ⛔ SELF-INVERTING BY TOKEN, NO MEDIA QUERY. hsl(var(--base-content)) is dark on
   the light theme and light on the dark one, so ONE declaration is a cast SHADOW
   on light (which is what rescues a white thobe on a near-white page) and a POOL
   OF LIGHT on dark. prefers-color-scheme is deliberately NOT used: it follows the
   OS, which the site's own theme toggle does not touch, so on an OS-dark machine
   viewing the LIGHT theme it would weaken the ground on the exact case this
   design exists to fix.

   ⛔ Mask stops are ALPHA, not ink — plain rgb() there is correct, not a token
   violation. The colour rule governs the two gradients and the filter.
   ⛔ overflow MUST stay visible, or the ground is clipped back into a rectangle. */
.a7ab-portrait{
  position: relative;
  isolation: isolate;
  margin: 0;
  /* the one knob. Crown 0%, chin ~20%, shoulders ~26-28%; 52% is mid-chest — a
     32-point clearance below his chin at EVERY width, because the img carries the
     asset's true aspect-ratio. Safe down to ~44% before the shoulders thin.
     ⛔ Coupled to the trim: re-export untrimmed and every stop climbs to his face. */
  --a7ab-melt-start: 66%;   /* the one knob: raise = more solid, lower = longer fade */
  /* ⛔ MANY stops, deliberately. A near-white thobe is a huge flat field, which
     is the worst case for 8-bit gradient banding — with 5 stops the ramp showed
     as visible horizontal steps ("كأنه له خط"). Doubling the stops keeps every
     step under ~7% alpha, below the banding threshold on flat cloth. */
  --a7ab-melt: linear-gradient(to bottom,
    rgb(0 0 0)       0%,
    rgb(0 0 0)       var(--a7ab-melt-start),
    rgb(0 0 0 / .93) 72%,
    rgb(0 0 0 / .84) 78%,
    rgb(0 0 0 / .71) 83%,
    rgb(0 0 0 / .55) 87%,
    rgb(0 0 0 / .38) 90%,
    rgb(0 0 0 / .23) 93%,
    rgb(0 0 0 / .11) 95%,
    rgb(0 0 0 / .04) 97%,
    rgb(0 0 0 / 0)   99%);   /* gone BEFORE the file's hard bottom cut can show */
}

/* the silhouette. Two tight passes, not one hard band: a 1px core plus a 3px
   falloff reads as an EDGE, where a single uniform trace reads as a sticker
   outline — the one failure mode that makes a cutout look pasted. Zero offset on
   both, so it follows the whole contour (shoulders, sleeves, the outer curve of
   the ghutra), which is precisely why it can replace a border.
   ⛔ Plain rgb() fallback FIRST: color-mix() inside drop-shadow() fails as a whole
   unit, silently taking the entire filter with it — and the light theme is the
   one that cannot afford to lose it. */
.a7ab-portrait picture{
  display: block;                 /* <picture> is inline by default */
  position: relative; z-index: 2;
  /* ⛔ THE ZERO-OFFSET CONTOUR SHADOWS ARE GONE. `drop-shadow(0 0 …)` traces the
     ENTIRE silhouette equally — both sides, the sleeves, the ghutra — and on a
     near-white page that reads as a hard outline: the owner saw it immediately
     («و في حدود بعد يمين و يسارها»). It was added to rescue white-on-white and
     it created a sticker instead. A cutout must have NO contour. What stays is a
     single offset CAST shadow — it falls downward like real light, so it can
     never appear as a border beside him. */
  filter: drop-shadow(0 18px 26px rgb(0 0 0 / .13));
  filter: drop-shadow(0 18px 26px color-mix(in oklch, hsl(var(--base-content)) 13%, transparent));
}

.a7ab-portrait-img{
  display: block; width: 100%; height: auto;
  aspect-ratio: 1347 / 2234;      /* the asset's true ratio — never crop a cutout */
  -webkit-mask-image: var(--a7ab-melt);
          mask-image: var(--a7ab-melt);
  -webkit-mask-size: 100% 100%; -webkit-mask-repeat: no-repeat;
          mask-size: 100% 100%;         mask-repeat: no-repeat;
}

/* ⛔ NO GROUND POOL, NO BLOOM — deliberately removed, and this is a design
   decision not an omission. A cast pool on the floor means "a solid body standing
   on a surface"; the melt means "a body with no bottom". Those two metaphors
   contradict each other, so the pool was a shadow with no caster — which is why
   every position for it looked wrong. The owner's instinct was right.

   Presence on the near-white light theme is carried entirely by the single cast
   drop-shadow on <picture> above: it follows his actual silhouette (not a box),
   and because the filter is computed from the already-masked child it DISSOLVES
   WITH HIM — no shadow is left hanging under a figure that is no longer there. */


.a7ab-hero-body{ min-width: 0; }
.a7ab-name{
  margin: 0; font-size: clamp(2.3rem, 7vw, 3.5rem);
  font-weight: 800; line-height: 1.04; letter-spacing: -.015em;
}
.a7ab-name .t-en{ letter-spacing: -.03em; }
/* signature — a single stroke that draws itself under the name */
/* signature — one pen stroke, drawn under the name.
   ⛔ These are BASE rules. They replace about.css:276-281 IN PLACE, which puts
      them above EVERY @media in the file (first is `@media (hover: hover)` at
      563; the terminal reduce block is at 1575). Do not relocate them lower —
      base and breakpoint rules tie on specificity and source order decides.

   GEOMETRY CONTRACT — re-derive all four numbers after ANY edit to either `d`:
     · max ink half-width          3.180u   vs mask half-band 4.500u  (1.32u clearance)
     · min spine self-clearance   24.94u    vs required 4.50+3.18 = 7.68u
     · ink bbox  x 3.77..199.92, y 1.96..20.22   (inside 0 0 205 28.1875)
     · ink and guide are ONE subpath each — SVG restarts the dash pattern at every
       subpath, so a second subpath would paint in full on the first frame.       */
.a7ab-name-sig{ display: block; width: 168px; max-width: 62%; margin-top: 12px; color: hsl(var(--primary)); }
.a7ab-sig-svg{ display: block; width: 100%; height: auto; overflow: visible; }
.a7ab-sig-wipe{ stroke-dasharray: 1; stroke-dashoffset: 0; }   /* base = mask fully open = finished mark */

/* ⛔ the flip must stay on .a7ab-sig-svg. The mask is maskUnits="userSpaceOnUse"
   INSIDE this svg, so one declaration mirrors the ink AND the direction the pen
   front travels. Moving it to an inner <g> leaves the mask unflipped and the
   reveal runs backwards against a mirrored mark. Verified in WebKit: at 47% the
   LTR frame paints x 6..205, the mirrored frame paints x 67..266.               */
body:not(.font-english) .a7ab-sig-svg{ transform: scaleX(-1); }

/* `.a7ab-sig-go` is added by about.js ONLY when the document is visible, and
   removed again on visibilitychange->hidden. No animation-fill-mode and no
   delay: there is no window in which a from-frame can be pinned. If the JS is
   never deployed the rule simply never matches and the finished mark stands. */
.a7ab-hero.is-in.a7ab-sig-go .a7ab-sig-wipe{ animation: a7ab-sign .95s cubic-bezier(.33, 0, .2, 1); }
@keyframes a7ab-sign{ from{ stroke-dashoffset: 1; } to{ stroke-dashoffset: 0; } }

/* «A7 = أحمد» — decode the wordmark (7 stands for ح in Arabizi).
   The container rule is gone with the hero footnote it framed; the mark and
   its gloss now live at station 01 inside .a7ab-jr-decode, which supplies the
   layout. These two are shared, so they stay here. */
/* ⛔ inline-BLOCK, never inline-flex. A flex container wraps each contiguous
   text run in its own anonymous item, so "A<b>7</b> = أ<b>ح</b>مد" split into
   FIVE independent shaping runs — Arabic joining cannot cross an item boundary,
   so «ح» rendered in ISOLATED form and «أ | ح | مد» sat as three gapped
   fragments. It is the owner's own name, and station 01 promotes this mark to
   ~38px display type, so the break is unmissable there. inline-block keeps one
   inline formatting context: shaping crosses the inline <b> boundaries and
   «أحمد» joins correctly, with no markup change. The gap goes with it (it was
   the flex gutter that widened the fracture). */
.a7ab-decode-mark{
  display: inline-block;
  padding: .3rem .7rem; border-radius: var(--a7ab-r); font-weight: 800; letter-spacing: .01em;
  background: var(--a7ab-tint); border: 1px solid var(--a7ab-tint-bd); color: var(--a7ab-ink);
}
.a7ab-decode-mark b{ color: var(--a7ab-primary); font-weight: 900; }
/* ⛔ the Arabic «ح» highlight is COLOUR-ONLY (no weight change): a heavier <b>
   forces a separate bold font face, and Arabic shaping does NOT join letters
   across a face boundary → «ح» detaches into «أح مد» (esp. mobile, static
   weights). Inheriting the container's 800 keeps it in one face → «أحمد» joins. */
.a7ab-decode-mark .a7ab-hl{ color: var(--a7ab-primary); font-weight: inherit; }
.a7ab-decode-why{ color: var(--a7ab-muted); font-weight: 600; }

/* role line with location dot */
.a7ab-role{
  display: flex; align-items: center; flex-wrap: wrap; gap: .55rem;
  margin: .8rem 0 0; color: var(--a7ab-muted);
  font-size: clamp(.92rem, 2.3vw, 1rem); font-weight: 600;
}
.a7ab-role .t-en{ letter-spacing: .01em; }
.a7ab-role-dot{ width: 4px; height: 4px; border-radius: 50%; background: var(--a7ab-faint); flex: 0 0 auto; }
.a7ab-bio{
  margin: clamp(16px, 3vw, 22px) 0 0; max-width: 46ch;
  color: color-mix(in oklch, hsl(var(--base-content)) 84%, transparent);
  font-size: clamp(1rem, 2.5vw, 1.08rem); line-height: 1.75; text-wrap: pretty;
}

/* ── the hero stat pill ─────────────────────────────────────────────────────
   .a7ab-stat-metric is a baseline-aligned 999px stadium. Its label has a hard
   length budget: at 880px the hero body is ~424px and the 2rem figure plus
   padding leaves ~290px for the label; at 360px the hero collapses to one
   column and the whole pill gets 324px. So the pill carries the SHORT
   noun-bearing label (the string that ships today, digits derived) and the
   forward-pointing sentence lives on its own .a7ab-stat-hint line beneath,
   where wrapping is free. The group stacks.

   ⚠ HONEST STATE, measured — an earlier revision of this comment claimed the
   label was "proven to fit at 360px and 880px" and that was false. It fits from
   ~414px up. Below that it wraps to two lines: English overruns by ~37px
   (", 6 channels" at .88rem), Arabic by a hair. LIVE wraps identically with the
   same string, so this is a standing defect rather than a regression, and the
   real cure is shorter English copy — an owner decision. What this file does is
   make the degradation graceful instead of broken: flex-wrap keeps it inside
   the box, and the ≤420px step-down at the bottom of this file buys back enough
   width that most phones land on one line.

   ⛔ The FIGURE and its UNIT are separate spans. `direction: ltr` belongs on the
   digits ONLY. When "8.73 مليون" sat inside one ltr span, the unit was laid to
   the RIGHT of the digits, so an Arabic reader scanning right-to-left met
   «مليون» first — «مليون ٨٫٧٣» — while station 04's .a7ab-pf-total rendered the
   same value correctly, because it has always kept its unit in a sibling. Cover
   and receipt now read in the same order. .a7ab-stat-fig inherits the page
   direction, so its two children order themselves correctly in both languages
   with no override. */
.a7ab-stat{
  display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
  margin-top: clamp(20px, 4vw, 28px);
}
/* the pill IS the <a>, so the link reset has to live on it — without
   text-decoration:none the browser underlines both spans. */
.a7ab-stat-metric{
  display: inline-flex; align-items: baseline; gap: .3rem;
  /* the label can wrap to its own line below ~414px (see the note above); this
     keeps that as a taller card instead of an overflow */
  flex-wrap: wrap;
  padding: .7rem 1.1rem;
  /* CARD radius, not a pill. At 318×69 (and taller when the label wraps) a
     999px pill draws a ~34px curve that fights the 14px surfaces beside it —
     it reads as a different design language, not as a chip. Pills are kept
     for single-line chips and for .a7ab-btn, which earns its own shape. */
  border-radius: var(--a7ab-r);
  background: var(--a7ab-tint); border: 1px solid var(--a7ab-tint-bd);
  color: inherit; text-decoration: none;
  transition: border-color .18s ease, background .18s ease;
}
/* figure + unit travel together and break together — never mid-number */
.a7ab-stat-fig{ display: inline-flex; align-items: baseline; gap: .28em; }
.a7ab-stat-num{
  font-size: clamp(1.5rem, 4.6vw, 2rem); font-weight: 800; line-height: 1;
  color: var(--a7ab-primary); letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate;
}
.a7ab-stat-unit{
  font-size: clamp(.9rem, 2.6vw, 1.1rem); font-weight: 800; line-height: 1;
  color: var(--a7ab-primary);
}
.a7ab-stat-label{
  margin-inline-start: .28rem;
  font-size: .88rem; font-weight: 600; color: var(--a7ab-muted);
}
/* the forward pointer the pill's href already performs, said in words. MUTED,
   not faint: it is the only thing telling a scanner the receipt exists. */
.a7ab-stat-hint{
  margin: 0; max-width: 40ch;
  color: var(--a7ab-muted); font-size: .82rem; font-weight: 600; line-height: 1.55;
  text-wrap: pretty;
}
/* ═══════════════════════════════════════════════════════════════════════════
   STATION 02 · «وهذان هما المجالان» — the two fields as a HANGING-GLYPH PAIR.

   Replaces .a7ab-int-pair / .a7ab-int-cell / .a7ab-interest-media /
   .a7ab-interest-img. Those rules existed only to frame and clip two raster
   PNGs; there is no raster left to clip.
   ⛔ BEFORE DELETING THEM: grep the repo for `a7ab-interest` and `a7ab-int-`.
   page.py's B3 note records a prior over-deletion of exactly this range. Note
   that `.a7ab-interest-text{ min-width: 0 }` sits INSIDE that range and is a
   different rule — check by SELECTOR, never by line number. Also delete the
   `.a7ab-int-pair` overrides in the @media (max-width: 560px) block, including
   the one carrying `gap` (a surviving gap would double the mobile gutter).

   ⛔ BEHAVIOUR CHANGE, stated deliberately so nobody "restores" it: the old
   pair stacked at 560px; this stacks at 700px, inside the existing block. The
   561–700px band is where a two-up is tightest with a 48px trough, so it
   stacks earlier on purpose.

   No surface. At 44px a 1px border around the mark would be a box drawn around
   a bullet — and it is the card that made a 4:1 picture-to-text ratio look
   deliberate. Station 02 becomes typographic, which is also what keeps station
   05 the only filled surface on the page.

   Radius: this block introduces NO radius at all. Nothing here is a surface,
   so --a7ab-r / --a7ab-r-in are correctly not referenced.

   ⛔ Direction is NOT declared in CSS anywhere on this page — pinDir() in
   about.js sets it and re-applies it on language flip. Every logical property
   below therefore depends on that JS having run. This is the same contract the
   rest of the station lives under; it is written down here so the next reader
   does not assume a CSS floor exists.
   ═══════════════════════════════════════════════════════════════════════════ */
.a7ab-fields{
  --a7ab-fx-mark: clamp(38px, 4.6vw, 48px);   /* the glyph column */
  --a7ab-fx-i: 0;                             /* per-cell index, drives stagger */
  --a7ab-fx-nudge: 1px;                       /* optical: Arabic (Tajawal) */
  list-style: none;
  margin: clamp(18px, 2.8vw, 24px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(28px, 4vw, 48px);
  row-gap: clamp(22px, 6vw, 30px);
}
/* Arabic and Latin sit differently inside the same line box, so the mark's
   optical centring is per-script. ⛔ It MUST be expressed as a margin — the
   beat keyframes own `transform` and would silently clobber a translate. */
body.font-english .a7ab-fields{ --a7ab-fx-nudge: 2px; }

.a7ab-fx{
  min-width: 0;
  display: grid;
  grid-template-columns: var(--a7ab-fx-mark) minmax(0, 1fr);
  column-gap: clamp(.6rem, 1.4vw, .85rem);
  row-gap: .38rem;
  align-items: start;
}
.a7ab-fx + .a7ab-fx{ --a7ab-fx-i: 1; }

/* ⛔ NO DIVIDER between the cells, on purpose. A 1px vertical rule here would
   be the third ruling system in ~150px of scroll: the journey's own rail
   (.a7ab-jr::before), this rule, then station 03's hairline rows. The trough
   separates the pair; stacked, vertical rhythm does. */

/* ── THE RING ───────────────────────────────────────────────────────────────
   The owner's reference is a ringed badge: a tinted disc, a hairline ring, the
   mark centred inside — and the RING is what pulses, radar-style.
   ⛔ This replaced a version that scaled the glyph itself by 4%. That was under
   1px of travel and was invisible on a real device. An expanding ring reads at
   a glance because the moving edge sweeps several px across a static ground,
   which is a far stronger signal than a symbol changing size. */
.a7ab-fx-ring{
  grid-column: 1; grid-row: 1;
  align-self: center;
  position: relative;
  display: grid; place-items: center;
  inline-size: var(--a7ab-fx-mark);
  block-size:  var(--a7ab-fx-mark);
  margin-block-start: var(--a7ab-fx-nudge);
  border-radius: 50%;                       /* a TRUE circle — the scale allows 50% */
  background: var(--a7ab-tint);
  box-shadow: 0 0 0 1px var(--a7ab-tint-bd) inset;
  color: var(--a7ab-primary);
}
/* the pulse itself: a ring that expands out of the disc edge and fades.
   ⛔ pointer-events:none — it overflows the badge and must never eat a tap. */
.a7ab-fx-ring::after{
  content: ""; position: absolute; inset: 0;
  border-radius: 50%;
  border: 2px solid var(--a7ab-primary);
  opacity: 0;                                /* base = invisible, rests clean */
  pointer-events: none;
}
.a7ab-fx-mark{
  display: block;
  inline-size: 58%; block-size: 58%;         /* the glyph sits INSIDE the disc */
  color: var(--a7ab-primary);
}

/* Type: reuse .a7ab-h3 / .a7ab-p and override only what the inline pairing
   needs. Both selectors are (0,2,0) and beat the (0,1,0) base rules. */
.a7ab-fx .a7ab-h3{
  grid-column: 2; grid-row: 1;
  align-self: center;
  margin: 0;
}
.a7ab-fx .a7ab-p{
  grid-column: 2; grid-row: 2;
  margin: 0;
  /* ⛔ em, not ch. `ch` is the advance of "0" in the FIRST font of the stack —
     a Latin metric — and this paragraph renders in two scripts and two faces.
     An em cap scales with whatever face is actually serving the text. */
  max-width: 21em;
}
/* ⛔ Scoped to .t-en ONLY. Arabic is a connected script: a mid-word break
   rewrites the contextual letterforms on both sides of the break, so the word
   changes shape and reads as a misspelling. Never apply this to .t-ar. */
.a7ab-fx .a7ab-p .t-en{ overflow-wrap: break-word; }

/* Row 2 / column 1 is left empty on purpose: the description hangs under the
   TITLE, the mark hangs outside the text block. That indent is the editorial
   idea — a marginal mark, not a header image — and because it is built from
   grid tracks it lands on the reading-start side in both directions. */

/* ── MOTION ─────────────────────────────────────────────────────────────────
   ⛔ Base state IS the rested state: fully drawn (dashoffset 0), scale 1,
   opacity 1. The block is complete at frame zero with JS off, in a background
   tab, and in a headless renderer. Nothing here gates visibility.

   ⛔ These rules must be declared ABOVE the file's single terminal
   @media (prefers-reduced-motion: reduce) block. Nothing may be appended after
   that block — the cancels below are specificity TIES broken by source order,
   and @media adds no specificity.

   The trigger is a dedicated observer on .a7ab-fields, NOT the station's
   .a7ab-jr-st.is-in. That class lands at threshold 0.08 on a station tall
   enough that it fires several hundred px of scroll before these marks enter
   the viewport — the draw-on would be over before anyone saw it. */

.a7ab-fx-stroke{ stroke-dasharray: 1; stroke-dashoffset: 0; }   /* base = drawn */

.a7ab-fields.is-fx-in .a7ab-fx-stroke{
  animation-name: a7ab-fx-draw;
  animation-duration: .8s;
  animation-timing-function: cubic-bezier(.16, 1, .3, 1);
  animation-delay: calc(var(--a7ab-fx-i) * .14s);
  animation-fill-mode: backwards;
  animation-iteration-count: 1;
}
@keyframes a7ab-fx-draw{ from{ stroke-dashoffset: 1; } to{ stroke-dashoffset: 0; } }

/* THE BEAT — «ينبضون بشكل جميل». 4% over 4.6s is ~0.9px of excursion at 44px:
   felt, not watched. IDENTICAL on both marks so the pair reads as a set, and
   counter-phased by half a cycle so one exhales while the other inhales.
   ⛔ FINITE. Three breaths on arrival, then rest. `.is-fx-in` is never removed,
   so `infinite` here would be permanent peripheral motion beside body copy —
   which is the case constraint 6 forbids. Nothing anywhere loops on opacity. */
.a7ab-fields.is-fx-in .a7ab-fx-ring::after{
  animation-name: a7ab-fx-beat;
  animation-duration: 2.4s;
  animation-timing-function: cubic-bezier(.22,.61,.36,1);
  animation-delay: calc(.4s + var(--a7ab-fx-i) * 1.2s);
  animation-iteration-count: infinite;
  animation-fill-mode: backwards;
}
@keyframes a7ab-fx-beat{
  0%  { transform: scale(1);    opacity: .55; }
  70% { opacity: 0; }
  100%{ transform: scale(1.55); opacity: 0; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   SCROLL REVEAL — «مثل موقع أبل». Content ARRIVES as you scroll instead of
   already being there. The page had ONE reveal (a7ab-rise, 18px, .6s) fired per
   SECTION, which reads as a section appearing — not as content arriving. This
   stages each station's CHILDREN on their own beat.

   ⛔ RESTS VISIBLE. Base state IS the arrived state: opacity 1, no transform,
   no blur. Only `.is-in` — added by the observer that already ships — opts an
   element in, and the animation ENDS exactly where the base state already was.
   JS off, background tab, headless renderer: the page is complete. This file
   has shipped blank sections from reveal-gating before. Not again.
   ⛔ ANIMATION, NOT TRANSITION: a transition would require a hidden start
   state, which is that exact trap.
   ⛔ transform + opacity + filter only — all compositor-friendly. Zero JS, zero
   requests, zero markup change. */
/* ⛔ THE HIDDEN STATE IS GATED ON `html.a7ab-js`, AND THAT GATE IS THE WHOLE
   DESIGN. The marker is set by an inline script during head parse (page.py),
   so it is present before the body paints.
     JS on  → children start hidden, then arrive on scroll.  ← the effect
     JS off → this rule never matches, everything renders visible. ← the safety
   The first version had no gate: children shipped VISIBLE and the animation
   started from opacity 0, so each one flashed present → gone → back. The owner
   reported exactly that. Do not "simplify" the gate away. */
html.a7ab-js .a7ab-jr-st > *{
  opacity: 0;
  transform: translateY(26px);
  filter: blur(6px);
  will-change: opacity, transform;
}
/* forwards, so the arrived state STICKS after the animation ends */
html.a7ab-js .a7ab-jr-st.is-in > *{
  animation: a7ab-arrive .72s cubic-bezier(.16, 1, .3, 1) forwards;
  will-change: auto;
}
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(1){ animation-delay: .02s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(2){ animation-delay: .10s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(3){ animation-delay: .18s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(4){ animation-delay: .26s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(5){ animation-delay: .34s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(6){ animation-delay: .42s; }
html.a7ab-js .a7ab-jr-st.is-in > *:nth-child(n+7){ animation-delay: .50s; }

@keyframes a7ab-arrive{
  from{ opacity: 0; transform: translateY(26px); filter: blur(6px); }
  to  { opacity: 1; transform: none;             filter: blur(0);   }
}


/* Layer separation is deliberate: the draw animates <path>, the beat animates
   the <svg>. Different elements, so no `animation` shorthand can ever silently
   replace the other. Keep it that way. */

@media (hover: hover){
  .a7ab-stat-metric:hover{
    border-color: color-mix(in oklch, hsl(var(--primary)) 45%, transparent);
    background: color-mix(in oklch, hsl(var(--primary)) 12%, transparent);
  }
  .a7ab-stat-metric:hover .a7ab-stat-label{ color: var(--a7ab-primary); }
}
.a7ab-stat-metric:focus-visible{ outline: 2px solid hsl(var(--primary)); outline-offset: 3px; }

/* focus tags — moved down to station 01, under the promoted decode */
.a7ab-tags{ display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0; padding: 0; }
.a7ab-tags li{
  font-size: .84rem; font-weight: 600; color: var(--a7ab-muted);
  padding: .5rem .8rem; border-radius: var(--a7ab-r-in);
  background: var(--a7ab-card); border: 1px solid var(--a7ab-card-bd);
}

/* follow map — real handles (per-platform counts stay owner-supplied).
   min-height:44px: these six are the most-tapped links on the page and were
   ~39.6px (.55rem padding + a ~22px line box). */
/* ── follow map · 2×3 ledger ─────────────────────────────────────────────────
   WAS: a wrapping flex row. Three rows of two, each a different total width
   (405 / 331 / 378px measured at a 415px column) against one shared start
   edge. A straight start edge and a ragged end edge is what «عشوائيين» means.

   ⛔ THE GRID IS NOT THE FIX. `.a7ab-soc-tx` stacking name OVER handle is.
   Inline, a chip costs `name + .4rem + handle` — the 57px spread is the SUM of
   two free variables. Stacked, it costs `max(name, handle)`, and since the six
   handles are near one width while the names swing wide, the handle becomes a
   FLOOR under the short names («إكس» / "X") and the name a CEILING over the
   long ones. The two variables stop adding and start covering for each other.
   COROLLARY, and the reason the handle must NOT be hoisted into a caption:
   name-only chips are sized by the name alone, so the spread REOPENS (wider
   than today in English, where "X" is ~11px against "Instagram" ~75px) and
   X's cell reads empty. The handle is what fills X's cell.

   ⛔ `width: fit-content` + `minmax(0,1fr)` is load-bearing — do not swap it
   for a px max-width. Under max-content sizing every flexible track resolves
   to the largest hypothetical fr size (CSS Grid §12.7), so both columns equal
   the WIDEST chip and the grid shrink-wraps to `2 × widest + gap`. That is
   what serves «إنستغرام» and "Instagram" from one declaration with no
   bilingual media query and no number that goes stale when a handle changes.
   It also means the block does NOT span the text column — that is deliberate
   and consistent with what is already above it: `.a7ab-bio` is capped at 46ch,
   `.a7ab-stat-hint` at 40ch, `.a7ab-name-sig` at 62%, and `.a7ab-stat-metric`
   is a content-sized pill. The hero body is a stack of measured blocks sharing
   a start edge; this is one more. Stretching the tracks to 415px instead gives
   203.5px cells holding ~120px of content — a ragged END edge traded for six
   ragged CONTENT edges, i.e. the same complaint one level down. There is no
   third state: the ~121px of slack goes either into the gutter or into the
   cells, and a 121px gutter between two chip columns is worse than neither.
   ───────────────────────────────────────────────────────────────────────── */
.a7ab-follow{ --a7ab-follow-gap: 8px; margin-top: clamp(18px, 3.5vw, 26px); }
.a7ab-follow-h{ display: block; font-size: .86rem; font-weight: 700; color: var(--a7ab-muted); margin-bottom: 10px; }

.a7ab-follow-row{
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;                        /* all three rows one height */
  gap: var(--a7ab-follow-gap);
  width: fit-content;                         /* ⛔ see block comment */
  max-width: 100%;                            /* never overruns the column */
}
/* the <li> is the grid item; `display:grid` stretches its single <a> to fill
   the cell. NOT `display:contents` — that drops the listitem role in some
   engines, and this is a real <ul> of six links. */
.a7ab-follow-row > li{ display: grid; min-width: 0; }

.a7ab-soc{
  display: grid;
  grid-template-columns: auto minmax(0, 1fr); /* icon rail | text block */
  align-items: center;
  column-gap: .6rem;
  width: 100%; height: 100%;
  min-height: 48px;                           /* floor; real box ≈52.8px */
  padding: .4rem .85rem;
  /* 999px on a 53px two-line tile puts a 26px curve where the icon sits and
     reads as a loose pill. The one-line pills above it (.a7ab-stat-metric,
     .a7ab-tags) keep 999px, so this also separates the directory from them.
     Revert to `999px` if the owner prefers — nothing else depends on it. */
  border-radius: var(--a7ab-r);   /* = 14px, see note above */
  text-decoration: none; color: var(--a7ab-ink);
  background: var(--a7ab-card); border: 1px solid var(--a7ab-card-bd);
  transition: transform .18s ease, border-color .18s ease, background .18s ease;
}
@media (hover: hover){ .a7ab-soc:hover{
  transform: translateY(-2px);
  border-color: color-mix(in oklch, hsl(var(--primary)) 50%, transparent);
  background: color-mix(in oklch, hsl(var(--primary)) 7%, transparent); } }
/* six adjacent links in a tight grid — the ring must clear the 8px gutter. */
.a7ab-soc:focus-visible{
  outline: 2px solid color-mix(in oklch, hsl(var(--primary)) 70%, transparent);
  outline-offset: 2px;
}

.a7ab-soc-ic{ display: inline-flex; color: color-mix(in oklch, hsl(var(--primary)) 62%, hsl(var(--base-content))); }
.a7ab-soc-ic svg{ width: 17px; height: 17px; display: block; }

/* two rows of ONE column: name and handle share a start edge, and the old
   .4rem inline gap is recovered as free width in every chip. */
.a7ab-soc-tx{
  display: grid; grid-auto-flow: row; row-gap: 1px;
  min-width: 0; text-align: start;            /* RTL → right, LTR → left */
}
/* the NAME never clips. Both lines sit in the same minmax(0,1fr) track, so
   whichever is wider would ellipsis first — and the name is the wider token in
   most chips, i.e. the identifying string would be the first thing lost. With
   `fit-content` the track IS the widest name plus chrome, so overflow is
   unreachable at desktop; the handle absorbs any font-metric surprise instead.
   line-height 1.4, not 1.25 — ج/ع descenders in «تيليجرام». */
.a7ab-soc-name{
  font-size: .9rem; font-weight: 700; line-height: 1.4;
  white-space: nowrap;                        /* «سناب شات» / «تيك توك» must
                                                 never break at their space */
  overflow: visible;
}
.a7ab-soc-handle{
  font-size: .78rem; line-height: 1.35; color: var(--a7ab-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ═══════════════════════════════════════════════════════════════════════════
   SHARED TYPE — decks, body copy, the single eyebrow
   ═══════════════════════════════════════════════════════════════════════════ */
/* the deck. `.a7ab-sec-head` carried this element's bottom margin and is gone,
   so inside a station the deck owns the gap to the body (see the spine block). */
.a7ab-sec-sub{
  margin: .6rem 0 0; max-width: 48ch;
  color: var(--a7ab-muted); font-size: clamp(1rem, 2.3vw, 1.04rem); line-height: 1.6;
}
.a7ab-h3{
  margin: 0 0 .5rem; font-size: clamp(1.15rem, 3vw, 1.42rem);
  font-weight: 700; line-height: 1.2; letter-spacing: -.01em;
}
.a7ab-h3 .t-en{ letter-spacing: -.015em; }
.a7ab-p{
  margin: 0; max-width: 46ch;
  color: color-mix(in oklch, hsl(var(--base-content)) 80%, transparent);
  font-size: clamp(1rem, 2.4vw, 1.06rem); line-height: 1.7; text-wrap: pretty;
}

/* the kicker — now EVERY station label plus station 04's three sub-beat
   headings. It is the exact treatment of the band the owner screenshotted and
   called beautiful, which is why no station gets a display-scale <h2>: the pin
   is anchored at .3em against a .8rem first line, and a big first child rides
   the pin up near the cap-top. Range is restored INSIDE stations instead
   (.a7ab-jr-decode, .a7ab-jr-lead, .a7ab-pf-total). */
.a7ab-kicker{
  margin: 0 0 .7rem; font-size: .8rem; font-weight: 800; color: var(--a7ab-primary);
}
.a7ab-kicker .t-en{ text-transform: uppercase; letter-spacing: .16em; }
.a7ab-kicker .t-ar{ letter-spacing: .02em; }

/* deliverables — what a brand can commission. STATION 05 ONLY, and the page's
   only filled-card grid: that is what stops it reading as the same thing as
   station 03's free-work directory. */
.a7ab-deliv-grid{
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: clamp(10px, 2vw, 14px);
}
.a7ab-deliv{
  display: flex; align-items: center; gap: .7rem; min-width: 0;
  padding: clamp(13px, 2.2vw, 16px); border-radius: var(--a7ab-r);
  background: var(--a7ab-card); border: 1px solid var(--a7ab-card-bd);
}
/* ⛔ WAS a font-size on an emoji glyph. Inline SVG now, so the box is sized
   directly and the ink follows the token — same system as .a7ab-dir-ico. */
.a7ab-deliv-ico{ flex: 0 0 auto; display: inline-flex; line-height: 0;
  color: var(--a7ab-primary); }
.a7ab-deliv-ico svg{ display: block; width: 22px; height: 22px; }
.a7ab-deliv-tx{ font-size: .98rem; font-weight: 600; min-width: 0; }

/* partnership lanes — intent-routed CTA chips. start-aligned (they were centred
   only because .a7ab-cta was a centred island, and that island is gone), and
   min-height:44px on the four conversion chips (they were ~43.4px). */
.a7ab-lanes{
  display: flex; flex-wrap: wrap; justify-content: flex-start; gap: 10px;
  margin-top: clamp(18px, 3.5vw, 26px);
}
.a7ab-lane{
  display: inline-flex; align-items: center; gap: .45rem; text-decoration: none;
  min-height: 44px;
  padding: .7rem 1.1rem; border-radius: var(--a7ab-r); font-weight: 700; font-size: .95rem;
  color: var(--a7ab-ink);
  background: color-mix(in oklch, hsl(var(--primary)) 7%, transparent);
  border: 1px solid color-mix(in oklch, hsl(var(--primary)) 26%, transparent);
  transition: transform .18s ease, background .18s ease, border-color .18s ease;
}
@media (hover: hover){ .a7ab-lane:hover{
  transform: translateY(-2px);
  background: color-mix(in oklch, hsl(var(--primary)) 14%, transparent);
  border-color: color-mix(in oklch, hsl(var(--primary)) 55%, transparent); } }
.a7ab-lane-arrow{ display: inline-flex; color: var(--a7ab-primary); }
.a7ab-lane-arrow svg{ display: block; width: 14px; height: 14px; }
body:not(.font-english) .a7ab-lane-arrow{ transform: scaleX(-1); }

/* CENTRED. This is the terminal action of the whole station, not part of the
   reading flow — under a full-width symmetric grid a start-hugged button reads
   as orphaned. Centring also behaves identically in AR and EN, so there is no
   direction-specific case to maintain.
   ⛔ This is NOT a reversal of the "centred island" note above .a7ab-lanes —
   that decision was about the LANE CHIPS, which stay start-aligned. Only the
   single closing CTA centres. Don't "restore consistency" by centring the
   chips too; the asymmetry between them is deliberate. */
.a7ab-btn{
  display: flex; width: fit-content; margin-inline: auto;
  align-items: center; gap: .6rem;
  margin-top: clamp(22px, 4vw, 30px); padding: .85rem 1.5rem; border-radius: 999px;
  background: hsl(var(--primary)); color: hsl(var(--primary-content, var(--base-100)));
  font-weight: 700; font-size: 1.02rem; text-decoration: none;
  box-shadow: 0 14px 30px -14px color-mix(in oklch, hsl(var(--primary)) 60%, transparent);
  transition: transform .18s ease, box-shadow .18s ease, filter .18s ease;
}
.a7ab-btn:hover{
  transform: translateY(-2px); filter: brightness(1.03);
  box-shadow: 0 20px 40px -16px color-mix(in oklch, hsl(var(--primary)) 70%, transparent);
}
.a7ab-btn-arrow{ display: inline-flex; transition: transform .18s ease; }
.a7ab-btn-arrow svg{ display: block; }
.a7ab-btn:hover .a7ab-btn-arrow{ transform: translateX(3px); }
body:not(.font-english) .a7ab-btn-arrow{ transform: scaleX(-1); }
body:not(.font-english) .a7ab-btn:hover .a7ab-btn-arrow{ transform: scaleX(-1) translateX(3px); }

/* ═══════════════════════════════════════════════════════════════════════════
   THE SPINE
   The path is drawn at rest. NO keyframe ever touches it — which is what makes
   the no-JS and reduced-motion fallbacks free rather than engineered.
   ═══════════════════════════════════════════════════════════════════════════ */
.a7ab-jr{
  --pf-pad: 30px;   /* content inset from the .a7ab-jr edge */
  --pf-x:    5px;   /* rail's inline-start edge → rail centre = 5.5px */
  --pf-node: 11px;  /* station pin diameter */
  position: relative;
  padding-inline-start: var(--pf-pad);
  padding-block: 26px 34px;                 /* lead-in / lead-out for the fade */
  margin-top: clamp(30px, 5vw, 44px);
}

/* ⛔ ABSOLUTE fade stops (22px / 26px), never the band's old 10%/88%. Over a
   ~4,000px page a percentage fade becomes a ~500px wash that reads as a
   rendering bug rather than a beginning. */
.a7ab-jr::before{
  content: ""; position: absolute; inset-block: 0;
  inset-inline-start: var(--pf-x); width: 1px;
  background: linear-gradient(to bottom,
    transparent 0,
    var(--a7ab-line-strong) 22px,
    var(--a7ab-line-strong) calc(100% - 26px),
    transparent 100%);
}

/* ── the station ────────────────────────────────────────────────────────────
   ⛔ INVARIANT 1: nothing between .a7ab-jr and a pin/tick host may add
   padding-inline-start or margin-inline-start. The offset formula assumes the
   host's inline-start edge sits exactly at --pf-pad. (This is precisely why
   .a7ab-pf-spine is deleted rather than nested — a nested spine would pay
   --pf-pad twice and throw every tick ~30px off the rail.)
   ⛔ INVARIANT 2: a station's first child is always its .a7ab-kicker heading.
   The .3em anchor is tuned to a .8rem first line.
   ⛔ INVARIANT 3: the gutter stays base-100. The pin's 2.5px halo is what masks
   the rail passing behind it; over a card, an image or the tinted .a7ab-permit
   it stops reading as sitting ON the line.
   ⛔ INVARIANT 4: the line stays NEUTRAL (--a7ab-line-strong), only pins are
   primary. The owner's «خط … و نقاط زرقا» IS that contrast — tint the line and
   the device dies.
   ⛔ INVARIANT 5: blue-mark budget on the rail = 5. Five station pins. Ticks
   neutral, hand-offs unmarked, no terminal caps. Do not add a sixth. */

/* terminal caps — solid discs closing the line at both ends. Solid fill against
   the stations' knockout ring reads as terminus vs. chapter, same hue, no new
   colour. The 3px base-100 halo keeps the disc off the rail it sits on. */
.a7ab-jr-cap{ display: block; position: relative; height: clamp(18px, 3.5vw, 26px); }
.a7ab-jr-cap::before{
  content: ""; position: absolute;
  inset-inline-start: calc(var(--pf-x) - var(--pf-pad) - (var(--pf-node) - 1px)/2);
  width: var(--pf-node); height: var(--pf-node); border-radius: 50%;
  background: hsl(var(--primary)); box-shadow: 0 0 0 3px hsl(var(--base-100));
}
.a7ab-jr-cap--start::before{ inset-block-start: 0; }
.a7ab-jr-cap--end::before{ inset-block-end: 0; }

.a7ab-jr-st{ position: relative; }
.a7ab-jr-st + .a7ab-jr-st{ margin-top: clamp(56px, 8vw, 76px); }

.a7ab-jr-st::before{
  content: ""; position: absolute; inset-block-start: .3em;
  /* desktop: 5 − 30 − 5 = −30 → pin spans 0…11, centre 5.5 = rail centre ✓
     mobile:  3.5 − 20 − 4 = −20.5 → pin spans −0.5…8.5, centre 4 = rail centre ✓ */
  inset-inline-start: calc(var(--pf-x) - var(--pf-pad) - (var(--pf-node) - 1px) / 2);
  width: var(--pf-node); height: var(--pf-node); border-radius: 50%;
  background: hsl(var(--base-100));
  box-shadow: inset 0 0 0 2.5px hsl(var(--base-100)),
              inset 0 0 0 6px color-mix(in oklch, hsl(var(--primary)) 78%, transparent);
}

/* the hero pill deep-links to #a7ab-pf-h (station 04's kicker). Without this the
   shared sticky nav lands on top of the pin the anchor exists to show. */
.a7ab-jr-h{ scroll-margin-block-start: clamp(72px, 12vh, 110px); }

/* station label — compound selector (0,2,0) so it beats .a7ab-kicker's own
   margin (0,1,0) regardless of source order in the file */
.a7ab-kicker.a7ab-jr-h{ margin: 0 0 .45rem; }

/* the deck owns the gap to the station body */
.a7ab-jr-st > .a7ab-sec-sub{ margin: 0 0 clamp(18px, 3vw, 26px); }

/* station 05 is the one station with no deck (it opens on the ask). Without
   this it falls back to the kicker's .45rem = ~7px between a .8rem eyebrow and
   a clamp(1.25rem,3.4vw,1.75rem) display line, and the two read as one crowded
   unit at the exact point the page asks for the commission. 0,3,0. */
.a7ab-jr-st > .a7ab-jr-h + .a7ab-jr-lead{ margin-top: clamp(18px, 3vw, 26px); }

/* ── the hand-off — the sentence that makes the NEXT station necessary.
   Ends stations 01–04; station 05 has none, the line ends there.

   ⚠ DEVIATION FROM SPEC, STATED: spec §4 sets this to var(--a7ab-faint)
   (base-content 42% ≈ 2.9:1 in light theme). --a7ab-faint is correct for
   .a7ab-pf-foot, which is fine print. The hand-offs are the narrative device
   itself — the four sentences carrying the whole "one continuous story" claim.
   Shipping the page's load-bearing copy below 4.5:1 is self-defeating, so this
   uses --a7ab-muted (68% ≈ 4.9:1). Everything else about the treatment —
   small, quiet, sub-body, unmarked on the rail — is unchanged. */
.a7ab-jr-hand{
  display: block; margin: clamp(22px, 3.4vw, 30px) 0 0; max-width: 40ch;
  font-size: .88rem; line-height: 1.6; font-weight: 600;
  color: var(--a7ab-muted); text-align: start; text-wrap: pretty;
}

/* ═══════════════════════════════════════════════════════════════════════════
   STATION FORMS — five genuinely different shapes, so the walk cannot read as
   pattern-fill: 01 type-only · 02 type → image pair · 03 unfilled hairline
   directory · 04 the heavy instrumented band · 05 the only filled cards.
   ═══════════════════════════════════════════════════════════════════════════ */

/* the display scale — used EXACTLY twice (02's thesis, 05's ask).
   Two uses is a system. Five identical eyebrows would have been a template. */
.a7ab-jr-lead{
  margin: 0; max-width: 34ch; text-align: start;
  font-size: clamp(1.25rem, 3.4vw, 1.75rem); font-weight: 600;
  line-height: 1.5; letter-spacing: -.01em; text-wrap: balance;
}
.a7ab-jr-lead .t-en{ letter-spacing: -.015em; }

.a7ab-jr-conn{
  margin: clamp(20px, 3vw, 28px) 0 0;
  color: var(--a7ab-muted); font-size: .97rem; line-height: 1.6;
}

/* ── STATION 01 · «من أنا» — the decode promoted from a .92rem hero footnote to
   the station's centrepiece. It is the one piece of the page that is purely
   his, and it is the answer to the single most-asked question about the brand. */
.a7ab-jr-decode{
  display: flex; flex-direction: column; align-items: flex-start;
  gap: .6rem; margin: 0; font-size: 1rem;
}
.a7ab-jr-decode .a7ab-decode-mark{
  font-size: clamp(1.6rem, 5vw, 2.4rem);
  padding: .42rem .9rem;
}
.a7ab-jr-decode .a7ab-decode-why{ font-size: 1rem; }
/* the two focus tags move down from the hero and sit under the decode */
.a7ab-jr-decode + .a7ab-tags{ margin-top: clamp(18px, 3vw, 24px); }

/* ── STATION 02 · «ما أؤمن به» — the two interests as a flat 2-up pair.
   No zig-zag (an A/B is not a pattern — you need three alternations before a
   reader reads alternation as intentional, and at ≤640px the flip was cancelled
   outright), no 01/02 badges. They are EVIDENCE for the thesis above them, not
   two competing features.
   ⛔ .a7ab-interest-media / -img below are the SHIPPED rules, unchanged. They
   carry the frame, radius, overflow clip, width:100% and object-fit:cover; the
   pair overrides only max-width / margin / aspect-ratio. Delete them and both
   images render as unframed, unclipped intrinsic-size PNGs. */
/* both selectors on purpose: .a7ab-int-cell is the class page.py emits, and the
   `> article` form is the belt-and-braces that keeps the pair from ever
   inheriting a two-column grid by accident */
/* ── STATION 03 · «ما أنشره» — a hairline DIRECTORY, not cards.
   This is the whole fan/buyer fix: station 03 (free work, for the fan) and
   station 05 (commissionable formats, for the buyer) used to read identically
   because both were filled-card grids built from the same recipe. Unfilling 03
   separates them without a word of explanation, and makes 05's cards the only
   filled surface on the page — so the ask is where the weight is.
   A side effect: the 5-tile orphan disappears. auto-fit minmax(180px) resolved
   to 4 columns at 880px and stranded tile five as a full-width stretched card.
   A list has no orphan. */
.a7ab-dir{
  list-style: none; margin: 0; padding: 0;
  border-top: 1px solid var(--a7ab-line);
}
.a7ab-dir-row{ border-bottom: 1px solid var(--a7ab-line); }
.a7ab-dir-a{
  display: flex; align-items: center; gap: .75rem;
  min-height: 56px; padding: .7rem 2px;
  text-decoration: none; color: var(--a7ab-ink);
  transition: color .18s ease, padding-inline-start .18s ease;
}
/* ⛔ WAS font-size on an emoji glyph. These are inline SVGs now, so the box is
   sized directly and the ink follows the row's own colour — which is what lets
   them dim with the row at rest and warm with it on hover, exactly like the
   label beside them. An emoji could never do either. */
.a7ab-dir-ico{ flex: 0 0 auto; display: inline-flex; line-height: 0;
  color: color-mix(in oklch, hsl(var(--base-content)) 52%, transparent);
  transition: color .18s ease; }
.a7ab-dir-ico svg{ display: block; width: 21px; height: 21px; }
/* «تطبيقاتي» icon — the owner's exact icons8 App Store PNG, painted with
   currentColor via a mask so it inherits the same colour + hover as the SVG icons. */
.a7ab-appstore-ico{
  display: block; width: 21px; height: 21px; background-color: currentColor;
  -webkit-mask: url(/static/about/icons8-app-store-100.png) center / contain no-repeat;
          mask: url(/static/about/icons8-app-store-100.png) center / contain no-repeat;
}
.a7ab-dir-a:hover .a7ab-dir-ico{ color: var(--a7ab-primary); }
.a7ab-dir-label{ font-size: 1.02rem; font-weight: 600; min-width: 0; }
.a7ab-dir-arrow{
  margin-inline-start: auto; display: inline-flex; color: var(--a7ab-faint);
  transition: transform .18s ease, color .18s ease;
}
.a7ab-dir-arrow svg{ display: block; }
body:not(.font-english) .a7ab-dir-arrow{ transform: scaleX(-1); }
@media (hover: hover){
  .a7ab-dir-a:hover{ color: var(--a7ab-primary); padding-inline-start: 8px; }
  .a7ab-dir-a:hover .a7ab-dir-arrow{ color: var(--a7ab-primary); transform: translateX(3px); }
  body:not(.font-english) .a7ab-dir-a:hover .a7ab-dir-arrow{ transform: scaleX(-1) translateX(3px); }
}

/* ══ «وثلاثة تطبيقات» — the ONE horizontal branch off the vertical spine ══════
   Station 03 grows a limb. A neutral 1px rail leaves the spine's own centre
   line and carries three solid blue leaves at their true App Store release
   dates, each with its icon hanging directly beneath it.

   ⛔ INVARIANT 5 IS AMENDED, NOT BROKEN. The blue-mark budget is now:
   5 knockout station pins on the VERTICAL rail + 2 terminal caps + 3 solid 7px
   leaves on the ONE branch. No second branch, no leaf on the vertical rail, and
   no mark at the joint where the limb leaves the spine.

   ⛔ INVARIANT 6 (new): never add padding-inline/margin-inline to .a7ab-apl or
   .a7ab-apl-grid. The rail offset below assumes the host's inline-start edge
   sits exactly at --pf-pad; any inline padding throws the limb off the spine.

   ⛔ INVARIANT 7 (new): never tint the horizontal rail. The line stays
   --a7ab-line-strong and ONLY the dots are primary — that contrast is the
   owner's «خط … و نقاط زرقا» device. Tint the line and the block dies.

   ⛔ Do NOT set overflow-x on .a7ab-apl-grid or any ancestor inside the
   station: computed overflow-y becomes `auto` and clips the negatively-offset
   rail AND every node, shipping a line with no dots — invisible in desktop
   testing because the clip only bites once the rail is pulled outside. */
.a7ab-apl{ margin-top: clamp(30px, 4.5vw, 42px); }
.a7ab-apl-lead{
  margin: 0 0 clamp(20px, 3vw, 26px); max-width: 40ch;
  font-size: .88rem; line-height: 1.6; font-weight: 600;
  color: var(--a7ab-muted); text-wrap: pretty;
}

/* ⛔ The mask url() lives HERE, keyed off [data-app] — NEVER in a style="…"
   attribute. A url("…") inside a double-quoted HTML attribute closes it early,
   silently dropping the mask; that exact bug already shipped once on the brand
   wall as ten solid colour bars. Only unit-less numbers go in the attribute. */
/* ⛔ FAIL-CLOSED DEFAULT. If a data-app key ever has no matching rule below,
   --ap-mask resolves to nothing, `mask` becomes invalid, NO mask is applied and
   `background: currentColor` paints the mark as a SOLID INK RECTANGLE — the
   same silent failure that once shipped ten solid colour bars on the brand
   wall. A fully transparent gradient makes the unknown case render NOTHING
   instead of a block. It must sit on .a7ab-apl-it (0,1,0) so the per-app
   [data-app] rules (0,2,0) still win; putting it on .a7ab-apl-mk itself would
   beat the INHERITED per-app value and blank all three marks. */
.a7ab-apl-it{ --ap-mask: linear-gradient(transparent, transparent); }
.a7ab-apl-it[data-app="shadow"]{ --ap-mask: url("/static/about/apps/shadow-mk.webp"); }
.a7ab-apl-it[data-app="velura"]{ --ap-mask: url("/static/about/apps/velura-mk.webp"); }
.a7ab-apl-it[data-app="tartib"]{ --ap-mask: url("/static/about/apps/tartib-mk2.webp"); }
.a7ab-apl-it[data-app="madd"]{ --ap-mask: url("/static/about/apps/madd-mk.webp"); }

.a7ab-apl-grid{
  --ap-cap:   clamp(26px, 3.6vw, 34px);
  --ap-plate: clamp(44px, 6vw, 56px);
  --ap-node:  7px;
  --ap-r1:    16px;                                 /* date row            */
  --ap-r2:    14px;                                 /* rail band           */
  --ap-rail:  calc(var(--ap-r1) + var(--ap-r2) / 2);
  list-style: none; margin: 0; padding: 0; position: relative;
  display: grid;
  /* ⛔ EQUAL columns, count DERIVED from the render (--ap-cols = len(apps)).
     Was `repeat(3, 1fr)`; a 4th app then landed in an implicit `auto` column
     ~2.6× the others' width (owner: «مب بالتساوي»). grid-auto-columns is the
     belt-and-suspenders if --ap-cols is ever absent AND count exceeds 3. */
  grid-template-columns: repeat(var(--ap-cols, 3), minmax(0, 1fr));
  grid-auto-columns: minmax(0, 1fr);
  grid-template-rows: var(--ap-r1) var(--ap-r2) var(--ap-plate) auto auto;
  column-gap: clamp(12px, 2.6vw, 34px); row-gap: 0;
}
/* subgrid keeps all three columns on shared baselines however the names wrap */
.a7ab-apl-it{
  grid-row: 1 / -1; display: grid; grid-template-rows: subgrid;
  position: relative; min-width: 0;
}
@supports not (grid-template-rows: subgrid){
  .a7ab-apl-it{ grid-template-rows: var(--ap-r1) var(--ap-r2) var(--ap-plate) auto auto; }
  .a7ab-apl-nm{ min-block-size: 2.6em; }
  .a7ab-apl-ds{ min-block-size: 3.2em; }
}
.a7ab-apl-band{ display: block; }

/* THE RAIL — attached to the spine, fading only at the far end.
   ⛔ `linear-gradient(to inline-end, …)` is UNIMPLEMENTED in every engine: the
   declaration is invalid, the whole background is dropped and the rail simply
   never renders, silently, in BOTH directions. Physical `to right` + an
   explicit RTL twin is the only correct form (precedent: about.css:136). */
.a7ab-apl-grid::before{
  content: ""; position: absolute; height: 1px; pointer-events: none;
  inset-block-start: var(--ap-rail);
  inset-inline-start: calc(var(--pf-x) + .5px - var(--pf-pad));
  inset-inline-end: 0;
  background: linear-gradient(to right,
    var(--a7ab-line-strong) 0,
    var(--a7ab-line-strong) calc(100% - 30px),
    transparent 100%);
  transform-origin: left center;
}
body:not(.font-english) .a7ab-apl-grid::before{
  background: linear-gradient(to left,
    var(--a7ab-line-strong) 0,
    var(--a7ab-line-strong) calc(100% - 30px),
    transparent 100%);
  transform-origin: right center;
}

/* THE LEAVES — solid discs, centred on the PLATE (not the column) so the
   owner's «تحت كل نقطه ايقونه التطبيق» is literally true. The base-100 halo
   is what makes the rail appear to pass BEHIND each node. */
/* ⛔ EVERY leaf is centred on ONE vertical axis: date → node → plate → name.
   It used to be inline-START aligned, which looked fine while the plate was the
   widest thing in the cell — but the NAME is wider than the 44px plate, so it
   overhung toward the inline-end and the column read as crooked (owner:
   «ماخذ محذاه يسار»). Centring is also direction-proof: start-alignment has to
   be re-reasoned for RTL, a centred axis does not. */
.a7ab-apl-it{ justify-items: center; text-align: center; }

.a7ab-apl-it::before{
  content: ""; position: absolute; border-radius: 50%;
  /* ⛔ the `+ .5px` is load-bearing, not a fudge. --ap-rail is the rail's
     inset-block-START and the rail is 1px tall, so its CENTRE is --ap-rail
     + .5px. Without this the node centre lands 0.5px above the line and the
     dot sits visibly high on a 2x display — the same sub-pixel discipline the
     vertical spine's pin already meets (verified 5.50 desktop / 4.00 mobile).
     Holds at both breakpoints: desktop 23 + .5 − 3.5 = 20 (centre 23.5);
     mobile 20 + .5 − 3 = 17.5 (centre 20.5). */
  inset-block-start: calc(var(--ap-rail) + .5px - var(--ap-node) / 2);
  /* the plate is now centred in the column, so the node centres on the COLUMN.
     (It was `--ap-plate/2 - node/2` back when the plate hugged inline-start.)
     Either way the invariant is the same and is what the owner asked for:
     the icon sits exactly under its dot. */
  inset-inline-start: calc(50% - var(--ap-node) / 2);
  width: var(--ap-node); height: var(--ap-node);
  background: hsl(var(--primary));
  box-shadow: 0 0 0 2.5px hsl(var(--base-100));
}

/* THE MARK — currentColor knockout on an unfilled hairline plate. No raster is
   ever painted, so the light theme gains zero heavy blocks. ONE declaration
   self-inverts across both themes because --base-content flips. */
.a7ab-apl-plate{
  inline-size: var(--ap-plate); block-size: var(--ap-plate);
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--a7ab-r-in);
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--a7ab-line);
  transition: box-shadow .45s cubic-bezier(.22,1,.36,1),
              background  .45s cubic-bezier(.22,1,.36,1);
}
.a7ab-apl-mk{
  --ap-boost: 1;
  display: block;
  block-size:  calc(var(--ap-cap) * var(--ap-size));
  inline-size: calc(var(--ap-cap) * var(--ap-size) * var(--ap-ar));
  background: color-mix(in oklch, hsl(var(--base-content))
              calc(var(--ap-a) * var(--ap-boost) * 100%), transparent);
  -webkit-mask: var(--ap-mask) no-repeat center / contain;
          mask: var(--ap-mask) no-repeat center / contain;
  transition: background .45s cubic-bezier(.22,1,.36,1);
}
/* mask unsupported → hide the MARK ONLY. Unlike the brand wall (decorative,
   hides entirely), every name, description, date and link here is load-bearing
   and must survive. */
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))){
  .a7ab-apl-mk{ display: none; }
}

/* ⛔ NOT --a7ab-faint (42%). MEASURED: 42% ink gives 2.68:1 on the light theme
   and 2.85:1 on dark — below even the 3:1 non-text floor. These dates are not
   decoration: they are what turns the rail into a timeline, so they are content
   and owe 4.5:1. 60% is the MINIMUM alpha that clears 4.5 in BOTH themes
   (4.63 light / 4.70 dark), which keeps them quieter than the 68% descriptions
   instead of promoting them to the same voice. Do not lower it. */
.a7ab-apl-dt{
  font-size: .68rem; line-height: 1;
  color: color-mix(in oklch, hsl(var(--base-content)) 60%, transparent);
  font-variant-numeric: tabular-nums; align-self: start;
}
.a7ab-apl-nm{
  margin: .8rem 0 0; font-size: .95rem; font-weight: 600; line-height: 1.3;
  color: var(--a7ab-ink); overflow-wrap: anywhere;
}
.a7ab-apl-ltr{ unicode-bidi: isolate; }
/* The description is QUIET AT REST and arrives on hover (owner: «شله بس خله
   يظهر بطريقه ذكيه لو حد مرر الموسه»).

   ⛔ It stays IN FLOW — only opacity and a 3px lift animate. Reserving its space
   is the whole trick: revealing a block that was `display:none` would reflow the
   card under the cursor and shove the row below it, which is exactly the jank
   that makes hover reveals feel cheap. Nothing here triggers layout.

   ⛔ It is NOT hidden from assistive tech. `opacity: 0` keeps the text in the
   accessibility tree, so a screen reader still reads what each app does — and
   `:focus-within` gives keyboard users the same reveal a mouse gets. This is a
   VISUAL quieting, not a content removal. */
.a7ab-apl-ds{
  margin: .28rem 0 0; font-size: .8rem; line-height: 1.5;
  color: var(--a7ab-muted); text-wrap: pretty; overflow-wrap: anywhere;
  opacity: 0; transform: translateY(3px);
  transition: opacity .32s cubic-bezier(.22,1,.36,1),
              transform .32s cubic-bezier(.22,1,.36,1);
}
.a7ab-apl-it:focus-within .a7ab-apl-ds{ opacity: 1; transform: none; }
@media (hover: hover) and (pointer: fine){
  .a7ab-apl-it:hover .a7ab-apl-ds{ opacity: 1; transform: none; }
}
/* ⛔ TOUCH HAS NO HOVER. On a phone there is no gesture that reveals this
   without also following the link, so hiding it there would silently delete
   real content from the device most of his traffic uses. Where hover does not
   exist, the description simply stays visible — the hover behaviour is an
   enhancement for pointers, never the only way to read it. */
@media (hover: none){
  .a7ab-apl-ds{ opacity: 1; transform: none; }
}

/* ONE tab stop per app; the whole cell is the target. */
.a7ab-apl-a{ color: inherit; text-decoration: none; }
.a7ab-apl-a::after{
  content: ""; position: absolute; inset: 0; z-index: 1;
  border-radius: var(--a7ab-r-in);
}
/* ⛔ `.a7ab-wrap` is REQUIRED on these two, not decoration. The file's global
   focus rule is `.a7ab-wrap a:focus-visible` = (0,2,1). A bare
   `.a7ab-apl-a:focus-visible` is (0,2,0) and LOSES, so `outline: none` never
   applied and a keyboard user got the default ring on the 0-height inline <a>
   PLUS this ::after ring — two rings, one of them in the wrong place.
   `.a7ab-wrap .a7ab-apl-a:focus-visible` is (0,3,0) and wins. */
.a7ab-wrap .a7ab-apl-a:focus-visible{ outline: none; }
.a7ab-wrap .a7ab-apl-a:focus-visible::after{
  outline: 2px solid hsl(var(--primary)); outline-offset: 3px;
}

/* Reward is INK INTENSITY ONLY — never colour restoration, per the owner's
   «خليهم ابيض و اسود». --ap-boost is a MULTIPLIER so the per-app weight
   calibration survives hover (.62→.90, .29→.42; nothing exceeds 100%). */
@media (hover: hover) and (pointer: fine){
  .a7ab-apl-it:hover .a7ab-apl-mk{ --ap-boost: 1.45; }
  .a7ab-apl-it:hover .a7ab-apl-plate{
    background: color-mix(in oklch, hsl(var(--primary)) 8%, transparent);
    box-shadow: inset 0 0 0 1px color-mix(in oklch, hsl(var(--primary)) 22%, transparent);
  }
  .a7ab-apl-it:hover .a7ab-apl-nm{ color: var(--a7ab-primary); }
}
.a7ab-apl-it:focus-within .a7ab-apl-mk{ --ap-boost: 1.45; }
.a7ab-apl-it:focus-within .a7ab-apl-plate{
  background: color-mix(in oklch, hsl(var(--primary)) 8%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in oklch, hsl(var(--primary)) 22%, transparent);
}

/* ── STATION 05 · «لنعمل معًا» — the merged ask. «صيغ التعاون» and «مهتم بالعمل
   معي؟» were one request said twice, four positions apart, with the entire
   proof band between them. (.a7ab-deliv-grid, .a7ab-lane, .a7ab-btn are all
   reused above; only .a7ab-lanes changed, from centred to start-aligned.) */
.a7ab-jr-lead + .a7ab-deliv-grid{ margin-top: clamp(22px, 3.6vw, 30px); }

/* ═══════════════════════════════════════════════════════════════════════════
   focus
   ═══════════════════════════════════════════════════════════════════════════ */
.a7ab-wrap a:focus-visible, .a7ab-btn:focus-visible{
  /* ⛔ NO border-radius here. It used to be 8px, which RESHAPED the element on
     focus — .a7ab-btn is a 999px pill, so tabbing to it visibly snapped it into
     a rounded rectangle. The outline already follows each element's own radius,
     so the ring is correct for pills, cards and circles alike without it. */
  outline: 2px solid hsl(var(--primary)); outline-offset: 3px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   STATION 04 «من وثق بي» — the credibility band: reach → trust → licence.

   Rest state is FULLY VISIBLE everywhere: rail at full width, rows placed,
   marquee at frame zero, numbers already final. Motion is added only by .is-in
   and by JS opting in. ⛔ every token stays wrapped in hsl() inside color-mix.

   ⛔ THE .a7ab-pf CLASS IS A RUNTIME CONTRACT, NOT GEOMETRY. There is no
   `.a7ab-pf{}` base rule anywhere in this file — the class only carries
   behaviour. Every band animation keys off `.a7ab-pf.is-in`, and about.js
   hard-codes `document.querySelector('.a7ab-pf')` for the reach odometer. That
   is exactly why station 04 IS the .a7ab-pf element rather than being wrapped
   in one: move the class and the rail fill, ledger draw, permit rise and both
   count-ups silently no-op while everything still rests visible — it passes
   review and fails at runtime.
   ═══════════════════════════════════════════════════════════════════════════ */

/* the three sub-beats. Their marks are TICKS, not pins — see below. */
.a7ab-mv{ position: relative; }
.a7ab-mv + .a7ab-mv{ margin-top: clamp(34px, 5.5vw, 52px); }

/* the sub-beat TICK — perpendicular, NEUTRAL, never blue. Replaces the old blue
   .a7ab-mv::before node. Rank is now encoded in FORM (disc vs dash) as well as
   in colour, so the hierarchy still reads at 360px and to anyone who cannot
   separate the two hues — and the blue-mark budget on the rail stays at 5.
   desktop: 5 + .5 − 30 − 4.5 = −29    → spans 1…10,      centre 5.5 ✓
   mobile:  3.5 + .5 − 20 − 4.5 = −20.5 → spans −0.5…8.5, centre 4   ✓
   (the +.5px is half the rail width, the −4.5px half the tick width — the
   formula is independent of --pf-node, which the tick does not use) */
.a7ab-mv::before{
  content: ""; position: absolute; inset-block-start: .42em;
  inset-inline-start: calc(var(--pf-x) + .5px - var(--pf-pad) - 4.5px);
  width: 9px; height: 1px; border-radius: 1px;
  background: color-mix(in oklch, hsl(var(--base-content)) 34%, transparent);
  transform-origin: center;   /* symmetric grow → needs no RTL variant */
}
.a7ab-mv-h{ margin: 0 0 .5rem; }
/* sub-beat kickers go NEUTRAL. 0,2,0 beats .a7ab-kicker's 0,1,0 primary,
   independent of source order. Station 04's OWN kicker (#a7ab-pf-h) is not
   .a7ab-mv-h, so it stays primary like every other station label. */
.a7ab-jr .a7ab-mv-h{ color: var(--a7ab-muted); }
.a7ab-mv-sub{
  margin: 0 0 clamp(14px, 2.6vw, 20px);
  color: var(--a7ab-muted); font-size: .97rem; line-height: 1.6;
}
.a7ab-pf-foot{
  margin: clamp(12px, 2.2vw, 16px) 0 0; max-width: 56ch;
  /* ⛔ --a7ab-muted (~4.9:1), NOT --a7ab-faint (~2.7:1). These footnotes are real
     informative disclaimers (reach snapshot, trust, commercial terms), so they owe
     WCAG AA 4.5:1 — faint fails it in both themes. */
  color: var(--a7ab-muted); font-size: .8rem; line-height: 1.6; text-wrap: pretty;
}

/* the concession — the page's one line that is not in his favour, and the only
   reason the other five figures are believable. MUTED not faint: it must clear
   4.5:1 or it reads as a disclaimer he hoped you would skip. */
.a7ab-jr-owned{
  margin: .55rem 0 0; max-width: 40ch;
  color: var(--a7ab-muted); font-size: .86rem; font-weight: 600; line-height: 1.6;
}

/* shared numerals — tabular so counting never jitters, LTR-isolated so Arabic
   can never re-order "8.73" / "2,811,400" / "24,600" */
.a7ab-pf-n,
.a7ab-led-n,
.a7ab-led-idx,
.a7ab-rail-pc,
.a7ab-reach-exact b,
.a7ab-permit-no{
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  direction: ltr; unicode-bidi: isolate;
}
.a7ab-led-sub{ font-variant-numeric: tabular-nums; }
.a7ab-pf-unit .t-ar{ padding-inline-start: .24em; }   /* "8.73 مليون" vs "8.73M" */

/* ── sub-beat 1 · the reach ledger ──────────────────────────────────────────
   ⛔ CORRECTED COMMENT. The block that used to sit here asserted "no percentage
   is rendered anywhere", which page.py has always contradicted — it renders
   .a7ab-rail-seg with flex-grow:{share} and prints pct inside every segment.
   What actually ships: the rail draws TRUE share, floored at a legibility
   minimum (min-width) so a 24,600-follower channel cannot collapse into a ~2px
   sliver that reads as failure. The floor makes the drawn widths approximate at
   the small end — which is precisely why the exact percentage sits inside each
   segment and the exact integer sits in the ledger below. Nothing on screen
   depends on the reader measuring a bar.
   The ledger's two columns also refuse to rank. The owner's mandated order is
   not descending, so a single sorted column would look broken; with two columns
   the largest entry lands mid-grid directly beside the smallest, and any
   "is this a leaderboard?" reading is disproved at a glance. */
.a7ab-reach-mast{
  display: grid; grid-template-columns: minmax(0, auto) minmax(0, 1fr);
  gap: clamp(16px, 3.4vw, 40px); align-items: end;
}
.a7ab-reach-main{ min-width: 0; }
.a7ab-pf-total{ margin: 0; display: flex; align-items: baseline; gap: .28em; line-height: .92; }
.a7ab-pf-total .a7ab-pf-n{
  font-size: clamp(3rem, 11vw, 4.6rem); font-weight: 800; letter-spacing: -.042em;
  color: var(--a7ab-primary);
  /* 4.4ch, not 2.6ch: the odometer target is 4 glyphs ("8.73"), and a box
     sized for 3 would let the label under it reflow mid-count. */
  min-width: 4.4ch; display: inline-block;
}
.a7ab-pf-unit{ font-weight: 800; color: var(--a7ab-primary); line-height: 1; }
.a7ab-pf-unit-lg{ font-size: clamp(1.05rem, 3vw, 1.45rem); }
.a7ab-pf-total-lb{ margin: .5rem 0 0; font-size: .95rem; font-weight: 700; color: var(--a7ab-ink); }
/* the receipt under the rounded headline — 8.73M is a rounding, this is not */
.a7ab-reach-exact{ margin: .3rem 0 0; font-size: .82rem; font-weight: 700; color: var(--a7ab-muted); }
.a7ab-reach-exact b{ font-weight: 800; }
.a7ab-pf-stamp{
  margin: .4rem 0 0; display: inline-flex; align-items: center; gap: .42rem;
  /* ⛔ AA 4.5:1 — the «محدّث يوليو ٢٠٢٦» recency stamp is exactly the datum a
     skeptical reader needs to read; --a7ab-faint (~2.7:1) fails it. */
  font-size: .78rem; font-weight: 600; color: var(--a7ab-muted);
}
.a7ab-pf-dot{
  width: 5px; height: 5px; border-radius: 50%; flex: 0 0 auto;
  background: color-mix(in oklch, hsl(var(--primary)) 70%, transparent);
}
.a7ab-pf-recon{
  margin: 0 0 .3rem; max-width: 34ch;
  padding-inline-start: clamp(12px, 2vw, 18px);
  border-inline-start: 1px solid var(--a7ab-line-strong);
  color: var(--a7ab-muted); font-size: .88rem; line-height: 1.65; text-wrap: pretty;
}

/* ── the colour rail ────────────────────────────────────────────────────────
   flex-grow carries each channel's TRUE share, but min-width floors every
   segment so a small channel keeps a readable name + percentage instead of
   collapsing into an invisible sliver. The floor makes the drawn widths
   approximate at the small end — which is why the exact percentage sits inside
   every segment and the exact integer sits in the ledger below. Nothing on
   screen depends on measuring a bar. */
.a7ab-rail{
  display: flex; gap: 0; height: 44px;
  margin-top: clamp(20px, 3.4vw, 30px);
  border-radius: var(--a7ab-r); overflow: hidden;   /* ONE line, split by colour */
  box-shadow: inset 0 0 0 1px color-mix(in oklch, hsl(var(--base-content)) 9%, transparent);
}
.a7ab-rail-seg{
  flex-basis: 0; min-width: 62px;                 /* the legibility floor */
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 1px;
  padding-inline: .45rem; overflow: hidden; white-space: nowrap;
  /* hue blended toward base-content, ink from base-100 → legible in BOTH themes */
  background: color-mix(in oklch, hsl(var(--pf-tint)) 72%, hsl(var(--base-content)));
  color: hsl(var(--base-100));
  /* ⛔ no transform-origin here. The fill animates clip-path on the PARENT
     `.a7ab-rail`; nothing ever transforms a segment. A leftover
     `transform-origin: left center` sat here and read as though segments were
     transformed — which would also have been physically wrong in RTL. */
}
.a7ab-rail-seg + .a7ab-rail-seg{
  border-inline-start: 1px solid color-mix(in oklch, hsl(var(--base-100)) 26%, transparent);
}
.a7ab-rail-lb{
  font-size: .72rem; font-weight: 700; letter-spacing: -.01em;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.a7ab-rail-pc{
  font-size: .72rem; font-weight: 800; opacity: .92;
  min-width: 4.6ch; text-align: center;            /* counting must not shift the label */
}

/* the press rule — inked before the entries are set. Base = fully drawn. */
.a7ab-led-rule{
  height: 2px; margin: clamp(26px, 4.4vw, 38px) 0 0;
  background: color-mix(in oklch, hsl(var(--base-content)) 38%, transparent);
  transform: scaleX(1); transform-origin: left center;
}
body:not(.font-english) .a7ab-led-rule{ transform-origin: right center; }

/* the ledger */
.a7ab-led{
  position: relative; list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(20px, 4vw, 44px);
}
.a7ab-led::before{                                   /* column gutter hairline */
  content: ""; position: absolute; inset-block: 6px; inset-inline-start: 50%;
  width: 1px; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--a7ab-line) 12%,
                              var(--a7ab-line) 88%, transparent);
}
.a7ab-led-row{ position: relative; border-bottom: 1px solid var(--a7ab-line); }
.a7ab-led-row:nth-last-child(-n+2){ border-bottom: 0; }   /* bottom row of BOTH columns */
/* per-platform accent, hover/focus only. AUDITED AND DECLARED: it is per-platform
   TINT, never primary, and it lands 17px inside the rail — it is not a rail mark
   and does not enter the blue-mark budget. ⛔ Anyone adding a PRIMARY stroke in
   this zone breaks spine invariant 5. */
.a7ab-led-row::before{
  content: ""; position: absolute; inset-block: 10px; inset-inline-start: -8px;
  width: 2px; border-radius: 2px; opacity: 0; transition: opacity .2s ease;
  background: color-mix(in oklch, hsl(var(--pf-tint)) 70%, transparent);
}
@media (hover: hover){ .a7ab-led-row:hover::before{ opacity: 1; } }
.a7ab-led-row:focus-within::before{ opacity: 1; }

.a7ab-led-a{
  display: grid; grid-template-columns: auto auto minmax(0, 1fr) auto;
  align-items: center; column-gap: clamp(8px, 1.6vw, 12px); row-gap: 5px;
  padding: clamp(12px, 2vw, 15px) 4px;
  min-height: clamp(64px, 9vw, 78px);
  /* fill the stretched grid row: without this a sub-less cell (Snapchat,
     YouTube) is shorter than the one beside it and its name/figure ride high
     instead of sharing a centre line with its neighbour. Also makes the whole
     row the tap target. */
  height: 100%;
  text-decoration: none; color: var(--a7ab-ink);
  border-radius: var(--a7ab-r-in); transition: background .2s ease;
}
@media (hover: hover){
  .a7ab-led-a:hover{ background: color-mix(in oklch, hsl(var(--base-content)) 4%, transparent); }
  .a7ab-led-a:hover .a7ab-led-ex{ opacity: 1; transform: translate(1px, -1px); }
  body:not(.font-english) .a7ab-led-a:hover .a7ab-led-ex{ transform: scaleX(-1) translate(-1px, -1px); }
}
.a7ab-led-a:focus-visible{ outline: 2px solid hsl(var(--primary)); outline-offset: 2px; }

/* the page's ONE numeral series. .a7ab-interest-idx used to be a second,
   competing "01" system at station 02; it is deleted, not replaced. */
.a7ab-led-idx{
  min-width: 2.6ch; font-size: .66rem; font-weight: 700; letter-spacing: .14em;
  color: var(--a7ab-faint);
}
.a7ab-led-ic{
  display: inline-flex; flex: 0 0 auto;
  color: color-mix(in oklch, hsl(var(--pf-tint)) 58%, hsl(var(--base-content)));
}
.a7ab-led-ic svg{ width: 17px; height: 17px; display: block; }
.a7ab-led-id{ display: flex; flex-direction: column; gap: .14rem; min-width: 0; }
.a7ab-led-name{ font-size: .95rem; font-weight: 700; letter-spacing: -.01em; }
.a7ab-led-handle{
  display: inline-flex; align-items: center; gap: .3em; min-width: 0;
  font-size: .76rem; color: var(--a7ab-muted); white-space: nowrap;
}
.a7ab-led-ex{ flex: 0 0 auto; opacity: .45; transition: opacity .18s ease, transform .18s ease; }
.a7ab-led-ex svg{ width: 11px; height: 11px; display: block; }
body:not(.font-english) .a7ab-led-ex{ transform: scaleX(-1); }

.a7ab-led-fig{
  display: flex; flex-direction: column; align-items: flex-end;
  gap: .08rem; text-align: end;
}
/* EVERY figure at the SAME size — equal optical weight is equal standing.
   2,811,400 and 24,600 differ only in glyph count, which is a quiet fact. */
.a7ab-led-n{
  font-size: clamp(1.08rem, 3.1vw, 1.34rem); font-weight: 800;
  letter-spacing: -.025em; line-height: 1.1;
}
.a7ab-led-noun{ font-size: .74rem; font-weight: 600; color: var(--a7ab-muted); }
/* second row, under the name. MUTED not faint — X's "since 2012" is the string
   that reframes a small account as a legacy one, so it must clear 4.5:1. */
.a7ab-led-sub{
  grid-column: 3 / -1; margin-top: -2px;
  font-size: .72rem; font-weight: 600; color: var(--a7ab-muted); line-height: 1.5;
}
/* reserves its line even when empty — see the note in page.py: identical grid
   geometry in every cell is what keeps two neighbouring figures on one line.
   ⛔ 1.5em, matching this element's own line-height. At the old 1.08em the
   reserve measured ~12.4px against a populated row's ~17.3px, so the rule
   under-reserved by ~5px and only looked right because grid row stretch was
   quietly absorbing the difference. If the line-height above changes, change
   this with it — they are the same number by definition. */
.a7ab-led-sub:empty{ min-height: 1.5em; }
.a7ab-led-sub[hidden]{ display: none; }   /* explicit-display + [hidden] guard */

/* ── sub-beat 2 · the brand wall (one cap-height optical system) ──────────── */
/* BASE = static, centred, wrapped grid. No JS, no motion, everything visible. */
.a7ab-bw-row--b{ display: none; }                 /* second row is a motion-only layer */
.a7ab-bw-belt{ display: block; }
.a7ab-bw-set{
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: clamp(12px, 2.2vw, 20px) clamp(18px, 3.6vw, 44px);
  margin: 0; padding: 0; list-style: none;
}
.a7ab-bw-set.is-dupe{ display: none; }

.a7ab-bw-plate{
  /* The FLOOR is set by the widest mark, not the average. SAMSUNG carries the
     smallest optical multiplier (.767), so a 17px floor rendered it ~13px tall
     on a 375px phone — too small to read. 19px puts it at ~15px while the rows
     stay inside the viewport (they scroll, and the row clips with overflow). */
  --bw-cap: clamp(19px, 2.1vw, 23px);
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  height: calc(var(--bw-cap) * 2.5);
  padding-inline: clamp(9px, 1.3vw, 15px);
  border-radius: var(--a7ab-r);
  font-family: "Inter", "Helvetica Neue", Arial, ui-sans-serif, system-ui, sans-serif;
  font-size: calc(var(--bw-cap) * var(--bw-size, 1));
  font-weight: var(--bw-w, 600);
  letter-spacing: var(--bw-tr, .2em);
  text-transform: var(--bw-case, uppercase);
  line-height: 1; white-space: nowrap; unicode-bidi: isolate;
  -webkit-font-smoothing: antialiased;
  color: color-mix(in oklch, hsl(var(--base-content)) 44%, transparent);
  background: transparent;
  box-shadow: 0 0 0 1px transparent inset;
  transition: color .45s cubic-bezier(.22,1,.36,1), background .45s cubic-bezier(.22,1,.36,1),
              box-shadow .45s cubic-bezier(.22,1,.36,1);
}
/* cancel the trailing sidebearing letter-spacing adds after the last glyph */
.a7ab-bw-wm{ display: inline-block; margin-inline-end: calc(-1 * var(--bw-tr, 0em)); }

/* THE REAL MARKS. The asset supplies only a SHAPE (alpha); the fill is
   `currentColor`, so one file serves both themes and inherits every colour
   rule already written for this plate — dim at rest, warm on hover, and the
   row-B parallax dim, with no per-theme asset and no extra JS.
   Height is driven off the shared cap line so all ten sit on one baseline;
   width follows each mark's own aspect ratio so nothing is ever squashed. */
.a7ab-bw-mk{
  display: block;
  height: calc(var(--bw-cap) * var(--bw-size, 1));
  width: calc(var(--bw-cap) * var(--bw-size, 1) * var(--bw-ar, 3));
  background: currentColor;
  -webkit-mask: var(--bw-mask) no-repeat center / contain;
          mask: var(--bw-mask) no-repeat center / contain;
}
/* If mask-image is unsupported the block above would paint ten solid colour
   bars. Anything that can't mask gets nothing at all instead — the wall is
   decorative and the brand names are already in the .a7ab-sr list. */
@supports not ((-webkit-mask-image: url("")) or (mask-image: url(""))){
  .a7ab-bw-plate{ display: none; }
}
.a7ab-bw-plate.is-more{ color: color-mix(in oklch, hsl(var(--base-content)) 30%, transparent); }
.a7ab-bw-plate.is-more .a7ab-bw-wm{
  padding: .4em .9em; border-radius: var(--a7ab-r-in);
  border: 1px dashed color-mix(in oklch, hsl(var(--base-content)) 18%, transparent);
  margin-inline-end: 0; font-style: italic;
}
.a7ab-bw-plate.is-more .t-ar{ font-family: "Tajawal", ui-sans-serif, system-ui, sans-serif; letter-spacing: 0; }

@media (hover: hover) and (pointer: fine){
  .a7ab-bw-plate:hover{
    color: hsl(var(--base-content));
    background: color-mix(in oklch, hsl(var(--primary)) 8%, transparent);
    box-shadow: 0 0 0 1px color-mix(in oklch, hsl(var(--primary)) 22%, transparent) inset;
  }
  .a7ab-bw-plate.is-more:hover{ color: color-mix(in oklch, hsl(var(--base-content)) 66%, transparent); }
}

/* MARQUEE UPGRADE — only once JS adds .is-marquee */
.a7ab-bw.is-marquee .a7ab-bw-row{
  --bw-gap: clamp(22px, 4vw, 54px);
  direction: ltr;                       /* pin the Latin sequence + the maths in RTL */
  overflow: hidden; contain: content;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%);
}
.a7ab-bw.is-marquee .a7ab-bw-row--a{ margin-bottom: clamp(8px, 1.4vw, 14px); }
.a7ab-bw.is-marquee .a7ab-bw-row--b{ display: block; }
.a7ab-bw.is-marquee .a7ab-bw-belt{
  display: flex; width: max-content;
  backface-visibility: hidden; will-change: transform;
  animation: a7ab-bw-drift var(--bw-dur, 56s) linear infinite;
  animation-play-state: paused;         /* rests visible; JS opts into motion */
}
.a7ab-bw.is-marquee.is-live .a7ab-bw-belt{ animation-play-state: running; }
/* pause the whole wall on hover/focus so a mark never slides out from under you */
.a7ab-bw.is-marquee:hover .a7ab-bw-belt,
.a7ab-bw.is-marquee:focus-within .a7ab-bw-belt{ animation-play-state: paused; }
.a7ab-bw.is-marquee .a7ab-bw-set{
  flex-wrap: nowrap; justify-content: flex-start;
  gap: var(--bw-gap); padding-inline-end: var(--bw-gap);   /* keeps the seam even */
}
.a7ab-bw.is-marquee .a7ab-bw-set.is-dupe{ display: flex; }
/* row B recedes — smaller, dimmer, slower, reversed = parallax depth */
.a7ab-bw.is-marquee .a7ab-bw-row--b .a7ab-bw-plate{
  --bw-cap: clamp(16px, 1.7vw, 19px);   /* floor raised with row A, same reason */
  color: color-mix(in oklch, hsl(var(--base-content)) 26%, transparent);
}
.a7ab-bw.is-marquee .a7ab-bw-row--b .a7ab-bw-belt{ animation-direction: reverse; }
/* RTL: the lead edge comes from the right — flip which row runs which way */
body:not(.font-english) .a7ab-bw.is-marquee .a7ab-bw-row--a .a7ab-bw-belt{ animation-direction: reverse; }
body:not(.font-english) .a7ab-bw.is-marquee .a7ab-bw-row--b .a7ab-bw-belt{ animation-direction: normal; }
@keyframes a7ab-bw-drift{ from{ transform: translate3d(0,0,0); } to{ transform: translate3d(-50%,0,0); } }

/* ── sub-beat 3 · the permit seal ───────────────────────────────────────────
   ⛔ INVARIANT 3 lives here in practice: this is a TINTED surface, and no pin
   or tick may ever sit over it — the mark's base-100 halo is what masks the
   rail behind it, and over this background it stops reading as on the line. */
.a7ab-permit{
  display: flex; align-items: center; flex-wrap: wrap; gap: clamp(12px, 2.4vw, 18px);
  padding: clamp(15px, 2.6vw, 20px) clamp(16px, 3vw, 22px);
  border-radius: var(--a7ab-r); border: 1px solid var(--a7ab-tint-bd);
  background:
    repeating-linear-gradient(115deg,
      color-mix(in oklch, hsl(var(--primary)) 5%, transparent) 0 1px,
      transparent 1px 9px),
    var(--a7ab-tint);
}
.a7ab-permit-seal{ display: inline-flex; flex: 0 0 auto; color: var(--a7ab-primary); }
.a7ab-permit-seal svg{ width: 34px; height: 34px; display: block; }
.a7ab-permit-tx{ display: flex; flex-direction: column; gap: .24rem; min-width: 0; }
.a7ab-permit-lb{ font-size: .9rem; font-weight: 700; color: var(--a7ab-ink); }
.a7ab-permit-no{
  font-size: clamp(1.15rem, 3.4vw, 1.4rem); font-weight: 800;
  letter-spacing: .16em; color: var(--a7ab-primary);
}
.a7ab-permit-note{
  margin-inline-start: auto; padding: .34rem .68rem; border-radius: var(--a7ab-r-in);
  font-size: .76rem; font-weight: 700; white-space: nowrap; color: var(--a7ab-muted);
  background: color-mix(in oklch, hsl(var(--base-100)) 70%, transparent);
  border: 1px solid var(--a7ab-line);
}

/* screen-reader-only (the marquee itself is decorative) */
.a7ab-sr{
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════════════════
   ENTRANCE MOTION — enhancement only; the base IS the rested state.

   Reading order at a station: pin pops (0ms) → content fades in (0ms) → ticks
   grow in sequence (+90ms each, station 04 only) → the ledger sets (+55ms per
   row). Nothing waits on anything measured, nothing is hidden at rest.

   ⛔ NO nth-of-type / nth-child STAGGERS. They count typed siblings and desync
   silently the moment a <section>/<div>/<p> joins the container — which a
   page-wide spine guarantees. Every delay below keys off a server-emitted --i
   with a var() fallback, so a missing attribute costs synchrony, never
   visibility. page.py must emit:
     style="--i:0|1|2"  on each of the three <article class="a7ab-mv">
     --i:{i} appended to the existing style="--pf-tint:…" on .a7ab-led-row

   ⛔ NO KEYFRAME MAY TOUCH .a7ab-jr::before. The path is drawn at rest — that
   is what makes both fallbacks free.

   ⛔ NO TRANSFORM ON .a7ab-jr-st. A transform on the station makes it the
   containing block for its own ::before, so the blue pin would ride down with
   the station while the rail — drawn on the untransformed .a7ab-jr::before —
   held still, and the pin would visibly grow BELOW the line then snap onto it.
   The shipped band never had this because rail and nodes both lived inside one
   transformed element. Stations therefore FADE ONLY; the pin's own scale-pop
   supplies the motion and scales about its own centre.
   ═══════════════════════════════════════════════════════════════════════════ */
@keyframes a7ab-led-draw{ from{ transform: scaleX(0); } }
@keyframes a7ab-rail-fill{ from{ clip-path: inset(0 100% 0 0); } }
@keyframes a7ab-rail-fill-rtl{ from{ clip-path: inset(0 0 0 100%); } }
@keyframes a7ab-pf-rise{ from{ opacity: 0; transform: translateY(10px); } }

/* The limb grows OUT of the spine, then its three leaves land in chronological
   order in its wake. Both keyed off the station's EXISTING `.is-in` — no new
   observer, no new JS, no new file.
   ⛔ Every keyframe here is `from{}`-only with `backwards`: the resting state
   IS the finished state, so no-JS / reduced-motion / hidden-tab / crawler all
   render the rail drawn, the nodes placed and every mark painted. Nothing in
   this block is gated on a class-triggered transition.
   ⛔ The RTL twin has an identical body ON PURPOSE — transform-origin is set
   per-direction on the rule, and having a NAMED rtl variant is what lets the
   reduced-motion cancel be written at matching specificity (same pattern as
   a7ab-rail-fill / -rtl above). */
@keyframes a7ab-apl-draw    { from{ transform: scaleX(0); } }
@keyframes a7ab-apl-draw-rtl{ from{ transform: scaleX(0); } }
@keyframes a7ab-apl-pop     { from{ opacity: 0; transform: scale(.4); } }

.a7ab-jr-st.is-in .a7ab-apl-grid::before{
  animation: a7ab-apl-draw .64s cubic-bezier(.16,1,.3,1) .34s backwards;
}
body:not(.font-english) .a7ab-jr-st.is-in .a7ab-apl-grid::before{
  animation-name: a7ab-apl-draw-rtl;
}
.a7ab-jr-st.is-in .a7ab-apl-it::before{
  animation: a7ab-apl-pop .34s cubic-bezier(.34,1.4,.64,1)
             calc(.70s + var(--i, 0) * .08s) backwards;
}
@keyframes a7ab-jr-in  { from{ opacity: 0; } }
@keyframes a7ab-jr-pin { from{ opacity: 0; transform: scale(.2); } }
@keyframes a7ab-jr-tick{ from{ opacity: 0; transform: scaleX(0); } }

/* the station: opacity only (see the transform warning above).
   0,3,0 correctly beats .a7ab-reveal.is-in (0,2,0), so stations do NOT inherit
   the hero's 18px lurch — five full-block 18px fades down one continuous line
   lurches; this reads as settling into place. */
.a7ab-jr-st.a7ab-reveal.is-in{ animation: a7ab-jr-in .55s cubic-bezier(.16, 1, .3, 1); }
.a7ab-jr-st.is-in::before{ animation: a7ab-jr-pin .5s cubic-bezier(.16, 1, .3, 1) backwards; }

/* the line fills as ONE object (site easing), while about.js counts the
   percentages up to meet it */
.a7ab-pf.is-in .a7ab-rail{ animation: a7ab-rail-fill 1.1s cubic-bezier(.16,1,.3,1) .12s backwards; }
body:not(.font-english) .a7ab-pf.is-in .a7ab-rail{ animation-name: a7ab-rail-fill-rtl; }

/* the rule inks (transform-origin already mirrors for RTL), then the six
   entries set in the owner's order — a stagger that follows DOM order reads
   correctly in both directions with no extra rules. */
.a7ab-pf.is-in .a7ab-led-rule{ animation: a7ab-led-draw .75s cubic-bezier(.16,1,.3,1) .1s backwards; }

/* six ledger rows — one rule, keyed off --i (replaces six nth-child rules) */
.a7ab-pf.is-in .a7ab-led-row{
  animation: a7ab-pf-rise .5s cubic-bezier(.16,1,.3,1) backwards;
  animation-delay: calc(.26s + var(--i, 0) * .055s);
}

/* the three ticks — one rule, keyed off --i (replaces the a7ab-pf-node rules) */
.a7ab-pf.is-in .a7ab-mv::before{
  animation: a7ab-jr-tick .45s cubic-bezier(.16,1,.3,1) backwards;
  animation-delay: calc(var(--i, 0) * .09s);
}

.a7ab-pf.is-in .a7ab-permit{ animation: a7ab-pf-rise .6s cubic-bezier(.16,1,.3,1) .12s backwards; }

/* NO JS: .is-in is never applied → no animation rule matches → every base state
   renders. Correct BY CONSTRUCTION, not by fallback. The existing IO in
   about.js already unobserves per element, so it generalises from 8 to 7
   .a7ab-reveal targets (titlerow, hero, 5 stations) with ZERO JS changes. */

/* ═══════════════════════════════════════════════════════════════════════════
   RESPONSIVE — 700 / 640 / 620 / 560 / 420
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 700px){
  /* station 02 field pair — stacks EARLIER than the old 560px on purpose;
     561-700px is where a two-up is tightest against the 48px trough. */
  .a7ab-fields{ grid-template-columns: 1fr; }
  .a7ab-fields .a7ab-p{ max-width: none; }

  .a7ab-reach-mast{ grid-template-columns: 1fr; align-items: start; }
  /* the recon rule reads as a sidebar only while it IS beside something */
  .a7ab-pf-recon{
    max-width: none; padding-inline-start: 0; border-inline-start: 0;
    padding-top: 12px; border-top: 1px solid var(--a7ab-line); margin-bottom: 0;
  }
  .a7ab-jr-lead{ max-width: none; }
  .a7ab-jr-hand{ max-width: none; }
}

@media (max-width: 640px){
  /* 420px x 0.603 = a 697px tower on a phone. The cutout is far taller than the
     4/5 crop it replaces, so the cap comes down: 360px -> 230px wide, 381px tall.
     The melt is percentage-based and needs no change here. */
  .a7ab-portrait{ width: 100%; max-width: clamp(230px, 64vw, 300px); margin-inline: auto; }
  /* the ground spills past the figure by design; on a phone that spill is what
     pushed the document to 387px in a 360px viewport. Tighten it here rather
     than clipping the figure — clipping would square the ground back off, which
     is the one thing this treatment must never do. */

  .a7ab-hero{ grid-template-columns: 1fr; gap: clamp(22px, 6vw, 30px); }
}

@media (max-width: 620px){
  /* the spine narrows — the same three numbers the band used to declare on its
     own wrapper, now governing the whole page instead of one section. */
  .a7ab-jr{ --pf-pad: 20px; --pf-x: 3.5px; --pf-node: 9px; padding-block: 22px 30px; }
  .a7ab-jr::before{
    background: linear-gradient(to bottom,
      transparent 0,
      var(--a7ab-line-strong) 18px,
      var(--a7ab-line-strong) calc(100% - 22px),
      transparent 100%);
  }
  .a7ab-jr-st + .a7ab-jr-st{ margin-top: clamp(48px, 11vw, 60px); }
  /* one column: uniform cell height was a two-column concern only, and ragged
     heights read as natural ledger rhythm here.
     names drop, colour + % remain: six floored segments no longer fit a phone */
  .a7ab-rail{ height: 34px; }
  .a7ab-rail-seg{ min-width: 42px; padding-inline: .25rem; }
  .a7ab-rail-pc{ font-size: .68rem; min-width: 4.4ch; }
  .a7ab-rail-lb{ display: none; }
  .a7ab-led{ grid-template-columns: 1fr; }
  .a7ab-led::before{ display: none; }
  .a7ab-led-row:nth-last-child(-n+2){ border-bottom: 1px solid var(--a7ab-line); }
  .a7ab-led-row:last-child{ border-bottom: 0; }
  .a7ab-led-a{ min-height: 0; }
  .a7ab-permit-note{ margin-inline-start: 0; }
  /* ⛔ THIS is the rule that wins on phones — it is unqualified and comes after
     both the base and the row-B rules, so it overrides them BOTH. Any change to
     the wall's cap height has to be made here too, or it silently does nothing
     on mobile. 19px keeps SAMSUNG (the smallest multiplier, .767) at ~15px. */
  .a7ab-bw-plate{ --bw-cap: 19px; padding-inline: 8px; }
  .a7ab-bw.is-marquee .a7ab-bw-row{
    --bw-gap: 28px;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 10%, #000 90%, transparent 100%);
  }
}

@media (max-width: 560px){
  /* the pair stacks. Its images then get the full column, so the 4:3 crop that
     read as a thumbnail beside its twin becomes a proper editorial plate. */

}

/* ── partnership-lane chips: equal 2-col grid where they would otherwise wrap ──
   They were content-width items in a wrapping flex row, so they came out
   ragged. Measured at a 333.7px container (393px device): AR 90.3 / 137.6 /
   129 / 110.2 in a 2+2 zigzag, and EN WORSE — four separate rows (152.1 /
   190.4 / 197.6 / 139.2). An equal 2-col grid gives every chip the same box.

   ⛔ FOUR-ACROSS IS IMPOSSIBLE ON A PHONE — do not "improve" this to 4 columns.
   At a 333.7px container each chip gets (333.7 - 3*10)/4 = 75.9px, of which
   58.4px is padding + arrow + gap + border, leaving 17.5px of text for a label
   needing 79.2px in AR and 139.2px in EN. Even deleting the arrow AND halving
   the padding only reaches ~61px. The 44px touch target and the labels both
   have to go before it fits, so this is not a layout problem to solve.

   Breakpoints are measured, not guessed. Container = viewport - 80 (the wrap's
   2x30px + the spine's 20px), capped at 800 by the wrap's 880px max-width. One
   flex row needs 497.1px in AR and 709.3px in EN, i.e. viewport 577 / 789. The
   AR cut is set at 620 rather than 577 so it keeps ~43px of headroom against
   font-metric drift instead of 2.9px; EN at 810 for the same reason.

   Chips centre their content and the EN label may wrap to two lines; grid's
   default stretch keeps both cells in a row equal height, so a wrap can never
   stagger its neighbour. */
@media (max-width: 810px){
  /* EN labels run ~40% longer than their AR counterparts, so English is still
     ragged in the 621-810 band where AR already fits all four on one line.
     The site keeps the active language on `body.font-english`, so English can
     be gridded alone instead of forcing AR into two rows it does not need.
     Specificity (0,2,1) beats the base rule regardless of source order. */
  body.font-english .a7ab-lanes{ display: grid; grid-template-columns: 1fr 1fr; }
  body.font-english .a7ab-lane{ justify-content: center; text-align: center; padding-inline: .75rem; }
}

@media (max-width: 620px){
  .a7ab-lanes{ display: grid; grid-template-columns: 1fr 1fr; }
  .a7ab-lane{ justify-content: center; text-align: center; padding-inline: .75rem; }
  .a7ab-lane-tx{ min-width: 0; }

  /* The app branch stays ALL-UP in one row at every width down to 320px — no
     stack, no horizontal scroller/snap rail (a nested horizontal gesture inside
     a vertical scroll would hide an app on the device most of his traffic uses).
     ⛔ OWNER 2026-07-21: on the PHONE ONLY — (a) the per-app description is
     hidden (it varied 2↔3 lines and threw the row off-balance); (b) names are
     forced to ONE line via a fluid size that fits even «Shadow Studio» (the
     longest) — measured 65px in a 70px column at 360px, growing with the
     viewport up to the .82rem ceiling; (c) the 8px gap + the equal --ap-cols
     track (base rule) make the leaves even and «جنب بعض». Descriptions stay on
     desktop where they reveal on hover. --ap-cap/--ap-plate keep their clamp
     floors (26/44px, 44 = touch-target floor); --ap-rail + rail inline-start
     recompute from the mobile --pf-* tokens with no hard-coded mobile literal. */
  .a7ab-apl{ margin-top: 26px; }
  .a7ab-apl-grid{
    --ap-node: 6px; --ap-r1: 14px; --ap-r2: 12px;
    column-gap: 8px;
  }
  .a7ab-apl-dt{ font-size: .62rem; }
  .a7ab-apl-nm{
    font-size: clamp(.56rem, 2.5vw, .82rem);
    margin-top: .55rem;
    /* ONE line. No overflow:hidden/ellipsis here — that would clip the stretched
       `.a7ab-apl-a::after` hit target (it resolves to .a7ab-apl-it, a descendant
       of this <h3> in the box tree). The fluid size is proven to fit ≥360px. */
    white-space: nowrap;
  }
  .a7ab-apl-ds{ display: none; }
}

@media (max-width: 420px){
  /* 360px viewport → ~324px column. The grid is content-driven and already fits;
   this buys 6.4px per chip (12.8px on the row) of margin against font-metric
   drift on a device face, and swaps the name's `overflow: visible` for a safe
   ellipsis so a surprise degrades inside the chip instead of spilling past its
   border. Icon stays 17px, both font sizes untouched — only chrome tightens. */
  /* ⛔ NOT nested in a second @media(max-width:400px) — that left 401–420px
     (iPhone XR/11/11 Pro Max/8 Plus = 414px) without the ellipsis safety net, so
     an over-long owner-added social name could spill its chip. This is the 420px
     scope where the comment above already promises the fallback. */
  .a7ab-soc{ padding-inline: .7rem; column-gap: .5rem; }
  .a7ab-soc-name{ overflow: hidden; text-overflow: ellipsis; }
  .a7ab-led-a{ column-gap: 8px; padding-inline: 2px; }
  .a7ab-led-idx{ min-width: 2.2ch; font-size: .62rem; }
  .a7ab-led-n{ font-size: 1.06rem; }
  .a7ab-led-sub{ line-height: 1.45; }
  .a7ab-dir-a{ gap: .6rem; min-height: 52px; }
  .a7ab-dir-label{ font-size: .98rem; }
  .a7ab-jr-decode .a7ab-decode-mark{ padding: .38rem .75rem; }
  /* the stat pill's narrow-viewport budget. Below ~414px the hero collapses to
     one column and the pill has ~324px; the shipped label overruns that in
     English. This buys back ~45px of it so most phones keep one line, and
     flex-wrap above catches whatever is left. ⛔ It MITIGATES — see the pill's
     note near the top of this file; it does not make the label fit at 360px,
     and nothing here should be read as claiming it does. */
  .a7ab-stat-metric{ padding: .6rem .9rem; gap: .26rem; }
  .a7ab-stat-label{ margin-inline-start: .2rem; font-size: .8rem; }
}

/* ════════════════════════════════════════════════════════════════════════════
   SECTION-CLOSED / «جارٍ التطوير» screen — the Card Designer's «.a7cs-» closure
   standard, rendered by page.py _closure_html() when settings.maintenance.mode
   is on. Theme-aware via the shell's DaisyUI vars (--primary / --base-content /
   --base-100/200), identical to /cards and /app. Self-contained (own reduced-
   motion cancel with !important), so its position ahead of the file's final
   reduced-motion block is safe — it never touches an .a7ab-* selector.
   ════════════════════════════════════════════════════════════════════════════ */
.a7cs-wrap {
  --cs-accent:        hsl(var(--primary));
  --cs-accent-press:  color-mix(in oklch, hsl(var(--primary)) 82%, #000);
  --cs-accent-soft:   color-mix(in oklch, hsl(var(--primary)) 12%, transparent);
  --cs-text:          hsl(var(--base-content));
  --cs-muted:         color-mix(in oklch, hsl(var(--base-content)) 62%, transparent);
  --cs-faint:         color-mix(in oklch, hsl(var(--base-content)) 60%, transparent);
  --cs-surface-2:     hsl(var(--base-200));
  --cs-border:        color-mix(in oklch, hsl(var(--base-content)) 12%, transparent);
  --cs-border-strong: color-mix(in oklch, hsl(var(--base-content)) 18%, transparent);
  min-height: 66vh;
  display: flex; align-items: center; justify-content: center;
  padding: 40px 22px;
  font-family: inherit;
  color: var(--cs-text);
  background: radial-gradient(120% 80% at 50% -10%,
    color-mix(in oklch, hsl(var(--base-content)) 5%, transparent), transparent 60%);
}
.a7cs-card { width: 100%; max-width: 560px; text-align: center; display: flex; flex-direction: column; align-items: center; }
.a7cs-badge {
  display: inline-flex; align-items: center; gap: 7px; padding: 7px 14px; border-radius: 999px;
  background: var(--cs-accent-soft); color: var(--cs-accent); font-size: 13px; font-weight: 700;
  margin-bottom: 30px; border: 1px solid color-mix(in oklch, hsl(var(--primary)) 22%, transparent);
}
.a7cs-pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--cs-accent); position: relative; }
.a7cs-pulse::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--cs-accent);
  animation: a7cs-pulse 1.8s cubic-bezier(.22,.61,.36,1) infinite;
}
@keyframes a7cs-pulse { 0% { transform: scale(1); opacity: .6 } 70% { transform: scale(3); opacity: 0 } 100% { opacity: 0 } }
.a7cs-iconwrap { position: relative; width: 128px; height: 128px; margin-bottom: 34px; display: flex; align-items: center; justify-content: center; }
.a7cs-ring { position: absolute; inset: 0; border-radius: 34px; border: 1.5px solid var(--cs-border-strong); animation: a7cs-floaty 5s ease-in-out infinite; }
.a7cs-ring--b { inset: -14px; border-radius: 42px; opacity: .5; animation-delay: .4s; }
.a7cs-iconbox {
  width: 96px; height: 96px; border-radius: 28px;
  background: linear-gradient(155deg, hsl(var(--primary)), var(--cs-accent-press));
  display: flex; align-items: center; justify-content: center; color: hsl(var(--primary-content));
  box-shadow: 0 14px 34px -10px color-mix(in oklch, hsl(var(--primary)) 45%, transparent);
  animation: a7cs-floaty 5s ease-in-out infinite;
}
.a7cs-iconbox svg { width: 48px; height: 48px; }
@keyframes a7cs-floaty { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(-7px) } }
.a7cs-title {
  font-family: inherit; font-weight: 800; font-size: clamp(22px, 3.8vw, 28px);
  line-height: 1.3; margin: 0 0 14px; letter-spacing: -.3px; color: var(--cs-text); text-wrap: balance;
}
.a7cs-sub { font-size: clamp(15px, 2.4vw, 18px); color: var(--cs-muted); line-height: 1.7; margin: 0 0 34px; max-width: 430px; }
.a7cs-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
.a7cs-btn {
  display: inline-flex; align-items: center; gap: 9px; height: 50px; padding: 0 24px; border-radius: 14px;
  font-size: 15px; font-weight: 700; text-decoration: none; cursor: pointer; border: none; font-family: inherit;
  transition: transform .08s, background .14s;
}
.a7cs-btn svg { width: 18px; height: 18px; }
/* Back-arrow points toward "home" per reading direction (flips in RTL). */
[dir="rtl"] .a7cs-btn svg { transform: scaleX(-1); }
.a7cs-btn:active { transform: scale(.97); }
.a7cs-btn--primary {
  background: hsl(var(--primary)); color: hsl(var(--primary-content));
  box-shadow: 0 6px 18px -4px color-mix(in oklch, hsl(var(--primary)) 55%, transparent);
}
.a7cs-btn--primary:hover { background: var(--cs-accent-press); }
.a7cs-progress { margin-top: 40px; width: 100%; max-width: 300px; }
.a7cs-bar { height: 6px; border-radius: 999px; background: var(--cs-surface-2); overflow: hidden; border: 1px solid var(--cs-border); }
.a7cs-fill { height: 100%; width: var(--cs-progress, 68%); border-radius: 999px; background: linear-gradient(90deg, hsl(var(--primary)), var(--cs-accent-press)); animation: a7cs-grow 2.4s cubic-bezier(.22,.61,.36,1) both; }
@keyframes a7cs-grow { from { width: 0 } to { width: var(--cs-progress, 68%) } }
.a7cs-lbl { display: flex; justify-content: space-between; margin-top: 10px; font-size: 12.5px; font-weight: 600; color: var(--cs-faint); }
@media (prefers-reduced-motion: reduce) {
  .a7cs-ring, .a7cs-iconbox, .a7cs-pulse::after, .a7cs-fill { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   REDUCED MOTION — ⛔ THIS IS THE LAST BLOCK IN THE FILE, AND IT MUST STAY THAT
   WAY. Every selector here ties EXACTLY on specificity with the rule it
   cancels, and @media contributes ZERO specificity, so SOURCE ORDER is the only
   tie-break. Append one animated rule after this block and that animation plays
   for a user who asked for none — and it fails invisibly on any machine without
   the OS setting on. New animation → declare it ABOVE, cancel it INSIDE.

   Result: line drawn, pins drawn, ticks drawn, rail full, rows placed, wall a
   static wrapped grid, all content in place. The page is complete and correct
   with zero motion — because the base state IS the rested state, everywhere.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce){
  /* off-spine reveals + the signature */
  .a7ab-reveal.is-in{ animation: none; }
  .a7ab-hero.is-in.a7ab-sig-go .a7ab-sig-wipe{ animation: none; }

  /* the spine */
  .a7ab-jr-st.a7ab-reveal.is-in{ animation: none; }
  .a7ab-jr-st.is-in::before{ animation: none; }

  /* the app branch. ⛔ The RTL twin is MANDATORY and must be explicit, for the
     exact reason spelled out for the rail below: the normal-motion override
     `body:not(.font-english) .a7ab-jr-st.is-in .a7ab-apl-grid::before` sets
     animation-NAME at (0,4,0), and the (0,3,0) reset cannot beat it — so under
     `reduce` in ARABIC, the page's default language, the draw would survive.
     The hover ink/plate state is deliberately KEPT (it becomes instantaneous):
     a state change is not motion. */
  .a7ab-jr-st.is-in .a7ab-apl-grid::before{ animation: none; }
  body:not(.font-english) .a7ab-jr-st.is-in .a7ab-apl-grid::before{ animation: none; }
  .a7ab-jr-st.is-in .a7ab-apl-it::before{ animation: none; }
  /* the description still REVEALS on hover/focus under `reduce` — it just
     arrives instantly and without the 3px lift. A state change is not motion,
     and removing the reveal would hide the text from these users entirely. */
  .a7ab-apl-plate, .a7ab-apl-mk, .a7ab-apl-ds{ transition: none; }
  .a7ab-apl-ds{ transform: none; }

  /* the band */
  .a7ab-pf.is-in .a7ab-rail,
  .a7ab-pf.is-in .a7ab-led-rule,
  .a7ab-pf.is-in .a7ab-led-row,
  .a7ab-pf.is-in .a7ab-permit,
  .a7ab-pf.is-in .a7ab-mv::before{ animation: none; }
  /* ⛔ the RTL rail needs its OWN cancel at matching specificity. The normal-
     motion override `body:not(.font-english) .a7ab-pf.is-in .a7ab-rail` sets
     animation-NAME and is (0,4,0); the (0,3,0) reset above cannot beat it, so
     under `reduce` in ARABIC — the page's default language — the name survived
     and only collapsed to a 0s duration by accident. This makes it explicit. */
  body:not(.font-english) .a7ab-pf.is-in .a7ab-rail{ animation: none; }

  /* transitions.
     ⛔ `.a7ab-crumb[href]`, NOT `.a7ab-crumb`. The transition being cancelled is
     declared on the ATTRIBUTE form (0,2,0); a bare class (0,1,0) loses on
     specificity no matter where it sits, so the breadcrumb's 180ms colour fade
     survived `reduce`. This is the exact failure class B4 exists to eliminate —
     verify these with a specificity-aware pass, not by matching names. */
  .a7ab-btn, .a7ab-btn-arrow, .a7ab-crumb[href], .a7ab-soc, .a7ab-lane,
  .a7ab-stat-metric, .a7ab-dir-a, .a7ab-dir-arrow,
  .a7ab-led-a, .a7ab-led-row::before, .a7ab-led-ex, .a7ab-bw-plate{ transition: none; }
  .a7ab-btn:hover, .a7ab-soc:hover, .a7ab-lane:hover{ transform: none; }
  /* the primary CTA's arrow. Killing `.a7ab-btn-arrow`'s transition above only
     makes the 3px shift INSTANT; the transform itself needs cancelling, exactly
     as .a7ab-dir-arrow and .a7ab-led-ex get below. RTL keeps its mirror. */
  .a7ab-btn:hover .a7ab-btn-arrow{ transform: none; }
  body:not(.font-english) .a7ab-btn:hover .a7ab-btn-arrow{ transform: scaleX(-1); }
  /* kill the directory's 6px inline shift; the colour change still marks hover */
  .a7ab-dir-a:hover{ padding-inline-start: 2px; }
  .a7ab-dir-a:hover .a7ab-dir-arrow{ transform: none; }
  body:not(.font-english) .a7ab-dir-a:hover .a7ab-dir-arrow{ transform: scaleX(-1); }
  .a7ab-led-a:hover .a7ab-led-ex{ transform: none; }
  body:not(.font-english) .a7ab-led-a:hover .a7ab-led-ex{ transform: scaleX(-1); }

  /* the wall falls all the way back to the static wrapped grid */
  .a7ab-bw.is-marquee .a7ab-bw-row{
    overflow: visible; contain: none; direction: inherit;
    -webkit-mask-image: none; mask-image: none;
  }
  .a7ab-bw.is-marquee .a7ab-bw-row--b{ display: none; }
  .a7ab-bw.is-marquee .a7ab-bw-belt{ display: block; width: auto; animation: none; will-change: auto; }
  .a7ab-bw.is-marquee .a7ab-bw-set{
    flex-wrap: wrap; justify-content: center;
    gap: clamp(12px, 2.2vw, 20px) clamp(18px, 3.6vw, 44px); padding-inline-end: 0;
  }
  .a7ab-bw.is-marquee .a7ab-bw-set.is-dupe{ display: none; }


  /* station 02 · the two field marks. Selectors are byte-identical to the
     rules they cancel, so they TIE at (0,3,0) and win on source order. */
  .a7ab-fields.is-fx-in .a7ab-fx-stroke{ animation: none; }
  .a7ab-fields.is-fx-in .a7ab-fx-ring::after{ animation: none; opacity: 0; }

  html.a7ab-js .a7ab-jr-st > *{ opacity: 1; transform: none; filter: none; }
  html.a7ab-js .a7ab-jr-st.is-in > *{ animation: none; }
}
