@charset "utf-8";

/* ===========================================================================
   Amantx — coming soon
   Built to the Option 2 identity: the amantx logotype, the Midnight Indigo /
   Cyber Purple / Signal Teal palette, and Tajawal.

   Worth stating up front, because it inverts the previous build: the gradient
   is now part of the identity rather than something forbidden. The palette
   ships one, the logotype carries it in both 'a' counters, and the background
   is built from it. The old navy-and-Plex rules — no gradients, opacity steps
   on one element only, two permitted diagonals — do not apply here.

   One family covers both scripts. Tajawal sets Latin and Arabic, so the page
   ships three faces where the old identity needed three Latin plus three
   Arabic.
   =========================================================================== */


/* ---------------------------------------------------------------------------
   1. Typeface
   Self-hosted from the supplied Tajawal.zip, converted to woff2. The licence
   travels with them: OFL.txt sits beside the fonts.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: "Tajawal";
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/Tajawal-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Tajawal";
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/Tajawal-Medium.woff2") format("woff2");
}
@font-face {
  font-family: "Tajawal";
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  src: url("../fonts/Tajawal-Bold.woff2") format("woff2");
}


/* ---------------------------------------------------------------------------
   2. Colour

   Primary    Midnight Indigo #14143F   Cyber Purple #6633CC   Signal Teal #00D8BC
   Secondary  Deep Indigo     #13027A   Pulse Blue   #2059F0   Cloud White  #F5F7FF

   Two grounds: Midnight Indigo for the dark theme, Cloud White for the light
   one. Dark stays the default — it is the ground the primary logo is presented
   on throughout the guidelines.
   --------------------------------------------------------------------------- */

:root {
  --indigo:  #14143F;
  --deep:    #13027A;
  --purple:  #6633CC;
  --blue:    #2059F0;
  --teal:    #00D8BC;
  --white:   #F5F7FF;

  --ground:  var(--indigo);
  --ink:     var(--white);
  --accent:  var(--teal);
  --rule:    #2E2E63;                /* indigo lifted, for hairlines */

  /* Secondary text is a fixed colour rather than the ink at reduced alpha.
     It sits over a drifting gradient field, and alpha would let its contrast
     drift with it; #B4B7D2 holds 8.9:1 on Midnight Indigo whatever passes
     behind. --card and --bar are translucent on purpose — those are surfaces,
     and the blooms are meant to show through them. */
  --ink-soft: #B4B7D2;
  --rule-hi:  #46468C;
  --card:     rgba(245, 247, 255, 0.045);
  --card-hi:  rgba(245, 247, 255, 0.08);
  --bar:      rgba(20, 20, 63, 0.78);

  color-scheme: dark;
}

:root[data-theme="light"] {
  --ground:  var(--white);
  --ink:     var(--indigo);
  --accent:  var(--purple);          /* teal is too light to read on Cloud White */
  --rule:    #D3D7EA;

  --ink-soft: #4F5273;               /* 7.0:1 on Cloud White */
  --rule-hi:  #A9AFD0;
  --card:     rgba(255, 255, 255, 0.66);
  --card-hi:  rgba(255, 255, 255, 0.92);
  --bar:      rgba(245, 247, 255, 0.82);

  color-scheme: light;
}


/* ---------------------------------------------------------------------------
   3. Tokens
   --------------------------------------------------------------------------- */

:root {
  --font: "Tajawal", "Segoe UI", system-ui, -apple-system, sans-serif;

  --x: clamp(0.5rem, 0.28vw + 0.42rem, 0.75rem);
  --y: clamp(0.5rem, 1.15vh, 1rem);

  --fs-display: clamp(1.5rem, min(2.8vw, 3.8vh), 2.5rem);
  --fs-control: clamp(0.8125rem, 0.22vw + 0.74rem, 1rem);

  /* The services page. Only --fs-title and --fs-index scale hard; the reading
     sizes stay close to constant, because a description that grows with the
     viewport is a description nobody finishes. */
  --fs-title:    clamp(2rem, 1.1rem + 4vw, 4rem);
  --fs-index:    clamp(1.75rem, 1rem + 3.2vw, 3.25rem);
  --fs-practice: clamp(1.3rem, 0.95rem + 1.5vw, 2rem);
  --fs-lede:     clamp(1rem, 0.94rem + 0.3vw, 1.1875rem);
  --fs-card:     clamp(1.0625rem, 1rem + 0.25vw, 1.1875rem);
  --fs-body:     clamp(0.9375rem, 0.9rem + 0.16vw, 1rem);
  --fs-small:    clamp(0.8125rem, 0.78vw + 0.62rem, 0.9375rem);

  /* The about page. --fs-root is the name broken into its three pieces, so it
     scales like a display size; --fs-statement is read, so it barely moves. */
  --fs-root:      clamp(2.25rem, 1.2rem + 4.6vw, 4.5rem);
  --fs-statement: clamp(1.125rem, 1rem + 0.55vw, 1.5rem);

  /* The logotype's ink measures 304.0345 x 51.2496 — aspect 5.9324. That is
     the browser's own bbox for the recovered artwork, not the crop rectangle
     the PDF export reported: those two disagreed, and taking the export's word
     for it cut 16% off the bottom of the mark.

     Clear space is one third of its height on every side, matching the spacing
     the guidelines show around the primary logo. */
  --logo-w: clamp(13rem, min(46vw, 40vh), 30rem);
  --logo-clear: calc(var(--logo-w) / 5.9324 / 3);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --field-bleed: 64px;
}


/* ---------------------------------------------------------------------------
   4. Which artwork each theme shows
   The logotype ships one file per ground: Cloud White letters for the dark
   theme, Midnight Indigo letters for the light one. The two 'a' counters keep
   their gradient in both, exactly as the Reverse variation does.
   --------------------------------------------------------------------------- */

:root:not([data-theme="light"]) .on-light { display: none; }
:root[data-theme="light"]       .on-dark  { display: none; }


/* ---------------------------------------------------------------------------
   5. Page
   --------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;

  /* On `html`, not on `body`. `overflow-x: hidden` computes the other axis to
     `auto`, which turns whichever element carries it into a scroll container —
     and a `position: sticky` descendant then sticks to a box that never
     scrolls, which is to say it stops working. Declared here it propagates to
     the viewport instead and leaves `body` alone, so the services header
     sticks. */
  overflow-x: hidden;
}

body {
  margin: 0;
  min-height: 100svh;

  background-color: var(--ground);
  color: var(--ink);

  font-family: var(--font);
  font-weight: 400;
  line-height: 1.5;
}

/* Everything above is shared by all three pages. What differs is only how the
   page is laid out: the holding page centres one panel on one screen, while
   the services and about pages stack a header, a scrolling body and a footer.
   Both of those carry `.page--inner`. */

.page--home {
  display: grid;
  place-items: center;
  padding: calc(var(--y) * 3) calc(var(--x) * 3);
}

.page--inner {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
}

::selection { background: var(--accent); color: var(--ground); }

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

.u-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ---------------------------------------------------------------------------
   6. The gradient field

   The old identity had the lattice; this one has the gradient, so that is what
   the background is made of. Three soft blooms — Cyber Purple, Pulse Blue,
   Signal Teal, the three colours of the palette's gradient bar — drifting
   slowly behind the panel on long, mismatched cycles so the field never
   repeats a pose.

   Bled past the viewport on every side, because it moves: at exactly viewport
   size the drift would pull a bare edge into view.
   --------------------------------------------------------------------------- */

.field {
  position: fixed;
  inset: calc(var(--field-bleed) * -1);
  z-index: 0;
  pointer-events: none;
  overflow: hidden;

  translate: var(--px, 0px) var(--py, 0px);
  transition: translate 1.4s var(--ease-out);

  animation: field-in 1.6s var(--ease-out) backwards;
}

@keyframes field-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.bloom {
  position: absolute;
  inline-size: 78vmax;
  block-size: 78vmax;
  border-radius: 50%;
  will-change: transform;
}

.bloom--purple {
  background: radial-gradient(circle closest-side, var(--purple) 0%, transparent 70%);
  opacity: 0.42;
  inset-block-start: -26vmax;
  inset-inline-start: -22vmax;
  animation: drift-a 34s ease-in-out infinite;
}

.bloom--blue {
  background: radial-gradient(circle closest-side, var(--blue) 0%, transparent 70%);
  opacity: 0.30;
  inset-block-start: 6vmax;
  inset-inline-start: 34vmax;
  animation: drift-b 43s ease-in-out infinite;
}

.bloom--teal {
  background: radial-gradient(circle closest-side, var(--teal) 0%, transparent 70%);
  opacity: 0.26;
  inset-block-end: -30vmax;
  inset-inline-end: -18vmax;
  animation: drift-c 51s ease-in-out infinite;
}

/* Softer on Cloud White — the same strengths that read as a glow on Midnight
   Indigo turn muddy on a light ground. */
:root[data-theme="light"] .bloom--purple { opacity: 0.20; }
:root[data-theme="light"] .bloom--blue   { opacity: 0.16; }
:root[data-theme="light"] .bloom--teal   { opacity: 0.20; }

@keyframes drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33%      { transform: translate3d(14vmax, 8vmax, 0) scale(1.12); }
  66%      { transform: translate3d(5vmax, 18vmax, 0) scale(0.94); }
}
@keyframes drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  40%      { transform: translate3d(-18vmax, 12vmax, 0) scale(1.16); }
  70%      { transform: translate3d(-8vmax, -10vmax, 0) scale(0.9); }
}
@keyframes drift-c {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  45%      { transform: translate3d(-16vmax, -14vmax, 0) scale(1.1); }
  75%      { transform: translate3d(10vmax, -6vmax, 0) scale(0.92); }
}


/* ---------------------------------------------------------------------------
   7. Panel
   Filled with the ground colour so the blooms read around the composition
   rather than behind the type, and edged with a single hairline.

   Tracks are pinned to minmax(0, 1fr) here and on every grid down to the logo.
   An auto track sizes to its content, which would let the logo widen the very
   box its own max-inline-size resolves against — and push past the panel edge
   on a narrow screen.
   --------------------------------------------------------------------------- */

.panel {
  position: relative;
  z-index: 1;

  inline-size: min(46rem, 100%);
  padding: calc(var(--y) * 6) calc(var(--x) * 5);

  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: calc(var(--x) * 2);

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: calc(var(--y) * 5);

  animation: panel-in 1s var(--ease-out) backwards;
}

@keyframes panel-in {
  from { opacity: 0; transform: translateY(calc(var(--y) * 2)); }
  to   { opacity: 1; transform: none; }
}


/* ---------------------------------------------------------------------------
   8. Hero
   --------------------------------------------------------------------------- */

.hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
  inline-size: 100%;
  gap: calc(var(--y) * 2);
}

.hero__title {
  margin: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  inline-size: 100%;
  gap: calc(var(--y) * 2);
}

.mark {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-items: center;

  inline-size: var(--logo-w);
  margin: var(--logo-clear);
  max-inline-size: calc(100% - 2 * var(--logo-clear));

  transition: scale 0.6s var(--ease-out);
  animation: mark-in 1.1s var(--ease-out) 0.3s backwards;
}

.mark:hover { scale: 1.02; }

@keyframes mark-in {
  from { opacity: 0; transform: translateY(calc(var(--y) * 3)) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

/* Both ground variants share one cell, so swapping theme cannot shift layout.
   The aspect ratio is the measured artwork, declared so the box is reserved
   before the SVG arrives. */
.logo {
  grid-area: 1 / 1;
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 304.0345 / 51.2496;
}


/* ---------------------------------------------------------------------------
   9. The line

   The new identity has no Arabic logotype — the Option 2 variations are Latin
   only — so the mark stays put and the line carries both readings instead.
   Tajawal sets Latin and Arabic alike, so this is one typeface switching
   script, not two fonts swapping over.
   --------------------------------------------------------------------------- */

.display {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  align-items: center;
  margin: 0;

  animation: rise-in 0.9s var(--ease-out) 0.75s backwards;
}

.line {
  grid-area: 1 / 1;
  color: var(--ink);

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  gap: calc(var(--y) * 0.6);
}

.line--en {
  font-weight: 700;
  font-size: var(--fs-display);
  letter-spacing: -0.01em;
  line-height: 1.2;
}

/* Arabic sets a touch larger for matched optical weight, and is never
   letter-spaced — tracking breaks the joins between letters. */
.line--ar {
  font-weight: 700;
  font-size: calc(var(--fs-display) * 1.06);
  letter-spacing: 0;
  line-height: 1.4;
}

@keyframes rise-in {
  from { opacity: 0; transform: translateY(calc(var(--y) * 2)); }
  to   { opacity: 1; transform: none; }
}

/* The ellipsis: nothing, one dot, two, three, repeat — centred under the word.

   Three complete strings stacked in one grid cell, not three dots in a row.
   That is what keeps it centred at every step: each string is centred in the
   same cell, so it grows outward from the middle rather than filling from a
   left edge. The cell is as wide as the widest, so nothing reflows.

   It cycles on `visibility`, not `display` — an element hidden that way cannot
   run the animation meant to bring it back. Every shown state is
   `visibility: inherit`, never `visible`: visibility inherits, but a descendant
   declaring `visible` overrides a hidden ancestor and paints anyway, which
   would leave the switched-off line's dots blinking beside the other script. */

.dots {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  font-size: 0.62em;
  line-height: 1;
  direction: ltr;          /* full stops are neutral; pin them so RTL cannot
                              flip the trailing letter-space to the far side */
  color: var(--accent);
}

.dots__n {
  grid-area: 1 / 1;
  white-space: nowrap;
  letter-spacing: 0.3em;
  margin-inline-end: -0.3em;    /* take back the space after the last dot */
  visibility: hidden;
}

.dots__n--3 { visibility: inherit; }

/* 2.5s — a quarter of the shutter's 10s and half of each script's turn, with
   the same 1.5s delay, so every script arrives with no dots under it and
   cycles the full run twice before handing over. */
.dots__n--1 { animation: dots-one   2.5s steps(1, end) 1.5s infinite backwards; }
.dots__n--2 { animation: dots-two   2.5s steps(1, end) 1.5s infinite backwards; }
.dots__n--3 { animation: dots-three 2.5s steps(1, end) 1.5s infinite backwards; }

@keyframes dots-one   { 0% { visibility: hidden; } 25% { visibility: inherit; } 50% { visibility: hidden; } 100% { visibility: hidden; } }
@keyframes dots-two   { 0% { visibility: hidden; } 50% { visibility: inherit; } 75% { visibility: hidden; } 100% { visibility: hidden; } }
@keyframes dots-three { 0% { visibility: hidden; } 75% { visibility: inherit; } 100% { visibility: hidden; } }


/* ---------------------------------------------------------------------------
   10. The shutter — on the line only

   Three changes per handover and no more: WCAG 2.3.1 permits at most three
   flashes in any one-second window. Each handover finishes on an exact
   half-cycle, 50% and 100%, which is what keeps the ellipsis phase-locked to
   it. Under prefers-reduced-motion the whole thing stops on the Latin reading.
   --------------------------------------------------------------------------- */

.line--ar { visibility: hidden; }

.line--en { animation: shutter-a 10s steps(1, end) 1.5s infinite backwards; }
.line--ar { animation: shutter-b 10s steps(1, end) 1.5s infinite backwards; }

@keyframes shutter-a {
  0%     { visibility: visible; }
  48.2%  { visibility: hidden; }
  49.1%  { visibility: visible; }
  50%    { visibility: hidden; }
  98.2%  { visibility: visible; }
  99.1%  { visibility: hidden; }
  100%   { visibility: visible; }
}

@keyframes shutter-b {
  0%     { visibility: hidden; }
  48.2%  { visibility: visible; }
  49.1%  { visibility: hidden; }
  50%    { visibility: visible; }
  98.2%  { visibility: hidden; }
  99.1%  { visibility: visible; }
  100%   { visibility: hidden; }
}


/* ---------------------------------------------------------------------------
   11. Controls — the outline button

   Full strength: a dimmed control is both a contrast failure and, on the light
   ground, genuinely hard to read. `.control` is the theme switch, `.ghost` the
   same shape as a link.
   --------------------------------------------------------------------------- */

.control,
.ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: calc(var(--x) * 0.9);

  font-family: var(--font);
  font-weight: 500;
  font-size: var(--fs-control);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;

  color: var(--accent);

  appearance: none;
  background: none;
  border: 1px solid var(--rule);
  border-radius: calc(var(--x) * 0.75);
  cursor: pointer;
  padding: calc(var(--y) * 1.1) calc(var(--x) * 2.75);

  transition: border-color 0.35s var(--ease-out);
}

.control:hover,
.control:focus-visible,
.ghost:hover,
.ghost:focus-visible { border-color: var(--accent); }

/* Hidden until script confirms it can do anything. The class is set by the
   head script before first paint, so this never flashes. `.ghost` is a plain
   link and stays put with or without script. */
.control { display: none; }
html.js .control { display: inline-flex; }

/* The row on the holding page arrives left to right: the services pill, then
   About, then the theme switch. */
.page--home .ghost   { animation: rise-in 0.9s var(--ease-out) 1.15s backwards; }
.page--home .control { animation: rise-in 0.9s var(--ease-out) 1.25s backwards; }


/* ---------------------------------------------------------------------------
   12. The call to action

   One pill, two pages: "Our services" on the holding page, "Back to home" on
   the other. It carries its own ground — Cyber Purple to Pulse Blue — so it is
   the one element that does not change between themes, and does not need to:
   Cloud White on that ramp measures 5.2:1 at its lightest point and 6.8:1 at
   its darkest, past AA on both.

   The sheen is a slow sweep rather than a pulse. It reads as a highlight
   catching an edge, and at one pass every seven seconds it is nowhere near
   WCAG 2.3.1's flash threshold.
   --------------------------------------------------------------------------- */

.cta {
  position: relative;
  overflow: hidden;
  isolation: isolate;

  display: inline-flex;
  align-items: center;
  gap: calc(var(--x) * 1.4);

  /* Same vertical padding and same border width as `.control`, so the two sit
     in a row at matching heights. A transparent border rather than none: with
     `box-sizing: border-box` the border is part of the height either way, and
     dropping it here would leave the pill 2px shorter than the button. */
  padding: calc(var(--y) * 1.1) calc(var(--x) * 3.25);
  border: 1px solid transparent;
  border-radius: 999px;

  background-image: linear-gradient(100deg, var(--purple), var(--blue));
  color: var(--white);
  text-decoration: none;

  font-family: var(--font);
  font-weight: 500;
  font-size: var(--fs-control);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;

  transition: transform 0.45s var(--ease-out), box-shadow 0.45s var(--ease-out);
}

.cta:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 34px -12px var(--blue);
}

.cta__arrow {
  font-size: 1.15em;
  line-height: 0;
  transition: translate 0.4s var(--ease-out);
}

.cta:hover .cta__arrow { translate: 5px 0; }

/* Sent the other way on the services page, because it points home. */
.cta--back .cta__arrow { rotate: 180deg; }
.cta--back:hover .cta__arrow { translate: -5px 0; }

.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  /* 8%, and that ceiling is a contrast budget rather than a taste call. The
     sheen passes over the label, so at its brightest instant it *is* the
     label's background. At 30% it took Cloud White on the ramp down to
     3.09:1 — under AA for the moment it crossed. At 8% the worst instant
     measures 4.57:1, so the label never drops below AA at any point in the
     sweep. Faint, and still plainly visible in motion. */
  background-image: linear-gradient(100deg,
    transparent 34%, rgba(245, 247, 255, 0.08) 50%, transparent 66%);
  translate: -110% 0;
  animation: sheen 7s var(--ease-out) 2.4s infinite;
}

@keyframes sheen {
  0%        { translate: -110% 0; }
  18%, 100% { translate:  110% 0; }
}

.page--home .cta { animation: rise-in 0.9s var(--ease-out) 1.05s backwards; }

/* The row under the hero. Centred, and it wraps rather than squeezing — the
   theme control is absent without script, and the row has to look deliberate
   either way. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: calc(var(--y) * 1.6) calc(var(--x) * 2);
}

/* Every entrance above fills `backwards`, never `both`, so the resting page is
   the plain un-animated page and a reveal can only fail *open*. If an
   animation never runs, the content is simply already there. */


/* ---------------------------------------------------------------------------
   13. The services page — shell, header, footer

   Same foundation as the holding page: same field, same palette, same three
   faces. What is added is a scroll.
   --------------------------------------------------------------------------- */

.shell {
  position: relative;
  z-index: 1;
  inline-size: min(74rem, 100%);
  margin-inline: auto;
  padding-inline: calc(var(--x) * 4);
}

/* Sticky, and translucent so the blooms keep moving behind it. The blur is on
   one element rather than on nineteen cards — the effect is worth its cost
   here and would not be there. */
.topbar {
  position: sticky;
  inset-block-start: 0;
  z-index: 3;

  background-color: var(--bar);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-block-end: 1px solid var(--rule);
}

/* Wraps, and has to. Below about 360px the logotype and the two controls do
   not fit on one line, and without a wrap the header sets a floor on the
   document width — which is horizontal overflow on the whole page, not just a
   crowded header. Wrapping costs one row on the narrowest phones and nothing
   anywhere else. */
.topbar__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--y) * 1.2) calc(var(--x) * 2);
  padding-block: calc(var(--y) * 1.6);
}

.topbar__brand {
  display: block;
  inline-size: clamp(7.5rem, 18vw, 10.5rem);
  flex: 0 1 auto;
  border-radius: calc(var(--x) * 0.5);
  transition: opacity 0.35s var(--ease-out);
}

.topbar__brand:hover { opacity: 0.78; }

.topbar__nav {
  display: flex;
  align-items: center;
  gap: calc(var(--x) * 1.5);
  flex: 0 0 auto;
}

.page--inner .control,
.page--inner .ghost { animation: rise-in 0.8s var(--ease-out) 0.15s backwards; }

.foot {
  position: relative;
  z-index: 1;
  border-block-start: 1px solid var(--rule);
  margin-block-start: calc(var(--y) * 6);
}

.foot__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--y) * 2.5) calc(var(--x) * 3);
  padding-block: calc(var(--y) * 5);
}

.foot__note {
  margin: 0;
  color: var(--ink-soft);
  font-size: var(--fs-small);
  letter-spacing: 0.02em;
}


/* ---------------------------------------------------------------------------
   14. The services page — the opening
   --------------------------------------------------------------------------- */

.intro {
  padding-block: calc(var(--y) * 9) calc(var(--y) * 7);
  max-inline-size: 46rem;
}

.eyebrow {
  margin: 0 0 calc(var(--y) * 2);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);

  animation: rise-in 0.9s var(--ease-out) 0.1s backwards;
}

.eyebrow__ar {
  font-family: var(--font);
  letter-spacing: 0;
  text-transform: none;
}

.intro__title {
  margin: 0;
  font-size: var(--fs-title);
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: -0.02em;
  text-wrap: balance;

  animation: rise-in 1s var(--ease-out) 0.22s backwards;
}

.intro__lede {
  margin: calc(var(--y) * 3) 0 0;
  font-size: var(--fs-lede);
  line-height: 1.6;
  color: var(--ink-soft);
  text-wrap: pretty;

  animation: rise-in 1s var(--ease-out) 0.36s backwards;
}


/* ---------------------------------------------------------------------------
   15. The services page — a practice and its cards

   Four practices, and the palette has three accents that read on both grounds.
   So the key is not one colour each but a different span of the brand gradient
   each: purple to blue, blue to teal, teal to purple, and the full ramp for the
   last. Four distinguishable marks out of three colours, and every one of them
   is the identity's own gradient.

   The gradient is decorative throughout — the rule, the numeral. The names and
   descriptions are set in ink, so nothing that carries meaning depends on it.
   --------------------------------------------------------------------------- */

.practice { padding-block: calc(var(--y) * 4); }

.practice--mssp  { --grad: linear-gradient(90deg, var(--purple), var(--blue)); --tone: var(--purple); }
.practice--vapt  { --grad: linear-gradient(90deg, var(--blue), var(--teal));   --tone: var(--blue); }
.practice--aware { --grad: linear-gradient(90deg, var(--teal), var(--purple)); --tone: var(--teal); }
.practice--grc   { --grad: linear-gradient(90deg, var(--purple), var(--blue), var(--teal)); --tone: var(--blue); }

.practice__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: baseline;
  column-gap: calc(var(--x) * 2.5);
  row-gap: calc(var(--y) * 0.8);
  margin-block-end: calc(var(--y) * 4);
}

.practice__index {
  grid-row: 1 / span 2;
  align-self: center;

  font-size: var(--fs-index);
  font-weight: 700;
  line-height: 0.9;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;

  color: var(--tone);
}

/* Painted with the practice's own span of the gradient where the browser can
   do it, and left as the flat tone where it cannot. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .practice__index {
    background-image: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.practice__title {
  margin: 0;
  font-size: var(--fs-practice);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.practice__abbr {
  display: inline-block;
  margin-inline-start: calc(var(--x) * 1.4);
  font-size: 0.52em;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent);
  vertical-align: middle;
}

/* `dir="rtl"` is right for the text and wrong for the box: it also makes the
   paragraph's default alignment `right`, which strands the Arabic at the far
   edge of a wide column, nowhere near the heading it belongs to. Pinning the
   alignment left keeps the bidi ordering correct and sets the line under its
   own title. */
.practice__ar {
  margin: 0;
  text-align: left;
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.7;
  color: var(--ink-soft);
}

/* Shared with the about page's sections — same device, same gradient, same
   left-anchored draw. */
.practice__rule,
.sec__rule {
  block-size: 2px;
  border-radius: 2px;
  background-image: var(--grad);
  margin-block-end: calc(var(--y) * 3.5);
  transform-origin: 0 50%;
}

.cards {
  list-style: none;
  margin: 0;
  padding: 0;

  display: grid;
  /* auto-fill, not auto-fit. auto-fit collapses the empty tracks, so the
     two-card practice would stretch its pair to the full width and read as
     more important than the five-card ones. Keeping the tracks keeps every
     card on the page the same size. */
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr));
  gap: calc(var(--y) * 2.5) calc(var(--x) * 2.5);
}

.card {
  position: relative;
  padding: calc(var(--y) * 3.2) calc(var(--x) * 3);

  background-color: var(--card);
  border: 1px solid var(--rule);
  border-radius: calc(var(--x) * 1.5);

  transition:
    border-color 0.45s var(--ease-out),
    background-color 0.45s var(--ease-out),
    transform 0.45s var(--ease-out);
}

/* The tone arrives on hover as a hairline down the leading edge — the only
   place a card differs from its neighbours, so nothing competes at rest. */
.card::before {
  content: "";
  position: absolute;
  inset-block: calc(var(--y) * 3.2);
  inset-inline-start: -1px;
  inline-size: 2px;
  border-radius: 2px;
  background-color: var(--tone);
  opacity: 0;
  transition: opacity 0.45s var(--ease-out);
}

.card:hover {
  background-color: var(--card-hi);
  border-color: var(--rule-hi);
  transform: translateY(-3px);
}

.card:hover::before { opacity: 1; }

.card__name {
  margin: 0;
  font-size: var(--fs-card);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.005em;
  text-wrap: balance;
}

.card__tag {
  display: inline-block;
  margin-inline-start: calc(var(--x) * 1);
  padding: 0.25em 0.7em;
  border: 1px solid var(--rule-hi);
  border-radius: 999px;

  font-size: 0.62em;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
  vertical-align: middle;
  white-space: nowrap;
}

.card__text {
  margin: calc(var(--y) * 1.4) 0 0;
  font-size: var(--fs-body);
  line-height: 1.65;
  color: var(--ink-soft);
  text-wrap: pretty;
}


/* ---------------------------------------------------------------------------
   16. Arriving on scroll

   No script and no observer: a view() timeline, which the browser drives off
   the scroll position directly. Wrapped in @supports so a browser without it
   simply gets the finished page — and the hidden opening state lives only
   inside the keyframes, never on the element, so removing the animation for
   any reason leaves the content visible rather than stranding it at zero.

   The stagger is done with three ranges rather than three delays: a
   scroll-driven animation has no time axis for `animation-delay` to act on.
   --------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* Longhands, not the shorthand. `animation: linear both` would reset
       `animation-duration` to its initial value, and a scroll-driven animation
       needs `auto` there — that is what makes it span its range instead of
       finishing instantly. */
    .practice__head,
    .practice__rule,
    .card,
    .roots,
    .sec__head,
    .sec__rule,
    .pillar,
    .statement,
    .reach__item,
    .social {
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
    }

    .practice__head,
    .sec__head  { animation-name: lift-in; animation-range: entry 8% entry 62%; }
    .practice__rule,
    .sec__rule  { animation-name: draw;    animation-range: entry 12% entry 78%; }

    /* The whole name-breakdown moves as one piece. Animating the three roots
       separately would leave the `+` signs between them standing still. */
    .roots      { animation-name: lift-in; animation-range: entry 5% entry 55%; }
    .statement  { animation-name: lift-in; animation-range: entry 8% entry 62%; }
    .social     { animation-name: lift-in; animation-range: entry 8% entry 62%; }

    .card,
    .pillar,
    .reach__item { animation-name: lift-in; }

    .card:nth-child(3n + 1),
    .pillar:nth-child(3n + 1),
    .reach__item:nth-child(3n + 1) { animation-range: entry 6%  entry 58%; }
    .card:nth-child(3n + 2),
    .pillar:nth-child(3n + 2),
    .reach__item:nth-child(3n + 2) { animation-range: entry 10% entry 64%; }
    .card:nth-child(3n + 3),
    .pillar:nth-child(3n + 3),
    .reach__item:nth-child(3n + 3) { animation-range: entry 14% entry 70%; }

    @keyframes lift-in {
      from { opacity: 0; transform: translateY(calc(var(--y) * 2.5)); }
      to   { opacity: 1; transform: none; }
    }

    @keyframes draw {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }
  }
}


/* ---------------------------------------------------------------------------
   17. The about page — the name, taken apart

   amantx = aman + t + x. The three segments are set in the identity's own
   gradient, and the words under them in full-strength ink — so the colour is
   decoration and the meaning is never carried by it. The segments are
   aria-hidden in the markup for the same reason.

   The `+` is sized to exactly the height of the segment line and centres its
   own content, so it lands on the optical middle of the big type rather than
   the middle of the whole three-line column.
   --------------------------------------------------------------------------- */

.roots {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: center;
  gap: calc(var(--y) * 2) calc(var(--x) * 3.5);
  padding-block: calc(var(--y) * 3) calc(var(--y) * 7);
}

.root--1 { --grad: linear-gradient(100deg, var(--purple), var(--blue)); }
.root--2 { --grad: linear-gradient(100deg, var(--blue), var(--teal)); }
.root--3 { --grad: linear-gradient(100deg, var(--teal), var(--purple)); }

.root {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
  text-align: center;
  gap: calc(var(--y) * 0.7);
}

.root__seg {
  font-size: var(--fs-root);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--accent);
}

@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .root__seg {
    background-image: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}

.root__word {
  font-size: var(--fs-card);
  font-weight: 700;
  line-height: 1.2;
}

/* The letter each word lends to the name. */
.root__cap { color: var(--accent); }

.root__ar {
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--ink-soft);
}

.root__op {
  align-self: flex-start;
  block-size: var(--fs-root);     /* the segment sets line-height: 1 */
  display: flex;
  align-items: center;

  font-size: var(--fs-practice);
  font-weight: 400;
  color: var(--ink-soft);
}


/* ---------------------------------------------------------------------------
   18. The about page — sections, pillars and statements

   The same shape as a practice on the services page — a head, a gradient rule,
   then content — minus the numeral. Four sections, four different spans of the
   brand gradient, exactly as over there.
   --------------------------------------------------------------------------- */

.sec { padding-block: calc(var(--y) * 4); }

.sec--a { --grad: linear-gradient(90deg, var(--purple), var(--blue)); --tone: var(--purple); }
.sec--b { --grad: linear-gradient(90deg, var(--blue), var(--teal));   --tone: var(--blue); }
.sec--c { --grad: linear-gradient(90deg, var(--teal), var(--purple)); --tone: var(--teal); }
.sec--d { --grad: linear-gradient(90deg, var(--purple), var(--blue), var(--teal)); --tone: var(--blue); }
.sec--e { --grad: linear-gradient(90deg, var(--blue), var(--purple)); --tone: var(--purple); }

.sec__head { margin-block-end: calc(var(--y) * 2.5); }

.sec__label {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--x) * 1.75);

  font-size: var(--fs-practice);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
}

.sec__ar {
  font-size: 0.6em;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--ink-soft);
}

/* Explicit column counts rather than auto-fill, because both of these hold
   exactly four items. auto-fill sizes to whatever happens to fit and at most
   widths that is three — leaving one card alone on a second row. Stepping
   1 -> 2 -> 4 keeps them balanced at every width; the contact list stops at
   two, because the office address needs the wider column. */
.pillars,
.reach {
  list-style: none;
  margin: 0;
  padding: 0;

  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: calc(var(--y) * 2.5) calc(var(--x) * 2.5);
}

@media (min-width: 34rem) {
  .pillars,
  .reach { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 64rem) {
  .pillars { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.pillar {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: calc(var(--y) * 0.7);
  padding: calc(var(--y) * 3.2) calc(var(--x) * 3);

  background-color: var(--card);
  border: 1px solid var(--rule);
  border-radius: calc(var(--x) * 1.5);
}

.pillar__en {
  font-size: var(--fs-card);
  font-weight: 700;
  line-height: 1.3;
  text-wrap: balance;
}

/* Left, not the RTL default: these are single phrases sitting under an English
   line, and flush right would strand them at the far edge of the card. The
   bidi ordering of the text itself is untouched. */
.pillar__ar {
  text-align: left;
  font-size: var(--fs-small);
  font-weight: 500;
  color: var(--ink-soft);
}

/* Arabic original and English translation side by side, each starting at its
   own edge — Arabic flush right, English flush left. Stacking them would force
   one of the two to be set against its own reading direction. */
.statement {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: calc(var(--y) * 2.5) calc(var(--x) * 4);
  align-items: start;
}

.statement__ar {
  grid-area: 1 / 2;
  margin: 0;
  text-align: right;

  font-size: var(--fs-statement);
  font-weight: 700;
  line-height: 1.95;        /* Arabic needs the extra leading */
}

.statement__en {
  grid-area: 1 / 1;
  margin: 0;
  font-size: var(--fs-lede);
  line-height: 1.7;
  color: var(--ink-soft);
  text-wrap: pretty;
}


/* ---------------------------------------------------------------------------
   19. The about page — contact

   No form, and that is the design rather than a shortcut: a form needs
   somewhere to post to, and `form-action 'none'` blocks submission outright.
   Every value is shown in full as text, so it is readable and copyable whether
   or not the visitor has a mail client or a dialler; the link on top of it is
   a convenience, not the only way through.
   --------------------------------------------------------------------------- */

.reach__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-content: start;
  gap: calc(var(--y) * 0.9);
  padding: calc(var(--y) * 3.2) calc(var(--x) * 3);

  background-color: var(--card);
  border: 1px solid var(--rule);
  border-radius: calc(var(--x) * 1.5);

  transition:
    border-color 0.45s var(--ease-out),
    background-color 0.45s var(--ease-out);
}

.reach__item:hover {
  background-color: var(--card-hi);
  border-color: var(--rule-hi);
}

.reach__label {
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.reach__value {
  font-size: var(--fs-card);
  font-weight: 700;
  line-height: 1.45;
  color: var(--ink);
  text-decoration: none;
  overflow-wrap: anywhere;   /* an address should never widen the card */
}

a.reach__value:hover,
a.reach__value:focus-visible {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.22em;
}

.reach__value--static {
  font-size: var(--fs-body);
  font-weight: 500;
  line-height: 1.6;
}

.reach__sub {
  margin: calc(var(--y) * 4.5) 0 calc(var(--y) * 2);
  font-size: var(--fs-small);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
}

.social {
  list-style: none;
  margin: 0;
  padding: 0;

  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--y) * 1.4) calc(var(--x) * 1.5);
}

.social__link {
  display: inline-flex;
  align-items: baseline;
  gap: calc(var(--x) * 1.1);

  padding: calc(var(--y) * 1.15) calc(var(--x) * 2.5);
  border: 1px solid var(--rule);
  border-radius: 999px;
  text-decoration: none;

  transition:
    border-color 0.35s var(--ease-out),
    background-color 0.35s var(--ease-out);
}

.social__link:hover,
.social__link:focus-visible {
  border-color: var(--accent);
  background-color: var(--card);
}

.social__net {
  font-size: var(--fs-body);
  font-weight: 700;
  color: var(--ink);
}

.social__handle {
  font-size: var(--fs-small);
  color: var(--ink-soft);
}


/* ---------------------------------------------------------------------------
   20. Narrow viewports
   --------------------------------------------------------------------------- */

@media (max-width: 40rem) {
  .page--home { padding: calc(var(--y) * 2) calc(var(--x) * 2); }

  .panel {
    padding: calc(var(--y) * 5) calc(var(--x) * 3);
    gap: calc(var(--y) * 4);
  }

  .shell { padding-inline: calc(var(--x) * 2.5); }

  .intro { padding-block: calc(var(--y) * 6) calc(var(--y) * 5); }

  /* The numeral moves above the title rather than beside it — at this width a
     side-by-side head leaves the practice name in a two-word column. */
  .practice__head {
    grid-template-columns: minmax(0, 1fr);
    row-gap: calc(var(--y) * 1.2);
  }

  .practice__index { grid-row: auto; }

  .card,
  .pillar,
  .reach__item { padding: calc(var(--y) * 2.8) calc(var(--x) * 2.5); }

  .foot__inner { justify-content: center; text-align: center; }

  /* The name stacks, and the `+` goes back to sitting between the pieces
     rather than beside them. */
  .roots {
    flex-direction: column;
    align-items: center;
    gap: calc(var(--y) * 1.4);
  }

  .root__op {
    align-self: center;
    block-size: auto;
  }

  /* One column, and the explicit grid areas have to go with it — otherwise
     both statements would keep fighting over row 1. Arabic first, as authored. */
  .statement { grid-template-columns: minmax(0, 1fr); }

  .statement__ar,
  .statement__en { grid-area: auto; }
}

/* Narrower than this the header has wrapped to two rows, so centre both rather
   than leaving the controls hanging off one edge. */
@media (max-width: 23rem) {
  .topbar__inner { justify-content: center; }
  .topbar__nav   { gap: calc(var(--x) * 1.2); }
}


/* ---------------------------------------------------------------------------
   21. Reduced motion
   `animation: none`, not a near-zero duration: the entrances fill `backwards`,
   so a shortened duration would still hold each hidden opening frame for the
   length of its delay.
   --------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0.001ms !important;
  }

  .field { translate: none; transition: none; }
}
