/* ==========================================================================
   KH Haircare — layout
   Shared page furniture and public-page sections.
   Consumes tokens.css; assumes base.css is already loaded.

   Built so far:  site header · hero · galleries · contact row · footer
   ========================================================================== */


/* ==========================================================================
   SITE HEADER
   Solid cream at every scroll position, on every page. It used to be
   transparent until the page scrolled, which put the nav over the top of the
   hero portrait — the pills floated over a face, and the whole header read as
   a rendering fault. Nothing passes behind it now: the hero starts below it
   (see HERO), and every other page already clears it with --header-h.

   Because it is opaque there is nothing left to blur, so no backdrop-filter
   here or on the bar inside it. That also keeps this element from becoming
   the containing block for any fixed descendant, which is worth having for
   free — anything that establishes one (transform, filter, backdrop-filter,
   perspective, will-change) should stay off the header.
   ========================================================================== */

.site-header{
  position:fixed;
  inset-block-start:0;
  inset-inline:0;
  z-index:100;
  display:flex;
  align-items:center;
  min-block-size:var(--header-h);
  background:var(--kh-cream);
  border-block-end:1px solid var(--kh-hairline);
  /* Only the shadow moves now, and .45s so it settles in rather than
     switching on. */
  transition:box-shadow .45s var(--ease);
}

/* The scrolled state is the shadow and nothing else: the background and the
   border are already there. It is the one cue that the page has moved under a
   header that otherwise never changes. */
.site-header.is-scrolled{
  box-shadow:var(--shadow-soft);
}

/* Three columns: logo | nav | actions. The nav column is auto so the bar is
   only ever as wide as its pills; the two 1fr flanks are what centre it. */
.site-header__inner{
  inline-size:100%;
  display:grid;
  grid-template-columns:1fr auto 1fr;
  align-items:center;
  gap:1.5rem;
}

/* --- Logo ----------------------------------------------------------------- */

.site-header__logo{
  justify-self:start;
  display:inline-flex;
  align-items:center;
}

.site-header__logo img{
  block-size:46px;
  inline-size:auto;
}

/* --- Nav ------------------------------------------------------------------
   A floating pill bar: the links sit inside one grouped surface, and the
   active page is a copper pill that slides between them rather than cutting.

   The pill that moves is .site-nav__indicator, one absolutely-positioned box
   behind the links. Sliding a single element is the only way to get the
   travel: a background on the links themselves can cross-fade but can never
   move. Its size and offset are measured in main.js — see initNavIndicator()
   for what forces a re-measure and why.

   This is the whole navigation at every width — there is no panel and no
   hamburger. On a narrow screen the bar drops to a second header row and, if
   the pills still do not fit, scrolls inside itself rather than wrapping.
   -------------------------------------------------------------------------- */

/* min-inline-size:0 is what lets the bar shrink below its content width in the
   grid track — without it an over-wide bar pushes the header past the viewport
   and the whole page picks up a horizontal scrollbar. */
.site-nav{
  justify-self:center;
  min-inline-size:0;
  max-inline-size:100%;
}

/* No background and no blur of its own: the header behind it is already the
   same cream, and a cream bar on a cream header is a shape you cannot see
   drawn twice. The hairline is what groups the pills. */
.site-nav__bar{
  position:relative;   /* the indicator measures against this box */
  display:flex;
  max-inline-size:100%;
  padding:.35rem .5rem;
  border:1px solid var(--kh-hairline);
  border-radius:var(--radius-pill);
  /* The overflow only ever engages on a narrow screen with long labels, but it
     lives here rather than in the media query so the bar can never wrap or
     overrun at any width. The scrollbar is hidden — a visible one inside a
     pill would be its own ornament — and the bar stays keyboard-scrollable
     either way. */
  overflow-x:auto;
  scrollbar-width:none;
  -ms-overflow-style:none;
  overscroll-behavior-inline:contain;
}

.site-nav__bar::-webkit-scrollbar{
  display:none;
}

.site-nav__list{
  display:flex;
  align-items:center;
  /* The pills carry the separation now, so this is only the gap between two
     adjacent faces rather than the old open tracking. */
  gap:.25rem;
  list-style:none;
}

/* Physical top/left on purpose. The offset is measured in physical pixels off
   the bar's own box, so it lands identically under LTR and RTL; a logical
   inset would flip the anchor while the measurement stayed physical, and the
   pill would fly off the end of the bar in Arabic. */
.site-nav__indicator{
  position:absolute;
  top:0;
  left:0;
  z-index:0;
  border-radius:var(--radius-pill);
  background:var(--kh-copper);
  opacity:0;
  pointer-events:none;
  transition:
    transform .45s var(--ease),
    width .45s var(--ease),
    opacity .25s var(--ease);
}

/* First placement happens before .is-ready, so the pill appears where it
   belongs instead of travelling in from the corner of the bar on load. */
.site-nav__indicator:not(.is-ready){
  transition:none;
}

.site-nav__indicator.is-placed{
  opacity:1;
}

.site-nav__link{
  position:relative;   /* over the indicator */
  z-index:1;
  display:inline-block;
  padding:.5rem 1.1rem;
  border-radius:var(--radius-pill);
  background:transparent;
  font-size:var(--step--1);
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--kh-ink);
  white-space:nowrap;
}

/* The active pill is already copper — a sand wash over it would only muddy it,
   and lifting the label off a pill that stays put reads as a glitch. */
@media (hover:hover){
  .site-nav__link:not([aria-current]):hover{
    background:var(--kh-nav-hover);
  }
}

/* Keyboard focus gets the same face, deliberately outside the hover query: a
   keyboard is not a pointer, and this state must not depend on one being
   present. */
.site-nav__link:not([aria-current]):focus-visible{
  background:var(--kh-nav-hover);
}

.site-nav__link[aria-current]{
  background:var(--kh-copper);
  color:var(--kh-cream);
}

/* Once the sliding indicator is live it draws the active pill instead. The
   background above is the fallback for a page view where main.js never runs —
   without it the active link would be cream text on cream. */
.site-nav--sliding .site-nav__link[aria-current]{
  background:transparent;
}

/* --- Actions: language toggle + Book -------------------------------------- */

.site-header__actions{
  justify-self:end;
  display:flex;
  align-items:center;
  gap:1.4rem;
}

.lang-toggle{
  display:inline-flex;
  align-items:center;
  gap:.6rem;
}

.lang-toggle__btn{
  padding:.2em .1em;
  background:none;
  border:0;
  font-size:var(--step--1);
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--kh-muted);
  cursor:pointer;
  transition:color .35s var(--ease);
}

.lang-toggle__btn:hover{
  color:var(--kh-ink);
}

.lang-toggle__btn.is-active{
  color:var(--kh-copper-deep);
}

.lang-toggle__divider{
  inline-size:1px;
  block-size:1em;
  background:var(--kh-hairline);
}

/* Slightly tighter than the page-level button. */
.site-header .btn{
  padding:.85em 1.9em;
}

/* Two labels, one shown: the full one here, the short one on phones — see
   NARROW HEADER. display:none also takes the hidden one out of what a screen
   reader reads. */
.site-header__book-short{
  display:none;
}

/* One Book at a time: on the home page the header's steps aside while the
   hero's is on screen (initBookSwap() in main.js). Faded and lifted, never
   removed, so the row keeps its shape and nothing beside it moves. The
   transition list is the press feedback's (base.css) plus the fade; the
   visibility flip waits for the fade on the way out and is instant on the
   way back. */
.site-header__book{
  transition:
    background-color .35s var(--ease),
    color .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease),
    transform .25s var(--ease),
    opacity .25s var(--ease),
    visibility 0s;
}

.site-header__book.is-tucked{
  opacity:0;
  visibility:hidden;
  transform:translateY(calc(var(--book-tuck) * -1));
  transition:
    transform .25s var(--ease),
    opacity .25s var(--ease),
    visibility 0s linear .25s;
}

/* ==========================================================================
   INTRO CURTAIN  —  home page only
   A cream sheet over the whole viewport that splits and slides away, revealing
   the hero already sitting behind it.

   Timeline, from the first frame:
        0 →  250ms   hold, mark at full opacity
      250 →  700ms   mark fades and drifts up
      700 → 1500ms   the two halves part, top up and bottom down
           1500ms    the hero plays its own entrance (main.js)

   The markup for this is in index.html and it is styled visible from the
   start rather than armed by script — a curtain that waits for JS to appear
   would show the hero first and then cover it. The consequence is that the
   whole sequence has to survive main.js never running, so the halves clear
   themselves and the sheet hides itself on the same clock. main.js does the
   parts CSS cannot: the scroll lock, Escape, and taking the node out of the
   DOM for good. .no-intro (set before first paint by the inline script in
   index.html) and reduced motion both suppress it outright.
   ========================================================================== */

.intro{
  position:fixed;
  inset:0;
  z-index:200;              /* over the header, which sits at 100 */
  display:grid;
  place-items:center;       /* centres the mark; the halves are fixed, so they
                               take no part in this grid */
  animation:intro-spent 0s linear 1500ms forwards;
}

/* Failsafe end state. If main.js never removes the node, it must at least stop
   sitting over the page catching clicks. */
@keyframes intro-spent{
  to{ visibility:hidden; }
}

.intro__half{
  position:fixed;
  inset-inline:0;
  /* 50svh is the height. The max() only guards phones, where the visible
     viewport can be taller than the small one and two 50svh halves would leave
     a strip of hero showing between them; a fixed box's percentage resolves
     against the large viewport, so this is 50svh anywhere the toolbars don't
     move. */
  block-size:max(50svh, 50%);
  background:var(--kh-cream);
  /* transform only — never block-size, never inset — so parting the curtain
     costs no layout. */
  animation-duration:800ms;
  animation-delay:700ms;
  animation-timing-function:var(--ease-curtain);
  animation-fill-mode:both;
  will-change:transform;
}

.intro__half--top{
  inset-block-start:0;
  animation-name:intro-part-up;
}

.intro__half--bottom{
  inset-block-end:0;
  animation-name:intro-part-down;
}

@keyframes intro-part-up{
  to{ transform:translateY(-100%); }
}

@keyframes intro-part-down{
  to{ transform:translateY(100%); }
}

/* position:relative only to lift the mark above the two fixed halves.
   Full opacity from the first frame: a fade-in would mean an empty cream
   screen for as long as it lasted. 35.71% of 700ms is the 250ms hold. */
.intro__logo{
  position:relative;
  block-size:var(--intro-logo);
  inline-size:auto;
  animation:intro-mark-out 700ms var(--ease) forwards;
}

@keyframes intro-mark-out{
  0%, 35.71%{
    opacity:1;
    transform:translateY(0);
  }
  100%{
    opacity:0;
    transform:translateY(calc(var(--intro-lift) * -1));
  }
}

/* Arriving with a hash, or on a back/forward entry — see index.html. */
.no-intro .intro{
  display:none;
}


/* ==========================================================================
   HERO
   Asymmetric editorial split: copy on cream, and alongside it a column of
   veiled video with the arched portrait centred on top. Below 1024px they
   stack: the video, the portrait on its bottom edge, the copy under it —
   see MOBILE.
   ========================================================================== */

.hero{
  position:relative;
  display:grid;
  grid-template-columns:46% 54%;
  /* The header is fixed and opaque, so the hero begins underneath it rather
     than behind it — on the whole section, not on the copy column, because it
     is the media that must never end up behind the nav. Every other page
     clears the header the same way from its own first section. */
  padding-block-start:var(--header-h);
  /* One screen. The old photo-derived bounds went with the full-bleed photo:
     the portrait is now a fixed-ratio frame and the video behind it is
     cropped freely. Content taller than this still grows the hero — this is
     a minimum, so nothing is ever clipped. */
  min-block-size:100vh;
  min-block-size:100svh;
}

/* Both layers sit in the stage, which is a box only for the phone scroll
   effect (HERO SCROLL). Everywhere else it has none, and the copy and the
   media are grid items of .hero exactly as if it were not there. */
.hero__stage{
  display:contents;
}

/* --- Copy ----------------------------------------------------------------- */

.hero__content{
  display:flex;
  flex-direction:column;
  justify-content:center;
  background:var(--kh-cream);
  /* The header is cleared by .hero itself now, so this is only breathing room. */
  padding-block:var(--gutter);
  padding-inline:calc(var(--gutter) + clamp(0rem,2.5vw,2.5rem));
}

.hero__rule{
  display:block;
  inline-size:64px;
  block-size:1px;
  margin-block:2rem;
  background:var(--kh-copper);
  border:0;
}

.hero__details{
  margin-block-start:2rem;
  font-size:var(--step--1);
  letter-spacing:.12em;
  line-height:2;
  color:var(--kh-muted);
  list-style:none;
}

.hero__cta{
  align-self:start;
  margin-block-start:2.5rem;
}

.hero__trust{
  margin-block-start:1.1rem;
  font-size:var(--step--1);
  color:var(--kh-muted);
}

/* The line under the title: the h1's own face (Almarai under RTL, from
   base.css), two steps down the scale — a subtitle, not an eyebrow. */
.hero__specialist{
  display:block;
  margin-block:.4em .3em;
  font-size:var(--step-1);
  line-height:1.3;
  color:var(--kh-ink);
}

/* --- Media: video, veil, portrait ------------------------------------------ */

/* Three layers, bottom to top: the video, the cream veil (::after) and the
   portrait. The gradient is the fallback under all of them — if neither the
   video nor its poster arrives, the column still reads as a deliberate warm
   block rather than a hole in the page. */
.hero__media{
  position:relative;
  overflow:hidden;
  display:grid;
  place-items:center;
  background:linear-gradient(150deg,var(--kh-sand),var(--kh-mocha));
}

/* Absolutely positioned so the footage fills whatever height the row ends up
   with, and never sizes the row itself. Cover, and nothing more: the frame is
   never scaled past its container by a transform, which would soften it —
   sharpness comes from choosing the right file (see initAmbientVideos() in
   main.js). */
.hero__video{
  position:absolute;
  inset:0;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
}

/* The veil warms the footage and settles it behind the portrait without
   hiding it — a fifth or so of cream, a touch more at the top where it meets
   the header. */
.hero__media::after{
  content:"";
  position:absolute;
  inset:0;
  z-index:1;
  background:linear-gradient(180deg,var(--hero-veil-top),var(--hero-veil-bottom));
  pointer-events:none;
}

/* An arch, not a pasted rectangle. The top corners are elliptical — half the
   width across, 40% of the height down, which at 4/5 is the same length — so
   they meet in a true semicircle without triggering the proportional
   scale-down a 999px radius would, which flattens the bottom corners to
   nothing. The crown sits at y=2% of the photo and the head spans roughly the
   middle third, so the arch clears it at every size; center 22% spends the
   sliver the 4/5 crop takes off the photo mostly on the bottom. main.js
   removes the <img> if the file fails to load. */
.hero__portrait{
  position:relative;
  z-index:2;
  inline-size:min(44%, var(--hero-portrait));
  block-size:auto;
  aspect-ratio:4/5;
  object-fit:cover;
  object-position:center 22%;
  border-radius:50% 50% var(--radius) var(--radius) / 40% 40% var(--radius) var(--radius);
  box-shadow:0 0 0 1px var(--kh-copper), var(--shadow-soft);
}

/* --- "Book an appointment." ------------------------------------------------- */

/* The line that has to be noticed, and the hero's link on to booking. Three
   things move on it, all on one clock (--glint-cycle: a one-second sweep,
   then two seconds at rest), and none of them blinks or pulses:

     shine      a wide warm-gold band with a near-cream core sweeps across the
                copper-deep text from the start edge to the end
     glow       a copper edge and a gold halo that swell as the band crosses
                the middle and are gone at either end of it — they hold the
                lit letters against the cream while they are at their palest
     underline  a 1px copper rule that draws in under the line with the band,
                start to end, then fades before the next sweep

   Between sweeps the line is plain copper-deep, so it always reads.

   The shine is painted with background-clip:text, which clips to the
   element's own box — so the box hugs the text (fit-content) to keep the
   sweep true to the words, and is padded out by the depth of a descender and
   an italic overhang so neither is shaved off the glyphs. The matching
   negative margins give that room back to the layout. */
.hero__accent{
  position:relative;   /* the underline */
  inline-size:fit-content;
  padding-block:.16em;
  padding-inline:.12em;
  margin-block:-.16em;
  margin-inline:-.12em;
  color:var(--kh-copper-deep);
  border-radius:var(--radius-sm);   /* the focus ring's corners */
}

/* It is a link, so it gets a ring the keyboard can see. The padding above
   keeps the ring clear of the glyphs. */
.hero__accent:focus-visible{
  outline:2px solid var(--kh-copper-deep);
  outline-offset:3px;
}

/* Only where the text can actually be clipped to — elsewhere the plain colour
   above stands, rather than transparent glyphs over nothing. The image is
   three times the box: at 100% the band sits off the start edge, at 0% off
   the end, and every frame in between shows copper-deep but for the band.

   The first sweep waits for the entrance: the curtain clears at 1.5s and the
   accent has risen into place a little over a second later. */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .hero__accent{
    background-image:linear-gradient(100deg,
      var(--kh-copper-deep) 34%,
      var(--kh-shine-gold) 43%,
      var(--kh-shine-peak) 50%,
      var(--kh-shine-gold) 57%,
      var(--kh-copper-deep) 66%);
    background-size:300% 100%;
    background-position:100% 0;
    -webkit-background-clip:text;
    background-clip:text;
    -webkit-text-fill-color:transparent;
    animation:
      hero-shine var(--glint-cycle) ease-in-out 2.6s infinite,
      hero-glow var(--glint-cycle) ease-in-out 2.6s infinite;
  }

  [dir="rtl"] .hero__accent{
    background-image:linear-gradient(260deg,
      var(--kh-copper-deep) 34%,
      var(--kh-shine-gold) 43%,
      var(--kh-shine-peak) 50%,
      var(--kh-shine-gold) 57%,
      var(--kh-copper-deep) 66%);
    background-position:0% 0;
    animation-name:hero-shine-rtl, hero-glow;
  }
}

/* The underline. Its own box, so the text clip above does not apply to it.
   It sits in the padding under the line and spans the text (not the
   padding). At rest it is drawn to nothing; each sweep grows it from the
   start edge in step with the band, and it fades once the band has gone. */
.hero__accent::after{
  content:"";
  position:absolute;
  inset-inline:.12em;
  inset-block-end:.04em;
  block-size:var(--accent-rule);
  background:var(--kh-copper);
  transform:scaleX(0);
  transform-origin:left center;
  animation:hero-underline var(--glint-cycle) ease-in-out 2.6s infinite;
}

[dir="rtl"] .hero__accent::after{
  transform-origin:right center;
}

/* 33.33% of 3s is the one-second sweep; the remaining two seconds the band
   rests off the edge. Start and end frames are both plain copper-deep, so the
   loop has no seam. Keyframe offsets cannot read a variable: change
   --glint-cycle and the sweep scales with it. */
@keyframes hero-shine{
  0%{ background-position:100% 0; }
  33.33%, 100%{ background-position:0% 0; }
}

/* Under RTL the band enters from the right and leaves on the left. */
@keyframes hero-shine-rtl{
  0%{ background-position:0% 0; }
  33.33%, 100%{ background-position:100% 0; }
}

/* Rides with the sweep: nothing at either end of it, fullest as the band
   crosses the middle. Both shadows are in every frame so they interpolate. */
@keyframes hero-glow{
  0%, 33.33%, 100%{
    filter:drop-shadow(0 0 0 transparent) drop-shadow(0 0 0 transparent);
  }
  16.67%{
    filter:drop-shadow(0 0 .03em var(--kh-copper-deep)) drop-shadow(0 0 .3em var(--kh-glow));
  }
}

/* Draws with the band, holds a beat, fades over half a second. It is back to
   nothing while invisible, so the reset to the first frame never shows. The
   direction is transform-origin's, which RTL flips above. */
@keyframes hero-underline{
  0%{ transform:scaleX(0); opacity:1; }
  33.33%{ transform:scaleX(1); opacity:1; }
  40%{ transform:scaleX(1); opacity:1; }
  56.67%, 100%{ transform:scaleX(1); opacity:0; }
}

/* --- Entrance ------------------------------------------------------------- */

/* .can-reveal is added by JS, so the copy is never stuck invisible if the
   script fails to run. .is-revealed is flipped on the next frame. */
.hero.can-reveal .reveal{
  opacity:0;
  transform:translateY(18px);
  transition:opacity .8s var(--ease), transform .8s var(--ease);
}

.hero.can-reveal.is-revealed .reveal{
  opacity:1;
  transform:none;
}

.hero__content > .reveal:nth-child(1){transition-delay:0ms;}
.hero__content > .reveal:nth-child(2){transition-delay:90ms;}
.hero__content > .reveal:nth-child(3){transition-delay:180ms;}
.hero__content > .reveal:nth-child(4){transition-delay:270ms;}
.hero__content > .reveal:nth-child(5){transition-delay:360ms;}
.hero__content > .reveal:nth-child(6){transition-delay:450ms;}
.hero__content > .reveal:nth-child(7){transition-delay:540ms;}

/* The accent line takes its own step inside the h1's: it rises a little less
   far, and a beat behind the title line, so the eye lands on it last within
   the heading. */
.hero.can-reveal .hero__accent{
  opacity:0;
  transform:translateY(var(--accent-rise));
  transition:opacity .8s var(--ease), transform .8s var(--ease);
  transition-delay:240ms;
}

.hero.can-reveal.is-revealed .hero__accent{
  opacity:1;
  transform:none;
}


/* ==========================================================================
   NARROW HEADER  (< 640px)
   The bar cannot share a row with the logo, the language toggle and a button
   at 360px — something has to give, and it is the row, not the nav. So the
   header becomes two rows with the bar centred underneath. The first row
   keeps Book, compact and with a shorter label: the header is fixed, so on a
   long page it is the Book that stays within reach (on the home page it
   steps aside while the hero's own is on screen). Measured at 360px
   in Arabic, the tightest case, the row still has room to spare.

   --header-h goes up to match, because every page below the home page clears
   the header with it.
   ========================================================================== */

@media (max-width:639.98px){

  :root{
    --header-h:112px;
  }

  /* Two rows. The logo and the language toggle with Book share the first; the
     bar has the second to itself and the full width the gutters leave. The
     first row is Book's 44px tap height, and the gaps are trimmed to keep the
     whole header inside the 112px every page clears. */
  .site-header__inner{
    grid-template-columns:auto auto;
    grid-template-areas:
      "logo lang"
      "nav  nav";
    justify-content:space-between;
    gap:.3rem 1rem;
    padding-block:.4rem;
  }

  .site-header__logo{
    grid-area:logo;
  }

  .site-header__logo img{
    block-size:32px;
  }

  .site-header__actions{
    grid-area:lang;
    justify-self:end;
  }

  /* Compact, but never below a thumb's worth of height. The extra class
     outranks `.site-header .btn` in MOBILE below, which comes later in the
     file and would otherwise put the tablet padding back. */
  .site-header .btn.site-header__book{
    min-block-size:var(--tap-min);
    padding:.5rem .95rem;
    font-size:var(--step--1);
    letter-spacing:.14em;
  }

  /* Tracking pulls Arabic letters apart; the RTL rule in base.css zeroes it
     for every .btn, but the selector above outranks that one. */
  [dir="rtl"] .site-header .btn.site-header__book{
    letter-spacing:0;
  }

  .site-header__book-full{
    display:none;
  }

  .site-header__book-short{
    display:inline;
  }

  .site-nav{
    grid-area:nav;
    inline-size:100%;   /* the row the bar may scroll inside */
  }

  .site-nav__bar{
    /* Centred while the pills fit, filling the row and scrolling once they do
       not. Auto margins do both: they collapse to zero the moment the content
       is wider than the box. */
    margin-inline:auto;
    inline-size:fit-content;
    padding:.3rem .35rem;
  }

  .site-nav__link{
    padding:.4rem .75rem;
    font-size:var(--step--1);
    letter-spacing:.12em;
  }
}


/* ==========================================================================
   MOBILE  (< 1024px)
   ========================================================================== */

@media (max-width:1023.98px){

  /* --- Header --- */

  .site-header__logo img{
    block-size:38px;
  }

  .site-header .btn{
    padding:.75em 1.5em;
  }

  /* --- Hero: cover and profile ---
     Stacked, not layered: the video alone across the top, the arched
     portrait straddling its bottom edge, and the copy on plain cream under
     the portrait — nothing lies over the footage but the portrait's lower
     half, which sits below the hands. A first view shows the video, the
     portrait, the title, the specialism and "Book an appointment."; the
     hours, Book and the address line follow below.

     The media and the copy still share one grid cell (the scroll effect
     pins the media inside it — see HERO SCROLL). The media box runs the
     cell's height, but the video and its seam fill only the top
     --hero-video-h of it; the copy is padded down past the portrait. */

  .hero{
    --hero-video-h:clamp(
      (100svh - var(--header-h)) * var(--hero-video-share),
      100vw * var(--hero-video-band),
      (100svh - var(--header-h)) * var(--hero-video-share-max));
    grid-template-columns:1fr;
    min-block-size:100vh;
    min-block-size:100svh;
    isolation:isolate;
    overflow:hidden;
    overflow:clip;     /* ...without making a scroller, so the media can pin */
  }

  .hero__media{
    grid-area:1 / 1;
    align-self:stretch;
    align-items:start;
    overflow:visible;
    background:none;
  }

  /* Framed low: the top of the clip is bare wall. The frame is centred
     across, so it is the same picture in both directions — never mirrored. */
  .hero__video{
    inset-block-end:auto;
    block-size:var(--hero-video-h);
    object-position:50% var(--hero-video-y);
  }

  /* No veil here — only the seam: the last few percent of the video fade
     into the page's cream so its foot is not a hard line. */
  .hero__media::after{
    inset-block-end:auto;
    block-size:var(--hero-video-h);
    background:linear-gradient(180deg,
      transparent calc(100% - var(--hero-video-fade)),
      var(--kh-cream));
  }

  /* Centred on the video's bottom edge: at 4/5 the portrait is 1.25 times
     as tall as it is wide, so half its height is .625 of its width. */
  .hero__portrait{
    inline-size:var(--hero-portrait-stack);
    margin-block-start:calc(var(--hero-video-h) - var(--hero-portrait-stack) * .625);
  }

  .hero__content{
    grid-area:1 / 1;
    position:relative;
    z-index:2;
    display:grid;
    align-content:start;
    justify-items:center;
    text-align:center;
    background:none;
    padding-block:
      calc(var(--hero-video-h) + var(--hero-portrait-stack) * .625 + var(--hero-stack-gap))
      clamp(2rem,6svh,3.5rem);
    padding-inline:var(--gutter);
  }

  .hero__content h1{
    justify-self:stretch;
    font-size:var(--hero-title-sm);
  }

  .hero__specialist{
    margin-block:.3em .25em;
  }

  .hero__accent{
    margin-inline:auto;
  }

  /* The name leads the wide layout only: on a phone the first screen opens
     on the title. The rule is decoration the column can do without. */
  .hero__eyebrow,
  .hero__rule{
    display:none;
  }

  /* Compact: two short rows instead of four, each pair meeting at a dot on
     the centre line. */
  .hero__details{
    display:grid;
    grid-template-columns:auto auto;
    justify-content:center;
    column-gap:.6em;
    margin-block-start:.9rem;
    letter-spacing:.08em;
    line-height:1.6;
  }

  .hero__details li:nth-child(odd){
    justify-self:end;
  }

  .hero__details li:nth-child(even){
    justify-self:start;
  }

  .hero__details li:nth-child(even)::before{
    content:"·";
    margin-inline-end:.6em;
    color:var(--kh-copper);
  }

  .hero__cta{
    margin-block-start:clamp(1rem,3svh,1.75rem);
  }

  .hero__trust{
    margin-block-start:.7rem;
  }
}


/* A phone on its side has too little height for the stack at its usual
   share: the video becomes a shorter strip so the three lines stay above
   the fold. */
@media (max-width:1023.98px) and (orientation:landscape){

  .hero{
    --hero-video-share:var(--hero-video-share-flat);
    --hero-video-share-max:var(--hero-video-share-flat);
  }
}


/* ==========================================================================
   HERO SCROLL  —  phones and tablets held upright
   The media pins under the header while the copy scrolls on as usual: the
   video and the portrait lift away together and fade, and they are gone by
   the time the page has scrolled the length of that lift. From there the
   hero scrolls off as one and the page carries on to the work band.

   The media is sticky inside its grid cell and stretched to the cell's
   height less the lift, so it stays pinned for exactly the lift's length
   whatever the phone, the language or the copy. initHeroScroll() in
   main.js turns the scroll position across that distance into --hero-p,
   0 to 1, on .hero — one property, written at most once a frame — and every
   move below is derived from it. --hero-m is the media's own progress: 0 to 1
   over the first --hero-fade-end of the run, then held at 1. With the run
   the same length as the lift, the media rises a little faster than the
   copy, so the copy never passes over the footage.

   Only transform and opacity change, so nothing here costs layout. The video
   and the portrait are moved separately rather than as one layer: a transform
   on the media box would unpin nothing but would take the seam and the video
   with it as one flat picture, and scale the video. The lift is a length
   rather than a percentage so all three travel the same distance.

   The copy needs nothing: it is in normal flow and simply scrolls.

   .hero--pin is added by main.js, so without the script the media scrolls
   away with the page like everything else. Not under reduced motion, and not
   in landscape, where a phone's screen is too short to be worth pinning.
   ========================================================================== */

@media (max-width:1023.98px) and (orientation:portrait) and (prefers-reduced-motion:no-preference){

  .hero--pin{
    --hero-m:min(calc(var(--hero-p, 0) / var(--hero-fade-end)), 1);
    --hero-lift:calc((100svh - var(--header-h)) * var(--hero-rise) * -1);
  }

  /* The margin is the run: the box ends one lift short of its cell. */
  .hero--pin .hero__media{
    position:sticky;
    inset-block-start:var(--header-h);
    margin-block-end:calc(var(--hero-lift) * -1);
  }

  /* Translated only, never scaled — see .hero__video. The seam (::after)
     travels with the video, so its foot never shows as a hard line. */
  .hero--pin .hero__video,
  .hero--pin .hero__media::after{
    transform:translateY(calc(var(--hero-m) * var(--hero-lift)));
    opacity:calc(1 - var(--hero-m));
    will-change:transform, opacity;
  }

  .hero--pin .hero__portrait{
    transform:
      translateY(calc(var(--hero-m) * var(--hero-lift)))
      scale(calc(1 - var(--hero-m) * var(--hero-shrink)));
    opacity:calc(1 - var(--hero-m));
    will-change:transform, opacity;
  }
}

/* Body scroll lock, held only by the intro curtain now that there is no menu
   panel. position:fixed rather than overflow:hidden alone, which iOS Safari
   scrolls straight through. Fixing the body also collapses it to the top of
   the document, so main.js holds the page in place by writing the scroll
   offset to `top` and restores it when the lock is released. */
body.is-intro-playing{
  position:fixed;
  inset-inline:0;
  inline-size:100%;
  overflow:hidden;
}


/* ==========================================================================
   IMAGE BANDS — shared treatment
   The two things that make a block of photography read as part of this palette
   rather than as pasted-in images: cream seams that hand off to the sections
   either side, and a warm veil over the pictures themselves.

   Both galleries opt in through these classes — the home-page strip
   (.gallery, below) and the about-page masonry (.about-work__grid, styled in
   about.css). They live here because layout.css is the one stylesheet every
   page loads, so about.css never has to restate them.

   Seam depth comes from --band-fade, which a host can override locally.
   ========================================================================== */

.band-fade{
  position:relative;
}

.band-fade::before,
.band-fade::after{
  content:"";
  position:absolute;
  inset-inline:0;
  block-size:var(--band-fade);
  z-index:2;
  pointer-events:none;
}

.band-fade::before{
  inset-block-start:0;
  background:linear-gradient(to bottom, var(--kh-cream), transparent);
}

.band-fade::after{
  inset-block-end:0;
  background:linear-gradient(to bottom, transparent, var(--kh-cream));
}

/* Wraps one image. The veil is a pseudo-element rather than a filter so a host
   can lift it on hover without touching the photograph itself. */
.band-veil{
  position:relative;
  overflow:hidden;
}

.band-veil::after{
  content:"";
  position:absolute;
  inset:0;
  background-image:linear-gradient(var(--kh-veil-soft), var(--kh-veil-soft));
  pointer-events:none;
  transition:opacity .6s var(--ease);
}

.band-veil img{
  transition:filter .6s var(--ease);
}

/* Hover: the veil lifts and the photograph gains about 3% brightness. Two
   small moves that read as one — neither is meant to be noticeable alone.
   opacity and filter are both composited, so nothing here triggers layout.
   Pointer devices only; there is no hover state to reason about on touch. */
@media (hover:hover){
  .band-veil:hover::after{
    opacity:.55;
  }

  .band-veil:hover img{
    filter:brightness(1.03);
  }
}


/* ==========================================================================
   GALLERY TRACKS
   The home-page work strip and the about-page gallery are the same component:
   photographs edge to edge as one continuous band, full-bleed past .wrap,
   drifting on their own and scrollable by hand. Both take .band-fade and
   .band-veil from the block above. What follows describes the about-page
   band, which loops; the home-page one is finite and drifts once to its end
   — see .gallery--finite at the end of this block for what it changes.

   Two mechanisms move the band, and only ever one at a time:

     idle    a CSS transform animation on the track, 0 → -50% of its own
             width, linear and infinite. It runs on the compositor, which is
             the whole point — driving the drift from script by writing
             scrollLeft each frame steps visibly instead of gliding, because a
             60s cycle advances about a third of a pixel per frame and a
             scroll offset that rounds swallows most of them.

     manual  native overflow scrolling, with snapping, once the visitor
             touches it. initGalleryTracks() in main.js hands one over to the
             other on the first interaction and never hands back.

   Snapping belongs strictly to the second: a snap point pulls after every
   scroll, and against a continuous drift it pulls on every frame.

   What this file owns is the shape and the affordances: the card sizing, the
   hidden scrollbar, the cream seams, the drift itself, and the cursor.

   The one constraint the loop places on the layout: a single copy of the set
   must never be narrower than the viewport, or the wrap would open a gap of
   cream at the trailing edge. That is why --gallery-card is a share of the
   viewport at every breakpoint and never a capped length — three cards at 34vw
   are 102vw, and the 2vw of slack is what the loop rides on. The about-page
   gallery holds eight, so it clears this many times over.
   ========================================================================== */

.gallery{
  /* Deeper seam than the shared default — this band is full-bleed and has more
     height to fade across. */
  --band-fade:var(--gallery-fade);
  background:var(--kh-cream);
}

.gallery__viewport{
  overflow-x:auto;
  overflow-y:hidden;

  /* The scrollbar is hidden, not the scrolling: this is still a focusable
     scroll container that answers to the arrow keys, and a grey bar under a
     full-bleed band of photographs would be the loudest thing on the page. */
  scrollbar-width:none;
  -ms-overflow-style:none;

  /* Keeps a fling inside the band instead of handing the overscroll to the
     page, and stops a horizontal swipe from triggering back-navigation. */
  overscroll-behavior-inline:contain;

  /* The leading and trailing seams. A mask rather than the two gradient
     overlays this used to carry: an absolutely-positioned child of a scroll
     container scrolls with the content, so the seams would have travelled off
     the edges with the first swipe. A mask is painted against the border box
     and stays put. Only the alpha of the stops matters — the colour is any
     opaque value, and taking it from a token keeps the hex out of here. */
  -webkit-mask-image:linear-gradient(
    to right,
    transparent,
    var(--kh-ink) var(--gallery-edge),
    var(--kh-ink) calc(100% - var(--gallery-edge)),
    transparent);
  mask-image:linear-gradient(
    to right,
    transparent,
    var(--kh-ink) var(--gallery-edge),
    var(--kh-ink) calc(100% - var(--gallery-edge)),
    transparent);

  cursor:grab;
}

.gallery__viewport::-webkit-scrollbar{
  display:none;
}

/* Inset so the mask above does not fade the ring away at the edges. */
.gallery__viewport:focus-visible{
  outline:2px solid var(--kh-copper-deep);
  outline-offset:-4px;
}

.gallery__viewport.is-dragging{
  cursor:grabbing;
}

/* --- Idle: the drift ------------------------------------------------------
   -50% of the track's own width is exactly one copy of the set, so the last
   frame of the cycle is pixel-identical to the first and the repeat has no
   seam. Transform only, so nothing here touches layout or the scroll offset —
   which is what leaves the scroll position free for the visitor to take. */

.gallery__track{
  display:flex;
  inline-size:max-content;
  animation:gallery-drift var(--gallery-drift) linear infinite;
  will-change:transform;
}

@keyframes gallery-drift{
  to{ transform:translateX(-50%); }
}

/* Arabic reads the other way, so the band drifts the other way with it. The
   flex row already lays the cards out from the right and the track sits
   against the viewport's right edge with its overflow to the left, so the
   travel is the same distance with the sign flipped. */
[dir="rtl"] .gallery__track{
  animation-name:gallery-drift-rtl;
}

@keyframes gallery-drift-rtl{
  to{ transform:translateX(50%); }
}

/* Ambience — it holds still for anyone actually looking at it. Hover is
   pointer-only: on touch it sticks after a tap, and a tap has handed the
   track over anyway. */
@media (hover:hover){
  .gallery__viewport:hover .gallery__track{
    animation-play-state:paused;
  }
}

.gallery__viewport:focus-within .gallery__track{
  animation-play-state:paused;
}

/* --- Manual: the handover -------------------------------------------------
   Two classes, because the two halves of the handover cannot happen at the
   same moment.

   .is-manual is set by main.js on the first interaction. It ends the drift, in
   the same frame that the script converts the drift's transform into the
   equivalent scroll offset — so the band carries on from exactly where it had
   reached. The RTL selector is repeated because [dir="rtl"] .gallery__track
   outranks the bare class and would otherwise put its animation straight back.

   .is-snapping is set later, when that first gesture finishes. Turning snap on
   at a position that is not already a snap point makes the browser re-snap on
   the spot, and the drift stops wherever it stops — measured at 150px of a
   430px card. At handover that lurch would come out of nowhere, the instant a
   hand touched the band; at the end of a gesture the very same pull is the
   settle that snapping is for. It also comes off again for the duration of a
   pointer drag, which drives scrollLeft directly and would otherwise be snapped
   card to card on every pointermove instead of following the cursor.

   Proximity rather than mandatory: it settles a swipe without ever refusing to
   leave a card alone. The wrap is safe against it either way — one set is a
   whole number of cards, so subtracting one lands on the same snap grid. */

.gallery__viewport.is-manual .gallery__track,
[dir="rtl"] .gallery__viewport.is-manual .gallery__track{
  animation-name:none;
  will-change:auto;
}

.gallery__viewport.is-snapping{
  scroll-snap-type:x proximity;
}

.gallery__item{
  flex:none;
  inline-size:var(--gallery-card);
  aspect-ratio:4 / 5;
  scroll-snap-align:start;
  /* A drag across the band should never leave a selection highlight behind. */
  user-select:none;
  -webkit-user-select:none;
}

.gallery__item img{
  display:block;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  -webkit-user-drag:none;
}

/* Fewer, wider cards as the viewport narrows: at the desktop share a phone
   would be showing three slivers. Each value still keeps one copy of the set
   wider than the viewport — see the note at the top of this block. */
@media (min-width:768px) and (max-width:1023.98px){
  .gallery{
    --gallery-card:44vw;
  }
}

@media (max-width:767.98px){
  .gallery{
    --gallery-card:58vw;
  }
}

/* --- The finite band (home page) -------------------------------------------
   The home-page strip is not a loop. It holds four pieces of work and a
   "See more" card, written once, and it drifts a single time from its start to
   its end — then stops, with the card fully in view. The about-page gallery is
   the endless one above and nothing here touches it.

   Same compositor-only drift, different shape. The endless keyframes above
   travel a fixed -50%; this one travels a measured distance, so its two ends
   are custom properties that initGalleryTracks() writes from the track and the
   band's real widths, and rewrites on resize or a change of direction:

     --drift-from   where this run starts (0, or wherever a resize caught it)
     --drift-to     the end: track width less band width, signed for the
                    writing direction — negative LTR, positive RTL
     --drift-time   that distance over --gallery-speed

   forwards holds the last frame, so the band rests at the end rather than
   springing back. Nothing runs until main.js adds .is-drifting, which it does
   only once the band has come into view. The animation properties are set as
   longhands on purpose: the shorthand would reset animation-play-state, and
   the hover, focus and off-screen pauses all work through that one property. */

.gallery--finite{
  --drift-from:0px;
  --drift-to:0px;
  --drift-time:0s;
}

/* No endless drift. animation-name rather than the shorthand for the same
   reason as below — the RTL selector ties with the hover pause on specificity
   and, coming later, would otherwise reset it. */
.gallery--finite .gallery__track,
[dir="rtl"] .gallery--finite .gallery__track{
  animation-name:none;
  will-change:auto;
}

.gallery--finite .gallery__viewport.is-drifting .gallery__track{
  animation-name:gallery-run;
  animation-duration:var(--drift-time);
  animation-timing-function:linear;
  animation-fill-mode:forwards;
  animation-iteration-count:1;
  will-change:transform;
}

@keyframes gallery-run{
  from{ transform:translateX(var(--drift-from)); }
  to{ transform:translateX(var(--drift-to)); }
}

/* Scrolled out of sight mid-run, it waits for the visitor to come back rather
   than finishing to an empty screen. */
.gallery--finite .gallery__viewport.is-offscreen .gallery__track{
  animation-play-state:paused;
}

/* The trailing seam would dim the one card that asks for a tap, and there is
   nothing past it to fade into. The leading seam stays. The gradient runs
   from the start edge, so under RTL it is turned round. */
.gallery--finite .gallery__viewport{
  -webkit-mask-image:linear-gradient(to right, transparent, var(--kh-ink) var(--gallery-edge));
  mask-image:linear-gradient(to right, transparent, var(--kh-ink) var(--gallery-edge));
}

[dir="rtl"] .gallery--finite .gallery__viewport{
  -webkit-mask-image:linear-gradient(to left, transparent, var(--kh-ink) var(--gallery-edge));
  mask-image:linear-gradient(to left, transparent, var(--kh-ink) var(--gallery-edge));
}

/* A video is a photograph that moves: same box, same crop, and the same
   hover lift the band gives its images. */
.gallery__video{
  display:block;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  transition:filter .6s var(--ease);
}

@media (hover:hover){
  .band-veil:hover .gallery__video{
    filter:brightness(1.03);
  }
}

/* --- "See more" -------------------------------------------------------------
   One more card in the row, the same 4/5 as the work beside it so the band
   stays even, and the whole of it one link on to the about page. */

.gallery__more{
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:center;
  gap:.75rem;
  background:var(--kh-blush);
  border:1px solid var(--kh-hairline);
  color:var(--kh-copper-deep);
  text-align:center;
  transition:
    background-color .35s var(--ease),
    box-shadow .35s var(--ease),
    transform .35s var(--ease);
  -webkit-user-drag:none;
}

.gallery__more-arrow{
  inline-size:var(--more-arrow);
  block-size:var(--more-arrow);
  transition:transform .35s var(--ease);
}

.gallery__more-label{
  font-family:var(--font-display);
  font-size:var(--step-1);
  line-height:1.2;
}

/* The track runs the other way, so the arrow does too. */
[dir="rtl"] .gallery__more-arrow{
  transform:scaleX(-1);
}

[dir="rtl"] .gallery__more-label{
  font-family:var(--font-ar);
}

@media (hover:hover){
  .gallery__more:hover{
    background-color:var(--kh-more-hover);
  }

  .gallery__more:hover .gallery__more-arrow{
    transform:translateX(var(--more-nudge));
  }

  [dir="rtl"] .gallery__more:hover .gallery__more-arrow{
    transform:translateX(calc(var(--more-nudge) * -1)) scaleX(-1);
  }
}

/* The site's press: down a pixel onto a shallower edge, quickly. */
.gallery__more:active{
  transform:translateY(1px);
  box-shadow:var(--press-down);
  transition-duration:.12s;
}

/* Inset, like the band's own ring: the viewport clips anything outside the
   card, and the neighbouring photograph would cover the rest. */
.gallery__more:focus-visible{
  outline:2px solid var(--kh-copper-deep);
  outline-offset:-4px;
}


/* ==========================================================================
   CONTACT ROW
   Two links directly beneath the work strip. Deliberately not a .section:
   the band and the row are meant to read as one unit, so it takes its own
   tighter padding rather than the full section rhythm.
   ========================================================================== */

.contact-row{
  padding-block:clamp(2rem,5vw,3.5rem);
  background:var(--kh-cream);
}

.contact-row__actions{
  display:flex;
  align-items:center;
  justify-content:center;
  gap:1rem;
}

/* flex:none so the mark keeps its size when the label wraps or the button
   stretches. Nothing here is direction-dependent, so both glyphs stay upright
   under RTL — only the icon/label pair swaps sides, which flexbox does. */
.contact-row__icon{
  flex:none;
  inline-size:var(--icon-sm);
  block-size:var(--icon-sm);
}

/* Phones: stack full-width. align-items:stretch overrides the centring above,
   which would otherwise leave the inline-flex buttons at their intrinsic
   width once the row turns into a column. */
@media (max-width:559.98px){
  .contact-row__actions{
    flex-direction:column;
    align-items:stretch;
  }
}

/* ==========================================================================
   SITE FOOTER
   The same markup on every public page. Four columns on a wide screen —
   the mark with one factual line, then Pages, Information and Contact — and a
   bottom bar under a hairline with the copyright and the two legal links.

     ≥ 1024px    four columns
     768–1023    two columns, the mark spanning both on the first row
     < 768       one centred column, same order

   Blush rather than cream, so it reads as the end of the page without a rule
   of its own above it. Everything is grid, flex and logical properties, so
   the column order and the text alignment both mirror under RTL with no
   override.
   ========================================================================== */

.site-footer{
  padding-block:clamp(2.5rem,6vw,4rem);
  background:var(--kh-blush);
  color:var(--kh-muted);
}

.site-footer__grid{
  display:grid;
  grid-template-columns:1.4fr repeat(3,1fr);
  gap:2.5rem clamp(1.5rem,3vw,3rem);
  align-items:start;
}

/* --- Mark ----------------------------------------------------------------- */

.site-footer__logo{
  display:block;
  block-size:var(--footer-logo);
  inline-size:auto;
}

.site-footer__tagline{
  max-inline-size:26ch;
  margin-block-start:1.1rem;
  font-size:var(--step--1);
  line-height:1.8;
}

/* --- Columns -------------------------------------------------------------- */

/* An h2 for the outline, dressed as the site's eyebrow. The family has to be
   put back: h2 carries the display face from base.css. Under RTL the
   [dir="rtl"] h2 rule outranks this one and sets Almarai, which is right. */
.site-footer__heading{
  margin-block-end:1.1rem;
  font-family:var(--font-body);
  font-weight:500;
  line-height:1.4;
}

.site-footer__list{
  display:grid;
  gap:.55rem;
  list-style:none;
}

.site-footer__link{
  display:inline-flex;
  align-items:center;
  gap:.55rem;
  font-size:var(--step-0);
  color:var(--kh-espresso);
  transition:color .25s var(--ease);
}

@media (hover:hover){
  .site-footer__link:hover{
    color:var(--kh-copper);
  }
}

.site-footer__link:focus-visible,
.site-footer__bar-link:focus-visible{
  outline:2px solid var(--kh-copper-deep);
  outline-offset:3px;
  border-radius:var(--radius-sm);
}

/* flex:none so the mark keeps its size if the label ever wraps. */
.site-footer__icon{
  flex:none;
  inline-size:var(--icon-sm);
  block-size:var(--icon-sm);
}

/* The number is Latin digits in both languages. dir="ltr" on the link keeps
   "+971" at the front of it inside an Arabic paragraph; this only stops a
   narrow column from breaking it across two lines. */
.site-footer__phone{
  white-space:nowrap;
}

/* Almarai's space is narrow enough that the digit groups run together. */
[dir="rtl"] .site-footer__phone{
  word-spacing:.25em;
}

/* Hours and "Ladies only", under the Information links. Facts, not links, so
   they sit a step down and in the muted ink. */
.site-footer__facts{
  margin-block-start:1.4rem;
  font-size:var(--step--1);
  line-height:1.9;
  list-style:none;
}

/* --- Bottom bar ----------------------------------------------------------- */

.site-footer__bar{
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:space-between;
  gap:.75rem 1.5rem;
  margin-block-start:clamp(2.25rem,5vw,3.25rem);
  padding-block-start:1.25rem;
  border-block-start:1px solid var(--kh-hairline);
  font-size:var(--step--1);
  color:var(--kh-muted);
}

.site-footer__bar-links{
  display:flex;
  gap:1.5rem;
  list-style:none;
}

.site-footer__bar-link{
  color:var(--kh-muted);
  transition:color .25s var(--ease);
}

@media (hover:hover){
  .site-footer__bar-link:hover{
    color:var(--kh-copper);
  }
}

/* --- Tablet: two columns, the mark across both ------------------------- */

@media (min-width:768px) and (max-width:1023.98px){
  .site-footer__grid{
    grid-template-columns:repeat(2,1fr);
  }

  .site-footer__brand{
    grid-column:1 / -1;
  }

  .site-footer__tagline{
    max-inline-size:none;
  }
}

/* --- Phones: one centred column ---------------------------------------- */

@media (max-width:767.98px){
  .site-footer__grid{
    grid-template-columns:1fr;
    justify-items:center;
    gap:2.25rem;
    text-align:center;
  }

  .site-footer__logo{
    margin-inline:auto;
  }

  .site-footer__tagline{
    margin-inline:auto;
  }

  .site-footer__list{
    justify-items:center;
  }

  .site-footer__bar{
    flex-direction:column;
    justify-content:center;
    text-align:center;
  }
}


/* ==========================================================================
   RTL
   Arabic gets no tracking. Everything else in the header mirrors on its own:
   the grid columns, the flex rows and the bar's own overflow all follow the
   writing direction, and the indicator is positioned from a measurement.
   ========================================================================== */

[dir="rtl"] .site-nav__link,
[dir="rtl"] .lang-toggle__btn,
[dir="rtl"] .hero__details,
[dir="rtl"] .btn{
  letter-spacing:0;
}


/* ==========================================================================
   MOTION PREFERENCES
   ========================================================================== */

@media (prefers-reduced-motion:reduce){
  /* No curtain at all, and nothing in its place — the hero is simply there.
     Suppressed here rather than in script so it is never painted for a frame
     first. main.js removes the node on the same condition. */
  .intro{
    display:none;
  }

  /* No brightening on either band; the veil may still lift. */
  .band-veil img,
  .band-veil:hover img,
  .band-veil .gallery__video,
  .band-veil:hover .gallery__video{
    filter:none;
    transition:none;
  }

  /* Neither gallery drifts. The global rule in base.css only shortens an
     animation to nothing, which would land the track on its end frame — so
     the name is dropped instead, and the RTL selector repeated because it
     outranks the bare class. Both tracks stay scrollable by hand: that is
     movement the visitor asked for, not movement done to them. main.js never
     sets .is-drifting here; the last selector is for a preference switched on
     mid-run. */
  .gallery__track,
  [dir="rtl"] .gallery__track,
  .gallery--finite .gallery__viewport.is-drifting .gallery__track{
    animation-name:none;
    will-change:auto;
  }

  /* "See more" keeps its colour change and loses its travel, the way the
     buttons do in base.css. */
  .gallery__more:active{
    transform:none;
  }

  .gallery__more:hover .gallery__more-arrow{
    transform:none;
  }

  [dir="rtl"] .gallery__more:hover .gallery__more-arrow{
    transform:scaleX(-1);
  }

  /* The header's Book swaps with the hero's in one frame. transition:none
     rather than the global near-zero duration, which would leave the
     visibility delay in place. */
  .site-header__book,
  .site-header__book.is-tucked{
    transition:none;
  }

  /* Show the hero immediately — no fade, no travel. */
  .hero.can-reveal .reveal,
  .hero.can-reveal.is-revealed .reveal,
  .hero.can-reveal .hero__accent,
  .hero.can-reveal.is-revealed .hero__accent{
    opacity:1;
    transform:none;
    transition:none;
  }

  /* No shine, no glow and no moving underline: the accent line is plain
     copper-deep text with its underline drawn and still, so it still reads as
     a link. The gradient goes too, not just the animation — the global rule
     in base.css would otherwise land it on a frame of its choosing. The RTL
     selectors are repeated because they outrank the bare ones. main.js never
     starts the video here either. */
  .hero__accent,
  [dir="rtl"] .hero__accent{
    animation:none;
    background:none;
    -webkit-text-fill-color:currentColor;
    filter:none;
  }

  .hero__accent::after,
  [dir="rtl"] .hero__accent::after{
    animation:none;
    transform:none;
    opacity:1;
  }
}
