/* =========================================================
   Page — Home

   Section order: hero · shoppable marquee · trust strip · how it
   works · the maker's note · the console · hub · with-without ·
   video showcase · AI assistant · builders · fronts · who · plans
   · FAQ.

   Two of those moved. The maker's note was thirteenth of sixteen,
   which put the only answer to "who is behind this" behind eleven
   scrolls of product — and with no testimonials to print until the
   platform has merchants, that note is the whole of the page's
   social proof. It sits fifth now, right after the three steps: the
   reader learns how it works, then who is saying so.

   With-without was eleventh, on the far side of five consecutive
   product sections (console · hub · video · agent · builders). That
   run is the flattest stretch of the page and the comparison is the
   sharpest thing on it, so it breaks the run after two rather than
   waiting until the reader is through all five.

   Their grounds swapped to keep the alternation: the maker's note
   takes the band it now needs between two canvas sections, and
   with-without gives one up between a band and a canvas.

   Two kinds of picture live here. The mocks around the storefront
   are drawn in CSS, so they follow the theme and cannot go stale.
   The console cards and the storefront shots are photographs of
   the running product, and they are re-shot when it changes.
   ========================================================= */

/* Arabic headlines here run two lines at most; balancing them keeps
   a single word from being left alone on the second. */
.hero__title,
.maker__title { text-wrap: balance; }

/* ---------------------------------------------------------
   Hero
   --------------------------------------------------------- */
.hero {
  position: relative;
  /* 80 above because the sticky header already stands there; a full
     --space-24 under, because everything below this hands off on the
     one beat and the hero is not an exception to it. */
  padding-block: var(--space-20) var(--space-24);
  overflow: hidden;
  isolation: isolate;
}

/* The only ambient light on the page, and it is brass, not teal —
   a warm pool behind the headline rather than a product glow. */
.hero__glow {
  position: absolute;
  inset-block-start: -22%;
  inset-inline-start: -6%;
  width: min(760px, 90%);
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background: radial-gradient(
    circle at center,
    var(--color-gold-soft) 0%,
    transparent 62%
  );
  pointer-events: none;
}

/* The hero's ground. A hero that ends on the same flat canvas the
   rest of the page uses has nothing to lift the mock off, which is
   why the shot read as pasted on. This is a single wide wash in the
   brand ramp — teal at the top corner falling to nothing before the
   fold — plus a hairline where the section ends, so the hero closes
   rather than just stopping. Alpha is low enough that the headline
   contrast is untouched in either theme. */
.hero__wash {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(120% 88% at 82% -12%, var(--hero-wash-brand) 0%, transparent 58%),
    radial-gradient(90% 70% at 8% 4%, var(--hero-wash-deep) 0%, transparent 60%),
    linear-gradient(180deg, var(--hero-wash-top) 0%, transparent 72%);
  pointer-events: none;
}
.hero__wash::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, var(--color-line), transparent);
}

.hero__inner {
  position: relative;
  display: grid;
  gap: var(--space-12);
  align-items: center;
}

.hero__copy { max-width: 34rem; }
.hero__badge { margin-bottom: var(--space-6); }

/* One step down from --fs-h1. The display cap is set for a page that
   opens on a headline alone; this hero opens on a headline beside a
   storefront, and at the full 4.5rem the two were competing for the
   same eye. Almarai also sets wider than Inter at equal size, so the
   Arabic needs less of it to carry the same weight. */
.hero__title {
  margin: 0 0 var(--space-6);
  font-size: clamp(2.25rem, 1.5rem + 2.6vw, 3.5rem);
  line-height: 1.18;
  letter-spacing: var(--tracking-tight);
}
.hero__title .u-word-mark { white-space: nowrap; }

.hero__lead {
  margin: 0 0 var(--space-8);
  color: var(--color-text-secondary);
  max-width: 40ch;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

/* The reassurance row. Brass rather than muted grey, because these
   two lines answer the objection that stops the click and a footnote
   colour told the eye to skip them. The tick is a mask over the same
   brass, so there is one colour to change and no icon to ship. */
.hero__note {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--color-gold-text);
}
.hero__note li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.hero__note li::before {
  content: "";
  width: 14px;
  height: 14px;
  flex: none;
  background-color: var(--color-gold-text);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 14px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.5l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 14px no-repeat;
}

/* ---- The stage: browser shot + phone ---- */
/* Positioned, so the phone hangs off the shot's own corner. It used
   to resolve against .hero__inner instead, which put it at the bottom
   of the whole grid — on top of a product card rather than beside
   the storefront it is meant to sit next to. */
.hero__stage {
  position: relative;
  padding-block-end: var(--space-10);
  padding-inline-end: var(--space-16);
}

/* The shot floats above the wash rather than sitting on it.
   --shadow-deep is the platform's «picked up» elevation, and this
   is the object the whole hero is arranged around. The premium
   radius matches every other large surface since §7. */
.shot {
  position: relative;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-premium);
  background: var(--gradient-surface);
  box-shadow: var(--shadow-deep);
  overflow: hidden;
}

/* One hairline of light along the top edge — what a real surface
   lit from above does, and the reason it reads as raised rather
   than pasted on. */
.shot::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 12%;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.11), transparent);
  pointer-events: none;
}

.shot__chrome {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-line);
  background: var(--color-surface-raised);
}
.shot__dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--color-line-strong);
}
.shot__url {
  margin-inline-start: var(--space-4);
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
  font-size: 0.72rem;
  color: var(--color-text-muted);
  direction: ltr;
}

.shot__screen { padding: var(--space-4); }

.shot__bar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-line);
}
.shot__store {
  font-family: var(--font-display-ar);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--color-text);
}
.shot__navset {
  display: flex;
  gap: var(--space-3);
  margin-inline-end: auto;
}
.shot__navset i {
  display: block;
  width: 30px;
  height: 5px;
  border-radius: var(--radius-pill);
  background: var(--color-line);
}
.shot__cart {
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
  color: var(--color-text-on-brand);
  font-size: 0.7rem;
  font-weight: var(--fw-bold);
}

/* ---- The storefront's banner row ---- */
.shot__banner {
  position: relative;
  margin: var(--space-4) 0 0;
  aspect-ratio: 16 / 7;
  border-radius: var(--radius-md);
  background: var(--color-surface-raised);
  overflow: hidden;
}
.shot__banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 30%;
  display: block;
}
/* Bottom-up, not from the reading edge. A horizontal scrim would have
   to know the writing direction, and the caption sits at the bottom
   in both — so the gradient that gives it its ground can be one
   direction-neutral value instead of two that can disagree. */
.shot__banner::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    rgb(0 0 0 / 0.62) 0%,
    rgb(0 0 0 / 0.22) 34%,
    transparent 62%
  );
}
.shot__banner-copy {
  position: absolute;
  inset-block-end: var(--space-3);
  inset-inline-start: var(--space-4);
  z-index: 1;
  display: grid;
  gap: 6px;
  justify-items: start;
}
.shot__banner-title {
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.4);
}
.shot__banner-btn {
  padding: 3px var(--space-3);
  border-radius: var(--radius-xs);
  background: rgb(255 255 255 / 0.94);
  color: var(--athar-neutral-900);
  font-size: 0.6rem;
  font-weight: var(--fw-bold);
}

.shot__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-3);
  padding-top: var(--space-3);
}
.shot__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
/* Photographs, not tints. The tiles used to be three gradients that
   avoided «pretending to be photographs» — but the band lower down
   the page is real product photography, so the mock above it read as
   the unfinished one of the two. A storefront's whole argument is
   that products look good in it; drawing that in flat colour argues
   the opposite. */
.shot__thumb {
  position: relative;
  display: block;
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-sm);
  background: var(--color-surface-raised);
  overflow: hidden;
}
.shot__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.shot__play {
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  box-shadow: var(--shadow-sm);
}
/* A play triangle points right in Arabic too: it is a media
   convention, not a reading direction. */
.shot__play::after {
  content: "";
  margin-inline-start: 2px;
  border-left: 8px solid var(--color-text);
  border-block: 5px solid transparent;
}

.shot__tag {
  position: absolute;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  padding: 1px var(--space-2);
  border-radius: var(--radius-xs);
  background: var(--color-canvas-deep);
  color: var(--color-growth-text);
  font-size: 0.65rem;
  font-weight: var(--fw-bold);
}

.shot__name {
  font-size: var(--fs-xs);
  color: var(--color-text);
}
.shot__price {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

/* ---- Phone, overlapping the browser ---- */
/* In front of the shot, so it carries the heavier of the two
   shadows. A phone lying at the same depth as the browser behind
   it reads as a sticker on a screenshot. */
/* Hung off the storefront's outer corner, not laid across it. The
   overlap is the point — two devices at two depths is what says
   «the same shop, on both» — but it has to be the frame it covers,
   not a product. Roughly a third of the phone sits over the shot. */
.phone {
  position: absolute;
  inset-block-end: 0;
  /* Held inside the container's own gutter. A larger negative offset
     framed better on a wide screen and was cut off by the hero's
     overflow at 1200px, which is a common laptop and not an edge
     case. The stage's inline padding is what creates the overlap;
     this only softens it. */
  inset-inline-end: calc(-1 * var(--space-4));
  width: 128px;
  padding: 7px;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-xl);
  background: var(--gradient-surface);
  box-shadow: var(--shadow-deep);
}
.phone__screen {
  position: relative;
  aspect-ratio: 9 / 17;
  border-radius: 14px;
  background: var(--color-canvas-deep);
  overflow: hidden;
}
/* The clip that is playing. Held slightly back so the pin and the
   live chip stay legible over it without a scrim of their own. */
.phone__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.94) brightness(0.88);
}
.phone__live {
  position: absolute;
  inset-block-start: var(--space-2);
  inset-inline-start: var(--space-2);
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px var(--space-2);
  border-radius: var(--radius-xs);
  background: var(--color-danger-soft);
  color: var(--color-danger);
  font-size: 0.6rem;
  font-weight: var(--fw-bold);
}
.phone__live-pip {
  width: 5px;
  height: 5px;
  border-radius: var(--radius-pill);
  background: currentColor;
}

.phone__frame {
  position: absolute;
  inset-block-start: 34%;
  inset-inline: 18%;
  height: 26%;
  border: 1.5px dashed var(--color-growth);
  border-radius: var(--radius-xs);
  opacity: 0.85;
}

/* The buy pin sits on a photograph, not on the theme's ground, so its
   two colours are written as literals rather than tokens: a token pair
   would swap with the theme and the ink would stop matching the still
   underneath it.

   It was black on white, which is what a buy button looks like when
   nobody has chosen a colour for it — and on a warm, low-lit still it
   read as a strip of paper laid over the photograph. It carries the
   brand teal now, the same fill the storefront's own «أضف إلى السلة»
   wears, so the mock shows the button the reader will actually get.
   The two values are the ramp steps --athar-teal-400 and
   --athar-on-primary, copied here rather than referenced for the
   reason above; the ink clears 7.3:1 on the fill. */
.phone__pin {
  position: absolute;
  inset-block-end: var(--space-2);
  inset-inline: var(--space-2);
  padding: 5px;
  border-radius: var(--radius-xs);
  background: #22d8c6;
  color: #003732;
  font-size: 0.6rem;
  font-weight: var(--fw-bold);
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .phone__live-pip { animation: none; }
}

@media (min-width: 980px) {
  .hero { padding-block: var(--space-24) var(--space-20); }
  .hero__inner { grid-template-columns: 1fr 1.05fr; }
  .phone { width: 158px; }
}

/* ---------------------------------------------------------
   How it works
   Copy on one side, three stacked cards on the other. The
   numbers are joined by a rail down the inline-start edge — a
   logical edge, so it lands on the right in RTL and the left in
   LTR without a second rule.
   --------------------------------------------------------- */
.hiw__inner {
  display: grid;
  gap: var(--space-10);
  align-items: start;
}

.hiw__copy .btn { margin-top: var(--space-6); }

.hiw__list {
  display: grid;
  gap: var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
}

/* Three tinted grounds instead of three identical white slabs.

   The tint is a wash of the brand hue already carried by the step's
   own icon, laid over the card's surface rather than replacing it —
   so the card is still a card and the colour is light falling on it.
   The stops are the system's three soft tokens, which means the
   three cards cannot drift out of the palette and both themes are
   handled by the tokens rather than by a second rule.

   Deliberately not frosted glass: a backdrop blur over a flat page
   canvas blurs nothing and costs a compositor layer per card. What
   reads as glass here is the translucent tint, the hairline that
   picks up the same hue, and the highlight along the top edge. */
.hiw__card {
  --card-bg:
    linear-gradient(180deg, var(--hiw-tint) 0%, transparent 62%),
    var(--color-surface);
  --card-border: var(--hiw-edge);
  --card-shadow: var(--shadow-soft);

  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-4);
  align-items: start;
  position: relative;
  overflow: hidden;
}

/* The light along the top edge. One pixel, the card's own hue. */
.hiw__card::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 1px;
  background: linear-gradient(90deg, transparent, var(--hiw-glint), transparent);
}

.hiw__card--one   { --hiw-tint: var(--color-primary-soft); --hiw-edge: var(--color-line); --hiw-glint: var(--color-primary); --hiw-ink: var(--color-primary-text); }
.hiw__card--two   { --hiw-tint: var(--color-entry-soft);   --hiw-edge: var(--color-line-entry); --hiw-glint: var(--color-entry); --hiw-ink: var(--color-entry-text); }
.hiw__card--three { --hiw-tint: var(--color-gold-soft);    --hiw-edge: var(--color-line-gold);  --hiw-glint: var(--color-gold);  --hiw-ink: var(--color-gold-text); }

.hiw__card:not(:last-child)::after {
  content: "";
  position: absolute;
  inset-inline-start: calc(var(--space-5) + 17px);
  top: 100%;
  height: var(--space-4);
  width: 1px;
  background: var(--color-line);
}

/* The step's mark. It replaced a numeral because a number tells the
   reader the order, which the list already tells them, and an icon
   tells them the thing. The order is still carried — by the rail
   between the cards, and by the reading direction. */
.hiw__icon {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  flex: none;
  border: 1px solid var(--hiw-edge);
  border-radius: var(--radius-md);
  background: var(--color-surface-raised);
  color: var(--hiw-ink);
}
.hiw__icon svg { width: 21px; height: 21px; }

/* The clock on each step. Stated in the same brass the strip uses
   for a promise, so the three of them read as one running total. */
.hiw__time {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
  margin: var(--space-1) 0 0;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--hiw-ink);
}

@media (min-width: 980px) {
  .hiw__inner { grid-template-columns: 0.9fr 1.1fr; align-items: center; }
}

.player {
  padding: var(--space-4);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-md);
}

.player__screen {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-md);
  background: linear-gradient(160deg, var(--color-surface-raised), var(--color-canvas-deep));
  overflow: hidden;
}

/* The clip fills the screen and everything else in the mock sits above
   it. It is the watch footage, under-exposed on purpose — see the note
   on .vcp--watches; the same lift applies here. */
.player__video {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  filter: brightness(2.6) contrast(1.08) saturate(1.1);
}

/* The play control used to be decoration on an empty rectangle. Now it
   drives real footage, so it says which state it is in — and it steps
   back once playback starts rather than sitting on top of the shot.
   Hover or keyboard focus brings it back, so it is never unreachable. */
.player__play .player__icon-pause { display: none; }
.player.is-playing .player__play .player__icon-play { display: none; }
.player.is-playing .player__play .player__icon-pause { display: block; }

.player.is-playing .player__play {
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
.player.is-playing:hover .player__play,
.player.is-playing:focus-within .player__play {
  opacity: 1;
}
.player__glow {
  position: absolute;
  inset-block-start: -30%;
  inset-inline-end: -20%;
  width: 70%;
  aspect-ratio: 1;
  border-radius: var(--radius-pill);
  background: radial-gradient(circle at center, var(--color-growth-soft), transparent 65%);
}

.player__play {
  position: relative;
  display: grid;
  place-items: center;
  width: 62px;
  height: 62px;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--color-surface) 80%, transparent);
  color: var(--color-text);
  cursor: pointer;
  transition: transform var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.player__play:hover {
  transform: scale(1.06);
  border-color: var(--color-growth);
}
.player__play svg { width: 30px; height: 30px; }

.player__chip {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-start: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 3px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-canvas-deep);
  color: var(--color-danger);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-bold);
}
.player__chip-pip {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: currentColor;
}

.player__timeline {
  position: relative;
  height: 4px;
  margin-block: var(--space-4) var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-line);
}
.player__played {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 46%;
  border-radius: var(--radius-pill);
  background: var(--color-growth);
}
.player__mark {
  position: absolute;
  top: 50%;
  width: 10px;
  height: 10px;
  translate: 50% -50%;
  border-radius: var(--radius-pill);
  background: var(--color-gold);
  box-shadow: 0 0 0 3px var(--color-gold-soft);
}
:root[dir="ltr"] .player__mark { translate: -50% -50%; }
.player__mark--1 { inset-inline-start: 22%; }
.player__mark--2 { inset-inline-start: 46%; }
.player__mark--3 { inset-inline-start: 74%; }

.player__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}

/* The buy card that appears at the tagged moment, shown here as it
   lands: overlapping the player, not floating beside it. */
.buycard {
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: var(--space-8);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: min(320px, calc(100% - var(--space-10)));
  padding: var(--space-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-md);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-lg);
}
.buycard__thumb {
  width: 44px;
  height: 44px;
  flex: none;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--color-surface-sunken);
}
.buycard__body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.buycard__name {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.buycard__price {
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}
.buycard__cta {
  margin-inline-start: auto;
  flex: none;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-text-on-brand);
  font-size: 0.7rem;
  font-weight: var(--fw-bold);
}

/* ---------------------------------------------------------
   The maker's note
   --------------------------------------------------------- */
/* --space-panel, not --space-10. The density tokens exist because
   every panel on the platform was reaching into the space scale for
   the same answer and picking a different number per file; this card
   was on 40px while the cards either side of it were on 24 and 28. */
/* The card that signs the page.

   It read as one more white panel in a run of white panels, which is
   the wrong weight for the only place on this page where the company
   speaks in its own voice. Three changes, none of them loud: a brass
   wash lifting out of the corner the mark sits in, the brass hairline
   the system reserves for a real boundary, and the depth of a thing
   picked up rather than printed. */
.maker__card {
  display: grid;
  gap: var(--space-panel);
  align-items: center;
  padding: var(--space-panel);
  border: 1px solid var(--color-line-gold);
  border-radius: var(--radius-premium);
  background:
    radial-gradient(80% 120% at 8% 0%,
      color-mix(in srgb, var(--color-gold) 13%, transparent) 0%, transparent 62%),
    var(--color-surface);
  /* Lifted rather than outlined. The hairline stays because on the
     dark theme --color-surface sits only a few values off the canvas
     and a shadow alone will not find the edge; on paper the shadow is
     doing all the work and the line is almost invisible, which is the
     correct division of labour on each ground. */
  box-shadow: var(--shadow-deep);
}
.maker__mark { display: flex; justify-content: center; }
/* The mark is the section's illustration here, not a lockup, so it is
   drawn at a size where the letterforms are actually readable — but
   104px was nearly two lines of the text beside it and turned a note
   into a poster. 76px still reads and stops competing. */
.maker__mark .brand { --brand-mark: 76px; }
.maker__mark .brand { pointer-events: none; }
.maker__title {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-h3);
}
.maker__text {
  margin: 0 0 var(--space-6);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

/* The undertakings. Two columns where there is room, so four short
   lines read as a block of four rather than as a list that goes on —
   and each one gets the brass tick the strip near the top of the page
   uses, because these are the same kind of statement. */
.maker__promises {
  display: grid;
  gap: var(--space-3) var(--space-6);
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--fs-sm);
  color: var(--color-text);
}
.maker__promises li {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  line-height: var(--lh-snug);
}
.maker__promises li::before {
  content: "";
  inline-size: 16px;
  block-size: 16px;
  flex: none;
  margin-block-start: 0.15em;
  background-color: var(--color-gold-text);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.6l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.6l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.1' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 16px no-repeat;
}

@media (min-width: 620px) {
  .maker__promises { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 820px) {
  .maker__card {
    grid-template-columns: auto 1fr;
    gap: var(--space-panel-loose);
    padding: var(--space-panel-loose);
  }
}

/* ---------------------------------------------------------
   Plans

   The cards themselves are components/plan-card.css, shared with the
   pricing page. What is left here is the foot line under them, which
   is this page's alone: pricing ends its grid with a sentence, and
   this one ends it with a sentence and the way through to the table.
   --------------------------------------------------------- */
.plans__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin: var(--space-10) 0 0;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

.plans__cycle { margin-block-end: var(--space-10); }

/* ---------------------------------------------------------
   The row: the door, then the three tiers

   Two columns, and the wide gutter between them is the whole point.
   The free tier is not a fourth thing to compare against three — put
   it in the line and the reader weighs four cards on price and finds
   the one that costs nothing cheapest, which is the opposite of what
   it is for. Stood out to the side it reads as the way in.

   1fr against 3fr rather than four equal columns: the trio carries its
   own 20px gaps inside its share, so a paid card comes out about 260px
   against the free card's 273. Thirteen pixels is not a hierarchy
   anyone will read, and making them exactly equal would mean matching
   the outer gutter to the inner one — which would put the free tier
   back in the row.
   --------------------------------------------------------- */
.plans__row {
  display: grid;
  gap: var(--space-10);
  grid-template-columns: 1fr;
  align-items: stretch;
}

@media (min-width: 1080px) {
  .plans__row { grid-template-columns: 1fr 3fr; }
}

/* The card grows and the note sits under it, so the free column ends
   on the same line the trio does however long the lists get. */
.plans__free {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.plans__free > .pr-card { flex: 1; }

.plans__door {
  margin: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-relaxed);
  color: var(--color-text-muted);
}

/* Stacked, the free tier has no side of the row to stand on, and left
   to fill the column it came out 937px wide — one card the width of the
   page carrying four short lines, which reads as a layout that has come
   apart rather than as an invitation. Capped and centred it stays the
   size of a card, and «to the side» becomes «above», which is the same
   sentence in one column. */
@media (max-width: 1079px) {
  .plans__free {
    max-width: 24rem;
    margin-inline: auto;
  }
}

/* =========================================================
   Sections added in م1.1 — the density pass
   ========================================================= */

/* ---------------------------------------------------------
   Marquee band
   Sits on the sunken ground with a hairline above and below, so
   the two moving rails read as one inset strip rather than as
   loose text drifting across the page.
   --------------------------------------------------------- */
/* The short step, the same one the clip rail takes. A strip is
   punctuation between two sections and there is one measure for it. */
.marquee-band {
  padding-block: var(--space-12);
  background: var(--color-canvas-band);
}
.marquee-band .marquee + .marquee { margin-top: var(--space-3); }

/* The agent console mock lived here until م3, when the AI-agent
   page needed it five times over; it is now
   components/console.css. */

/* ---------------------------------------------------------
   Builders — a split band
   Two raised halves side by side: the storefront builder and the
   app builder. The brass hairline sits on the band's first half
   only, so the page still changes ground once rather than twice.
   --------------------------------------------------------- */
.builders__split {
  display: grid;
  gap: var(--space-6);
}

.builders__half {
  position: relative;
  display: grid;
  gap: var(--space-6);
  align-content: start;
  padding: var(--space-panel);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-xl);
  background:
    radial-gradient(120% 120% at 100% 0%, var(--color-primary-soft), transparent 58%),
    var(--color-surface);
  overflow: hidden;
}
.builders__half::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--gradient-trace);
  opacity: 0.55;
}

.builders__title { margin: 0 0 var(--space-3); font-size: var(--fs-h3); text-wrap: balance; }
.builders__body {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-base);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}
.builders__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
/* Was a text arrow, now a button — it needs the space a button
   takes and the same top margin the chips above it end on. */
.builders__try {
  justify-self: start;
  margin-top: var(--space-5);
}

/* ---- The builder mock: a block list beside a canvas ---- */
.bld {
  display: grid;
  grid-template-columns: 108px 1fr;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  background: var(--color-canvas-deep);
  box-shadow: var(--shadow-md);
}
.bld__side {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.bld__block {
  padding: var(--space-2) var(--space-3);
  border: 1px dashed var(--color-line-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}
.bld__block--active {
  border-style: solid;
  border-color: var(--color-growth);
  background: var(--color-growth-soft);
  color: var(--color-growth-text);
}

.bld__canvas {
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--color-surface);
  aspect-ratio: 16 / 11;
}
/* Anchored to the top edge: a storefront is recognised by its header and
   hero, and a centred crop throws both away. */
.bld__canvas img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: top center;
}
.bld__row {
  display: block;
  border-radius: var(--radius-xs);
  background: var(--color-surface-raised);
}
.bld__row--hero {
  height: 62px;
  background: linear-gradient(120deg, var(--color-primary-soft), var(--color-growth-soft));
}
.bld__row--wide { height: 22px; }
.bld__grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}
.bld__grid i {
  height: 46px;
  border-radius: var(--radius-xs);
  background: var(--color-surface-raised);
}

/* ---- The app mock: one phone screen, drawn not photographed ---- */
.appmock {
  display: grid;
  gap: var(--space-2);
  align-content: start;
  width: min(190px, 100%);
  margin-inline: auto;
  padding: var(--space-3);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
}
.appmock__bar {
  height: 6px;
  width: 42%;
  margin-inline: auto;
  border-radius: var(--radius-pill);
  background: var(--color-line-strong);
}
/* The screen is a photograph now, not a gradient bar over four grey
   tiles. 9:16 and cropped from the top: the shot is a whole mobile
   storefront two and a half screens tall, and what belongs in a
   phone frame this size is the part a customer opens on. */
.appmock__shot {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 9 / 16;
  object-fit: cover;
  object-position: top center;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
}
.appmock__tabs i {
  border-radius: var(--radius-xs);
  background: var(--color-surface);
  border: 1px solid var(--color-line);
}
.appmock__tabs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-1);
}
.appmock__tabs i { height: 10px; }

@media (min-width: 980px) {
  .builders__split { grid-template-columns: 1fr 1fr; }
  .builders__half { padding: var(--space-panel-loose) var(--space-panel); }
}

/* ---------------------------------------------------------
   With / without
   --------------------------------------------------------- */
.versus__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
}
.versus__col { gap: var(--space-5); }
.versus__head { margin: 0; font-size: var(--fs-h4); }

/* The two cards used to differ by one step of ground, and a reader
   scanning the band could not tell at a glance which side the page
   was arguing for. They now sit at opposite ends of the same ramp:
   one lit from inside and edged in the brand teal, the other sunk
   and edged in nothing.

   The tint is a wash, not a fill — the card keeps its own surface
   underneath, so the ink on it is the ink the tokens measured. */
.versus__col--with {
  --card-bg:
    linear-gradient(165deg,
      color-mix(in srgb, var(--color-primary) 14%, transparent) 0%,
      transparent 58%),
    var(--color-surface-raised);
  --card-border: var(--color-primary);
  /* Depth, then a soft brand halo around it. Not --shadow-bloom: that
     token resolves to `none` on paper, and `box-shadow: <deep>, none`
     is invalid as a whole — the card lost its shadow entirely rather
     than losing the glow. color-mix works in both themes. */
  --card-shadow:
    var(--shadow-deep),
    0 0 0 4px color-mix(in srgb, var(--color-primary) 12%, transparent);
}
.versus__col--with .versus__head { color: var(--color-primary-text); }

.versus__col--without {
  --card-bg: var(--color-surface-sunken);
  --card-border: var(--color-line);
  --card-shadow: none;
}
.versus__col--without .versus__head { color: var(--color-text-muted); }

.versus__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.versus__list li {
  position: relative;
  padding-inline-start: var(--space-8);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}

/* One disc per row, with the glyph cut out of it as a mask rather
   than drawn from borders and crossing gradients. Same two shapes
   the rest of the page uses, so a tick here is the tick in the hero
   and in the strip above — and changing one changes all three. */
.versus__list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.02em;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
}
.versus__list li::after {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.02em;
  width: 22px;
  height: 22px;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 13px;
  mask-size: 13px;
}

.versus__list--yes li { color: var(--color-text); }
.versus__list--yes li::before { background: var(--color-success-soft); }
.versus__list--yes li::after {
  background-color: var(--color-success);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.6l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.6l3 3 6-7' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.versus__list--no li { color: var(--color-text-muted); }
.versus__list--no li::before { background: var(--color-danger-soft); }
.versus__list--no li::after {
  background-color: var(--color-danger);
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 4.5l7 7M11.5 4.5l-7 7' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4.5 4.5l7 7M11.5 4.5l-7 7' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
}

.versus__foot {
  margin-block-start: var(--space-12);
  text-align: center;
}

/* ---------------------------------------------------------
   Who it is for
   Typographic cards: a brass rule above the title, no picture.
   --------------------------------------------------------- */
.who__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}

/* The card carries a photograph now, so its padding moves inside: a
   picture inset from the card edge reads as a thumbnail pasted onto a
   panel, and the point of the picture is that it is the panel. */
.who__item {
  --card-pad: 0;
  overflow: hidden;
  gap: 0;
}

/* 4:3 gave the goods a strip and the text the rest of the card; the
   photograph is the half of this card that says who the audience is,
   so it takes the taller frame and the copy sits under it. */
.who__shot {
  margin: 0;
  aspect-ratio: 5 / 4;
  overflow: hidden;
  background: var(--color-surface-sunken);
}
.who__shot img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  /* The photographs come from four different shops and four different
     phones. A shared, very slight desaturation is what makes them read
     as one set rather than four borrowed pictures. */
  filter: saturate(0.92);
  transition: transform var(--dur-slow) var(--ease-out);
}
.who__item:hover .who__shot img { transform: scale(1.03); }

/* The copy sits on a petrol-blue wash rather than on the card's own
   white. Two jobs: it separates the words from the photograph above
   without drawing a line between them, and it gives four cards of
   four different-coloured goods one shared ground, so the row reads
   as a set.

   Petrol because it is the palette's blue — the same token the second
   step card and the hub's chart flags are tinted with — and because
   a wash at 10% keeps the body text on the ink the tokens measured. */
.who__text {
  padding: var(--space-5);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--color-entry) 14%, transparent) 0%,
      color-mix(in srgb, var(--color-entry) 6%, transparent) 100%),
    var(--color-surface);
  border-block-start: 1px solid color-mix(in srgb, var(--color-entry) 22%, transparent);
  flex: 1;
}

/* The gold rule used to run the full width above the title. Against a
   photograph edge that reads as a second border, so it becomes a short
   mark instead — the same signal, spent quietly. */
.who__title {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-h4);
}
.who__title::before {
  content: "";
  display: block;
  inline-size: 1.75rem;
  block-size: 2px;
  margin-block-end: var(--space-3);
  background: var(--color-gold);
  opacity: 0.85;
}

/* Every other band on this page ends on a request; this one
   used to end on a full stop. */
.who__foot {
  margin-block-start: var(--space-12);
  text-align: center;
}

.who__body {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

@media (prefers-reduced-motion: reduce) {
  .who__shot img { transition: none; }
  .who__item:hover .who__shot img { transform: none; }
}

/* ---------------------------------------------------------
   The stage

   A blurred pool sits behind the two mockups and nowhere else
   on the page. It is what separates a hero that is a picture
   from a hero that is a lit set: wider than the shot, softer
   than the glow above it, and it moves the eye to the object
   rather than to the light.
   --------------------------------------------------------- */
.hero__stage {
  position: relative;
  isolation: isolate;
}

.hero__stage::before {
  content: "";
  position: absolute;
  inset: -12% -8% -18%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(
    58% 52% at 50% 50%,
    var(--color-primary-soft) 0%,
    transparent 72%
  );
  filter: blur(34px);
  pointer-events: none;
}

/* The live pip is the only thing in the hero that moves on its
   own. Two seconds, opacity only, and the reduced-motion rule in
   app.css stops it — a breathing dot beside «مباشر» is the whole
   of what a live badge has to say. */
@keyframes athar-pip {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
.phone__live-pip {
  animation: athar-pip 2s var(--ease-in-out) infinite;
}


/* =========================================================
   The connectivity hub

   A card in the middle, six badges around it, and curves
   joining them. Everything is absolutely placed on one stage
   so the wires and the badges share a coordinate space.
   ========================================================= */
.hub__stage {
  position: relative;
  aspect-ratio: 600 / 320;
  min-block-size: 20rem;
  /* 48px, the same step every other head on the page takes down to
     its content. It was 64 on the argument that the diagram deserves
     more room; what that bought was one section out of rhythm with
     the eleven around it. */
  margin-block-start: var(--space-12);

  /* The unit that makes the drift trackable.
     100cqw is the stage's width, and the stage's width is 600 viewBox
     units, so 1cqw = 6 units exactly and at every viewport. That is
     what lets a tile drift in CSS by the same real distance its wire's
     endpoint drifts in SVG — a drift in px or rem would agree with the
     path at one viewport and separate from it at all others. Every
     --dx / --dy below is (viewBox units ÷ 6)cqw. */
  container-type: inline-size;
}

.hub__wires {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  /* SVG clips to its viewport by default, which would shave the pulse's
     drop-shadow at the edges of the stage. */
  overflow: visible;

  /* A warning for whoever tunes this next: while it was being fitted
     the wires appeared to vanish outside the glass card in every
     screenshot, which sent two rounds of opacity changes chasing a
     contrast problem that did not exist. The paths were painting
     correctly the whole time — the preview pane was compositing the
     SVG layer late, and forcing a repaint showed them in full. Check
     a repainted frame before touching these values. */
}

/* ---------------------------------------------------------
   The bed — the connection at rest

   --color-primary, and quiet. This layer's job is to say the two
   things are joined; it is not the thing being looked at, and a
   line at full strength competes with the badges it is serving.

   Not a border token: --color-line-strong is what a border uses,
   and a border only ever sits against the surface it encloses. On
   the open canvas it measures 1.6:1 and disappears.

   pathLength="100" in the markup is what lets one dash value serve
   all six curves — see the comment on the <svg>.
   --------------------------------------------------------- */
.hub__wire {
  fill: none;
  stroke: var(--color-primary);
  stroke-width: 1.5;
  stroke-linecap: round;
  opacity: 0.4;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  /* Rides the same d animation as its pulse — the bed is the line the
     ray runs along, so if only one of them followed the drifting icon
     the ray would slide off its own wire. */
  animation: var(--wire-anim) var(--float-dur) var(--ease-in-out) var(--float-delay) infinite;
}
:root.js [data-reveal].hub__stage .hub__wire {
  transition: stroke-dashoffset 1.1s var(--ease-out);
}
:root.js [data-reveal].hub__stage.is-revealed .hub__wire,
:root:not(.js) .hub__wire { stroke-dashoffset: 0; }

/* Six wires, drawn one after another rather than together — six
   lines arriving at once reads as a flash, not as a connection. */
.hub__wire:nth-child(2) { transition-delay: 0.08s; }
.hub__wire:nth-child(3) { transition-delay: 0.16s; }
.hub__wire:nth-child(4) { transition-delay: 0.24s; }
.hub__wire:nth-child(5) { transition-delay: 0.32s; }
.hub__wire:nth-child(6) { transition-delay: 0.40s; }

/* ---------------------------------------------------------
   The flow — traffic on the wire

   One short lit segment per curve, running core → badge and
   repeating. --color-focus is the brightest teal the system will
   admit to and the only token that reads as "live" against both
   grounds, which is exactly what a packet of traffic is.

   The dash pattern is 11 units lit in a 100-unit period, and the
   period equals the whole path, so there is never more than one
   ray on a wire at a time.

   The offset runs 100 → 0, and the direction is not arbitrary: a
   dash sits at path distance s when (s + offset) wraps into the
   lit band, so a *rising* offset walks the ray backwards, from the
   badge to the core. Counting down is what sends it out from the
   core, which is the direction the sentence above the diagram
   claims — the shop reaching the services, not the other way. This
   was built the wrong way round first and only caught by pinning
   the animation mid-cycle; if the rays ever look like they are
   draining inward, this is the line.

   Nothing here is scripted: no JS, no canvas, no
   requestAnimationFrame — six paths and one keyframe rule.
   --------------------------------------------------------- */
.hub__pulse {
  fill: none;
  stroke: var(--color-focus);
  stroke-width: 2.25;
  stroke-linecap: round;
  stroke-dasharray: 11 89;
  stroke-dashoffset: 100;
  /* Enough bloom for the segment to read as light rather than as a
     second, brighter wire. Deliberately short of a neon glow — the
     restraint is the difference between expensive and arcade. */
  filter: drop-shadow(0 0 3px color-mix(in srgb, var(--color-focus) 40%, transparent));
  /* Two animations, two properties, no conflict: hub-ray owns
     stroke-dashoffset and opacity, hub-wire-N owns d. */
  animation:
    hub-ray var(--ray-dur) var(--ease-linear) var(--ray-delay) infinite,
    var(--wire-anim) var(--float-dur) var(--ease-in-out) var(--float-delay) infinite;
}

@keyframes hub-ray {
  /* Fading in off the core and out at the badge, so the ray is never
     seen appearing or being cut off — it arrives and it lands. */
  from { stroke-dashoffset: 100; opacity: 0; }
  12%  { opacity: 1; }
  82%  { opacity: 1; }
  to   { stroke-dashoffset: 0; opacity: 0; }
}

/* ---------------------------------------------------------
   Per-wire schedule

   Set on the path in both layers at once, so the bed and the flow
   ride the same d animation and can never separate. Read by
   .hub__wire and .hub__pulse above.

   --ray-*    the travelling light. 2s, down from 3.2s: the rays now
              cross in about the time it takes to read one badge.
              Stagger is duration ÷ 6 ≈ 0.333s, so the six stay
              evenly spread rather than clumping.
   --float-*  the drift. MUST match the --float-dur / --float-delay
              on the matching .hub__node--at-N below, or the wire and
              the tile it is attached to will breathe out of step and
              the line will visibly leave the icon.
   --------------------------------------------------------- */
.hub__wires g > :nth-child(1) { --wire-anim: hub-wire-1; --ray-delay: 0.80s; --float-dur: 5.6s; --float-delay: 0s; }
.hub__wires g > :nth-child(2) { --wire-anim: hub-wire-2; --ray-delay: 1.13s; --float-dur: 5.6s; --float-delay: 0s; }
.hub__wires g > :nth-child(3) { --wire-anim: hub-wire-3; --ray-delay: 1.47s; --float-dur: 5.6s; --float-delay: 0s; }
.hub__wires g > :nth-child(4) { --wire-anim: hub-wire-4; --ray-delay: 1.80s; --float-dur: 5.6s; --float-delay: 0s; }
.hub__wires g > :nth-child(5) { --wire-anim: hub-wire-5; --ray-delay: 2.13s; --float-dur: 5.6s; --float-delay: 0s; }
.hub__wires g > :nth-child(6) { --wire-anim: hub-wire-6; --ray-delay: 2.47s; --float-dur: 5.6s; --float-delay: 0s; }

.hub__wires { --ray-dur: 2s; }

/* ---------------------------------------------------------
   The wires follow their icons

   `d` is an animatable CSS property, so the far end of each curve can
   be walked to a second position and back. Only two points move: the
   endpoint and the control point nearest it. The start stays pinned
   to the square's edge — that anchor is the one thing in this diagram
   that must never move.

   The 50% frame is the 0% frame with the spoke's drift vector added
   to both moving points. Every vector now points at the centre of
   the stage — eight units along the line from the badge to (300,160)
   — so the whole ring closes on the mark and opens back out
   together. In viewBox units:

     1  (+7, +4)     2  (−7, +4)     3  (+8,  0)
     4  (−8,  0)     5  (+7, −4)     6  (−7, −4)

   and each tile below carries the same vector divided by 6, in cqw.
   Change one and you must change the other.

   Fallback: a browser that cannot interpolate `d` simply holds the 0%
   path. The tiles still drift, but they are opaque and 3.25rem across
   while the drift is at most 5 units ≈ 9px, so the fixed endpoint
   stays underneath the icon either way — the join never opens up.
   --------------------------------------------------------- */
@keyframes hub-wire-1 {
  0%, 100% { d: path("M264 138 C 214 138, 196 61, 132 61"); }
  50%      { d: path("M264 138 C 214 138, 203 65, 139 65"); }
}
@keyframes hub-wire-2 {
  0%, 100% { d: path("M336 138 C 386 138, 404 61, 462 61"); }
  50%      { d: path("M336 138 C 386 138, 397 65, 455 65"); }
}
@keyframes hub-wire-3 {
  0%, 100% { d: path("M264 160 C 216 160, 152 157, 96 157"); }
  50%      { d: path("M264 160 C 216 160, 160 157, 104 157"); }
}
@keyframes hub-wire-4 {
  0%, 100% { d: path("M336 160 C 384 160, 448 157, 504 157"); }
  50%      { d: path("M336 160 C 384 160, 440 157, 496 157"); }
}
@keyframes hub-wire-5 {
  0%, 100% { d: path("M264 182 C 214 182, 196 259, 132 259"); }
  50%      { d: path("M264 182 C 214 182, 203 255, 139 255"); }
}
@keyframes hub-wire-6 {
  0%, 100% { d: path("M336 182 C 386 182, 404 259, 462 259"); }
  50%      { d: path("M336 182 C 386 182, 397 255, 455 255"); }
}

/* Motion is the whole of this layer, so with it suppressed the layer
   has no job. Hidden rather than parked: a bright dash frozen on a
   curve reads as a rendering fault, not as a still. The bed is lifted
   a step to carry the diagram on its own. */
@media (prefers-reduced-motion: reduce) {
  .hub__flow { display: none; }
  .hub__wire { opacity: 0.55; }
  /* The drift and the pulse go with it. The wires' d animation has to
     go too, and in the same breath: a wire still walking its endpoint
     while its tile sits still is the one state where the line visibly
     detaches from the icon. */
  .hub__wire,
  .hub__node,
  .hub__tile { animation: none; }
}

/* ---------------------------------------------------------
   The core — a square, and only the mark in it

   Sized as a percentage of the stage rather than in rem, and this
   is the whole reason the wires can land on it accurately. The
   stage is locked to aspect-ratio 600/320 — the viewBox's own
   ratio — so 12% of its width with aspect-ratio 1 is exactly
   72 × 72 of the 600 × 320 units the paths are drawn in, at every
   width the stage is ever laid out at. A box in rem would only
   agree with the anchors at one viewport and drift at all others.

   Square, not the old portrait card: the card was that shape
   because it held a name and a tagline under the logo, and both
   were repeating the heading directly above the diagram.

   Opaque, and deeper than the canvas: the paths now stop at its
   edge instead of running underneath it, so there is nothing left
   for glass to reveal, and the deep ground reads as the well the
   wires plug into.
   --------------------------------------------------------- */
.hub__core {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  transform: translate(50%, -50%);
  /* 9%, down from 12%. The square was reading as a panel the wires
     plugged into; at this size it reads as the mark itself, which is
     what the six spokes are pointing at. */
  inline-size: 9%;
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  padding: var(--space-4);
  /* The same surface the six tiles wear, so the centre reads as one
     of them rather than as a grey plate dropped behind them — the
     deep canvas was chosen when this band was the dark anchor. What
     marks it as the centre is the brand hairline and the ring of
     light under it, not a different ground. */
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  box-shadow: var(--shadow-soft), 0 0 0 5px var(--color-primary-soft);
}
:root[dir="ltr"] .hub__core { transform: translate(-50%, -50%); }

/* The lockup fills the square so its mark can be sized against a
   definite box; brand.css's fixed 38px would not scale with a
   stage that is fluid between 720px and 1180px. */
.hub__core .brand {
  inline-size: 100%;
  block-size: 100%;
  justify-content: center;
}
.hub__core .brand__mark {
  inline-size: 74%;
  block-size: auto;
}

/* ---------------------------------------------------------
   The orbiting nodes

   The node is a bare positioner that drifts. The tile inside it is
   what pulses. They are two elements because the drift is a
   `translate` and the pulse is a `scale` on their own independent
   schedules, and one element running both would need them in a
   single transform on a single timeline.

   Positioned with physical `left` / `top`, not the logical
   properties they used to use. The old pills mirrored under RTL, so
   slot at-1 sat on the left in English and on the right in Arabic —
   harmless while the wires met a symmetric ring of text, fatal now:
   wire 1 is drawn to a fixed point in SVG space and animates along
   at-1's drift vector, so at-1 has to be at that point in both
   directions. The arrangement is a radial diagram with no reading
   order, so there is nothing for the mirror to gain.
   --------------------------------------------------------- */
.hub__node {
  position: absolute;
  display: block;
  transform: translate(-50%, -50%);
  animation: hub-float var(--float-dur) var(--ease-in-out) var(--float-delay) infinite;
}

/* Each slot's centre, on the same 600×320 grid the wires are drawn
   on. --dx / --dy are the drift, and they are the matching
   @keyframes hub-wire-N vector ÷ 6, because 1cqw = 6 viewBox units.
   --float-dur / --float-delay MUST equal the values on
   .hub__wires g > :nth-child(N) above.

   The six vectors used to point in six unrelated directions on six
   unrelated schedules — deliberately, so the drift read as
   unscripted. What it actually read as was six icons wobbling.

   Now every vector points at the centre of the stage and every
   spoke runs the same 5.6s, so the ring draws in toward the mark
   and opens back out as one movement. Eight viewBox units along the
   line to (300,160), normalised per slot — which is why 3 and 4,
   sitting level with the core, travel almost purely horizontally
   while the corners travel on their own diagonal. */
.hub__node--at-1 { top: 19%; left: 22%; --dx:  1.15cqw; --dy:  0.68cqw; --float-dur: 5.6s; --float-delay: 0s; }
.hub__node--at-2 { top: 19%; left: 77%; --dx: -1.13cqw; --dy:  0.70cqw; --float-dur: 5.6s; --float-delay: 0s; }
.hub__node--at-3 { top: 49%; left: 16%; --dx:  1.33cqw; --dy:  0.02cqw; --float-dur: 5.6s; --float-delay: 0s; }
.hub__node--at-4 { top: 49%; left: 84%; --dx: -1.33cqw; --dy:  0.02cqw; --float-dur: 5.6s; --float-delay: 0s; }
.hub__node--at-5 { top: 81%; left: 22%; --dx:  1.15cqw; --dy: -0.68cqw; --float-dur: 5.6s; --float-delay: 0s; }
.hub__node--at-6 { top: 81%; left: 77%; --dx: -1.13cqw; --dy: -0.70cqw; --float-dur: 5.6s; --float-delay: 0s; }

@keyframes hub-float {
  0%, 100% { translate: 0 0; }
  50%      { translate: var(--dx) var(--dy); }
}

/* Push-pull, on its own schedule. Six periods that share no common
   factor with the six drift periods, so no two tiles ever repeat the
   same combination of position and size — that is where the sense of
   independent, unscripted motion comes from. */
@keyframes hub-pulse {
  0%, 100% { scale: 0.96; }
  50%      { scale: 1.08; }
}

.hub__tile {
  display: grid;
  place-items: center;
  inline-size: 3.25rem;
  aspect-ratio: 1;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  background: var(--tile, var(--color-surface));
  box-shadow: var(--shadow-soft);
  animation: hub-pulse var(--pulse-dur) var(--ease-in-out) var(--pulse-delay) infinite;
}
/* One schedule, matching the drift. The six periods used to share no
   common factor so that no two tiles ever repeated the same pair of
   position and size — unscripted by design. Against a ring that now
   breathes together, that independence read as six things failing to
   agree, so the scale rides the same 5.6s: every tile is at its
   largest at the moment it is nearest the mark. */
.hub__tile { --pulse-dur: 5.6s; --pulse-delay: 0s; }

.hub__glyph {
  inline-size: 56%;
  block-size: auto;
  color: var(--icon, var(--color-text));
  fill: currentColor;
}

/* Each service's own colour, on our own rendition of its mark — see
   the note in home.blade.php and build rules §6, item 2. Snapchat is
   the exception that needs a tile of its own: its yellow is the mark,
   and yellow artwork on a white surface is invisible, so the tile
   carries the colour and the glyph goes dark on top of it. */
.hub__node--wa   { --icon: #25d366; }
.hub__node--ig   { --icon: #e1306c; }
.hub__node--tt   { --icon: var(--color-text); }
.hub__node--tg   { --icon: #2aabee; }
.hub__node--ga   { --icon: #e37400; }
.hub__node--sham { --icon: var(--color-gold); }

/* Below this the stage stops being a diagram. The wires are
   meaningless in a column, so they go and the badges stack. */
@media (max-width: 720px) {
  .hub__stage {
    aspect-ratio: auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    /* Not the default stretch. The core is now a square sharing a row
       with the badges rather than a full-width banner above them, and
       stretch pulled every pill on that row up to the square's 88px —
       they rendered as tall ovals next to normal ones underneath. */
    align-items: center;
    gap: var(--space-3);
    min-block-size: 0;
  }
  .hub__wires { display: none; }
  .hub__core,
  .hub__node {
    position: static;
    transform: none;
  }
  :root[dir="ltr"] .hub__core { transform: none; }
  /* A fixed square now, not a full-width banner: 12% of the stage
     means nothing once the stage is a wrapping column of pills, and
     stretching the box would un-square the one thing this change was
     for. It leads the first row and the tiles wrap after it. */
  .hub__core { inline-size: 5.5rem; }
  /* The drift is anchored to a diagram that no longer exists here —
     six tiles wandering inside a wrap-flow would just shove each
     other around. The pulse stays: it costs no layout and it is the
     only thing left saying these are live connections. */
  .hub__node { animation: none; }
}

/* ---------------------------------------------------------
   Shoppable marquee — phone frames alternating with storefront
   previews. The container and track run LTR: under dir="rtl" a
   flex track lays its duplicate half to the inline-start, and
   translateX(-50%) then walks the cards out of the container
   instead of through it. Cards are set back to rtl.
   --------------------------------------------------------- */
/* The band was the last of the pre-token Komrz paste still standing:
   a #030712 slab, a #00f2fe neon border, and a 0 20px 40px cyan glow
   on hover. On the dark theme it read as a black hole two shades below
   a canvas that is already graphite; on the light theme it was a hard
   black rectangle dropped into a paper page, with nothing between the
   two but an edge. That is the vibration.

   --color-surface-sunken is the same idea done as elevation: one step
   below the canvas on either theme, so the band reads as recessed
   rather than as a different material. The cards then come back up out
   of it on --shadow-soft, and the hairline is a hairline again instead
   of a light source.

   Height went with it. 600px around a 420px card left 180px of empty
   slab; the band now takes its height from what is in it. */
/* The clip rail and the fact strip under it wear the same tint and
   touch, so they were never two bands — they were one band with an
   18px seam down the middle of it, which is what made the top of the
   page look assembled. They are now spaced as one: the short step
   above and below the pair, and a smaller one at the join. */
.marquee-container {
  --marquee-ground: var(--color-canvas-band);

  position: relative;
  display: flex;
  /* A column now, because the band carries a heading above the rail.
     The rail itself still runs edge to edge; only the heading is
     held to the container's width. */
  flex-direction: column;
  align-items: stretch;
  overflow: hidden;
  padding-block: var(--space-12) 0;
  background: var(--marquee-ground);
}

/* The rail is written left-to-right so the -50% loop lands the same
   way in both languages, and the container carries that direction.
   The heading is prose, so it takes the page's own direction back. */
.marquee-head {
  direction: rtl;
  margin-bottom: var(--space-8);
  text-align: center;
}
:root[dir="ltr"] .marquee-head { direction: ltr; }

/* The emblem's ramp — teal running into petrol — laid on the glyphs.

   Neither --gradient-brand nor --gradient-word could be used: both
   are built from the fixed bright primitives, which is correct on a
   dark ground and close to invisible on paper (#45e5d3 clears 1.7:1
   against the light band). These two semantic tokens carry the same
   two hues and swap with the theme, so the ramp deepens to a
   readable teal-to-petrol in light and brightens again in dark. */
.marquee-head__title {
  margin: 0;
  font-size: var(--fs-h2);
  line-height: var(--lh-heading);
  background-image: linear-gradient(
    100deg,
    var(--color-primary) 0%,
    var(--color-chart-petrol) 100%
  );
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* The fallback if background-clip is unsupported: the flat brand
     ink, never the transparent above it. */
  -webkit-text-fill-color: transparent;
}
@supports not ((background-clip: text) or (-webkit-background-clip: text)) {
  .marquee-head__title {
    color: var(--color-primary-text);
    -webkit-text-fill-color: currentColor;
  }
}
.marquee-container + .strip .strip__inner {
  padding-block: var(--space-8) var(--space-12);
}
.marquee-track{display: flex; gap: var(--space-4); position: relative; z-index: 1; animation: scrollLeftMarquee 55s linear infinite; width: max-content;}
@keyframes scrollLeftMarquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
.vid-card-premium {
  /* 216 × 384 rather than 250 × 420: the same 9:16 the clip is shot
     in, stated as a ratio so the two can never drift apart. */
  inline-size: 216px;
  aspect-ratio: 9 / 16;
  flex-shrink: 0;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-premium);
  background-size: cover;
  background-position: center;
  box-shadow: var(--shadow-soft);
  transform-origin: center center;
  will-change: transform;
  transition:
    transform var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
/* A card being picked up, not lit from inside. 1.15 threw the
   neighbours out of the way and the glow announced itself; a sixth of
   that lift plus the system's deeper shadow says the same thing. */
.vid-card-premium:hover {
  transform: scale(1.04) translateY(-4px);
  z-index: 2;
  box-shadow: var(--shadow-deep);
}

.marquee-container,
.marquee-track { direction: ltr; }
.marquee-track > .vid-card-premium { direction: rtl; position: relative; overflow: hidden; }
/* The tail closes the loop: without it -50% stops half a gap short
   of the duplicate and the strip jumps once per cycle. */
.marquee-track::after { content: ""; flex: 0 0 0; inline-size: 0; }
.marquee-container::before,
.marquee-container::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inline-size: 10%;
  z-index: 2;
  pointer-events: none;
}
.marquee-container::before { inset-inline-start: 0; background: linear-gradient(to right, var(--marquee-ground), transparent); }
.marquee-container::after { inset-inline-end: 0; background: linear-gradient(to left, var(--marquee-ground), transparent); }
.marquee-container:hover .marquee-track,
.marquee-container:focus-within .marquee-track { animation-play-state: paused; }

.vcp--phone { padding: 9px; background: linear-gradient(165deg, #243040, #0a0f18); }
.vcp__screen { position: absolute; inset: 9px; border-radius: 17px; overflow: hidden; background: #05070c; }
.vcp__screen > video,
.vcp__screen > img { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; object-fit: cover; }

/* The watch clip was filmed against black at a very low exposure — a
   jeweller's shot, not a mistake, but two stops under what a card this
   size can carry. The lift lives here rather than being baked into the
   file so the footage on disk stays what the merchant shot, and the
   poster (captured with the same correction) matches the moving frame. */
.vcp--watches .vcp__screen > video { filter: brightness(2.6) contrast(1.08) saturate(1.1); }
.vcp__notch { position: absolute; top: 7px; inset-inline-start: 50%; transform: translateX(50%); inline-size: 58px; block-size: 14px; border-radius: var(--radius-pill); background: #05070c; z-index: 3; }
.vcp__rail { position: absolute; top: 24%; inset-inline-end: 7px; display: grid; gap: 7px; z-index: 3; }
.vcp__rail i { display: grid; place-items: center; inline-size: 25px; block-size: 25px; border-radius: 50%; background: rgba(255, 255, 255, 0.2); color: #fff; font-style: normal; }
.vcp__rail svg { inline-size: 13px; block-size: 13px; }
.vcp__comment { position: absolute; inset-inline: 9px; bottom: 9px; block-size: 24px; border-radius: var(--radius-pill); background: rgba(255, 255, 255, 0.16); z-index: 3; }
.vcp__buy {
  position: absolute;
  inset-inline: 9px;
  bottom: 41px;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.92);
  color: #0f172a;
}
.vcp__thumb { flex: none; inline-size: 32px; block-size: 32px; border-radius: var(--radius-sm); background: var(--gradient-trace); }
.vcp__meta { flex: 1 1 auto; min-inline-size: 0; }
.vcp__name { display: block; font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vcp__price { display: block; font-size: var(--fs-xs); color: #57534e; white-space: nowrap; }
.vcp__cart { flex: none; display: grid; place-items: center; inline-size: 28px; block-size: 28px; border-radius: var(--radius-sm); background: var(--gradient-trace); color: #fff; }
.vcp__live { position: absolute; top: 12px; inset-inline-start: 12px; z-index: 3; padding: 2px 9px; border-radius: var(--radius-pill); background: var(--color-gold); color: #0b1120; font-size: var(--fs-xs); font-weight: 700; }

.vcp--ui { background: #fbf8f3; }
.ui { position: absolute; inset: 0; display: flex; flex-direction: column; gap: 6px; padding: 9px; color: #1f2937; font-size: 8px; line-height: 1.35; }
.ui__bar { display: flex; align-items: center; justify-content: space-between; padding-inline: 2px; }
.ui__logo { inline-size: 34px; block-size: 7px; border-radius: 2px; background: #1f2937; opacity: 0.85; }
.ui__dots { display: flex; gap: 5px; }
.ui__dots i { inline-size: 6px; block-size: 6px; border-radius: 50%; background: #1f2937; opacity: 0.3; }
.ui__hero { position: relative; flex: 0 0 33%; margin: 0; border-radius: 9px; overflow: hidden; }
.ui__cap { position: absolute; inset-inline-start: 9px; bottom: 9px; color: #fff; }
.ui__cap b { display: block; font-size: 11px; text-shadow: 0 1px 6px rgb(0 0 0 / 45%); }
.ui__cap span { display: inline-block; margin-top: 5px; padding: 3px 9px; border-radius: var(--radius-pill); background: #b98a4b; font-size: 7px; font-weight: 600; }
.ui__label { display: flex; align-items: center; justify-content: space-between; margin: 0; padding-inline: 2px; font-weight: 700; }
.ui__label em { font-style: normal; color: var(--color-gold); }
.ui__cats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.ui__cats i { display: block; aspect-ratio: 1; border-radius: 50%; overflow: hidden; }
.ui__promo { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; flex: 0 0 20%; }
.ui__promo i { display: block; border-radius: 9px; overflow: hidden; }
.ui__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; flex: 1 1 auto; min-block-size: 0; }
.ui__grid i { display: block; border-radius: 7px; overflow: hidden; background: #fff; border: 1px solid rgb(31 41 55 / 10%); }
.ui img { inline-size: 100%; block-size: 100%; object-fit: cover; display: block; }

@media (max-width: 640px) {
  .marquee-container { padding-block: var(--space-8) 0; }
  .vid-card-premium { inline-size: 168px; }
  .marquee-track { gap: var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
  .marquee-container { overflow-x: auto; }
}

/* ---------------------------------------------------------
   Four storefronts

   Screenshots of light pages, so they sit on the anchor ground
   rather than the canvas: a pale rectangle needs something dark
   behind it before it reads as a screen rather than as a hole.

   Two up rather than four. At a quarter of the width a storefront
   becomes a swatch — you can tell the colours apart and nothing
   else — and the whole claim here is that these are different
   shops, which you have to be able to read to believe.
   --------------------------------------------------------- */
/* ---------------------------------------------------------
   The storefront band's ground

   .section--anchor gives this section a flat deep petrol, and
   flat is what made it read as a slab dropped into the page.
   The tokens it sets for ink stay — the band is still dark, so
   the light screenshots still read as lit — and only the fill is
   replaced: an olive warmed out of the brass, falling into the
   petrol it started as, with two soft washes over it.

   Written in color-mix over the same tokens rather than in hex,
   so the band follows the palette if the palette moves.
   --------------------------------------------------------- */
.fronts {
  background:
    radial-gradient(90% 70% at 12% 0%,
      color-mix(in srgb, var(--color-gold) 24%, transparent) 0%, transparent 58%),
    radial-gradient(85% 80% at 88% 100%,
      color-mix(in srgb, var(--color-primary) 18%, transparent) 0%, transparent 62%),
    linear-gradient(155deg,
      color-mix(in srgb, var(--color-gold) 22%, var(--color-canvas-anchor)) 0%,
      var(--color-canvas-anchor) 52%,
      color-mix(in srgb, var(--color-primary-deep) 34%, var(--color-canvas-anchor)) 100%);
}

/* ---------------------------------------------------------
   The rotating storefront

   Four shops in one frame, three seconds each. Stacked
   absolutely so the frame never changes height between them,
   with the first laid out in flow to give the stack that
   height in the first place.
   --------------------------------------------------------- */
.fronts__stage {
  position: relative;
  /* 40rem. At 52 the frame was 832px of storefront and the section ran
     a screen and a quarter tall; at 40 the shop is still read rather
     than glanced at, and the band comes inside one screen. */
  max-inline-size: 40rem;
  margin-block-start: var(--space-8);
  /* The ticks hang out of flow 24px below the frame, so the stage
     carries that distance itself — otherwise the section's bottom
     padding is measured from the caption and the ticks sit in it. */
  margin-block-end: var(--space-6);
  margin-inline: auto;
}

.fronts__item {
  margin: 0;
  /* 0 → 1 over the first beat, held for the shop's three seconds,
     then back to 0. The four run the same 12s and sit three
     seconds apart, so exactly one is up at any moment and the
     hand-off is a crossfade rather than a cut. */
  animation: fronts-show 12s var(--ease-in-out) infinite backwards;
}
.fronts__item:not(:first-child) {
  position: absolute;
  inset: 0;
}
.fronts__item--1 { animation-delay: 0s; }
.fronts__item--2 { animation-delay: 3s; }
.fronts__item--3 { animation-delay: 6s; }
.fronts__item--4 { animation-delay: 9s; }

/* The scale is the difference between a slideshow and a cut: the
   shop settles into place over its first half-second instead of
   appearing already there. 1.015 — enough to read as movement,
   too little to look like a zoom. */
@keyframes fronts-show {
  0%           { opacity: 0; scale: 1.015; }
  4%           { opacity: 1; scale: 1; }
  25%          { opacity: 1; scale: 1; }
  29%          { opacity: 0; scale: 1; }
  100%         { opacity: 0; scale: 1; }
}

.fronts__shot {
  overflow: hidden;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
  /* The screens are the lit thing in a dark room. */
  box-shadow: var(--shadow-lg);
}
.fronts__shot img {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

.fronts__meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-block-start: var(--space-5);
}
.fronts__name {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}
.fronts__name b { font-size: var(--fs-h4); }

.fronts__line {
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

/* The host is what their own shop gets, free, on the day they sign
   up. It is a label and not a link: these addresses do not resolve
   yet, and a host that looks clickable and is not is worse than one
   that never offered. */
.fronts__host {
  font-size: var(--fs-xs);
  color: var(--color-primary-text);
  letter-spacing: 0.01em;
}

/* ---- The ticks ---- */
.fronts__ticks {
  position: absolute;
  inset-block-end: calc(var(--space-6) * -1);
  inset-inline: 0;
  display: flex;
  justify-content: center;
  gap: var(--space-2);
}
.fronts__ticks i {
  position: relative;
  inline-size: 2.25rem;
  block-size: 3px;
  border-radius: var(--radius-pill);
  background: var(--color-line);
  overflow: hidden;
}
/* The fill is a second bar inside the track, wiped across on the
   same 12s clock as the frame it reports on. */
.fronts__ticks i::after {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  background: var(--color-primary);
  transform-origin: inline-start center;
  animation: fronts-tick 12s linear infinite backwards;
}
.fronts__ticks i:nth-child(1)::after { animation-delay: 0s; }
.fronts__ticks i:nth-child(2)::after { animation-delay: 3s; }
.fronts__ticks i:nth-child(3)::after { animation-delay: 6s; }
.fronts__ticks i:nth-child(4)::after { animation-delay: 9s; }

@keyframes fronts-tick {
  0%          { transform: scaleX(0); }
  25%         { transform: scaleX(1); }
  27%, 100%   { transform: scaleX(0); }
}

/* Motion off: the rotation stops on the first shop and the other
   three are removed rather than parked underneath it — four
   captions stacked in one place is unreadable, and the section
   still makes its point with one storefront and the heading. */
@media (prefers-reduced-motion: reduce) {
  .fronts__item { animation: none; }
  .fronts__item:not(:first-child) { display: none; }
  .fronts__ticks { display: none; }
}

/* =========================================================
   Owns — the merchant owns every figure

   This band was «ما بداخل المنصة»: fourteen drawn cells naming
   capabilities. Nothing was wrong with the drawing; what was
   wrong is that a list of feature names is the cheapest claim a
   page can make. Anybody can type «تقارير المبيعات» beside a
   tick, and the reader knows it.

   Three photographs of the console replace the fourteen words.
   They are shot from the dashboard's own components at the
   dashboard's own type and colour (tools/shoot-console.php), so
   the card on this page and the card behind the login are the
   same card.

   The frame is a hairline and a radius. The shot already carries
   the console's ground inside it, so anything else the card
   painted would be a second surface behind the first.
   ========================================================= */
.owns__grid {
  display: grid;
  gap: var(--space-6);
  margin: var(--space-8) 0 0;
  padding: 0;
  list-style: none;
}

.owns__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* No frame, and that is the point. The shot already carries the
   console card — its own hairline, its own 12px radius, its own
   lift — so a border here draws a second line a pixel outside the
   first and a radius here rounds a corner that is already round. */
.owns__shot { margin: 0; }

.owns__img {
  display: block;
  inline-size: 100%;
  block-size: auto;
}

/* Dark is this page's written-in ground, so the dark shot is the one
   in the markup first and the light pair waits. The swap hangs off
   [data-theme] rather than prefers-color-scheme because the visitor
   has a button for this: a screenshot that keeps its own light while
   the page turns dark is a hole in the ground. */
.owns__img--light { display: none; }
:root[data-theme="light"] .owns__img--dark { display: none; }
:root[data-theme="light"] .owns__img--light { display: block; }

.owns__title {
  margin: 0 0 var(--space-2);
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--color-text);
}

.owns__line {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

/* Three across, and not before 900px. At 720 the cards are narrow
   enough that a thirty-day chart becomes a picket fence — the shot
   is photographed at one width and scaling it down past that is
   what loses the axis. */
@media (min-width: 900px) {
  .owns__grid { grid-template-columns: repeat(3, 1fr); }
}

/* ---------------------------------------------------------
   The agent panel on this page only

   The running step already turns a spinner. What it did not do
   was breathe — and against a panel that is otherwise entirely
   still, one rotating ring reads as a loading state that has
   hung rather than as an agent at work. The row it sits on
   fades a little on the same slow beat, so the eye reads the
   step as live and the two finished steps above it as done.

   Scoped to the home page: /ai-agent runs the same component
   with several consoles on screen at once, and six of these
   breathing together would be a page that will not settle.
   --------------------------------------------------------- */
.showcase--flip .console__step.is-running {
  animation: home-step-breathe 2.4s var(--ease-in-out) infinite;
}
@keyframes home-step-breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.62; }
}

@media (prefers-reduced-motion: reduce) {
  .showcase--flip .console__step.is-running { animation: none; }
}

/* ---------------------------------------------------------
   The plan row's glow

   Each of the three paid cards carries a halo in its own tier
   colour — petrol at the entry, teal in the middle, brass at
   the top — so the row reads as three lit objects rather than
   three panels, and the tier accent that was only a 2px rule at
   the head now shows around the whole card.

   Home only. /pricing shows the same cards inside a comparison
   table where a glow on every row would be noise, and the tier
   colours there are already carrying the table's legend.
   --------------------------------------------------------- */
.plans .pr-plans__grid--stacked > .pr-card {
  box-shadow:
    var(--shadow-soft),
    0 0 26px color-mix(in srgb, var(--tier) 26%, transparent);
}
.plans .pr-plans__grid--stacked > .pr-card--featured {
  box-shadow:
    var(--shadow-lg),
    0 0 34px color-mix(in srgb, var(--tier) 38%, transparent);
}
.plans .pr-plans__grid--stacked > .pr-card:hover,
.plans .pr-plans__grid--stacked > .pr-card:focus-within {
  box-shadow:
    var(--shadow-lg),
    0 0 40px color-mix(in srgb, var(--tier) 44%, transparent);
}
