/* ==========================================================================
   JUSTO ACCOUNTANT - stylesheet
   --------------------------------------------------------------------------
   Frosted glass on white. Ink on paper, in three values: white, grey and
   black. Every surface is still a translucent pane that blurs what is
   behind it, but the page is bright now, not dark, and the brief was that
   people should see a bright solution.

   Three rules hold the look together:
     1. There is no brand colour. Black is the accent. Green and red are
        the only exceptions, and both are functional: WhatsApp is their
        green, red is only ever an error. External brand colours appear
        on the social links, on hover, and nowhere else.
     2. Every pane has a white 1px edge along its top and a hair of ink
        along its bottom. On a light page the specular runs both ways, or
        the pane has no under-edge and floats.
     3. Separation comes from a hairline and a shadow, never from filling
        a shape with a darker grey. If a band needs to read as different,
        it takes a 2-4% ink wash, not a fill.

   SIZING IS MOBILE-FIRST AND TOKEN-DRIVEN. Every rule below is written for
   a phone first; wider screens are added with min-width blocks only. Do not
   write a raw px font-size and do not add a max-width query - the scales in
   section 0 already cover every screen. The full contract is in
   USE-THESE-MEASUREMENTS.md at the project root. Read it before changing
   anything here.

   0.  Design tokens (EDIT COLOURS AND SIZES HERE)
   1.  Reset & base
   2.  Aurora backdrop + layout helpers
   3.  Buttons
   4.  Header / navigation / language switch
   5.  Hero (particle text)
   6.  Sections: about, concern wall, solutions, values, CTA, contact
   7.  Footer
   8.  Cookie consent
   9.  Modal + form
   10. Privacy page
   11. Scroll reveal
   12. RTL (Arabic) overrides
   13. Responsive breakpoints + device capabilities
   ========================================================================== */

/* ==========================================================================
   0. DESIGN TOKENS  <-- change the palette AND the measurements here
   ========================================================================== */
:root {
  /* ---- The field: white, stepping down through two greys. Warmed a
          hair off pure neutral so a white pane still reads as a pane
          against it rather than disappearing. ------------------------- */
  /* Not pure white. A translucent white pane on a pure white page is
     invisible: the blur has nothing to pick up and the glass stops
     reading as glass. The page is a bright off-white that steps down as
     it descends, so every pane sits a shade lighter than its ground. */
  --void:   #FAFAF8;
  --deep:   #EFEFEC;
  --deep-2: #E4E4E0;

  /* ---- ONE accent. Oxblood.
          It is deliberately scarce: the rule beside a section label, the
          primary button, the current nav link, a focused field, an error.
          If you are about to colour a sixth kind of thing, use type or
          space to make the distinction instead. A different hue per card
          is what makes a page look generated. ------------------------- */
  --accent:      #141413;   /* black. the palette is white, grey, black */
  --accent-lift: #3C3B39;   /* the one step off black, for hover */
  --accent-ink:  #FFFFFF;   /* type sitting on the accent */

  --whatsapp: #12833F;      /* their green, darkened to carry white type */
  --danger:   #B3261E;      /* the one red, and only ever for an error */

  /* ---- Type: near-black, never pure #000 on pure white ------------- */
  --text:   #141413;
  --text-2: rgba(20, 20, 19, 0.66);
  --text-3: rgba(20, 20, 19, 0.44);

  /* ---- Glass ----------------------------------------------------------
     --pane      the fill: a top-lit gradient, brighter at the top edge
     --pane-line the hairline border
     --spec      the specular inner highlight that sells the material
     --lift      the outer shadow that separates a pane from the aurora  */
  --pane:      linear-gradient(158deg, rgba(255,255,255,0.72), rgba(255,255,255,0.40) 62%);
  --pane-hi:   linear-gradient(158deg, rgba(255,255,255,0.90), rgba(255,255,255,0.62) 62%);
  --pane-line: rgba(20, 20, 19, 0.10);
  --pane-line-hi: rgba(20, 20, 19, 0.20);
  /* On a light page the specular runs the other way: white along the top,
     and a hair of ink along the bottom so the pane has an under-edge. */
  /* Three edges, not one. A white top edge, a white rim all the way
     round to catch the light, and a hair of ink underneath so the pane
     has a bottom. Take the rim away and it flattens onto the page. */
  --spec: inset 0 1px 0 rgba(255, 255, 255, 1),
          inset 0 0 0 1px rgba(255, 255, 255, 0.55),
          inset 0 -1px 0 rgba(20, 20, 19, 0.06);
  --lift: 0 20px 48px -24px rgba(20, 20, 19, 0.26),
          0 2px 6px -3px rgba(20, 20, 19, 0.10);

  /* Blur is the most expensive thing on this page: a backdrop-filter makes
     the browser re-sample everything behind the pane on every frame.
     Phones get a cheaper radius; section 13 raises it from 48rem up, where
     there is a GPU budget for it. */
  --blur:    saturate(135%) brightness(1.04) blur(14px);
  --blur-sm: saturate(125%) brightness(1.03) blur(10px);

  /* Typography: Outfit echoes the geometric wordmark, Inter sets the text */
  --font-display: "Outfit", ui-sans-serif, system-ui, "Segoe UI", Arial, sans-serif;
  --font-sans:    "Inter", ui-sans-serif, system-ui, "Segoe UI", Roboto, Arial, sans-serif;
  --font-ar:      "Noto Kufi Arabic", "Segoe UI", Tahoma, sans-serif;

  /* ======================================================================
     THE TYPE SCALE - fluid, phone to desktop, one clamp() each.
     ----------------------------------------------------------------------
     Use a token. Do not write a font-size in px, and do not wrap a
     font-size in a media query: the clamp already interpolates between a
     360px phone and a 1280px desktop, so one declaration covers every
     screen.

       token         phone -> desktop     what it is for
       --fs-3xs      10.0 -> 10.6px       the tiniest tracked caps
       --fs-2xs      10.6 -> 11.5px       eyebrow, stat labels, footer tag
       --fs-xs       11.5 -> 12.5px       button and nav labels
       --fs-sm       12.8 -> 13.9px       fine print, form errors, notes
       --fs-md       14.4 -> 15.2px       card copy, list items
       --fs-base       16 -> 17px         body copy   <- never go smaller
       --fs-lg       16.8 -> 18.9px       hero lead, section sub
       --fs-xl       18.4 -> 21.6px       h3
       --fs-2xl      20.8 -> 28px         service titles
       --fs-3xl      25.6 -> 38px         large display
       --fs-4xl      30.4 -> 52px         h2, CTA title
       --fs-5xl      36.8 -> 70px         h1
     ====================================================================== */
  --fs-3xs:  clamp(0.625rem, 0.60rem + 0.10vw, 0.66rem);
  --fs-2xs:  clamp(0.66rem,  0.63rem + 0.12vw, 0.72rem);
  --fs-xs:   clamp(0.72rem,  0.69rem + 0.14vw, 0.78rem);
  --fs-sm:   clamp(0.80rem,  0.77rem + 0.16vw, 0.87rem);
  --fs-md:   clamp(0.90rem,  0.87rem + 0.16vw, 0.95rem);
  --fs-base: clamp(1rem,     0.96rem + 0.18vw, 1.0625rem);
  --fs-lg:   clamp(1.05rem,  1.00rem + 0.30vw, 1.18rem);
  --fs-xl:   clamp(1.15rem,  1.05rem + 0.50vw, 1.35rem);
  --fs-2xl:  clamp(1.30rem,  1.10rem + 0.90vw, 1.75rem);
  --fs-3xl:  clamp(1.60rem,  1.20rem + 1.80vw, 2.40rem);
  --fs-4xl:  clamp(1.90rem,  1.30rem + 2.80vw, 3.25rem);
  --fs-5xl:  clamp(2.30rem,  1.50rem + 3.90vw, 4.40rem);

  /* One display size of its own: the hero stat numerals sit three abreast
     on a phone and must not blow their column out. */
  --fs-stat: clamp(1.75rem, 1.30rem + 2vw, 2.5rem);

  /* The hero headline. It is an h1, so on a phone it is simply --fs-5xl.
     From lg the hero splits into two columns and the headline shares the
     row with the particle stage, so it is redefined smaller in section 13
     - in one place, rather than as a raw clamp at the call site. */
  --fs-hero: var(--fs-5xl);

  /* Icon glyphs drawn as text: the modal close x, the FAQ + and -. Sized
     as artwork rather than copy, which is why they are not on the scale. */
  --fs-glyph: 1.5rem;

  /* Oversized decorative marks - the pull-quote " behind the founder card.
     Ornament, not type: it is never read, and it is clipped on purpose. */
  --fs-ornament: clamp(9rem, 20vw, 18rem);

  /* ======================================================================
     THE SPACE SCALE - padding, margin and gap.
     ----------------------------------------------------------------------
     Small steps are fixed: 4/8/12/16/24px do not need to scale, and
     scaling them only breaks optical alignment. Everything above 24px is
     fluid, because that is where a desktop rhythm starts to feel cavernous
     on a phone.

       --sp-1   4px      --sp-6   24 ->  36px
       --sp-2   8px      --sp-7   32 ->  52px
       --sp-3  12px      --sp-8   44 ->  72px
       --sp-4  16px      --sp-9   56 -> 104px
       --sp-5  24px
     ====================================================================== */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: clamp(1.5rem,  1.15rem + 1.5vw, 2.25rem);
  --sp-7: clamp(2rem,    1.40rem + 2.6vw, 3.25rem);
  --sp-8: clamp(2.75rem, 1.80rem + 4.0vw, 4.5rem);
  --sp-9: clamp(3.5rem,  2.00rem + 6.5vw, 6.5rem);
  /* Room under a client logo for its name on touch screens, where the name
     shows without a hover. Fixed, because the logo box itself is a fixed
     7rem: a fluid gap would squeeze the logos on tablets. */
  --logo-name-band: 2rem;

  /* The founder portrait in the home About credit. Fluid, so the photo
     column grows with the screen without a breakpoint of its own. */
  --portrait-w: clamp(14rem, 9rem + 12vw, 19rem);

  /* The home services rail. --icon-box is the tinted square each glyph
     sits in, 60px on a phone to about 98px on a desktop; it fills with its
     service's colour when its column opens. --tile-grow is how many times
     wider an open column is than a shut one. The rail works both of its
     text measures out from it, so change it here and nowhere else. */
  --icon-box: clamp(3.75rem, 2.5rem + 4.5vw, 6.5rem);
  --tile-grow: 2;

  /* Each service's own colour, on the home rail's icons and the rule over
     an open column. The one exception to the one-accent rule above, asked
     for on 18 October 2026. All three hold 3:1 against the page. */
  --svc-accounting: #2F5BD3;   /* ledger blue */
  --svc-taxation:   #C4610F;   /* amber */
  --svc-finance:    #12835A;   /* green */

  /* ======================================================================
     TOUCH TARGETS - a floor, not a suggestion.
     ----------------------------------------------------------------------
     --tap        44px. WCAG 2.5.5 and the Apple HIG. Anything a finger
                  taps gets at least this as min-block-size, small buttons
                  included.
     --tap-dense  36px. Only for controls inside a tight group that its
                  container already pads out - the EN/ID/AR switch. Still
                  clears WCAG 2.5.8 AA, which asks for 24px.
     --control-h  48px. Form fields and the primary call to action.
     --field-fs   The font-size of any input or textarea. iOS Safari zooms
                  the whole page when a focused field is under 16px, so
                  this is floored with max() and must never be swapped for
                  a smaller token.
     ====================================================================== */
  --dock-size: 4.5rem;      /* the WhatsApp dock: the mark needs room to read */
  --dock-open-w: 13rem;     /* the button the mark opens into on hover       */
  --dock-open-h: 3.25rem;
  --tap:       2.75rem;
  --tap-dense: 2.25rem;
  --control-h: 3rem;
  --field-fs:  max(1rem, var(--fs-md));

  /* ======================================================================
     LAYOUT
     ----------------------------------------------------------------------
     --gutter    the page margin. 18px on a phone so copy is not squeezed,
                 opening to 52px on a desktop.
     --gutter-x  the same, but never underneath a notch or a rounded
                 corner. index.html sets viewport-fit=cover, so the page
                 really does extend under the sensor housing in landscape;
                 every full-bleed edge uses this instead of --gutter.
     --header-h  64px on a phone, 92px on a desktop. Fluid, so nothing has
                 to restate it per breakpoint and scroll-padding-top tracks
                 it for free.
     ====================================================================== */
  /* The page's width. It used to stop at 1240px, which left a laptop at
     1600px with 230px of empty margin either side and a 1920px screen with
     390. Now it is 92% of the screen, never narrower than 1240 and never
     wider than 1760, so the margin stays near 110-130px (with --gutter)
     from a 1366 laptop to a 1920 monitor, and lines do not run on for ever
     on an ultrawide. */
  --container:        clamp(1240px, 92vw, 1760px);
  --container-narrow: 46rem;
  /* The outer clamp is 18px -> 52px, as it always was. The inner floor only
     bites under 320px - the cover screens of a Galaxy Fold (280) and a Z Fold
     2 or 3 (311, 317) - where the margin keeps narrowing, to 14px at 280, so
     the header row still fits. At 320 and up the value is unchanged. */
  --gutter:    clamp(0.875rem, 10vw - 0.875rem, clamp(1.125rem, 4vw, 3.25rem));
  --section-y: clamp(3.5rem, 7vw, 8.5rem);
  /* Compact by default and opened up at min-height: 36rem, further down.
     --header-h is driven by viewport WIDTH, so a phone held sideways asks
     for a 72px header on a screen only 360px tall - a fifth of it gone
     before anything is read. Short screens keep 52px. Everything that
     clears the header (scroll-padding, the hero, the drawer) reads this
     token, so they all follow without restating it. */
  --header-h:  3.25rem;
  --gap-grid:  clamp(0.85rem, 2vw, 1.15rem);
  --mortar:    5px;          /* the brick-wall gap, from the logo artwork */

  /* --gap-col  the gutter between the two columns of a split page section:
                About, the founder card, the FAQ, Contact. They all had
                their own hand-tuned clamp, which is exactly how a layout
                starts to drift - one token now, so they stay in step.
     --gap-nav  the rhythm of the desktop nav row, shared by the links and
                the rule before the tagline.                              */
  /* The consent panel's height budget, as tokens rather than numbers in
     two places. .consent__text bounds itself with the same pair, because a
     percentage cannot: the panel's height comes from max-block-size, not a
     definite height, so there is nothing for a percentage to resolve
     against, and the text would size to its content and be clipped by the
     panel's overflow: hidden. Both values are restated for a taller screen
     and for the expanded state further down. */
  --consent-pad: var(--sp-4);
  --consent-max: min(78svh, calc(100svh - var(--header-h) - var(--sp-9)));

  --gap-col: clamp(2rem, 6vw, 5.5rem);
  /* The max term is 1.75rem, not 2.1rem, and that ceiling is load-bearing.
     When .container stopped at a fixed 1240px, the header row had a fixed
     1136px from about 1300px up while a vw term kept growing: at 2.1rem the
     nav gap reached 33.6px on a 1440px screen, the row needed 1139px of the
     1136, the actions wrapped and the header became 161px tall. --container
     now grows with the screen, which gives the row more room, but the cap
     still keeps the desktop nav rhythm steady.

     If you change --container or add a nav item, re-measure: the row needs
     brand + nav + actions + 2 header gaps to stay under .container minus
     its gutters. tools/check-devices.mjs fails if it ever wraps. */
  --gap-nav: clamp(0.9rem, 2.2vw, 1.75rem);

  /* The phone header row: the cube mark, the wordmark and one EN/ID/AR
     button. Each inner clamp is the size that held a 320px header. Below
     320 each keeps shrinking with the screen, the same way --gutter does,
     because brand + switch + burger at their 320 sizes need 321px and a
     Galaxy Fold cover screen is 280. The burger stays --tap: it is the one
     thing that may not shrink. From 320 up nothing changes. */
  --brand-mark: clamp(36px, 20vw - 20px, clamp(44px, 11vw, 52px));
  --brand-word: clamp(58px, 25vw - 12px, clamp(68px, 18vw, 86px));
  --lang-btn:   clamp(1.625rem, 15vw - 1rem, clamp(2rem, 8vw, 2.5rem));

  /* Two screens and a hinge (Surface Duo), read from the browser. They are
     only used inside the viewport-segments blocks in section 13; anywhere
     else the hinge is 0 and the fallbacks never apply.
     --hinge      the hinge between screens side by side
     --hinge-y    the hinge between screens one above the other
     --pane-w     the measure of one screen, inside the page gutters
     --gap-hinge  a column gap that straddles the hinge with a gutter of
                  clear space either side, so two equal columns split
                  exactly at it
     --screen-a-h / --screen-b-h  the upper and lower screen's height    */
  --hinge:      calc(env(viewport-segment-left 1 0, 0px) - env(viewport-segment-right 0 0, 0px));
  --hinge-y:    calc(env(viewport-segment-top 0 1, 0px) - env(viewport-segment-bottom 0 0, 0px));
  --pane-w:     calc(env(viewport-segment-width 0 0, 100vw) - 2 * var(--gutter-x));
  --gap-hinge:  calc(var(--hinge) + 2 * var(--gutter-x));
  --screen-a-h: env(viewport-segment-height 0 0, 100svh);
  --screen-b-h: env(viewport-segment-height 0 1, 100svh);

  /* Safe areas. Zero everywhere except on a notched phone. */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --gutter-x: max(var(--gutter), var(--safe-l), var(--safe-r));

  /* Shape - everything is rounded; glass has no sharp edges */
  /* Tight. A 34px corner on every surface is the house style of generated
     layouts; at 3-8px the same pane reads as a cut sheet of glass. */
  /* spacing of the ruled lines drifting behind the page */
  --rule-gap: 2.75rem;

  --r-sm:   2px;
  --r:      3px;
  --r-lg:   5px;
  --r-xl:   8px;
  --r-circle: 50%;  /* an actual circle; --r-pill is square in this design */
  --r-pill: 3px;   /* nothing is a pill any more; kept so call sites hold */

  /* Stacking order, in one place so nothing has to guess a number */
  --z-bg:      -1;
  --z-particles: 1;         /* the hero dots: over the page, under the copy  */
  --z-dock:    900;         /* the WhatsApp dock the dots fly into           */
  --z-header:  800;
  --z-consent: 1000;
  --z-modal:   1100;

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

/* ==========================================================================
   1. RESET & BASE
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + var(--sp-5));
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: 1.7;
  color: var(--text-2);
  background: var(--void);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  /* Android and iOS paint a grey box over whatever a finger touches. Every
     interactive element below has its own :active state instead. */
  -webkit-tap-highlight-color: transparent;
}

/* Locking the body while a dialog is open. overscroll-behavior stops the
   scroll chaining through to the page behind the panel. */
body.is-locked { overflow: hidden; overscroll-behavior: none; }

/* The [hidden] attribute must beat the display values set by component
   classes below (.btn, .consent__options, .modal ...). */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  font-family: var(--font-display);
  color: var(--text);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.035em;
  /* Long words in a headline overflow a 320px phone rather than wrap. */
  overflow-wrap: break-word;
}
h1 { font-size: var(--fs-5xl); font-weight: 800; line-height: 1.04; }
h2 { font-size: var(--fs-4xl); }
h3 { font-size: var(--fs-xl); letter-spacing: -0.02em; line-height: 1.2; }

p { margin: 0 0 1.1em; overflow-wrap: break-word; }
p:last-child { margin-bottom: 0; }

a { color: inherit; text-decoration: none; }
img, svg, canvas { display: block; max-width: 100%; }
img { block-size: auto; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button, input, textarea, select { font: inherit; color: inherit; }

/* touch-action tells the browser the element will never be double-tapped
   to zoom, which is what removes the 300ms delay before a tap registers. */
button, a, label, summary, input, textarea, select { touch-action: manipulation; }

::selection { background: rgba(20, 20, 19, 0.14); color: var(--text); }

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

/* The logo artwork is dark-on-transparent, drawn for paper. Everything here
   sits on a dark canvas, so it is flipped to read as light. */
/* The logo artwork is dark-on-transparent: drawn for paper, and the page
   is paper again, so it needs no inversion. */
.brand__word img { filter: brightness(0); opacity: 0.92; }
.brand__mark img { filter: none; }

/* ==========================================================================
   2. AURORA BACKDROP + LAYOUT HELPERS
   --------------------------------------------------------------------------
   One fixed layer behind everything. The static radial gradients cost
   nothing and lay down the colour field; the soft blobs on top of them
   are what actually moves. Keep the blob count low - each one is a large
   surface composited on every frame, which is exactly the work a phone GPU
   is worst at. Phones run two; the third joins at 48rem.

   The blobs carry no filter: blur(). Each is already a radial gradient
   that fades to nothing, so the blur changed no pixel by more than 7 of
   255 - and re-blurring three moving surfaces on every frame was half of
   all the GPU work on the page (measured 18 October 2026 on a laptop's
   Intel graphics), the main cause of the stutter. Do not put it back.
   ========================================================================== */
.bg {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  overflow: hidden;
  pointer-events: none;
  background:
    radial-gradient(110% 80% at 72% 4%,  rgba(20, 20, 19, 0.055), transparent 62%),
    radial-gradient(90% 70% at 10% 30%,  rgba(20, 20, 19, 0.085), transparent 64%),
    radial-gradient(80% 60% at 92% 84%,  rgba(20, 20, 19, 0.050), transparent 62%),
    linear-gradient(175deg, var(--void) 0%, var(--deep) 55%, var(--deep-2) 100%);
}

.bg__blob {
  position: absolute;
  border-radius: 50%;
  opacity: 0.5;
  will-change: transform;
}
.bg__blob--a {
  inline-size: 46vw; block-size: 46vw;
  inset-block-start: -8vh; inset-inline-start: 52%;
  background: radial-gradient(circle, rgba(20, 20, 19, 0.10), transparent 68%);
  animation: drift-a 34s var(--ease) infinite;
}
.bg__blob--b {
  inline-size: 40vw; block-size: 40vw;
  inset-block-start: 38vh; inset-inline-start: -10vw;
  background: radial-gradient(circle, rgba(20, 20, 19, 0.13), transparent 68%);
  animation: drift-b 41s var(--ease) infinite;
}
.bg__blob--c {
  display: none;                     /* phones: two blobs are enough */
  inline-size: 34vw; block-size: 34vw;
  inset-block-end: -6vh; inset-inline-end: 6vw;
  background: radial-gradient(circle, rgba(20, 20, 19, 0.075), transparent 70%);
  animation: drift-c 47s var(--ease) infinite;
  opacity: 0.34;
}

@keyframes drift-a {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33%      { transform: translate3d(-7vw, 8vh, 0) scale(1.18); }
  66%      { transform: translate3d(5vw, -5vh, 0) scale(0.9); }
}
@keyframes drift-b {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  40%      { transform: translate3d(9vw, -7vh, 0) scale(0.88); }
  70%      { transform: translate3d(-4vw, 6vh, 0) scale(1.14); }
}
@keyframes drift-c {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  50%      { transform: translate3d(-8vw, -9vh, 0) scale(1.22); }
}

/* Film grain, and it moves.

   The grain is doing two jobs. Large blurred gradients band badly on 8-bit
   displays and the noise dithers the ramp, but a still grain also reads as
   a texture laid over the page. Stepping it between eight positions at 8fps
   makes it read as the surface itself, the way grain sits in film rather
   than on top of it. steps() is deliberate: a smooth tween would slide the
   noise and look like a moving pattern instead of a live one. */
.bg::after {
  content: "";
  position: absolute;
  inset: -50%;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain 1s steps(1, end) infinite;
  will-change: transform;
}

@keyframes grain {
  0%   { transform: translate3d(0, 0, 0); }
  12%  { transform: translate3d(-3%, -2%, 0); }
  25%  { transform: translate3d(2%, -4%, 0); }
  37%  { transform: translate3d(-4%, 3%, 0); }
  50%  { transform: translate3d(3%, 4%, 0); }
  62%  { transform: translate3d(-2%, -3%, 0); }
  75%  { transform: translate3d(4%, 2%, 0); }
  87%  { transform: translate3d(-3%, 4%, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* Ruled lines, drifting upward.

   An accountant's page has ruled paper behind it. The rules are a single
   repeating-linear-gradient on one element, moved with a transform, so the
   whole thing is one composited layer and costs nothing per frame. The
   element is twice the viewport tall and travels exactly one line spacing,
   which is what makes the loop seamless.

   Masked to fade out top and bottom so the lines never collide with the
   header or run under the footer as hard edges. */
.bg__rules {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  block-size: 200%;
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(20, 20, 19, 0.055) 0,
    rgba(20, 20, 19, 0.055) 1px,
    transparent 1px,
    transparent var(--rule-gap)
  );
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
  animation: rules 26s linear infinite;
  will-change: transform;
}

@keyframes rules {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, calc(var(--rule-gap) * -1), 0); }
}

/* --- The glass recipe ----------------------------------------------------
   Everything that looks like a pane gets these four declarations, listed
   here once and composed by the components below rather than repeated.   */
.lang,
.nav-toggle,
.faith__card,
.founder-card,
.contact-card,
.privacy-choice,
.cta-band__inner,
.consent__inner,
.modal__panel,
.btn--ghost,
.btn--outline,
.form input,
.form textarea {
  background: var(--pane);
  -webkit-backdrop-filter: var(--blur-sm);
          backdrop-filter: var(--blur-sm);
  border: 1px solid var(--pane-line);
  box-shadow: var(--spec);
}

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter-x);
}

.section { padding-block: var(--section-y); position: relative; }

/* Section tints are veils over the aurora, not fills. They darken just
   enough to separate one band from the next. */
.section--muted { background: rgba(20, 20, 19, 0.022); }
.section--dark  { background: rgba(20, 20, 19, 0.045); }
.section--muted, .section--dark {
  -webkit-backdrop-filter: blur(3px);
          backdrop-filter: blur(3px);
}

.section__head { max-width: var(--container-narrow); margin-bottom: var(--sp-7); }
.section__title { margin-bottom: 0.35em; }
.section__sub { color: var(--text-2); font-size: var(--fs-lg); max-width: 38rem; }

/* Section label: a small glowing capsule + tracked caps. */
.eyebrow {
  margin: 0 0 var(--sp-5);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-3);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
.eyebrow::before {
  content: "";
  inline-size: 30px;
  block-size: 6px;
  flex: none;
  border-radius: var(--r-pill);
  background: var(--accent);
}

.lead {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  line-height: 1.4;
  letter-spacing: -0.02em;
  color: var(--text);
  font-weight: 500;
}

.linklike {
  background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
  text-decoration: underline; text-underline-offset: 3px; opacity: 0.7;
  /* A bare text button is still a tap target. */
  min-block-size: var(--tap-dense);
  display: inline-flex; align-items: center;
  text-align: start;
  transition: opacity 0.2s, color 0.2s;
}
.linklike:active { opacity: 1; color: var(--accent-lift); }

/* ==========================================================================
   3. BUTTONS - pills. Solid ones are lit gradients, quiet ones are glass.
   --------------------------------------------------------------------------
   Every button clears --tap on its short side, .btn--sm included. A 33px
   pill is comfortable with a mouse and a coin-toss with a thumb.
   ========================================================================== */
.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-block-size: var(--tap);
  padding: var(--sp-3) var(--sp-6);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  text-align: center;
  -webkit-user-select: none;
          user-select: none;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.3s var(--ease), box-shadow 0.3s var(--ease),
              border-color 0.3s var(--ease), background 0.3s var(--ease);
}

/* Button labels are controls, not copy to highlight. Preventing selection
   also stops browser translation popovers from covering the CTA. The
   selection colours are a fallback for browsers that ignore user-select. */
.btn::selection,
.btn *::selection {
  background: rgba(255, 255, 255, 0.22);
  color: inherit;
}

/* Touch has no hover: a :hover rule fired by a tap sticks to the element
   until something else is tapped. Everything that lifts, blooms or sweeps
   below is therefore behind (hover: hover), and touch gets an :active
   state instead - which is real feedback, because it ends on release. */
@media (hover: hover) {
  .btn:hover { transform: translateY(-2px); }
}
.btn:active { transform: scale(0.98); }

.btn--primary {
  background: var(--accent);
  color: #fff;
  box-shadow: var(--spec);
}

/* The free-offer button. Warm, so it never reads as "just another link". */
.btn--accent {
  background: var(--text);
  color: var(--accent-ink);
  box-shadow: var(--spec);
}

/* Glass buttons: the pane rule in section 2 already gave these their fill,
   blur, border and specular edge. */
.btn--whatsapp {
  background: linear-gradient(135deg, #16A34A 0%, var(--whatsapp) 55%, #0E7A3C 100%);
  color: #FFFFFF;
  box-shadow: var(--spec);
}
.btn--whatsapp::before {
  content: "";
  inline-size: 17px; block-size: 17px; flex: none;
  background: currentColor;
  -webkit-mask: var(--wa-icon) center / contain no-repeat;
          mask: var(--wa-icon) center / contain no-repeat;
  --wa-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.04 2c-5.46 0-9.9 4.44-9.9 9.9 0 1.75.46 3.45 1.32 4.95L2 22l5.3-1.38a9.9 9.9 0 0 0 4.74 1.2c5.46 0 9.9-4.43 9.9-9.9S17.5 2 12.04 2zm5.8 14.02c-.24.68-1.42 1.3-1.95 1.35-.5.05-.97.24-3.27-.68-2.75-1.08-4.5-3.87-4.64-4.05-.13-.18-1.1-1.47-1.1-2.8 0-1.32.7-1.97.94-2.24.25-.27.54-.34.72-.34l.52.01c.17 0 .4-.06.62.48.24.56.8 1.94.87 2.08.07.14.12.3.02.48-.1.18-.15.29-.29.45-.14.16-.3.36-.43.48-.14.14-.29.29-.12.57.17.27.75 1.24 1.62 2.01 1.11.99 2.05 1.3 2.33 1.44.28.14.45.12.61-.07.17-.2.71-.83.9-1.11.19-.28.37-.23.63-.14.25.09 1.63.77 1.9.91.29.14.47.2.54.32.07.11.07.65-.17 1.33z'/%3E%3C/svg%3E");
}

@media (hover: hover) {
  .btn--ghost:hover, .btn--outline:hover {
    background: var(--pane-hi);
    border-color: var(--pane-line-hi);
  }
  .btn--primary:hover  { background: var(--accent-lift); }
  .btn--accent:hover {
    background: var(--accent-lift);
    color: var(--accent-ink);
    box-shadow: 0 10px 24px rgba(20, 20, 19, 0.18), var(--spec);
  }
  .btn--whatsapp:hover { background: #22894F; }
}

/* --sm is visually smaller but still --tap tall: the padding shrinks, the
   hit area does not. */
.btn--sm { padding: var(--sp-2) var(--sp-5); font-size: var(--fs-2xs); letter-spacing: 0.12em; }
.btn--lg { min-block-size: var(--control-h); padding: var(--sp-4) var(--sp-7); font-size: var(--fs-sm); }
.btn--block { display: flex; inline-size: 100%; }

/* ==========================================================================
   4. HEADER / NAVIGATION
   --------------------------------------------------------------------------
   A floating glass pill rather than a bar welded to the top edge, so the
   aurora shows above and around it. The pane only materialises once the
   page is scrolled.

   Written phone-first: the burger is the default and the nav is a drawer.
   Section 13 turns it into a row from 64rem up, which is the width at
   which the links, the language switch and the call to action stop
   fighting each other. An iPad in portrait (768px) keeps the drawer.
   ========================================================================== */
.site-header {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-header);
  /* min-, not a fixed block-size: if a visitor runs their browser at a
     larger text size the row can need more than --header-h, and a fixed
     height would clip it rather than let it grow. */
  min-block-size: calc(var(--header-h) + var(--safe-t));
  display: flex;
  align-items: center;
  /* Flush to the top edge. This used to float on --sp-3 of padding, which
     left a strip the page scrolled through: you could see the hero moving
     above the bar. Nothing sits above it now. */
  padding-block-start: var(--safe-t);
  background: transparent;
  border-block-end: 1px solid transparent;
  transition: background 0.4s var(--ease), border-color 0.4s var(--ease),
              box-shadow 0.4s var(--ease);
}
/* The glass is on the bar, not on a pill inside it, so it spans the full
   width and there is no gutter for the page to show through either. */
.site-header.is-stuck,
.site-header.is-open {
  background: var(--pane-hi);
  border-block-end-color: var(--pane-line);
  -webkit-backdrop-filter: var(--blur);
          backdrop-filter: var(--blur);
  box-shadow: var(--lift);
}
.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  /* Logical padding, so RTL mirrors it without an override */
  padding-block: var(--sp-2);
}


/* Brand lockup: the real cube mark + the real wordmark. The mark sits in a
   small tinted tile so the monochrome artwork picks up the palette.
   Both sizes are clamped against the viewport rather than stepped at a
   breakpoint, which is what keeps the lockup, the language switch and the
   burger inside a 320px header. */
/* The brand may shrink - a 320px header has no room to spare - but never
   below its own contents. It used to carry min-inline-size: 0, which made it
   the one item in the row that could absorb an over-full header: at a 125%
   browser text size on a 1280px screen the nav and the actions grew, the
   brand took the whole shortfall and collapsed to 12px wide with the logo
   spilling out of it, leaving a 12px tap target. min-content holds the
   lockup together and lets the row wrap instead. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 1 auto;
  /* The floor is the cube mark. The wordmark beside it may be trimmed on a
     narrow header - it carries overflow: hidden for exactly that - but the
     mark itself is never squeezed, so the link is always a real target. */
  min-inline-size: clamp(32px, 9vw, 40px);
}
.brand__mark {
  position: relative;
  inline-size: clamp(32px, 9vw, 40px);
  aspect-ratio: 1;
  flex: none;
  display: grid; place-items: center;
  padding: 5px;
  overflow: hidden;
  isolation: isolate;
  border-radius: 13px;
  background: rgba(20, 20, 19, 0.045);
  border: 1px solid var(--pane-line);
  box-shadow: var(--spec);
}
.brand__mark img { inline-size: 100%; }
.site-header .brand {
  gap: clamp(3px, 0.5vw, 6px);
}
.site-header .brand__mark {
  inline-size: var(--brand-mark);
  padding: 0;
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}
.site-header .brand__mark img {
  position: absolute;
  z-index: 2;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  inline-size: 60%;
  block-size: auto;
  transform: translate(-50%, -50%);
}
.brand__orbit {
  position: absolute;
  z-index: 1;
  inset: 0;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  pointer-events: none;
  transition: filter 0.25s var(--ease), opacity 0.25s var(--ease);
}
/* Focus gets the glow on any device - it is how a keyboard user sees where
   they are. Hover is guarded, because on touch a tap fires :hover and it
   sticks: the mark would light up on the first tap and stay lit. */
.site-header .brand:focus-visible .brand__orbit {
  filter: drop-shadow(0 0 3px rgba(20, 20, 19, 0.35));
}
@media (hover: hover) {
  .site-header .brand:hover .brand__orbit {
    filter: drop-shadow(0 0 3px rgba(20, 20, 19, 0.35));
  }
}
[dir="rtl"] .site-header .brand__mark img { transform: translate(50%, -50%); }
.brand__word { inline-size: clamp(72px, 22vw, 124px); flex: 0 1 auto; min-inline-size: 0; overflow: hidden; }
.brand__word img { inline-size: 100%; }
.site-header .brand__word {
  position: relative;
  inline-size: var(--brand-word);
  aspect-ratio: 413 / 152;
  flex: none;
}
.site-header .brand__word img {
  position: absolute;
  inset-block-start: -36.18%;
  inset-inline-start: -42.37%;
  inline-size: 184.02%;
  max-inline-size: none;
}
[dir="rtl"] .site-header .brand__word img {
  inset-inline-start: auto;
  inset-inline-end: -42.37%;
}

/* --- The drawer (phone + tablet portrait) -------------------------------- */
.nav {
  position: fixed;
  inset-block-start: calc(var(--header-h) + var(--safe-t) + var(--sp-2));
  inset-inline: var(--gutter-x);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  margin: 0;
  padding-inline: var(--sp-5);
  padding-block: var(--sp-2);
  border-radius: var(--r-lg);
  background: var(--pane-hi);
  -webkit-backdrop-filter: var(--blur);
          backdrop-filter: var(--blur);
  border: 1px solid var(--pane-line);
  box-shadow: var(--lift), var(--spec);
  transform: translateY(-14px);
  opacity: 0;
  visibility: hidden;
  transition: transform 0.4s var(--ease), opacity 0.3s var(--ease), visibility 0.3s;
}
.site-header.is-open .nav { transform: translateY(0); opacity: 1; visibility: visible; }

.nav__link {
  position: relative;
  display: flex;
  align-items: center;
  min-block-size: var(--tap);
  inline-size: 100%;
  font-size: var(--fs-base);
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-3);
  transition: color 0.25s;
}
.nav__link + .nav__link { border-block-start: 1px solid var(--pane-line); }
.nav__link.is-current { color: var(--text); }

/* The sliding underline is a desktop affordance; it is switched on with
   the row layout in section 13. */
.nav__link::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  inline-size: 0;
  block-size: 2px;
  border-radius: var(--r-pill);
  background: var(--accent);
  transition: inline-size 0.35s var(--ease);
}

.nav__tagline { display: none; }

.site-header__actions {
  display: flex; align-items: center; gap: var(--sp-2);
  margin-inline-start: auto;
  flex: none;
}

/* Language switch ---------------------------------------------------------
   The one place --tap-dense is allowed: three buttons in a padded group,
   so the group itself is a comfortable 44px even though each button is 36.
   The width is --lang-btn, so all three still fit a 280px header.        */
.lang {
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-pill);
  padding: 3px;
  gap: 2px;
}
.lang__btn {
  border: 0;
  background: none;
  cursor: pointer;
  display: inline-grid;
  place-items: center;
  min-inline-size: var(--lang-btn);
  min-block-size: var(--tap-dense);
  padding-inline: var(--sp-1);
  border-radius: var(--r-pill);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--text-3);
  transition: background 0.3s var(--ease), color 0.3s var(--ease), box-shadow 0.3s;
}
@media (hover: hover) { .lang__btn:hover { color: var(--text); } }
.lang__btn.is-active {
  background: var(--accent);
  color: #fff;
  box-shadow: var(--spec);
}

/* Burger ------------------------------------------------------------------- */
.nav-toggle {
  display: grid;
  inline-size: var(--tap); block-size: var(--tap);
  flex: none;
  border-radius: var(--r-pill);
  cursor: pointer;
  padding: 0;
  place-content: center;
  gap: 5px;
}
.nav-toggle span {
  display: block; inline-size: 17px; block-size: 2px; margin-inline: auto;
  border-radius: 2px;
  background: var(--text);
  transition: transform 0.35s var(--ease), opacity 0.2s;
}
.site-header.is-open .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.site-header.is-open .nav-toggle span:nth-child(2) { opacity: 0; }
.site-header.is-open .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* ==========================================================================
   5. HERO
   --------------------------------------------------------------------------
   Phone: the particle stage is a band in normal flow above the copy, so
   the headline is never read through a moving field of dots.
   From 64rem the stage goes absolute and takes the inline-end half, which
   is the layout the artwork was drawn for.

   Heights use svh, not vh. On a phone vh is the height with the URL bar
   hidden, so a 100vh hero is always taller than the screen actually is and
   the first thing below it can never be seen without scrolling twice.
   ========================================================================== */
.hero {
  position: relative;
  display: grid;
  gap: var(--sp-6);
  padding-block: calc(var(--header-h) + var(--safe-t) + var(--sp-6)) var(--sp-8);
  overflow: hidden;
}

/* pointer-events:none keeps the buttons clickable; the cursor (and a
   dragging finger) is tracked at window level by particle-text.js. */
.hero__stage {
  position: relative;
  inline-size: 100%;
  block-size: min(38svh, 17rem);
  z-index: 0;
  pointer-events: none;
}
.hero__content { position: relative; z-index: 2; max-inline-size: 36rem; }

/* The headline is the one place the palette is used as ink */
.hero__title {
  margin-bottom: 0.3em;
  color: var(--text);
  font-size: var(--fs-hero);
}
.hero__lead {
  font-size: var(--fs-lg);
  color: var(--text-2);
  max-inline-size: 31rem;
  margin-bottom: var(--sp-7);
}

/* Stacked full-width buttons on a phone - a thumb should not have to aim.
   They go back to a wrapping row at 30rem. */
.hero__actions { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-8); }

.hero__stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-4);
  padding-block-start: var(--sp-5);
  border-block-start: 1px solid var(--pane-line);
}
.hero__stats li { display: grid; gap: var(--sp-1); align-content: start; min-inline-size: 0; }
.hero__stats strong {
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--text);
}
.hero__stats span {
  font-size: var(--fs-2xs);
  line-height: 1.45;
  color: var(--text-2);   /* 4.5:1 at this size; --text-3 was 2.9:1 */
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* In flow under the stats on a phone; pinned to the corner from 64rem. */
.hero__hint {
  position: relative;
  z-index: 2;
  margin: 0;
  padding-inline: var(--gutter-x);
  font-size: var(--fs-3xs);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-2);   /* --text-3 is 2.9:1 here, under the 4.5:1 small text needs */
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.hero__hint::before {
  content: "";
  inline-size: 6px; block-size: 6px; border-radius: 50%; flex: none;
  background: var(--accent-lift);
  animation: pulse 2.4s ease-in-out infinite;
}
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50%      { transform: scale(2); opacity: 0.25; }
}

/* ==========================================================================
   5b. PAGE HEAD
   --------------------------------------------------------------------------
   The home page opens on the hero. The other three open on this: an
   eyebrow, the page h1, and one line saying what is on the page. It has to
   clear the fixed header itself, because unlike the hero it is not a
   full-height block.
   ========================================================================== */
.page-head {
  padding-block: calc(var(--header-h) + var(--sp-8)) var(--sp-7);
  border-block-end: 1px solid var(--pane-line);
}
.page-head__title {
  font-size: var(--fs-4xl);
  margin-block-end: var(--sp-4);
  max-inline-size: 18ch;
}
.page-head__lead {
  font-size: var(--fs-lg);
  color: var(--text-2);
  max-inline-size: var(--container-narrow);
  margin: 0;
}

/* The home page shows the shape of a section and sends you to the page
   with the rest of it. */
.more { margin: var(--sp-6) 0 0; }
.more__link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  min-block-size: var(--tap-dense);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
  border-block-end: 1px solid var(--accent);
  padding-block-end: var(--sp-1);
  transition: color 0.25s var(--ease), gap 0.25s var(--ease);
}
.more__link::after {
  content: "";
  inline-size: 1.1em; block-size: 1px;
  background: currentColor;
  transition: inline-size 0.25s var(--ease);
}
@media (hover: hover) {
  .more__link:hover { color: var(--accent-lift); gap: var(--sp-4); }
  .more__link:hover::after { inline-size: 1.6em; }
}
[dir="rtl"] .more__link::after { transform: scaleX(-1); }
[dir="rtl"] .svc-tile__go { transform: scaleX(-1); }

/* The page head already carries the section label, so the first section
   under it must not repeat it. */
.page-head + .section .eyebrow { display: none; }

/* On the home page About is a teaser: one paragraph, no fact list. The two
   column split has nothing to balance at that length, so it stays single
   column there even on a wide screen. */
body[data-page="index.html"] .about { grid-template-columns: minmax(0, 1fr); }
body[data-page="index.html"] .about__head { position: static; }

/* ==========================================================================
   6. SECTIONS
   --------------------------------------------------------------------------
   Every grid here is auto-fit with a minmax(min(100%, Xrem), 1fr) track.
   That pattern reflows on its own - one column on a phone, two on a tablet,
   three on a desktop - with no breakpoint to maintain, and the min(100%)
   guard is what stops a 17rem track overflowing a 320px screen. Use it for
   any new grid rather than stepping column counts by hand.
   ========================================================================== */

/* --- About --------------------------------------------------------------- */
.about {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
  align-items: start;
}
.about__body > p + p { margin-top: var(--sp-5); }

.about__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 9.5rem), 1fr));
  column-gap: var(--sp-6);
  margin: var(--sp-7) 0 0;
  border-block-start: 1px solid var(--pane-line);
}
.about__facts > div {
  padding-block: var(--sp-4);
  border-block-end: 1px solid var(--pane-line);
}
.about__facts dt {
  font-size: var(--fs-2xs);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
  margin-bottom: var(--sp-2);
}
.about__facts dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.02em;
}

/* --- The founder, credited ----------------------------------------------
   His portrait beside a byline: the name, the role, three sentences on who
   he is, then three ruled facts. Every fact is one the site already states
   elsewhere. Stacked on a phone; from sm the photo takes its own column. */
.credit {
  display: grid;
  gap: var(--sp-5);
  margin-block-start: var(--sp-6);
  padding-block-start: var(--sp-5);
  border-block-start: 1px solid var(--pane-line);
  max-inline-size: 100%;
}
/* From sm the photo floats at the start and the words run beside it, then
   on under it at the full width. As two grid columns, on a phone held
   sideways or a tablet the bio was a long narrow strip beside the photo
   with an empty column under the picture. From lg the text column is wide
   enough to sit beside the photo for its whole length, so it is a grid
   again. clear keeps the facts in one full-width row below both. */
@media (min-width: 30rem) {
  .credit { display: flow-root; }
  .credit__photo {
    float: inline-start;
    inline-size: var(--portrait-w);
    margin-inline-end: var(--sp-6);
    margin-block-end: var(--sp-4);
  }
  .credit__facts { clear: both; }
}
@media (min-width: 64rem) {
  .credit {
    display: grid;
    grid-template-columns: var(--portrait-w) minmax(0, 1fr);
    align-items: start;
    column-gap: var(--sp-6);
  }
  .credit__photo { float: none; margin: 0; }
  .credit__facts { clear: none; }
}
.credit__photo {
  position: relative;
  margin: 0;
  inline-size: 100%;
  max-inline-size: var(--portrait-w);
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 50%;
  border: 1px solid var(--pane-line);
  background: var(--deep);
  box-shadow: var(--lift);
}
/* The white rim the panes have, drawn over the photo so it sits in the
   same material rather than looking pasted on. */
.credit__photo::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.35);
  pointer-events: none;
}
.credit__photo img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform 0.9s var(--ease);
}
@media (hover: hover) {
  .credit:hover .credit__photo img { transform: scale(1.04); }
}
.credit__text { min-inline-size: 0; }
.credit__name {
  margin: 0 0 var(--sp-1);
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
}
.credit__role {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 1.45;
  color: var(--text-2);
}
.credit__bio {
  margin: var(--sp-4) 0 0;
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--text-2);
  max-inline-size: 58em;
}
.credit__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
  column-gap: var(--sp-5);
  margin: var(--sp-5) 0 0;
  border-block-start: 1px solid var(--pane-line);
}
.credit__facts > div {
  padding-block: var(--sp-3);
  border-block-end: 1px solid var(--pane-line);
}
.credit__facts dt {
  margin-block-end: var(--sp-1);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
}
.credit__facts dd {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* --- Our concern: six glass tiles ----------------------------------------
   Each tile carries its own accent, set once by :nth-child and then used by
   the number, the top edge and the hover bloom. That is what keeps the
   grid colourful without painting six different backgrounds.             */
/* Six short lines of copy do not need six boxes. Ruled rows in as many
   columns as fit, but never more than three: on a wide screen four or five
   would fit, and six blocks would break into an uneven 4 + 2. The max()
   keeps each track at least a third of the row. */
.wall {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, max(20rem, (100% - 2 * var(--sp-7)) / 3)), 1fr));
  column-gap: var(--sp-7);
  border-block-start: 1px solid var(--pane-line);
}
.brick {
  position: relative;
  display: grid;
  gap: var(--sp-2);
  padding-block: var(--sp-5);
  border-block-end: 1px solid var(--pane-line);
}

@media (hover: hover) {
  .brick:hover .brick__num { color: var(--accent-lift); }
}

.brick__num {
  position: relative;
  font-family: var(--font-display);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.24em;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.brick p {
  position: relative;
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: -0.025em;
  color: var(--text);
  max-inline-size: 15em;
}

/* --- Our solutions: three glass cards ------------------------------------ */
/* Three services as three cards is the layout a generator reaches for. As
   rows it reads like a printed services page: the name and what it covers
   on the left, everything included on the right, a hairline between. */
.services {
  display: grid;
  gap: 0;
  border-block-start: 1px solid var(--pane-line);
}
.service {
  position: relative;
  display: grid;
  align-content: start;
  gap: var(--sp-4);
  padding-block: var(--sp-6);
  border-block-end: 1px solid var(--pane-line);
  transition: background 0.3s var(--ease);
}
@media (min-width: 64rem) {
  .service {
    grid-template-columns: 3.5rem minmax(0, 0.9fr) minmax(0, 1.1fr);
    column-gap: var(--sp-7);
    row-gap: var(--sp-4);
    padding-inline: var(--sp-4);
    margin-inline: calc(var(--sp-4) * -1);
  }
}

@media (hover: hover) {
  .service:hover { background: rgba(20, 20, 19, 0.028); }
}

/* The index numerals. They were 42px tinted tiles with a rounded corner and
   an inner highlight, which is a lot of chrome to say "this is item two".
   Set as small tracked figures above a hairline instead: the number is
   still findable, and it stops competing with the heading under it. */
.service__index {
  position: relative;
  justify-self: start;
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.24em;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  padding-block-start: 0.4em;
}
.service__head { position: relative; }
.service__title { margin: 0 0 var(--sp-2); font-size: var(--fs-2xl); }
.service__desc { color: var(--text-2); margin: 0; font-size: var(--fs-md); }
/* Who is licensed to give a service, where the law requires a licence. */
.service__note { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--text-3); }

.service__list {
  position: relative;
  display: grid;
  gap: var(--sp-2);
}
@media (min-width: 30rem) {
  .service__list {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    column-gap: var(--sp-5);
  }
}
.service__list li {
  position: relative;
  padding-inline-start: var(--sp-5);
  font-size: var(--fs-md);
  color: var(--text-2);
}
/* A short rule, not a glowing dot. There are two dozen of these on the
   page; at that count the accent stops being an accent. */
.service__list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.72em;
  inline-size: 10px; block-size: 1px;
  background: var(--text-3);
}

/* --- Home services rail ---------------------------------------------------
   The three services as one ruled band: a hairline above and below, a
   hairline between, and in each column an icon and a name. Not cards.
   Three glass boxes with most of their area empty read as unfinished, and
   the rest of the page separates with rules, not boxes.
   With a pointer, from lg, the column under it widens and its line of
   copy fades in; left alone, the first column stays open, so the
   behaviour shows before anyone has to discover it. On a phone, a tablet
   or any touch screen every column is simply open: nothing hides behind
   a hover a finger cannot make.                                           */
.svc-rail {
  display: flex;
  flex-direction: column;
  border-block: 1px solid var(--pane-line);
}

/* Phone: one row per service - icon, words, arrow. Each row takes its own
   content height: a zero flex-basis here collapses the rows to nothing. */
.svc-tile {
  position: relative;
  flex: none;
  min-inline-size: 0;
  min-block-size: var(--tap);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas: "icon body go";
  align-items: start;
  /* Top-aligned rows. A column shorter than its neighbours would otherwise
     share the spare height out between its rows and drop its name lower. */
  align-content: start;
  column-gap: var(--sp-4);
  padding-block: var(--sp-5);
  color: var(--text);
  transition: flex-grow 0.6s var(--ease), background 0.3s var(--ease);
}
.svc-tile + .svc-tile { border-block-start: 1px solid var(--pane-line); }
.svc-tile:active { background: rgba(20, 20, 19, 0.028); }

/* Each tile names its colour once; the icon and the open rule read --svc. */
.svc-tile--accounting { --svc: var(--svc-accounting); }
.svc-tile--taxation   { --svc: var(--svc-taxation); }
.svc-tile--finance    { --svc: var(--svc-finance); }

/* Shut: the glyph in its colour on a tint of it. Open: the solid colour,
   lit from the top like the glass, with the glyph in white. */
.svc-tile__icon {
  grid-area: icon;
  display: grid;
  place-items: center;
  inline-size: var(--icon-box);
  block-size: var(--icon-box);
  border: 1px solid color-mix(in srgb, var(--svc, var(--text)) 28%, transparent);
  border-radius: var(--r-xl);
  background: color-mix(in srgb, var(--svc, var(--text)) 12%, transparent);
  color: var(--svc, var(--text));
  transition: background 0.35s var(--ease), border-color 0.35s var(--ease),
              color 0.35s var(--ease), box-shadow 0.35s var(--ease);
}
.svc-tile__icon svg { inline-size: 54%; block-size: 54%; }
.svc-tile:active .svc-tile__icon {
  background: linear-gradient(158deg, color-mix(in srgb, var(--svc, var(--accent)) 82%, #FFFFFF), var(--svc, var(--accent)));
  border-color: var(--svc, var(--accent));
  color: var(--accent-ink);
  box-shadow: 0 14px 30px -14px color-mix(in srgb, var(--svc, var(--accent)) 75%, transparent);
}

/* The arrow says the column is a link to the full service. */
.svc-tile__go {
  grid-area: go;
  inline-size: 1.25em;
  block-size: 1.25em;
  margin-block-start: calc((var(--icon-box) - 1.25em) / 2);   /* centred on the icon box */
  color: var(--text-3);
  transition: color 0.3s var(--ease);
}

.svc-tile__body {
  grid-area: body;
  min-inline-size: 0;
  /* the name sits level with the middle of the icon */
  padding-block-start: calc((var(--icon-box) - 1.2 * var(--fs-xl)) / 2);
}
.svc-tile__title {
  margin: 0;
  font-size: var(--fs-xl);
  line-height: 1.2;
  letter-spacing: -0.02em;
}
.svc-tile__desc {
  margin: var(--sp-2) 0 0;
  font-size: var(--fs-md);
  line-height: 1.55;
  color: var(--text-2);
}

/* md: three columns. The icon and the arrow share the top line and the
   words run underneath. The negative margin lets the first and last
   columns' padding hang into the gutter, so their text still lines up
   with the heading above. */
@media (min-width: 48rem) {
  .svc-rail {
    flex-direction: row;
    margin-inline: calc(var(--sp-5) * -1);
  }
  .svc-rail > .svc-tile { flex: 1 1 0; }
  .svc-tile {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "icon go" "body body";
    row-gap: var(--sp-5);
    padding: var(--sp-6) var(--sp-5);
  }
  .svc-tile + .svc-tile {
    border-block-start: 0;
    border-inline-start: 1px solid var(--pane-line);
  }
  .svc-tile__go { align-self: center; margin-block-start: 0; }
  .svc-tile__body { padding-block-start: 0; }
}

@media (min-width: 64rem) and (hover: hover) {
  /* Both measures are fixed to the width they will have in a shut column
     (the name) or an open one (the copy), worked out from the rail's own
     width and --tile-grow. The words therefore wrap once and never reflow
     while the columns resize, every column has the same height in every
     state, and nothing below the rail can jump. The six paddings come off
     first; the --sp-1 of slack covers the two hairlines. */
  .svc-rail { container-type: inline-size; }
  .svc-tile { overflow: hidden; }
  .svc-tile:focus-visible { outline-offset: calc(var(--sp-1) * -1); }
  .svc-tile__title {
    inline-size: calc((100cqi - 6 * var(--sp-5)) / (var(--tile-grow) + 2) - var(--sp-1));
  }
  .svc-tile__desc {
    inline-size: calc((100cqi - 6 * var(--sp-5)) * var(--tile-grow) / (var(--tile-grow) + 2) - var(--sp-1));
    opacity: 0;
    transform: translateY(var(--sp-2));
    transition: opacity 0.2s var(--ease), transform 0.2s var(--ease);
  }
  /* The rule along the top of the open column, as under the current nav link. */
  .svc-tile::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    inline-size: 0;
    block-size: 2px;
    background: var(--svc, var(--accent));
    transition: inline-size 0.5s var(--ease);
  }

  /* Open: the column under the pointer, the one with keyboard focus, or
     the first while the rail is left alone. */
  .svc-tile:is(:hover, :focus-visible),
  .svc-rail:not(:hover):not(:has(:focus-visible)) > .svc-tile:first-child {
    flex-grow: var(--tile-grow);
    background: rgba(20, 20, 19, 0.028);
  }
  .svc-tile:is(:hover, :focus-visible)::before,
  .svc-rail:not(:hover):not(:has(:focus-visible)) > .svc-tile:first-child::before {
    inline-size: 100%;
  }
  .svc-tile:is(:hover, :focus-visible) .svc-tile__desc,
  .svc-rail:not(:hover):not(:has(:focus-visible)) > .svc-tile:first-child .svc-tile__desc {
    opacity: 1;
    transform: none;
    transition: opacity 0.45s var(--ease) 0.15s, transform 0.45s var(--ease) 0.15s;
  }
  .svc-tile:is(:hover, :focus-visible) .svc-tile__icon,
  .svc-rail:not(:hover):not(:has(:focus-visible)) > .svc-tile:first-child .svc-tile__icon {
    background: linear-gradient(158deg, color-mix(in srgb, var(--svc, var(--accent)) 82%, #FFFFFF), var(--svc, var(--accent)));
    border-color: var(--svc, var(--accent));
    color: var(--accent-ink);
    box-shadow: 0 14px 30px -14px color-mix(in srgb, var(--svc, var(--accent)) 75%, transparent);
  }
  .svc-tile:is(:hover, :focus-visible) .svc-tile__go,
  .svc-rail:not(:hover):not(:has(:focus-visible)) > .svc-tile:first-child .svc-tile__go {
    color: var(--text);
  }
}

/* --- How it works -------------------------------------------------------- */
.process-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: var(--gap-grid);
}
.process-card {
  position: relative;
  padding-block: var(--sp-5);
  border-block-start: 1px solid var(--pane-line);
}
.process-card__num {
  display: block;
  margin-block-end: var(--sp-3);
  color: var(--text-3);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.24em;
  font-variant-numeric: tabular-nums;
}
.process-card h3 { margin-block-end: var(--sp-2); }
.process-card p { margin: 0; font-size: var(--fs-md); color: var(--text-2); }

/* --- Key values ----------------------------------------------------------- */
.value-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16.25rem), 1fr));
  column-gap: var(--sp-7);
}
.value {
  position: relative;
  padding-block: var(--sp-5);
  border-block-start: 1px solid var(--pane-line);
}
@media (hover: hover) {
  .value:hover .value__num { color: var(--accent-lift); }
}

.value__num {
  position: relative;
  display: block;
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.24em;
  color: var(--text-3);
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--sp-3);
  transition: color 0.3s var(--ease);
}
.value h3 { position: relative; margin-bottom: var(--sp-2); }
.value p { position: relative; color: var(--text-2); font-size: var(--fs-md); margin: 0; }

/* --- Founder ------------------------------------------------------------- */
.founder-card {
  position: relative;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
  align-items: center;
  padding: var(--sp-7) var(--sp-6);
  border-radius: var(--r-xl);
  overflow: hidden;
}
.founder-card::before {
  content: "\201C";
  position: absolute;
  inset-block-start: -0.25em;
  inset-inline-end: 0.08em;
  font-family: Georgia, serif;
  font-size: var(--fs-ornament);
  line-height: 1;
  color: rgba(20, 20, 19, 0.07);
  pointer-events: none;
}
.founder-card__intro,
.founder-card__quote { position: relative; }
.founder-card__quote {
  margin: 0;
  padding-inline-start: var(--sp-5);
  border-inline-start: 2px solid var(--accent);
}
.founder-card__quote > p {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  line-height: 1.5;
  letter-spacing: -0.02em;
  color: var(--text);
}
.founder-card__quote footer { display: grid; gap: var(--sp-1); margin-block-start: var(--sp-5); }
.founder-card__quote strong { color: var(--text); font-family: var(--font-display); }
.founder-card__quote span { color: var(--text-3); font-size: var(--fs-sm); }

/* --- FAQ ----------------------------------------------------------------- */
.faq-layout {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-7);
  align-items: start;
}
.faq-layout .section__head { margin: 0; }
.faq-list {
  display: grid;
  gap: 0;
  border-block-start: 1px solid var(--pane-line);
}
.faq-list details {
  padding: 0;
  border-block-end: 1px solid var(--pane-line);
}
.faq-list summary {
  position: relative;
  padding-block: var(--sp-5);
  padding-inline: var(--sp-5) 3.5rem;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.35;
  cursor: pointer;
  list-style: none;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary::after {
  content: "+";
  position: absolute;
  inset-inline-end: var(--sp-5);
  inset-block-start: 50%;
  transform: translateY(-50%);
  color: var(--accent-lift);
  font-size: var(--fs-glyph);
  font-weight: 400;
}
.faq-list details[open] summary::after { content: "\2212"; }
.faq-list details[open] summary { border-block-end: 1px solid var(--pane-line); }
.faq-list details p { padding: var(--sp-4) var(--sp-5) var(--sp-5); margin: 0; font-size: var(--fs-md); }

/* --- Client experience ---------------------------------------------------
   Each company uses its own high-resolution source. The company name remains
   in the link's accessible label, while the moving rail shows only logos. */
.clients { overflow: hidden; }
.clients__label {
  margin: 0 0 var(--sp-4);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.client-logo {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0;
  flex: 0 0 13rem;
  inline-size: 13rem;
  block-size: 8.5rem;
  margin: 0;
  padding: 0.8rem 1rem;
  border: 0;
  border-radius: 0.85rem;
  background: rgba(255, 255, 255, 0.46);
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.2s var(--ease), box-shadow 0.2s var(--ease),
              filter 0.2s var(--ease), transform 0.2s var(--ease);
}
/* Logo cards are deliberately presentational: none opens an external site. */
.client-logo--plain { cursor: default; }
.client-logo__image {
  display: block;
  flex: 0 1 auto;
  inline-size: 100%;
  min-block-size: 0;
  block-size: 100%;
  max-block-size: 6.5rem;
  object-fit: contain;
}
.client-logo__pair {
  display: flex;
  flex: 0 1 auto;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  inline-size: 100%;
  min-block-size: 0;
  block-size: 100%;
  max-block-size: 6.5rem;
}
.client-logo__pair .client-logo__image:first-child {
  flex: 0 0 30%;
  inline-size: 30%;
}
.client-logo__pair .client-logo__image:last-child {
  flex: 1 1 auto;
  inline-size: 62%;
}
.client-logo__name {
  display: none;
}
.client-logo:focus-visible {
  filter: saturate(1.08) contrast(1.03);
  background: rgba(255, 255, 255, 0.9);
  box-shadow: 0 0.55rem 1.25rem rgba(20, 20, 19, 0.09);
  transform: translateY(-0.12rem);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
@media (hover: hover) {
  a.client-logo:hover {
    filter: saturate(1.08) contrast(1.03);
    background: rgba(255, 255, 255, 0.9);
    box-shadow: 0 0.55rem 1.25rem rgba(20, 20, 19, 0.09);
    transform: translateY(-0.12rem);
  }
}
.clients__moving-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: baseline;
  gap: var(--sp-2) var(--sp-5);
  margin-block-end: var(--sp-4);
}
.clients__moving-head .clients__label { margin: 0; }
/* The permission line under the client heading: small print, not a claim. */
.clients__permission { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--text-3); }

.clients__hint {
  margin: 0;
  min-inline-size: 0;
  max-inline-size: 100%;
  color: var(--text-3);
  font-size: var(--fs-2xs);
  line-height: 1.5;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.client-marquee {
  position: relative;
  isolation: isolate;
  inline-size: 100%;
  margin-inline: auto;
  overflow: hidden;
  padding: 0.65rem;
  border: 1px solid rgba(20, 20, 19, 0.09);
  border-radius: 1.15rem;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(255, 255, 255, 0.58));
  box-shadow:
    0 1rem 2.5rem rgba(20, 20, 19, 0.055),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
  direction: ltr;
}
.client-marquee::before,
.client-marquee::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset-block: 0;
  inline-size: clamp(1.5rem, 4vw, 4rem);
  pointer-events: none;
}
.client-marquee::before {
  inset-inline-start: 0;
  background: linear-gradient(90deg, rgba(247, 247, 245, 0.98), transparent);
}
.client-marquee::after {
  inset-inline-end: 0;
  background: linear-gradient(270deg, rgba(247, 247, 245, 0.98), transparent);
}
.client-marquee:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 0.2rem;
}
.client-marquee__track {
  display: flex;
  inline-size: max-content;
  will-change: transform;
  transform: translate3d(-50%, 0, 0);
  backface-visibility: hidden;
  contain: layout paint style;
  background: transparent;
}
.client-marquee__group {
  display: flex;
  flex: none;
  gap: clamp(0.65rem, 1.3vw, 1.25rem);
  padding-inline-end: clamp(0.65rem, 1.3vw, 1.25rem);
}
.client-marquee.is-ready .client-marquee__track {
  animation: clients-left-to-right 62s linear infinite;
}
/* The strip must keep moving after a logo is clicked. A link retains focus
   when its new tab opens, and pausing on :focus-within left the old marquee
   frozen indefinitely. Reduced-motion users still get the static layout in
   the dedicated media query below. */

@media (hover: none) {
  .client-marquee .client-logo {
    block-size: 8.5rem;
  }
}

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

/* --- Faith and values ---------------------------------------------------
   A quiet da'wah moment that connects the founder's faith to the practical
   meaning of amanah in professional work. The photograph remains the focus;
   the copy is intentionally concise and welcoming. */
.faith {
  overflow: hidden;
  background:
    radial-gradient(circle at 12% 20%, rgba(20, 20, 19, 0.045), transparent 32%),
    rgba(20, 20, 19, 0.018);
}
.faith__card {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  overflow: hidden;
  border-radius: var(--r-xl);
}
.faith__media {
  position: relative;
  min-inline-size: 0;
  aspect-ratio: 16 / 9;
  margin: 0;
  overflow: hidden;
  background: var(--void);
}
.faith__media img {
  display: block;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: center;
}

/* --- CTA band ------------------------------------------------------------- */
.cta-band {
  position: relative;
  padding-block: var(--sp-9);
  overflow: hidden;
  isolation: isolate;
}
.cta-band::before {
  content: "";
  position: absolute;
  inset-block: -30% auto;
  inset-inline-start: 50%;
  inline-size: min(1100px, 120%);
  aspect-ratio: 2 / 1;
  transform: translateX(-50%);
  z-index: -1;
  background: radial-gradient(ellipse at center, rgba(20, 20, 19, 0.06), transparent 68%);
  filter: blur(40px);
}
.cta-band__inner {
  max-inline-size: 76rem;
  text-align: center;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: center;
  gap: var(--sp-7);
  padding-block: var(--sp-7);
  padding-inline: var(--sp-6);
  border-radius: var(--r-xl);
  background: var(--pane);
  border: 1px solid var(--pane-line);
  -webkit-backdrop-filter: var(--blur);
          backdrop-filter: var(--blur);
  box-shadow: var(--lift), var(--spec);
}
.cta-band__copy {
  display: grid;
  justify-items: center;
  min-inline-size: 0;
}
.cta-band .eyebrow { justify-content: center; }
.cta-band .eyebrow::before {
  background: var(--accent);
}
.cta-band__title {
  max-inline-size: 48rem;
  margin-bottom: var(--sp-3);
  font-size: var(--fs-4xl);
  letter-spacing: -0.025em;
  line-height: 1.02;
  text-transform: uppercase;
  color: var(--text);
}
.cta-band__sub {
  max-inline-size: 36rem;
  margin-bottom: var(--sp-6);
  color: var(--text-2);
  font-size: var(--fs-lg);
}
.cta-band .btn { inline-size: min(100%, 20rem); }
.cta-band__button-icon {
  font-size: 1.15em;
  line-height: 1;
  transition: transform 0.3s var(--ease);
}
@media (hover: hover) {
  .cta-band__button:hover .cta-band__button-icon { transform: translate(2px, -2px); }
}

.cta-band__visual {
  position: relative;
  inline-size: min(13.5rem, 62vw);
  aspect-ratio: 4 / 5;
  margin-inline: auto;
  filter: drop-shadow(0 20px 26px rgba(20, 20, 19, 0.16));
  pointer-events: none;
  -webkit-user-select: none;
          user-select: none;
  -webkit-touch-callout: none;
}
.cta-band__visual *,
.cta-band__visual *::before,
.cta-band__visual *::after {
  pointer-events: none;
  -webkit-user-select: none;
          user-select: none;
}
.cta-band__visual::selection,
.cta-band__visual *::selection {
  background: transparent;
  color: inherit;
}
.cta-band__visual::before {
  content: "";
  position: absolute;
  inset: 7% -7% -5% 9%;
  border: 1px solid rgba(20, 20, 19, 0.14);
  border-radius: var(--r-md);
  background: rgba(255, 255, 255, 0.55);
  transform: rotate(5deg);
}
.cta-band__sheet {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: clamp(1.2rem, 4vw, 2rem);
  overflow: hidden;
  border: 1px solid rgba(20, 20, 19, 0.18);
  border-radius: var(--r-md);
  background: linear-gradient(145deg, rgba(255, 255, 255, 0.98), rgba(239, 238, 234, 0.96));
  color: var(--text);
  text-align: start;
  transform: rotate(-2deg);
}
.cta-band__sheet::after {
  content: "";
  position: absolute;
  inline-size: 9rem;
  block-size: 9rem;
  inset-inline-end: -4.5rem;
  inset-block-end: -4.5rem;
  border: 1.5rem solid rgba(20, 20, 19, 0.06);
  border-radius: 50%;
}
.cta-band__sheet-brand {
  font-size: var(--fs-xs);
  font-weight: 800;
  letter-spacing: 0.22em;
}
.cta-band__sheet-kicker {
  margin-block-start: auto;
  margin-block-end: var(--sp-2);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  color: var(--text-3);
}
.cta-band__sheet-title {
  font-size: clamp(1.15rem, 4vw, 1.65rem);
  font-weight: 750;
  line-height: 0.95;
  letter-spacing: -0.045em;
}
.cta-band__sheet-line {
  inline-size: 78%;
  block-size: 2px;
  margin-block-start: var(--sp-4);
  background: rgba(20, 20, 19, 0.22);
}
.cta-band__sheet-line--short {
  inline-size: 48%;
  margin-block-start: var(--sp-2);
}
.cta-band__sheet strong {
  margin-block-start: var(--sp-5);
  padding: 0.35rem 0.5rem;
  border-radius: 0.25rem;
  background: var(--text);
  color: #fff;
  font-size: 0.62rem;
  letter-spacing: 0.13em;
}

@media (min-width: 48rem) {
  .cta-band__inner {
    grid-template-columns: minmax(0, 1fr) minmax(12rem, 0.38fr);
    gap: var(--sp-7);
    padding-block: var(--sp-8);
    padding-inline: var(--sp-8);
    text-align: start;
  }
  .cta-band__copy { justify-items: start; }
  .cta-band .eyebrow { justify-content: flex-start; }
  .cta-band__visual {
    inline-size: min(14rem, 100%);
    margin-inline: 0;
    justify-self: end;
  }

  /* An open tab can briefly retain the older flat CTA markup while the
     versioned stylesheet refreshes. Keep that legacy structure in one clean
     column instead of letting its four children populate the two-column grid. */
  .cta-band__inner:has(> .eyebrow) {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
  }
  .cta-band__inner:has(> .eyebrow) > .eyebrow { justify-content: center; }
  .cta-band__inner:has(> .eyebrow) > .cta-band__sub { margin-bottom: 0; }
  .cta-band__inner:has(> .eyebrow) > .btn {
    inline-size: min(100%, 20rem);
    justify-self: center;
  }
}

/* --- Contact -------------------------------------------------------------- */
.contact {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: var(--sp-8);
}
.contact__intro { max-inline-size: 44rem; }
.contact__actions { display: grid; gap: var(--sp-3); margin-top: var(--sp-6); }

.contact-card {
  padding: var(--sp-6);
  border-radius: var(--r-xl);
}
.contact-card__list { margin: 0; display: grid; }
.contact-card__list > div { padding-block: var(--sp-4); }
.contact-card__list > div:first-child { padding-block-start: 0; }
.contact-card__list > div + div { border-block-start: 1px solid var(--pane-line); }
.contact-card__list dt {
  margin-block-end: var(--sp-1);
  color: var(--text-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.contact-card__list dd { margin: 0; color: var(--text); line-height: 1.55; }
/* The WhatsApp number and the email address are the two things a visitor
   actually taps in this card. A bare inline link is a 20px line box, so
   they get padded up to --tap-dense without disturbing the row rhythm. */
.contact-card__list a {
  color: var(--text);
  overflow-wrap: anywhere;
  display: inline-block;
  min-block-size: var(--tap-dense);
  padding-block: var(--sp-2);
}
@media (hover: hover) {
  .contact-card__list a:hover { text-decoration: underline; text-underline-offset: 3px; }
}
/* A second email address, on the contact card and the privacy page, for a
   language that has one: the Arabic pages list Ahmed and Monzer. Where the
   dictionary leaves contact.emailAddress2 empty, the row is simply not
   there, rather than an empty line holding a link to nowhere. */
.email-also:has(> a:empty),
.privacy__email:empty { display: none; }
.contact-card__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--pane-line);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-3);
}
.contact-card__links a { min-block-size: var(--tap-dense); display: inline-flex; align-items: center; transition: color 0.2s; }
@media (hover: hover) { .contact-card__links a:hover { color: var(--accent-lift); } }

/* --- Social and map icons ------------------------------------------------
   Matched on the href rather than on a class, so the markup stays as it is
   and a new link picks its own icon up by being pointed at the right place.

   Each one is a CSS mask filled with currentColor, the same technique as
   .btn--whatsapp above: one monochrome glyph that inherits the link colour,
   so it goes muted with the row and warms to the accent on hover. Brand
   colours would put five more hues on a page whose whole point is that it
   has one.                                                                */
.contact-card__links a { gap: var(--sp-2); }

.contact-card__links a::before,
.site-footer__social a::before {
  content: "";
  inline-size: 1.05em;
  block-size: 1.05em;
  flex: none;
  background: currentColor;
  transition: background 0.2s;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}

a[href*="maps.app.goo.gl"],
a[href*="google.com/maps"] {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M12 2a7.5 7.5 0 0 0-7.5 7.5c0 5.4 6.7 12 7 12.3a.7.7 0 0 0 1 0c.3-.3 7-6.9 7-12.3A7.5 7.5 0 0 0 12 2zm0 10.2a2.7 2.7 0 1 1 0-5.4 2.7 2.7 0 0 1 0 5.4z'/%3E%3C/svg%3E");
}

a[href*="instagram.com"] {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M7 2h10a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H7a5 5 0 0 1-5-5V7a5 5 0 0 1 5-5zm0 2a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V7a3 3 0 0 0-3-3H7zm5 3.4a4.6 4.6 0 1 1 0 9.2 4.6 4.6 0 0 1 0-9.2zm0 2a2.6 2.6 0 1 0 0 5.2 2.6 2.6 0 0 0 0-5.2zm5.9-2.6a1.2 1.2 0 1 1 0 2.4 1.2 1.2 0 0 1 0-2.4z'/%3E%3C/svg%3E");
}

a[href*="youtube.com"] {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill-rule='evenodd' d='M22.5 6.7a3 3 0 0 0-2.1-2.1C18.5 4 12 4 12 4s-6.5 0-8.4.6A3 3 0 0 0 1.5 6.7C1 8.6 1 12 1 12s0 3.4.5 5.3a3 3 0 0 0 2.1 2.1c1.9.6 8.4.6 8.4.6s6.5 0 8.4-.6a3 3 0 0 0 2.1-2.1c.5-1.9.5-5.3.5-5.3s0-3.4-.5-5.3zM9.8 15.4V8.6l5.9 3.4-5.9 3.4z'/%3E%3C/svg%3E");
}

a[href*="tiktok.com"] {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.7 8.9a6.9 6.9 0 0 1-4.3-1.5v6.9a5.9 5.9 0 1 1-5.1-5.8v3a2.9 2.9 0 1 0 2 2.8V2h3.1a4.2 4.2 0 0 0 4.3 4.1v2.8z'/%3E%3C/svg%3E");
}

/* The bare "in", not the brand lockup. LinkedIn's own asset wraps the
   letterform in a filled rounded square, which makes it the only glyph in
   this row that brings its own container - next to an open map pin and an
   open camera outline it reads as a button someone forgot to remove.
   Dropping the enclosing subpath leaves the mark LinkedIn itself uses
   wherever the square would compete. */
a[href*="linkedin.com"] {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 0 1-2.063-2.065 2.064 2.064 0 1 1 2.063 2.065zm1.782 13.019H3.555V9h3.564v11.452z'/%3E%3C/svg%3E");
}

a[href*="wa.me"],
a[href*="whatsapp"],
#footer-whatsapp {
  --icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12.04 2c-5.46 0-9.9 4.44-9.9 9.9 0 1.75.46 3.45 1.32 4.95L2 22l5.3-1.38a9.9 9.9 0 0 0 4.74 1.2c5.46 0 9.9-4.43 9.9-9.9S17.5 2 12.04 2zm5.8 14.02c-.24.68-1.42 1.3-1.95 1.35-.5.05-.97.24-3.27-.68-2.75-1.08-4.5-3.87-4.64-4.05-.13-.18-1.1-1.47-1.1-2.8 0-1.32.7-1.97.94-2.24.25-.27.54-.34.72-.34l.52.01c.17 0 .4-.06.62.48.24.56.8 1.94.87 2.08.07.14.12.3.02.48-.1.18-.15.29-.29.45-.14.16-.3.36-.43.48-.14.14-.29.29-.12.57.17.27.75 1.24 1.62 2.01 1.11.99 2.05 1.3 2.33 1.44.28.14.45.12.61-.07.17-.2.71-.83.9-1.11.19-.28.37-.23.63-.14.25.09 1.63.77 1.9.91.29.14.47.2.54.32.07.11.07.65-.17 1.33z'/%3E%3C/svg%3E");
}

/* A link that matched none of the above has no --icon, so its box would be
   a filled square. Fall back to a small dot instead of a block. */
.contact-card__links a:not([href*="maps.app.goo.gl"]):not([href*="google.com/maps"]):not([href*="instagram.com"]):not([href*="youtube.com"]):not([href*="tiktok.com"]):not([href*="linkedin.com"]):not([href*="wa.me"]):not([href*="whatsapp"])::before {
  -webkit-mask: none;
          mask: none;
  inline-size: 4px;
  block-size: 4px;
  border-radius: 50%;
  opacity: 0.55;
}



/* --- Brand colour on hover -----------------------------------------------
   The row sits muted until you reach for it, then each link answers in its
   own colour. The icon comes along for free: it is a mask filled with
   currentColor, so setting `color` paints the glyph and the label together.

   These are the real brand hexes except where one is illegible on a
   #121111 background, which is a judgement the brand guidelines do not
   make for you:

     LinkedIn  #0A66C2 -> #3B91DF   the true blue lands at ~3.2:1 here,
                                    under the 4.5:1 floor for text
     TikTok    #000000 -> #D9DEE3   "metallic black" is invisible on black,
                                    so it reads as brushed metal instead,
                                    which is the same intent inverted

   Scoped to the two rows that use them, NOT to a[href] on its own. The
   WhatsApp call to action also points at wa.me, and an unscoped
   a[href*="wa.me"]:hover outweighs .btn--whatsapp, which would paint the
   button's label the same green as its background.

   Everything inside @media (hover: hover): a :hover fired by a tap sticks
   to the element until something else is tapped.                          */
@media (hover: hover) {
  .contact-card__links a[href*="youtube.com"]:hover,
  .site-footer__social a[href*="youtube.com"]:hover   { color: #FF0000; }
  .contact-card__links a[href*="instagram.com"]:hover,
  .site-footer__social a[href*="instagram.com"]:hover { color: #E1306C; }
  .contact-card__links a[href*="tiktok.com"]:hover,
  .site-footer__social a[href*="tiktok.com"]:hover    { color: #161616; }
  .contact-card__links a[href*="linkedin.com"]:hover,
  .site-footer__social a[href*="linkedin.com"]:hover  { color: #0A66C2; }
  .contact-card__links a[href*="wa.me"]:hover,
  .site-footer__social a[href*="wa.me"]:hover         { color: #25D366; }

  /* Maps is four colours, and `color` only holds one. The label takes
     Google blue; the pin takes all four, because a mask will accept any
     background and a gradient is still a background. */
  .contact-card__links a[href*="maps.app.goo.gl"]:hover,
  .site-footer__social a[href*="maps.app.goo.gl"]:hover { color: #4285F4; }

  .contact-card__links a[href*="maps.app.goo.gl"]:hover::before,
  .site-footer__social a[href*="maps.app.goo.gl"]:hover::before {
    background: linear-gradient(145deg,
      #4285F4 0%, #4285F4 28%,
      #34A853 28%, #34A853 52%,
      #FBBC04 52%, #FBBC04 76%,
      #EA4335 76%);
  }
}

/* ==========================================================================
   7. FOOTER
   ========================================================================== */
.site-footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: rgba(20, 20, 19, 0.03);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  border-block-start: 1px solid var(--pane-line);
  padding-block-start: var(--sp-7);
  /* Clear of the home indicator on a gesture-navigation phone */
  padding-block-end: calc(var(--sp-5) + var(--safe-b));
}
.site-footer::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: -8rem;
  inset-inline-start: 50%;
  inline-size: min(54rem, 88vw);
  block-size: 12rem;
  transform: translateX(-50%);
  background: radial-gradient(ellipse, rgba(20, 20, 19, 0.05), transparent 68%);
  pointer-events: none;
}
.site-footer__inner {
  display: grid;
  grid-template-areas:
    "brand"
    "nav"
    "social"
    "meta";
  grid-template-columns: minmax(0, 1fr);
  align-items: flex-start;
  gap: var(--sp-4);
}
.site-footer__brand {
  grid-area: brand;
  display: grid;
  gap: var(--sp-3);
}
.site-footer__lockup {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  inline-size: fit-content;
  border-radius: var(--r);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.site-footer__lockup:focus-visible { outline-offset: 6px; }
.site-footer__lockup .brand__mark { inline-size: 44px; }
.site-footer__lockup .brand__word { inline-size: 130px; }
.site-footer__combined-logo {
  position: relative;
  display: block;
  inline-size: clamp(11rem, 20vw, 15.5rem);
  aspect-ratio: 738 / 182;
  flex: none;
  overflow: hidden;
}
.site-footer__combined-logo img {
  position: absolute;
  inset-block-start: -29.67%;
  inset-inline-start: -7.86%;
  inline-size: 115.72%;
  max-inline-size: none;
  filter: none;
}
[dir="rtl"] .site-footer__combined-logo img {
  inset-inline-start: auto;
  inset-inline-end: -7.86%;
}
.site-footer__tag {
  margin: 0; font-size: var(--fs-2xs); letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--text-3);
}

.site-footer__nav {
  grid-area: nav;
  display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-5);
  font-size: var(--fs-xs);
  text-transform: uppercase; letter-spacing: 0.13em; font-weight: 600;
  color: var(--text-3);
}
.site-footer__nav a {
  display: inline-flex; align-items: center;
  min-block-size: var(--tap-dense);
  transition: color 0.25s;
}
@media (hover: hover) {
  .site-footer__lockup:hover { opacity: 0.82; transform: translateY(-2px); }
  .site-footer__nav a:hover { color: var(--accent-lift); }
}


/* Social row, directly under the nav links. Icon only: the nav above it is
   already four words wide and five more would turn it into a paragraph.
   Each link carries an aria-label instead, so the glyph is not the only
   thing naming it.

   No border, no plate, no chip. Each link still occupies a --tap-dense
   square, because a finger needs one, but that square is a target and not
   a button and has no business being visible. Hover answers in the brand
   colour further down, which says the same thing without drawing a box
   around the glyph to say it.

   The glyphs come from the same href-keyed --icon definitions the contact
   card uses. */
.site-footer__social {
  grid-area: social;
  /* The drawn size of a mark, and the box it sits in, in one place: the
     optical pull below is the difference between the two, and the box is
     --tap rather than --tap-dense now the marks have grown into it. */
  --glyph: 1.5rem;
  --glyph-box: var(--tap);
  display: flex;
  flex-wrap: wrap;
  /* The box already carries 10px of air on each side, so the gap only has
     to cover the rest of the distance between two marks. */
  gap: var(--sp-1);
  /* That air also means the row would start a few pixels in from the nav
     links above. Pull it back by half the difference and the first mark
     lines up with the first word. */
  margin-inline: calc((var(--glyph-box) - var(--glyph)) * -0.5);
}
.site-footer__social a {
  display: inline-grid;
  place-items: center;
  inline-size: var(--glyph-box);
  block-size: var(--glyph-box);
  border-radius: var(--r-sm);
  color: var(--text-3);
  transition: color 0.2s, transform 0.2s var(--ease);
}
.site-footer__social a::before {
  inline-size: var(--glyph);
  block-size: var(--glyph);
}
@media (hover: hover) {
  .site-footer__social a:hover { transform: translateY(-2px); }
}

/* The copyright on its own line, the two legal links sharing the next one.
   It wraps, so a long translation on the narrowest screen still fits. */
.site-footer__meta {
  grid-area: meta;
  inline-size: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-3);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--pane-line);
  font-size: var(--fs-sm);
  text-align: start;
  color: var(--text-3);
}
.site-footer__meta p { flex-basis: 100%; margin: 0; color: var(--text-2); }

/* ==========================================================================
   8. COOKIE CONSENT
   --------------------------------------------------------------------------
   A bottom sheet on a phone, a bar on a desktop. It is the first thing a
   visitor meets, so the buttons are full width and full height on touch,
   and the panel is capped at 85svh with its own scroll: on a small phone
   in landscape the three toggles would otherwise push the buttons off.
   ========================================================================== */
.consent {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-consent);
  padding: var(--sp-3);
  padding-block-end: calc(var(--sp-3) + var(--safe-b));
  padding-inline: var(--gutter-x);
  transform: translateY(130%);
  transition: transform 0.65s var(--ease);
}
.consent.is-visible { transform: translateY(0); }

/* Sized for a short screen first, because that is where this panel is a
   problem. The notice is long and its wording is fixed, so on a phone in
   landscape (360px tall) or an iPhone SE the text cannot all fit. What must
   never happen is the text pushing the answers out of view: a visitor who
   cannot see Reject and Accept cannot answer.

   So the panel itself does not scroll - only .consent__text does, and
   .consent__actions is held outside that scroller. Whatever the viewport,
   the two answers are on screen. Padding and gap open up again at
   min-height: 36rem, which is every phone held upright. */
.consent__inner {
  max-inline-size: var(--container);
  /* Never taller than the screen minus the header it would otherwise slide
     under. The second term is what keeps the two off each other at a large
     browser text size, where the notice grows but the screen does not. */
  max-block-size: var(--consent-max);
  overflow: hidden;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-3);
  padding: var(--consent-pad);
  border-radius: var(--r-xl);
  background: var(--pane-hi);
  -webkit-backdrop-filter: var(--blur);
          backdrop-filter: var(--blur);
  box-shadow: var(--lift), var(--spec);
}
/* Opening "Choose" turns a passive banner into a deliberate step. JavaScript
   gives the banner an explicit state class so this layout cannot silently
   fall back to the desktop bar and clip the lower option copy. */
.consent.is-customising .consent__inner {
  /* Still clear of the header. The banner is aria-modal="false" on purpose,
     so it must not behave like a dialog and cover the page furniture - it
     takes the room it needs out of its own padding instead, which is what
     --consent-pad does below. */
  --consent-max: min(92svh, calc(100svh - var(--header-h) - var(--sp-5)));
  --consent-pad: var(--sp-5);
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  justify-content: flex-start;
}

.consent__text {
  flex: 1 1 auto;
  /* Two different constraints, because this element lives in two different
     flex directions. In the phone layout the panel is a column, block size
     is the MAIN axis, and min-block-size: 0 is what lets the item shrink
     below its content. At lg the panel is a row, block size is the CROSS
     axis governed by align-items: center, and min-block-size does nothing -
     the item sizes to its content and spills out of the capped panel, where
     overflow: hidden cuts it off mid-sentence with no scrollbar to say so.
     max-block-size: 100% is what constrains it there. Both are needed. */
  min-block-size: 0;
  max-block-size: calc(var(--consent-max) - 2 * var(--consent-pad));
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  /* A visible scrollbar is the affordance: without it a clipped paragraph
     reads as a broken layout rather than as more text below. */
  scrollbar-width: thin;
  scrollbar-gutter: stable;
}
.consent__actions { flex: none; }

/* Anything taller than a landscape phone gets the roomier panel, the full
   header and a little more of the screen back. 36rem is 576px: every phone
   held upright clears it, every phone held sideways does not.

   This is the one place the stylesheet asks about height, and the only
   place a max- style constraint is legitimate - written as min-height so
   it still reads mobile-first. Height has no "narrow first" direction: a
   rule that is right for a 360px-tall screen is not a smaller version of
   the tall one, it is a different layout. */
@media (min-height: 36rem) {
  :root { --header-h: clamp(4rem, 2.4rem + 4.2vw, 5.75rem); }
  :root {
    --consent-pad: var(--sp-6);
    --consent-max: min(85svh, calc(100svh - var(--header-h) - var(--sp-9)));
  }
  .consent__inner { gap: var(--sp-5); }
}
.consent__text h2 { font-size: var(--fs-xl); margin-bottom: var(--sp-2); }
.consent__text p { font-size: var(--fs-md); color: var(--text-2); margin: 0; }
/* What refusing means, said on the banner itself rather than left for the
   notice: a visitor who says no should not be surprised later to learn a
   page view was still counted. */
.consent__text .consent__note { margin-block-start: var(--sp-2); font-size: var(--fs-sm); color: var(--text-3); }
.consent__link {
  display: inline-flex;
  align-items: center;
  min-block-size: var(--tap);
  font-size: var(--fs-sm);
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.consent__text .consent__choose {
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
}

/* auto-fit rather than a stack: two short buttons sit side by side as soon
   as there is room for them, on one row of three by tablet width. Reject
   and Accept are the same class on purpose - the same size and the same
   weight, so refusing is never the harder of the two to find. */
/* 8rem is what lets the two answers share the top row from a 360px phone
   up, with "Choose" on the row below (9rem stacked them under 380px, so
   Accept sat below Reject instead of beside it). Stretch, so a label that
   wraps in one language cannot make one answer taller than the other. */
/* 6.5rem, not 8: the markup puts Reject and Accept first so they share the
   top row and neither sits further down the screen than the other. At 8rem
   that intent survived a 360px phone but broke on a 320px one, where the
   track no longer fit twice and all three buttons stacked - which both lost
   the pairing and made the panel 52px taller on the screen least able to
   afford it. 6.5rem still fits two columns inside a 320px viewport. */
.consent__actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 6.5rem), 1fr));
  gap: var(--sp-2);
  align-items: stretch;
}

.consent__options { display: grid; gap: var(--sp-3); margin-top: var(--sp-5); }
.switch { display: flex; align-items: flex-start; gap: var(--sp-3); cursor: pointer; padding-block: var(--sp-1); }
.switch input {
  margin-block-start: 0.2rem;
  accent-color: var(--accent-lift);
  inline-size: 1.25rem; block-size: 1.25rem;
  flex: none;
}
.switch input:disabled { opacity: 0.5; }
.switch span { display: grid; }
.switch strong { font-size: var(--fs-md); color: var(--text); }
.switch em { font-style: normal; font-size: var(--fs-sm); color: var(--text-3); }

/* ==========================================================================
   9. MODAL + FORM
   --------------------------------------------------------------------------
   The lead form is a bottom sheet on a phone: anchored to the thumb, and
   with the submit button where a thumb already is. It becomes a centred
   dialog at 48rem.

   max-block-size is in svh for the same reason the hero is - with vh, the
   bottom of the panel sits behind the browser chrome and the submit button
   cannot be reached.
   ========================================================================== */
.modal {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  opacity: 0; transition: opacity 0.35s var(--ease);
}
.modal.is-open { opacity: 1; }
.modal__backdrop {
  position: absolute; inset: 0;
  background: rgba(20, 20, 19, 0.40);
  -webkit-backdrop-filter: blur(10px);
          backdrop-filter: blur(10px);
}

.modal__panel {
  position: relative;
  inline-size: 100%;
  max-block-size: 92svh;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: var(--sp-6);
  padding-block-end: calc(var(--sp-6) + var(--safe-b));
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  background: var(--pane-hi);
  -webkit-backdrop-filter: var(--blur);
          backdrop-filter: var(--blur);
  box-shadow: var(--lift), var(--spec);
  transform: translateY(24px);
  transition: transform 0.45s var(--ease);
}
.modal.is-open .modal__panel { transform: translateY(0) scale(1); }

.modal__close {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-end: var(--sp-3);
  inline-size: var(--tap); block-size: var(--tap);
  border-radius: 50%;
  border: 1px solid var(--pane-line);
  background: rgba(20, 20, 19, 0.05);
  color: var(--text-2);
  font-size: var(--fs-glyph); line-height: 1; cursor: pointer;
  transition: background-color 0.25s, color 0.25s, border-color 0.25s, transform 0.25s;
}
@media (hover: hover) {
  .modal__close:hover {
    background: rgba(20, 20, 19, 0.10);
    border-color: var(--pane-line-hi);
    color: var(--text);
    transform: rotate(90deg);
  }
}

/* A dialog is not a page: the h2 scale that suits a section heading costs
   two lines here and pushes the submit button out of view. */
.modal__panel .eyebrow {
  margin-bottom: var(--sp-3);
  /* clear of the close button */
  padding-inline-end: calc(var(--tap) + var(--sp-2));
}
.modal__panel h2 {
  font-size: var(--fs-2xl);
  line-height: 1.15;
  margin-bottom: var(--sp-2);
}
.modal__sub { color: var(--text-2); font-size: var(--fs-md); margin-bottom: var(--sp-5); }

/* auto-fit pairs name/email and phone/message into two columns as soon as
   two 13rem tracks fit, and drops back to one on a phone - no breakpoint to
   maintain. Everything that is not a field spans the full width. */
.form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  align-items: start;
  gap: var(--sp-3) var(--sp-4);
}
.form > * { grid-column: 1 / -1; }
.form__row { grid-column: auto; display: grid; gap: var(--sp-2); }
.form__row label {
  font-size: var(--fs-2xs); font-weight: 600; color: var(--text-2);
  text-transform: uppercase; letter-spacing: 0.13em;
}
.form__row label em { font-style: normal; font-weight: 400; color: var(--text-3); letter-spacing: 0.06em; }

.form input, .form textarea {
  inline-size: 100%;
  min-block-size: var(--control-h);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  color: var(--text);
  /* --field-fs is floored at 16px on purpose: below that, iOS Safari zooms
     the whole page the moment the field takes focus, and never zooms back
     out. Do not replace this with a smaller token. */
  font-size: var(--field-fs);
  transition: border-color 0.25s, box-shadow 0.25s, background 0.25s;
  resize: vertical;
}
.form textarea { min-block-size: calc(var(--control-h) * 1.5); }
.form input::placeholder, .form textarea::placeholder { color: var(--text-3); }
.form input:focus, .form textarea:focus {
  outline: none;
  border-color: var(--accent-lift);
  box-shadow: 0 0 0 3px rgba(20, 20, 19, 0.16), var(--spec);
}
.form input.has-error, .form textarea.has-error {
  border-color: color-mix(in srgb, var(--danger) 70%, transparent);
}

.form__error { display: none; font-size: var(--fs-sm); color: var(--danger); }
.form__error.is-shown { display: block; }
/* The server did not confirm the enquiry: said under the button that sent it. */
.form__error--send { text-align: center; margin: 0; }

/* Under the message field: what not to put in it. */
.form__hint { font-size: var(--fs-sm); color: var(--text-3); margin: 0; }

/* Sending: the label says so, and a second press does nothing. */
.btn[aria-busy="true"] { opacity: 0.7; cursor: progress; }

.form__note { font-size: var(--fs-sm); color: var(--text-3); text-align: center; margin: 0; }
.form__note a { color: var(--text-2); text-decoration: underline; text-underline-offset: 3px; }

/* The honeypot: in the page for a bot, out of sight for a person. Clipped
   rather than display:none, because the bots worth catching skip fields
   they can tell are hidden. */
.form__trap {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.form__success { display: grid; justify-items: center; text-align: center; gap: var(--sp-2); padding-block: var(--sp-5); }
.form__success-mark {
  inline-size: 60px; block-size: 60px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--whatsapp);
  background: color-mix(in srgb, var(--whatsapp) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--whatsapp) 45%, transparent);
  box-shadow: 0 0 28px -8px color-mix(in srgb, var(--whatsapp) 70%, transparent),
              inset 0 1px 0 rgba(255, 255, 255, 0.25);
  margin-bottom: var(--sp-3);
}
.form__success-mark svg { inline-size: 26px; block-size: 26px; }
/* Shown only once the server has confirmed it has the enquiry: the
   confirmation, its reference, then WhatsApp as an optional next step with
   what it means said before the button. */
.form__success p { color: var(--text-2); font-size: var(--fs-md); margin: 0; }
.form__success h3:focus { outline: none; }
.form__success .form__ref { color: var(--text); font-size: var(--fs-sm); letter-spacing: 0.06em; }
.form__success .form__wa-note { margin-block: var(--sp-5) var(--sp-2); font-size: var(--fs-sm); color: var(--text-3); }

/* ==========================================================================
   10. PRIVACY PAGE
   --------------------------------------------------------------------------
   One readable column. The notice is prose people should actually be able
   to read, so it is set at body size on the narrow measure rather than as
   fine print. The choice panel is the one pane, because it is the one
   thing on the page you act on.
   ========================================================================== */
.privacy { display: grid; gap: var(--sp-7); }
.privacy > * { max-inline-size: var(--container-narrow); }
.privacy h2 { font-size: var(--fs-xl); margin-bottom: var(--sp-3); }
.privacy p { font-size: var(--fs-base); color: var(--text-2); margin: 0; }

.privacy__facts { display: grid; gap: var(--sp-3); margin: var(--sp-5) 0 0; }
.privacy__facts div {
  display: grid;
  gap: var(--sp-1);
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--pane-line);
}
.privacy__facts dt,
.privacy .privacy__label {
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-3);
}
.privacy__facts dd { margin: 0; font-size: var(--fs-md); color: var(--text); }
/* These two are the only things on the notice a visitor taps - the address
   they write to about their data. A bare inline link is a 19px line box, so
   they are padded up to --tap-dense, the same treatment the contact card
   gives its WhatsApp and email links. */
.privacy__facts a,
.privacy__email {
  color: var(--text);
  text-decoration: underline;
  text-underline-offset: 3px;
  /* Each address on its own line when a language lists two. */
  display: block;
  inline-size: fit-content;
  min-block-size: var(--tap-dense);
  padding-block: var(--sp-2);
  overflow-wrap: anywhere;
}

.privacy__sections { display: grid; gap: var(--sp-6); }
.privacy__section { padding-block-start: var(--sp-5); border-block-start: 1px solid var(--pane-line); }

.privacy-choice { padding: var(--sp-6); border-radius: var(--r-xl); }
.privacy-choice .privacy-choice__status { font-size: var(--fs-md); }
/* The visitor's id is long and unbroken; let it wrap rather than push the
   pane wider than a phone. */
.privacy-choice .privacy-choice__id {
  margin-block-start: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--text-3);
  overflow-wrap: anywhere;
}
.privacy-choice__list { display: grid; margin: var(--sp-5) 0 0; }
.privacy-choice__list div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-3);
  border-block-start: 1px solid var(--pane-line);
}
.privacy-choice__list div:first-child { border-block-start: 0; }
.privacy-choice__list dt { font-size: var(--fs-md); color: var(--text); }
.privacy-choice__list dd {
  margin: 0;
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-3);
}
.privacy-choice__list dd.is-on { color: var(--whatsapp); }
/* Same rule as the banner: the two ways out sit side by side. */
.privacy-choice__actions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: var(--sp-2);
  margin-block-start: var(--sp-5);
}

.privacy__email { font-family: var(--font-display); font-size: var(--fs-xl); }
.privacy .privacy__updated { margin-block-start: var(--sp-3); font-size: var(--fs-sm); color: var(--text-3); }

/* ==========================================================================
   11. SCROLL REVEAL
   ========================================================================== */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
.reveal.is-visible { opacity: 1; transform: none; }
.wall .reveal:nth-child(2), .services .reveal:nth-child(2), .value-grid .reveal:nth-child(2), .process-grid .reveal:nth-child(2) { transition-delay: 0.07s; }
.wall .reveal:nth-child(3), .services .reveal:nth-child(3), .value-grid .reveal:nth-child(3), .process-grid .reveal:nth-child(3) { transition-delay: 0.14s; }
.wall .reveal:nth-child(4) { transition-delay: 0.21s; }
.wall .reveal:nth-child(5) { transition-delay: 0.28s; }
.wall .reveal:nth-child(6) { transition-delay: 0.35s; }

/* ==========================================================================
   12. RTL (ARABIC)
   --------------------------------------------------------------------------
   Nearly everything above uses logical properties, so the layout mirrors
   automatically when dir="rtl" is set on <html>. Only genuinely
   direction-aware details need overriding here.
   ========================================================================== */
[dir="rtl"] { --font-sans: var(--font-ar); --font-display: var(--font-ar); }
html[lang="ar"] body { font-family: var(--font-ar); line-height: 1.95; letter-spacing: 0; }
html[lang="ar"] h1, html[lang="ar"] h2, html[lang="ar"] h3 { letter-spacing: 0; line-height: 1.45; }

/* Latin-only tracking tricks look wrong in Arabic */
html[lang="ar"] .eyebrow,
html[lang="ar"] .nav__link,
html[lang="ar"] .nav__tagline,
html[lang="ar"] .btn,
html[lang="ar"] .site-footer__nav,
html[lang="ar"] .hero__stats span,
html[lang="ar"] .form__row label,
html[lang="ar"] .contact-card__list dt,
html[lang="ar"] .privacy__facts dt,
html[lang="ar"] .privacy-choice__list dd,
html[lang="ar"] .consent__choose,
html[lang="ar"] .site-footer__tag,
html[lang="ar"] .hero__hint { letter-spacing: 0.02em; }

html[lang="ar"] .cta-band__title { text-transform: none; }

/* Numerals inside an RTL page.

   `unicode-bidi: isolate` only. Do NOT add `direction: ltr` here, which is
   what this rule used to do: it lays the box out left to right, so the
   numeral sits flush against the LEFT edge of its grid cell while the
   Arabic label under it sits flush RIGHT, and the pair reads as two
   unrelated things.

   The override was never needed. Western digits are bidi class EN and
   already form a left-to-right run inside right-to-left text, so "2018"
   renders as "2018" on its own. Isolation is kept so a numeral can never
   reorder against text beside it. */
[dir="rtl"] .brick__num,
[dir="rtl"] .service__index,
[dir="rtl"] .process-card__num,
[dir="rtl"] .value__num,
[dir="rtl"] .hero__stats strong,
[dir="rtl"] .about__facts dd { unicode-bidi: isolate; }

/* The contact rows are mixed: a Latin address, an Arabic hours line, a
   phone number, an email. A blanket direction would be wrong for at least
   one of them, so each row decides for itself from its own first strong
   character, and text-align pins them all to the same edge so the column
   still reads as a column. */
[dir="rtl"] .contact-card__list dd {
  unicode-bidi: plaintext;
  text-align: right;
}

/* Except the phone number, which has no strong character to decide from:
   it is all digits and punctuation, so it inherits RTL and the leading "+"
   jumps to the far end. This is the one row that has to be told. */
[dir="rtl"] .contact-card__list a[href^="https://wa.me"],
[dir="rtl"] .contact-card__list a[href^="tel:"] {
  direction: ltr;
  unicode-bidi: isolate;
}

/* ==========================================================================
   13. RESPONSIVE BREAKPOINTS + DEVICE CAPABILITIES
   --------------------------------------------------------------------------
   Four breakpoints. There are no others, and there are no max-width
   queries: everything above is already the phone layout, and each block
   here only adds to it.

     sm   30rem /  480px   large phone, phone in landscape
     md   48rem /  768px   tablet portrait          (iPad 768, Air 820)
     lg   64rem / 1024px   tablet landscape, laptop (iPad landscape 1024)
     xl   80rem / 1280px   wide desktop

   They are written in rem, not px, so a visitor who has raised their
   browser font size gets the simpler layout at the same *readable* width
   rather than a desktop grid full of oversized type.

   lg is where the site becomes a desktop: the burger turns into a row of
   links and the hero splits into two columns. A tablet in portrait keeps
   the phone layout on purpose - at 768px the nav, the language switch and
   the call to action do not fit on one line without crushing all three.

   Two kinds of block here ask about the device rather than its width, and
   neither is a breakpoint: min-height: 36rem (section 8) and the two
   viewport-segments blocks at the end, for a Surface Duo spanned across
   both screens.
   ========================================================================== */

/* --- Utilities ------------------------------------------------------------
   .hide-sm hides the header call to action, which has no room next to the
   burger, until the row layout returns at lg. It lives here rather than up
   with the layout helpers because it has to beat .btn { display:inline-flex }
   in section 3 - same specificity, so source order is what decides. Keep any
   new display utility down here for the same reason.                      */
.hide-sm { display: none; }

/* --- sm : 480px ---------------------------------------------------------- */
@media (min-width: 30rem) {
  /* Enough width for buttons to sit side by side instead of stacking */
  .hero__actions,
  .contact__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
  .cta-band .btn { inline-size: auto; }
}

/* --- md : 768px, tablet portrait ----------------------------------------- */
@media (min-width: 48rem) {
  :root {
    /* A tablet has the GPU budget for the full glass recipe */
    --blur:    saturate(180%) blur(22px);
    --blur-sm: saturate(170%) blur(13px);
  }

  .clients__moving-head { grid-template-columns: auto minmax(0, 1fr); }
  .clients__hint { text-align: end; }

  .bg__blob--c { display: block; }

  .hero__stage { block-size: min(42svh, 22rem); }

  /* The lead form stops being a bottom sheet and becomes a dialog */
  .modal { align-items: center; padding: var(--sp-5); }
  .modal__panel {
    /* Wide enough for two field columns, and for the consent line and the
       footnote to each stay on one line - every wrap here costs ~20px of a
       height budget that has to fit the submit button on screen. */
    inline-size: min(640px, 100%);
    /* .modal's own padding already keeps the panel off the window edge, so
       this can take most of the viewport before the safety-net scroll. */
    max-block-size: min(94svh, 860px);
    border-radius: var(--r-xl);
    padding-block-end: var(--sp-6);
    transform: translateY(18px) scale(0.98);
  }

  .site-footer__inner {
    grid-template-areas:
      "brand nav"
      "brand social"
      "meta  meta";
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--sp-8);
    row-gap: var(--sp-3);
  }
  .site-footer__brand {
    display: flex;
    align-items: center;
    gap: var(--sp-5);
  }
  .site-footer__tag {
    padding-inline-start: var(--sp-5);
    border-inline-start: 1px solid var(--pane-line);
  }
  /* From here the nav and the icon row under it both hang off the right
     edge, so the two read as one column answering the lockup on the left
     rather than as two unrelated rows. */
  .site-footer__nav { justify-content: flex-end; }
  .site-footer__social { justify-content: flex-end; }
  .site-footer__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--sp-1) var(--sp-5);
    text-align: end;
  }
  /* The copyright takes the spare room, so it sits at the start of the row
     and the legal links gather at the end. Logical, so Arabic mirrors it. */
  .site-footer__meta p {
    flex-basis: auto;
    margin-inline-end: auto;
    text-align: start;
  }
}

/* --- lg : 1024px, the desktop layout ------------------------------------- */
@media (min-width: 64rem) {
  .nav-toggle { display: none; }
  .hide-sm { display: inline-flex; }

  .site-header .brand { gap: 8px; }
  .site-header .brand__mark { inline-size: 64px; }
  .site-header .brand__word { inline-size: 110px; }
  /* Desktop only. At a large browser text size the nav and the actions can
     outgrow a 1280px row. Letting the row wrap keeps the whole lockup
     intact instead of the brand absorbing the entire shortfall. On a phone
     the header is brand + language + burger and has to stay on one line -
     wrapping there doubles its height and pushes the consent panel off the
     bottom of the screen - so this is deliberately not in the base rule. */
  .site-header__inner { gap: var(--sp-6); flex-wrap: wrap; }
  .brand { min-inline-size: min-content; }

  /* The drawer unwinds back into a row */
  .nav {
    position: static;
    inset: auto;
    flex-direction: row;
    align-items: center;
    gap: var(--gap-nav);
    margin-inline-start: auto;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
    box-shadow: none;
    transform: none;
    opacity: 1;
    visibility: visible;
  }
  .nav__link {
    inline-size: auto;
    min-block-size: var(--tap);
    font-size: var(--fs-xs);
  }
  .nav__link + .nav__link { border-block-start: 0; }
  .nav__link.is-current::after { inline-size: 100%; }

  /* The particle canvas takes the inline-end half, so the wordmark can be
     big and bleed off the edge without ever crossing the copy. It mirrors
     automatically in RTL. */
  .hero {
    display: flex;
    align-items: center;
    block-size: 100svh;
    min-block-size: 100svh;
    gap: 0;
    padding-block: calc(var(--header-h) + var(--sp-5)) var(--sp-5);
  }
  .hero .container { inline-size: 100%; }
  .hero__content .eyebrow { margin-block-end: var(--sp-3); }
  /* The hero splits into two columns here, so the headline steps down.
     Redefining the token beats overriding the font-size: the size stays
     a named thing, and anything else keyed to --fs-hero follows it. */
  :root { --fs-hero: clamp(2.4rem, 3.2vw, 3.4rem); }
  .hero__title { margin-block-end: var(--sp-3); }
  .hero__lead {
    margin-block-end: var(--sp-5);
    line-height: 1.55;
  }
  .hero__stage {
    position: absolute;
    /* inline-size must go back to auto here. The base rule sets it to 100%
       for the stacked phone layout, and on an absolutely positioned box a
       declared width beats inset-inline-end: the stage would be a full
       viewport wide, start at 52%, and hang its whole right half off the
       screen. Only .hero's overflow:hidden was keeping that off the page. */
    inline-size: auto;
    inset-inline: 52% 0;
    inset-block-start: 54%;
    transform: translateY(-50%);
    block-size: min(72svh, 42rem);
  }
  .hero__actions { margin-block-end: var(--sp-5); }
  .hero__stats {
    gap: var(--sp-7);
    padding-block-start: var(--sp-4);
  }
  .hero__stats span { font-size: var(--fs-xs); letter-spacing: 0.13em; }

  /* Pinned to the corner, on touch screens too. It used to be pinned only
     with a pointer, and left in flow on a touch screen this wide (an iPad
     or a Fold held sideways) it became a third item in the hero's flex row
     and sat on top of the art. main.js swaps the wording on touch. */
  .hero__hint {
    position: absolute;
    inset-block-end: var(--sp-6);
    inset-inline-end: var(--gutter-x);
    padding-inline: 0;
  }

  .about {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--gap-col);
  }
  .about__head { position: sticky; top: calc(var(--header-h) + var(--sp-6)); }

  .founder-card {
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: var(--gap-col);
    padding: var(--sp-8);
  }

  .faq-layout {
    grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr);
    gap: var(--gap-col);
  }
  .faq-layout .section__head { position: sticky; top: calc(var(--header-h) + var(--sp-6)); }

  .contact {
    grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
    gap: var(--gap-col);
  }

  .contact__actions { margin-top: var(--sp-7); }

  .consent__inner {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
  }
  .consent__text { flex: 1 1 26rem; }
  /* One row, equal columns: each as wide as the widest label. A flex row
     sized each button to its own text, which made "Reject all" narrower
     than "Accept all" - in Indonesian, "Tolak semua" against "Terima
     semua". The two answers must weigh the same in every language. */
  .consent__actions {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
  }

  /* Preferences need the whole panel width. Keeping the ordinary desktop
     banner's side-by-side layout here left a narrow text scroller and made
     the final lines look missing. Three option columns keep every category
     readable on a short laptop without adding unnecessary panel height. */
  .consent.is-customising .consent__options {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--sp-6);
  }
  .consent.is-customising .consent__choose { grid-column: 1 / -1; }
  .consent.is-customising .consent__actions {
    inline-size: min(100%, 34rem);
    align-self: flex-end;
  }

  /* Present the owner's photograph as one large, uninterrupted image while
     keeping the complete frame inside a desktop viewport. */
  .faith {
    display: grid;
    align-items: center;
    min-block-size: calc(100svh - var(--header-h));
    padding-block: var(--sp-4);
  }
  .faith .container { inline-size: 100%; }
  .faith__card {
    grid-template-columns: minmax(0, 1fr);
    block-size: clamp(36rem, calc(100svh - var(--header-h) - 2rem), 42rem);
  }
  .faith__media {
    aspect-ratio: auto;
    min-block-size: 0;
    block-size: 100%;
  }

  .reveal { transform: translateY(26px); }
}

/* --- lg + a real pointer ------------------------------------------------- */
@media (min-width: 64rem) and (hover: hover) {
  .nav__link:hover { color: var(--text); }
  .nav__link:hover::after { inline-size: 100%; }
}

/* --- xl : 1280px --------------------------------------------------------- */
@media (min-width: 80rem) {
  .nav__tagline {
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-3xs);
    font-weight: 600;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--text-3);
    padding-inline-start: var(--gap-nav);
    border-inline-start: 1px solid var(--pane-line);
    white-space: nowrap;
  }

  /* From 1280px the header row has as little as 1136px to work in: up to
     about 1350px --container is at its 1240px floor. Whether the row fits
     there is purely a question of how long the wording is in each language:

       English    "BENEFICIAL TO YOU"      176px -> row needs 1121, fits
       Arabic                                    -> fits
       Indonesian "BERMANFAAT UNTUK ANDA"  229px -> row needs 1201, does not

     Without this the Indonesian row wrapped to two lines, which made the
     header 161px while scroll-padding-top still trusted --header-h (92px),
     so every in-page anchor landed 69px short. The tagline is decorative
     and is still shown in full in the footer on every page, so it is the
     item that gives. tools/check-devices.mjs fails if this stops being
     true - if the nav wording changes, re-run it rather than assuming. */
  html[lang="id"] .nav__tagline { display: none; }
}

/* --- Two screens: Surface Duo and other dual-screen devices ---------------
   A browser spanned across both screens of a Surface Duo reports a single
   viewport with a hinge through it that hides about 34px of whatever is
   underneath. These two blocks keep the words off the hinge. They are not
   width breakpoints: they ask about the shape of the device, the way the
   min-height: 36rem block asks about height, and a browser without the
   viewport-segments API simply never matches them. A Galaxy Z Fold's inner
   screen is one continuous panel, so it does not match either; it gets the
   ordinary layout for its width.

   Side by side (1114x720, so the lg layout): anything in two columns
   splits exactly at the hinge, anything in one column stays on the first
   screen, and the hero puts its words on one screen and its art on the
   other. The desktop nav row needs more than one screen, so it turns back
   into the drawer, which opens on the second screen.                     */
@media (horizontal-viewport-segments: 2) {
  .about, .founder-card, .faq-layout, .contact, .credit,
  .value-grid, .process-grid, .cta-band__inner {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--gap-hinge);
  }
  .cta-band__visual { justify-self: center; }

  .hero__content, .section__head, .section__title, .section__sub, .lead,
  .page-head__lead, .privacy > * {
    max-inline-size: var(--pane-w);
  }
  .page-head__title { max-inline-size: min(18ch, var(--pane-w)); }
  .hero__stage { inset-inline: calc(50% + var(--hinge) / 2) 0; }

  /* A service row: its number and name on the first screen, its list on
     the second. */
  .service {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* The list spans both rows; without this its height is shared out
       between them and the number floats away from the name. */
    grid-template-rows: auto 1fr;
    column-gap: var(--gap-hinge);
  }
  .service__index, .service__head { grid-column: 1; }
  .service__list { grid-column: 2; grid-row: 1 / span 2; }

  /* At a large browser text size the footer is in its tablet layout, where
     the lockup and the tagline share a row that can reach the hinge. */
  .site-footer__brand { flex-wrap: wrap; max-inline-size: var(--pane-w); }

  /* Three columns cannot split at a hinge in the middle, so the rail goes
     back to its phone rows on the first screen. */
  .svc-rail {
    flex-direction: column;
    max-inline-size: calc(var(--pane-w) + 2 * var(--sp-5));
  }
  .svc-tile + .svc-tile {
    border-inline-start: 0;
    border-block-start: 1px solid var(--pane-line);
  }

  .nav-toggle { display: grid; }
  .nav {
    position: fixed;
    inset-block-start: calc(var(--header-h) + var(--safe-t) + var(--sp-2));
    inset-inline: calc(50% + var(--hinge) / 2 + var(--gutter-x)) var(--gutter-x);
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding-inline: var(--sp-5);
    padding-block: var(--sp-2);
    border: 1px solid var(--pane-line);
    border-radius: var(--r-lg);
    background: var(--pane-hi);
    -webkit-backdrop-filter: var(--blur);
            backdrop-filter: var(--blur);
    box-shadow: var(--lift), var(--spec);
    transform: translateY(-14px);
    opacity: 0;
    visibility: hidden;
  }
  .site-header.is-open .nav { transform: translateY(0); opacity: 1; visibility: visible; }
  .nav__link { inline-size: 100%; font-size: var(--fs-base); }
  .nav__link + .nav__link { border-block-start: 1px solid var(--pane-line); }
  .nav__link.is-current::after { inline-size: 0; }

  /* The cookie question and the lead form each take one screen. */
  .consent { inset-inline-end: calc(50% + var(--hinge) / 2); }
  .consent.is-customising .consent__options { grid-template-columns: minmax(0, 1fr); }
  /* The phone panel, not the desktop bar: the bar sizes every answer to
     the widest label, and three of those are wider than one screen. In the
     column the answers take the full width and share one row out. */
  .consent__inner { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
  .consent__text { flex: 1 1 auto; }
  .consent__actions {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 6.5rem), 1fr));
    grid-auto-flow: row;
  }
  .modal { padding-inline-end: calc(50% + var(--hinge) / 2 + var(--sp-5)); }
}

/* One above the other (720x1114): the page scrolls through the hinge, which
   is fine for reading, so only what is on screen before any scrolling is
   arranged around it. The hero art fills the upper screen and the words
   start on the lower one; on the other pages the page head fills the upper
   screen, its words at the foot of it, and the page starts on the lower
   one. The cookie panel and the lead form are capped to the lower screen
   so neither is cut in half. */
@media (vertical-viewport-segments: 2) {
  .hero__stage {
    block-size: calc(var(--screen-a-h) - var(--header-h) - var(--safe-t) - 2 * var(--sp-6));
    margin-block-end: calc(var(--hinge-y) + var(--sp-6));
  }
  .page-head {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-block-size: var(--screen-a-h);
    margin-block-end: var(--hinge-y);
  }
  .consent__inner,
  .consent.is-customising .consent__inner { --consent-max: calc(var(--screen-b-h) - var(--sp-5)); }
  .modal__panel { max-block-size: calc(var(--screen-b-h) - var(--sp-3)); }
}

/* ==========================================================================
   FALLBACK - no backdrop-filter
   --------------------------------------------------------------------------
   Without the blur, a pane at 10% white over the aurora is unreadable.
   These browsers get a flat tinted surface instead of glass.
   ========================================================================== */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header.is-stuck,
  .site-header.is-open,
  .nav,
  .faith__card, .founder-card, .contact-card, .privacy-choice,
  .cta-band__inner, .consent__inner, .modal__panel,
  .btn--ghost, .btn--outline,
  .form input, .form textarea {
    background: rgba(255, 255, 255, 0.96);
  }
  .modal__backdrop { background: rgba(20, 20, 19, 0.62); }
}

/* ==========================================================================
   MOTION PREFERENCES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  /* The grain and the ruled lines are the two things that actually move
     on this page, so name them: the blanket rule above only shortens an
     animation, and a 1ms grain still flickers. */
  .bg__blob, .bg__rules, .bg::after { animation: none !important; }
  .client-marquee {
    overflow-x: auto;
    -webkit-mask-image: none;
            mask-image: none;
  }
  .client-marquee__track { animation: none !important; transform: none !important; }
  .client-marquee__group[aria-hidden="true"] { display: none; }
}

/* ==========================================================================
   PRINT - drop the glass and the aurora, print dark ink on white
   ========================================================================== */
@media print {
  .site-header, .nav, .consent, .modal, .hero__stage, .hero__hint, .bg { display: none !important; }
  body { color: #000; background: #fff; }
  .reveal { opacity: 1 !important; transform: none !important; }
  h1, h2, h3, .hero__title, .hero__stats strong {
    color: #000 !important;
    -webkit-text-fill-color: #000 !important;
    background: none !important;
  }
  .faith__card, .founder-card, .contact-card, .privacy-choice, .cta-band__inner {
    background: none !important;
    border: 1px solid #999 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
            backdrop-filter: none !important;
  }
  .brand__word img, .brand__mark img { filter: none !important; }
  .client-marquee { overflow: visible; inline-size: 100%; margin: 0; -webkit-mask-image: none; mask-image: none; }
  .client-marquee__track { animation: none !important; transform: none !important; }
  .client-marquee__group { flex-wrap: wrap; }
  .client-marquee__group[aria-hidden="true"] { display: none; }
}


/* ==========================================================================
   14. THE PARTICLE CANVAS AND THE WHATSAPP DOCK
   --------------------------------------------------------------------------
   The canvas is a sibling of the page, fixed over the whole viewport, rather
   than a child of .hero__stage. It has to be: at `lg` the stage carries a
   transform, and a transform makes an element the containing block for any
   fixed-position descendant, so a canvas inside it would be pinned to the
   stage and clipped by .hero's overflow. Out here it can take dots anywhere
   on screen, which is the whole point of the dock.

   .hero__stage stays where it was and keeps its size. Nothing is drawn into
   it any more; the canvas just follows it, so the hero art still sits where
   the layout puts it and still moves with the page.
   ========================================================================== */
.particles {
  position: fixed;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  z-index: var(--z-particles);
  pointer-events: none;        /* the dots are scenery, never a target */
}

.wa-dock {
  position: fixed;
  inset-inline-end: calc(var(--sp-5) + var(--safe-r));
  inset-block-end: calc(var(--sp-5) + var(--safe-b));
  z-index: var(--z-dock);
  inline-size: var(--dock-size);
  block-size: var(--dock-size);
  display: grid;
  place-items: center;
  border-radius: var(--r-pill);
  /* Invisible on purpose: the dots ARE the icon. This is the hit area and
     the thing a keyboard can reach. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s var(--ease), visibility 0s linear 0.4s;
}
.wa-dock.is-in {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.6s var(--ease) 0.9s, visibility 0s;
}

/* The dock floats over the bottom corner, so at the very end of the page it
   would sit on the footer's last rows - on "Privacy notice" and the last
   social icon, catching clicks meant for them. Keep the footer's content out
   of the dock's column instead of adding height under the footer. Logical,
   so in Arabic - where the dock sits on the left - the space is kept on the
   left. Only where a dock exists.

   On a phone the rows are left-aligned and short, so only the legal row
   needs telling. Its lane is the dock's width and its offset from the
   window edge, less the gutter the row already keeps, plus --sp-1 of air:
   just enough that "Cookie preferences" and "Privacy notice" still share
   one line on a 360px phone in all three languages. From md the nav, the
   icons and the legal links all hang off the end edge, so the whole footer
   stops at the lane. The lane is measured from the window edge and the
   centred container's own margin counts towards it (100% here is the
   footer's width, and the margin is nothing until the footer is wider than
   the container), so on a screen wide enough to leave that much room
   already, nothing moves. */
body:has(.wa-dock) .site-footer__meta {
  padding-inline-end: max(0px, calc(var(--dock-size) + var(--sp-5) + var(--sp-1) + var(--safe-r) - var(--gutter-x)));
}
@media (min-width: 48rem) {
  body:has(.wa-dock) .site-footer__meta { padding-inline-end: 0; }
  body:has(.wa-dock) .site-footer__inner {
    padding-inline-end: max(
      var(--gutter-x),
      calc(var(--dock-size) + var(--sp-5) + var(--sp-3) + var(--safe-r) - (100% - min(100%, var(--container))) / 2)
    );
  }
}

/* Hovering opens the mark into a button: the element changes shape, the dots
   are re-pointed at the new box and fly into a rounded rectangle, and the
   number fades in over them. No size transition on purpose - the dots flying
   into the new shape IS the animation, and a CSS transition would mean the
   box we hand the engine is whatever width it happened to be mid-way. */
.wa-dock.is-open {
  inline-size: var(--dock-open-w);
  block-size: var(--dock-open-h);
}

.wa-dock__tip {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  /* Over the dots, which are the button's fill. */
  color: #FFFFFF;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  /* Held back a beat so the dots arrive first and the number lands on a
     button rather than on empty space. */
  transition: opacity 0.2s var(--ease);
}
.wa-dock.is-open .wa-dock__tip {
  opacity: 1;
  transition: opacity 0.25s var(--ease) 0.35s;
}

@media (prefers-reduced-motion: reduce) {
  .wa-dock, .wa-dock__tip { transition-duration: 0.01ms; }
}
