/* --- fonts.css --- */
/* ---------------------------------------------------------------------------
   Self-hosted webfonts.

   These were served from fonts.googleapis.com until 2026-08-19. Self-hosting
   removes two DNS lookups and two TLS handshakes from the critical path, and
   the stylesheet that declared them was itself a render-blocking request to a
   third party.

   Only the latin and latin-ext subsets are here. Google also serves cyrillic
   and vietnamese cuts of both faces; nothing on this site needs them, and
   Arabic will bring its own face rather than a subset of these.

   `font-display: swap` throughout: text is readable in the fallback
   immediately and reflows once, rather than being invisible while a face
   downloads. Archivo is the fallback-metric-closest to Helvetica/Arial, which
   is what the stack falls back to.

   Regenerate by re-running the download in docs/DESIGN-IMPORT.md. Do not
   hand-edit.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url('/static/build/fonts/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url('/static/build/fonts/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/build/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/build/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/build/fonts/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/static/build/fonts/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------------------------------------------------------------------------
   The design's four families.

   DELIBERATE OVERRIDE. claude-code-prompt.md says these are stand-ins and its
   release checklist fails the build if either Bricolage Grotesque or DM Sans
   appears. The client has asked three times for the page to match the design
   exactly and has confirmed that overrides the brief, so they ship. See
   docs/DESIGN-IMPORT.md; take it up with whoever owns the prompt before
   reverting again.

   Variable rather than the static weights Google serves by default: three
   static cuts of Bricolage are 225 KB against 75 KB for the one variable file
   covering 200-800. Four families come to 768 KB static, 372 KB variable.

   Only the subsets this site renders — latin and latin-ext for the three
   latin faces, arabic for Noto Kufi, whose unicode-range means an English
   reader never fetches its 121 KB.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/static/build/fonts/bricolage-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Bricolage Grotesque';
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url('/static/build/fonts/bricolage-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('/static/build/fonts/dm-sans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 100 1000;
  font-display: swap;
  src: url('/static/build/fonts/dm-sans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('/static/build/fonts/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url('/static/build/fonts/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Noto Kufi Arabic';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/static/build/fonts/noto-kufi-arabic-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}

/* --- tokens.css --- */
/* ---------------------------------------------------------------------------
   Design tokens — extracted from the Claude Design source
   ("Mubarmij V1.0": MubarmiJ Home.dc.html, MubarmiJ Case Studies.dc.html).

   Every literal colour, size and easing in those two files resolves to a token
   here. Nothing downstream should hardcode a hex value.
   --------------------------------------------------------------------------- */

:root {
  /* -- Colour, as RGB triplets so alpha variants stay one source of truth -- */
  --mj-bg-rgb: 10 22 40;         /* #0A1628 page ground — the design's exact   */
  --mj-ink-rgb: 242 239 232;     /* #F2EFE8 primary text, warm not blue        */
  --mj-gold-rgb: 212 162 76;     /* #D4A24C accent — the design's exact        */
  --mj-blue-rgb: 40 70 140;      /* ambient glow only                         */

  --mj-bg: rgb(var(--mj-bg-rgb));
  --mj-surface: #122542;         /* cards, chips and pills — the design's     */
  --mj-surface-2: #16294A;       /* device mock body, one step above surface  */
  --mj-ink: rgb(var(--mj-ink-rgb));
  --mj-gold: rgb(var(--mj-gold-rgb));
  --mj-gold-hi: #E4BE7C;         /* link hover, the same lift above the gold  */

  /* Text steps. Named by role, not by opacity, so intent survives a retune.

     `--mj-muted` is a colour, not a step: the design's #94A3B8 is a cool
     slate and the text it sits beside is warm cream, so no alpha of one
     produces the other. Measured at 7.07:1 on the ground and 5.98:1 on a
     card, both clear of AA. */
  --mj-muted: #94A3B8;
  --mj-ink-strong: rgb(var(--mj-ink-rgb) / 1);
  --mj-ink-body: var(--mj-muted);
  --mj-ink-muted: var(--mj-muted);
  --mj-ink-dim: rgb(var(--mj-ink-rgb) / 0.45);
  --mj-ink-faint: rgb(var(--mj-ink-rgb) / 0.4);

  /* Hairlines and fills. The source leans on 1px gaps in a tinted grid to
     draw its tables, so the "line" tokens double as grid backgrounds. */
  --mj-line: rgb(var(--mj-ink-rgb) / 0.12);
  --mj-line-soft: rgb(var(--mj-ink-rgb) / 0.08);
  --mj-line-strong: rgb(var(--mj-ink-rgb) / 0.2);
  --mj-fill: rgb(var(--mj-ink-rgb) / 0.02);
  --mj-fill-raised: rgb(var(--mj-ink-rgb) / 0.04);

  --mj-gold-line: rgb(var(--mj-gold-rgb) / 0.45);
  --mj-gold-line-hi: rgb(var(--mj-gold-rgb) / 0.6);
  --mj-gold-wash: rgb(var(--mj-gold-rgb) / 0.05);
  --mj-gold-wash-hi: rgb(var(--mj-gold-rgb) / 0.08);
  --mj-gold-glow: 0 16px 44px rgb(var(--mj-gold-rgb) / 0.32);
  --mj-gold-glow-sm: 0 10px 30px rgb(var(--mj-gold-rgb) / 0.35);

  --mj-header-bg: rgb(var(--mj-bg-rgb) / 0.72);

  /* -- Type -- */
  --mj-font: Archivo, "Helvetica Neue", Helvetica, Arial, sans-serif;
  --mj-font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Archivo is a variable font; the source uses the width axis for display
     type. Kept as tokens so the fallback stack degrades predictably. */
  --mj-wdth-display: 120;
  --mj-wdth-heading: 118;
  --mj-wdth-sub: 116;
  --mj-wdth-wordmark: 115;

  --mj-size-hero: clamp(44px, 7.4vw, 104px);
  --mj-size-page-title: clamp(40px, 6.4vw, 88px);
  --mj-size-h2-lg: clamp(36px, 5.6vw, 76px);
  --mj-size-h2: clamp(32px, 4vw, 54px);
  --mj-size-h3: clamp(21px, 3.4vw, 26px);
  --mj-size-stat: clamp(32px, 7vw, 46px);
  --mj-size-lede: clamp(16px, 2.2vw, 19px);
  --mj-size-body: 16px;
  --mj-size-sm: 15px;
  --mj-size-xs: 14px;
  --mj-size-mono: 12px;
  --mj-size-mono-sm: 11px;

  --mj-track-display: -0.045em;
  --mj-track-heading: -0.04em;
  --mj-track-tight: -0.03em;
  --mj-track-mono: 0.08em;
  --mj-track-mono-wide: 0.14em;

  /* -- Space and shape -- */
  --mj-container: 1240px;
  --mj-gutter: clamp(16px, 4vw, 32px);
  --mj-section-y: clamp(64px, 12vw, 112px);
  --mj-card-pad: clamp(24px, 4vw, 32px);
  --mj-panel-pad-y: clamp(28px, 5vw, 40px);
  --mj-panel-pad-x: clamp(22px, 4vw, 36px);

  --mj-radius-sm: 6px;
  --mj-radius: 12px;
  --mj-radius-md: 16px;
  --mj-radius-lg: 18px;
  --mj-radius-xl: 22px;
  --mj-radius-pill: 999px;

  /* -- Motion -- */
  --mj-ease: cubic-bezier(.22, .7, .24, 1);
  --mj-ease-reveal: cubic-bezier(.16, .84, .34, 1);
  --mj-dur-fast: .25s;
  --mj-dur: .3s;
  --mj-dur-slow: .45s;
  --mj-reveal-dur: 620ms;

  /* Sticky header height, also the scroll-margin for in-page anchors. */
  --mj-header-offset: 96px;

  /* -----------------------------------------------------------------------
     v4 — "Mubarmij Site v4.dc.html", imported 2026-08-18.

     Every value below is lifted from the design's own :root block. The names
     are this file's, not the design's (`--brand-accent` there is `--mj-gold`
     here); only values with no equivalent get a new token. The design states
     no new hues enter the system, and none do: the two new colours are
     lightness shifts of the surface and the gold.
     ----------------------------------------------------------------------- */

  /* The panel colour v4 uses for cards and the nav surface. Lighter than
     --mj-surface, which stays as it is for the ten pages built on it. */
  --mj-surface-v4: #16243B;

  /* Gold dark enough to read as text on the light Numbers band. Lightness
     only — same hue as --mj-gold. */
  --mj-gold-deep: #7F5D1A;

  /* Text steps v4 asks for that this file did not already have. */
  --mj-ink-70: rgb(var(--mj-ink-rgb) / 0.7);
  --mj-ink-50: rgb(var(--mj-ink-rgb) / 0.5);
  --mj-gold-12: rgb(var(--mj-gold-rgb) / 0.12);
  --mj-gold-30: rgb(var(--mj-gold-rgb) / 0.3);

  /* The design sets the Process step numerals at 30% gold, which composites to
     1.83:1 against the page ground — under the 3:1 that large text needs, and
     Lighthouse fails the page on it. They are big and deliberately quiet, so
     this is the smallest alpha that passes rather than a rethink: 0.55 gives
     3.62:1 and still reads as a background numeral. */
  --mj-gold-numeral: rgb(var(--mj-gold-rgb) / 0.55);

  /* Shape. v4 is rounder than V1.0 everywhere; kept as its own scale rather
     than retuning --mj-radius-*, which the other ten pages still use. */
  --mj-r-sm: 12px;
  --mj-r-md: 24px;
  --mj-r-lg: 32px;
  --mj-shadow-v4: 0 1px 2px rgb(0 0 0 / 0.04), 0 32px 64px -32px rgb(0 0 0 / 0.14);

  /* Space. This is the largest departure from V1.0 — roughly 2.5x the section
     rhythm and 5x the page edge. The whitespace is the design; do not
     compress it to fit more content above the fold. */
  --mj-sec-y: clamp(140px, 18vw, 280px);
  --mj-edge: clamp(48px, 8vw, 160px);
  --mj-gap-head: clamp(64px, 8vw, 128px);
  --mj-gap-row: clamp(40px, 5vw, 80px);
  --mj-gap-case: clamp(96px, 12vw, 180px);

  /* Display type. v4 sets static weights and does not touch Archivo's width
     axis, so --mj-wdth-* above is unused on this page. */
  --mj-d1: clamp(68px, 9vw, 124px);
  --mj-d2: clamp(44px, 5.4vw, 72px);
  --mj-d3: clamp(26px, 2.4vw, 34px);
  --mj-body-v4: clamp(17px, 1.4vw, 19px);

  /* Arabic face, applied on [dir="rtl"]. Loaded but unused until Arabic copy
     exists — see docs/DESIGN-IMPORT.md. */
  --mj-font-ar: "IBM Plex Sans Arabic", Archivo, sans-serif;

  /* Client logos sit on a white plaque in v4: they are other companies' marks,
     supplied on white, and knocking them out to a tint would misrepresent them. */
  --mj-logo-plaque: #FFFFFF;

}

/* v4 narrow-viewport overrides, from the design's own media block. */
@media (max-width: 760px) {
  :root {
    --mj-edge: clamp(20px, 6vw, 48px);
    --mj-sec-y: clamp(96px, 16vw, 180px);
    --mj-gap-head: clamp(40px, 8vw, 96px);
    --mj-gap-case: clamp(72px, 12vw, 140px);
  }
}

/* --- base.css --- */
/* ---------------------------------------------------------------------------
   Base — element defaults and the shared keyframes.

   Logical properties are used throughout (padding-inline, inset-inline-end,
   text-align: start) so the Arabic RTL locale in PLAN.md milestone 03 is an
   attribute flip on <html dir>, not a stylesheet rewrite. The Claude Design
   source used physical properties; that is the one deliberate divergence.
   --------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--mj-header-offset);
}

/* The v4 home page runs Lenis, which owns scrolling. Leaving the CSS engine
   smooth as well means two of them ease the same anchor jump against each
   other. :has() keeps this in one place rather than a second stylesheet. */
html:has(.mj-v4) { scroll-behavior: auto; }

body {
  margin: 0;
  overflow-x: hidden;
  background: var(--mj-bg);
  color: var(--mj-ink);
  font-family: var(--mj-font);
  -webkit-font-smoothing: antialiased;
  text-wrap: pretty;
}

a {
  color: var(--mj-gold);
  text-decoration: none;
}
a:hover { color: var(--mj-gold-hi); }

/* `height: auto` is not optional next to max-width. Every <img> here carries
   width/height attributes so it reserves space and does not shift the layout;
   those attributes are presentational hints, and without this the height one
   wins as a literal pixel height that `aspect-ratio` cannot override. The v4
   hero cards rendered 1202px tall inside a 162px box before this line. */
img {
  max-width: 100%;
  height: auto;
}

canvas { max-width: 100%; }

h1, h2, h3, p, span, a { overflow-wrap: anywhere; }

/* Skip link. Off-screen until focused, then the design's gold pill. */
.mj-skip {
  position: absolute;
  inset-block-start: -200px;
  inset-inline-start: 16px;
  z-index: 200;
  padding: 14px 20px;
  border-radius: var(--mj-radius-pill);
  background: var(--mj-gold);
  color: var(--mj-bg);
  font-weight: 600;
}
.mj-skip:focus-visible {
  inset-block-start: 16px;
  color: var(--mj-bg);
}

::selection {
  background: var(--mj-gold);
  color: var(--mj-bg);
}

/* Keyboard focus was absent from the design source. Added rather than
   inherited: a gold ring on the existing palette costs nothing visually and
   the site is a lead funnel that has to stay keyboard-operable. */
:focus-visible {
  outline: 2px solid var(--mj-gold);
  outline-offset: 3px;
  border-radius: 4px;
}

button {
  font: inherit;
  color: inherit;
}

/* Visible to assistive technology only. Used where a visual label is hidden
   at some breakpoint (the wordmark next to the logo mark, below 700px). */
.mj-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* -- Keyframes -------------------------------------------------------------- */

@keyframes mjMarquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

@keyframes mjFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes mjPulse {
  0%, 100% { opacity: .35; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.6); }
}

@keyframes mjOrbit {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* Reduced motion.

   `animation-duration: .001ms` alone is a trap. It retires a *finite*
   animation instantly, but an `infinite` one just loops a million times a
   second — so the marquee, the floating phone mock and the orbit ring vibrate
   at full speed instead of stopping. That is the opposite of what the setting
   asks for, and it is what the design source shipped.

   `animation-iteration-count: 1` is the missing half: it caps every animation
   at a single pass, which the near-zero duration then finishes immediately.
   `animationend` still fires, so nothing that listens for it stalls. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }

  /* These three exist only to move. Skipping to their final keyframe would
     leave the marquee parked mid-scroll and the pulse dot stuck at 35%
     opacity, so switch them off outright and let each rest in its natural
     un-animated position instead. */
  .mj-marquee__track,
  .mj-orbit,
  .mj-eyebrow__dot {
    animation: none !important;
  }
}

/* -- Scroll reveal ---------------------------------------------------------- */

/* The hidden state is applied by app.js, not here, so the page is fully
   readable if JavaScript never runs.

   Deliberately no `transition` on this rule. A transition is driven by the
   properties in effect *after* a change, so putting it here would do two wrong
   things at once: nothing would animate on the way in (the shown state would
   carry no transition), and applying `pending` at load would animate the
   element from visible to hidden — a fade-out flash on every section. */
[data-reveal][data-reveal-state="pending"] {
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  will-change: opacity, transform;
  backface-visibility: hidden;
}

/* The transition lives here, on the destination state, which is what actually
   makes the reveal animate. app.js sets an inline transition-delay just before
   flipping the attribute, to stagger a batch. */
[data-reveal][data-reveal-state="shown"] {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition:
    opacity var(--mj-reveal-dur) var(--mj-ease-reveal),
    transform var(--mj-reveal-dur) var(--mj-ease-reveal);
}

/* -- Layout primitives ------------------------------------------------------ */

.mj-shell {
  position: relative;
  overflow-x: clip;
  background: var(--mj-bg);
}

/* The starfield and the ambient gradient are fixed behind everything. */
.mj-sky,
.mj-glow {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.mj-sky {
  width: 100%;
  height: 100%;
  animation: mjFade 1.2s ease both;
}

.mj-glow {
  background:
    radial-gradient(120% 80% at 50% -10%, rgb(var(--mj-gold-rgb) / 0.14), transparent 55%),
    radial-gradient(80% 60% at 100% 100%, rgb(var(--mj-blue-rgb) / 0.22), transparent 60%);
}

.mj-glow--subtle {
  background:
    radial-gradient(110% 70% at 20% -10%, rgb(var(--mj-gold-rgb) / 0.12), transparent 55%),
    radial-gradient(70% 60% at 100% 90%, rgb(var(--mj-blue-rgb) / 0.2), transparent 60%);
}

.mj-content {
  position: relative;
  z-index: 1;
}

.mj-wrap {
  max-width: var(--mj-container);
  margin-inline: auto;
  padding-inline: var(--mj-gutter);
}

.mj-section {
  max-width: var(--mj-container);
  margin-inline: auto;
  padding-inline: var(--mj-gutter);
  padding-block-end: var(--mj-section-y);
}

.mj-section--ruled {
  max-width: none;
  border-block-start: 1px solid var(--mj-line-soft);
  padding-block: var(--mj-section-y);
}

.mj-section--ruled > .mj-wrap { padding-inline: 0; }

/* --- whatsapp.css --- */
/* ---------------------------------------------------------------------------
   The floating WhatsApp button.

   Its own file because it is the one thing every page shares: base.html
   renders it on the V1 pages and on the v4 home page alike. Leaving it in
   sections.css meant the v4 page had to load 15 KB of V1 section styles to get
   one button.

   Behaviour and the reasoning behind it: templates/partials/whatsapp.html.
   --------------------------------------------------------------------------- */

/* -- Floating WhatsApp button ---------------------------------------------- */

.mj-wa {
  position: fixed;
  z-index: 90;
  inset-inline-end: clamp(16px, 3vw, 28px);
  inset-block-end: clamp(16px, 3vw, 28px);
  width: 58px;
  height: 58px;
  border-radius: 50%;
  background: #25D366;
  display: grid;
  place-items: center;
  box-shadow: 0 14px 38px rgb(37 211 102 / 0.34);
  transition: transform var(--mj-dur) var(--mj-ease),
              box-shadow var(--mj-dur) ease,
              opacity var(--mj-dur-slow) var(--mj-ease-reveal);

  /* Visible by default. app.js arms it on load and reveals it on the first
     scroll, so it does not sit over the hero before the visitor has done
     anything — but a failed script leaves a working button rather than an
     invisible one. Hover lifts it and nothing else moves: a button that
     pulses at you is an advert. */
}

/* The button is fixed, so on a narrow screen it lands on whatever the page
   ends with — the copyright line, measured. Reserve its footprint at the end
   of the document so it covers nothing. Every page carries the button, so this
   is not scoped to one of them. */
@media (max-width: 640px) {
  body { padding-block-end: 92px; }
}

.mj-wa[data-armed]:not([data-shown]) {
  opacity: 0;
  transform: translateY(12px);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .mj-wa { transition: none; }
}

.mj-wa[data-shown]:hover,
.mj-wa[data-shown]:focus-visible {
  transform: translateY(-3px) scale(1.04);
  box-shadow: 0 20px 48px rgb(37 211 102 / 0.46);
}

.mj-wa svg { display: block; }

/* --- components.css --- */


/* -- Auto-fitting grid -------------------------------------------------------
   The source repeats `repeat(auto-fit, minmax(min(100%, Npx), 1fr))` with a
   different N per section. One class, N as a custom property. */

.mj-autogrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--mj-col, 280px)), 1fr));
  gap: var(--mj-gap, 18px);
}

/* -- Type ------------------------------------------------------------------- */

.mj-eyebrow {
  display: flex;
  align-items: center;
  gap: 14px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: rgb(var(--mj-ink-rgb) / 0.5);
}

.mj-eyebrow__dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: 50%;
  background: var(--mj-gold);
  animation: mjPulse 2.8s ease-in-out infinite;
}

.mj-kicker {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: rgb(var(--mj-ink-rgb) / 0.42);
}

.mj-hero-title,
.mj-page-title,
.mj-h2,
.mj-h3 {
  margin: 0;
  font-weight: 600;
  letter-spacing: var(--mj-track-heading);
}

.mj-hero-title {
  max-width: 16ch;
  font-size: var(--mj-size-hero);
  line-height: 0.95;
  letter-spacing: var(--mj-track-display);
  font-variation-settings: 'wdth' var(--mj-wdth-display);
}

.mj-page-title {
  max-width: 18ch;
  font-size: var(--mj-size-page-title);
  line-height: 0.96;
  letter-spacing: var(--mj-track-display);
  font-variation-settings: 'wdth' var(--mj-wdth-display);
}

.mj-h2 {
  font-size: var(--mj-size-h2);
  line-height: 1.02;
  font-variation-settings: 'wdth' var(--mj-wdth-heading);
}

.mj-h2--lg {
  max-width: 20ch;
  font-size: var(--mj-size-h2-lg);
  line-height: 0.98;
  letter-spacing: var(--mj-track-display);
  font-variation-settings: 'wdth' var(--mj-wdth-display);
}

.mj-h3 {
  font-size: var(--mj-size-h3);
  letter-spacing: var(--mj-track-tight);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
}

/* Display-weight text that is not a heading in the document outline. */
.mj-display {
  font-size: clamp(30px, 5.5vw, 40px);
  letter-spacing: var(--mj-track-heading);
  font-variation-settings: 'wdth' var(--mj-wdth-display);
}

.mj-display--sm {
  font-size: clamp(20px, 3.2vw, 24px);
  letter-spacing: var(--mj-track-tight);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
}

.mj-lede {
  margin: 0;
  max-width: 56ch;
  font-size: var(--mj-size-lede);
  line-height: 1.6;
  color: var(--mj-ink-body);
}

.mj-copy {
  margin: 0;
  color: rgb(var(--mj-ink-rgb) / 0.6);
  font-size: var(--mj-size-body);
  line-height: 1.6;
}

.mj-copy--sm {
  font-size: var(--mj-size-sm);
  color: rgb(var(--mj-ink-rgb) / 0.58);
}

.mj-copy--xs {
  font-size: var(--mj-size-xs);
  line-height: 1.55;
  color: var(--mj-ink-muted);
}

.mj-monolink {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* -- Buttons ---------------------------------------------------------------- */

.mj-btn {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 16px clamp(20px, 4vw, 28px);
  border-radius: var(--mj-radius-pill);
  border: 1px solid transparent;
  font-size: var(--mj-size-sm);
  cursor: pointer;
  transition:
    transform var(--mj-dur) ease,
    box-shadow var(--mj-dur) ease,
    border-color var(--mj-dur) ease,
    background var(--mj-dur) ease;
}

.mj-btn--primary {
  background: var(--mj-gold);
  color: var(--mj-bg);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.mj-btn--primary:hover {
  color: var(--mj-bg);
  transform: translateY(-3px);
  box-shadow: var(--mj-gold-glow);
}

.mj-btn--ghost {
  color: var(--mj-ink);
  border-color: var(--mj-line-strong);
}

.mj-btn--ghost:hover {
  color: var(--mj-ink);
  border-color: var(--mj-gold-line-hi);
  background: var(--mj-gold-wash-hi);
}

/* Header call to action: same shape, mono type, tighter. */
.mj-btn--compact {
  padding: 11px 18px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--mj-gold);
  color: var(--mj-bg);
}

.mj-btn--compact:hover {
  color: var(--mj-bg);
  transform: translateY(-2px);
  box-shadow: var(--mj-gold-glow-sm);
}

.mj-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

/* -- Header ----------------------------------------------------------------- */

.mj-header {
  position: sticky;
  top: 0;
  z-index: 50;
  backdrop-filter: blur(10px);
  background: var(--mj-header-bg);
  border-block-end: 1px solid var(--mj-line-soft);
}

.mj-header__bar {
  max-width: var(--mj-container);
  margin-inline: auto;
  padding: 14px var(--mj-gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 14px clamp(16px, 3vw, 40px);
}

/* -- Mobile menu ------------------------------------------------------------
   Hidden above 700px, matching the JS media query in the design source. */

.mj-burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 42px;
  height: 42px;
  padding: 0 11px;
  cursor: pointer;
  background: transparent;
  border: 1px solid var(--mj-line-strong);
  border-radius: var(--mj-radius);
}

@media (max-width: 700px) {
  .mj-nav,
  .mj-brand__word { display: none; }

  .mj-burger { display: flex; }

  .mj-menu { display: grid; }
}

/* -- Hero ------------------------------------------------------------------- */

.mj-hero {
  max-width: var(--mj-container);
  margin-inline: auto;
  padding: clamp(72px, 14vw, 128px) var(--mj-gutter) 88px;
}

.mj-hero--page { padding: clamp(64px, 13vw, 104px) var(--mj-gutter) 56px; }

.mj-hero__title { margin-block-start: 28px; }
.mj-hero__lede { margin-block-start: 28px; }
.mj-hero__actions { margin-block-start: clamp(32px, 5vw, 44px); }

/* -- Grid panel --------------------------------------------------------------
   A tinted grid whose 1px gaps read as hairlines between opaque cells.
   Used for the stat strip, the spec tables and the stack columns. */

.mj-panel {
  display: flex;
  flex-wrap: wrap;
  gap: 1px;
  background: rgb(var(--mj-ink-rgb) / 0.1);
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.1);
  border-radius: var(--mj-radius-lg);
  overflow: hidden;
}

.mj-panel__cell {
  flex: 1 1 var(--mj-cell, 150px);
  background: var(--mj-bg);
  padding: clamp(20px, 4vw, 30px) clamp(18px, 3.5vw, 28px);
}

.mj-stats { margin-block-start: clamp(52px, 9vw, 84px); }

.mj-stat__value {
  font-size: var(--mj-size-stat);
  letter-spacing: var(--mj-track-heading);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
  color: var(--mj-gold);
}

.mj-stat__label {
  margin-block-start: 8px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: rgb(var(--mj-ink-rgb) / 0.5);
}

/* -- Chips and tags --------------------------------------------------------- */

.mj-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  color: var(--mj-ink-muted);
}

.mj-chip {
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.14);
  border-radius: var(--mj-radius-pill);
  padding: 6px 11px;
}

.mj-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  color: rgb(var(--mj-ink-rgb) / 0.72);
}

.mj-tag {
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.13);
  border-radius: var(--mj-radius-sm);
  padding: 5px 9px;
}

/* -- Feature (latest build) ------------------------------------------------- */

.mj-feature {
  margin-block-start: 20px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 1px;
  background: var(--mj-line);
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-xl);
  overflow: hidden;
}

.mj-feature__media {
  background: var(--mj-surface);
  padding: clamp(14px, 3vw, 22px);
  position: relative;
  display: flex;
  flex-direction: column;
}

.mj-feature__frame {
  flex: 1 1 auto;
  aspect-ratio: 16 / 10;
  border-radius: var(--mj-radius);
  overflow: hidden;
  background-color: rgb(var(--mj-ink-rgb) / 0.03);
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.1);
}

.mj-feature__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

/* The selected-work projects carry .mj-fit-cover / .mj-fit-contain, which is a
   weaker selector than the rule above and would lose to it. Restated at the
   frame's own specificity so a logo still shows whole instead of cropping. */
.mj-feature__frame img.mj-fit-cover { object-fit: cover; }

.mj-feature__frame img.mj-fit-contain {
  object-fit: contain;
  object-position: center;
  padding: var(--mj-fit-pad, 28px);
}

.mj-feature__body {
  background: var(--mj-bg);
  padding: var(--mj-panel-pad-y) var(--mj-panel-pad-x);
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* The selected-work stack. `.mj-feature` carries its own top margin for the
   single featured block under the hero; inside the stack the gap owns the
   spacing instead, so the margin is dropped rather than added to it. */
.mj-features {
  display: grid;
  gap: clamp(24px, 3vw, 36px);
}

.mj-features > .mj-feature { margin-block-start: 0; }

/* -- Trusted-by marquee ------------------------------------------------------
   The track holds the logo list twice; translating it -50% loops seamlessly. */

.mj-marquee {
  border-block: 1px solid var(--mj-line-soft);
  padding: 26px 0;
  overflow: hidden;
}

.mj-marquee__track {
  display: flex;
  align-items: center;
  gap: clamp(18px, 3vw, 30px);
  width: max-content;
  animation: mjMarquee 46s linear infinite;
  will-change: transform;
  font-family: var(--mj-font-mono);
  font-size: clamp(11px, 1.9vw, 13px);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(var(--mj-ink-rgb) / 0.34);
}

.mj-marquee__label { color: rgb(var(--mj-gold-rgb) / 0.75); }

.mj-logo-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  min-width: 96px;
  padding: 0 16px;
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.1);
  border-radius: var(--mj-radius);
  /* Client logos are mostly dark-on-transparent, so they sit on a light plate. */
  background: rgb(var(--mj-ink-rgb) / 0.9);
}

.mj-logo-chip img {
  max-height: 32px;
  max-width: 110px;
  width: auto;
  object-fit: contain;
  display: block;
}

/* -- Capability cards ------------------------------------------------------- */

.mj-card {
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-lg);
  padding: var(--mj-card-pad);
  background: var(--mj-fill);
  transition:
    transform var(--mj-dur) var(--mj-ease),
    border-color var(--mj-dur) ease,
    background var(--mj-dur) ease;
  will-change: transform;
}

.mj-card:hover {
  transform: translateY(-6px);
  border-color: var(--mj-gold-line);
  background: var(--mj-gold-wash);
}

.mj-card__meta,
.mj-meta-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mj-ink-dim);
}

.mj-card__meta em,
.mj-meta-row em {
  font-style: normal;
  color: var(--mj-gold);
}

.mj-card .mj-h3 { margin-block-start: 16px; }
.mj-card .mj-copy--sm { margin: 12px 0 20px; }

/* -- Stack columns ---------------------------------------------------------- */

.mj-stack__group {
  flex: 1 1 200px;
  background: var(--mj-bg);
  padding: 28px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  transition: background var(--mj-dur) ease;
}

.mj-stack__group:hover { background: var(--mj-gold-wash); }

.mj-stack__label {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mj-ink-dim);
}

/* -- Project tag -------------------------------------------------------------
   All that is left of the project card: the projects render in the feature
   build now, and this is the tag line above the name inside it. */

.mj-project__tag {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mj-gold);
  margin-block-end: 10px;
}

/* -- Reels ------------------------------------------------------------------ */

.mj-reel {
  position: relative;
  aspect-ratio: 9 / 14;
  border-radius: var(--mj-radius-md);
  overflow: hidden;
  border: 1px solid var(--mj-line);
  background-color: rgb(var(--mj-ink-rgb) / 0.03);
  color: var(--mj-ink);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 18px;
  transition: transform var(--mj-dur) var(--mj-ease), border-color var(--mj-dur) ease;
  will-change: transform;
}

.mj-reel:hover {
  color: var(--mj-ink);
  transform: translateY(-6px);
  border-color: var(--mj-gold-line);
}

.mj-reel__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.mj-reel__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgb(var(--mj-bg-rgb) / 0.15) 0%,
    rgb(var(--mj-bg-rgb) / 0.35) 45%,
    rgb(var(--mj-bg-rgb) / 0.9) 100%
  );
}

.mj-reel__play {
  position: absolute;
  top: 16px;
  inset-inline-start: 16px;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: 1px solid var(--mj-gold-line-hi);
  background: rgb(var(--mj-bg-rgb) / 0.5);
  color: var(--mj-gold);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
}

/* -- Reels that play in place ------------------------------------------------
   A playable tile is a <div>, not a link: the whole card is one transparent
   button over a <video>. Idle it looks exactly like the linked tile it
   replaced — poster, scrim, badge, label. Playing, everything but the video
   and the browser's own controls gets out of the way. --------------------- */

.mj-reel__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* The poster is drawn at the tile's own 9/14, so `cover` never crops it.
     The clips are 9/16 and would lose a band top and bottom, which is why
     playing switches this to `contain` below. */
  object-fit: cover;
  background-color: #fff;
}

/* The posters are a mark on white, so the linked tile's full-height wash would
   grey the logo out. Here the scrim is only the strip the label needs. */
.mj-reel--playable .mj-reel__scrim {
  background: linear-gradient(
    180deg,
    rgb(var(--mj-bg-rgb) / 0) 0%,
    rgb(var(--mj-bg-rgb) / 0) 50%,
    rgb(var(--mj-bg-rgb) / 0.8) 76%,
    rgb(var(--mj-bg-rgb) / 0.97) 100%
  );
  transition: opacity var(--mj-dur) ease;
}

.mj-reel__hit {
  position: absolute;
  inset: 0;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: inherit;
  font: inherit;
  transition: opacity var(--mj-dur) ease;
}

/* The badge sits inside the hit area, so it needs the tile's padding back. */
.mj-reel__hit .mj-reel__play { top: 16px; inset-inline-start: 16px; }

/* On white, the badge needs a denser ground than the linked tile's 0.5. */
.mj-reel--playable .mj-reel__play {
  background: rgb(var(--mj-bg-rgb) / 0.82);
  transition: transform var(--mj-dur) var(--mj-ease), background-color var(--mj-dur) ease;
}

.mj-reel--playable:hover .mj-reel__play { transform: scale(1.08); }

/* Decoration only: every click in the card belongs to the button under it. */
.mj-reel--playable .mj-reel__meta {
  pointer-events: none;
  transition: opacity var(--mj-dur) ease;
}

/* The poster is 9/14 and the clips are 9/16, so a playing tile takes the
   clip's shape. It grows the row by about 50px — cheaper than cropping a fifth
   of the clip away, or letterboxing it inside the poster's frame. `contain`
   stays, so a clip cut to some other ratio still arrives whole. */
.mj-reel--playable.is-playing {
  aspect-ratio: 9 / 16;
  background-color: var(--mj-surface);
  border-color: var(--mj-gold-line);
  transform: none;
}

.mj-reel--playable.is-playing .mj-reel__video {
  object-fit: contain;
  background-color: var(--mj-surface);
}

/* Nothing overlays a playing clip — the browser's own controls are the UI, and
   the hit area would swallow the clicks meant for them. */
.mj-reel--playable.is-playing .mj-reel__scrim,
.mj-reel--playable.is-playing .mj-reel__meta,
.mj-reel--playable.is-playing .mj-reel__hit {
  opacity: 0;
  pointer-events: none;
}

.mj-reel__meta { position: relative; }

.mj-reel__kind {
  font-family: var(--mj-font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgb(var(--mj-ink-rgb) / 0.6);
}

.mj-reel__name {
  margin-block-start: 6px;
  font-size: 17px;
  letter-spacing: -0.02em;
}

/* -- Process steps ---------------------------------------------------------- */

.mj-step {
  border-block-start: 1px solid rgb(var(--mj-ink-rgb) / 0.16);
  padding-block-start: 22px;
}

.mj-step__name { margin-block-start: 14px; }
.mj-step .mj-copy--xs { margin-block-start: 10px; line-height: 1.6; }

/* -- FAQ ---------------------------------------------------------------------
   Rendered as <details>/<summary>: open/close works with JavaScript disabled,
   and the panel animates on grid-template-rows so long answers are never
   clipped (the source used a fixed 220px max-height). */

.mj-faq__item {
  border-block-end: 1px solid var(--mj-line);
}

.mj-faq__q {
  display: flex;
  gap: 20px;
  align-items: baseline;
  justify-content: space-between;
  padding: 22px 0;
  cursor: pointer;
  list-style: none;
  font-size: clamp(16px, 2.4vw, 18px);
  letter-spacing: -0.02em;
  line-height: 1.4;
}

.mj-faq__q::-webkit-details-marker { display: none; }

.mj-faq__sign {
  flex: none;
  font-family: var(--mj-font-mono);
  color: var(--mj-gold);
  font-size: 18px;
  line-height: 1;
}

.mj-faq__sign::after { content: "+"; }
.mj-faq__item[open] .mj-faq__sign::after { content: "\2212"; }

.mj-faq__panel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows var(--mj-dur-slow) ease, opacity var(--mj-dur-slow) ease;
}

.mj-faq__item[open] .mj-faq__panel {
  grid-template-rows: 1fr;
  opacity: 1;
}

.mj-faq__panel > div { overflow: hidden; min-height: 0; }

.mj-faq__a {
  margin: 0 0 22px;
  max-width: 60ch;
  color: rgb(var(--mj-ink-rgb) / 0.58);
  font-size: var(--mj-size-sm);
  line-height: 1.65;
}

/* -- Footer ----------------------------------------------------------------- */

.mj-footer {
  border-block-start: 1px solid var(--mj-line-soft);
  padding: 44px var(--mj-gutter);
}

.mj-footer__row {
  max-width: var(--mj-container);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 20px 32px;
  justify-content: space-between;
  align-items: center;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  color: var(--mj-ink-faint);
}

.mj-footer__brand {
  display: flex;
  align-items: center;
  gap: 12px;
}

.mj-footer__brand img {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  display: block;
}

.mj-footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
}

.mj-footer__links a {
  color: var(--mj-ink-faint);
  transition: color var(--mj-dur) ease;
}

.mj-footer__links a:hover { color: var(--mj-gold); }

/* -- Case studies: filters -------------------------------------------------- */

.mj-filters {
  margin-block-start: 40px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.mj-filter {
  cursor: pointer;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: var(--mj-track-mono);
  text-transform: uppercase;
  padding: 10px 16px;
  border-radius: var(--mj-radius-pill);
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.18);
  background: transparent;
  color: rgb(var(--mj-ink-rgb) / 0.6);
  transition: border-color var(--mj-dur) ease, background var(--mj-dur) ease, color var(--mj-dur) ease;
}

.mj-filter:hover { border-color: var(--mj-gold-line); }

.mj-filter[aria-pressed="true"] {
  background: var(--mj-gold);
  color: var(--mj-bg);
  border-color: var(--mj-gold);
}

.mj-case__body .mj-copy {
  margin-block-start: 12px;
  max-width: 56ch;
}

/* Object-fit varies per case study: some shots are screenshots that should
   fill, some are logos that need padding and containment. */
.mj-fit-cover { object-fit: cover; }

.mj-fit-contain {
  object-fit: contain;
  padding: var(--mj-fit-pad, 28px);
}

/* -- Empty state (all filters removed) -------------------------------------- */

.mj-empty {
  padding: 48px 0;
  color: var(--mj-ink-dim);
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: var(--mj-track-mono);
  text-transform: uppercase;
}

/* -- Logo stand-in ----------------------------------------------------------
   A case study with no screenshot yet. Its `img` is the client's brand mark,
   which stretched across a full card looks like a mistake — and a dark mark on
   the dark frame is invisible, which TradeWithMH was.

   The mark sits small and centred on the same white plaque the client marquee
   uses, so every logo is legible whatever its own colours, and the card keeps
   the height its neighbours have instead of collapsing. */
.mj-case__frame:has(> img.mj-fit-logo),
.mj-feature__frame:has(> img.mj-fit-logo) {
  display: grid;
  place-items: center;
  padding: clamp(24px, 5%, 48px);
}

img.mj-fit-logo {
  width: auto;
  height: auto;
  min-height: 0;
  max-width: min(62%, 260px);
  max-height: 132px;
  object-fit: contain;
  padding: 22px 30px;
  border-radius: var(--mj-radius-md);
  background: var(--mj-logo-plaque);
}

/* -- The whole case card is a link -----------------------------------------
   A stretched anchor rather than wrapping the card in one: the card also holds
   a WhatsApp link, and an anchor inside an anchor is invalid and unusable with
   a keyboard. The stretch covers the card; the WhatsApp link sits above it.
   Both stay separate tab stops with their own accessible names. */
.mj-case--linked { position: relative; }

/* The pointer says the card is clickable, and the frame answers on hover of
   anywhere in it rather than only on the words. */
.mj-case--linked { cursor: pointer; }
/* --- sections.css --- */


/* -- Reveal, retuned -------------------------------------------------------
   Longer, softer, and with a slight scale so a section arrives rather than
   jumps. The pending rule deliberately carries no transition and the shown rule
   carries all of it, for the reason base.css spells out: a transition is driven
   by the properties in effect *after* the change, so declaring it on the hidden
   state animates the element into hiding at load and not out of it.

   app.js sets an inline transition-delay just before flipping the attribute, to
   stagger a batch. Nothing here touches transition-delay, so that still works. */

[data-reveal][data-reveal-state="pending"] {
  opacity: 0;
  transform: translate3d(0, 34px, 0) scale(.97);
}

[data-reveal][data-reveal-state="shown"] {
  transition:
    opacity 900ms cubic-bezier(.16, 1, .3, 1),
    transform 900ms cubic-bezier(.16, 1, .3, 1);
}

/* -- Stacked scroll deck ---------------------------------------------------
   Each card is sticky at a slightly lower offset than the one before it, so
   scrolling slides every card over the last and leaves a stack of edges behind.

   There is no JavaScript in this: no scroll listener, no measuring, no
   observer. It is position: sticky, an opaque background so the card underneath
   is actually covered, and a shadow cast upwards so the seam reads as a stack.
   Where sticky is unsupported the cards simply stay in flow and the section
   degrades to a plain column of cards, which is why nothing here is load-bearing
   for legibility.

   --mj-deck-top is the first card's resting offset (the sticky header, plus
   air); each card then adds --mj-deck-step once per index. The template sets
   --mj-deck-i from loop.index0, because the step has to come from document
   order and CSS cannot count siblings into a calc(). */

.mj-deck {
  display: grid;
  gap: clamp(20px, 3vw, 28px);
  margin-block-start: clamp(32px, 5vw, 52px);
}

/* A text-only card has no image to give it height, so it asks for its own. */
.mj-deck__card--tall { min-height: min(58vh, 440px); padding: clamp(28px, 5vw, 52px); }

/* Spec values as pills. The key is dropped rather than shown: in a deck card
   the value alone reads ("Lens selector"), and the pair needs a table. */
.mj-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  color: var(--mj-ink-muted);
}

/* -- Reel: expand to full size --------------------------------------------
   Sits opposite the play badge, which is top-start. The badge plays the clip in
   the tile; this one opens it in the lightbox below. It hides once the tile is
   playing, because at that point the tile is showing native video controls and a
   second control over them is just a target to hit by accident. */

.mj-reel__expand {
  position: absolute;
  z-index: 2;
  top: 16px;
  inset-inline-end: 16px;
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--mj-gold-line-hi);
  background: rgb(var(--mj-bg-rgb) / 0.82);
  color: var(--mj-gold);
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform var(--mj-dur) var(--mj-ease), background var(--mj-dur) ease,
              color var(--mj-dur) ease;
}

.mj-reel__expand:hover {
  transform: scale(1.08);
  background: var(--mj-gold);
  color: var(--mj-bg);
}

.mj-reel--playable.is-playing .mj-reel__expand { opacity: 0; pointer-events: none; }

/* -- Lightbox -------------------------------------------------------------
   Hidden by the `hidden` attribute, not by a class, so the markup is inert
   before design/sections.js runs and stays inert if it never does. */

.mj-lightbox {
  position: fixed;
  inset: 0;
  /* Above the fixed furniture, not under it. At 120 the client rail (170) and
     the pill nav (150) sat on top of the viewer — the title bar and the close
     button were behind them, so the one control that dismisses the thing was
     half hidden. Below the skip link at 220, which must stay reachable. */
  z-index: 210;
  background: rgb(var(--mj-bg-rgb) / 0.92);
  backdrop-filter: blur(8px);
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 48px);
  animation: mjFade var(--mj-dur) ease both;
}

.mj-lightbox[hidden] { display: none; }

/* The scrim is a real button, so closing by clicking away is keyboard- and
   screen-reader-reachable rather than a click handler on a div. */
.mj-lightbox__scrim {
  position: absolute;
  inset: 0;
  width: 100%;
  border: 0;
  background: none;
  cursor: pointer;
}

.mj-lightbox__inner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: 100%;
}

.mj-lightbox__bar {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 20px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: var(--mj-ink-muted);
}

.mj-lightbox__bar em { font-style: normal; color: var(--mj-gold); }

.mj-lightbox__close {
  cursor: pointer;
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.24);
  background: transparent;
  color: var(--mj-ink);
  border-radius: var(--mj-radius-pill);
  padding: 8px 14px;
  font-family: inherit;
  font-size: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}

.mj-lightbox__close:hover { border-color: var(--mj-gold-line-hi); color: var(--mj-gold); }

/* The clip fills the viewport, whatever shape it is.

   `object-fit` is left alone: a <video> already letterboxes rather than
   crops, so the frame is whole either way. What was making it small was the
   height cap — 78vh of a 900px window is 702px, and a 9:16 clip in that is
   395px wide with a quarter of the screen unused. The cap now takes the real
   furniture out of the viewport instead of guessing at a fraction: the
   lightbox's own padding, the title bar, and the gap between them.

   The width cap goes with it. It existed to stop a portrait clip growing
   absurdly wide, but the height bound already does that — 9:16 at any height
   is narrower than it is tall — and it was shrinking anything landscape to a
   strip. min() with 92vw keeps a wide clip inside the window. */
.mj-lightbox video {
  display: block;
  max-block-size: calc(100dvh - var(--mj-lb-chrome));
  max-inline-size: 92vw;
  /* Belt and braces on a browser that decides a replaced element should
     stretch: the intrinsic ratio decides the other axis. */
  inline-size: auto;
  block-size: auto;
  border-radius: 24px;
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.16);
  background: var(--mj-surface);
}

.mj-lightbox {
  /* Padding top and bottom, the title bar, and the gap under it. */
  --mj-lb-chrome: calc((clamp(16px, 4vw, 48px) * 2) + 44px);
}

/* dvh is what makes this correct on a phone, where 100vh includes the browser
   chrome that is actually covering the bottom of the clip. */
@supports not (height: 100dvh) {
  .mj-lightbox video { max-block-size: calc(100vh - var(--mj-lb-chrome)); }
}

/* -- Service / about / contact / book-a-call pages ------------------------- */

.mj-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-block-start: 28px;
}

.mj-tab {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 10px 16px;
  border-radius: var(--mj-radius-pill);
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.18);
  background: transparent;
  color: var(--mj-ink-body);
  cursor: pointer;
  transition: border-color var(--mj-dur) ease, background var(--mj-dur) ease,
              color var(--mj-dur) ease;
}

.mj-tab:hover { border-color: var(--mj-gold-line); }

/* Three ways the same pill says "this one": a link marked as the current page,
   a pressed button, and a label wrapping a checked radio. The last is what the
   book-a-call form uses — the radio itself is visually hidden, so without this
   the chosen day and window would look identical to the ones beside them. */
.mj-tab[aria-current="page"],
.mj-tab[aria-pressed="true"],
.mj-tab:has(input:checked) {
  background: var(--mj-gold);
  border-color: var(--mj-gold);
  color: var(--mj-bg);
}

/* The radio is off-screen, so its focus ring has to be drawn on the label. */
.mj-tab:has(input:focus-visible) { outline: 2px solid var(--mj-gold-hi); outline-offset: 2px; }

.mj-form {
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-xl);
  padding: clamp(24px, 4vw, 36px);
  background: var(--mj-fill);
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.mj-field {
  display: flex;
  flex-direction: column;
  gap: 8px;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mj-ink-muted);
}

/* The label is mono and uppercase; what the visitor types is neither. */
.mj-field input,
.mj-field select,
.mj-field textarea {
  font-family: var(--mj-font);
  font-size: var(--mj-size-body);
  color: var(--mj-ink);
  background: var(--mj-bg);
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.16);
  border-radius: var(--mj-radius);
  padding: 14px 16px;
  text-transform: none;
  letter-spacing: -0.01em;
}

.mj-field textarea { line-height: 1.5; resize: vertical; }

.mj-field input:focus,
.mj-field select:focus,
.mj-field textarea:focus { border-color: var(--mj-gold-line-hi); }

.mj-note {
  margin: 0;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  line-height: 1.6;
  color: var(--mj-ink-faint);
  text-transform: none;
  letter-spacing: 0;
}

.mj-contact-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 20px;
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-lg);
  padding: clamp(20px, 3vw, 28px);
  background: var(--mj-fill);
  color: var(--mj-ink);
  transition: transform var(--mj-dur) var(--mj-ease), border-color var(--mj-dur) ease,
              background var(--mj-dur) ease;
}

.mj-contact-row:hover {
  transform: translateY(-4px);
  border-color: var(--mj-gold-line);
  background: var(--mj-gold-wash);
  color: var(--mj-ink);
}

.mj-contact-row__label {
  display: block;
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: var(--mj-ink-dim);
}

.mj-contact-row__value {
  display: block;
  margin-block-start: 10px;
  font-size: clamp(19px, 2.6vw, 23px);
  letter-spacing: var(--mj-track-tight);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
}

/* -- Legal pages ----------------------------------------------------------
   The same trick the stack panel uses: a 1px gap in a tinted grid draws the
   hairlines, so there is no border to collapse between rows. */

.mj-legal {
  display: grid;
  gap: 1px;
  margin-block-start: 52px;
  background: var(--mj-line);
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-lg);
  overflow: hidden;
}

.mj-legal__row {
  background: var(--mj-bg);
  padding: clamp(24px, 4vw, 36px);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  gap: clamp(16px, 3vw, 40px);
  align-items: start;
}

.mj-legal__row h2 {
  margin: 0;
  font-weight: 600;
  font-size: clamp(21px, 3.4vw, 26px);
  letter-spacing: var(--mj-track-tight);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
}

.mj-legal__row p { margin: 0; max-width: 62ch; line-height: 1.7; }

/* -- Case study detail ---------------------------------------------------- */

.mj-cs__shot {
  margin-block-start: 44px;
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-xl);
  overflow: hidden;
  background: var(--mj-surface);
  padding: clamp(14px, 3vw, 22px);
}

.mj-cs__shot > div {
  aspect-ratio: 16 / 9;
  border-radius: var(--mj-radius);
  overflow: hidden;
  border: 1px solid rgb(var(--mj-ink-rgb) / 0.1);
}

.mj-cs__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

.mj-cs__facts { margin-block-start: 24px; }

.mj-cs__fact-k {
  font-family: var(--mj-font-mono);
  font-size: var(--mj-size-mono-sm);
  letter-spacing: var(--mj-track-mono-wide);
  text-transform: uppercase;
  color: var(--mj-ink-muted);
}

.mj-cs__fact-v {
  margin-block-start: 10px;
  font-size: clamp(19px, 2.6vw, 23px);
  letter-spacing: var(--mj-track-tight);
  font-variation-settings: 'wdth' var(--mj-wdth-sub);
}

.mj-cs__next {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
  margin-block-start: 52px;
  padding: clamp(24px, 4vw, 36px);
  border: 1px solid var(--mj-line);
  border-radius: var(--mj-radius-xl);
  color: var(--mj-ink);
  transition: border-color var(--mj-dur) ease, background var(--mj-dur) ease;
}

.mj-cs__next:hover {
  border-color: var(--mj-gold-line);
  background: var(--mj-gold-wash);
  color: var(--mj-ink);
}

.mj-cs__next span:last-child {
  font-size: clamp(24px, 3.4vw, 34px);
  letter-spacing: var(--mj-track-heading);
  font-variation-settings: 'wdth' var(--mj-wdth-heading);
}

/* -- Case study photos -----------------------------------------------------
   A grid of project stills. Each is a link to the full-size file, so it works
   without JavaScript and needs no viewer of its own. */
.mj-shots {
  margin-block-start: 32px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
  gap: clamp(12px, 2vw, 20px);
}

.mj-shot {
  display: block;
  border-radius: var(--mj-radius-lg);
  overflow: hidden;
  border: 1px solid var(--mj-line);
  background: var(--mj-fill-raised);
  transition: border-color var(--mj-dur) var(--mj-ease),
              transform var(--mj-dur) var(--mj-ease);
}
.mj-shot:hover,
.mj-shot:focus-visible { border-color: var(--mj-gold-line); transform: translateY(-2px); }

.mj-shot img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
  display: block;
}
/* --- v4.css --- */
/* ---------------------------------------------------------------------------
   v4 home page — "Mubarmij Site v4.dc.html", imported 2026-08-18.

   Scoped to `.mj-v4`, which only the home page carries, so none of this reaches
   the ten pages built on components.css. Every literal here resolves to a token
   in tokens.css; the design's inline `style="…"` attributes became these rules.

   Loaded last, after sections.css, for the same reason sections.css is loaded
   last: it overrides on source order rather than on specificity.
   --------------------------------------------------------------------------- */

.mj-v4 {
  position: relative;
  overflow-x: clip;
  font-size: var(--mj-body-v4);
  line-height: 1.7;
}

.mj-v4 :is(h1, h2, h3) {
  margin: 0;
  font-weight: 400;
  letter-spacing: var(--mj-track-heading);
  line-height: 1.02;
  text-wrap: balance;
}

/* base.css sets `overflow-wrap: anywhere` on h1/h2/h3/p/span/a. That property
   does more than allow a mid-word break: it collapses the element's min-content
   width to a single character, so in this page's auto-fit grids the text column
   shrinks far below its natural size. The hero headline is hand-broken into
   three lines and each sits in a one-line clipping box, so the extra wrapping
   put two visual lines inside a box built for one and the reveal dragged them
   through each other.

   The other ten pages have narrow content columns that want the global rule.
   This page does not, so it is switched off here rather than there. */
.mj-v4 :is(h1, h2, h3, p, span, a) { overflow-wrap: normal; }

.mj-v4 .mj-v4-wrap {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--mj-edge);
}

/* The design's uppercase mono-ish label, used as a kicker in seven places. */
.mj-v4-kicker {
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--mj-ink-50);
}

/* -- Primary call to action -------------------------------------------------
   The design's gold pill, values unchanged. Dark label on gold measures
   9.88:1. See templates/partials/wa_button.html for why it carries an arrow
   rather than the WhatsApp mark. */
.mj-v4-cta {
  display: inline-flex;
  align-items: center;
  border-radius: var(--mj-radius-pill);
  background: var(--mj-gold);
  color: var(--mj-bg);
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
  transition: filter var(--mj-dur) var(--mj-ease);
}
.mj-v4-cta:hover { filter: brightness(1.06); color: var(--mj-bg); }
.mj-v4-cta--sm { gap: 10px; padding: 14px 22px; font-size: 15px; }
.mj-v4-cta--md { gap: 12px; padding: 18px 30px; }
.mj-v4-cta--lg { gap: 14px; padding: 22px 42px; font-size: 18px; }

/* At the narrow end the large pill is wider than the viewport — 333px of
   button in a 320px window. The design has no rule for it because its own
   narrow block stops at the type scale. Let the label wrap instead of running
   off the edge; the pill keeps its shape. */
@media (max-width: 400px) {
  .mj-v4-cta--lg { padding: 20px 28px; font-size: 17px; white-space: normal; }
  .mj-v4-cta--md { padding: 16px 24px; white-space: normal; }
}
.mj-v4-cta svg { flex: none; }

/* Secondary, outlined. */
.mj-v4-ghost {
  padding: 18px 30px;
  border-radius: var(--mj-radius-pill);
  border: 1px solid var(--mj-line-soft);
  color: var(--mj-ink);
  text-decoration: none;
  transition: border-color var(--mj-dur) var(--mj-ease);
}
.mj-v4-ghost:hover { border-color: var(--mj-gold-30); }

/* -- 0 — Fixed nav ---------------------------------------------------------
   Starts transparent with a transparent hairline; initNav() in home-v4.js
   fades in the surface once the hero is 80px behind. Both properties are
   declared here so the un-animated state is the honest one. */
.mj-v4-nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 100;
  border-block-end: 1px solid transparent;
  background: rgb(var(--mj-bg-rgb) / 0);
}
.mj-v4-nav > div {
  max-width: 1280px;
  margin-inline: auto;
  padding: 22px var(--mj-edge);
  display: flex;
  align-items: center;
  gap: 32px;
}
.mj-v4-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  color: var(--mj-ink);
  text-decoration: none;
}
.mj-v4-brand img { width: 36px; height: 36px; display: block; }
.mj-v4-brand span { font-size: 18px; font-weight: 600; letter-spacing: -0.02em; }

.mj-v4-navlinks {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 32px;
  margin-inline-start: auto;
  font-size: 15px;
}
/* The underline is a scaleX(0) span the pointer timeline grows. Its
   transform-origin flips in RTL — see initMicroInteractions(). */
.mj-v4-navlinks a {
  position: relative;
  color: var(--mj-ink-70);
  padding-block: 4px;
  text-decoration: none;
}
.mj-v4-navlinks a > span {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  height: 1px;
  background: var(--mj-gold);
  transform: scaleX(0);
}

/* Language toggle. Present and functional in the markup, hidden until Arabic
   copy exists — see docs/DESIGN-IMPORT.md. Remove the [hidden] to ship it. */
.mj-v4-lang {
  padding: 10px 16px;
  border: 1px solid var(--mj-line-soft);
  border-radius: var(--mj-radius-pill);
  background: none;
  color: var(--mj-ink-70);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}

/* -- 1 — Hero -------------------------------------------------------------- */
.mj-v4-hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  padding: clamp(160px, 20vh, 240px) var(--mj-edge) clamp(96px, 14vh, 160px);
}
/* The backdrop v4 uses in place of the canvas starfield the other pages carry. */
.mj-v4-mesh {
  position: absolute;
  inset: -20%;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(46% 40% at 22% 26%, var(--mj-gold-12), transparent 68%),
    radial-gradient(40% 36% at 78% 62%, rgb(18 24 42 / 0.9), transparent 70%);
}
.mj-v4-hero__inner {
  position: relative;
  z-index: 1;
  max-width: 1280px;
  margin-inline: auto;
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 520px), 1fr));
  gap: var(--mj-gap-head);
  align-items: center;
}
/* The headline is sized by the column it sits in, not by the viewport.

   The design sizes it clamp(68px, 9vw, 124px). Measured against the design's
   own grid, the longest line ("your business", 13 characters at roughly 0.5em
   each) needs ~806px at the 124px cap, while the column is 547px at 1440 and
   576px at 1920 — the two-column breakpoint arrives long before the font stops
   growing. It overflows at 375 too. The design was authored at a width where
   the grid is still one column and 9vw resolves small, so the mismatch never
   showed.

   Container units fix it at the root: 14cqw of the text column keeps the three
   hand-broken lines intact at every width, and the design's 124px ceiling still
   applies wherever the column is wide enough to carry it. */
/* `min-width: 0` is load-bearing. A `1fr` grid track floors at the content's
   min-content width, and `white-space: nowrap` on the headline lines makes that
   the width of a whole line — so the column inflates to fit an unwrappable
   phrase and pushes off the viewport, where overflow-x: clip hides it. This is
   the same class of bug as the `overflow-wrap: anywhere` one, from the opposite
   direction: both let an intrinsic size decide a track that should be fluid. */
.mj-v4-hero__inner > div { min-width: 0; }
.mj-v4-hero__inner > div:first-child { container-type: inline-size; }

.mj-v4-hero h1 {
  margin-block-start: 32px;
  /* The design caps the headline at 15ch. That is a wrapping hint there,
     where the lines wrap; here each line is `nowrap` inside a clipping box,
     so a 22-character line against a 15ch cap is not a narrower headline, it
     is a headline with its last word cut off. The cap follows the content
     instead, and the font size below keeps it inside the column. */
  max-width: calc(var(--mj-hero-ch, 15) * 1ch);
  /* Three sizes, smallest wins, with a floor under all of them:
       14cqw    the design's proportion, correct while the lines are short
       100cqw/n the width that makes the longest line fit the column, where
                `n` is its character count times an average advance for
                Archivo at weight 800. Measured on the rendered headline
                (0.411) rather than guessed, with headroom -- the template
                sets --mj-hero-ch
       124px    the design's ceiling
     Without the middle term a headline broken into fewer, longer lines is
     clipped by .mj-v4-line's own overflow: hidden, which is how "your
     business runs on." shipped reading "your business run". */
  font-size: max(
    28px,
    min(14cqw, calc(100cqw / (var(--mj-hero-ch, 14) * 0.45)), 124px)
  );
}

/* Each line is one line. Without this the clipping box and the reveal disagree
   about how tall a line is, which is the bug this stage exists to fix. */
.mj-v4-line > i { white-space: nowrap; }
/* Each line is a clipping box; the <i> inside is what translates up from
   yPercent 100. Without the box the mask does not exist and the words slide
   over the line above. */
.mj-v4-line { display: block; overflow: hidden; padding-block-end: 0.06em; }
.mj-v4-line > i { display: block; font-style: normal; }

.mj-v4-hero__sub {
  margin-block-start: 32px;
  max-width: 46ch;
  color: var(--mj-ink-body);
}
.mj-v4-hero__cta {
  margin-block-start: 48px;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}

.mj-v4-scene {
  position: relative;
  aspect-ratio: 4 / 3.5;
  min-height: 340px;
}
.mj-v4-card {
  position: absolute;
  margin: 0;
  border-radius: var(--mj-r-md);
  overflow: hidden;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  box-shadow: var(--mj-shadow-v4);
}
.mj-v4-card img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  object-position: top center;
  display: block;
}
/* Three overlapping shots. The rotations are the design's. */
.mj-v4-card--a { inset-block-start: 0;   inset-inline-start: 0;   z-index: 3; width: 58%; transform: rotate(-5deg); }
.mj-v4-card--b { inset-block-start: 30%; inset-inline-end: 0;     z-index: 2; width: 58%; transform: rotate(4deg); }
.mj-v4-card--c { inset-block-start: 62%; inset-inline-start: 10%; z-index: 1; width: 52%; transform: rotate(-2deg); }

/* The card rotations are the one place a physical direction is baked into a
   transform. Mirrored so the stack leans the same way relative to the text. */
[dir="rtl"] .mj-v4-card--a { transform: rotate(5deg); }
[dir="rtl"] .mj-v4-card--b { transform: rotate(-4deg); }
[dir="rtl"] .mj-v4-card--c { transform: rotate(2deg); }

.mj-v4-hint {
  position: absolute;
  inset-block-end: 40px;
  inset-inline-start: var(--mj-edge);
}

@media (max-width: 760px) {
  .mj-v4-nav > div { flex-wrap: wrap; row-gap: 14px; padding-block: 16px; }
  .mj-v4-navlinks { order: 3; width: 100%; margin-inline-start: 0; gap: 8px 24px; font-size: 15px; }

  /* The nav wraps to three rows below 400px — brand, call to action, then the
     link row — and runs 184px tall, while the hero's top padding tops out at
     162px. The label sat 22px underneath it. The design has the same collision
     and its narrow block only retunes the type scale, so it was never seen at
     this width. Clear the nav instead of moving it. */
  .mj-v4-hero { padding-block-start: clamp(200px, 26vh, 240px); }

  /* The design keeps min-height: 340px here. With an aspect-ratio and no
     definite height, that floor drives the *width*: 340 x 4/3.2 = 425px inside
     a 330px column at 375px, so the card stack ran off both edges and was
     clipped. Dropping the floor lets the width lead and the height follow. */
  .mj-v4-scene { aspect-ratio: 4 / 3.2; min-height: 0; }
}

/* Nothing here moves for a visitor who asked for less motion. home-v4.js
   creates no timeline in that case, so the final state has to be the state
   the stylesheet already describes — which it is: no rule above hides
   anything. This block only stops the CSS-side transitions. */
@media (prefers-reduced-motion: reduce) {
  .mj-v4 *,
  .mj-v4 *::before,
  .mj-v4 *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}

/* -- 2 — Trust marquee -----------------------------------------------------
   One continuous x tween in home-v4.js, wrapped with gsap.utils.wrap. The
   track holds the logo list twice so the wrap point is invisible; the template
   renders the second copy aria-hidden so a screen reader hears each client
   once. */
.mj-v4-trust { padding-block: clamp(96px, 12vw, 180px); overflow: hidden; }
.mj-v4-trust__label {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--mj-edge);
  margin-block-end: clamp(40px, 5vw, 64px);
}
.mj-v4-marquee-wrap {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.mj-v4-marquee {
  display: flex;
  align-items: center;
  gap: clamp(24px, 3vw, 48px);
  width: max-content;
}
.mj-v4-marquee > span {
  flex: none;
  display: grid;
  place-items: center;
  height: 104px;
  width: auto;
  min-width: 176px;
  max-width: 300px;
  padding: 18px 30px;
  border-radius: var(--mj-r-md);
  background: var(--mj-logo-plaque);
}
.mj-v4-marquee img {
  max-height: 64px;
  max-width: 240px;
  width: auto;
  height: auto;
  object-fit: contain;
  display: block;
}

/* -- 3 — Services ----------------------------------------------------------
   The heading column is sticky rather than pinned: no pin-spacer means no
   layout shift and nothing to rebuild when direction flips. */
.mj-v4-services { padding: var(--mj-sec-y) var(--mj-edge); }
.mj-v4-services__grid {
  max-width: 1280px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
  gap: var(--mj-gap-head);
  align-items: start;
}
.mj-v4-services__head { position: sticky; inset-block-start: clamp(120px, 18vh, 200px); }
.mj-v4-services__head h2 {
  margin-block-start: 32px;
  font-size: var(--mj-d2);
  max-width: 18ch;
}
.mj-v4-services__head p {
  margin-block-start: 32px;
  max-width: 42ch;
  color: var(--mj-ink-body);
}
.mj-v4-services__list { display: grid; gap: var(--mj-gap-row); }

.mj-v4-svc {
  padding: clamp(32px, 4vw, 48px);
  border: 1px solid var(--mj-line-soft);
  border-radius: var(--mj-r-lg);
  background: var(--mj-surface-v4);
}
.mj-v4-svc__meta {
  display: flex;
  justify-content: space-between;
  gap: 24px;
}
.mj-v4-svc__meta > span:first-child { color: var(--mj-gold); }
.mj-v4-svc h3 { margin-block-start: 32px; font-size: var(--mj-d3); }
.mj-v4-svc p {
  margin-block-start: 16px;
  max-width: 46ch;
  color: var(--mj-ink-body);
}

/* -- 4 — Work ---------------------------------------------------------------
   Alternating rows: text and a phone frame, sides swapping each row. The swap
   is DOM order, as the design has it, so it survives an RTL flip untouched. */
.mj-v4-work { padding: var(--mj-sec-y) var(--mj-edge); }
.mj-v4-work__inner { max-width: 1280px; margin-inline: auto; }
.mj-v4-work h2 {
  margin-block-start: 32px;
  font-size: var(--mj-d2);
  max-width: 20ch;
}
.mj-v4-work__rows {
  margin-block-start: var(--mj-gap-head);
  display: grid;
  gap: var(--mj-gap-case);
}

.mj-v4-case {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr));
  gap: var(--mj-gap-head);
  align-items: center;
}
/* See the hero: a nowrap title would otherwise set this track's floor. */
.mj-v4-case > * { min-width: 0; }
/* Same clipping-box construction as the hero: the box is one line tall and the
   <i> inside is what rises from yPercent 100. */
.mj-v4-case__title { font-size: var(--mj-d2); }
.mj-v4-case__title span { display: block; overflow: hidden; padding-block-end: 0.08em; }
.mj-v4-case__title i { display: block; font-style: normal; white-space: nowrap; }

.mj-v4-case__out {
  margin-block-start: 24px;
  max-width: 40ch;
  color: var(--mj-ink);
}
.mj-v4-case__tags {
  margin-block-start: 32px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  font-size: 14px;
  color: var(--mj-ink-50);
}
.mj-v4-case__tags span {
  padding: 8px 16px;
  border: 1px solid var(--mj-line-soft);
  border-radius: var(--mj-radius-pill);
}
.mj-v4-case__link {
  display: inline-block;
  margin-block-start: 32px;
  font-size: 15px;
}

.mj-v4-case__media {
  position: relative;
  display: flex;
  justify-content: center;
  padding-block: clamp(24px, 4vw, 48px);
  border-radius: var(--mj-r-lg);
  background: radial-gradient(70% 60% at 50% 30%, var(--mj-gold-12), transparent 70%);
}
.mj-v4-phone {
  position: relative;
  width: min(300px, 66%);
  padding: 12px;
  border-radius: 44px;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  box-shadow: var(--mj-shadow-v4);
}
.mj-v4-phone__notch {
  position: absolute;
  inset-block-start: 26px;
  /* Same reason as the CTA glow: centre with logical insets, not a physical
     transform that cannot flip. */
  inset-inline: 0;
  margin-inline: auto;
  z-index: 2;
  width: 82px;
  height: 24px;
  border-radius: var(--mj-radius-pill);
  background: var(--mj-bg);
}
.mj-v4-phone video {
  width: 100%;
  /* 9/16, matching the clips, with `contain` so none of them is cropped.

     The design frames these at 9/17 and covers, which crops the sides of every
     source: three of the four reels are 540x960 (9:16) and amwally-showcase is
     720x1560 (9:19.5). These are recordings of app screens, so a cropped edge
     takes readable interface with it — the OGS HUB frame was losing the right
     third of the panel.

     The taller outlier now sits inside the frame with a narrow bar either side,
     in the same colour as the phone body, which reads as bezel rather than as
     a fault. aspect-ratio still reserves the box before the clip loads, so this
     costs no layout shift. */
  aspect-ratio: 9 / 16;
  object-fit: contain;
  display: block;
  border-radius: 34px;
  background: var(--mj-bg);
}
/* Revealed on hover by the pointer timeline. Starts hidden here so the
   un-scripted page does not show a floating chip over the artwork. */
.mj-v4-case__chip {
  position: absolute;
  inset-block-end: 20px;
  inset-inline-start: 20px;
  padding: 12px 20px;
  border-radius: var(--mj-radius-pill);
  background: var(--mj-gold);
  color: var(--mj-bg);
  font-size: 14px;
  font-weight: 600;
  opacity: 0;
}
/* Keyboard users get the chip too: the media is a link, and hover-only
   affordances are invisible to them. */
.mj-v4-case__media:focus-visible .mj-v4-case__chip { opacity: 1; }

/* -- 5 — Process -----------------------------------------------------------
   Pinned horizontal run above 860px: the track translates while the section
   holds the viewport, and the accent rule under it draws with the same
   progress, so the line reads as the scrollbar. Below 860px it is a stacked
   column and the rule scrubs on its own. */
.mj-v4-process {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: clamp(64px, 9vh, 160px);
}
.mj-v4-process__head {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--mj-edge);
}
.mj-v4-process__head h2 {
  margin-block-start: 32px;
  font-size: var(--mj-d2);
  max-width: 18ch;
}

.mj-v4-process__viewport {
  margin-block-start: clamp(40px, 6vh, 96px);
  overflow: hidden;
}
.mj-v4-process__track {
  display: flex;
  align-items: stretch;
  gap: clamp(40px, 5vw, 80px);
  padding-inline: var(--mj-edge);
  width: max-content;
}
.mj-v4-step {
  flex: none;
  width: min(78vw, 420px);
  padding-block: clamp(16px, 3vh, 48px);
}
.mj-v4-step__no {
  font-size: clamp(56px, 7vw, 96px);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--mj-gold-numeral);
}
.mj-v4-step__when { margin-block-start: clamp(20px, 4vh, 48px); }
.mj-v4-step h3 { margin-block-start: 16px; font-size: var(--mj-d3); }
.mj-v4-step p {
  margin-block-start: 24px;
  max-width: 30ch;
  color: var(--mj-ink-body);
}

.mj-v4-process__rule {
  margin-block-start: clamp(28px, 5vh, 72px);
  padding-inline: var(--mj-edge);
  max-width: 1280px;
  margin-inline: auto;
}
.mj-v4-process__rule svg {
  width: 100%;
  height: 2px;
  display: block;
  overflow: visible;
}

/* Stacked below the pin threshold. The design's own narrow block; the track
   stops being a track and the viewport stops clipping it. */
@media (max-width: 859px) {
  .mj-v4-process { min-height: 0; }
  .mj-v4-process__viewport { overflow: visible; }
  .mj-v4-process__track {
    flex-direction: column;
    width: 100%;
    gap: clamp(40px, 8vw, 64px);
  }
  .mj-v4-step { width: 100%; }
}

/* -- 6 — Numbers ------------------------------------------------------------
   The one band that breaks the page's rhythm: light ground, dark text. Gold
   has to darken to --mj-gold-deep to carry here; --mj-gold on this background
   is 1.6:1, while --mj-gold-deep measures 5.01:1. */
.mj-v4-numbers {
  background: var(--mj-ink);
  color: var(--mj-bg);
  padding: clamp(96px, 12vw, 200px) var(--mj-edge);
}
.mj-v4-numbers__grid {
  max-width: 1280px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: var(--mj-gap-row);
}
.mj-v4-stat__value {
  font-size: clamp(56px, 7vw, 96px);
  line-height: 1;
  letter-spacing: -0.04em;
  color: var(--mj-gold-deep);
  /* The count-up rewrites this text every frame. Without a reserved box the
     band reflows as the digits grow. */
  font-variant-numeric: tabular-nums;
}
.mj-v4-stat__label {
  margin-block-start: 24px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: rgb(var(--mj-bg-rgb) / 0.6);
}

/* -- 7 — Proof --------------------------------------------------------------
   Drag or swipe, snapping card to card. GSAP's inertia plugin is Club-only, so
   the design snaps on release rather than coasting; kept as-is. */
.mj-v4-proof { padding: var(--mj-sec-y) 0; overflow: hidden; }
.mj-v4-proof__head {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--mj-edge);
}
.mj-v4-proof__head h2 {
  margin-block-start: 32px;
  font-size: var(--mj-d2);
  max-width: 22ch;
}
.mj-v4-proof__viewport {
  margin-block-start: var(--mj-gap-head);
  cursor: grab;
}
.mj-v4-proof__track {
  display: flex;
  gap: clamp(24px, 3vw, 48px);
  padding-inline: var(--mj-edge);
  width: max-content;
}
.mj-v4-quote {
  flex: none;
  margin: 0;
  width: min(84vw, 620px);
  padding: clamp(40px, 5vw, 72px);
  border: 1px solid var(--mj-line-soft);
  border-radius: var(--mj-r-lg);
  background: var(--mj-surface-v4);
}
.mj-v4-quote p {
  margin: 0;
  font-size: clamp(24px, 2.6vw, 34px);
  line-height: 1.25;
  letter-spacing: -0.02em;
}
.mj-v4-quote figcaption {
  margin-block-start: 40px;
  font-size: 15px;
  color: var(--mj-ink-50);
}
.mj-v4-proof__nav {
  max-width: 1280px;
  margin-inline: auto;
  padding-inline: var(--mj-edge);
  margin-block-start: 48px;
  display: flex;
  gap: 16px;
}
.mj-v4-proof__nav button {
  width: 56px;
  height: 56px;
  border: 1px solid var(--mj-line-soft);
  border-radius: var(--mj-radius-pill);
  background: none;
  color: var(--mj-ink);
  font: inherit;
  cursor: pointer;
  transition: border-color var(--mj-dur) var(--mj-ease);
}
.mj-v4-proof__nav button:hover { border-color: var(--mj-gold-30); }

/* -- 8 — CTA ---------------------------------------------------------------- */
.mj-v4-cta-section {
  position: relative;
  padding: var(--mj-sec-y) var(--mj-edge);
  text-align: center;
}
.mj-v4-cta-section__glow {
  position: absolute;
  inset-block-start: 10%;
  /* Centred without a directional transform. The design pairs
     `inset-inline-start: 50%` with `translateX(-50%)`: the offset flips under
     RTL but the transform does not, so the glow ended up half a screen off and
     hanging outside the section. Two logical insets and an auto margin centre
     the same way whichever direction the page runs. */
  inset-inline: 0;
  margin-inline: auto;
  width: min(80vw, 720px);
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--mj-gold-12), transparent 62%);
  pointer-events: none;
}
.mj-v4-cta-section__inner {
  position: relative;
  max-width: 900px;
  margin-inline: auto;
  container-type: inline-size;
}
.mj-v4-cta-section h2 { font-size: clamp(34px, 14cqw, 124px); }
.mj-v4-cta-section p {
  margin-block-start: 32px;
  margin-inline: auto;
  max-width: 46ch;
  color: var(--mj-ink-body);
}
.mj-v4-cta-section__row {
  margin-block-start: 64px;
  display: flex;
  justify-content: center;
}

/* -- Footer ----------------------------------------------------------------- */
.mj-v4-footer {
  border-block-start: 1px solid var(--mj-line-soft);
  padding: clamp(64px, 8vw, 120px) var(--mj-edge);
}
.mj-v4-footer__grid {
  max-width: 1280px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: var(--mj-gap-row);
}
.mj-v4-footer__brand { display: flex; align-items: center; gap: 14px; }
.mj-v4-footer__brand img { width: 30px; height: 30px; display: block; }
.mj-v4-footer__brand span { font-size: 17px; font-weight: 600; letter-spacing: -0.02em; }
.mj-v4-footer__blurb {
  margin-block-start: 24px;
  max-width: 28ch;
  color: var(--mj-ink-50);
  font-size: 15px;
}
.mj-v4-footer__links {
  margin-block-start: 24px;
  display: grid;
  gap: 12px;
  font-size: 15px;
}
.mj-v4-footer__links a { color: var(--mj-ink-70); }
.mj-v4-footer__links a:hover { color: var(--mj-gold-hi); }
.mj-v4-footer__bottom {
  max-width: 1280px;
  margin-inline: auto;
  margin-block-start: clamp(48px, 6vw, 80px);
  padding-block-start: 32px;
  border-block-start: 1px solid var(--mj-line-soft);
  font-size: 14px;
  color: var(--mj-ink-50);
}

/* -- Window frame ----------------------------------------------------------
   For a clip that is showing a website rather than an app: browser chrome
   instead of a handset. Same box and the same clip sizing — only the dressing
   differs, so nothing about the row's layout changes with it. */
.mj-v4-phone--window {
  /* Browser chrome rather than a handset, and wider than the phone frame. The
     window takes the clip's own shape: cropping it to a landscape window did
     show the website larger, but it cut the top and bottom off the reel, and
     the whole clip is what should be seen. So the frame is portrait because
     the clip is — the title bar is what says "website" here, not the
     proportions. */
  width: min(420px, 82%);
  border-radius: 16px;
  padding: 0;
  padding-block-start: 26px;
}
.mj-v4-phone--window video {
  aspect-ratio: 9 / 16;
  object-fit: contain;
  border-radius: 0 0 14px 14px;
}

.mj-v4-window__bar {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 26px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding-inline: 12px;
  border-start-start-radius: 16px;
  border-start-end-radius: 16px;
  background: var(--mj-bg);
}
.mj-v4-window__bar i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--mj-ink-50);
}

/* -- Built for the Gulf ------------------------------------------------------

   Three cards, one row on desktop. Unlike Selected work — which alternates a
   large clip against copy, and caps at four — this section is a grid, because
   its job is "here are the Gulf builds" rather than "here is one project in
   depth". Each card links to the same case study page.

   The section renders only when a case study carries region "Gulf"; see
   templates/home.html. Nothing here assumes a fixed count: one card, two or
   three all sit correctly.
   ---------------------------------------------------------------------------- */

/* Blueprint treatment ------------------------------------------------------
   The Gulf band is the one section drawn as a technical sheet: a faint drafting
   grid, a hairline frame with corner ticks, and the section's own label set in
   the mono face. It sits directly under the hero, so it has to read as a
   deliberate change of register rather than another card row.

   The blue is --mj-blue-rgb, which is already in the palette (the hero's
   ambient glow uses it) — the design admits no new hues, so the sheet is drawn
   in the one blue this site already owns, at low alpha over the same ground. */
.mj-v4-gulf {
  padding-block: var(--mj-sec-y);
  position: relative;
  isolation: isolate;
}

/* The drafting grid: two repeating-linear-gradients, faded out at the edges so
   the sheet does not end on a hard line. Decorative and non-interactive. */
.mj-v4-gulf::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-image:
    repeating-linear-gradient(to right,
      rgb(var(--mj-blue-rgb) / 0.16) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(to bottom,
      rgb(var(--mj-blue-rgb) / 0.16) 0 1px, transparent 1px 64px);
  -webkit-mask-image: radial-gradient(120% 90% at 50% 50%, #000 40%, transparent 100%);
          mask-image: radial-gradient(120% 90% at 50% 50%, #000 40%, transparent 100%);
  /* Driven by home-v4.js: a pseudo-element cannot be selected by GSAP, so the
     sheet fades in through a custom property on the section instead. */
  opacity: var(--gulf-sheet, 1);
}

/* The sheet's frame, with a tick at each corner. */
.mj-v4-gulf__inner {
  position: relative;
  border: 1px solid rgb(var(--mj-blue-rgb) / 0.38);
  border-radius: var(--mj-r-sm);
  padding-block: clamp(32px, 5vw, 56px);
}

.mj-v4-gulf__inner::before,
.mj-v4-gulf__inner::after {
  content: "";
  position: absolute;
  width: 12px;
  height: 12px;
  border: 1px solid var(--mj-gold);
  pointer-events: none;
  opacity: var(--gulf-tick, 1);
}
.mj-v4-gulf__inner::before {
  inset-block-start: -6px;
  inset-inline-start: -6px;
  border-inline-end: 0;
  border-block-end: 0;
}
.mj-v4-gulf__inner::after {
  inset-block-end: -6px;
  inset-inline-end: -6px;
  border-inline-start: 0;
  border-block-start: 0;
}

/* The label reads as a drawing number rather than a heading. */
.mj-v4-gulf .mj-v4-kicker {
  font-family: var(--mj-font-mono);
  letter-spacing: var(--mj-track-mono-wide);
}

.mj-v4-gulf__inner {
  max-width: var(--mj-container);
  margin-inline: auto;
  padding-inline: var(--mj-edge);
}

@media (prefers-reduced-motion: reduce) {
  .mj-v4-gulf::before { opacity: 0.6; }
}

.mj-v4-gulf__lede {
  max-width: 56ch;
  margin-block: 14px 0;
  color: var(--mj-ink-70);
  font-size: var(--mj-body-v4);
}

.mj-v4-gulf__grid {
  display: grid;
  gap: 22px;
  margin-block-start: clamp(36px, 4vw, 64px);
  /* auto-FILL, not auto-fit. The two differ only when there are fewer cards
     than tracks, which is exactly this section's normal state while the Gulf
     work is still being added: auto-fit collapses the empty tracks and lets one
     project stretch across the full 1240px container as a single enormous card.
     auto-fill keeps the tracks, so one project renders the same width as it
     will when there are three beside it.

     290px, not 300px, is deliberate and tight. --mj-edge is clamp(48px, 8vw,
     160px), so on a 1920 screen the side padding grows to 153px and the grid
     is only 933px wide — 3x300 + 2x22 = 944 misses it by 11px, which would
     drop a 1920 screen to two columns while 1440 kept three. Fewer columns on
     a wider screen reads as a bug. 3x290 + 2x22 = 914 fits both. */
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
}

.mj-v4-gulfcard {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--mj-gold-12);
  border-radius: var(--mj-r-md);
  background: var(--mj-surface-v4);
  /* Only transform and opacity animate; the border colour is a paint change on
     hover and is cheap, but the lift is what the eye reads. */
  transition: transform var(--mj-dur) var(--mj-ease),
              border-color var(--mj-dur) ease;
}

.mj-v4-gulfcard:hover {
  transform: translate3d(0, -4px, 0);
  border-color: var(--mj-gold-30);
}

/* The card contains a stretched link, so the focus ring belongs on the card
   rather than on an invisible anchor the size of the whole thing. */
.mj-v4-gulfcard:focus-within {
  outline: 2px solid var(--mj-gold);
  outline-offset: 3px;
}

.mj-v4-gulfcard__media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--mj-surface-2);
  border-block-end: 1px solid var(--mj-gold-12);
}

.mj-v4-gulfcard__media img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  transition: transform var(--mj-dur-slow) var(--mj-ease);
}

.mj-v4-gulfcard:hover .mj-v4-gulfcard__media img { transform: scale(1.04); }

.mj-v4-gulfcard__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: 9px;
  padding: 20px;
}

.mj-v4-gulfcard__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  color: var(--mj-ink-50);
  font-family: var(--mj-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.mj-v4-gulfcard__tag {
  padding: 3px 9px;
  border: 1px solid var(--mj-gold-30);
  border-radius: var(--mj-radius-pill);
  color: var(--mj-gold);
}

.mj-v4-gulfcard__title {
  margin: 0;
  font-size: 21px;
  line-height: 1.2;
  letter-spacing: -0.01em;
}

.mj-v4-gulfcard__out {
  margin: 0;
  color: var(--mj-ink-70);
  font-size: 14.5px;
  line-height: 1.55;
}

.mj-v4-gulfcard__link {
  margin-block-start: auto;
  padding-block-start: 6px;
  color: var(--mj-gold);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
}

/* Stretches the anchor over the whole card, so the card is the click target and
   no interactive element has to be nested inside another. Same technique
   .mj-case--linked uses on /case-studies. */
.mj-v4-gulfcard__stretch {
  position: absolute;
  inset: 0;
}

@media (prefers-reduced-motion: reduce) {
  .mj-v4-gulfcard,
  .mj-v4-gulfcard__media img { transition: none; }
  .mj-v4-gulfcard:hover { transform: none; }
  .mj-v4-gulfcard:hover .mj-v4-gulfcard__media img { transform: none; }
}

/* -- Hero promo film ------------------------------------------------------
   The design puts a full-viewport clip behind the hero with a gradient scrim
   over it. Two things it does not say, both of which decide whether the hero
   is readable:

   The scrim is opaque at the bottom and clear at the top, so the headline —
   which sits low in the design's layout — always has ground behind it while
   the picture stays visible above. It is painted with the page ground rather
   than black so it dissolves into the section below instead of ending on a
   seam.

   `object-fit: cover` is what lets one 16:9 file sit behind a 9:16 phone
   viewport without letterboxing. It crops, so nothing load-bearing may live
   at the edges of the frame — the promo is centred artwork, which is why it
   survives the crop. */
.mj-v4-hero--film { position: relative; isolation: isolate; }

.mj-v4-hero__film {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.mj-v4-hero__film video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The poster is a flat logotype card; scaling it the same way the video is
     scaled keeps the first paint and the first frame identical. */
  object-position: center;
  display: block;
}

.mj-v4-hero__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgb(var(--mj-bg-rgb) / 0.55) 0%,
    rgb(var(--mj-bg-rgb) / 0.18) 22%,
    rgb(var(--mj-bg-rgb) / 0.30) 45%,
    rgb(var(--mj-bg-rgb) / 0.78) 74%,
    rgb(var(--mj-bg-rgb) / 0.95) 92%,
    rgb(var(--mj-bg-rgb) / 1) 100%
  );
}

/* Everything the hero already contained now has to sit above the film. The
   mesh keeps its place underneath: it is the fallback ground when no video
   is configured, and behind one it adds nothing worth compositing. */
.mj-v4-hero--film .mj-v4-hero__inner,
.mj-v4-hero--film .mj-v4-hint { position: relative; z-index: 2; }
.mj-v4-hero--film .mj-v4-mesh { z-index: 0; opacity: 0.35; }

/* The call-to-action arrow points at the label's end, so in Arabic it points
   the other way. A glyph is not text: `dir` reverses the writing, not the
   path, and an arrow left pointing right in an RTL layout reads as "back". */
[dir="rtl"] .mj-v4-cta svg { transform: scaleX(-1); }

/* A second scrim, across rather than down.

   The vertical gradient alone darkens the bottom of the frame, which is where
   the design puts its hero text. This layout puts the text in a column beside
   the product shots, so the words sit against the middle of the picture --
   and the promo's own captions ("MOBILE APPS", "WEB DEVELOPMENT") are set
   large in the same gold as the headline. Two golds at the same size is not a
   contrast problem a colour token can solve; the picture has to recede.

   Anchored to the text side rather than to the left: the angle is a custom
   property so RTL flips it, because a gradient direction is geometry and
   `dir` does not touch it. */
.mj-v4-hero--film { --mj-hero-scrim: 90deg; }
[dir="rtl"] .mj-v4-hero--film { --mj-hero-scrim: 270deg; }

.mj-v4-hero__scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    var(--mj-hero-scrim),
    rgb(var(--mj-bg-rgb) / 0.94) 0%,
    rgb(var(--mj-bg-rgb) / 0.82) 28%,
    rgb(var(--mj-bg-rgb) / 0.42) 52%,
    transparent 72%
  );
}

/* Below the breakpoint the text column is the whole width, so a side scrim
   has no side to sit on and the vertical one does the work alone. */
@media (max-width: 860px) {
  .mj-v4-hero__scrim::after { display: none; }
  .mj-v4-hero__scrim { background: linear-gradient(
      180deg,
      rgb(var(--mj-bg-rgb) / 0.72) 0%,
      rgb(var(--mj-bg-rgb) / 0.55) 30%,
      rgb(var(--mj-bg-rgb) / 0.80) 62%,
      rgb(var(--mj-bg-rgb) / 0.97) 88%,
      rgb(var(--mj-bg-rgb) / 1) 100%); }
}

/* -- FAQ ------------------------------------------------------------------
   The design draws a scripted accordion: a button, an aria-expanded, and a
   height transition. This uses <details>/<summary> instead, which gets the
   same behaviour from the platform, is reachable by find-in-page while
   closed, and works before the bundle arrives. Script only adds the
   one-open-at-a-time rule.

   `list-style: none` plus the ::-webkit-details-marker reset removes the
   native triangle in every engine; the "+" is drawn from the <i> so it can
   rotate. */
.mj-v4-faq {
  max-width: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding-block-end: var(--mj-section-y, 110px);
  padding-inline: 24px;
}

.mj-v4-faq__head { display: flex; flex-direction: column; gap: 16px; max-width: 62ch; }
.mj-v4-faq__head h2 {
  margin: 0;
  font-size: clamp(30px, 4.6vw, 62px);
  line-height: 0.94;
  letter-spacing: -0.035em;
}
.mj-v4-faq__head p { margin: 0; color: var(--mj-ink-body); font-size: var(--mj-lead, 17px); }

.mj-v4-faq__list {
  margin-block-start: 44px;
  border-block-start: 1px solid var(--mj-line-soft);
}

.mj-v4-faq__item { border-block-end: 1px solid var(--mj-line-soft); }

.mj-v4-faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  /* 44px minimum tap target, from the design's own accessibility note. */
  min-block-size: 44px;
  padding-block: 26px;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
  font-size: clamp(17px, 1.5vw, 21px);
  letter-spacing: -0.02em;
  transition: color 0.3s ease;
}
.mj-v4-faq__item summary::-webkit-details-marker { display: none; }
.mj-v4-faq__item summary:hover { color: var(--mj-gold); }

/* The marker is two bars; the vertical one hides when open, so "+" becomes
   "−" without swapping a glyph and without a second element to keep aligned. */
.mj-v4-faq__item summary i {
  position: relative;
  flex: none;
  inline-size: 16px;
  block-size: 16px;
}
.mj-v4-faq__item summary i::before,
.mj-v4-faq__item summary i::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 0;
  inline-size: 16px;
  block-size: 2px;
  margin-block-start: -1px;
  background: var(--mj-gold);
  transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
}
.mj-v4-faq__item summary i::after { transform: rotate(90deg); }
.mj-v4-faq__item[open] summary i::after { transform: rotate(0deg); opacity: 0; }

.mj-v4-faq__a { padding-block-end: 28px; }
.mj-v4-faq__a p {
  margin: 0;
  max-width: 76ch;
  color: var(--mj-ink-body);
  line-height: 1.6;
}

/* -- Animated code background --------------------------------------------
   A fixed layer behind every section: a drifting grid, columns of source
   scrolling upward, a terminal card floating, and a scan line crossing it.

   Everything animates `transform` and `opacity` only. Nothing here triggers
   layout, which is what lets a dozen simultaneous loops sit behind a page
   that also pins a section and scrubs a carousel.

   The design draws thirteen code columns unconditionally. That is thirteen
   composited layers of monospaced text, and on a phone it buys nothing: at
   375px the columns overlap into noise, and the device paying for it is the
   one with the smallest battery. Below 860px only the grid survives. */
.mj-v4-code {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  /* The layer is decorative and never interactive, so it is also never a
     scroll container and never a hit target. */
  contain: strict;
}

/* Sections paint above it. `.mj-v4` sets its own ground, so without this the
   background would be behind an opaque sheet and invisible.

   `position: static` is the guard, not decoration. The first version of this
   rule set `position: relative` on every child of .mj-v4 and took the fixed
   nav down with it — the nav is a child too, and it scrolled away with the
   page. Only elements that are not already positioned need lifting; the ones
   that are have chosen their own layer deliberately. */
.mj-v4 { position: relative; background: transparent; }
.mj-v4 > section,
.mj-v4 > div:not([class*="mj-v4-nav"]),
.mj-v4-footer { z-index: 2; }
.mj-v4 > section, .mj-v4-footer { position: relative; }

@keyframes mj-v4-grid  { from { transform: translate3d(0, 0, 0); }
                         to   { transform: translate3d(0, -64px, 0); } }
@keyframes mj-v4-code  { from { transform: translate3d(0, 0, 0); }
                         to   { transform: translate3d(0, -50%, 0); } }
@keyframes mj-v4-float { 0%, 100% { transform: translate3d(0, 0, 0) rotate(0deg); }
                         50%      { transform: translate3d(0, -26px, 0) rotate(6deg); } }
@keyframes mj-v4-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
@keyframes mj-v4-scan  { from { transform: translate3d(0, -100%, 0); }
                         to   { transform: translate3d(0, 900%, 0); } }
@keyframes mj-v4-glow  { 0%, 100% { opacity: 0.30; } 50% { opacity: 0.60; } }

/* The grid is inset past the viewport top and bottom so the 64px drift never
   exposes an edge. */
.mj-v4-code__grid {
  position: absolute;
  inset: -70px 0;
  background-image:
    linear-gradient(var(--mj-line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--mj-line-soft) 1px, transparent 1px);
  background-size: 64px 64px;
  opacity: 0.42;
  animation: mj-v4-grid 9s linear infinite;
  will-change: transform;
}

.mj-v4-code__cols { position: absolute; inset: 0; opacity: 0.055; overflow: hidden; }

/* Each column holds its snippet twice, so translating up by exactly half its
   own height lands on an identical frame and the loop has no seam. */
.mj-v4-code__col {
  position: absolute;
  inset-block-start: 0;
  animation: mj-v4-code 78s linear infinite;
  will-change: transform;
}
.mj-v4-code__col pre {
  margin: 0;
  white-space: pre;
  font-family: var(--mj-font-mono);
  font-size: 13px;
  line-height: 2.4;
  color: var(--mj-gold);
}

.mj-v4-code__term {
  position: absolute;
  inset-block-start: 18%;
  inset-inline-end: 6%;
  border: 1px solid var(--mj-gold);
  border-radius: 14px;
  overflow: hidden;
  opacity: 0.16;
  animation: mj-v4-float 15s ease-in-out infinite;
  will-change: transform;
}
.mj-v4-code__term b { display: flex; gap: 6px; padding: 10px 12px;
                      border-block-end: 1px solid var(--mj-gold); }
.mj-v4-code__term b i { inline-size: 8px; block-size: 8px; border-radius: 50%;
                        background: var(--mj-gold); }
.mj-v4-code__term pre { margin: 0; white-space: pre; padding: 14px;
                        font-family: var(--mj-font-mono); font-size: 12px;
                        line-height: 2; color: var(--mj-gold); }
.mj-v4-code__term pre u { text-decoration: none;
                          animation: mj-v4-blink 1.1s steps(1) infinite; }

.mj-v4-code__scan {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  block-size: 24vh;
  background: linear-gradient(180deg, transparent,
              rgb(var(--mj-gold-rgb) / 0.09), transparent);
  animation: mj-v4-scan 14s linear infinite;
  will-change: transform;
}

.mj-v4-code__glow {
  position: absolute;
  inset-block-start: 40%;
  inset-inline-start: 30%;
  inline-size: 46vw;
  block-size: 46vw;
  border-radius: 50%;
  background: radial-gradient(circle,
              rgb(var(--mj-blue-rgb) / 0.30) 0%, transparent 66%);
  animation: mj-v4-glow 11s ease-in-out infinite;
}

@media (max-width: 860px) {
  .mj-v4-code__cols,
  .mj-v4-code__term,
  .mj-v4-code__scan { display: none; }
  .mj-v4-code__grid { opacity: 0.3; }
}

/* -- Client rail, pinned to the top of the viewport ----------------------
   Above the nav in the stack and above the code background, opaque, so the
   nav's translucent pill has something solid to sit against rather than
   scrolling content. */
.mj-v4-rail {
  position: fixed;
  inset-inline: 0;
  inset-block-start: 0;
  z-index: 170;
  padding-block: 9px;
  overflow: hidden;
  background: var(--mj-bg);
  border-block-end: 1px solid var(--mj-line-soft);
}
.mj-v4-rail__wrap { overflow: hidden; }
.mj-v4-rail__track { display: flex; width: max-content; gap: 12px; }

/* White pills on a dark ground. The white is the plaque, not the mark: client
   logos are supplied on white and keying them out leaves halos, which is the
   same reason the header logo gained a plaque. */
.mj-v4-rail__pill {
  display: flex;
  align-items: center;
  gap: 11px;
  flex: none;
  background: #ffffff;
  border: 1px solid rgb(var(--mj-bg-rgb) / 0.12);
  border-radius: 999px;
  padding: 8px 20px 8px 10px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--mj-bg);
  white-space: nowrap;
}
.mj-v4-rail__pill img { inline-size: 22px; block-size: 22px; object-fit: contain; }
.mj-v4-rail__pill b { font-weight: 600; }

/* The rail occupies the top of the viewport permanently, so the nav drops
   below it and the page starts under both. Driven by one custom property so
   the three cannot disagree. */
.mj-v4 { --mj-rail-h: 56px; }
.mj-v4-nav { inset-block-start: calc(var(--mj-rail-h) + 18px); }
.mj-v4-hero { padding-block-start: calc(var(--mj-rail-h) + 96px); }

@media (max-width: 760px) {
  .mj-v4 { --mj-rail-h: 48px; }
  .mj-v4-rail__pill { padding: 6px 16px 6px 8px; font-size: 12.5px; }
  .mj-v4-rail__pill img { inline-size: 18px; block-size: 18px; }
}

/* -- Scroll progress ------------------------------------------------------
   Above everything, including the rail: it reports on the whole document, so
   being occluded by a fixed element would make it wrong exactly where it is
   most visible. The bar is a child rather than a background so the width
   animates without repainting the strip. */
.mj-v4-progress {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 3px;
  z-index: 200;
  pointer-events: none;
}
.mj-v4-progress i {
  display: block;
  block-size: 100%;
  inline-size: 0;
  background: var(--mj-gold);
  transform-origin: left center;
}
[dir="rtl"] .mj-v4-progress i { margin-inline-start: auto; }

/* -- Pointer ornaments ----------------------------------------------------
   A ring that follows the pointer and a soft light behind it. Both are
   `position: fixed` and moved with transforms only, so neither ever costs a
   layout, and both are removed outright on a coarse pointer. */
.mj-v4-cursor,
.mj-v4-light { position: fixed; inset-block-start: 0; inset-inline-start: 0;
               pointer-events: none; will-change: transform; }

.mj-v4-cursor {
  z-index: 199;
  inline-size: 24px;
  block-size: 24px;
  border-radius: 50%;
  border: 1.5px solid var(--mj-gold);
  opacity: 0;
  transform: translate3d(-100px, -100px, 0);
  transition: inline-size .22s ease, block-size .22s ease,
              background .22s ease, opacity .3s ease;
}
.mj-v4-cursor--on { opacity: 0.75; }
/* Over a link the ring swells and fills, so the pointer state is legible
   without a colour change the page's own hover already owns. */
.mj-v4-cursor--hot { inline-size: 52px; block-size: 52px;
                     background: rgb(var(--mj-gold-rgb) / 0.22); }

.mj-v4-light {
  z-index: 195;
  inline-size: 620px;
  block-size: 620px;
  border-radius: 50%;
  opacity: 0;
  background: radial-gradient(circle,
              rgb(var(--mj-gold-rgb) / 0.26) 0%,
              rgb(var(--mj-gold-rgb) / 0.09) 34%, transparent 68%);
  mix-blend-mode: screen;
  transition: opacity .6s ease;
}
.mj-v4-light--on { opacity: 1; }

@media (pointer: coarse) {
  .mj-v4-cursor, .mj-v4-light { display: none; }
}

/* The attributes carry each mark's true ratio, so the rendered box is fixed
   here instead. Without this a 384x70 wordmark would lay out 384px wide. */
.mj-v4-rail__pill img { inline-size: auto; block-size: 22px; max-inline-size: 74px; }
@media (max-width: 760px) {
  .mj-v4-rail__pill img { block-size: 18px; max-inline-size: 60px; }
}

/* -- Nav, per the style specification ------------------------------------
   Dark navy translucent over the page, not a white pill. I built it white
   from an earlier reading of the prototype; the spec reads the built file and
   gives rgba(10,22,40,.78) with a 16px blur, a hairline border and a soft
   shadow. Light text on it, which is why the links are --text.

   The fill is the page ground at 78%, so the nav dissolves into whatever it
   is over — video at the top of the page, cards further down — rather than
   sitting on it as an object. */
.mj-v4-nav > div {
  inline-size: calc(100% - 32px);
  max-inline-size: 1120px;
  margin-inline: auto;
  border-radius: 999px;
  padding: 9px 9px 9px 20px;
  background: rgb(var(--mj-bg-rgb) / 0.78);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border: 1px solid var(--mj-line-soft);
  box-shadow: 0 10px 34px rgb(var(--mj-bg-rgb) / 0.09);
}

.mj-v4-nav .mj-v4-brand,
.mj-v4-nav .mj-v4-navlinks a { color: var(--mj-ink); }
.mj-v4-nav .mj-v4-navlinks a { font-weight: 500; font-size: 14.5px; border-radius: 999px; }
.mj-v4-nav .mj-v4-navlinks a:hover { background: rgb(var(--mj-ink-rgb) / 0.06); }
.mj-v4-nav .mj-v4-navlinks a > span { display: none; }

.mj-v4-nav .mj-v4-lang {
  font-family: var(--mj-font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--mj-ink-muted);
  border-color: var(--mj-line-soft);
}
.mj-v4-nav .mj-v4-lang:hover { color: var(--mj-ink); border-color: var(--mj-gold); }

/* The mark is supplied light-on-dark and the pill is dark again, so it needs
   no plaque here — that was for the white version. */
.mj-v4-nav .mj-v4-brand img { background: none; padding: 0; border-radius: 0; }

/* The hamburger's bars are on a dark ground now. */
.mj-v4-navtoggle span { background: var(--mj-ink); }

/* Spec stacking order: nav 150, rail 170, FAB 190, light 195, ring 199,
   progress 200, skip link 220. The nav had been at 100. */
.mj-v4-nav { z-index: 150; inset-block-start: calc(var(--mj-rail-h, 56px) + 14px); }
.mj-wa { z-index: 190; }

/* -- Typography ----------------------------------------------------------
   The design's own families, by the client's decision. See the note in
   design/fonts.css: the design brief calls these stand-ins and forbids them,
   and the client has overridden that so the page matches the prototype.

   Set on :root rather than scoped to .mj-v4, because there is one shell now
   and the whole site is this design. */
:root {
  --mj-font-display: 'Bricolage Grotesque', Archivo, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --mj-font: 'DM Sans', Archivo, system-ui, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --mj-font-mono: 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --mj-font-ar: 'Noto Kufi Arabic', 'DM Sans', system-ui, sans-serif;
}

/* Archivo stays in every stack as the immediate fallback: it is already
   downloaded and sits close to these in proportion, so a slow face swaps into
   something similar rather than into Helvetica. */
[dir="rtl"] body { font-family: var(--mj-font-ar); }

/* Display face on headings, numerals, buttons and chips — rule 3 of the
   design's typography lock: buttons are display at 700, never body. */
:is(h1, h2, h3),
.mj-v4-cta, .mj-v4-ghost, .mj-v4-lang, .mj-v4-brand span,
.mj-v4-num__value, .mj-v4-rail__pill b, .mj-v4-faq__item summary,
.mj-v4-footer__brand span, .mj-v4-close__figure, .mj-v4-wcard__name,
.mj-page-title, .mj-h2, .mj-h2--lg, .mj-h3, .mj-btn, .mj-footer__talk {
  font-family: var(--mj-font-display);
}

.mj-v4 :is(h1, h2) { font-weight: 800; }
.mj-v4 h3 { font-weight: 700; }
.mj-v4-cta, .mj-v4-ghost { font-weight: 700; }

.mj-v4-kicker,
.mj-kicker, .mj-eyebrow,
.mj-v4-tile__tag,
.mj-v4-tile__when { font-family: var(--mj-font-mono); }

/* -- Hero, as the design lays it out --------------------------------------
   Full viewport height with the copy over the foot of the picture, rather
   than a text column beside it. The scrim between the two is what makes both
   readable; see .mj-v4-hero__scrim above. */
.mj-v4-hero--film {
  min-block-size: 100vh;
  min-block-size: 100svh;
  display: flex;
  align-items: flex-end;
  /* The floor the design asks for: "without that floor the proof pill rises
     behind the nav on short viewports". The rail and the nav are both fixed
     and both sit over this section, so the overlay needs their combined
     height cleared above it. Expressed as padding rather than as a computed
     min-height: with align-items:flex-end the content then pushes the section
     taller than the viewport when it has to, instead of sliding underneath.

     Both heights are measured at runtime — see initRailHeight(). Hardcoding
     them is right until someone changes the pill padding or the font loads
     wider, and then this silently overlaps again. */
  padding: calc(var(--mj-rail-h, 56px) + var(--mj-nav-h, 62px) + 34px) 0 0;
}

.mj-v4-hero__over {
  position: relative;
  z-index: 3;
  inline-size: 100%;
  display: grid;
  /* The claim gets the wider track, the detail the narrower one. */
  grid-template-columns: 1.25fr 0.75fr;
  align-items: end;
  gap: 40px;
  padding: 0 40px 52px;
  /* The headline sizes itself in container units, so the column it sits in
     has to be the container. */
  container-type: inline-size;
}

.mj-v4-hero__lead { display: flex; flex-direction: column; gap: 20px;
                    align-items: flex-start; min-inline-size: 0;
                    container-type: inline-size; }
.mj-v4-hero__aside { display: flex; flex-direction: column; gap: 22px;
                     align-items: flex-start; padding-block-end: 8px;
                     min-inline-size: 0; }

/* Overlapping client marks and the count. Each mark keeps its own aspect
   ratio inside a round white plaque, because these are supplied on white and
   keying them out leaves halos. */
.mj-v4-hero__proof {
  display: flex;
  align-items: center;
  gap: 12px;
  background: rgb(var(--mj-bg-rgb) / 0.62);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border: 1px solid var(--mj-line-soft);
  border-radius: 999px;
  padding: 7px 18px 7px 7px;
  font-size: 13.5px;
  font-weight: 500;
}
.mj-v4-hero__proof b { color: var(--mj-gold); font-weight: 700; }
.mj-v4-hero__faces { display: flex; }
.mj-v4-hero__faces img {
  inline-size: 30px;
  block-size: 30px;
  border-radius: 50%;
  object-fit: contain;
  background: #ffffff;
  padding: 4px;
  border: 2px solid var(--mj-bg);
}
/* Logical, so the stack leans the other way in Arabic instead of the marks
   overlapping backwards. */
.mj-v4-hero__faces img + img { margin-inline-start: -10px; }

.mj-v4-hero__badge {
  display: inline-flex;
  align-items: center;
  background: var(--mj-gold);
  color: var(--mj-bg);
  border-radius: 999px;
  padding: 11px 24px;
  font-family: var(--mj-font-display);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.mj-v4-hero h1 { margin-block-start: 0; }

/* Below the breakpoint the two tracks stack and the hero stops being a
   full-height picture with copy on it — at 390px that layout leaves the
   headline over the busiest part of the frame with nowhere to go. */
@media (max-width: 860px) {
  .mj-v4-hero__over { grid-template-columns: 1fr; gap: 26px; padding: 0 24px 44px; }
  .mj-v4-hero--film { min-block-size: 92vh; }
}

/* -- Product shots --------------------------------------------------------
   Three equal screenshots in a row below the hero, each drifting at its own
   rate as the band crosses the viewport. `data-shot` carries that rate, so
   the script reads it from the markup instead of holding a parallel list. */
.mj-v4-shots {
  max-width: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding: 96px 24px 90px;
}
.mj-v4-shots__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }

.mj-v4-shot {
  position: relative;
  display: block;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 20px 54px rgb(0 0 0 / 0.42);
  transition: box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1);
  will-change: transform;
}
.mj-v4-shot:hover { box-shadow: 0 30px 74px rgb(0 0 0 / 0.55); }
.mj-v4-shot img { display: block; inline-size: 100%; block-size: auto; }

/* A slow diagonal highlight crossing each shot. Transform only, so it costs
   a composite rather than a paint. */
.mj-v4-shot__sheen {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 42%;
  background: linear-gradient(90deg, transparent,
              rgb(255 255 255 / 0.10), transparent);
  transform: translateX(-130%) skewX(-18deg);
  animation: mj-v4-sheen 7s ease-in-out infinite;
  pointer-events: none;
}
@keyframes mj-v4-sheen {
  0%        { transform: translateX(-130%) skewX(-18deg); }
  55%, 100% { transform: translateX(240%) skewX(-18deg); }
}

@media (max-width: 860px) {
  .mj-v4-shots { padding: 56px 24px 48px; }
  .mj-v4-shots__grid { grid-template-columns: 1fr; gap: 16px; }
}

/* -- Work cards -----------------------------------------------------------
   The design's one card, used by both Gulf and Selected work: a 16/10 media
   panel with the category on a pill over it, then the name, the outcome and a
   round arrow sharing a baseline underneath.

   Two columns, and the row gap is wider than the column gap so the cards read
   as a grid of objects rather than as a block of texture. */
.mj-v4-wgrid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 56px 40px;
}

.mj-v4-wcard {
  inline-size: 100%;
  display: flex;
  flex-direction: column;
  gap: 22px;
  text-decoration: none;
  color: inherit;
}

.mj-v4-wcard__media {
  position: relative;
  display: block;
  border-radius: 26px;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--mj-surface-v4);
  box-shadow: 0 20px 54px rgb(0 0 0 / 0.42);
  transition: box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* The media fills its own panel; the intrinsic ratio is declared on the
   element so nothing shifts while it loads, and cover crops it to 16/10. */
.mj-v4-wcard__media > img,
.mj-v4-wcard__media > video {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
  transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-v4-wcard:hover .mj-v4-wcard__media { box-shadow: 0 30px 74px rgb(0 0 0 / 0.55); }
.mj-v4-wcard:hover .mj-v4-wcard__media > img,
.mj-v4-wcard:hover .mj-v4-wcard__media > video { transform: scale(1.03); }

/* Logical inset, so the pill sits in the leading corner in both directions
   rather than jumping across the card in Arabic. */
.mj-v4-wcard__tag {
  position: absolute;
  inset-block-start: 14px;
  inset-inline-start: 14px;
  background: rgb(var(--mj-bg-rgb) / 0.82);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  color: var(--mj-ink);
  border-radius: 999px;
  padding: 9px 16px;
  font-family: var(--mj-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.mj-v4-wcard__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
}
.mj-v4-wcard__text { display: flex; flex-direction: column; gap: 7px; min-inline-size: 0; }
.mj-v4-wcard__name {
  font-family: var(--mj-font-display);
  font-weight: 800;
  font-size: clamp(30px, 2.5vw, 40px);
  letter-spacing: -0.03em;
  color: var(--mj-ink);
}
.mj-v4-wcard__out {
  font-size: 17px;
  line-height: 1.5;
  color: var(--mj-ink-body);
  max-inline-size: 46ch;
}

/* The arrow fills on hover rather than moving: the media already scales, and
   two things travelling at once reads as drift. */
.mj-v4-wcard__pill {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 58px;
  block-size: 58px;
  border-radius: 50%;
  border: 1px solid var(--mj-line-soft);
  color: var(--mj-gold);
  font-size: 20px;
  transition: background 0.4s ease, border-color 0.4s ease, color 0.4s ease;
}
.mj-v4-wcard:hover .mj-v4-wcard__pill {
  background: var(--mj-gold);
  border-color: var(--mj-gold);
  color: var(--mj-bg);
}
[dir="rtl"] .mj-v4-wcard__pill { transform: scaleX(-1); }

@media (max-width: 860px) {
  .mj-v4-wgrid { grid-template-columns: 1fr; gap: 44px; }
  .mj-v4-wcard__media { border-radius: 20px; }
  .mj-v4-wcard__pill { inline-size: 48px; block-size: 48px; }
}

/* -- Bands: Capabilities, Process, Proof ---------------------------------
   One section shape used three times: a centred head, then a grid of tiles.
   The design draws all three identically apart from the grid's column count
   and one meta value, so they share everything here too. */
.mj-v4-band {
  max-width: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding: 20px 24px 110px;
}
.mj-v4-band__inner { display: flex; flex-direction: column; gap: 52px; }

.mj-v4-band__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  text-align: center;
}
.mj-v4-band__head h2 {
  margin: 0;
  font-size: clamp(32px, 5.4vw, 80px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  /* The design caps its centred headings so they break into two or three
     balanced lines rather than one very wide one. */
  max-inline-size: 16ch;
  text-wrap: balance;
}
.mj-v4-band__head p {
  margin: 0;
  max-inline-size: 62ch;
  font-size: var(--mj-lead, 17px);
  line-height: 1.6;
  color: var(--mj-ink-body);
}

/* The badge over each heading. Bordered rather than filled: the gold pill is
   the call to action, and a second gold pill in the same viewport competes
   with it. */
.mj-v4-badge {
  display: inline-flex;
  align-items: center;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 999px;
  padding: 9px 18px;
  font-family: var(--mj-font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--mj-ink);
}

.mj-v4-grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; }
.mj-v4-grid2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }

.mj-v4-tile {
  margin: 0;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 26px;
  padding: 36px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  transition: border-color 0.4s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-v4-tile:hover { border-color: var(--mj-gold); transform: translateY(-4px); }

.mj-v4-tile__meta {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
}
.mj-v4-tile__tag {
  font-family: var(--mj-font-mono);
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mj-gold);
}
.mj-v4-tile__when {
  font-family: var(--mj-font-mono);
  font-size: 11px;
  color: var(--mj-ink-muted);
}
/* Process puts its step number where Capabilities puts its category. */
.mj-v4-tile__no {
  font-family: var(--mj-font-display);
  font-weight: 800;
  font-size: 46px;
  line-height: 1;
  color: var(--mj-gold);
}
.mj-v4-tile h3 {
  margin: 0;
  font-size: 27px;
  letter-spacing: -0.02em;
}
.mj-v4-tile p {
  margin: 0;
  font-size: 16px;
  line-height: 1.58;
  color: var(--mj-ink-body);
}

/* Proof reads as a statement, so its text is set in the display face at a
   larger size and its source in mono underneath. */
.mj-v4-quote p {
  font-family: var(--mj-font-display);
  font-weight: 600;
  font-size: clamp(20px, 2vw, 28px);
  line-height: 1.28;
  letter-spacing: -0.02em;
  color: var(--mj-ink);
}
.mj-v4-quote footer {
  font-family: var(--mj-font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mj-gold);
}

/* -- Numbers --------------------------------------------------------------
   Four figures over a rule. No card: the design lets these sit on the page so
   the band reads as a summary rather than as four more tiles. */
.mj-v4-nums {
  max-width: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding: 0 24px 110px;
}
.mj-v4-nums__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 34px;
  padding-block-start: 44px;
  border-block-start: 1px solid var(--mj-line-soft);
}
.mj-v4-num { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.mj-v4-num__value {
  font-family: var(--mj-font-display);
  font-weight: 800;
  font-size: clamp(48px, 6.4vw, 92px);
  line-height: 1;
  letter-spacing: -0.04em;
  /* Tabular figures, so a counter running 0 -> 40 does not change width on
     every frame and shove its neighbours around. */
  font-variant-numeric: tabular-nums;
}
.mj-v4-num__label { font-size: 15px; color: var(--mj-ink-muted); text-align: center; }

@media (max-width: 1100px) {
  .mj-v4-grid4 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 760px) {
  .mj-v4-grid4, .mj-v4-grid2 { grid-template-columns: 1fr; }
  .mj-v4-nums__grid { grid-template-columns: repeat(2, 1fr); gap: 28px; }
  .mj-v4-tile { padding: 28px; border-radius: 20px; }
  .mj-v4-band { padding-block-end: 72px; }
}

/* -- Responsive, to the design's breakpoints ------------------------------
   400 / 620 / 900 / 1024 / 1060. Each one exists because something specific
   stops fitting there, not because it is a round number:

     1060  six nav links, the language pill and a filled CTA need about this
           much before they wrap, so the header collapses to a hamburger
     1024  the hero overlay's two tracks stop having room for both
      900  the work and Gulf grids go single-column
      620  capabilities and process go single-column, screenshots stack
      400  side padding tightens again on the narrowest phones            */

@media (max-width: 1060px) {
  /* Links move into a panel the button opens; see initNavPanel(). */
  .mj-v4-navlinks {
    position: fixed;
    inset-inline: 12px;
    inset-block-start: calc(var(--mj-rail-h) + 84px);
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    padding: 12px;
    border-radius: 22px;
    background: rgb(255 255 255 / 0.96);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    border: 1px solid rgb(var(--mj-bg-rgb) / 0.10);
    box-shadow: 0 18px 48px rgb(var(--mj-bg-rgb) / 0.28);
    /* Collapsed by default and not reachable by tab while collapsed. */
    display: none;
  }
  .mj-v4-navlinks[data-open="true"] { display: flex; }
  .mj-v4-nav .mj-v4-navlinks a { padding: 14px 16px; font-size: 16px; }
  .mj-v4-navtoggle { display: inline-flex; }
  /* The header keeps only the brand, the toggle and the CTA. */
  .mj-v4-nav > div { padding: 8px 8px 8px 16px; }
}

@media (max-width: 1024px) {
  .mj-v4-hero__over { grid-template-columns: 1fr; gap: 24px; }
  .mj-v4-grid4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 900px) {
  .mj-v4-wgrid { grid-template-columns: 1fr; gap: 40px; }
  .mj-v4-grid2 { grid-template-columns: 1fr; }
  .mj-v4-faq__head { position: static; }
}

@media (max-width: 620px) {
  .mj-v4-band, .mj-v4-nums, .mj-v4-faq, .mj-v4-shots { padding-inline: 18px; }
  .mj-v4-grid4 { grid-template-columns: 1fr; }
  .mj-v4-shots__grid { grid-template-columns: 1fr; }
  .mj-v4-nums__grid { grid-template-columns: repeat(2, 1fr); }
  /* The video block gives up some height on phones: 100vh with a fixed rail
     and a fixed nav over it leaves the headline almost no room. */
  .mj-v4-hero--film { min-block-size: 86vh; min-block-size: 86svh; }
  .mj-v4-hero__over { padding: 0 18px 36px; }
  .mj-v4-band__head h2 { max-inline-size: none; }
  .mj-v4-wcard__name { font-size: 28px; }
  .mj-v4-wcard__out { font-size: 15.5px; }
  .mj-v4-tile { padding: 24px; }
  .mj-v4-tile h3 { font-size: 22px; }
  .mj-v4-quote p { font-size: 19px; }
  /* Two buttons side by side at 360px leave each about 150px, which is not
     enough for either label. */
  .mj-v4-hero__cta { flex-direction: column; align-items: stretch; width: 100%; }
  .mj-v4-hero__cta > * { justify-content: center; }
}

@media (max-width: 400px) {
  .mj-v4-band, .mj-v4-nums, .mj-v4-faq, .mj-v4-shots { padding-inline: 16px; }
  .mj-v4-hero__over { padding: 0 16px 30px; }
  .mj-v4-hero__badge { font-size: 13px; padding: 9px 18px; }
  .mj-v4-hero__proof { font-size: 12.5px; }
}

/* Hidden on the wide layout, revealed by the 1060 rule. Declared with the
   other nav rules rather than after the media query: a later `display: none`
   at the same specificity wins over the one inside it, which is why the
   button stayed hidden at 360px on the first pass. */
.mj-v4-navtoggle {
  flex-direction: column;
  gap: 5px;
  background: transparent;
  border: 0;
  min-inline-size: 44px;
  min-block-size: 44px;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border-radius: 999px;
}
.mj-v4-navtoggle span {
  display: block;
  inline-size: 20px;
  block-size: 2px;
  background: var(--mj-bg);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.3s ease;
}
.mj-v4-navtoggle[aria-expanded="true"] span:first-child { transform: translateY(3.5px) rotate(45deg); }
.mj-v4-navtoggle[aria-expanded="true"] span:last-child  { transform: translateY(-3.5px) rotate(-45deg); }

/* Base state, before the 1060 rule turns it on. */
@media (min-width: 1060.02px) { .mj-v4-navtoggle { display: none; } }

/* -- Mobile header ---------------------------------------------------------
   At 390px the pill was carrying the brand, the toggle, the language switch
   and a filled CTA, and wrapping onto two rows — a 100px-tall header over a
   video, on the smallest screen there is.

   The row keeps the three things a first-time visitor needs: who this is, a
   way into the menu, and the one action. The language switch moves into the
   panel, where it sits above the links rather than competing with the CTA. */
@media (max-width: 1060px) {
  .mj-v4-nav > div { flex-wrap: nowrap; gap: 10px; }
  .mj-v4-nav .mj-v4-brand span { font-size: 16px; }

  /* Ordered so the toggle sits between the brand and the CTA rather than
     wherever source order puts it. */
  .mj-v4-nav .mj-v4-brand { order: 0; margin-inline-end: auto; }
  .mj-v4-navtoggle { order: 1; }
  .mj-v4-nav .mj-v4-cta { order: 2; }
  .mj-v4-nav .mj-v4-lang { order: 3; }

  /* The switch rides inside the panel. It is still one control in the tab
     order, just relocated, so nothing is hidden from a keyboard. */
  .mj-v4-nav .mj-v4-lang {
    position: fixed;
    inset-inline-end: 22px;
    inset-block-start: calc(var(--mj-rail-h) + 96px);
    z-index: 151;
    display: none;
  }
  .mj-v4-navlinks[data-open="true"] ~ .mj-v4-lang { display: inline-flex; }
  .mj-v4-navlinks[data-open="true"] { padding-block-start: 58px; }
}

@media (max-width: 620px) {
  /* A full label plus an arrow does not fit beside a brand and a toggle at
     390px, so the nav's CTA loses its arrow and tightens. The hero's own
     WhatsApp button, which has room, keeps both. */
  .mj-v4-nav .mj-v4-cta { padding: 11px 16px; font-size: 14px; }
  .mj-v4-nav .mj-v4-cta svg { display: none; }
  .mj-v4-nav > div { padding: 7px 7px 7px 12px; }

  /* The rail is decoration at this width; smaller pills fit more of it on
     screen and stop it reading as the loudest thing on the page. */
  .mj-v4-rail__pill { padding: 5px 13px 5px 6px; font-size: 11.5px; gap: 8px; }
  .mj-v4-rail__pill img { block-size: 15px; max-inline-size: 46px; }
  .mj-v4-rail { padding-block: 7px; }

  /* A little more air than the fit-exactly size, so a descender or an italic
     never touches the edge. */
  .mj-v4-hero h1 { font-size: max(26px, min(13cqw,
      calc(96cqw / (var(--mj-hero-ch, 14) * 0.45)), 124px)); }
}


/* The design names one hairline: rgba(242,239,232,0.13). The site derived
   three from the ink colour, which now IS #F2EFE8 — so `--mj-line` already
   resolves to the design's value and the two soft/strong steps stay for the
   places that need a lighter or heavier rule. */
.mj-v4, .mj-v4-rail, .mj-v4-nav, .mj-v4-footer { --mj-line-soft: rgb(var(--mj-ink-rgb) / 0.13); }

/* -- Closing block --------------------------------------------------------
   The one place the accent is the ground rather than the highlight, which is
   what makes it read as the end of the page. Everything inside is the page's
   own navy on gold — 7.83:1, the same pair every gold button already uses, so
   the inversion needs no second palette. */
.mj-v4-close {
  max-width: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding: 0 24px 110px;
}
.mj-v4-close__panel {
  background: var(--mj-gold);
  color: var(--mj-bg);
  border-radius: 38px;
  padding: 64px;
  display: grid;
  grid-template-columns: 1.35fr 0.65fr;
  align-items: end;
  gap: 40px;
}
.mj-v4-close__text { display: flex; flex-direction: column; gap: 22px; align-items: flex-start; }

/* Bordered rather than filled: a filled chip on a filled panel is invisible,
   and the border is the navy that is already the text colour here. */
.mj-v4-close__badge {
  display: inline-flex;
  align-items: center;
  border: 1.5px solid rgb(var(--mj-bg-rgb) / 0.42);
  border-radius: 999px;
  padding: 9px 18px;
  font-size: 13px;
  font-weight: 700;
}
.mj-v4-close__panel h2 {
  margin: 0;
  font-size: clamp(32px, 4.6vw, 72px);
  line-height: 0.92;
  letter-spacing: -0.04em;
  max-inline-size: 18ch;
  text-wrap: balance;
}
.mj-v4-close__panel p {
  margin: 0;
  max-inline-size: 54ch;
  font-size: var(--mj-lead, 17px);
  line-height: 1.6;
  /* Not an ink token: those are all tints of the page's text colour, and the
     text colour here is the ground. */
  color: rgb(var(--mj-bg-rgb) / 0.78);
}
.mj-v4-close__row { display: flex; flex-wrap: wrap; gap: 12px; margin-block-start: 4px; align-items: center; }

/* The button is gold on gold by default, so on this panel it inverts. */
.mj-v4-close__row .mj-v4-cta {
  background: var(--mj-bg);
  color: var(--mj-gold);
}
.mj-v4-close__tel {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  padding-inline: 8px;
  font-weight: 700;
  color: var(--mj-bg);
  text-decoration: none;
  border-block-end: 1.5px solid rgb(var(--mj-bg-rgb) / 0.35);
}
.mj-v4-close__tel:hover { border-block-end-color: var(--mj-bg); }

.mj-v4-close__aside {
  display: flex;
  align-items: center;
  gap: 22px;
  justify-content: flex-end;
}
.mj-v4-close__figure {
  font-weight: 800;
  font-size: clamp(80px, 10vw, 168px);
  line-height: 0.82;
  letter-spacing: -0.05em;
  font-variant-numeric: tabular-nums;
}
.mj-v4-close__note { font-size: 17px; line-height: 1.45; max-inline-size: 16ch; opacity: 0.8; }

.mj-v4-footer__talk {
  font-weight: 800;
  font-size: clamp(24px, 2.4vw, 34px);
  letter-spacing: -0.03em;
  color: var(--mj-gold);
  text-decoration: none;
}
.mj-v4-footer__talk:hover { color: var(--mj-gold-hi); }

@media (max-width: 1024px) {
  .mj-v4-close__panel { grid-template-columns: 1fr; padding: 48px; }
  .mj-v4-close__aside { justify-content: flex-start; }
}
@media (max-width: 620px) {
  .mj-v4-close { padding-inline: 18px; }
  .mj-v4-close__panel { padding: 32px 24px; border-radius: 28px; }
  .mj-v4-close__row { flex-direction: column; align-items: stretch; }
  .mj-v4-close__row .mj-v4-cta { justify-content: center; }
  .mj-v4-close__tel { justify-content: center; }
}

/* -- Reduced motion -------------------------------------------------------
   The design asks for this: "drop every transform, keep short opacity fades,
   stop all marquees and the background".

   The important word is *drop*, not *disable*. An earlier version of this
   site gated whole timelines behind the preference, and anything those
   timelines were meant to reveal stayed at opacity 0 — the page was not
   calmer, it was blank. That is why the guard was removed once already.

   So this stops movement and leaves everything in its finished state.
   `animation: none` on the marquee and background stops them where they are;
   the reveal states are forced to their end values in JS, because GSAP writes
   inline styles and no stylesheet can outrank those. */
@media (prefers-reduced-motion: reduce) {
  .mj-v4-code__grid,
  .mj-v4-code__col,
  .mj-v4-code__term,
  .mj-v4-code__scan,
  .mj-v4-code__glow,
  .mj-v4-shot__sheen { animation: none !important; }

  /* The sheen is a moving highlight over a still image; with nothing moving
     it is a grey band parked across the picture. */
  .mj-v4-shot__sheen { display: none; }

  /* Hover effects that translate become fades. A card that lifts is motion
     the visitor asked not to have; a card that brightens is not. */
  .mj-v4-tile:hover,
  .mj-v4-wcard:hover .mj-v4-wcard__media > img,
  .mj-v4-wcard:hover .mj-v4-wcard__media > video { transform: none; }

  .mj-v4-cursor, .mj-v4-light { display: none; }

  *, *::before, *::after {
    transition-duration: 0.12s !important;
    scroll-behavior: auto !important;
  }
}

/* -- FAQ, the design's detail --------------------------------------------
   The heading sticks beside the list on wide screens, so the question you are
   reading always has the section it belongs to next to it. It goes static
   below 900px, where there is no second column to stick within. */
@media (min-width: 900.02px) {
  .mj-v4-faq { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 56px; align-items: start;
               max-width: 1180px; }
  .mj-v4-faq__head { position: sticky; inset-block-start: calc(var(--mj-rail-h, 56px) + var(--mj-nav-h, 62px) + 28px); }
  .mj-v4-faq__list { margin-block-start: 0; }
}

/* An open card takes the accent border, so the one you are reading is marked
   without moving anything. */
.mj-v4-faq__item[open] { border-block-end-color: var(--mj-gold); }
.mj-v4-faq__item[open] summary { color: var(--mj-gold); }

/* The design rotates the marker 135° rather than hiding half of it: the "+"
   becomes an "×" leaning into a "−", which reads as a state change instead of
   a glyph swap. */
.mj-v4-faq__item summary i { transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1); }
.mj-v4-faq__item[open] summary i { transform: rotate(135deg); }
.mj-v4-faq__item[open] summary i::after { opacity: 1; transform: rotate(90deg); }

/* -- The design's responsive table, exactly -------------------------------
   Later than the earlier breakpoint block on purpose: these are the values
   from the prompt's table, and where the two disagree this one wins.

       ≥1060   full pill nav · hero 2-col · caps 4-col · FAQ 2-col sticky
    1024–1060  hamburger · hero 1-col · caps 2-col · FAQ 2-col
     900–1024  caps 2-col · FAQ 1-col static · CTA 1-col · footer 2-col
      620–900  Gulf and Work 1-col · 18px sides
        ≤620   everything 1-col · stats 2-col · shots 1-col · 92vh hero    */

@media (max-width: 1060px) {
  .mj-v4-hero__over { grid-template-columns: 1fr; }
}

@media (max-width: 1024px) {
  .mj-v4-faq { grid-template-columns: 1fr; }
  .mj-v4-faq__head { position: static; }
  .mj-v4-footer__grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 900px) {
  .mj-v4-band, .mj-v4-nums, .mj-v4-faq, .mj-v4-shots, .mj-v4-close,
  .mj-v4-footer { padding-inline: 18px; }
}

@media (max-width: 620px) {
  /* Three screenshots side by side at 390px are 110px each — the design keeps
     them 3-col right down to 620 and then stacks, rather than shrinking. */
  .mj-v4-shots__grid { grid-template-columns: 1fr; }
  .mj-v4-hero--film { min-block-size: 92vh; min-block-size: 92svh; }
  .mj-v4-footer__grid { grid-template-columns: 1fr; }
}

/* Containers, per the design: cards 1640, section heads 1260, FAQ 1180. */
.mj-v4-band__head { max-inline-size: 1260px; margin-inline: auto; }
.mj-v4-faq { max-width: 1180px; }

/* Shadows, per the design. */
.mj-v4-wcard:hover .mj-v4-wcard__media,
.mj-v4-shot:hover { box-shadow: 0 34px 78px rgb(0 0 0 / 0.58); }

/* Work card media lifts rather than scaling — the design says 8px. A scale
   crops the frame against its own rounded corners; a lift does not. */
.mj-v4-wcard:hover .mj-v4-wcard__media > img,
.mj-v4-wcard:hover .mj-v4-wcard__media > video { transform: translateY(-8px) scale(1.02); }

/* FAQ answer opens over 450ms. <details> has no height to animate, so the
   inner block carries the transition and the element only toggles. */
.mj-v4-faq__a {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 0.45s cubic-bezier(0.16, 1, 0.3, 1);
}

/* The floating WhatsApp button tucks in on phones. */
@media (max-width: 620px) {
  .mj-wa { inset-inline-start: 14px; inset-block-end: 14px; }
}

/* Hidden until the hero has scrolled past — the hero carries its own
   WhatsApp call to action in the same corner. */
.mj-wa--gated { opacity: 0; pointer-events: none; transition: opacity 0.5s ease; }
.mj-wa--gated.mj-wa--shown { opacity: 1; pointer-events: auto; }

/* -- Code background: glyphs, second terminal, mask ----------------------- */
.mj-v4-code__glyphs { position: absolute; inset: 0; opacity: 0.07; }
.mj-v4-code__glyphs span {
  position: absolute;
  font-family: var(--mj-font-mono);
  font-size: 17px;
  color: var(--mj-gold);
  animation-name: mj-v4-float;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  will-change: transform;
}

.mj-v4-code__term--a { inset-block-start: 18%; inset-inline-end: 6%; }
.mj-v4-code__term--b {
  inset-block-start: 62%;
  inset-inline-start: 8%;
  opacity: 0.06;
  transform: scale(0.82);
  animation-duration: 19s;
  animation-direction: reverse;
}

/* Fades the whole layer out downward, so it reads under the hero and is gone
   by the time the page is cards and copy. */
.mj-v4-code__mask {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, transparent, var(--mj-bg) 84%);
}

@media (max-width: 860px) {
  .mj-v4-code__glyphs { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .mj-v4-code__glyphs span { animation: none !important; }
}

/* =========================================================================
   Inner pages, in the home page's language
   =========================================================================
   The other ten pages are built from V1 components — .mj-hero, .mj-card,
   .mj-panel, .mj-btn and friends. Rather than rewrite ten templates in one
   go, those components are restyled here to the system home uses: same
   surface, same hairline, same radius, same hover, same type scale.

   This block loads after components.css, so it wins on source order without
   fighting for specificity. What it leaves unused in components.css is dead,
   and goes once the templates that still reference it are migrated.

   The point is the checklist item: a visitor moving home -> case studies ->
   services -> contact should see one site. */

/* -- Page hero ------------------------------------------------------------
   Centred, on the code background, and padded clear of the rail and the nav.
   No page hero carries an image — the video is the home page's alone, which
   is part of what makes it the home page. */
.mj-hero--page,
.mj-hero {
  max-width: 1260px;
  margin-inline: auto;
  padding-block-start: calc(var(--mj-rail-h, 56px) + var(--mj-nav-h, 62px) + 56px);
  padding-block-end: 56px;
  padding-inline: 24px;
}

.mj-page-title {
  font-size: clamp(32px, 5.4vw, 80px);
  font-weight: 800;
  line-height: 0.92;
  letter-spacing: -0.04em;
  max-inline-size: 16ch;
  text-wrap: balance;
}

.mj-lede {
  font-size: clamp(16px, 1.3vw, 19px);
  line-height: 1.6;
  color: var(--mj-ink-muted);
  max-inline-size: 58ch;
}

/* -- Kickers and chips ----------------------------------------------------
   One pill treatment for every label on the site: the surface fill, a
   hairline, 13px bold. Gold fill is reserved for the primary action, so a
   second gold pill never competes with the call to action. */
.mj-kicker,
.mj-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 999px;
  padding: 9px 18px;
  font-family: var(--mj-font-mono);
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--mj-gold);
}
.mj-eyebrow__dot { display: none; }

.mj-tag, .mj-tags span {
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 999px;
  padding: 8px 15px;
  font-size: 13px;
  color: var(--mj-ink-body);
}

/* -- Headings -------------------------------------------------------------
   Inner pages are read rather than scanned, so their section headings are
   left-aligned where home centres its own. */
.mj-h2, .mj-h2--lg {
  font-size: clamp(32px, 5.4vw, 80px);
  font-weight: 800;
  line-height: 0.92;
  letter-spacing: -0.04em;
  text-wrap: balance;
}
.mj-h3 { font-size: 27px; font-weight: 700; line-height: 1.2; letter-spacing: -0.02em; }
.mj-copy { font-size: 17px; line-height: 1.58; color: var(--mj-ink-body); }

/* -- Cards ----------------------------------------------------------------
   One rule replacing .mj-card, .mj-svc, .mj-step, .mj-quote and
   .mj-panel__cell: surface fill, hairline, 24px radius, 36px padding, gold
   border and a 6px lift on hover. */
.mj-card,
.mj-panel__cell,
.mj-svc,
.mj-step,
.mj-quote {
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 24px;
  padding: 36px;
  transition: border-color 0.4s ease,
              transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-card:hover,
.mj-panel__cell:hover,
.mj-svc:hover,
.mj-step:hover { border-color: var(--mj-gold); transform: translateY(-6px); }

/* The panel was a bordered table of cells; as cards they need the gap the
   border used to imply. */
.mj-panel { display: grid; gap: 16px; border: 0; background: none; }
.mj-card__meta { font-family: var(--mj-font-mono); font-size: 12px;
                 letter-spacing: 0.2em; text-transform: uppercase; color: var(--mj-gold); }

/* -- Buttons --------------------------------------------------------------
   The same pill the home page uses, so a call to action is the same object
   wherever it appears. */
.mj-btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-block-size: 44px;
  padding: 15px 26px;
  border-radius: 999px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-decoration: none;
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), background 0.3s ease;
}
.mj-btn--primary { background: var(--mj-gold); color: var(--mj-bg); border: 0; }
.mj-btn--ghost { background: transparent; color: var(--mj-ink);
                 border: 1.5px solid var(--mj-line-strong); }
.mj-btn:hover { transform: translateY(-2px); }

/* -- Form fields ----------------------------------------------------------
   Surface fill, hairline, 14px radius, 44px minimum — the design's numbers,
   and the tap target the accessibility list asks for. */
.mj-field input,
.mj-field textarea,
.mj-field select {
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 14px;
  padding: 16px;
  min-block-size: 44px;
  color: var(--mj-ink);
  font-family: inherit;
  font-size: 16px;
}
.mj-field input:focus-visible,
.mj-field textarea:focus-visible { border-color: var(--mj-gold); }

/* -- Contact rows ---------------------------------------------------------
   Full-width cards that lift like every other card, rather than a bordered
   list. */
.mj-contact-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 24px;
  padding: 24px 28px;
  transition: border-color 0.4s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-contact-row:hover { border-color: var(--mj-gold); transform: translateY(-6px); }
.mj-contact-row__label { font-family: var(--mj-font-mono); font-size: 11.5px;
                         letter-spacing: 0.16em; text-transform: uppercase;
                         color: var(--mj-ink-muted); }
.mj-contact-row__value { font-weight: 700; font-size: 18px; color: var(--mj-ink); }

/* -- Prose pages ----------------------------------------------------------
   book-call and the legal pages are read start to finish, so they get a
   measure rather than a layout. */
.mj-prose { max-inline-size: 68ch; font-size: 18px; line-height: 1.62; }

.mj-section { padding-block-end: var(--mj-section-y, 110px); }
.mj-monolink { color: var(--mj-gold); font-family: var(--mj-font-mono); font-size: 12.5px;
               letter-spacing: 0.14em; text-transform: uppercase; }

@media (max-width: 620px) {
  .mj-card, .mj-panel__cell, .mj-svc, .mj-step, .mj-quote { padding: 26px; }
  .mj-hero--page, .mj-hero { padding-inline: 18px; }
  .mj-contact-row { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* -- Case study page ------------------------------------------------------
   The shot is full-bleed at 16:10 with the site's media shadow, rather than
   the bordered frame the V1 page drew around it. */
.mj-cs__shot > div {
  border-radius: 26px;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  background: var(--mj-surface-v4);
  box-shadow: 0 20px 54px rgb(0 0 0 / 0.42);
  border: 0;
}
.mj-cs__shot img { inline-size: 100%; block-size: 100%; object-fit: cover;
                   object-position: center top; display: block; }

.mj-cs__facts .mj-panel__cell { display: flex; flex-direction: column; gap: 8px; }
.mj-cs__fact-k { font-family: var(--mj-font-mono); font-size: 11.5px;
                 letter-spacing: 0.16em; text-transform: uppercase;
                 color: var(--mj-ink-muted); }
.mj-cs__fact-v { font-weight: 700; font-size: 19px; color: var(--mj-ink); }

/* "Next case study" becomes a full-width row with the gold arrow, hovering
   like every other card. */
.mj-cs__next {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-block-start: var(--mj-section-y, 110px);
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  border-radius: 24px;
  padding: 28px 32px;
  text-decoration: none;
  transition: border-color 0.4s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-cs__next:hover { border-color: var(--mj-gold); transform: translateY(-6px); }
.mj-cs__next > span:last-child { font-family: var(--mj-font-display);
                                 font-weight: 800; font-size: clamp(22px, 2vw, 32px);
                                 letter-spacing: -0.03em; color: var(--mj-ink); }

.mj-shots { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px;
            margin-block-start: 32px; }
.mj-shot { display: block; border-radius: 24px; overflow: hidden;
           box-shadow: 0 20px 54px rgb(0 0 0 / 0.42); }
.mj-shot img { display: block; inline-size: 100%; block-size: auto; }

@media (max-width: 900px) { .mj-shots { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px) { .mj-shots { grid-template-columns: 1fr; } }

/* -- Services page --------------------------------------------------------
   Tabs as chip pills, the current service gold-filled. One URL per service
   and aria-current are untouched — this is a restyle, not a rewiring. */
.mj-tabs { display: flex; flex-wrap: wrap; gap: 10px; }
.mj-tabs a {
  display: inline-flex;
  align-items: center;
  min-block-size: 44px;
  padding: 11px 20px;
  border-radius: 999px;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  font-family: var(--mj-font-mono);
  font-size: 12px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--mj-ink-body);
  text-decoration: none;
  transition: border-color 0.3s ease, color 0.3s ease;
}
.mj-tabs a:hover { border-color: var(--mj-gold); color: var(--mj-ink); }
.mj-tabs a[aria-current="page"] {
  background: var(--mj-gold);
  border-color: var(--mj-gold);
  color: var(--mj-bg);
  font-weight: 700;
}

/* The stat row on the services page carries the same figures as home, so it
   reads the same rules rather than a second set. */
.mj-stats .mj-panel__cell { background: none; border: 0; padding: 0;
                            display: flex; flex-direction: column;
                            align-items: center; gap: 6px; }
.mj-stats .mj-panel__cell:hover { transform: none; border-color: transparent; }

/* =========================================================================
   Corrections from style-specification.md
   ========================================================================= */

/* The headline's second phrase is gold. */
.mj-v4-line--accent > i { color: var(--mj-gold); }

/* Hero headline: the spec's own clamp. The content-derived cap below it
   still applies, so a longer headline shrinks rather than being cut off —
   the spec allows exactly that latitude ("adjust the clamp() ceilings"). */
.mj-v4-hero h1 {
  line-height: 0.88;
  letter-spacing: -0.045em;
  font-size: max(28px, min(clamp(40px, 7.4vw, 116px),
                 calc(100cqw / (var(--mj-hero-ch, 14) * 0.45))));
}

/* Hero screenshots are 4:3, not their natural ratio, with their own shadow. */
.mj-v4-shot { border-radius: 20px; box-shadow: 0 18px 50px rgb(0 0 0 / 0.42); }
.mj-v4-shot:hover { box-shadow: 0 30px 74px rgb(0 0 0 / 0.55); }
.mj-v4-shot img { aspect-ratio: 4 / 3; object-fit: cover; object-position: center top; }
.mj-v4-shots__grid { gap: 22px; }

/* Cards, chips and FAQ */
.mj-v4-tile { border-radius: 26px; }
.mj-v4-tile h3 { font-size: 27px; line-height: 1.2; letter-spacing: -0.02em; }
/* A process step's heading is a size below a capability's. */
#process .mj-v4-tile h3 { font-size: 24px; }

.mj-v4-quote p {
  font-size: 22px;
  line-height: 1.42;
  letter-spacing: -0.015em;
  font-weight: 600;
  color: var(--mj-ink);
}
.mj-v4-quote footer { letter-spacing: 0.16em; }

.mj-v4-faq__item { border-radius: 22px; }
.mj-v4-faq__item summary { font-size: 20px; font-weight: 700; }
.mj-v4-faq__a p { font-size: 17px; line-height: 1.62; max-inline-size: 66ch; }

/* The FAQ is 0.8fr / 1.2fr, not 0.9 / 1.1, and its gap tightens. */
@media (min-width: 900.02px) {
  .mj-v4-faq { grid-template-columns: 0.8fr 1.2fr; gap: 64px; }
}

/* The closing block is 1.1fr / 0.9fr and drops to one column at 900. */
.mj-v4-close__panel { grid-template-columns: 1.1fr 0.9fr; gap: 56px; padding: 70px; }
@media (max-width: 900px) {
  .mj-v4-close__panel { grid-template-columns: 1fr; gap: 34px; padding: 48px; }
}
@media (max-width: 620px) {
  .mj-v4-close__panel { padding: 34px 26px; }
}

/* Type roles the spec pins exactly. */
.mj-v4-hero__sub, .mj-v4-band__head p { max-inline-size: 58ch; }
.mj-v4-wcard__out { font-size: 17px; line-height: 1.5; max-inline-size: 38ch;
                    color: var(--mj-ink-muted); }
.mj-v4-wcard__tag { font-family: var(--mj-font-display); font-size: 13.5px;
                    font-weight: 700; letter-spacing: 0; text-transform: none; }
.mj-v4-rail__pill { font-size: 14px; font-weight: 600; }
.mj-v4-hero__proof { font-size: 13.5px; font-weight: 500; }
.mj-v4-tile__when { font-size: 11px; }
.mj-v4-footer__talk { font-size: clamp(26px, 2.8vw, 40px); }
.mj-v4-footer__links a { font-size: 15px; color: rgb(var(--mj-ink-rgb) / 0.82); }

/* The mobile nav panel is a surface card, not white — it sits on the page,
   not over the video. */
@media (max-width: 1060px) {
  .mj-v4-navlinks {
    background: var(--mj-surface-v4);
    border: 1px solid var(--mj-line-soft);
    border-radius: 26px;
    box-shadow: 0 18px 44px rgb(var(--mj-bg-rgb) / 0.14);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .mj-v4-nav .mj-v4-navlinks a { color: var(--mj-ink); min-block-size: 44px; }
  .mj-v4-nav .mj-v4-lang { font-size: 13px; }
}

/* Rail bar height: 9px of padding, 7px on phones. */
.mj-v4-rail { padding-block: 9px; }
@media (max-width: 620px) { .mj-v4-rail { padding-block: 7px; } }

/* Focus ring: 3px of the ink gold, 3px offset, 4px radius. */
:focus-visible { outline: 3px solid var(--mj-gold-deep); outline-offset: 3px;
                 border-radius: 4px; }

/* Hero overlay padding ladder. */
@media (max-width: 1024px) { .mj-v4-hero__over { padding: 0 24px 44px; } }
@media (max-width: 620px)  { .mj-v4-hero__over { padding: 0 18px 34px; } }
@media (max-width: 400px)  {
  .mj-v4-band, .mj-v4-nums, .mj-v4-faq, .mj-v4-shots, .mj-v4-close,
  .mj-v4-footer { padding-inline: 16px; }
}

/* Grid gaps the spec names, at the widths it names them. */
@media (max-width: 900px) {
  .mj-v4-wgrid { gap: 34px; }
  .mj-v4-grid2 { grid-template-columns: 1fr; }
}
@media (max-width: 620px) {
  .mj-v4-shots__grid { gap: 14px; }
  .mj-v4-hero__over { gap: 22px; }
}

/* The pill closing Selected work, 56px under the grid. */
.mj-v4-work__more { display: flex; justify-content: center; margin-block-start: 56px; }

/* Card minimum heights, so a short body does not leave one card in a row
   noticeably shorter than its neighbours. */
#capabilities .mj-v4-tile { min-block-size: 280px; }
#process .mj-v4-tile { min-block-size: 256px; }
.mj-v4-tile { gap: 14px; }

/* Proof pushes the attribution to the foot of its card, so four quotes of
   different lengths still line their sources up. */
.mj-v4-quote { padding: 38px; justify-content: space-between; gap: 24px; }

/* FAQ: the spec's own numbers. */
.mj-v4-faq__list { display: flex; flex-direction: column; gap: 12px;
                   border-block-start: 0; }
.mj-v4-faq__item { border: 1px solid var(--mj-line-soft); border-radius: 22px;
                   background: var(--mj-surface-v4); padding-inline: 28px; }
.mj-v4-faq__item[open] { border-color: var(--mj-gold); }
.mj-v4-faq__item summary { padding-block: 28px; }
.mj-v4-faq__item summary i { inline-size: 26px; block-size: 26px; }
.mj-v4-faq__item summary i::before,
.mj-v4-faq__item summary i::after { inline-size: 26px; }
@media (min-width: 900.02px) { .mj-v4-faq__head { inset-block-start: 118px; } }

/* -- Floating WhatsApp ----------------------------------------------------
   The official mark on its own. It is already a finished badge — white glyph
   on their green, circular — so a pill around it was a badge inside a button,
   and tinting the glyph to match that pill would have recoloured a trademark.
   No fill, no padding, no label: just the mark and its shadow. */
.mj-wa {
  inset-inline-start: 22px;
  inset-block-end: 22px;
  inset-inline-end: auto;
  display: inline-flex;
  inline-size: 56px;
  block-size: 56px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  box-shadow: 0 16px 40px rgb(0 0 0 / 0.45);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-wa:hover { transform: translateY(-3px); }
.mj-wa svg { display: block; inline-size: 56px; block-size: 56px; }

@media (max-width: 620px) {
  .mj-wa { inset-inline-start: 14px; inset-block-end: 14px; }
}

/* Footer detail from the spec. */
.mj-v4-footer__blurb { max-inline-size: 30ch; font-size: 15px; color: var(--mj-ink-muted); }
.mj-v4-footer__links { display: flex; flex-direction: column; }
.mj-v4-footer__links a { min-block-size: 32px; display: flex; align-items: center; }

/* The WhatsApp mark keeps its sanctioned lockup — white on #25D366 — inside
   the gold pill. Recolouring it to match the pill would be a trademark
   breach; a disc lets the design keep its gold and the mark stay correct. */
.mj-wa__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 34px;
  block-size: 34px;
  border-radius: 50%;
  background: #25D366;
  flex: none;
}

/* -- Gulf projects, at the size the spec gives them -----------------------
   Two cards were rendering 484 CSS px wide inside a bordered inner panel —
   the "blue printed" treatment from an earlier pass — while the spec gives
   this section the same 1640px container as every other card section.

   With only two cards in that width they become the largest images on the
   page after the hero, which is the point: these are the Gulf work the
   section exists to show. The drafting-sheet border and corner ticks stay,
   but they now frame the section rather than squeezing it. */
.mj-v4-gulf__inner {
  max-inline-size: var(--mj-v4-max, 1640px);
  margin-inline: auto;
  padding-inline: clamp(20px, 3vw, 44px);
}

.mj-v4-gulf .mj-v4-wgrid {
  /* Wider gutter than Selected work: two cards this size need more air
     between them than four do, or they read as one block split down the
     middle. */
  gap: 56px 48px;
}

/* The media panel carries more of the card at this size, so its corner radius
   grows with it — 26px on an 800px panel reads as square. */
.mj-v4-gulf .mj-v4-wcard__media { border-radius: 30px; }

/* The name and the line under it scale with the card. */
.mj-v4-gulf .mj-v4-wcard__name { font-size: clamp(34px, 3vw, 48px); }
.mj-v4-gulf .mj-v4-wcard__out { font-size: 18px; max-inline-size: 46ch; }
.mj-v4-gulf .mj-v4-wcard__pill { inline-size: 64px; block-size: 64px; font-size: 22px; }

/* The tag sits on a bigger picture, so it can afford to be legible. */
.mj-v4-gulf .mj-v4-wcard__tag { font-size: 14px; padding: 11px 20px;
                                inset-block-start: 18px; inset-inline-start: 18px; }

@media (max-width: 900px) {
  .mj-v4-gulf .mj-v4-wgrid { gap: 40px; }
  .mj-v4-gulf .mj-v4-wcard__media { border-radius: 22px; }
}

/* -- Reel deck ------------------------------------------------------------
   The clips stacked and fanned rather than laid out in a row. Five portrait
   reels side by side are five thin strips; overlapped, each one is big enough
   to read and the stack still shows there are more behind it.

   Built on negative margins and a rotation keyed to --deck-i, so the fan is
   arithmetic rather than five hand-placed cards — adding a sixth reel in the
   admin extends the deck without anyone touching this file. */
.mj-v4-reels { padding-block-end: var(--mj-section-y, 110px); }

.mj-v4-deck {
  --deck-w: clamp(150px, 15vw, 232px);
  display: flex;
  justify-content: center;
  align-items: flex-start;
  /* The fan leans right, so the row needs headroom on the left of the first
     card and the right of the last. */
  padding-inline: clamp(16px, 6vw, 90px);
  padding-block: 30px;
  /* A rotated card's corner reaches outside its box. */
  overflow: visible;
}

.mj-v4-deck__card {
  position: relative;
  margin: 0;
  flex: none;
  inline-size: var(--deck-w);
  aspect-ratio: 9 / 16;
  border-radius: 22px;
  overflow: hidden;
  background: var(--mj-surface-v4);
  border: 1px solid var(--mj-line-soft);
  box-shadow: 0 20px 54px rgb(0 0 0 / 0.42);

  /* Each card overlaps the one before it and leans a little further. The
     first card sits square; the rotation grows with the index so the deck
     opens out rather than every card tilting the same way. */
  /* A flat pull of a third of a card, so the exposed strip of each is equal
     and about two thirds stays visible — enough to read a poster and still
     say "there are more behind this".

     Expressed against --deck-w, not as a percentage: a percentage margin
     resolves against the containing block, not the element, so -30% here
     meant -30% of the whole row and the deck collapsed into one pile. */
  margin-inline-start: calc(var(--deck-w) * -0.34);
  transform: rotate(calc((var(--deck-i) - 2) * 2.4deg))
             translateY(calc(var(--deck-i) * 6px));
  transform-origin: bottom center;
  /* Later cards sit on top, so the fan reads front-to-back left to right. */
  z-index: calc(10 + var(--deck-i));
  transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1),
              box-shadow 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-v4-deck__card:first-child { margin-inline-start: 0; }

/* Hovering or tabbing to a card pulls it out of the stack and straightens it,
   which is what makes an overlapped deck usable rather than decorative.
   `:focus-within` covers the keyboard, since the play button is the only
   focusable thing inside. */
.mj-v4-deck__card:hover,
.mj-v4-deck__card:focus-within {
  transform: rotate(0deg) translateY(-18px) scale(1.06);
  box-shadow: 0 34px 78px rgb(0 0 0 / 0.58);
  z-index: 40;
}

.mj-v4-deck__card video {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

/* The play button is the whole card's hit area — a 26px glyph is not a tap
   target, and the caption underneath should not be the only way in. */
.mj-v4-deck__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  block-size: 100%;
  padding: 0;
  border: 0;
  cursor: pointer;
  color: var(--mj-bg);
  background: linear-gradient(180deg,
              rgb(var(--mj-bg-rgb) / 0.10) 0%,
              rgb(var(--mj-bg-rgb) / 0.05) 45%,
              rgb(var(--mj-bg-rgb) / 0.72) 100%);
}
/* The disc is drawn on the ::before so the button can fill the card while the
   visible control stays 56px. */
.mj-v4-deck__play::before {
  content: "";
  position: absolute;
  inline-size: 56px;
  block-size: 56px;
  border-radius: 50%;
  background: var(--mj-gold);
  transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}
.mj-v4-deck__play svg { position: relative; margin-inline-start: 3px; }
.mj-v4-deck__card:hover .mj-v4-deck__play::before,
.mj-v4-deck__play:focus-visible::before { transform: scale(1.12); }

.mj-v4-deck__cap {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  padding: 14px 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  pointer-events: none;
}
.mj-v4-deck__name { font-family: var(--mj-font-display); font-weight: 700;
                    font-size: 15px; color: var(--mj-ink); }
.mj-v4-deck__kind { font-family: var(--mj-font-mono); font-size: 10.5px;
                    letter-spacing: 0.14em; text-transform: uppercase;
                    color: var(--mj-gold); }

/* Below 900 the fan becomes a scrolling row: overlapping cards on a phone
   leave the ones underneath unreachable, because there is no hover to pull
   them out. */
@media (max-width: 900px) {
  .mj-v4-deck {
    justify-content: flex-start;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 14px;
    padding-inline: 18px;
    -webkit-overflow-scrolling: touch;
  }
  .mj-v4-deck__card {
    margin-inline-start: 0;
    transform: none;
    scroll-snap-align: center;
    inline-size: min(62vw, 240px);
  }
  .mj-v4-deck__card:hover,
  .mj-v4-deck__card:focus-within { transform: translateY(-6px); }
}

@media (prefers-reduced-motion: reduce) {
  .mj-v4-deck__card { transform: none; margin-inline-start: 0; }
  .mj-v4-deck { gap: 14px; flex-wrap: wrap; }
}
