/* ==========================================================================
   H&L Menuiserie — one stylesheet, two languages, two directions.

   Written with CSS logical properties (margin-inline, inset-inline, text-align:
   start) throughout, so the Arabic RTL page needs no override file and no
   duplicated rules. If you add a rule here, use logical properties too — a
   stray `margin-left` will silently break /ar/.

   Palette sampled from media-raw/logo/logo.jpg.
   Mobile first: the base rules are the phone layout, media queries add width.
   ========================================================================== */

:root {
  /* --- colour, from the logo ------------------------------------------- */
  --ink:          #16100B;   /* logo ground, hero + contact background */
  --ink-soft:     #241A12;
  --noyer:        #4F321F;   /* dark walnut */
  --noyer-clair:  #956839;   /* mid wood */
  --laiton:       #CA9C5B;   /* brass — the only accent */
  --laiton-clair: #F1D69E;
  --papier:       #FAF7F2;   /* page */
  --sable:        #F1EAE0;   /* alternate section */
  --texte:        #241C16;
  --texte-doux:   #5C4F45;
  --ligne:        rgba(36, 28, 22, .13);
  --ligne-forte:  rgba(36, 28, 22, .26);

  /* WhatsApp green appears nowhere except the CTA, which is what makes the CTA
     impossible to mistake for anything else on the page.

     This is a deeper green than WhatsApp's own #25D366. White text on the brand
     green measures 2.86:1, which fails WCAG AA outright — unacceptable on the
     one element the whole site exists to get clicked. #0E8040 measures 5.02:1,
     still reads unmistakably as the WhatsApp green family, and sits better
     against the warm brass palette than the neon original did. */
  --wa:           #0E8040;
  --wa-hover:     #0B6B35;

  /* --- type ------------------------------------------------------------- */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
                  "Times New Roman", serif;
  --font-body:    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                  "Helvetica Neue", Arial, sans-serif;

  --step--1: clamp(.82rem, .79rem + .12vw, .88rem);
  --step-0:  clamp(1rem, .96rem + .2vw, 1.09rem);
  --step-1:  clamp(1.2rem, 1.12rem + .4vw, 1.45rem);
  --step-2:  clamp(1.5rem, 1.34rem + .8vw, 2.05rem);
  --step-3:  clamp(1.95rem, 1.6rem + 1.75vw, 3.1rem);
  --step-4:  clamp(2.05rem, 1.5rem + 2.75vw, 4rem);

  /* --- space ------------------------------------------------------------ */
  --gap:      clamp(1rem, .8rem + 1vw, 1.6rem);
  --gutter:   clamp(1.15rem, .8rem + 2vw, 2.5rem);
  --section:  clamp(3.5rem, 2.4rem + 5.2vw, 7rem);
  --wrap:     74rem;
  --radius:   3px;   /* joinery, not bubblegum: almost square corners */
}

/* Arabic gets its own faces. Both are system fonts on the platforms that
   matter in Morocco, so this still costs zero bytes over the network. */
html[lang="ar"] {
  --font-display: "Noto Naskh Arabic", "Amiri", "Traditional Arabic",
                  "Times New Roman", serif;
  --font-body:    "Noto Sans Arabic", "Segoe UI", Tahoma, Arial, sans-serif;
}

/* ==========================================================================
   reset
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-block-start: 5rem;
}

body {
  margin: 0;
  background: var(--papier);
  color: var(--texte);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.12;
  margin: 0;
  letter-spacing: -.015em;
}
html[lang="ar"] h1,
html[lang="ar"] h2,
html[lang="ar"] h3 { letter-spacing: 0; line-height: 1.35; }

p { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, picture, svg { display: block; max-width: 100%; }
img { height: auto; background: var(--sable); }

a { color: inherit; }

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: 0;
  background: none;
}

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

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

.skip-link {
  position: absolute;
  inset-block-start: -100%;
  inset-inline-start: 1rem;
  z-index: 100;
  padding: .7rem 1.1rem;
  background: var(--ink);
  color: var(--laiton-clair);
  text-decoration: none;
}
.skip-link:focus { inset-block-start: 1rem; }

.wrap {
  inline-size: min(100% - (var(--gutter) * 2), var(--wrap));
  margin-inline: auto;
}

/* ==========================================================================
   header
   ========================================================================== */

.site-head {
  position: sticky;
  inset-block-start: 0;
  z-index: 40;
  background: var(--ink);
  border-block-end: 1px solid rgba(202, 156, 91, .22);
}

.site-head__inner {
  display: flex;
  align-items: center;
  gap: var(--gap);
  min-block-size: 4.1rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: .7rem;
  text-decoration: none;
  color: var(--papier);
  margin-inline-end: auto;
}

.brand__mark img {
  inline-size: 44px;
  block-size: 44px;
  border-radius: 50%;
  background: var(--ink);
}

.brand__text { display: flex; flex-direction: column; line-height: 1.15; }

.brand__name {
  font-family: var(--font-display);
  font-size: 1.06rem;
  letter-spacing: .01em;
}

.brand__tagline {
  font-size: .66rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--laiton);
}
html[lang="ar"] .brand__tagline { letter-spacing: 0; text-transform: none; font-size: .72rem; }

.site-nav { display: none; gap: 1.6rem; }

.site-nav a {
  position: relative;
  text-decoration: none;
  font-size: var(--step--1);
  letter-spacing: .04em;
  color: rgba(250, 247, 242, .74);
  padding-block: .35rem;
  transition: color .25s;
}
.site-nav a::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 1px;
  background: var(--laiton);
  transform: scaleX(0);
  /* transform-origin has no logical keywords, so this is one of the few places
     that needs a physical value and an explicit RTL flip. */
  transform-origin: left;
  transition: transform .3s;
}
html[dir="rtl"] .site-nav a::after { transform-origin: right; }
.site-nav a:hover { color: var(--laiton-clair); }
.site-nav a:hover::after { transform: scaleX(1); }

.lang-switch {
  flex: none;
  text-decoration: none;
  font-size: var(--step--1);
  letter-spacing: .05em;
  color: var(--laiton-clair);
  border: 1px solid rgba(202, 156, 91, .4);
  padding: .34rem .8rem;
  border-radius: var(--radius);
  transition: background .25s, border-color .25s;
}
.lang-switch:hover { background: rgba(202, 156, 91, .13); border-color: var(--laiton); }

@media (min-width: 62rem) {
  .site-nav { display: flex; }
}

/* ==========================================================================
   buttons
   ========================================================================== */

.btn {
  --btn-bg: var(--wa);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .6rem;
  padding: .85rem 1.4rem;
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: #fff;
  font-weight: 600;
  font-size: var(--step-0);
  letter-spacing: .01em;
  text-decoration: none;
  text-align: center;
  transition: background .22s, transform .22s, box-shadow .22s;
  box-shadow: 0 1px 0 rgba(0, 0, 0, .12);
}
.btn:hover { --btn-bg: var(--wa-hover); transform: translateY(-1px); box-shadow: 0 6px 18px rgba(31, 175, 90, .28); }
.btn:active { transform: translateY(0); }

.btn--lg { padding: 1.05rem 1.8rem; font-size: var(--step-1); }
.btn--block { inline-size: 100%; }

.btn--ghost {
  --btn-bg: transparent;
  color: var(--noyer);
  border: 1px solid var(--ligne-forte);
  box-shadow: none;
  font-weight: 500;
}
.btn--ghost:hover {
  --btn-bg: transparent;
  border-color: var(--laiton);
  color: var(--noyer-clair);
  box-shadow: none;
}

.wa-icon { inline-size: 1.25em; block-size: 1.25em; flex: none; }

/* ==========================================================================
   section furniture
   ========================================================================== */

.section { padding-block: var(--section); }
.section:nth-of-type(even) { background: var(--sable); }

.eyebrow {
  display: flex;
  align-items: center;
  gap: .7rem;
  font-size: .72rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--noyer-clair);
  margin-block-end: .9rem;
}
.eyebrow::after {
  content: "";
  flex: 1;
  block-size: 1px;
  background: linear-gradient(to inline-end, var(--laiton), transparent);
  max-inline-size: 7rem;
}
html[lang="ar"] .eyebrow { letter-spacing: 0; text-transform: none; font-size: .85rem; }

.section__title {
  font-size: var(--step-3);
  max-inline-size: 20ch;
  text-wrap: balance;
}

.section__intro {
  margin-block-start: 1.1rem;
  max-inline-size: 58ch;
  color: var(--texte-doux);
  font-size: var(--step-1);
  line-height: 1.6;
}

/* ==========================================================================
   scroll reveal — pure CSS, no JavaScript

   Uses scroll-driven animations where the browser supports them. Everywhere
   else the content is simply visible from the start, which is the correct
   failure mode: nobody ever sees a blank section because a script did not run.
   ========================================================================== */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* Section headings are selected here rather than marked up with
       data-reveal, so adding a section never means remembering the attribute. */
    [data-reveal],
    .section .eyebrow,
    .section .section__title,
    .section .section__intro,
    .video {
      animation: reveal linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 52%;
    }

    /* The three heading lines arrive in order, close together. */
    .section .section__title { animation-range: entry 0% entry 60%; }
    .section .section__intro { animation-range: entry 0% entry 68%; }

    .step:nth-child(even) { animation-range: entry 0% entry 64%; }

    /* Phones get a shorter lift. The same 1.4rem that reads as a gentle rise on
       a desktop reads as a lurch in a 380px viewport where the element crosses
       the whole screen in a thumb-flick. */
    @media (max-width: 48rem) {
      [data-reveal] { animation-name: reveal-short; }
    }

    /* Stagger inside a grid: the tiles share a scroll range, so the only way to
       make them arrive in sequence is to let the later ones finish later.
       Four steps, then it repeats — enough to read as a cascade, not as a wave. */
    .grid__item:nth-child(4n + 2) { animation-range: entry 0% entry 62%; }
    .grid__item:nth-child(4n + 3) { animation-range: entry 0% entry 72%; }
    .grid__item:nth-child(4n + 4) { animation-range: entry 0% entry 82%; }

    .review:nth-child(2) { animation-range: entry 0% entry 66%; }
    .review:nth-child(3) { animation-range: entry 0% entry 80%; }

    .svc__row:nth-child(even) { animation-range: entry 0% entry 64%; }

    /* The hero photo settles as the page starts moving: a small, slow scale-down
       rather than a parallax shift, so nothing detaches from its caption. */
    .hero__img {
      animation: hero-settle linear both;
      animation-timeline: view();
      animation-range: entry 100% exit 100%;
    }
  }
}

@keyframes reveal {
  from { opacity: 0; transform: translateY(1.4rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes reveal-short {
  from { opacity: 0; transform: translateY(.7rem); }
  to   { opacity: 1; transform: none; }
}

@keyframes hero-settle {
  from { transform: scale(1.06); }
  to   { transform: scale(1); }
}

/* --------------------------------------------------------------------------
   reading-progress line under the header

   `scroll()` resolves to the nearest scrollport — the document here. The bar
   grows from the leading edge, which flips automatically on /ar/ because
   transform-origin is overridden for RTL below.
   -------------------------------------------------------------------------- */

.progress {
  block-size: 2px;
  background: var(--laiton);
  transform: scaleX(0);
  transform-origin: left;   /* no logical keyword exists; RTL override follows */
  opacity: .85;
}

html[dir="rtl"] .progress { transform-origin: right; }

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .progress {
      animation: progress linear both;
      animation-timeline: scroll();
    }
  }
}

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

/* ==========================================================================
   1. hero
   ========================================================================== */

.hero {
  position: relative;
  background: var(--ink);
  color: var(--papier);
  padding-block: clamp(1.75rem, 1rem + 4vw, 5rem) 0;
  overflow: hidden;
}

/* A slow brass wash across the top, echoing the glow in the logo. Purely
   decorative, costs one gradient, no image. */
.hero::before {
  content: "";
  position: absolute;
  inset-block-start: -35%;
  inset-inline-start: 50%;
  translate: -50% 0;
  inline-size: 120%;
  aspect-ratio: 2 / 1;
  background: radial-gradient(ellipse at center,
              rgba(202, 156, 91, .17), rgba(202, 156, 91, 0) 62%);
  pointer-events: none;
}

.hero__inner { position: relative; display: grid; gap: clamp(1.5rem, .8rem + 5vw, 3.5rem); }

.hero .eyebrow { color: var(--laiton); }
.hero .eyebrow::after { max-inline-size: 5rem; }

.hero__title {
  font-size: var(--step-4);
  text-wrap: balance;
}
.hero__title span { display: block; }

.hero__title-accent {
  color: var(--laiton-clair);
  font-style: italic;
}
html[lang="ar"] .hero__title-accent { font-style: normal; }

.hero__sub {
  margin-block: clamp(.9rem, .6rem + 1vw, 1.4rem) clamp(1.4rem, 1rem + 1.5vw, 2rem);
  max-inline-size: 46ch;
  font-size: var(--step-1);
  color: rgba(250, 247, 242, .8);
}

.hero__note {
  margin-block-start: 1rem;
  font-size: var(--step--1);
  letter-spacing: .05em;
  color: rgba(250, 247, 242, .58);
}
html[lang="ar"] .hero__note { letter-spacing: 0; }

/* The photo sits in a brass-outlined frame that is offset behind it, the way a
   finished panel sits proud of its surround. Source photos top out at 960px
   wide, so the frame is capped to keep the image at or below its native size —
   it stays sharp instead of being stretched across a desktop monitor. */
.hero__figure {
  position: relative;
  margin: 0;
  max-inline-size: 30rem;
  margin-inline: auto;
}
.hero__figure::before {
  content: "";
  position: absolute;
  inset: 0;
  translate: clamp(.5rem, 2vw, 1.1rem) clamp(.5rem, 2vw, 1.1rem);
  border: 1px solid rgba(202, 156, 91, .5);
  border-radius: var(--radius);
  pointer-events: none;
}
html[dir="rtl"] .hero__figure::before { translate: calc(-1 * clamp(.5rem, 2vw, 1.1rem)) clamp(.5rem, 2vw, 1.1rem); }

.hero__img {
  position: relative;
  inline-size: 100%;
  border-radius: var(--radius);
  box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
}

.trust {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  margin-block: clamp(2.5rem, 1.5rem + 4vw, 4rem) 0;
  padding-block: 1.15rem;
  border-block-start: 1px solid rgba(202, 156, 91, .22);
  font-size: var(--step--1);
  letter-spacing: .04em;
  color: rgba(250, 247, 242, .72);
}
html[lang="ar"] .trust { letter-spacing: 0; }

.trust li { display: flex; align-items: center; gap: .55rem; }
.trust li::before {
  content: "";
  inline-size: 5px;
  block-size: 5px;
  rotate: 45deg;
  background: var(--laiton);
  flex: none;
}

@media (min-width: 56rem) {
  .hero__inner {
    grid-template-columns: 1.05fr .95fr;
    align-items: center;
  }
  .hero__figure { margin-inline: 0; max-inline-size: none; }

  /* The source is a 3:4 portrait. Left alone it is 715px tall beside the text
     and eats a 13" laptop's whole viewport, so cap it and crop rather than
     scale — scaling a 960px-wide original any further would soften it. */
  .hero__img {
    block-size: min(72vh, 40rem);
    object-fit: cover;
    object-position: 50% 42%;
  }

  .trust { gap: .5rem 3rem; }
}

/* ==========================================================================
   2. process
   ========================================================================== */

.steps {
  margin-block-start: clamp(2.5rem, 1.5rem + 4vw, 4rem);
  display: grid;
  gap: 0;
  position: relative;
}

.step {
  position: relative;
  padding-inline-start: 3.6rem;
  padding-block-end: 2.4rem;
}
.step:last-child { padding-block-end: 0; }

/* the connector */
.step::before {
  content: "";
  position: absolute;
  inset-block: 2.55rem .2rem;
  inset-inline-start: 1.22rem;
  inline-size: 1px;
  background: var(--ligne-forte);
}
.step:last-child::before { display: none; }

.step__num {
  position: absolute;
  inset-block-start: .3rem;
  inset-inline-start: 0;
  display: grid;
  place-items: center;
  inline-size: 2.5rem;
  block-size: 2.5rem;
  border: 1px solid var(--laiton);
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--noyer-clair);
  background: var(--papier);
}
.section:nth-of-type(even) .step__num { background: var(--sable); }

.step__title { font-size: var(--step-2); }

.step__body {
  margin-block-start: .6rem;
  color: var(--texte-doux);
  max-inline-size: 62ch;
}

/* step 2 is the differentiator, so it is the only one that gets emphasis */
.step--key .step__num {
  background: var(--laiton);
  border-color: var(--laiton);
  color: var(--ink);
  font-weight: 700;
}

.step__callout {
  margin-block-start: 1.1rem;
  padding: .95rem 1.2rem;
  background: var(--ink);
  color: var(--laiton-clair);
  border-radius: var(--radius);
  font-family: var(--font-display);
  font-size: var(--step-1);
  max-inline-size: 42ch;
}

@media (min-width: 48rem) {
  .step { padding-inline-start: 4.5rem; padding-block-end: 3rem; }
  .step::before { inset-inline-start: 1.47rem; inset-block-start: 3.1rem; }
  .step__num { inline-size: 3rem; block-size: 3rem; font-size: 1.2rem; }
}

/* ==========================================================================
   3. gallery
   ========================================================================== */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: .55rem;
  margin-block: clamp(1.8rem, 1rem + 3vw, 2.6rem) 1.8rem;
}

.chip {
  padding: .45rem 1rem;
  border: 1px solid var(--ligne-forte);
  border-radius: 100px;
  font-size: var(--step--1);
  letter-spacing: .03em;
  color: var(--texte-doux);
  background: transparent;
  transition: background .2s, color .2s, border-color .2s;
}
html[lang="ar"] .chip { letter-spacing: 0; }

.chip--filter:hover { border-color: var(--laiton); color: var(--noyer); }
.chip--filter.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--laiton-clair);
}

/* Two up even on the narrowest phone. One photo per row turned the gallery
   into ~10 000px of scrolling on a 375px screen, which buries every section
   below it — including the contact details. */
/* --- group blocks ------------------------------------------------------ */

.gallery__groups {
  display: grid;
  gap: clamp(2.5rem, 1.6rem + 3.5vw, 4.5rem);
}

.gallery__group[hidden] { display: none; }

.gallery__group-title {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-block-end: 1.1rem;
  font-size: var(--step-1);
  color: var(--noyer);
  font-weight: 500;
}
/* a brass rule running off to the edge, the way a section is ruled on a plan */
.gallery__group-title::after {
  content: "";
  flex: 1;
  block-size: 1px;
  background: linear-gradient(to inline-end, var(--laiton), transparent 85%);
}

/* --- the tiles ---------------------------------------------------------- */

/* Two columns on a phone, three from 40rem, four from 64rem. The first tile of
   a large group spans 2x2. Because every ordinary tile carries the same
   aspect-ratio, the rows are all one tile tall, so a 2x2 tile lands exactly on
   the grid with no ragged gaps and no masonry script. */
.grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(.4rem, .2rem + 1vw, 1rem);
  grid-auto-flow: dense;
}

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

.grid__item {
  min-inline-size: 0;
  aspect-ratio: 4 / 5;
}
.grid__item[hidden] { display: none; }

/* At two columns a 2x2 feature would occupy both columns of both rows, leaving
   no ordinary tile to give those rows a height. So on the narrowest screens the
   feature is a single-row banner, which needs no sibling to size it. */
.grid__item--feature {
  grid-column: span 2;
  aspect-ratio: 8 / 5;
}

@media (min-width: 40rem) {
  /* From three columns up there is always an ordinary tile beside the feature
     in both rows, so the row heights are well defined and the feature can take
     its height from spanning them - landing exactly on the grid, gaps included. */
  .grid__item--feature {
    grid-row: span 2;
    aspect-ratio: auto;
  }
}

.grid__btn {
  display: block;
  position: relative;
  inline-size: 100%;
  block-size: 100%;
  padding: 0;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--sable);
  text-decoration: none;
  color: inherit;
}

/* The picture is taken out of flow so the photograph's own proportions never
   feed back into the grid. Without this the feature's 3:4 source forced its
   row taller than two tiles and every gap below it drifted. */
.grid__btn picture {
  position: absolute;
  inset: 0;
  display: block;
}

.grid__btn img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform .6s cubic-bezier(.2, .7, .3, 1);
}
.grid__btn:hover img { transform: scale(1.04); }

/* No permanent scrim and no label on the tile any more: the group heading says
   what these are, so the photographs can just be photographs. The scrim now
   only appears on hover, under the "agrandir" chip. */
.grid__btn::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(22, 16, 11, .45), transparent 42%);
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s;
}
.grid__btn:hover::after,
.grid__btn:focus-visible::after { opacity: 1; }

.grid__hint {
  position: absolute;
  inset-block-start: .7rem;
  inset-inline-end: .85rem;
  z-index: 1;
  padding: .25rem .6rem;
  border-radius: 100px;
  background: rgba(22, 16, 11, .78);
  color: var(--laiton-clair);
  font-size: .68rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  opacity: 0;
  translate: 0 -.35rem;
  transition: opacity .3s, translate .3s;
}
html[lang="ar"] .grid__hint { letter-spacing: 0; text-transform: none; font-size: .78rem; }
.grid__btn:hover .grid__hint,
.grid__btn:focus-visible .grid__hint { opacity: 1; translate: 0 0; }

.gallery__cta { margin-block-start: clamp(2rem, 1.2rem + 3vw, 3rem); }

/* Before/after slider. Built and styled, not yet rendered — no pair exists in
   media-raw/ yet. See the comment in src/page.html and TODO.md. */
.ba {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius);
  touch-action: pan-y;
}
.ba__after { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--ba-pos, 50%)); }
.ba__handle {
  position: absolute;
  inset-block: 0;
  inset-inline-start: var(--ba-pos, 50%);
  inline-size: 2px;
  background: var(--laiton);
  cursor: ew-resize;
}

/* ==========================================================================
   5. materials — the wood selector

   Without JavaScript every wood is a plain figure in a grid. main.js adds
   .is-live, and the figures collapse into one stage (all six in the same grid
   cell) with a swatch picker beside it.
   ========================================================================== */

.woods {
  display: grid;
  row-gap: clamp(2rem, 1.2rem + 3vw, 3rem);
}

@media (min-width: 56rem) {
  .woods {
    grid-template-columns: minmax(0, 34rem) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "show head" "show aside";
    column-gap: clamp(2.5rem, 1rem + 4vw, 4.5rem);
    align-items: start;
  }
  .woods__head  { grid-area: head; }
  .woods__show  { grid-area: show; }
  .woods__aside { grid-area: aside; }
}

.woods__stage {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
}
@media (min-width: 40rem) and (max-width: 55.99rem) {
  .woods__stage { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* The card is the illustrations' own backdrop colour, so the square image
   has no visible edge and the board sits on one continuous sheet. */
.wood {
  --wood-ground: #F9F8F5;
  margin: 0;
  background: var(--wood-ground);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 1px 0 var(--ligne), 0 22px 44px -30px rgba(36, 28, 22, .45);
}
.wood img { inline-size: 100%; background: var(--wood-ground); }

.wood__caption { padding: 0 clamp(1rem, .7rem + 1vw, 1.6rem) clamp(1rem, .7rem + 1vw, 1.5rem); }
.wood__name { font-size: var(--step-1); }
.wood__desc { margin-block-start: .3rem; font-size: var(--step--1); color: var(--texte-doux); }

.woods__note {
  margin-block-start: .85rem;
  font-size: var(--step--1);
  color: var(--texte-doux);
}

/* --- live: one stage, crossfaded ---------------------------------------- */

.woods.is-live .woods__stage { grid-template-columns: minmax(0, 1fr); }
.woods.is-live .wood__name { font-size: var(--step-2); }

/* The incoming wood fades in ON TOP of the outgoing one, which stays fully
   opaque underneath until the fade is over and only then switches off.
   Fading both at once would let the section background show through the
   middle of every switch, as a grey dip. */
.woods.is-live .wood {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0s linear .7s, visibility 0s linear .7s;
}
.woods.is-live .wood.is-active {
  z-index: 1;
  opacity: 1;
  visibility: visible;
  transition: opacity .7s cubic-bezier(.4, 0, .2, 1);
}

/* Text is different: two captions blended mid-fade read as a smudge. The old
   one goes quickly, the new one arrives just after. */
.woods.is-live .wood__caption > * {
  opacity: 0;
  transition: opacity .18s ease;
}
.woods.is-live .wood.is-active .wood__caption > * {
  opacity: 1;
  transition: opacity .5s ease .22s, transform .6s cubic-bezier(.2, .7, .3, 1) .22s;
}

@media (prefers-reduced-motion: no-preference) {
  /* The board settles as it arrives: a 3.5% zoom-out, no more. */
  .woods.is-live .wood img { transform: scale(1.035); transition: transform 0s linear .7s; }
  .woods.is-live .wood.is-active img {
    transform: none;
    transition: transform 1.1s cubic-bezier(.2, .7, .3, 1);
  }
  .woods.is-live .wood__caption > * { transform: translateY(.45rem); }
  .woods.is-live .wood.is-active .wood__caption > * { transform: none; }
}

/* --- the picker ----------------------------------------------------------
   One row of six, directly under the stage at every width, so the picture
   that changes is always the one right above your thumb. */

.woods__picker {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: .2rem;
  margin-block-start: 1rem;
}
.woods__picker[hidden] { display: none; }

.woods__opt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  padding: .5rem .1rem .4rem;
  border-radius: var(--radius);
  font-size: clamp(.72rem, .66rem + .3vw, .84rem);
  line-height: 1.25;
  text-align: center;
  color: var(--texte-doux);
  transition: color .25s;
}
.woods__opt:hover,
.woods__opt.is-active { color: var(--texte); }

.woods__swatch {
  display: block;
  inline-size: clamp(2.5rem, 1.7rem + 3vw, 3.25rem);
  block-size: clamp(2.5rem, 1.7rem + 3vw, 3.25rem);
  border-radius: 50%;
  overflow: hidden;
  box-shadow: 0 0 0 1px var(--ligne-forte);
  transition: box-shadow .35s ease, transform .35s cubic-bezier(.2, .7, .3, 1);
}
.woods__swatch picture,
.woods__swatch img { inline-size: 100%; block-size: 100%; object-fit: cover; }

/* A brass ring with a hairline of the section colour between it and the
   grain, like a stone set in a bezel. */
.woods__opt.is-active .woods__swatch {
  box-shadow: 0 0 0 2px var(--sable), 0 0 0 3.5px var(--laiton);
}
@media (prefers-reduced-motion: no-preference) {
  .woods__opt:hover .woods__swatch { transform: scale(1.07); }
  .woods__opt.is-active .woods__swatch { transform: scale(1.07); }
}

/* --- panels, and the rest of the aside ----------------------------------- */

.panels__title { font-size: var(--step-1); }
.panels__title--next { margin-block-start: clamp(1.6rem, 1.2rem + 1.4vw, 2.2rem); }

.panels { margin-block-start: .8rem; }
.panel {
  display: grid;
  gap: .15rem;
  padding-block: .85rem;
  border-block-start: 1px solid var(--ligne);
}
.panel:last-child { border-block-end: 1px solid var(--ligne); }
.panel__name { font-family: var(--font-display); font-size: var(--step-1); color: var(--noyer); }
.panel__desc { font-size: var(--step--1); color: var(--texte-doux); }

.woods__more { margin-block-start: 1.4rem; color: var(--texte-doux); max-inline-size: 52ch; }
.materials .btn--ghost { margin-block-start: 1.3rem; }

.todo-card__cta { margin-block-start: 1.3rem; }

.todo-inline {
  display: inline-block;
  padding: .1rem .55rem;
  border: 1px dashed var(--ligne-forte);
  border-radius: 100px;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--texte-doux);
}
html[lang="ar"] .todo-inline { letter-spacing: 0; text-transform: none; font-size: .82rem; }

/* ==========================================================================
   4. services
   ========================================================================== */

.svc { margin-block-start: clamp(2rem, 1.2rem + 3vw, 3rem); }

.svc__row {
  display: grid;
  gap: .9rem;
  padding-block: clamp(1.3rem, 1rem + 1.2vw, 1.9rem);
  border-block-start: 1px solid var(--ligne);
  position: relative;
}
.svc__row:last-child { border-block-end: 1px solid var(--ligne); }

/* a brass rule that draws itself in on hover */
.svc__row::after {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 1px;
  background: var(--laiton);
  transform: scaleX(0);
  transform-origin: left;   /* see the note on .site-nav a::after */
  transition: transform .5s cubic-bezier(.2, .7, .3, 1);
}
html[dir="rtl"] .svc__row::after { transform-origin: right; }
.svc__row:hover::after { transform: scaleX(1); }

.svc__name { font-size: var(--step-2); }
.svc__desc { margin-block-start: .4rem; color: var(--texte-doux); max-inline-size: 52ch; }

.svc__side { display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap; }

.svc__price {
  display: flex;
  align-items: baseline;
  gap: .4rem;
  flex-wrap: wrap;
}

.svc__from {
  font-size: var(--step--1);
  color: var(--texte-doux);
  letter-spacing: .04em;
}
html[lang="ar"] .svc__from { letter-spacing: 0; }

.svc__amount {
  font-family: var(--font-display);
  font-size: var(--step-2);
  color: var(--noyer);
  font-variant-numeric: tabular-nums;
}

.svc__currency { font-size: var(--step--1); color: var(--texte-doux); }

.svc__cta {
  font-size: var(--step--1);
  color: var(--noyer-clair);
  text-decoration-color: var(--ligne-forte);
  text-underline-offset: .28em;
  transition: color .2s, text-decoration-color .2s;
}
.svc__cta:hover { color: var(--noyer); text-decoration-color: var(--laiton); }

/* The owner's caveat: prices swing widely with size and wood. Set apart with
   the same brass edge as the reviews, because it is the sentence a client
   most needs to have read before quoting a number back. */
.services__note {
  margin-block-start: 1.8rem;
  padding: 1.1rem clamp(1.1rem, .8rem + 1vw, 1.5rem);
  border-inline-start: 2px solid var(--laiton);
  background: rgba(202, 156, 91, .08);
  max-inline-size: 66ch;
}
.services__note p { font-size: var(--step--1); color: var(--texte); }
.services__woods-link {
  display: inline-block;
  margin-block-start: .55rem;
  font-size: var(--step--1);
  color: var(--noyer-clair);
  text-decoration-color: var(--ligne-forte);
  text-underline-offset: .28em;
  transition: color .2s, text-decoration-color .2s;
}
.services__woods-link:hover { color: var(--noyer); text-decoration-color: var(--laiton); }

@media (min-width: 46rem) {
  .svc__row {
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: 2rem;
  }
  .svc__side { flex-direction: column; align-items: flex-end; gap: .35rem; text-align: end; }
  html[dir="rtl"] .svc__side { align-items: flex-start; }
}

/* ==========================================================================
   6. reviews — real reviews only, verbatim, never invented copy

   No star ratings anywhere. We do not know what ratings these clients left,
   and drawing five stars beside a quote asserts one.
   ========================================================================== */

.reviews__list {
  display: grid;
  gap: var(--gap);
  margin-block-start: clamp(2rem, 1.2rem + 3vw, 2.8rem);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem, 100%), 1fr));
}

.review {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  padding: clamp(1.3rem, 1rem + 1vw, 1.8rem);
  background: var(--papier);
  border: 1px solid var(--ligne);
  border-radius: var(--radius);
  /* the brass edge is the only decoration; it reads as a pulled quote */
  border-inline-start: 2px solid var(--laiton);
}

.review__quote { margin: 0; flex: 1; }

.review__quote p {
  margin: 0;
  font-size: var(--step-0);
  line-height: 1.65;
  color: var(--texte);
}

/* Each quote carries its author's own dir. `text-align: start` would follow the
   page instead, and left-align the Arabic review on the French page. */
.review__quote[dir="ltr"] { text-align: left; }
.review__quote[dir="rtl"] { text-align: right; }

.review__by {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding-block-start: .9rem;
  border-block-start: 1px solid var(--ligne);
}

/* Initials, not a photograph and not a stock portrait: we have no pictures of
   these clients and will not imply that we do. */
.review__avatar {
  flex: none;
  inline-size: 2.4rem;
  block-size: 2.4rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--sable);
  border: 1px solid var(--ligne);
  color: var(--noyer);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: .04em;
}

.review__name {
  font-style: normal;
  font-weight: 600;
  font-size: var(--step--1);
  color: var(--texte-doux);
}

.reviews .todo-card__cta { margin-block-start: clamp(1.6rem, 1rem + 2vw, 2.4rem); }

/* ==========================================================================
   6b. film — one presentation video per language
   ========================================================================== */

.film__inner { display: grid; gap: clamp(1.8rem, 1rem + 4vw, 3.5rem); align-items: center; }

@media (min-width: 56rem) {
  .film__inner { grid-template-columns: 1fr auto; }
}

.video { margin: 0; display: grid; gap: .8rem; justify-items: center; }

/* The films are phone-shot portrait. Capping the width is what stops a 9:16
   video becoming a full-height wall on a desktop screen. */
.video__frame {
  inline-size: min(100%, 20rem);
  background: var(--ink);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 18px 40px -22px rgba(22, 16, 11, .55);
}

.video__el {
  display: block;
  inline-size: 100%;
  block-size: auto;
  aspect-ratio: 9 / 16;
}

/* The poster facade: a lazy <picture> that becomes a real <video> on click.

   Centring is done with grid rather than inset-inline + translate. "Centre" is
   the same place in both directions, but `inset-inline-start: 50%` is not — it
   resolves to `right: 50%` on /ar/ and puts the button off to one side. */
.video__facade {
  display: grid;
  place-items: center;
  position: relative;
  inline-size: 100%;
  aspect-ratio: 9 / 16;
  overflow: hidden;
}

/* Absolute, so the poster's own proportions never fight the 9/16 box. */
.video__facade picture { position: absolute; inset: 0; display: block; }

.video__facade img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform .6s cubic-bezier(.2, .7, .3, 1);
}

.video__facade:hover img,
.video__facade:focus-visible img { transform: scale(1.03); }

.video__play {
  position: relative;   /* above the absolutely-positioned poster */
  display: grid;
  place-items: center;
  inline-size: 4.2rem;
  block-size: 4.2rem;
  border-radius: 50%;
  background: rgba(22, 16, 11, .58);
  border: 1px solid rgba(250, 247, 242, .5);
  transition: background .25s ease, scale .25s ease;
}

/* The triangle, drawn with clip-path so there is no icon file and no glyph a
   font might be missing. It points right in both directions: media controls
   follow the direction of time, not the direction of the script — which is why
   the nudge off-centre is `margin-left` and not `margin-inline-start`. */
.video__play::after {
  content: "";
  inline-size: 1.15rem;
  block-size: 1.3rem;
  margin-left: .18rem;
  background: var(--papier);
  clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.video__facade:hover .video__play,
.video__facade:focus-visible .video__play {
  background: rgba(22, 16, 11, .74);
  scale: 1.06;
}

.video__caption {
  font-size: var(--step--1);
  color: var(--texte-doux);
  text-align: center;
  max-inline-size: 22rem;
}

/* ==========================================================================
   6c. opening hours — days and times in two columns
   ========================================================================== */

.hours { display: grid; gap: .3rem; margin-block-start: .15rem; }

.hours__row {
  display: flex;
  justify-content: space-between;
  gap: 1.5rem;
  max-inline-size: 18rem;
}

/* dir="ltr" lives on this element in the markup: "8:00 - 18:00" is two number
   runs separated by a neutral dash, which an RTL paragraph would reorder into
   "18:00 - 8:00". Same failure as the prices. */
.hours__time { white-space: nowrap; color: var(--texte-doux); }

.contact__note {
  display: block;
  font-size: var(--step--1);
  color: var(--texte-doux);
  margin-block-start: .15rem;
}

.contact__wa::before { content: "·"; margin-inline: .5rem; color: var(--ligne-forte); }

/* ==========================================================================
   7. zone
   ========================================================================== */

.zone__inner { display: grid; gap: clamp(2rem, 1rem + 4vw, 3.5rem); }

@media (min-width: 52rem) {
  .zone__inner { grid-template-columns: 1.2fr .8fr; align-items: start; }
}

.zone__more { margin-block-start: 1.1rem; color: var(--texte-doux); max-inline-size: 58ch; }

.zone__cities-label {
  font-size: .72rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--noyer-clair);
  margin-block-end: 1rem;
}
html[lang="ar"] .zone__cities-label { letter-spacing: 0; text-transform: none; font-size: .85rem; }

.chips { display: flex; flex-wrap: wrap; gap: .55rem; }

.zone .chip {
  border-color: var(--laiton);
  color: var(--noyer);
  background: rgba(202, 156, 91, .09);
}

/* ==========================================================================
   8. contact
   ========================================================================== */

.contact {
  background: var(--ink);
  color: var(--papier);
}
.section.contact:nth-of-type(even) { background: var(--ink); }

.contact .eyebrow { color: var(--laiton); }
.contact .section__intro { color: rgba(250, 247, 242, .78); }

.contact__inner { display: grid; gap: clamp(2.2rem, 1.2rem + 4vw, 4rem); }

@media (min-width: 52rem) {
  .contact__inner { grid-template-columns: 1.05fr .95fr; align-items: start; }
}

.contact .btn--lg { margin-block-start: 2rem; }

.contact__note {
  margin-block-start: 1.4rem;
  font-size: var(--step--1);
  color: rgba(250, 247, 242, .58);
  max-inline-size: 44ch;
}

.contact__details { margin: 0; }

.contact__row {
  display: grid;
  gap: .25rem;
  padding-block: 1.05rem;
  border-block-start: 1px solid rgba(202, 156, 91, .2);
}
.contact__row:last-child { border-block-end: 1px solid rgba(202, 156, 91, .2); }

.contact__row dt {
  font-size: .72rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--laiton);
}
html[lang="ar"] .contact__row dt { letter-spacing: 0; text-transform: none; font-size: .85rem; }

.contact__row dd { margin: 0; color: rgba(250, 247, 242, .9); }
.contact__row dd a { color: var(--laiton-clair); text-underline-offset: .25em; }

.contact__map { display: inline-block; margin-block-start: .35rem; font-size: var(--step--1); }

.contact .todo-inline { border-color: rgba(202, 156, 91, .4); color: rgba(250, 247, 242, .6); }

@media (min-width: 46rem) {
  .contact__row { grid-template-columns: 9rem 1fr; gap: 1rem; align-items: baseline; }
}

/* ==========================================================================
   footer
   ========================================================================== */

.site-foot {
  background: var(--ink-soft);
  color: rgba(250, 247, 242, .62);
  padding-block: 2.5rem;
  font-size: var(--step--1);
  /* clears the sticky CTA on mobile so the last line is never hidden */
  padding-block-end: calc(2.5rem + env(safe-area-inset-bottom));
}

.site-foot__inner { display: grid; gap: 1.2rem; }
.site-foot strong { color: var(--papier); font-weight: 600; }
/* .42 alpha measured 3.87:1 against --ink-soft, under the 4.5 AA floor. */
.site-foot__rights { color: rgba(250, 247, 242, .58); }
.site-foot__links { display: flex; gap: 1.4rem; }
.site-foot__links a { color: var(--laiton); text-underline-offset: .25em; }

@media (min-width: 46rem) {
  .site-foot__inner { grid-template-columns: 1fr auto; align-items: end; }
}

/* ==========================================================================
   sticky mobile CTA

   Visible by default and hidden by JavaScript while the hero button is on
   screen. That order matters: with JavaScript off the bar simply stays, so the
   primary action is never missing.
   ========================================================================== */

.sticky-cta {
  position: fixed;
  inset-inline: 0;
  inset-block-end: 0;
  z-index: 50;
  padding: .7rem var(--gutter) calc(.7rem + env(safe-area-inset-bottom));
  background: linear-gradient(to top, var(--papier) 68%, rgba(250, 247, 242, 0));
  transition: translate .35s cubic-bezier(.2, .7, .3, 1), opacity .3s;
}
.sticky-cta[data-hidden] { translate: 0 120%; opacity: 0; pointer-events: none; }

@media (min-width: 46rem) { .sticky-cta { display: none; } }

body { padding-block-end: 4.5rem; }
@media (min-width: 46rem) { body { padding-block-end: 0; } }

/* ==========================================================================
   lightbox
   ========================================================================== */

.lightbox {
  border: 0;
  padding: 0;
  max-inline-size: 100vw;
  max-block-size: 100dvh;
  inline-size: 100%;
  block-size: 100%;
  background: transparent;
  color: var(--papier);
  overflow: hidden;
}
.lightbox::backdrop { background: rgba(10, 7, 5, .93); }

.lightbox[open] {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: .5rem;
  padding: clamp(.5rem, 2vw, 2rem);
}

.lightbox__figure {
  margin: 0;
  display: grid;
  gap: .8rem;
  justify-items: center;
  min-inline-size: 0;
}

.lightbox__img {
  max-inline-size: 100%;
  max-block-size: 78dvh;
  inline-size: auto;
  object-fit: contain;
  border-radius: var(--radius);
  background: var(--ink);
}

.lightbox__caption {
  max-inline-size: 60ch;
  text-align: center;
  font-size: var(--step--1);
  color: rgba(250, 247, 242, .74);
  line-height: 1.5;
}

.lightbox__close {
  position: absolute;
  inset-block-start: clamp(.5rem, 2vw, 1.4rem);
  inset-inline-end: clamp(.5rem, 2vw, 1.4rem);
  inline-size: 2.75rem;
  block-size: 2.75rem;
  font-size: 1.9rem;
  line-height: 1;
  color: var(--laiton-clair);
  border-radius: 50%;
  transition: background .2s;
}
.lightbox__close:hover { background: rgba(202, 156, 91, .18); }

.lightbox__nav {
  inline-size: 2.75rem;
  block-size: 4.5rem;
  font-size: 2.2rem;
  line-height: 1;
  color: var(--laiton-clair);
  border-radius: var(--radius);
  transition: background .2s;
}
.lightbox__nav:hover { background: rgba(202, 156, 91, .18); }
/* the chevrons are directional glyphs, so flip them with the document */
html[dir="rtl"] .lightbox__nav { scale: -1 1; }

/* ==========================================================================
   motion preference — one switch, honoured everywhere
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   print — a client who prints the page should get the prices and the number
   ========================================================================== */

@media print {
  .site-head, .sticky-cta, .filters, .lightbox, .btn, .skip-link { display: none !important; }
  body { background: #fff; color: #000; padding: 0; }
  .hero, .contact, .site-foot { background: #fff !important; color: #000 !important; }
  .hero__title-accent, .contact .eyebrow, .hero .eyebrow { color: #000 !important; }
  .contact__row dd, .contact__row dd a { color: #000 !important; }
  .section { padding-block: 1.5rem; break-inside: avoid; }
}
