/* ═══════════════════════════════════════════════════════════════════════════
   The landing page — built on the design system, not beside it
   ═══════════════════════════════════════════════════════════════════════════

   Everything structural here comes from assets/css/gravity-ds.css: the tokens,
   the glass, the aurora, the accent card, the segmented bar, the ring, the
   display type, the entrance. This file is the page's own composition and
   nothing else — if a rule here could have been a device, it should have been.

   THE IDEA. The agency's name is its argument: mass attracts. So the page is
   built as a series of things being pulled toward a centre — the hero's glass
   sits on the mark, the logos gather, the work collects into one field, and
   the month resolves into a single bar. Nothing floats free.

   ARABIC FIRST, AND THE TYPE PROVES IT. The headline is live Arabic text in
   Expo Arabic at display size, with the English line kept beneath it in
   Poppins. It used to be a 25 KB PNG of English words, which is a picture of a
   headline rather than a headline: no heading for a search engine, no text to
   select or translate, and blurred at any zoom.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ---------------------------------------------------------------- 1. shell */

.s-wrap { width: min(1220px, 100%); margin-inline: auto; padding-inline: clamp(1rem, 4vw, 2.5rem); }

/* A section is a rhythm, not a box. One spacing rule, one label rule, and the
   page keeps its cadence however many sections are added from the panel. */
.s-sec { padding-block: clamp(3.5rem, 9vw, 7rem); }
.s-sec + .s-sec { padding-block-start: 0; }

.s-lbl {
  display: flex; align-items: center; gap: .7rem;
  font-size: .74rem; font-weight: 700; letter-spacing: .08em;
  color: var(--g-muted); text-transform: uppercase;
  margin-bottom: 1rem;
}
.s-lbl::after { content: ""; flex: 1; height: 1px; background: var(--g-line); }
:is([lang="ar"], [dir="rtl"]) .s-lbl { letter-spacing: normal; text-transform: none; font-size: .8rem; }

.s-h2 {
  font-size: clamp(1.6rem, 3.6vw, 2.6rem); font-weight: 800; line-height: 1.3;
  color: var(--g-title); margin-bottom: .6rem; text-wrap: balance;
}
.s-lede { max-width: 56ch; color: var(--g-muted); line-height: 1.9; font-size: .96rem; }

/* ----------------------------------------------------------------- 2. hero */

/* The glass sits ON the mark rather than beside it: the panel is the thing
   being attracted, and the art is what is doing the attracting. */
/* THE WRAP'S OWN GUTTER, PUBLISHED AS A VARIABLE. The art is absolute inside
   the section, so its percentages resolve against the viewport while the glass
   resolves against the 1220px wrap. Below about 1400px those are nearly the
   same box and the overlap held by luck; above it the wrap stops growing and
   the section does not, so the helmet walked to the far edge. This is the same
   arithmetic .s-wrap does, exposed so the art can be measured off the panel
   instead of off the window. */
.s-hero {
  position: relative; overflow: clip;
  padding-block: clamp(4rem, 10vw, 7rem) clamp(3rem, 7vw, 5rem);
  --s-pad: clamp(1rem, 4vw, 2.5rem);
  --s-gut: max(var(--s-pad), (100% - 1220px) / 2);
}
/* THREE PLANES, AND THE GLASS IS THE MIDDLE ONE.
   The mark sits behind the panel and the helmet stands in front of it. As one
   element behind the glass they made it a tinted sheet laid over a picture;
   apart, they make a space — something passing over the pane and something
   showing through it, which is the only way a backdrop-filter reads as
   material rather than as opacity. */
/* The mark is the biggest thing in the scene and the only one allowed to run
   off the edge — it is the ground the other two stand on, and a ground with a
   visible border is a shape. It is anchored a little further out than the
   helmet so it reads as further away. */
.s-hero-mark {
  position: absolute; inset-block: -8%;
  inset-inline-end: calc(var(--s-gut) - clamp(2rem, 7vw, 7rem));
  width: min(46%, 640px); pointer-events: none; z-index: 0;
  display: grid; place-items: center; opacity: .9;
}
.s-hero-mark img {
  height: 118%; width: auto; max-width: none;
  max-height: min(38rem, 104vh);
  object-fit: contain; display: block;
}

.s-hero-art {
  position: absolute; inset-block: 0;
  /* MEASURED OFF THE PANEL, NOT THE WINDOW. Anchored to the wrap's far gutter
     and pulled a little past it, so the helmet always sits over the outer
     third of the glass — the overlap IS the effect, and one that only happens
     at some window sizes is not an effect, it is a coincidence. A helmet that
     stops where the glass begins is two pictures side by side; one that
     crosses it is a figure standing in front of a pane. */
  inset-inline-end: calc(var(--s-gut) - clamp(1rem, 3vw, 3.5rem));
  width: min(40%, 520px); z-index: 2;          /* in front of the glass */
  display: grid; place-items: center;
  /* the door needs the pointer; the glass behind it keeps its own, because the
     helmet only covers the part of the panel it overlaps */
  pointer-events: auto;
}
/* SIZED BY THE BAND'S HEIGHT, NOT THE PAGE'S WIDTH. At 74% of a 2000px
   window the helmet was 920px against a hero only ~560px tall, so it grew
   past the band and cropped through the middle of the visor. Height-first
   keeps it in proportion to the section it lives in at every width, and the
   width cap only bites on a narrow screen. */
/* SIZED BY THE BAND'S HEIGHT, NOT THE PAGE'S WIDTH — at 74% of a 2000px
   window this was 920px against a hero only ~560px tall, so it grew past the
   band and cropped through the middle of the visor. Height-first keeps it in
   proportion to the section it lives in at every width. */
.s-hero-art img {
  height: 100%; width: auto; max-width: 100%;
  max-height: min(27rem, 82vh);
  object-fit: contain; display: block;
}

/* ---- the motion ----
   A slow drift and a slower counter-drift on the mark behind it. Twenty-two
   seconds is long enough that it is felt rather than watched, and the two
   speeds are what stop the pair reading as one flat cut-out sliding about:
   parallax is the difference in rates, not the movement itself. */
/* ---- 3D, and the pointer walks around it ----
   --tx/--ty are written by landing.js from the pointer, and the two planes
   read them at different strengths: the helmet swings wide and the mark barely
   moves. That difference is the whole illusion — parallax is not motion, it is
   two rates of it — and the perspective on the section is what turns the
   rotation into a turn rather than a skew.

   The idle float stays underneath, so the scene is alive before anybody
   touches it and does not snap when the pointer leaves. */
.s-hero { perspective: 1400px; }
@keyframes sFloat {
  0%, 100% { transform: translate3d(0, 0, 0); }
  50%      { transform: translate3d(0, -1.6%, 0); }
}
@keyframes sDrift {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(0, 1.1%, 0) scale(1.03); }
}
.s-hero-art, .s-hero-mark { transform-style: preserve-3d; will-change: transform; }
.s-hero-art > picture, .s-hero-mark > picture {
  display: block;
  transform:
    rotateY(calc(var(--tx, 0) * 7deg))
    rotateX(calc(var(--ty, 0) * -5deg))
    translate3d(calc(var(--tx, 0) * 14px), calc(var(--ty, 0) * 10px), 0);
  transition: transform 260ms var(--g-ease);
}
/* the mark sits further away, so it turns less and lags a little longer */
.s-hero-mark > picture {
  transform:
    rotateY(calc(var(--tx, 0) * 2.5deg))
    rotateX(calc(var(--ty, 0) * -1.8deg))
    translate3d(calc(var(--tx, 0) * 5px), calc(var(--ty, 0) * 4px), 0);
  transition: transform 460ms var(--g-ease);
}
.s-hero-art  { animation: sFloat 22s ease-in-out infinite; }
.s-hero-mark { animation: sDrift 31s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .s-hero-art, .s-hero-mark { animation: none; }
  .s-hero-art > picture, .s-hero-mark > picture { transform: none; transition: none; }
}

/* The wrapper is a full-width block sitting at z-index 1 over the art, so it
   was swallowing every click aimed at the helmet — the glass is narrow, but
   the box around it is not. It passes pointers through; only the panel inside
   it takes them. */
.s-hero-in { position: relative; z-index: 1; pointer-events: none; }
.s-hero-in > * { pointer-events: auto; }
/* and the art advertises itself as pressable, quietly */
.s-hero-art { cursor: pointer; }
/* THE PANEL TAKES THE WHOLE WRAP. At 46rem it was a card floating at the
   reading edge with the helmet stranded across a gap; full width, it is the
   surface the scene is built on and there is something under the helmet to
   overlap at every size.
   The text does not stretch with it. Each child keeps a reading measure and,
   having no auto margins, sits at the inline start — the right, in Arabic —
   so the far side of the panel stays clear for the helmet by construction
   rather than by a padding value that would need a different number at every
   breakpoint. */
.s-glass { padding: clamp(1.6rem, 4vw, 2.8rem); max-width: none; }
.s-hero .s-glass > * { max-width: min(46rem, 100%); }
@media (min-width: 1000px) {
  /* room for the helmet's leading edge, which crosses about a third of the
     panel — measured against the glass, so it tracks the same box the art is
     now anchored to */
  .s-hero .s-glass { padding-inline-end: clamp(3rem, 34%, 26rem); }
}

/* THE HEADLINE. Arabic at display size carrying the claim, English beneath
   carrying the name it is known by. Two weights, one colour each — the pair
   reads as a statement and its signature rather than as two headlines. */
.s-title { margin-bottom: .5rem; }
.s-title .ar {
  display: block; font-size: clamp(2rem, 6vw, 4rem); font-weight: 800;
  line-height: 1.25; color: #fff; text-wrap: balance;
}
.s-title .ar em {
  font-style: normal;
  background: var(--g-grad-flat); -webkit-background-clip: text;
  background-clip: text; color: transparent;
}
.s-title .en {
  display: block; margin-top: .5rem;
  /* Expo Arabic in the stack even though this line is always English: it is
     the signature the agency is known by and does not translate. The rule is
     still right — a stack without an Arabic face is one panel edit away from
     dropping Arabic to the device default, and nothing should depend on a
     field never being changed. */
  font-family: "Poppins", "Expo Arabic", system-ui, sans-serif;
  font-size: clamp(.9rem, 1.8vw, 1.15rem); font-weight: 500; letter-spacing: .01em;
  color: rgba(255, 255, 255, .62);
}
.s-hero .s-lede { color: rgba(255, 255, 255, .78); max-width: 44ch; }

.s-hero-cta { display: flex; gap: .7rem; flex-wrap: wrap; margin-top: 1.6rem; }

/* the proof, inside the glass — the same figures the panel counts up */
.s-proof {
  display: grid; grid-auto-flow: column; gap: clamp(1.2rem, 4vw, 2.6rem);
  margin-top: 1.8rem; padding-top: 1.4rem;
  border-top: 1px solid rgba(255, 255, 255, .16);
  justify-content: start;
}
.s-proof b { display: block; font-size: clamp(1.3rem, 3vw, 1.9rem); font-weight: 800; color: #fff; line-height: 1.1; }
.s-proof span { font-size: .76rem; color: rgba(255, 255, 255, .64); }
@media (max-width: 620px) {
  .s-hero-art { display: none; }        /* 600 KB of decoration on a phone */
  .s-proof { grid-auto-flow: row; gap: .8rem; }
}

/* ------------------------------------------------------------- 3. the wall */

/* Logos are evidence, and evidence is quiet. Grey at rest so the row reads as
   one texture; colour on approach, one at a time, so it reads as a list of
   names when you look at it. */
.s-logos {
  display: grid; gap: clamp(.6rem, 2vw, 1.2rem);
  grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
  align-items: center;
}
.s-logo {
  display: grid; place-items: center; padding: 1rem .8rem; min-height: 5.5rem;
  border: 1px solid var(--g-line); border-radius: var(--g-r-card);
  background: var(--g-surface); text-decoration: none;
  transition: border-color var(--g-t) var(--g-ease), transform var(--g-t) var(--g-ease-mag);
}
.s-logo:hover { border-color: var(--g-line-up); transform: translateY(-3px); }
.s-logo img {
  max-width: 100%; max-height: 3rem; width: auto; height: auto; display: block;
  filter: grayscale(1); opacity: .62;
  transition: filter var(--g-t) var(--g-ease), opacity var(--g-t) var(--g-ease);
}
.s-logo:hover img { filter: none; opacity: 1; }
/* a logo drawn on its own coloured plate must not be greyed — it is already
   a picture of a brand, and desaturating it just makes it look broken */
.s-logo.plate img { filter: none; opacity: .9; }

/* --------------------------------------------------------- 4. the services */

/* --s-cols is set by landing.js from the number of cards, so the grid always
   divides evenly and never ends on a half-empty row. The fallback of 3 is for
   the moment before the script runs. */
.s-serv {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(var(--s-cols, 3), minmax(0, 1fr));
}
@media (max-width: 1000px) { .s-serv { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .s-serv { grid-template-columns: 1fr; } }
.s-card {
  position: relative; padding: 1.6rem 1.5rem; border-radius: var(--g-r-card);
  background: var(--g-surface); border: 1px solid var(--g-line); box-shadow: var(--g-edge);
  transition: transform var(--g-t) var(--g-ease-mag), border-color var(--g-t) var(--g-ease);
}
.s-card:hover { transform: translateY(-4px); border-color: var(--g-line-up); }
.s-card h3 { font-size: 1.02rem; font-weight: 700; color: var(--g-title); margin-bottom: .45rem; }
.s-card p { font-size: .88rem; line-height: 1.85; color: var(--g-muted); }
.s-card .what {
  display: block; margin-top: .9rem; padding-top: .8rem;
  border-top: 1px solid var(--g-line);
  font-size: .76rem; color: var(--g-muted);
}
/* ONE accent card, and it is the one nobody else in the city offers. */
.s-card.hero { background: var(--g-grad); border-color: transparent; }
.s-card.hero h3, .s-card.hero p, .s-card.hero .what { color: #fff; }
.s-card.hero p { color: rgba(255, 255, 255, .88); }
.s-card.hero .what { color: rgba(255, 255, 255, .72); border-top-color: rgba(255, 255, 255, .28); }

/* ------------------------------------------------------------- 5. the work */

.s-work-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 1.2rem; flex-wrap: wrap; margin-bottom: 1.4rem; }

/* A field of work, packed rather than gridded: every piece is a different
   shape because every piece was made for a different place. */
/* SIX COLUMNS, FIXED. auto-fill with dense packing is what made this field
   ragged: the column count changed with the viewport, so the six-wide block
   the tiles are shaped for only fitted at some widths, and dense then shuffled
   late tiles into early gaps and opened new ones behind them. Six columns
   always, and the block always lands. */
.s-mosaic {
  display: grid; gap: .6rem;
  /* FOUR COLUMNS, NOT SIX. Six on a 1220px field is a 190px tile, and these
     are pieces of design — a poster at 190px is a swatch of colour. Four
     nearly doubles it, and the row height goes up with it so a square stays
     square. */
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: clamp(9rem, 15vw, 15rem);
  /* Variable shapes leave gaps; dense pulls later tiles back into them. What
     it cannot do is close the final row — that is the CTA band's job, and
     between the two the field has no holes and a straight edge. */
  grid-auto-flow: row dense;
}
.s-tile {
  position: relative; overflow: hidden; border-radius: var(--g-r-el);
  background: var(--g-sunk); border: 1px solid var(--g-line);
  display: block; text-decoration: none;
}
.s-tile.wide { grid-column: span 2; }
.s-tile.tall { grid-row: span 2; }
.s-tile.big  { grid-column: span 2; grid-row: span 2; }
.s-tile img, .s-tile video {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform var(--g-t-slow) var(--g-ease);
}
.s-tile:hover video { transform: scale(1.04); }
/* a tile that moves says so, quietly, in the corner it is not covering */
.s-tile.is-video::before {
  content: ""; position: absolute; inset-block-start: .5rem; inset-inline-start: .5rem;
  width: 22px; height: 22px; border-radius: 50%; z-index: 2;
  background: rgba(6,8,20,.55) url("data:image/svg+xml,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 24 24%27 fill=%27white%27%3E%3Cpath d=%27M8 5v14l11-7z%27/%3E%3C/svg%3E") center/11px no-repeat;
  backdrop-filter: blur(6px);
}
.s-tile:hover img { transform: scale(1.06); }
.s-tile .who {
  position: absolute; inset-inline-start: 0; inset-block-end: 0; z-index: 1;
  padding: .5rem .7rem; font-size: .74rem; font-weight: 700; color: #fff;
  background: linear-gradient(to top, rgba(6, 8, 20, .78), transparent);
  width: 100%; opacity: 0; transition: opacity var(--g-t) var(--g-ease);
}
.s-tile:hover .who, .s-tile:focus-visible .who { opacity: 1; }
/* Three columns on a tablet and two on a phone — both divide six, so the
   block still tiles, it just takes twice or three times as many rows. */
@media (max-width: 900px) {
  .s-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 11rem; }
}
@media (max-width: 560px) {
  .s-mosaic { grid-template-columns: repeat(2, minmax(0, 1fr)); grid-auto-rows: 9rem; }
  .s-tile.wide, .s-tile.big { grid-column: span 2; }
  .s-tile.big { grid-row: span 2; }
}

/* ------------------------------------------------------- 6. what a month is */

/* The same instrument the studio runs on. Showing a client the bar we price
   our own months with is a stronger claim than a paragraph about process. */
.s-month { display: grid; gap: 1.6rem; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); align-items: center; }
@media (max-width: 860px) { .s-month { grid-template-columns: 1fr; } }
.s-month-card { padding: clamp(1.4rem, 3vw, 2rem); }
.s-steps { list-style: none; margin: 1.2rem 0 0; padding: 0; display: grid; gap: .9rem; counter-reset: step; }
.s-steps li {
  display: grid; grid-template-columns: auto 1fr; gap: .8rem; align-items: baseline;
  font-size: .92rem; line-height: 1.8; color: var(--g-ink);
}
.s-steps li::before {
  counter-increment: step; content: counter(step);
  width: 1.7rem; height: 1.7rem; display: grid; place-items: center;
  border-radius: 50%; background: var(--g-sunk); border: 1px solid var(--g-line);
  font-size: .76rem; font-weight: 700; color: var(--g-muted);
}

/* ------------------------------------------------------------- 7. the reel */

.s-reel { position: relative; border-radius: var(--g-r-panel); overflow: hidden; border: 1px solid var(--g-line); }
.s-reel img, .s-reel iframe { width: 100%; display: block; aspect-ratio: 16 / 9; border: 0; }
.s-reel-play {
  position: absolute; inset: 0; display: grid; place-items: center; cursor: pointer;
  background: rgba(6, 8, 20, .28); border: 0;
}
.s-reel-play i {
  width: 74px; height: 74px; border-radius: 50%; display: grid; place-items: center;
  background: var(--g-grad); box-shadow: 0 12px 40px rgba(211, 29, 83, .42);
  transition: transform var(--g-t) var(--g-ease-mag);
}
.s-reel-play:hover i { transform: scale(1.08); }
.s-reel-play svg { width: 26px; height: 26px; fill: #fff; margin-inline-start: 4px; }

/* ------------------------------------------------------------ 8. the close */

.s-close { text-align: center; padding: clamp(2.5rem, 6vw, 4rem) clamp(1.2rem, 4vw, 3rem); }
.s-close h2 { font-size: clamp(1.8rem, 4.6vw, 3rem); font-weight: 800; color: #fff; line-height: 1.3; margin-bottom: .8rem; }
.s-close p { color: rgba(255, 255, 255, .82); max-width: 46ch; margin-inline: auto; line-height: 1.9; }
.s-close .s-hero-cta { justify-content: center; }

/* --------------------------------------------------------------- 9. buttons */

.s-btn {
  /* justify-content is load-bearing the moment a button is given a width.
     A shrink-to-fit button has no spare room, so flex-start and centre look
     identical and the missing line never shows. Give one a min-width — as
     .sv-w-go does — and the label snaps to the start edge, which on this
     RTL page is the RIGHT edge, and reads as a typo in the layout. */
  display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
  padding: .8rem 1.5rem; border-radius: var(--g-r-pill);
  font: inherit; font-size: .9rem; font-weight: 700; cursor: pointer;
  text-decoration: none; border: 1px solid transparent;
  transition: transform var(--g-t) var(--g-ease-mag), box-shadow var(--g-t) var(--g-ease);
}
.s-btn.primary { background: var(--g-grad); color: #fff; box-shadow: 0 8px 26px rgba(211, 29, 83, .32); }
.s-btn.ghost { border-color: rgba(255, 255, 255, .3); color: #fff; }
.s-btn:hover { transform: translateY(-2px); }
.s-btn.primary:hover { box-shadow: 0 12px 34px rgba(211, 29, 83, .42); }
/* on a light section the ghost button has to borrow the page's ink, not the
   hero's white */
.s-sec .s-btn.ghost { border-color: var(--g-line-up); color: var(--g-title); }

@media (prefers-reduced-motion: reduce) {
  .s-card, .s-logo, .s-btn, .s-tile img, .s-reel-play i { transition: none; }
  .s-card:hover, .s-logo:hover, .s-btn:hover { transform: none; }
  .s-tile:hover img { transform: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   10. DEEP SPACE — the landing page's own sky
   ═══════════════════════════════════════════════════════════════════════════

   THE PAGE COMMITS. Every other page on this site follows the reader's light
   or dark preference, and this one does not: an agency called Gravity, whose
   mark is a planet and whose hero is a helmet, cannot open on cream. So the
   landing page carries its own palette and ignores the toggle, the way a film
   poster ignores the colour of the wall it is pinned to. The toggle is hidden
   here rather than left to do nothing — a control that changes nothing is
   worse than one that is absent.

   THE PALETTE IS NOT "DARK MODE". Dark mode is grey with the lights off. This
   is a night sky: a near-black with a blue-violet bias, two nebula fields in
   the brand triad, and a star layer over both. The brand's orange-pink-purple
   sweep is doing the work it was drawn for — it IS a nebula — instead of being
   a button colour.
   ═══════════════════════════════════════════════════════════════════════════ */

body.space {
  /* the ground, and everything the design system draws on it */
  --g-ground:   #05060f;
  --g-surface:  rgba(19, 22, 46, .72);
  --g-raised:   #131630;
  --g-sunk:     rgba(10, 12, 28, .7);
  --g-line:     rgba(150, 165, 255, .14);
  --g-line-up:  rgba(160, 175, 255, .32);
  --g-ink:      #d9deff;
  --g-title:    #ffffff;
  --g-muted:    #8e97c9;

  background: var(--g-ground);
  color: var(--g-ink);
  /* the sky is fixed behind the page, so scrolling moves the content THROUGH
     space rather than dragging space along with it */
  position: relative;
}

/* ---- the two nebulae ----
   Enormous, soft, and slow. They are the only thing on the page that moves
   without being asked, and at 60s a cycle the movement is felt rather than
   seen — which is the difference between atmosphere and a screensaver. */
body.space::before {
  content: ""; position: fixed; inset: -20%; z-index: -2; pointer-events: none;
  background:
    radial-gradient(38% 32% at 74% 18%, rgba(211, 29, 83, .30), transparent 70%),
    radial-gradient(34% 30% at 22% 42%, rgba(70, 45, 147, .34), transparent 72%),
    radial-gradient(30% 26% at 62% 78%, rgba(239, 143, 32, .18), transparent 70%);
  filter: blur(30px);
  animation: neb 68s ease-in-out infinite alternate;
}
@keyframes neb {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-3%, 2%, 0) scale(1.1); }
}

/* the star canvas, painted by landing.js */
#sStars {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  width: 100%; height: 100%;
}

@media (prefers-reduced-motion: reduce) {
  body.space::before { animation: none; }
}

/* ---- surfaces in orbit ----
   A card on a night sky must not be an opaque box: the sky has to show through
   it or the page becomes a set of tiles with a picture behind them. Every
   surface here is translucent with a lit top edge, which is how something looks
   when the only light source is far away and above. */
body.space .s-card,
body.space .s-logo,
body.space .s-tile,
body.space .s-reel {
  background: var(--g-surface);
  backdrop-filter: blur(12px) saturate(1.1);
  border-color: var(--g-line);
}
body.space .s-card { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07), 0 18px 40px rgba(0, 0, 0, .38); }
body.space .s-card:hover { border-color: var(--g-line-up); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .12), 0 24px 60px rgba(0, 0, 0, .5); }

/* the header and footer join the sky rather than sitting on it */
body.space .site-header {
  background: rgba(5, 6, 15, .72);
  backdrop-filter: blur(16px) saturate(1.2);
  border-bottom: 1px solid var(--g-line);
}
body.space .site-header a, body.space .main-nav a { color: var(--g-ink); }
/* NAVY, AND OPAQUE. Left transparent, the footer showed the nebula bleeding
   through behind it — a warm brown haze under the wordmark that read as a
   rendering fault rather than as atmosphere. A solid navy closes the page:
   the sky ends and the company signs its name. */
body.space .site-footer {
  position: relative; z-index: 1;
  background: #0d1230;
  border-top: 1px solid var(--g-line);
  color: var(--g-muted);
}
html[data-theme="light"] body.space .site-footer { background: #101538; color: rgba(255,255,255,.66); }
html[data-theme="light"] body.space .site-footer a { color: rgba(255,255,255,.9); }
html[data-theme="light"] body.space .site-footer .footer-brand img { filter: brightness(0) invert(1); }
body.space .site-footer a { color: var(--g-ink); }

/* ---- DAYBREAK: the same sky, lit ----
   Hiding the theme control was the wrong answer. A landing page can commit to
   an art direction without refusing the reader a preference — so the page has
   a day as well as a night, and they are the same place at two hours rather
   than a dark design and a light one.

   What carries over: the nebulae, at a fraction of their strength, so the
   brand sweep is still what tints the ground. What does not: the stars. There
   is no such thing as a star on a white sky, and dark specks on pale blue read
   as dust on the screen — the canvas is simply hidden and the field goes back
   to being air. */
html[data-theme="light"] body.space {
  --g-ground:   #f2f3fa;
  --g-surface:  rgba(255, 255, 255, .78);
  --g-raised:   #ffffff;
  --g-sunk:     rgba(232, 235, 248, .8);
  --g-line:     rgba(38, 44, 92, .12);
  --g-line-up:  rgba(38, 44, 92, .26);
  --g-ink:      #2a2f52;
  --g-title:    #11142e;
  --g-muted:    #6b719c;
}
html[data-theme="light"] body.space::before {
  /* a wash, not a nebula: the same three lights at a quarter strength */
  background:
    radial-gradient(38% 32% at 74% 18%, rgba(211, 29, 83, .10), transparent 70%),
    radial-gradient(34% 30% at 22% 42%, rgba(70, 45, 147, .10), transparent 72%),
    radial-gradient(30% 26% at 62% 78%, rgba(239, 143, 32, .09), transparent 70%);
}
html[data-theme="light"] #sStars { display: none; }

/* The hero glass and both accent cards stay dark in either theme: they hold
   white type on the brand sweep, and that pairing does not have a light
   version — it has a lighter ground around it. */
html[data-theme="light"] body.space .s-hero { background: #0a0c1e; }
html[data-theme="light"] body.space .site-header {
  background: rgba(255, 255, 255, .8);
}
/* the mark goes back to its own colours where the ground is pale */
html[data-theme="light"] body.space .site-header .brand-logo,
html[data-theme="light"] body.space .site-footer .footer-brand img { filter: none; }

/* ---- 11. the figures, as instrument readouts ----
   «40+ عميل» in a plain bold face is a number in a sentence. On a flight deck
   it would be a lit readout, and that is what the hero is: monospace figures
   in the brand light, with the unit beneath in small caps. */
body.space .s-proof b {
  font-family: "Poppins", "Expo Arabic", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.6rem, 3.6vw, 2.4rem);
  letter-spacing: -.01em;
  background: linear-gradient(180deg, #fff 30%, #b9c2ff);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: 0 0 26px rgba(150, 165, 255, .28);
}
body.space .s-proof span { color: #9aa4d8; font-size: .72rem; letter-spacing: .02em; }
/* a hairline under each figure, lit at the near end — the readout's own rule */
body.space .s-proof > div { position: relative; padding-bottom: .3rem; }
body.space .s-proof > div::after {
  content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 1px;
  background: linear-gradient(to left, rgba(150, 165, 255, .55), transparent);
}

/* ---- 12. the accent card, on a night sky ----
   THE COLOUR BUG. A segmented bar draws its parts in the brand triad, and the
   accent card IS the brand triad — so the month card was a gradient bar on a
   gradient ground and every division in it disappeared. The card keeps the
   gradient as an EDGE and puts the bar on the dark, where its colours have
   something to be different from. */
body.space .s-month-card {
  background: linear-gradient(160deg, rgba(239, 143, 32, .16), rgba(211, 29, 83, .16) 52%, rgba(70, 45, 147, .22));
  border: 1px solid rgba(211, 29, 83, .38);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .04) inset, 0 24px 70px rgba(211, 29, 83, .16);
}
/* the bar sits in its own well, so the segments read against a neutral */
body.space .s-month-card .g-seg {
  background: rgba(5, 6, 15, .55);
  padding: .35rem; border-radius: var(--g-r-pill);
}

/* The closing card keeps the full gradient: it holds nothing but type, and
   type on the sweep is exactly what the sweep is good at. */
body.space .s-close { box-shadow: 0 30px 90px rgba(211, 29, 83, .28); }

/* ---- 13. the service cards do something ----
   They were six boxes of text. Each is a door now: the whole card is the link,
   an arrow says so, and the corner lights up in the brand as the pointer
   approaches — the same "attracted" idea the rest of the page is built on. */
.s-card {
  display: block; text-decoration: none; color: inherit;
  overflow: hidden; isolation: isolate;
}
.s-card .go {
  display: inline-flex; align-items: center; gap: .35rem;
  margin-top: .9rem; font-size: .78rem; font-weight: 700;
  color: var(--g-title); opacity: .55;
  transition: opacity var(--g-t) var(--g-ease), transform var(--g-t) var(--g-ease-mag);
}
.s-card .go svg { width: 14px; height: 14px; }
.s-card:hover .go { opacity: 1; transform: translateX(var(--g-arrow, -4px)); }
:is([lang="ar"], [dir="rtl"]) .s-card { --g-arrow: -4px; }
:is([lang="en"], [dir="ltr"]) .s-card { --g-arrow: 4px; }

/* the corner light: a soft brand glow that only exists on approach */
body.space .s-card::after {
  content: ""; position: absolute; z-index: -1;
  inset-inline-start: -30%; top: -60%; width: 70%; padding-top: 70%;
  border-radius: 50%; background: radial-gradient(circle, rgba(211, 29, 83, .5), transparent 68%);
  opacity: 0; transition: opacity var(--g-t-slow) var(--g-ease);
}
body.space .s-card:hover::after { opacity: 1; }
body.space .s-card.hero::after { display: none; }   /* it is already the light */
/* THE ACCENT CARD KEEPS ITS GRADIENT ON THE SKY. `body.space .s-card` sets a
   translucent surface at specificity 0,2,1 and `.s-card.hero` sets the sweep at
   0,2,0 — so the one card on the section that is supposed to be loud came out
   the same colour as the five quiet ones. Same weight, and it wins again. */
body.space .s-card.hero {
  background: var(--g-grad);
  border-color: transparent;
  backdrop-filter: none;
  box-shadow: 0 20px 60px rgba(211, 29, 83, .3);
}

/* ---- 14. the staff door ----
   The old hero hid the way in behind the helmet, which is charming and
   undiscoverable — and the address it hid (/boss) still works whether or not
   anything on the page points at it. This is a mark in the footer that lights
   up only when looked for: a person who knows it is there finds it in a
   second, and nobody else ever notices a dot. */
.s-door {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  background: var(--g-muted); opacity: .22; margin-inline-start: .6rem;
  vertical-align: middle; transition: opacity var(--g-t) var(--g-ease), box-shadow var(--g-t) var(--g-ease);
}
.s-door:hover, .s-door:focus-visible {
  opacity: 1; background: var(--g-pink, #d31d53);
  box-shadow: 0 0 0 4px rgba(211, 29, 83, .22);
}

/* ---- 15. the CTA that ends the work ----
   The mosaic ends wherever its last row runs out, and the hole it left was the
   largest empty thing on the page — sitting directly under the work somebody
   has just been looking at, which is the moment they are most likely to want
   to talk. It takes the brand sweep because it is the one thing in that field
   that is not a photograph. */
/* A TILE AMONG THE WORK, not a band under it. At the foot it was arrived at
   after everything, which is a footer; the same size as its neighbours and
   placed eight in, it is part of the field somebody is already looking at. */
.s-cta {
  background: var(--g-grad) !important;
  border-color: transparent !important;
  display: grid; place-items: center; text-align: center;
  padding: clamp(1.6rem, 4vw, 2.6rem); overflow: hidden;
}
.s-cta-in { max-width: 46ch; }
.s-cta-in { display: grid; gap: .4rem; }
.s-cta b { font-size: clamp(1.2rem, 2.6vw, 1.9rem); font-weight: 800; color: #fff; line-height: 1.25; }
.s-cta span { font-size: .82rem; line-height: 1.7; color: rgba(255, 255, 255, .86); max-width: 26ch; }
.s-cta:hover { transform: translateY(-3px); }
body.space .s-cta { backdrop-filter: none; box-shadow: 0 22px 60px rgba(211, 29, 83, .32); }

/* ---- 16. the mark, on a night sky ----
   The logo is drawn for a light ground: the droplet is a gradient and the
   wordmark beside it is near-black, which on this page is invisible.
   brightness(0) crushes the whole lockup to a silhouette and invert(1) lifts
   that to white — one flat white mark, rather than a second logo file to keep
   in step with the first. */
body.space .site-header .brand-logo,
body.space .site-footer .footer-brand img {
  filter: brightness(0) invert(1);
}

/* the ampersand in the closing lockup: the join, not a word */
.s-amp {
  font-style: normal; font-weight: 400;
  font-family: "Poppins", "Expo Arabic", system-ui, sans-serif;
  opacity: .72; padding-inline: .15em;
}
/* THE CTA BACKFILLS THE HOLE, IT DOES NOT CLOSE THE ROW.
   Stretching the last child to the end edge — the rule the panel's overview
   uses — is wrong here, and it is wrong in a way that is worth writing down:
   the mosaic is `grid-auto-flow: dense`, so a late item is meant to be pulled
   BACK into the first gap it fits. Pinning its end column forbids exactly
   that, which left the door parked on a fresh row at the bottom while the gap
   it was written to fill sat empty three rows above it. Let dense pack it. */

/* ---- 17. the work turns to face you ----
   Tipped back and lifted at rest, upright when it arrives. perspective on the
   grid rather than on each tile, so the whole field shares one vanishing point
   and the row reads as one surface turning rather than twelve separate cards.

   `visibility: hidden` and not opacity alone: a tile that is transparent is
   still a link, and a page of invisible links above the fold is a page where
   clicking empty space navigates somewhere. */
.s-mosaic { perspective: 1200px; }
.s-tile {
  opacity: 0; visibility: hidden;
  transform: rotateX(-14deg) translateY(18px);
  transform-origin: 50% 100%;
  transition: opacity 520ms var(--g-ease), transform 620ms var(--g-ease-mag),
              visibility 0s linear 520ms;
}
.s-tile.in {
  opacity: 1; visibility: visible;
  transform: none;
  transition: opacity 520ms var(--g-ease), transform 620ms var(--g-ease-mag),
              visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .s-tile { opacity: 1; visibility: visible; transform: none; transition: none; }
}

/* ---- and the turn is driven by the SCROLL, not by a trigger ----
   The version above fires once when a tile crosses a threshold and then plays
   on its own clock: scroll fast and the whole grid animates after you have
   gone past it; scroll back up and nothing happens at all, because it already
   fired. The turn belongs to the scroll — a tile should be mid-flip while it
   is mid-screen, and follow the wheel backwards if you scroll back.

   `animation-timeline: view()` gives exactly that natively: the animation is
   scrubbed by the element's own progress through the viewport, off the main
   thread, with no listener and no layout reads. Where it is missing the block
   simply does not apply and the IntersectionObserver reveal above remains —
   which is why that code stays rather than being replaced.

   `entry 5% cover 32%` is the range: the turn starts as the tile clears the
   bottom edge and is finished by the time it is a third of the way up, so the
   work is flat and readable in the part of the screen anybody is looking at,
   and only the incoming row is still turning. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .s-tile {
      /* THE RESTING STATE IS VISIBLE, AND THE ANIMATION ONLY ADDS THE TURN.
         Animating opacity from 0 here would mean that a timeline which never
         advances — a browser that reports support but does not scrub, a tile
         in a scroller it does not recognise — leaves the from-state stuck and
         the entire portfolio invisible. The decoration must never hold the
         only copy of the content. Flat and opaque is a missing effect; blank
         is a missing page. */
      opacity: 1;
      visibility: visible;
      transition: none;
      animation: sFlip linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 32%;
    }
    /* The CTA turns with the rest — it is a tile in the field, and a door that
       appears fully formed while the work around it moves reads as an advert
       dropped on top of a portfolio. */
    @keyframes sFlip {
      from { transform: rotateX(-20deg) translateY(30px) scale(.95); }
      to   { transform: rotateX(0) translateY(0) scale(1); }
    }
  }
}

/* the door acknowledges a tap — a half-second breath, nothing a visitor would
   read as an invitation */
/* The pulse runs ALONGSIDE the float rather than replacing it — a single
   animation property would drop the drift for the length of the tap and the
   helmet would visibly jump back to its resting position. */
.s-hero-art.tap { animation: sTap 420ms var(--g-ease), sFloat 22s ease-in-out infinite; }
@keyframes sTap {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.015); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) { .s-hero-art.tap { animation: none; } }

/* ═══════════════════════════════════════════════════════════════════════════
   18. NOTHING ON THE BRAND GRADIENT IS EVER NAVY
   ═══════════════════════════════════════════════════════════════════════════

   The gradient is the same in both themes — it is the mark, and it does not
   flip. The tokens on top of it DO: in daylight `--g-title` is #11142e and
   `--g-ink` is #2c3357, so every element that took its colour from a token
   and happened to sit on the sweep turned navy on orange. The header CTA, the
   activations card, the figures in the month card, the ghost button in the
   closing panel — four different components, one cause.

   So it is stated once, structurally: a gradient ground sets white on
   everything inside it. Listed by ground rather than by component, because the
   next thing dropped onto a gradient should inherit the fix rather than
   rediscover the bug. */
.s-card.hero,
.s-card.hero *,
.s-cta,
.s-cta *,
.g-accent,
.g-accent *,
.btn-gradient,
.btn-gradient * {
  color: #fff;
}
/* the secondary tiers keep their hierarchy — white at two weights, not one */
.s-card.hero p,
.s-cta span,
.g-accent .sub,
.s-close p { color: rgba(255, 255, 255, .88); }
.s-card.hero .what,
.g-accent .ov-spark-cap { color: rgba(255, 255, 255, .72); }

/* A ghost button on the gradient borrows the sweep's white, not the page's
   ink — `.s-sec .s-btn.ghost` sets the page ink and it wins on a section
   inside one, which is how WhatsApp came out navy on orange. */
.g-accent .s-btn.ghost,
.s-sec .g-accent .s-btn.ghost {
  border-color: rgba(255, 255, 255, .5);
  color: #fff;
}
/* and the gradient-filled button on a gradient ground inverts instead, or it
   disappears into what it is sitting on */
.g-accent .s-btn.primary { background: #fff; color: var(--g-pink, #d31d53); box-shadow: none; }
.g-accent .s-btn.primary:hover { box-shadow: 0 10px 30px rgba(0, 0, 0, .22); }

/* Two that needed naming rather than a wildcard.
   `body.space .site-header a` is three classes deep and set the page ink on
   every link in the bar, including the gradient CTA sitting in it — so the one
   button that is a gradient took the colour meant for the plain links beside
   it. And the segmented bar's key sets its own muted grey, which is right on a
   card and wrong on the sweep. Both are stated where they actually apply. */
body.space .site-header .btn-gradient,
body.space .site-header .btn-gradient * { color: #fff; }

.g-accent .g-seg-key span,
.g-accent .g-seg-key b,
.s-card.hero .g-seg-key span,
.s-cta .g-seg-key span { color: rgba(255, 255, 255, .88); }
.g-accent .g-seg-key b { color: #fff; }

/* ═══════════════════════════════════════════════════════════════════════════
   19. THE TURN, RAGGED — and only where it belongs
   ═══════════════════════════════════════════════════════════════════════════

   WHY NOT EVERY SECTION. Because if everything turns, nothing does: the effect
   stops being a device that draws the eye and becomes the page's default
   behaviour, which is the same as no behaviour. And prose that rotates while
   somebody is reading it is not atmosphere, it is a moving target. So it runs
   on FIELDS OF OBJECTS — the work, the logo wall, the service cards — where
   the thing turning is a thing rather than a sentence. Headings, ledes and
   body copy stay still.

   WHY RAGGED. On one shared range every tile in a row crosses the trigger at
   the same instant and the row lands as a slab, which reads as one object
   hinging rather than a field of separate pieces. Five buckets on a prime
   cycle give each tile its own start and its own distance, so neighbours are
   never quite in step and the pattern does not visibly repeat down the page.

   The values differ in BOTH numbers on purpose: a different start alone still
   turns everything at the same speed, and speed is most of what the eye reads
   as separateness. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* the fields that turn */
    .s-logo, .s-card {
      animation: sFlip linear both;
      animation-timeline: view();
      animation-range: entry 4% cover 30%;
    }

    /* five buckets, and 5 does not divide the 4-column grid, so the pattern
       walks across the rows instead of lining up in columns */
    .s-tile:nth-child(5n+1), .s-logo:nth-child(5n+1), .s-card:nth-child(5n+1) {
      animation-range: entry 2%  cover 26%;
    }
    .s-tile:nth-child(5n+2), .s-logo:nth-child(5n+2), .s-card:nth-child(5n+2) {
      animation-range: entry 9%  cover 38%;
    }
    .s-tile:nth-child(5n+3), .s-logo:nth-child(5n+3), .s-card:nth-child(5n+3) {
      animation-range: entry 0%  cover 33%;
    }
    .s-tile:nth-child(5n+4), .s-logo:nth-child(5n+4), .s-card:nth-child(5n+4) {
      animation-range: entry 13% cover 29%;
    }
    .s-tile:nth-child(5n+5), .s-logo:nth-child(5n+5), .s-card:nth-child(5n+5) {
      animation-range: entry 6%  cover 42%;
    }

    /* A logo is a small flat mark and a card is a block of type: both look
       wrong tipped as far as a photograph, which has depth to sell the angle.
       Same idea, less of it. */
    .s-logos { perspective: 1000px; }
    .s-serv  { perspective: 1400px; }
    .s-logo, .s-card { transform-origin: 50% 100%; }
    @keyframes sFlipSmall {
      from { transform: rotateX(-11deg) translateY(18px) scale(.97); }
      to   { transform: rotateX(0) translateY(0) scale(1); }
    }
    .s-logo, .s-card { animation-name: sFlipSmall; }
  }
}

/* --------------------------------------------- 20. the glow behind the helmet

   Two separate faults, and the second was hiding behind the first.

   THE BLUE. Five taps on one element is, to a browser, a triple-click and
   then some — so it selected. What was showing was the selection highlight,
   the one thing on the page whose colour the site does not choose. Nothing
   was wrong with the glow; it was underneath a blue rectangle.

   THE GLOW ITSELF. .g-aurora::after paints at z-index -1, which in the panel
   is right — there it lights a band with nothing in front of it. Here the
   scene has three planes stacked on an opaque #0a0c1e ground, so a light at
   the very back was behind the mark, the glass AND the helmet: lit, painting,
   and invisible. Moving it to z-index 1 puts it above the mark and the panel
   and below the helmet, which is what "behind the helmet" actually means —
   light spilling around a silhouette rather than a lamp in front of it.

   And it is scaled to the scene. 300px is a pointer-sized circle; against a
   520px helmet it read as a smudge near the chin rather than as the thing
   being lit from behind. */
.s-hero-art, .s-hero-mark, .s-hero .s-title { user-select: none; -webkit-user-select: none; }
.s-hero-art { -webkit-tap-highlight-color: transparent; }

.s-hero.g-aurora::after {
  z-index: 1;                       /* over the mark and the glass, under the helmet */
  background: radial-gradient(clamp(20rem, 34vw, 34rem) circle at var(--gx) var(--gy),
              color-mix(in srgb, var(--g-pink) 46%, transparent),
              color-mix(in srgb, var(--g-purple) 20%, transparent) 42%,
              transparent 72%);
  /* Screen, so it adds light to whatever it crosses instead of tinting it —
     a wash at 46% over the glass would otherwise just make the panel pink. */
  mix-blend-mode: screen;
}
/* The hero is the one place the glow must survive a touch device: on a phone
   it is not chasing a finger, it is the answer to a deliberate tap. The DS
   rule that hides it there is right everywhere else. */
@media (hover: none) {
  .s-hero.g-aurora::after { display: block; }
}
@media (prefers-reduced-motion: reduce) {
  .s-hero.g-aurora::after { display: none; }
}

/* ------------------------------------------------- 21. the services stage

   The six cards are gone. A rail names the services and a stage proves them,
   and every pixel of the proof is real work out of content.json — the same
   galleries, the same client copy, the same figures. A hand-made showcase is
   stale the week after it ships; this one is only ever as old as the panel.

   The rail is the fixed column and the stage takes the rest, because the
   stage is the argument and the rail is only the index to it. */
.s-stage {
  display: grid; grid-template-columns: minmax(13rem, 17rem) 1fr;
  gap: clamp(1rem, 2.5vw, 2rem); margin-top: 1.8rem; align-items: stretch;
}
.s-rail { display: flex; flex-direction: column; gap: .4rem; }
.s-rail-b {
  display: block; width: 100%; text-align: start; cursor: pointer;
  padding: .85rem 1rem; border-radius: var(--g-r-panel);
  background: transparent; border: 1px solid transparent; color: inherit;
  font: inherit; transition: background var(--g-t) var(--g-ease),
                            border-color var(--g-t) var(--g-ease);
}
.s-rail-b b { display: block; font-weight: 700; font-size: 1rem; line-height: 1.4; }
.s-rail-b span {
  display: block; margin-top: .15rem; font-size: .78rem; color: var(--g-muted);
  /* the hint only belongs to the one you are on — six of them at once is the
     wall of text this section was replaced for */
  max-height: 0; opacity: 0; overflow: hidden;
  transition: max-height var(--g-t) var(--g-ease), opacity var(--g-t) var(--g-ease);
}
.s-rail-b:hover { background: var(--g-sunk); }
.s-rail-b.on { background: var(--g-sunk); border-color: var(--g-line); }
.s-rail-b.on span { max-height: 3rem; opacity: 1; }
/* the one loud mark on the rail: a brand edge on the active service */
.s-rail-b.on { border-inline-start: 3px solid transparent; border-image: var(--g-grad) 1; }
.s-rail-b:focus-visible { outline: 2px solid var(--g-pink); outline-offset: 2px; }

.s-show { position: relative; min-height: clamp(20rem, 42vw, 27rem); }
.s-pane {
  position: absolute; inset: 0; display: grid; place-items: center;
  padding: clamp(1rem, 2.5vw, 1.8rem);
  border: 1px solid var(--g-line); border-radius: var(--g-r-card);
  background: var(--g-surface); overflow: hidden;
  opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity var(--g-t) var(--g-ease), transform var(--g-t) var(--g-ease),
              visibility 0s linear var(--g-t);
}
.s-pane.on { opacity: 1; visibility: visible; transform: none; transition-delay: 0s; }

/* ---- the month: a content calendar, which is the actual deliverable ---- */
.sv-month { width: 100%; max-width: 32rem; }
.sv-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: .3rem; margin-bottom: .4rem; }
.sv-days b { text-align: center; font-size: .68rem; font-weight: 600; color: var(--g-muted); }
.sv-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: .3rem; }
.sv-grid i {
  display: block; aspect-ratio: 1; border-radius: 6px; overflow: hidden;
  background: var(--g-sunk); border: 1px solid var(--g-line);
}
.sv-grid i.on { border-color: color-mix(in srgb, var(--c) 55%, transparent); }
.sv-grid i img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sv-grid picture { display: block; width: 100%; height: 100%; }

/* ---- the film strip ---- */
.sv-film {
  width: 100%; overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.sv-film.one video { width: 100%; max-height: 22rem; object-fit: cover; border-radius: var(--g-r-panel); display: block; }
.sv-strip { display: flex; gap: .6rem; width: max-content; animation: svRoll 34s linear infinite; }
.sv-strip span { display: block; width: clamp(11rem, 20vw, 17rem); flex: none; }
.sv-strip img { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: var(--g-r-el); display: block; }
@keyframes svRoll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.s-pane:not(.on) .sv-strip { animation-play-state: paused; }

/* ---- the stack: real posters, flicked through ---- */
.sv-stack { position: relative; width: min(19rem, 68%); aspect-ratio: 4/5; cursor: pointer; }
.sv-stack:focus-visible { outline: 2px solid var(--g-pink); outline-offset: 6px; border-radius: var(--g-r-panel); }
.sv-stack figure {
  position: absolute; inset: 0; margin: 0; border-radius: var(--g-r-panel); overflow: hidden;
  background: var(--g-sunk); box-shadow: var(--g-float);
  transform: translate(calc(var(--i) * 1.4%), calc(var(--i) * -1.8%)) rotate(calc(var(--i) * -1.1deg));
  transition: transform 380ms var(--g-ease), opacity 380ms var(--g-ease);
}
.sv-stack figure:first-child { z-index: 6; }
.sv-stack figure:nth-child(2) { z-index: 5; }
.sv-stack figure:nth-child(3) { z-index: 4; }
.sv-stack figure:nth-child(4) { z-index: 3; }
.sv-stack figure:nth-child(5) { z-index: 2; }
.sv-stack figure.out { transform: translateX(-42%) rotate(-13deg); opacity: 0; }
.sv-stack img, .sv-stack picture { width: 100%; height: 100%; object-fit: cover; display: block; }
.sv-stack .cw {
  position: absolute; inset-block-end: 0; inset-inline: 0; z-index: 2;
  padding: 1.6rem .8rem .6rem; font-size: .76rem; color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, .72));
}

/* ---- the copy, typed at reading speed ---- */
.sv-copy { width: 100%; max-width: 34rem; }
.sv-who { display: flex; align-items: center; gap: .7rem; margin-bottom: .9rem; }
.sv-who img { width: 2.4rem; height: 2.4rem; object-fit: contain; border-radius: 8px; }
.sv-who b { font-size: .95rem; }
.sv-who span { font-size: .76rem; color: var(--g-muted); }
.sv-type {
  min-height: 7.5rem; line-height: 1.95; font-size: .95rem;
  border-inline-start: 3px solid var(--c, var(--g-pink)); padding-inline-start: .9rem;
}
/* the caret only exists while it is writing */
.sv-type.go::after {
  content: ""; display: inline-block; width: .55em; height: 1.05em;
  vertical-align: -.16em; margin-inline-start: .1em;
  background: var(--c, var(--g-pink)); animation: svCaret .9s steps(1) infinite;
}
@keyframes svCaret { 50% { opacity: 0; } }
.sv-alts { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: 1rem; }
.sv-alts button {
  padding: .35rem .75rem; border-radius: 999px; cursor: pointer; font: inherit;
  font-size: .76rem; background: var(--g-sunk);
  border: 1px solid var(--g-line); color: var(--g-muted);
}
.sv-alts button:hover { color: var(--g-ink); border-color: var(--g-pink); }

/* ---- the figures ---- */
.sv-figs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
  gap: clamp(1rem, 3vw, 2.4rem); width: 100%;
}
.sv-figs div { text-align: center; }
.sv-figs b {
  display: block; font-size: clamp(2rem, 5vw, 3.2rem); font-weight: 800; line-height: 1;
  background: var(--g-grad-flat); -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums;
}
.sv-figs span { display: block; margin-top: .5rem; font-size: .8rem; color: var(--g-muted); }

/* ---- the wheel: the one pane that does something ---- */
.sv-wheel { display: grid; justify-items: center; gap: .9rem; position: relative; }
.sv-w-face {
  position: relative; width: clamp(11rem, 24vw, 15rem); aspect-ratio: 1;
  border-radius: 50%; overflow: hidden; border: 3px solid var(--g-line);
  transition: transform 3.2s cubic-bezier(.16, .84, .28, 1);
}
.sv-w-face i {
  position: absolute; inset: 0; background: var(--c);
  /* one wedge, cut by its own angle — no images, no canvas */
  clip-path: polygon(50% 50%, 50% 0,
    calc(50% + 50% * sin(var(--seg))) calc(50% - 50% * cos(var(--seg))));
  transform: rotate(var(--a));
}
.sv-w-hub {
  position: absolute; inset: 38%; border-radius: 50%; z-index: 2;
  background: var(--g-surface); border: 2px solid var(--g-line);
}
.sv-w-pin {
  position: absolute; top: -4px; inset-inline-start: 50%; translate: -50% 0; z-index: 3;
  width: 0; height: 0; border-inline: 9px solid transparent; border-top: 16px solid var(--g-pink);
}
.sv-w-out { font-size: .8rem; color: var(--g-muted); text-align: center; max-width: 26rem; min-height: 2.4em; }

@media (prefers-reduced-motion: reduce) {
  .sv-strip { animation: none; }
  .sv-w-face { transition: none; }
  .sv-type.go::after { animation: none; }
}
@media (max-width: 820px) {
  .s-stage { grid-template-columns: 1fr; }
  /* the rail becomes a scroller across the top, which is the same index in
     the shape a thumb can use */
  .s-rail { flex-direction: row; overflow-x: auto; gap: .3rem; padding-bottom: .3rem; }
  .s-rail-b { width: auto; flex: none; white-space: nowrap; padding: .6rem .9rem; }
  .s-rail-b span { display: none; }
  .s-show { min-height: 24rem; }
}

/* ------------------------------- 21b. the stage, off the card and awake

   NO RECTANGLE. The panes sat on var(--g-surface) with a border, which put a
   white card in the middle of a page whose whole argument is that the ground
   is deep space. A card is what you reach for when the content needs
   containing; this content is photographs, a calendar and a wheel, all of
   which carry their own edges. Removing it lets the work sit on the page
   instead of on a tray.

   AND IT PERFORMS. A pane that is simply present when you arrive is a
   picture. Each one now plays itself in on arrival — the month fills day by
   day, the posters land, the figures count — so choosing a service is
   answered by something happening rather than by something appearing. */
.s-pane {
  background: none; border: 0; border-radius: 0; padding: 0;
  align-content: center;
}
.s-show { min-height: clamp(21rem, 40vw, 26rem); }

/* the rail loses its filled state too — the active service is marked by the
   brand edge and by weight, which is enough, and a filled chip in a column of
   empty ones was the second rectangle on the section */
.s-rail-b, .s-rail-b:hover, .s-rail-b.on { background: none; }
.s-rail-b { border: 0; border-inline-start: 2px solid var(--g-line); border-radius: 0; opacity: .55; }
.s-rail-b:hover { opacity: .85; }
.s-rail-b.on {
  opacity: 1; border-inline-start: 3px solid transparent; border-image: var(--g-grad) 1;
}
.s-rail-b b { transition: letter-spacing var(--g-t) var(--g-ease); }
.s-rail-b.on b { letter-spacing: .01em; }

/* ---- arrival: each pane plays itself in ---- */
@media (prefers-reduced-motion: no-preference) {
  /* the month fills day by day, in reading order */
  .s-pane.on .sv-grid i { animation: svPop 420ms var(--g-ease) backwards; animation-delay: calc(var(--d, 0) * 26ms); }
  /* the posters land on the stack */
  .s-pane.on .sv-stack figure { animation: svLand 520ms var(--g-ease) backwards; animation-delay: calc(var(--i) * 70ms); }
  /* the figures arrive one after another rather than all at once */
  .s-pane.on .sv-figs div { animation: svRise 480ms var(--g-ease) backwards; animation-delay: calc(var(--d, 0) * 90ms); }
  .s-pane.on .sv-who { animation: svRise 420ms var(--g-ease) backwards; }
  .s-pane.on .sv-w-box { animation: svRise 480ms var(--g-ease) backwards; }
}
@keyframes svPop  { from { opacity: 0; transform: scale(.82); } to { opacity: 1; transform: none; } }
@keyframes svLand { from { opacity: 0; transform: translateY(-14px) rotate(4deg); } }
@keyframes svRise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }

/* the month grid gains its own hairlines back, since the card is gone */
.sv-grid i { background: color-mix(in srgb, var(--g-ink) 5%, transparent); border-color: var(--g-line); }
.sv-month { max-width: 34rem; }

/* the film strip needs no mask against a card edge that no longer exists —
   it fades into the page itself */
.sv-film { mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }

/* ---- the wheel, redrawn ---- */
.sv-wheel { display: grid; justify-items: center; gap: 1.1rem; }
.sv-w-box { position: relative; width: clamp(13rem, 26vw, 18rem); aspect-ratio: 1; }
.sv-w-face {
  width: 100%; height: 100%; display: block; border-radius: 50%;
  filter: drop-shadow(0 12px 30px rgba(0, 0, 0, .35));
  transition: transform 3.4s cubic-bezier(.13, .78, .24, 1);
}
.sv-w-face text {
  font-size: 9px; font-weight: 700; fill: #fff;
  font-family: "Expo Arabic", system-ui, sans-serif;
}
.sv-w-face text.lose { fill: rgba(255, 255, 255, .5); font-weight: 500; }
.sv-w-hub {
  position: absolute; inset: 39%; border-radius: 50%; z-index: 2;
  background: var(--g-grad-flat);
  box-shadow: 0 0 0 5px var(--g-ground), 0 6px 18px rgba(0, 0, 0, .45);
}
/* the pointer sits ON the rim, pointing in — outside it, it read as an arrow
   near a circle rather than as the thing the wheel stops against */
.sv-w-pin {
  position: absolute; top: -2%; inset-inline-start: 50%; translate: -50% 0; z-index: 3;
  width: 0; height: 0; border-inline: 11px solid transparent;
  border-top: 22px solid #fff; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .5));
}
.sv-w-go { min-width: 9rem; }
.sv-w-out {
  font-size: .8rem; color: var(--g-muted); text-align: center; max-width: 30rem;
  min-height: 3.6em; opacity: 0; transition: opacity var(--g-t-slow) var(--g-ease);
}
.sv-w-out.show { opacity: 1; }
.sv-w-out b {
  display: block; font-size: 1.15rem; margin-bottom: .3rem;
  background: var(--g-grad-flat); -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* --------------------------------------------- 22. anchors clear the header

   THE NAV LANDED 81 PIXELS TOO HIGH ON EVERY LINK. The header is fixed and
   the sections had no scroll-margin, so an anchor scrolled the section's top
   edge to the top of the WINDOW — which is underneath the header. What you
   lost was the first 81px of the section: its label and the top of its
   heading. On «أعمالنا بالحركة», where those two lines are most of what is
   above the video, that reads as a link that goes nowhere.

   Derived from the header rather than typed, so moving the bar moves the
   landing point with it, and with a little air underneath so the section
   arrives with a margin rather than wedged against the chrome. */
:root { --s-head: 81px; }
.s-sec[id], #s-hero[id], section[id] { scroll-margin-top: calc(var(--s-head) + 1.2rem); }

/* --------------------------------------- 23. the stage reads on both grounds

   IN DARK MODE THE CALENDAR STOPPED BEING A CALENDAR. The empty cells were
   `color-mix(--g-text 5%)`, and --g-text on deep space is near-white — five
   percent of white over #0b0e1c is nothing at all. So the wells vanished, the
   hairlines went with them, and what was left was a scatter of thumbnails
   floating in space with seven letters hovering above them. The grid was
   doing all the work of a calendar and showing none of it.

   The wells are now declared per ground rather than mixed from the text
   colour: a lift on dark, a recess on light. Both are the same idea — an
   empty day is a place where something could go — and neither borrows a value
   from a token that means something else. */
.s-stage {
  --sv-well: rgba(15, 18, 42, .055);
  --sv-line: rgba(15, 18, 42, .12);
  --sv-off:  rgba(15, 18, 42, .02);
}
:root:not([data-theme="light"]) .s-stage {
  --sv-well: rgba(255, 255, 255, .055);
  --sv-line: rgba(255, 255, 255, .12);
  --sv-off:  rgba(255, 255, 255, .022);
}
:root[data-theme="dark"] .s-stage {
  --sv-well: rgba(255, 255, 255, .055);
  --sv-line: rgba(255, 255, 255, .12);
  --sv-off:  rgba(255, 255, 255, .022);
}
/* the landing page paints its own deep ground in BOTH themes, so the section
   is dark even when the OS says light — the light values above are for the
   panel, where this stylesheet is not loaded, and this keeps the page honest
   about the ground it actually has */
body.space .s-stage {
  --sv-well: rgba(255, 255, 255, .055);
  --sv-line: rgba(255, 255, 255, .12);
  --sv-off:  rgba(255, 255, 255, .022);
}

.sv-grid i { background: var(--sv-well); border: 1px solid var(--sv-line); }
/* Friday, and the tail of last month: present, but plainly not a working day.
   A calendar with no quiet days in it is a grid. */
.sv-grid i.off { background: var(--sv-off); border-style: dashed; border-color: var(--sv-off); }
.sv-grid i.pad { background: none; border-color: transparent; }
.sv-grid i.on  { border-color: color-mix(in srgb, var(--c) 60%, transparent); background: none; }

/* the weekday row belongs to the grid, not above it */
.sv-days {
  border-bottom: 1px solid var(--sv-line);
  padding-bottom: .45rem; margin-bottom: .5rem;
}
.sv-days b { opacity: .8; letter-spacing: .04em; }
.sv-cap {
  margin-top: .7rem; font-size: .76rem; color: var(--g-muted); text-align: center;
  font-variant-numeric: tabular-nums;
}

/* the pane fills the stage rather than floating in the top third of it */
.s-pane { align-content: center; justify-items: center; }
.s-show { display: grid; }

/* the film strip and the stack pick up the same hairline, so nothing on this
   section is outlined in a colour that only exists on one ground */
.sv-strip img, .sv-stack figure { border: 1px solid var(--sv-line); }
.sv-alts button { background: var(--sv-well); border-color: var(--sv-line); }
.s-rail-b { border-inline-start-color: var(--sv-line); }
.sv-w-face { border-color: var(--sv-line); }

/* ------------------------------------------ 24. the stage sits on the glass

   BIGGER, AND ON A PANE. The demonstrations were floating on the section at
   the size the six cards used to be, which made them read as illustrations of
   the services rather than as the services. On the glass — the same pane the
   hero uses — they have a surface to sit on that is not a white card, and the
   pane gives the page permission to make them large: a poster at 19rem is a
   thumbnail, at 26rem it is a poster.

   TEXT IS WHITE, NOT MUTED. Muted greys were chosen against the panel's light
   ground and carried over here unchanged. On glass over deep space they are
   the one thing on the section that looks unfinished. */
.s-stage {
  padding: clamp(1.4rem, 3vw, 2.6rem);
  border-radius: var(--g-r-card);
  background: rgba(255, 255, 255, .045);
  border: 1px solid rgba(255, 255, 255, .13);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .32);
  grid-template-columns: minmax(14rem, 19rem) 1fr;
  gap: clamp(1.4rem, 3vw, 2.6rem);
}
/* on the glass, every value is measured against white rather than against the
   panel's light ground */
.s-stage {
  --sv-well: rgba(255, 255, 255, .07);
  --sv-line: rgba(255, 255, 255, .16);
  --sv-off:  rgba(255, 255, 255, .028);
}
.s-stage, .s-stage .sv-cap, .s-stage .sv-w-out,
.s-stage .sv-days b, .s-stage .sv-who span, .s-stage .sv-alts button { color: #fff; }
.s-stage .sv-cap, .s-stage .sv-w-out, .s-stage .sv-days b { color: rgba(255, 255, 255, .72); }
.s-rail-b { color: #fff; }
.s-rail-b span { color: rgba(255, 255, 255, .72); }
.sv-type { color: #fff; }

/* ---- and everything grows ---- */
.s-show { min-height: clamp(24rem, 44vw, 32rem); }
.s-rail-b { padding: 1.05rem 1.15rem; }
.s-rail-b b { font-size: clamp(1rem, 1.5vw, 1.22rem); }
.s-rail-b span { font-size: .84rem; }

.sv-month { max-width: 42rem; }
.sv-days b { font-size: .8rem; }
.sv-grid { gap: .38rem; }
.sv-cap { font-size: .84rem; }

.sv-strip span { width: clamp(14rem, 26vw, 23rem); }
.sv-strip { gap: .8rem; }

.sv-stack { width: min(26rem, 82%); }
.sv-stack .cw { font-size: .9rem; padding: 2rem 1rem .8rem; }

.sv-copy { max-width: 44rem; }
.sv-who img { width: 3.2rem; height: 3.2rem; }
.sv-who b { font-size: 1.15rem; }
.sv-who span { font-size: .88rem; }
.sv-type { font-size: clamp(1rem, 1.5vw, 1.18rem); line-height: 2.05; min-height: 9rem; }
.sv-alts button { font-size: .84rem; padding: .45rem .95rem; }

.sv-figs b { font-size: clamp(2.6rem, 6vw, 4.2rem); }
.sv-figs span { font-size: .9rem; }

.sv-w-box { width: clamp(15rem, 30vw, 22rem); }
.sv-w-face text { font-size: 8.5px; }
.sv-w-out { font-size: .88rem; max-width: 34rem; }
.sv-w-out b { font-size: 1.35rem; }

@media (max-width: 820px) {
  .s-stage { padding: 1rem; }
  .s-show { min-height: 26rem; }
}

/* ----------------------------------- 25. round edges, and nothing clipped

   THE INVISIBLE RECTANGLE. .s-pane carried `overflow: hidden` from when it
   was a card, and a card is exactly what it stopped being. Two things were
   being cut by a box with no visible edge: the figures — «100٪» rendered as
   «00٪» and «20M+» as «0M+», because the type is clamped to 6vw and the
   auto-fit column is not — and every drop-shadow on the stack and the wheel,
   which stopped dead at a border nobody could see.

   Only the film strip actually needs clipping, and it can hold its own. */
.s-pane { overflow: visible; }
.sv-film { overflow: hidden; }

/* the figures get room instead of being trimmed to fit */
.sv-figs { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
.sv-figs b { font-size: clamp(2.2rem, 4.4vw, 3.6rem); white-space: nowrap; }
.sv-figs div { min-width: 0; padding-inline: .4rem; }

/* ROUND EDGES. --g-r4 is the panel's card radius, which is right for a card
   in a dense grid and too tight for a pane this size — a large surface with a
   small radius reads as a rectangle somebody rounded slightly, rather than as
   a rounded surface. */
.s-stage { border-radius: clamp(1.4rem, 2.4vw, 2.2rem); }
.sv-stack figure { border-radius: clamp(.9rem, 1.4vw, 1.3rem); }
.sv-strip img { border-radius: clamp(.7rem, 1.1vw, 1rem); }
.sv-grid i { border-radius: clamp(7px, .7vw, 11px); }
.sv-film { border-radius: clamp(.7rem, 1.1vw, 1rem); }

/* four figures want one row, not three and an orphan — auto-fit was picking
   three at this width and dropping 360° onto a line of its own */
@media (min-width: 900px) {
  .sv-figs { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 899px) {
  .sv-figs { grid-template-columns: repeat(2, 1fr); row-gap: 2rem; }
}

/* ------------------------- 26. the glass carries its own ground, and the cut

   THE PANE HOLDS ITS LOOK AT EITHER HOUR. White type on a translucent pane
   needs something dark behind it, and depending on the sky for that meant the
   whole section fell apart in the light theme. So the stage supplies its own
   deep plate — the same decision already made for the hero and the accent
   cards, written down there as: that pairing has no light version, it has a
   lighter ground around it. The blur still reads, because the plate is a
   layer under the glass rather than a replacement for it. */
.s-stage { position: relative; isolation: isolate; }
.s-stage::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background:
    radial-gradient(58% 52% at 16% 10%, rgba(239, 143, 32, .17), transparent 70%),
    radial-gradient(52% 48% at 86% 22%, rgba(211, 29, 83, .19), transparent 70%),
    radial-gradient(66% 58% at 62% 96%, rgba(112, 45, 190, .22), transparent 72%),
    #0c1029;
}

/* ---- the cut: one frame at a time, which is what editing looks like ---- */
.sv-cut { width: 100%; max-width: 44rem; }
.sv-cut-stage {
  position: relative; aspect-ratio: 16 / 9; width: 100%;
  border-radius: clamp(.8rem, 1.3vw, 1.2rem); overflow: hidden;
  border: 1px solid var(--sv-line); background: rgba(0, 0, 0, .35);
}
.sv-cut-stage figure {
  position: absolute; inset: 0; margin: 0;
  opacity: 0; visibility: hidden;
  transition: opacity 460ms var(--g-ease), visibility 0s linear 460ms,
              transform 5s linear;
  transform: scale(1.04);
}
/* A slow push while a frame is up. It is the difference between a slide and a
   shot — nothing in footage is ever perfectly still. */
.sv-cut-stage figure.on { opacity: 1; visibility: visible; transition-delay: 0s; transform: scale(1); }
.sv-cut-stage img, .sv-cut-stage picture { width: 100%; height: 100%; object-fit: cover; display: block; }
.sv-cut-stage .who {
  position: absolute; inset-block-end: 0; inset-inline: 0; z-index: 2;
  padding: 2.4rem 1.1rem .9rem; font-size: .9rem; color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, .74));
}
.sv-cut.one video { width: 100%; border-radius: clamp(.8rem, 1.3vw, 1.2rem); display: block; }
/* the cut points, marked — the timeline is the part of the job you cannot
   photograph, so it is the part worth drawing */
.sv-cut-bar { display: flex; gap: .3rem; margin-top: .7rem; }
.sv-cut-bar i {
  flex: 1; height: 3px; border-radius: 2px; background: var(--sv-line);
  transition: background var(--g-t) var(--g-ease);
}
.sv-cut-bar i.on { background: var(--g-grad-flat); }
@media (prefers-reduced-motion: reduce) {
  .sv-cut-stage figure { transition: opacity 200ms linear, visibility 0s linear 200ms; transform: none; }
  .sv-cut-stage figure.on { transform: none; }
}

/* ---- 360° leads, and it is enormous ---- */
.sv-figs-wrap { display: grid; justify-items: center; gap: clamp(1.4rem, 3vw, 2.6rem); width: 100%; }
.sv-lead { text-align: center; }
.sv-lead b {
  display: block; line-height: .92; font-weight: 800;
  font-size: clamp(4.5rem, 13vw, 10rem);
  background: var(--g-grad-flat); -webkit-background-clip: text; background-clip: text; color: transparent;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.sv-lead span {
  display: block; margin-top: .5rem; font-size: clamp(.9rem, 1.5vw, 1.1rem);
  color: rgba(255, 255, 255, .8);
}
/* the three counts become the supporting line under it */
.sv-figs-wrap .sv-figs { padding-top: clamp(1rem, 2.4vw, 1.8rem); border-top: 1px solid var(--sv-line); }
.sv-figs-wrap .sv-figs b { font-size: clamp(1.7rem, 3.4vw, 2.8rem); }
@media (min-width: 900px) { .sv-figs-wrap .sv-figs { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 899px) { .sv-figs-wrap .sv-figs { grid-template-columns: repeat(3, 1fr); row-gap: 1.2rem; } }

/* ───────────────────────────── 27. the wheel, as a rendered object

   This supersedes every .sv-w-* rule above it. Those built a flat dial: eight
   coloured triangles, a hairline border and a floating arrow. What makes a
   wheel read as an OBJECT is four things, and none of them is the colour —

     · the rim is metal, with one light source;
     · the wedges are domed, not painted;
     · there is exactly one specular highlight;
     · it casts a shadow and sits in its own bloom.

   Leaned back thirty degrees, the NEAR edge of the rim is the one turned up
   toward the light, so the bright band sits low and the far rim falls into
   shade — the opposite of what a wheel facing the reader would do. Getting
   that backwards is what makes a tilted disc look like a sticker.
   ───────────────────────────────────────────────────────────────────────── */

.sv-wheel { display: grid; justify-items: center; gap: 1.3rem; position: relative; }

.sv-w-stage {
  position: relative; width: min(22rem, 78vw); aspect-ratio: 1;
  display: grid; place-items: center;
  /* short perspective, so the far rim reads noticeably smaller than the near
     one — that convergence is what the eye reads as depth, not the squash.
     THE ROLL IS GONE ON PURPOSE. A rotateZ(-4deg) sat here for the character
     it gave the disc, and a disc is radially symmetric so it cost the disc
     nothing. It cost the POINTER, which is the one part whose alignment is
     functional rather than decorative: rolled four degrees about the centre,
     the pin swings eleven pixels off the top and reads as broken rather than
     as leaning. Measured, both ways — with the roll every element sits 11 to
     23px left of the wheel's own centre line; without it, all four measure
     zero. The depth is in the perspective and the X tilt, exactly as the line
     above says; the roll was only ever adding a tell. */
  transform: perspective(760px) rotateX(30deg);
  transform-style: preserve-3d;
}

.sv-w-bloom {
  position: absolute; inset: -22%; border-radius: 50%; z-index: 0; pointer-events: none;
  background: radial-gradient(circle, rgba(211, 29, 83, .34), rgba(112, 45, 190, .16) 48%, transparent 72%);
  filter: blur(22px);
}
/* A leaning disc throws a wider, shallower shadow, and throws it further from
   its own base. A shadow tucked tight under a tilted object is the fastest way
   to make the tilt look painted on. */
.sv-w-shadow {
  position: absolute; bottom: -14%; left: 50%; translate: -50% 0;
  width: 88%; height: 9%; border-radius: 50%;
  background: rgba(0, 0, 0, .66); filter: blur(20px); z-index: 0;
}

.sv-w-rim {
  position: absolute; inset: 0; border-radius: 50%; z-index: 2;
  background: linear-gradient(#a55a0c 0%, #c96a10 14%, #ef8f20 40%,
                              #ffb43a 66%, #ffe6a8 86%, #ffcf72 100%);
  box-shadow:
    inset 0 3px 5px rgba(255, 255, 255, .85),
    inset 0 -5px 10px rgba(0, 0, 0, .55),
    inset 0 0 0 1px rgba(255, 255, 255, .28),
    0 20px 46px rgba(0, 0, 0, .62),
    0 0 42px 6px rgba(239, 143, 32, .22);
}
/* the inner lip: where the rim turns down toward the face */
.sv-w-lip {
  position: absolute; inset: 9.5%; border-radius: 50%; z-index: 3;
  background: linear-gradient(#8f4708, #c96a10 40%, #ffb43a);
  box-shadow: inset 0 4px 8px rgba(0, 0, 0, .6), 0 0 0 1px rgba(255, 220, 150, .35);
}

/* the bulbs sit IN the rim, not over it */
.sv-w-bulbs { position: absolute; inset: 0; z-index: 6; pointer-events: none; }
.sv-w-bulbs i {
  position: absolute; left: 50%; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 32%, #fff, #ffe9a8 40%, #f5b53c 75%, #b4700c);
  box-shadow: 0 0 10px 3px rgba(255, 228, 150, .85), inset 0 -1px 2px rgba(0, 0, 0, .4);
  animation: svChase 1.25s linear infinite; animation-delay: calc(var(--i) * .0625s);
}
@keyframes svChase {
  0%, 45%   { opacity: 1;   box-shadow: 0 0 14px 4px rgba(255, 235, 170, .95), inset 0 -1px 2px rgba(0, 0, 0, .35); }
  55%, 100% { opacity: .42; box-shadow: 0 0 3px 1px rgba(200, 140, 40, .5),    inset 0 -1px 2px rgba(0, 0, 0, .5); }
}

/* the face turns; everything else is fixed furniture */
.sv-w-face {
  position: absolute; inset: 11.5%; z-index: 4; border-radius: 50%; overflow: hidden;
  border: 0; width: auto; height: auto;
  transition: transform 4.4s cubic-bezier(.09, .72, .16, 1);
  box-shadow: inset 0 0 24px rgba(0, 0, 0, .55);
}
.sv-w-face svg { width: 100%; height: 100%; display: block; }
.sv-w-face .sv-lbl { font-weight: 800; font-family: inherit; letter-spacing: .01em; }

/* ONE specular, upper left, over the face but under the hub */
.sv-w-gloss {
  position: absolute; inset: 11.5%; border-radius: 50%; z-index: 7; pointer-events: none;
  background: radial-gradient(60% 48% at 30% 20%, rgba(255, 255, 255, .34),
                              rgba(255, 255, 255, .10) 45%, transparent 70%);
}
.sv-w-gloss::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: linear-gradient(rgba(255, 255, 255, .22), transparent 26%);
}

/* the bearing — small, because the centre is where the spokes converge and
   filling it hides the one thing that says this turns */
.sv-w-hub {
  /* `inset` FIRST, then left/top — and the order is the whole point. An older
     rule above sets `inset: 39%` on this class, so it has to be cleared; but
     `inset` is a shorthand for top/right/bottom/left, so declaring it AFTER
     `left: 50%; top: 50%` resets both back to auto. That is what put the star
     up and to the left of the point the wedges converge on, while the wheel
     itself was centred correctly — the bearing was the only thing adrift. */
  position: absolute; inset: auto; left: 50%; top: 50%;
  translate: -50% -50%; z-index: 9;
  width: 19%; aspect-ratio: 1; border-radius: 50%; display: grid; place-items: center;
  background: radial-gradient(circle at 36% 30%, #fff, #f3f5ff 45%, #c9cee8);
  box-shadow: 0 0 0 4px #ffb43a, 0 0 0 6px #b4700c, 0 6px 16px rgba(0, 0, 0, .6),
              inset 0 -3px 7px rgba(0, 0, 0, .25);
}
.sv-w-hub svg { width: 52%; height: 52%; }

/* The pointer is not part of the disc, so it does not lie down with it: it is
   counter-rotated back to vertical and lifted toward the reader, which is also
   what stops it disappearing edge-on at thirty degrees. */
.sv-w-pin {
  /* THE SAME TRAP THE HUB FELL INTO, ONE ELEMENT OVER — and this one hid for
     longer because it is invisible in a left-to-right page.
     The two older .sv-w-pin rules above set `inset-inline-start: 50%`, which
     on an RTL document IS `right: 50%`. This rule then set `left: 50%` and
     never cleared it, so the pin carried left:176px AND right:176px on a
     352px stage — over-constrained. CSS resolves that by discarding one of
     them, and in RTL the one it discards is `left`. The pin was therefore
     positioned from the right edge and landed off-centre.
     Those rules also set the `translate` longhand, which is a SEPARATE
     property from `transform` and survives it — so the -50% was applied
     twice, once by each, and the pin drifted a further half-width.
     Hence both resets, and both must come before the values they clear. */
  position: absolute; inset: auto; translate: none;
  top: -9%; left: 50%; z-index: 20;
  width: 32px; height: 44px; border: 0; background: none;
  transform: translateX(-50%) rotateX(-30deg) translateZ(26px);
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, .75));
}

.sv-w-go { min-width: 10rem; }
.sv-w-go:disabled { opacity: .55; cursor: default; }
.sv-w-out {
  font-size: .86rem; color: rgba(255, 255, 255, .75); text-align: center;
  max-width: 32rem; min-height: 3.4em; line-height: 1.8;
  opacity: 0; transition: opacity var(--g-t-slow) var(--g-ease);
}
.sv-w-out.show { opacity: 1; }
.sv-w-out b {
  display: block; font-size: 1.4rem; margin-bottom: .3rem; font-weight: 800;
  background: var(--g-grad-flat); -webkit-background-clip: text;
  background-clip: text; color: transparent;
}

/* the burst, over everything, and never in the way of a click */
.sv-confetti {
  position: fixed; inset: 0; z-index: 9999; pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .sv-w-bulbs i { animation: none; }
  .sv-w-face { transition: none; }
}
@media (max-width: 620px) {
  /* less lean on a phone: the labels are already small, and thirty degrees of
     foreshortening on a 15rem disc costs more than the depth is worth */
  .sv-w-stage { width: min(17rem, 82vw); transform: perspective(700px) rotateX(20deg); }
  .sv-w-pin { transform: translateX(-50%) rotateX(-20deg) translateZ(18px); }
}

/* ── the two dials the panel actually still controls ──────────────────────────
   «سُمك الحد حول الأرقام» and «قوة التعتيم خلف الأرقام» set --gf-stroke and
   --gf-scrim on .sv-figs-wrap. Both were written by the OLD renderer into an
   element this page no longer has, so the sliders saved, reported success, and
   changed nothing anybody could see. The figures still exist here; only the
   wiring was lost in the rebuild.
   Defaults match the panel's own defaults, so a content.json that has never
   been touched renders exactly as it does today. */
.sv-figs-wrap { --gf-stroke: .035em; --gf-scrim: .62; position: relative; }

/* The pool. Behind the figures, never over them — it is what makes a number
   legible on top of an image, which is the whole reason the dial exists. */
.sv-figs-wrap::before {
  content: ""; position: absolute; inset: -8% -6%; z-index: 0; pointer-events: none;
  background: radial-gradient(60% 70% at 50% 45%,
              rgba(5, 7, 15, var(--gf-scrim)), transparent 72%);
}
.sv-figs-wrap > * { position: relative; z-index: 1; }

/* The outline. paint-order:stroke keeps the stroke UNDER the fill, so the
   gradient stays clean and the stroke reads as separation rather than as a
   second colour on the glyph. */
.sv-figs-wrap .sv-lead b,
.sv-figs-wrap .sv-figs b {
  -webkit-text-stroke: var(--gf-stroke) rgba(255, 255, 255, .92);
  paint-order: stroke fill;
}
