/* ==========================================================================
   Home page (index.html, en/index.html) — hero, category strip, about,
   marquee, category picker, catalogue rows, factory story, why us, process,
   partners and the factory gallery strip.
   ========================================================================== */

/* --- Hero ---------------------------------------------------------------
   Each slide is a full-bleed lifestyle banner (assets/hero/banner-1..3.png — real
   photos, plant included in-shot already, no need for a decorative stand-in)
   with the copy overlaid on top. .hero-frame runs edge-to-edge (no inset
   margin, no corner radius) rather than reading as an inset card; the fixed
   header floats directly over the photo on top of it. */
.hero { padding: 0 0 var(--space-8); }
.hero-frame {
  position: relative;
  margin: 0;
  border-radius: 0;
  overflow: hidden;
}
.hero-slides { position: relative; }
.hero-slide { position: relative; display: none; min-height: clamp(530px, 65vw, 780px); }
.hero-slide.is-active { display: block; animation: rise 0.6s ease both; }


/* Slightly overscanned (inset < 0) so the parallax translate on scroll never
   reveals a bare edge at the top/bottom of the frame. */
.hero-slide-bg {
  position: absolute; inset: -6% -3%;
  background-size: cover; background-position: center;
}
/* The banner photos (portrait crops for phones are in the ≤720px block).
   Not preloaded: at ~2MB, a preload competes with the stylesheets for
   bandwidth and delayed the first paint from 1.8s to 11s on slow 4G.
   Worth adding once the banners are web-sized copies. */
.hero-slide-center .hero-slide-bg { background-image: url('../../assets/hero/banner-1.png'); }
.hero-slide-mid .hero-slide-bg { background-image: url('../../assets/hero/banner-2.png'); }
.hero-slide-3 .hero-slide-bg { background-image: url('../../assets/hero/banner-3.png'); }
/* Top padding clears the fixed header floating over the photo (roughly
   space-4 top offset + its own ~102px box height, driven by the 76px
   logo); bottom padding is the normal breathing room before the frame's
   lower edge. */
.hero-inner { position: relative; z-index: 2; padding: clamp(132px, 17vw, 180px) 0 clamp(32px, 6vw, 72px); }
/* margin-right (physical, not margin-inline-end) is deliberate: pins the
   copy to the same left-hand empty-wall area in every banner regardless of
   [dir="rtl"] — all three banner photos have their furniture on the right
   and open wall on the left, so this follows the photo's own content, not
   the page's logical reading direction. */
.hero-copy { max-width: 480px; margin-right: auto; }
/* A soft white halo (not a background wash — there's no scrim behind this
   copy anymore) so the dark text stays readable if a slide's furniture or
   shadow ever crosses into its area, instead of legibility depending
   entirely on each photo's empty-wall region staying light. Kept off the
   buttons below (own solid backgrounds, don't need it). */
.hero-copy > .kicker, .hero-copy h1, .hero-copy > .lede {
  text-shadow:
    0 0 18px color-mix(in srgb, #ffffff 85%, transparent),
    0 1px 4px color-mix(in srgb, #ffffff 70%, transparent);
}
.hero-copy h1 {
  font-size: clamp(34px, 4.6vw, 58px); line-height: 1.06;
  margin-bottom: var(--space-4);
}
/* The one word per slide singled out in accent color — this site's own
   equivalent of the reference's script-font "Furniture" treatment; Arabic
   has no comparable decorative-script convention, so color alone carries it. */
.hero-copy h1 .accent { color: var(--color-accent-700); }
.lede {
  font-size: 16px; max-width: 42ch;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
}
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); margin-top: var(--space-6); }
/* Nudges the trailing arrow away from its label on hover — a small bit of
   the reference's "Go to Shop →" affordance, done by widening the .btn gap
   rather than animating the arrow glyph itself. */
.hero-link { transition: gap 0.2s ease, background-color 0.2s ease; }
.hero-link:hover { gap: 10px; }

/* Slide 1 only: centered instead of the left-pinned wall-anchored spot the
   other two slides use — auto margins on both sides center regardless of
   [dir="rtl"], so no physical-vs-logical tradeoff here like .hero-copy's
   own margin-right rule above. */
/* transform, not margin/inset: nudges it off its centered spot without
   disturbing the margin-inline:auto centering math itself. Physical
   translateX (left) per this slide's own established convention of
   physical over logical positioning here, same reasoning as .hero-copy's
   margin-right rule above — a screen-left nudge, not a [dir]-relative one. */
.hero-slide-center .hero-copy { max-width: 700px; margin-inline: auto; text-align: center; transform: translate(-35px, 80px); }
.hero-slide-center .hero-actions { justify-content: center; }
/* This slide's <br> in the heading text is meant to be the only break — at
   the h1's max clamp size the second line ("مباشرة من المصنع.") is wide
   enough to wrap a second time on its own, turning 2 lines into 3. nowrap
   stops that soft-wrap while leaving the explicit <br> (a forced break,
   unaffected by white-space) doing its job, so the heading is always
   exactly 2 lines regardless of translation length. */
.hero-slide-center .hero-copy h1 { white-space: nowrap; }
/* A bit smaller across the board for this slide — kicker, heading, and lede
   each trimmed modestly rather than any one of them alone. */
.hero-slide-center .hero-copy > .kicker { font-size: 11px; }
.hero-slide-center .hero-copy h1 { font-size: clamp(21px, 3vw, 37px); }
/* The lede here has no <br> to anchor a line count the way the heading
   does — it's one long sentence that would otherwise run 3-4 lines at the
   base 42ch width. Widened so each of the 2 lines carries more of the
   sentence, and line-clamp is the hard stop (with an ellipsis on the rare
   viewport where it's still too long) rather than relying on the sentence
   happening to break evenly on its own. */
.hero-slide-center .lede {
  max-width: 65ch; margin-inline: auto; font-size: 14px;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Slide 2 only: banner-2.png's clear wall gap runs from past the plant/
   pendant lights on the left to short of the cabinet on the right — centered
   (margin-inline:auto, like slide 1) sits it in the middle of that gap
   instead of pinned to either edge. The heading's own <br> was dropped from
   the heading text for a single line; nowrap plus a wider, slightly
   smaller-type box is what actually keeps that one line from wrapping or
   overflowing since the full phrase is noticeably longer than slide 1's two
   short lines. */
/* The fixed nudge is capped by max(): below ~1160px wide a flat -160px pushed
   the copy under the 48px prev arrow. (840px - 100vw) / 2 is the shift that
   keeps a centered 640px box's left edge ~84px in from the frame — just clear
   of the arrow — so narrower laptops get as much of the nudge as fits. */
.hero-slide-mid .hero-copy { max-width: 640px; margin-inline: auto; transform: translateX(max(-160px, calc((840px - 100vw) / 2))); }
.hero-slide-mid .hero-copy h1 {
  white-space: nowrap; font-size: clamp(30px, 3.6vw, 46px);
}
.hero-slide-mid .lede { font-size: 17px; }

/* Slide 3 only: same wrap-past-2-lines issue as slide 1's heading ("لكل ركن
   في المنزل." is long enough to soft-wrap a second time at the base clamp
   size) plus a requested size reduction — nowrap keeps the existing <br>
   as the only break, the smaller clamp is the "a bit smaller" ask, and the
   box is widened slightly so that second line still fits on one line at
   this size without the two changes fighting each other. */
/* margin-inline expects a length/auto, not a "left" keyword — that value is
   invalid and is dropped by the parser, so this was silently falling back
   to .hero-copy's own margin-right:auto the whole time. margin-inline:auto
   (matching text-align:center below) is what was actually intended. */
/* Same max() cap as .hero-slide-mid above, for a centered 560px box: a flat
   -320px ran the copy off the frame's left edge below ~1200px wide ("for
   every corner…" lost its first letters, since the frame clips) and under
   the prev arrow up to ~1400px. */
.hero-slide-3 .hero-copy { max-width: 560px; margin-inline: auto; text-align: center; transform: translateX(max(-320px, calc((760px - 100vw) / 2))); }
.hero-slide-3 .hero-copy h1 {
  white-space: nowrap; font-size: clamp(28px, 3.8vw, 48px);
}
.hero-slide-3 .lede { margin-inline: auto; }
.hero-slide-3 .hero-actions { justify-content: center; }

/* Prev/next arrows, overlaid on the photo itself (vs. .hero-dots below the
   frame) — same navy-fill/gold-hover language as .prod-slider-nav in the
   catalogue, just larger given the hero's own scale. A translucent navy
   with a hairline border reads better over varied photo content than a
   solid fill would. */
.hero-arrow {
  position: absolute; top: 50%; z-index: 4; transform: translateY(-50%);
  width: 48px; height: 48px; border-radius: 999px; padding: 0;
  display: grid; place-items: center; cursor: pointer;
  background: color-mix(in srgb, var(--color-accent-2-900) 55%, transparent);
  color: var(--color-neutral-100);
  border: 1px solid color-mix(in srgb, #ffffff 25%, transparent);
  box-shadow: var(--shadow-sm);
  transition: background-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
}
.hero-arrow:hover {
  background: var(--color-accent); border-color: var(--color-accent); color: var(--color-text);
  box-shadow: var(--shadow-md); transform: translateY(-50%) scale(1.08);
}
.hero-arrow:active { transform: translateY(-50%) scale(1); }
.hero-arrow-prev { inset-inline-start: var(--space-6); }
.hero-arrow-next { inset-inline-end: var(--space-6); }
/* Same reasoning as .prod-slider-nav's own flip: plain left/right glyphs,
   mirrored under RTL so "prev" still points toward the start of reading
   order instead of a fixed screen direction. */
[dir="rtl"] .hero-arrow svg { transform: scaleX(-1); }
/* The hero already overlays a full copy block on the photo at this size —
   two more floating circles would crowd it. Dots (still visible) plus
   autoplay cover navigation here instead. */
@media (max-width: 720px) { .hero-arrow { display: none; } }

/* The category strip below overlaps this frame's bottom edge by
   --hero-strip-overlap, which is exactly where this row sits in flow — it
   had been covering the dots (and their focus ring) completely at every
   width. The row keeps its in-flow box, so the hero's height is unchanged,
   but is lifted to sit just clear of the strip, on a translucent page-colored
   pill (the header/strip's floating-card language) so the dots stay legible
   over the photo instead of the plain gap they were first drawn for. */
.hero { --hero-strip-overlap: calc(1.3 * var(--space-8)); }
.hero-nav {
  position: relative; z-index: 4;
  display: flex; align-items: center; justify-content: center;
  width: max-content; max-width: none; margin: var(--space-4) auto; padding: 0 var(--space-2);
  background: color-mix(in srgb, var(--color-bg) 82%, transparent);
  border-radius: 999px; box-shadow: var(--shadow-sm);
  transform: translateY(calc(var(--space-4) - var(--hero-strip-overlap) - var(--space-3)));
}
/* No gap here — each dot's own 44px tap box already has enough padding
   around its small visible dot to keep the row from looking cramped. */
.hero-dots { display: flex; }
/* 44×44 tap target with a small visual dot centered inside it via ::before
   — keeps the indicator itself compact while the actual button someone has
   to hit on a touch screen meets the minimum comfortable tap size. */
.hero-dot {
  width: 44px; height: 44px; padding: 0; border: 0; cursor: pointer;
  background: none;
  display: grid; place-items: center;
}
.hero-dot::before {
  content: '';
  width: 10px; height: 10px; border-radius: 999px;
  /* neutral-600: these sit in the plain --color-bg gap below the photo,
     where -400 read too faint and -500 (2.9:1) was still under the 3:1
     WCAG minimum for a control's visual state; -600 is 4.9:1. */
  background: var(--color-neutral-600);
  transition: width 0.25s ease, background 0.25s ease;
}
.hero-dot.is-active::before { background: var(--color-accent); width: 30px; }
/* Pause/play for the autoplay (js/components/hero-slider.js — WCAG
   2.2.2). Same 44px tap box and neutral-600 ink as the dots beside it, so it
   reads as part of that one control row rather than a new feature. The play
   glyph is not mirrored under RTL: media controls keep their direction. */
.hero-pause {
  width: 44px; height: 44px; padding: 0; border: 0; cursor: pointer;
  background: none; display: grid; place-items: center;
  color: var(--color-neutral-600); transition: color 0.2s ease;
}
.hero-pause:hover { color: var(--color-accent-700); }
.hero-pause .icon-play, .hero-pause[aria-pressed="true"] .icon-pause { display: none; }
.hero-pause[aria-pressed="true"] .icon-play { display: block; }

/* --- Hero category strip — floats over the frame's bottom edge, teasing the
   same 4 categories/counts the catalog-preview section shows in full. Sized
   and spaced to feel like its own deliberate card rather than a thin bar
   squeezed under the photo. ------------------------------------------------ */
.hero-strip-wrap { position: relative; z-index: 3; margin-top: calc(-1 * var(--hero-strip-overlap)); }
/* Flex + justify-content:space-between, not equal-width grid columns: with
   4 category names as different in length as "طاولات التلفزيون" and
   "طاولات المجالس والاستقبال", forcing equal-width columns means the short
   ones always sit in a mostly-empty column and the long one fills its
   column edge to edge — no amount of padding/centering on top of that
   makes the GAPS between items equal, only the leftover space within each
   column. Letting every item size to its own content and distributing the
   remaining row width as gaps is what actually guarantees equal spacing
   regardless of text length. Collapses back to the grid at ≤1020px, where
   there's less width for this to matter and wrapping needs the grid's
   row-handling anyway. */
.hero-strip {
  list-style: none; margin: 0; padding: var(--space-6) var(--space-8);
  display: flex; align-items: center; justify-content: space-between;
  background: var(--color-bg);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  /* Belt and suspenders alongside .hero-strip li's own min-width:0 below:
     that fix relies on the browser correctly propagating a nested flex
     item's min-width:0 opt-out up through the row so the ellipsis can
     engage — Chromium does, but this is exactly the kind of min-content
     edge case WebKit/older engines have been inconsistent on. flex-wrap
     means that even on an engine that ignores the opt-out and still floors
     each item at its nowrap title's full width, the row drops the item
     that doesn't fit onto a second line instead of pushing the page wide —
     the one outcome that's never a horizontal-scroll bug regardless of
     engine. Only matters in the ~1021-1155px tablet-landscape band this
     component's own ≤1020px breakpoint doesn't reach; comfortably fits on
     one row everywhere else. */
  flex-wrap: wrap;
  row-gap: var(--space-4);
}
/* padding-inline (both sides, every item — not just padding-inline-start)
   is what actually makes the 4 columns' content areas the same width: the
   first item used to get padding-inline-start:0 (no divider before it, so
   "no padding either"), which quietly gave it more room to center its
   group in than the other three and made the gaps read as uneven even
   though .hero-strip-link's own centering math was correct. Only the
   divider line itself — not the padding — should differ for the first item. */
.hero-strip li {
  /* var(--space-6), not a "--space-5" — that token was never actually
     defined in :root (the scale only has 1/2/3/4/6/8), so this had been
     silently computing to 0 padding this whole time: an undefined custom
     property with no fallback makes the declaration invalid, not an error,
     so nothing flagged it. That's the real reason there was no visible gap
     between the link and the divider line no matter how the rest of this
     component's layout changed. */
  padding-inline: var(--space-6);
  border-inline-start: 1px solid var(--color-divider);
  /* Flex items default to min-width:auto, which floors their size at the
     nowrap title's full text width no matter how little room is actually
     left — .hero-strip-copy strong's own text-overflow:ellipsis never got
     a chance to engage. That floor pushed the row past its own container
     at ~1021-1155px (a real width band: iPad landscape, small laptops),
     the one gap this component's own ≤1020px breakpoint below doesn't
     cover, causing the page to scroll sideways. min-width:0 here lets the
     item actually shrink so the ellipsis can do its job. */
  min-width: 0;
}
.hero-strip li:first-child { border-inline-start: 0; }
/* Every other icon/copy row on this page (.why-feat, .cp-tile, .process
   .step…) responds to hover — this strip was the one static, un-clickable
   exception even though it's teasing real catalog categories. A real link
   to #collections plus the same lift-the-icon treatment .why-icon already
   uses brings it in line instead of reading as dead decoration. */
.hero-strip-link {
  display: flex; align-items: center; justify-content: center; gap: var(--space-4);
  text-decoration: none; color: inherit;
  border-radius: var(--radius-md);
}
.hero-strip-icon {
  width: 56px; height: 56px; flex: none; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--color-accent-2-100); color: var(--color-accent-2-700);
  transition: transform 0.25s ease, box-shadow 0.25s ease, background-color 0.25s ease;
}
.hero-strip-icon svg { width: 24px; height: 24px; }
.hero-strip-link:hover .hero-strip-icon {
  transform: translateY(-3px) scale(1.05);
  box-shadow: var(--shadow-md);
  background: var(--color-accent-2-600); color: var(--color-neutral-100);
}
.hero-strip-link:hover .hero-strip-copy strong { color: var(--color-accent-2-700); }
/* start, not the flex default of stretch: without it this column becomes as
   wide as .hero-strip-link's flex sizing allows, which right-pads only the
   short titles (nothing after "طاولات التلفزيون" ties it to its own icon)
   while the long one ("طاولات المجالس والاستقبال") fills the column — very
   different effective title-to-icon gaps from one item to the next even
   though the link itself is now centered as a group. */
.hero-strip-copy { display: flex; flex-direction: column; align-items: start; gap: 4px; min-width: 0; }
.hero-strip-copy strong {
  display: block; font-size: 15px; font-weight: 600;
  font-family: var(--font-body); color: var(--color-text); text-align: start;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color 0.2s ease;
}
.hero-strip-copy small { font-size: 13px; color: color-mix(in srgb, var(--color-text) 65%, transparent); text-align: start; } /* 60% was 4.46:1 — just under AA */

@media (max-width: 1020px) {
  /* display:grid, not the base rule's flex: with only 2 per row here,
     wrapping back to equal-width columns (plus the padding-symmetry fix
     below) reads fine, and grid handles the row-break cleanly — flex's
     space-between only pays off with all 4 in a single wide row. */
  .hero-strip { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-6) var(--space-4); }
  /* Same fix as the base rule above: drop only the divider (item 3 starts
     row 2, so there's nothing to its physical right to divide from here),
     not the padding — keeps its content centered in the same effective
     width as the other three instead of drifting toward the edge. */
  .hero-strip li:nth-child(3) { border-inline-start: 0; }
  /* flex-start, not the base rule's centered group: items 1+3 share a
     column and 2+4 share the other, so centering each independently (which
     shifts by however long that item's own title runs) put the icons in
     the same column at different horizontal positions row to row. */
  .hero-strip-link { justify-content: flex-start; }
  /* Per-slide desktop positioning (translateX shifts up to -320px, a fixed
     max-width, and a forced nowrap heading) was only being reset at
     ≤720px — every viewport from 721-1020px (a common tablet/phablet
     range) was still getting the full desktop treatment: hero copy
     shoved off-center with a large empty gap where it should have
     centered, and headings that could overflow instead of wrapping. */
  .hero-slide-center .hero-copy,
  .hero-slide-mid .hero-copy,
  .hero-slide-3 .hero-copy {
    max-width: none; margin-inline: auto; transform: none;
  }
  .hero-slide-center .hero-copy h1,
  .hero-slide-mid .hero-copy h1,
  .hero-slide-3 .hero-copy h1 {
    white-space: normal;
  }
}
@media (max-width: 720px) {
  /* Nudges the floating strip up into the hero photo further than the
     base rule's overlap, mobile only, by request — the previous ~9px bump
     here wasn't a noticeable enough move, so this one is a clearly bigger
     jump rather than another small increment. */
  .hero { --hero-strip-overlap: 130px; } /* drives .hero-strip-wrap's margin and .hero-nav's lift */
  /* gap:0 — padding-block on each li (below) now owns 100% of the vertical
     rhythm instead of splitting it with the grid's own row-gap. With both
     contributing at once, the divider line sat asymmetrically close to the
     row above it and far from the row below (gap landed above the line,
     padding-top landed below it) — equal padding-block on every row is
     what actually centers each divider between its two neighbors. */
  .hero-strip { display: grid; grid-template-columns: 1fr; padding: var(--space-6); gap: 0; }
  .hero-strip li {
    /* padding-inline (both sides), not just -start: the base rule set both
       via its own padding-inline shorthand, so zeroing only -start here
       left -end still at space-6 — off-center content in this single-
       column layout, same class of bug as the divider padding fix above. */
    border-inline-start: 0; padding-inline: 0;
    border-top: 1px solid var(--color-divider); padding-block: var(--space-4);
  }
  .hero-strip li:first-child { border-top: 0; padding-top: 0; }
  /* Without this the last row's own padding-bottom stacks on top of the
     strip's own container padding below it, leaving noticeably more air
     under the last item than above the first. */
  .hero-strip li:last-child { padding-bottom: 0; }
  /* flex-start, not the base rule's centered group: centering an icon+text
     pair whose text length varies row to row ("طاولات التلفزيون" vs.
     "طاولات المجالس والاستقبال") put every icon at a different horizontal
     position instead of forming a clean aligned column down the list. */
  .hero-strip-link { justify-content: flex-start; }
  /* Single column here means each name has the card's full width instead of
     a quarter of it — plenty of room to wrap, so let the longer category
     names (e.g. "طاولات المجالس والاستقبال") do that instead of ellipsis-
     truncating the way they need to in the cramped 2/4-column layouts above. */
  .hero-strip-copy strong { white-space: normal; }
  .hero-slide { min-height: 495px; }
  /* The -6%/-3% overscan above exists purely so the parallax translate
     never reveals a bare edge — necessary room for that on desktop's wide
     crop, but at phone widths this box is much narrower/taller relative to
     the photo, so "cover" was already cropping in hard on the sides before
     that extra 3-6% zoom even applies on top of it — the furniture's
     surrounding room context (plants, lamp) was getting cut off entirely.
     Parallax moves slowly (data-speed 0.06) so it doesn't need nearly as
     much margin; cutting the overscan back here shows meaningfully more of
     each photo without actually risking a bare edge during scroll. */
  .hero-slide-bg { inset: -2% -1%; }
  /* Dedicated portrait crops (assets/hero/banner-1..3-mobile.png, 1024x1536 —
     already shot as reframed versions of the 3 desktop banners, empty-wall
     space kept high in-frame the same way as the landscape originals) swap
     in for the desktop banners set above.
     Portrait art at roughly the box's own aspect ratio means "cover" no
     longer needs to crop the sides at all to fill it, unlike the landscape
     originals the .hero-slide-bg comment above was written against. */
  .hero-slide-center .hero-slide-bg { background-image: url('../../assets/hero/banner-1-mobile.png'); }
  .hero-slide-mid .hero-slide-bg { background-image: url('../../assets/hero/banner-2-mobile.png'); }
  .hero-slide-3 .hero-slide-bg { background-image: url('../../assets/hero/banner-3-mobile.png'); }
  /* No card behind the copy on mobile, by request — same treatment as
     desktop now: transparent, resting directly on the photo with only the
     base rule's soft text-shadow halo (.hero-copy, above) for legibility.
     text-align:center here (not just on slide-center/slide-3 the way
     desktop has it) is what actually centers it — .hero-slide-mid never
     had text-align:center even on desktop, so without this its copy sat
     right-aligned on mobile while the other two slides looked centered. */
  .hero-copy { max-width: none; margin-right: 0; text-align: center; }
  /* The 3 per-slide desktop tweaks above (translate/transform nudges to sit
     copy in each banner's own empty-wall gap, plus white-space:nowrap to
     force a heading to a fixed line count) are all sized for the wide
     desktop copy column — at phone widths there's no wall gap to aim for,
     and a nowrap heading at even the clamp's floor size runs wider than the
     viewport, forcing the whole page to scroll horizontally. Reset to a
     plain centered, wrapping block for every slide here instead of
     patching each one's own fixed pixel offset individually. */
  .hero-slide-center .hero-copy,
  .hero-slide-mid .hero-copy,
  .hero-slide-3 .hero-copy {
    max-width: none; margin-inline: auto; transform: none;
  }
  /* Slides 1 and 2, each nudged down a bit further than the base reset
     above, by request — same treatment, added separately as each slide
     asked for it. Slide 2 pushed down further still, by a follow-up
     request scoped to it alone — slide 1 unchanged. */
  .hero-slide-center .hero-copy { margin-top: var(--space-8); }
  .hero-slide-mid .hero-copy { margin-top: calc(var(--space-8) * 1.6); }
  /* Minimized further, by request — was clamp(26px,7vw,36px), still fairly
     large on a phone-width heading that wraps to 2-3 lines. line-height is
     the desktop rule's own 1.06 otherwise — fine for the single/double
     line desktop treats this as, too tight once it's actually wrapping to
     2-3 short lines at these sizes: Arabic diacritics/ligatures need a
     little more room to keep close-set lines from feeling cramped. Tighter
     margin-bottom below it than the desktop space-4 keeps the block's
     overall height roughly net-neutral despite the taller line-height, so
     this doesn't creep further down over the banner photo. Shrunk twice
     more since — was clamp(26px,7vw,36px), then clamp(20px,6vw,28px) — to
     bring the whole hero-copy block down further, by request. */
  .hero-slide-center .hero-copy h1,
  .hero-slide-mid .hero-copy h1,
  .hero-slide-3 .hero-copy h1 {
    white-space: normal; font-size: clamp(16px, 4.6vw, 20px);
    line-height: 1.28; margin-bottom: var(--space-3);
  }
  .hero-copy > .kicker { font-size: 11px; margin-bottom: var(--space-1); }
  /* Lede hidden on mobile only, by request — kicker, heading, and buttons
     stay; just the body paragraph goes, so the copy block reads shorter
     over the banner photo instead of the full desktop pitch. */
  .hero-copy .lede { display: none; }
  /* When the primary + ghost buttons don't both fit on one line at phone
     widths, they wrap — gap alone should space the two resulting lines,
     but on-device reports showed them sitting with little to no visible
     gap. margin-block on the items themselves is a belt-and-suspenders
     fallback that doesn't depend on flexbox gap support in whatever
     rendering engine the visitor's phone uses. justify-content:center
     keeps the button row centered under the now-centered text above it.
     margin-top down from the desktop space-6: the text column above it is
     shorter now (smaller type, tighter rhythm), so the old gap read as too
     large relative to it. */
  .hero-actions { margin-top: var(--space-4); row-gap: var(--space-3); justify-content: center; }
  .hero-actions > .btn { margin-block: var(--space-1); }
  /* Smaller CTA pill on mobile, by request — scoped to the hero's own
     button, not the shared .btn-primary class used elsewhere on the page
     (quick-view, catalogue CTAs, etc.), which stay at their normal size. */
  .hero-actions .btn-primary { padding: 9px 18px; font-size: 13px; }
  /* Ghost "secondary" link dropped entirely on mobile, by request, so the
     hero ends on just the one clear action instead of two competing ones —
     kept on desktop where there's room for both without it reading busy. */
  .hero-actions .hero-link { display: none; }
}

/* --- About / Mission / Vision ---------------------------------------------
   Was "Promo banners" — three marketing CTA tiles all linking to #catalogue.
   Repurposed as informational About/Mission/Vision statements, which changes
   what "the right background" means: a product photo behind an abstract
   Mission statement reads as arbitrary, not branded, so only the About tile
   (which is genuinely about the factory/product) keeps a photo. Mission and
   Vision get solid brand-color treatments instead — navy and amber, the same
   two colors the icons in Why-Choose-Us already use. Renamed .promo-* to
   .about-* throughout since "promo" no longer describes what this is. */
/* No longer nested in .wrap (see index.html) — replicates .wrap's own
   max-width/centering/padding directly so desktop is unaffected, but lets
   the ≤1020px slider override drop that cap for a true full-bleed swipe
   all the way to the screen edge, matching .gal-strip's own technique. */
.about-grid {
  display: grid; grid-template-columns: 1fr 1fr; grid-auto-rows: 1fr; gap: var(--space-6);
  max-width: 1240px; margin-inline: auto; padding-inline: var(--space-6);
}
.about-tile-lg { grid-row: 1 / 3; }
.about-tile {
  position: relative; display: flex; flex-direction: column; justify-content: flex-end;
  overflow: hidden; min-height: 340px; padding: var(--space-6);
  border-radius: var(--radius-lg); text-decoration: none;
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.about-tile-photo { color: var(--color-neutral-100); }
.about-tile-photo:hover { box-shadow: var(--shadow-lg); }
.about-ph { position: absolute; inset: 0; z-index: 0; border-radius: 0; transition: transform 0.5s ease; }
.about-tile-photo:hover .about-ph { transform: scale(1.04); }
.about-ph::after { display: none; } /* our own overlay copy replaces the placeholder caption here */
.about-tile-photo::after {
  /* Scrim so light overlay text stays legible over the (light) placeholder
     pattern — stands in for the natural darkening a real lifestyle photo's
     shadow areas would give the text for free. */
  content: ''; position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--color-accent-2-900) 80%, transparent) 0%,
    color-mix(in srgb, var(--color-accent-2-900) 35%, transparent) 55%,
    transparent 85%);
}
/* 2px, not a hairline, plus a soft matching-color glow (not just the
   ambient .about-tile shadow) so the border itself reads as a deliberate
   accent edge rather than a thin outline — brightens a step further on
   hover, alongside the existing lift. border-color joins the shared
   .about-tile transition list so that brightening animates too. */
.about-tile-mission, .about-tile-vision { transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.2s ease; }
/* Amber, not white: Vision's border below is Mission's own background
   color (--color-accent-2-900) — a deliberate swap where each card's border
   is the other's fill, using only the two brand colors (navy + amber) so
   the pair reads as one coordinated idea rather than one branded edge and
   one generic neutral one. */
.about-tile-mission {
  background: linear-gradient(155deg, var(--color-accent-2-800), var(--color-accent-2-900));
  color: var(--color-neutral-100);
  border: 2px solid var(--color-accent); /* #cd8f2a */
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-accent) 30%, transparent),
              0 8px 28px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
.about-tile-vision {
  background: var(--color-accent); color: var(--color-text);
  border: 2px solid var(--color-accent-2-900); /* #1f1408 */
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-accent-2-900) 22%, transparent),
              0 8px 28px color-mix(in srgb, var(--color-accent-2-900) 16%, transparent);
}
.about-tile-mission:hover { border-color: var(--color-accent-300); }
.about-tile-vision:hover { border-color: var(--color-accent-2-700); }
/* Same brand-mark shape used throughout the rest of the page (.cp, #craft,
   #process, .cta-band…) — Mission/Vision were the one pair of major tiles
   with zero connection to that decorative language, just flat color fills.
   Tucked in the far corner, low-contrast against each tile's own solid
   background, so it reads as texture rather than competing with the copy. */
.about-tile-mission::before, .about-tile-vision::before {
  content: ''; position: absolute; z-index: 0;
  width: 46%; aspect-ratio: 1;
  inset-inline-end: -14%; bottom: -16%;
  -webkit-mask-image: var(--shape-mark); mask-image: var(--shape-mark);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.about-tile-mission::before { background: color-mix(in srgb, var(--color-neutral-100) 8%, transparent); }
.about-tile-vision::before { background: color-mix(in srgb, var(--color-text) 8%, transparent); }
/* Layers the lift shadow on top of each tile's own glow rather than
   replacing it — a plain box-shadow:var(--shadow-md) here would blow away
   the border glow set above the moment you hover. */
.about-tile-mission:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md),
              0 0 0 1px color-mix(in srgb, var(--color-accent) 30%, transparent),
              0 8px 28px color-mix(in srgb, var(--color-accent) 22%, transparent);
}
.about-tile-vision:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md),
              0 0 0 1px color-mix(in srgb, var(--color-accent-2-900) 22%, transparent),
              0 8px 28px color-mix(in srgb, var(--color-accent-2-900) 16%, transparent);
}
.about-icon {
  position: relative; z-index: 2; width: 48px; height: 48px; flex: none;
  display: grid; place-items: center; border-radius: 999px; margin-bottom: var(--space-4);
  transition: transform 0.25s ease;
}
.about-tile-mission .about-icon { background: color-mix(in srgb, var(--color-neutral-100) 15%, transparent); }
.about-tile-vision .about-icon { background: color-mix(in srgb, var(--color-text) 12%, transparent); }
.about-tile:hover .about-icon { transform: translateY(-2px) scale(1.08); }
.about-copy { position: relative; z-index: 2; display: flex; flex-direction: column; gap: var(--space-2); }
.about-tile-photo .about-copy .kicker { color: color-mix(in srgb, var(--color-neutral-100) 85%, transparent); }
.about-tile-mission .kicker { color: color-mix(in srgb, var(--color-neutral-100) 80%, transparent); }
.about-tile-vision .kicker { color: color-mix(in srgb, var(--color-text) 85%, transparent); } /* 70% on amber was 3.6:1 */
.about-copy .kicker { margin-bottom: 0; }
/* .about-tile .about-copy h2 (not just .about-copy h2): same specificity fix
   as the hero h1 above — otherwise html[dir="rtl"] h2's line-height:1.35 wins
   over this tighter overlay value in the default Arabic view. */
.about-tile .about-copy h2 { margin: 0; font-size: clamp(19px, 1.6vw, 24px); line-height: 1.2; }
.about-tile-photo .about-copy h2 { color: var(--color-neutral-100); }
.about-tile-lg .about-copy h2 { font-size: clamp(24px, 2.4vw, 34px); }
.about-copy p { margin: 0; font-size: 14px; line-height: 1.5; max-width: 42ch; }
.about-tile-photo .about-copy p { color: color-mix(in srgb, var(--color-neutral-100) 88%, transparent); }
.about-tile-mission .about-copy p { color: color-mix(in srgb, var(--color-neutral-100) 88%, transparent); }
.about-tile-vision .about-copy p { color: color-mix(in srgb, var(--color-text) 85%, transparent); }
/* Reinforces that the whole (large) About tile is a link to #craft, the
   same arrow-suffixed-text convention as .hero-link elsewhere — without
   it, About was the only "read more" card on the page with no visible cue
   beyond the tile-wide hover shadow. */
.about-tile-cta {
  display: block; margin-top: var(--space-1);
  font-size: 13px; font-weight: 600; letter-spacing: var(--tracking-wide-sm);
  color: var(--color-accent);
  transition: color 0.2s ease;
}
.about-tile-lg:hover .about-tile-cta { color: var(--color-accent-300); }

/* --- Marquee ------------------------------------------------------------
   Kept flat and quiet on purpose (a gradient/mask/bright-dot pass here was
   tried and rejected as too busy) — the upgrade this time is a thin
   accent-tinted ring to lift it off the navy .cp section below plus a
   touch more breathing room, with the separators toned down to small
   muted tick marks instead of loud punctuation.
   The strip is a <button> (a pause toggle, see js/components/marquee.js),
   so the first lines undo the button defaults: full width, inherited font,
   start-aligned content. */
.marquee {
  display: block; width: calc(100% - 2 * var(--space-6));
  appearance: none; font: inherit; text-align: start; cursor: pointer;
  overflow: hidden; background: var(--color-accent-2-800); color: var(--color-neutral-100);
  padding: var(--space-4) 0; border-radius: 999px;
  box-shadow: var(--shadow-md);
  border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
  /* Bottom margin matters here specifically: the next section (.cp) is also a
     dark navy full-bleed block, so with no gap the two same-colored shapes
     fuse into one instead of reading as two separate pieces. */
  margin: 0 var(--space-6) var(--space-8);
}
.marquee-track {
  display: flex; align-items: center; gap: var(--space-6); width: max-content;
  animation: slide 34s linear infinite;
  font-family: var(--font-heading); font-size: 15px; letter-spacing: var(--tracking-wide-md);
  /* The one transform that's animating continuously for as long as it's on
     screen (vs. everything else, which only moves on a discrete hover/state
     change) — worth the standing compositor layer this promotes it to. */
  will-change: transform;
}
.marquee:hover .marquee-track,
.marquee[aria-pressed="true"] .marquee-track { animation-play-state: paused; }
.marquee:disabled { cursor: default; } /* reduced motion: nothing to pause */
.marquee-track span:nth-child(even) {
  color: color-mix(in srgb, var(--color-neutral-100) 40%, transparent); font-size: 8px;
}
@keyframes slide { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } }

/* --- Catalog preview — clean product cards with new collection images ------- */
.cp {
  position: relative; overflow: hidden;
  color: var(--color-neutral-100);
  --focus-ring-color: var(--color-accent-300); /* light ring on the dark section */
  /* assets/backgrounds/ribbed-texture-web.svg — a vector ribbed-wood
     texture (~42KB). It's a repeating texture, not a single photo — tiled at
     a fixed, moderate size rather than stretched full-bleed with `cover`
     (which blows one instance up across the whole wide section, distorting
     the ribs). A strong dark-brown scrim keeps it a quiet backdrop instead
     of competing with the white text and tile photos on top of it. */
  background-color: var(--color-accent-2-900); /* shows through if the image fails to load */
  background-image:
    linear-gradient(color-mix(in srgb, var(--color-accent-2-900) 72%, transparent), color-mix(in srgb, var(--color-accent-2-900) 72%, transparent)),
    url('../../assets/backgrounds/ribbed-texture-web.svg');
  background-size: auto, 480px auto;
  background-position: center;
  background-repeat: repeat;
}
/* Standalone, top-center — a deliberate exception to this page's usual
   left-aligned/asymmetric section intros (.sec-head elsewhere), since this
   grid reads as its own "featured categories" showcase. */
.cp-head { position: relative; z-index: 1; text-align: center; max-width: 520px; margin: 0 auto var(--space-8); }
.cp-kicker {
  margin: 0 0 var(--space-2);
  font-size: 14px;
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: color-mix(in srgb, var(--color-neutral-100) 75%, transparent);
}
.cp-title {
  position: relative;
  display: inline-block;
  margin: 0; padding-bottom: var(--space-4);
  font-size: clamp(32px, 4.6vw, 48px);
  letter-spacing: var(--tracking-wide-lg);
  text-transform: uppercase;
  color: var(--color-neutral-100);
}
/* Centered accent rule under the title, in the same amber the rest of the
   page uses for emphasis (kickers, active nav tab, hover states) — anchors
   the header instead of leaving it as plain text. */
.cp-title::after {
  content: ''; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 48px; height: 3px; border-radius: 999px;
  background: var(--color-accent);
}

/* Two independent stacked flex columns, not a synced grid: column A
   (chairs, side tables) and column B (sofas, tables) each size to their
   own content, so they can fall out of step with each other — that
   stagger is what gives the reference its brick/pinwheel look, and it
   only happens if the columns are free to size independently. direction:
   ltr pins column A physically left / column B physically right
   regardless of the page's own [dir="rtl"] — reproducing a specific fixed
   reference layout here, not a mirrorable one (same technique as
   .head-inner/.hero-copy). */
.cp-grid {
  position: relative; z-index: 1;
  direction: ltr;
  display: flex;
  align-items: flex-start;
  gap: var(--space-6);
  padding: var(--space-6) 0;
}
.cp-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}
/* Explicit offset, now that the header no longer lives in column A: both
   columns used to naturally fall out of step because column A started
   with a short text block and column B started with a full tile — with
   the header pulled out above the grid, both columns start with a same-
   height tile and just align flush, losing the brick/interlock look this
   layout is built around. Half a tile's own height (~210px) pushes column
   B down so its tiles sit between column A's, restoring that stagger. */
.cp-col-b { margin-top: 210px; }
.cp-cell {
  display: flex;
  flex-direction: column;
  padding: var(--space-4);
}
.cp-tile {
  align-items: center;
  justify-content: center;
  text-decoration: none;
}
/* Shape backdrop and photo are siblings, not parent/child: a transform on an
   element carries its whole rendered subtree with it, so if the photo lived
   inside the rotated/masked shape element it would tilt along with it. Kept
   as separate layers in a shared box instead — the shape can rotate freely
   while the photo and the title/count stay flat and upright on top of it. */
.cp-shape-wrap {
  position: relative;
  width: 100%;
  max-width: 340px;
  aspect-ratio: 1 / 1;
  transition: transform 0.35s ease;
}
.cp-tile:hover .cp-shape-wrap {
  transform: scale(1.04);
}
.cp-shape {
  position: absolute; inset: 0;
  background-color: color-mix(in srgb, var(--color-neutral-100) 12%, transparent);
  -webkit-mask-image: var(--shape-mark);
  mask-image: var(--shape-mark);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  transform: rotate(0deg);
  transition: background-color 0.25s ease, transform 3.6s cubic-bezier(0.22, 1, 0.36, 1) 0.15s;
  will-change: transform;
}
.cp-tile:hover .cp-shape {
  background-color: color-mix(in srgb, var(--color-accent) 30%, transparent);
}
/* Each of the 7 shapes does one slow full 360° spin and lands on its own slight,
   distinct resting angle — a subtle pinwheel of angles rather than one
   repeated circle. The resting angles are kept small (±20°) rather than the
   90° increments used elsewhere for this same shape token, so the cut-out
   notch stays roughly in the same top/right quarter for every tile. The
   angle lives on the tile as a custom property and only applies once the
   tile's own .reveal/.is-in scroll trigger fires (see
   js/components/reveal.js), so each shape spins into place as the section scrolls into
   view instead of loading pre-tilted. The text/photo layers are siblings of
   .cp-shape (see the comment on .cp-shape-wrap above), so they stay upright
   and readable through the entire spin, not just at rest. */
.cp-col-a .cp-tile:nth-child(1) { --cp-angle: -14deg; }
.cp-col-a .cp-tile:nth-child(2) { --cp-angle: 10deg; }
.cp-col-a .cp-tile:nth-child(3) { --cp-angle: -8deg; }
.cp-col-a .cp-tile:nth-child(4) { --cp-angle: 12deg; }
.cp-col-b .cp-tile:nth-child(1) { --cp-angle: 16deg; }
.cp-col-b .cp-tile:nth-child(2) { --cp-angle: -11deg; }
.cp-col-b .cp-tile:nth-child(3) { --cp-angle: 7deg; }
.cp-tile.is-in .cp-shape { transform: rotate(calc(1turn + var(--cp-angle, 0deg))); }
.cp-tile .ph {
  position: absolute; inset: 0;
  background-size: 70%;
  background-repeat: no-repeat;
  background-position: center 38%;
}
/* collection-8.png (floor majlis) fills its whole canvas, unlike the other
   collection images, which have empty space around the product — shown a
   little smaller and lower so the tile's label clears it. */
.cp-col-a .cp-tile:nth-child(4) .ph { background-size: 56%; background-position: center 64%; }
/* Sits inside the shape now, not below it — positioned over the notch cut
   out of .cp-shape. Traced the --shape-mark path: the missing wedge runs
   from the top edge to the right edge (a ~90° bite between 12 and 3 o'clock
   on the unrotated mark, bisector at -45° in screen-angle terms), not the
   bottom. Each tile rotates that bisector by its own --cp-angle though (new
   bisector = -45° + --cp-angle), and the six angles above span 30° of
   spread — enough that one fixed anchor visibly missed the notch on the
   tiles furthest from -45° (col-a-1 at -59°, col-b-1 at -29°). The top/
   inset-inline-end pair below is that trig worked out per tile (r≈40% from
   centre along each one's own bisector) instead of one shared guess. A
   light text-shadow is the legibility insurance for whatever ends up behind
   it (shape, photo edge, or the section's own dark background showing
   through the cut). */
.cp-tile-copy {
  position: absolute; z-index: 2;
  top: 9%; inset-inline-end: 9%;
  max-width: 68%;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  text-shadow: 0 1px 6px color-mix(in srgb, var(--color-accent-2-900) 70%, transparent);
}
.cp-col-a .cp-tile:nth-child(1) .cp-tile-copy { top: 10%; inset-inline-end: 10%; }
.cp-col-a .cp-tile:nth-child(2) .cp-tile-copy { top: 15%; inset-inline-end: -20%; }
.cp-col-a .cp-tile:nth-child(3) .cp-tile-copy { top: 10%; inset-inline-end: -5%; }
.cp-col-a .cp-tile:nth-child(4) .cp-tile-copy { top: 10%; inset-inline-end: -8%; }
.cp-col-b .cp-tile:nth-child(1) .cp-tile-copy { top: 30%; inset-inline-end: -5%; }
.cp-col-b .cp-tile:nth-child(2) .cp-tile-copy { top: 5%; inset-inline-end: 5%; }
.cp-col-b .cp-tile:nth-child(3) .cp-tile-copy { top: 15%; inset-inline-end: -10%; }
.cp-tile h3 {
  margin: 0;
  font-size: clamp(14px, 1.4vw, 17px);
  color: var(--color-neutral-100);
  letter-spacing: var(--tracking-wide-md);
  text-transform: uppercase;
  text-align: end;
}
.cp-count {
  margin: 0;
  font-size: 11px;
  color: color-mix(in srgb, var(--color-neutral-100) 85%, transparent);
  text-align: end;
}
/* Slower, larger-travel entrance than the sitewide .reveal default (higher
   specificity than the base .reveal rule, so it overrides its distance/speed
   here without touching how .reveal behaves anywhere else) — reads as tiles
   floating up into place rather than the quick sitewide fade, while still
   riding the same scroll-triggered .reveal/.is-in mechanism. */
.cp-tile.reveal {
  transform: translateY(56px) scale(0.94);
  transition: opacity 1.1s cubic-bezier(0.16, 1, 0.3, 1), transform 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}
.cp-tile.reveal.is-in {
  transform: none;
}

@media (max-width: 720px) {
  /* align-items:stretch (not the base rule's flex-start): once flex-
     direction flips to column, flex-start is the CROSS axis (now
     horizontal) alignment — every .cp-col shrink-wraps to its own content
     width instead of filling the row, which in turn starves .cp-shape-wrap's
     width:100% down to whatever the text content measures, collapsing the
     whole pinwheel of photo tiles into a bare, illegibly-narrow text list.
     stretch is what actually gives each column (and its tiles) the full
     row width to lay out in. */
  .cp-grid { flex-direction: column; align-items: stretch; }
  /* The 210px stagger offset only makes sense side-by-side — stacked into
     one column here, it would just open a large, pointless gap between
     column A and column B. */
  .cp-col-b { margin-top: 0; }
  /* max-width:300px (the desktop cap, meant to keep tiles from ballooning
     in a wide column) is pointless now that the column IS the tile's only
     constraint — dropping it lets the tile use the real available width
     instead of stopping short of it on anything wider than 300px. */
  .cp-shape-wrap { max-width: none; }
  /* Each tile's .cp-tile-copy has its own hand-tuned top/inset-inline-end
     (see the comment above those seven rules) worked out for the desktop
     pinwheel layout, where .cp-shape-wrap is capped at 340px — several go
     negative to deliberately bleed the text past the shape's own edge into
     the surrounding whitespace of the wide two-column grid. Now that
     .cp-shape-wrap is uncapped (full column width, easily 300-400px+) that
     same percentage offset is a much bigger pixel value, pushing text past
     not just the shape but the viewport itself — confirmed by measurement,
     cat4 was clipping outright at 390px wide and three other tiles were
     already sitting outside their own shape-wrap even where the viewport
     still happened to have slack. One safe, uniform inset for all seven here:
     the varied pinwheel positioning only reads on the wide staggered
     desktop grid anyway, not this single stacked column. */
  .cp-col-a .cp-tile:nth-child(1) .cp-tile-copy,
  .cp-col-a .cp-tile:nth-child(2) .cp-tile-copy,
  .cp-col-a .cp-tile:nth-child(3) .cp-tile-copy,
  .cp-col-a .cp-tile:nth-child(4) .cp-tile-copy,
  .cp-col-b .cp-tile:nth-child(1) .cp-tile-copy,
  .cp-col-b .cp-tile:nth-child(2) .cp-tile-copy,
  .cp-col-b .cp-tile:nth-child(3) .cp-tile-copy {
    top: 8%; inset-inline-end: 6%;
  }
}

/* Same brand-mark watermark as #craft/.site-foot, but flattened to white
   (brightness(0) invert(1), same trick as .watermark-foot) since this
   section shares that dark navy background rather than a light one.
   Dead-centered rather than corner-anchored, so this one overrides the
   shared .watermark rule's inset-inline-end positioning entirely instead
   of just its size. */
.watermark-cp {
  width: 34vw; max-width: 460px;
  inset-inline-end: auto; bottom: auto;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  filter: brightness(0) invert(1);
  opacity: 0.05; /* fainter than the shared .watermark default (0.12) — this one is much bigger and dead-centered, so it needs to stay further back to not compete with the tiles sitting on top of it */
}

/* Wider than the site's standard 1240px .wrap — with 3 slides worth of
   feature cards and 44px nav buttons on each side of the sliders below,
   the standard width was leaving rows 2–3 showing barely 1.5 cards at a
   time. Scoped to #catalogue's own .wrap only, sec-head included, so the
   header doesn't end up narrower than the rows sitting under it. */
#catalogue .wrap { max-width: 1480px; }

/* Three rows, all sliders — replaces the old flat 4-column .prod-grid. All
   three now share one card size (see .prod-slider-track > .prod below) —
   row 1 used to run wider 420px feature slides, but with every row's photo
   now the same square catalogue render (see .ph-prod's aspect-ratio:1/1),
   there's no content-driven reason left for row 1 to be a different size,
   and a uniform grid reads as more consistent than "one row deliberately
   bigger". */
.prod-rows { display: flex; flex-direction: column; gap: var(--space-8); }
.catalogue-more { display: flex; justify-content: center; margin-top: var(--space-8); }

/* Rows 2–3 — horizontally scrolling sliders. Arrow buttons step by DOM
   index and hand off to scrollIntoView (see
   js/components/product-sliders.js) rather than computing scrollBy pixel offsets by hand — that
   sidesteps the cross-browser inconsistency in how scrollLeft behaves
   under [dir="rtl"], since scrollIntoView's own inline:'start' already
   resolves against the container's actual direction. */
.prod-row-slider { position: relative; display: flex; align-items: center; gap: var(--space-4); }
.prod-slider-track {
  display: flex; gap: var(--space-6); flex: 1;
  overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-padding-inline-start: var(--space-2);
  padding-block: var(--space-2); /* clearance for .quick's own translateY so it doesn't clip */
  scrollbar-width: none;
}
.prod-slider-track::-webkit-scrollbar { display: none; }
.prod-slider-track > .prod { flex: 0 0 auto; width: min(360px, 76vw); scroll-snap-align: start; }

/* --- Story -------------------------------------------------------------- */
/* Watermark anchored behind the copy column, not the photo figure — never
   competing with real photography, and (via inset-inline-end, not left/right)
   it stays on the copy's side whichever edge that is in RTL vs LTR. Overrides
   the shared .watermark rule's inset-inline-end here: that formula anchors to
   .wrap's content-column edge, but this instance is meant to sit at the
   section's own physical edge instead. */
#craft { position: relative; }
.watermark-craft { width: 22vw; max-width: 300px; bottom: var(--space-8); inset-inline-end: var(--space-6); }
.story {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 0.9fr 1.1fr; gap: calc(var(--space-8) * 1.4); align-items: center;
}
.story-figure { position: relative; overflow: visible; }
.ph-story {
  aspect-ratio: 4 / 5; border-radius: 999px 999px var(--radius-lg) var(--radius-lg);
  transition: transform 0.5s ease;
}
/* Slow, deliberate zoom on hover — same 0.5s treatment .ph-prod uses for
   catalogue cards, echoed here so real photography across the page shares
   one "considered reveal" feel rather than a twitchy default. Guarded by
   the figure (not the photo itself) so the floating badge below can still
   sit outside the photo's own clipped box without being clipped too. */
.story-figure:hover .ph-story { transform: scale(1.04); }
/* Floating location badge — overlaps the pill photo's bottom-start corner,
   same "real card floating over a photo" language as .prod::before's
   category chip, but a light surface here (not a dark chip) since it sits
   on empty page background past the photo edge, not on top of the image
   itself. inset-inline-* keeps it correctly mirrored under [dir="rtl"]. */
.story-badge {
  position: absolute; z-index: 2; bottom: calc(var(--space-6) * -1); inset-inline-start: calc(var(--space-6) * -1);
  display: inline-flex; align-items: center; gap: var(--space-2);
  max-width: calc(100% - var(--space-4));
  background: var(--color-bg); color: var(--color-text);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  font-size: 12px; font-weight: 600; line-height: 1.3;
}
.story-badge svg { flex: none; color: var(--color-accent-700); }
.story-copy h2 { font-size: clamp(30px, 3.6vw, 44px); }
.story-copy p { max-width: 52ch; color: color-mix(in srgb, var(--color-text) 74%, transparent); }
.story-list { list-style: none; padding: 0; margin: var(--space-6) 0; display: grid; gap: var(--space-3); }
.story-list li {
  display: flex; align-items: center; gap: var(--space-3); font-size: 14px;
}
.story-list svg {
  flex: none; width: 34px; height: 34px; padding: 8px; box-sizing: border-box;
  border-radius: 999px; color: var(--color-accent-2-600);
  background: var(--color-accent-2-100);
}
.story-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }

/* --- Why choose us -------------------------------------------------------- */
.why { position: relative; overflow: hidden; }
/* .why.section-alt (2 classes) to outrank the plain .section-alt fill —
   a soft navy-tinted gradient instead of the flat neutral gray, echoing
   the accent-2 watermark/icon colors already in this section rather than
   introducing a new hue. Scoped here only so #catalogue (the other
   .section-alt section) keeps its plain fill. */
.why.section-alt {
  background: linear-gradient(165deg, var(--color-neutral-100) 0%, var(--color-accent-2-100) 100%);
}
.why::before {
  /* Same brand-mark shape as the catalog-preview section, at a whisper of
     opacity — ties this section into that decorative language instead of
     sitting flat and disconnected from it. */
  content: ''; position: absolute; z-index: 0;
  width: 26vw; max-width: 320px; aspect-ratio: 1;
  inset-inline-end: -6%; bottom: -10%;
  background: color-mix(in srgb, var(--color-accent-2-600) 7%, transparent);
  -webkit-mask-image: var(--shape-mark); mask-image: var(--shape-mark);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.why-grid {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: 0.85fr 1.15fr; gap: calc(var(--space-8) * 1.4); align-items: start;
}
.why-intro h2 { font-size: clamp(28px, 3.4vw, 40px); }
.why-intro .sec-note { max-width: 40ch; margin-top: var(--space-2); }
/* Each feature is now a real card (background + shadow) rather than icon
   and text floating directly on .section-alt's own gray — matches the
   .step cards' fix elsewhere in the page (same problem: no contrast
   against the alt background without one). A quiet counter-based index
   number and a hover-revealed top accent line are the restrained "premium
   editorial" touches; kept deliberately simple after the marquee's busier
   pass didn't land. */
.why-features { counter-reset: why-count; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6); }
.why-feat {
  position: relative; counter-increment: why-count;
  display: flex; flex-direction: column; gap: var(--space-3);
  background: var(--color-bg); border-radius: var(--radius-lg);
  padding: var(--space-6); box-shadow: var(--shadow-sm);
  border-top: 3px solid transparent;
  transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
}
.why-feat:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-top-color: var(--color-accent); }
.why-feat::after {
  /* --space-5 was never defined (the scale only has 1/2/3/4/6/8) — an
     undefined custom property with no fallback makes the whole declaration
     invalid rather than an error, so this silently computed to top:auto
     this entire time instead of the intended inset. */
  content: counter(why-count, decimal-leading-zero);
  position: absolute; top: var(--space-4); inset-inline-end: var(--space-6);
  font-family: var(--font-heading); font-weight: var(--font-heading-weight);
  font-size: 13px; letter-spacing: var(--tracking-wide-md);
  color: color-mix(in srgb, var(--color-text) 18%, transparent);
}
.why-icon {
  width: 68px; height: 68px; flex: none; border-radius: 999px;
  display: grid; place-items: center;
  background: var(--color-accent-2-100); box-shadow: var(--shadow-sm);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.why-feat:hover .why-icon { transform: translateY(-3px) scale(1.05); box-shadow: var(--shadow-md); }
.why-feat h3 { font-size: 17px; margin: 0; transition: color 0.2s ease; }
.why-feat:hover h3 { color: var(--color-accent-2-700); }
.why-feat p { font-size: 13px; margin: 0; color: color-mix(in srgb, var(--color-text) 62%, transparent); }

/* --- Process ------------------------------------------------------------ */
#process { position: relative; overflow: hidden; }
#process::before {
  /* Same brand-mark shape as the other major sections — Process was the one
     section with no connection to that decorative system. */
  content: ''; position: absolute; z-index: 0;
  width: 22vw; max-width: 260px; aspect-ratio: 1;
  inset-inline-end: -6%; top: -10%; transform: rotate(180deg);
  background: color-mix(in srgb, var(--color-accent-2-600) 6%, transparent);
  -webkit-mask-image: var(--shape-mark); mask-image: var(--shape-mark);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
#process > .wrap { position: relative; z-index: 1; }
/* Both "من التصفح إلى التسليم" and "From browsing to delivery" are short
   enough to hold on one line at a smaller floor than .sec-head h2's shared
   30px minimum — lowered here (scoped to #process only, not the shared
   rule every other section's heading also uses) specifically so nowrap
   below has room to keep it there down to the narrowest phone widths
   without overflowing past the viewport. */
#process .sec-head h2 { white-space: nowrap; font-size: clamp(22px, 6vw, 46px); }

/* No longer nested in #process's .wrap (see index.html) — replicates
   .wrap's own max-width/centering/padding directly, same reasoning as
   .about-grid: keeps desktop unchanged while letting the ≤1020px slider
   override drop the cap for a true full-bleed swipe to the screen edge.
   position:relative + z-index:1 replicates the stacking #process > .wrap
   used to give it for free — without it, a plain static block paints
   *under* #process::before's positioned (if z-index:0) decorative shape. */
.steps {
  position: relative; z-index: 1;
  list-style: none; padding-block: 0; margin-block: 0;
  max-width: 1240px; margin-inline: auto; padding-inline: var(--space-6);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-6);
}
/* One brand-color "journey" per step — amber at the start, navy at
   delivery, the two middle steps interpolated between them via color-mix
   rather than hand-picked — reused below by both .step-no's fill and
   .step-ghost's tint, so the two stay in sync from one definition instead
   of two separate color decisions that could drift apart. Text pairs with
   each fill for contrast: dark on the light amber end, white from the
   midpoint on — each pair at least 4.5:1 (step 1's accent-900 ink and a
   62/38 mix for step 2 fell just short). Scoped to desktop only (see the
   ≥1021px block further down) — the color still applies to .step-no on
   mobile via the base values below, just without the extra staggered
   rhythm that only reads correctly in a row. */
.step:nth-child(1) { --step-tint: var(--color-accent-500); --step-tint-ink: var(--color-neutral-900); }
.step:nth-child(2) { --step-tint: color-mix(in srgb, var(--color-accent-500) 50%, var(--color-accent-2-600) 50%); --step-tint-ink: var(--color-neutral-100); }
.step:nth-child(3) { --step-tint: color-mix(in srgb, var(--color-accent-500) 28%, var(--color-accent-2-600) 72%); --step-tint-ink: var(--color-neutral-100); }
.step:nth-child(4) { --step-tint: var(--color-accent-2-600); --step-tint-ink: var(--color-neutral-100); }
.step {
  position: relative;
  /* Was the exact same color as .section-alt's own background — the four
     "cards" had no visible edge, shadow, or separation from the section at
     all. --color-bg is a genuinely distinct value, plus a real shadow. */
  background: var(--color-bg); border-radius: var(--radius-lg);
  padding: var(--space-6); box-shadow: var(--shadow-sm);
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.step:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
/* Oversized ghost numeral behind each card's own content — a large, quiet
   "1"–"4" in the step's own journey-color, editorial-poster style rather
   than another small badge. Sits at z-index:0 under everything else (the
   real content stack gets z-index:1 below); deliberately not clipped by
   .step's own overflow (.step has none set — the connecting waypoint
   pseudo-element a few rules down relies on being able to bleed past this
   same card's edge into the gap, so overflow:hidden here isn't an option),
   but low opacity plus a modest inset keeps any edge bleed unnoticeable. */
.step-ghost {
  position: absolute; z-index: 0; inset-block-end: calc(var(--space-3) * -1); inset-inline-end: var(--space-3);
  font-family: var(--font-heading); font-weight: var(--font-heading-weight); line-height: 1;
  font-size: clamp(72px, 7vw, 96px);
  color: var(--step-tint); opacity: 0.1;
  pointer-events: none; user-select: none;
}
.step-no {
  position: relative; z-index: 1;
  display: grid; place-items: center; width: 44px; height: 44px;
  border-radius: 999px; background: var(--step-tint, var(--color-accent-200)); color: var(--step-tint-ink, var(--color-accent-800));
  font-family: var(--font-heading); font-size: 15px; margin-bottom: var(--space-4);
  box-shadow: var(--shadow-sm);
}
/* Small corner icon — one per step, so the "what happens here" registers at
   a glance before a shopper reads a word of the title/description (the
   clarity + speed half of this section's own brief). Kept unboxed and
   quiet (no background badge, no competing with .step-no's own circle as
   the card's one strong focal point) — inset from the opposite corner. */
.step-icon {
  position: absolute; z-index: 1; top: var(--space-6); inset-inline-end: var(--space-6);
  color: color-mix(in srgb, var(--color-accent-2-600) 55%, transparent);
}
.step h3 { position: relative; z-index: 1; font-size: 19px; padding-inline-end: var(--space-8); }
.step p { position: relative; z-index: 1; margin: 0; font-size: 14px; color: color-mix(in srgb, var(--color-text) 70%, transparent); }

/* Gentle staircase rhythm — desktop only (needs a single row to read as
   motion rather than a misaligned grid; the ≤1020px stacked column below
   turns this back off). Cards stay the grid's default align-items:stretch
   (all four the same size, matching the tallest) — transform:translateY
   only shifts an element's painted position, not its box size or the
   grid's own row track sizing, so the stagger doesn't need — and shouldn't
   use — natural/unequal heights to work. */
@media (min-width: 1021px) {
  .step:nth-child(even) { transform: translateY(28px); }
  .step:nth-child(even):hover { transform: translateY(24px); }
  /* The connecting puck's top:50% is each step's own vertical center — true
     to the eye on its own, but a staggered neighbor's center now sits 28px
     off from that. Splitting the difference (±14px, sign flipped between
     odd/even anchors since the stagger direction reverses each time) keeps
     every puck sitting at the actual visual midpoint between the two cards
     it's bridging instead of the unshifted card's own center. */
  .step:nth-child(odd):not(:last-child)::after { top: calc(50% + 14px); }
  .step:nth-child(even):not(:last-child)::after { top: calc(50% - 14px); }
}

/* Connecting waypoint between steps — a small floating puck in the gap
   rather than a bare arrow glyph, so it reads as a stop on a path instead
   of punctuation. Anchored to the card's own edge then shifted by 50% of
   its own size, so it centers in the gap regardless of the gap's exact
   width/height — same trick in both the horizontal (desktop) and vertical
   (stacked, ≤1020px) orientations below, just on the other axis. */
.step:not(:last-child)::after {
  content: '←'; position: absolute; z-index: 1;
  top: 50%; inset-inline-end: 0; transform: translate(50%, -50%);
  width: 28px; height: 28px; display: grid; place-items: center;
  border-radius: 999px; background: var(--color-bg); box-shadow: var(--shadow-sm);
  font-size: 14px; color: var(--color-accent-2-400);
}
html[dir="ltr"] .step:not(:last-child)::after { content: '→'; }

/* --- Partner testimonials -------------------------------------------------
   Logos are real partner marks (assets/partners/), but the quotes are
   placeholder copy — deliberately generic rather than invented claims
   attributed to a named company, ready to be swapped for real testimonials
   later the same way every .ph photo placeholder is meant to be. */
.partners-head { text-align: center; max-width: 560px; margin: 0 auto var(--space-8); }
/* No longer nested in .wrap (see index.html) — same reasoning and
   technique as .about-grid/.steps: replicates .wrap's own max-width/
   centering/padding here so desktop is unaffected, while the ≤1020px
   slider override below drops the cap for a full-bleed swipe. */
.partners-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-6);
  max-width: 1240px; margin-inline: auto; padding-inline: var(--space-6);
}
.partner-card {
  position: relative;
  display: flex; flex-direction: column; align-items: center; text-align: center;
  background: var(--color-bg); border-radius: var(--radius-lg);
  border-top: 3px solid var(--color-accent);
  padding: var(--space-8) var(--space-6) var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.partner-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.partner-quote {
  position: absolute; top: var(--space-4); inset-inline-end: var(--space-4);
  color: color-mix(in srgb, var(--color-accent) 22%, transparent);
}
/* object-fit:contain, not cover — the three logos are very different
   proportions (a wide wordmark, a near-square mark, a mid-width badge), so
   a fixed-height box + contain shows each intact rather than cropping any
   of them to force a common shape. */
.partner-logo {
  max-width: 76%; height: 46px; width: auto;
  object-fit: contain; margin-bottom: var(--space-4);
}
.partner-stars { display: flex; gap: 2px; color: var(--color-accent); margin-bottom: var(--space-4); }
.partner-text {
  margin: 0; font-size: 14px; line-height: 1.6;
  color: color-mix(in srgb, var(--color-text) 78%, transparent);
}
.partner-divider { width: 48px; height: 1px; background: var(--color-divider); margin: var(--space-6) 0 var(--space-4); }
.partner-name { margin: 0; font-family: var(--font-heading); font-weight: var(--font-heading-weight); font-size: 15px; }
@media (max-width: 1020px) {
  /* Swipeable horizontal slider instead of a stacked column — same
     scroll-snap + peek-of-next-card pattern as .about-grid/.steps/
     .gal-strip elsewhere on the page. */
  .partners-grid {
    display: flex; grid-template-columns: none;
    overflow-x: auto; scroll-snap-type: x mandatory;
    gap: var(--space-4); padding-bottom: var(--space-3);
    max-width: none; margin-inline: 0;
    scrollbar-width: thin; scrollbar-color: var(--color-neutral-400) transparent;
  }
  .partners-grid::-webkit-scrollbar { height: 6px; }
  .partners-grid::-webkit-scrollbar-track { background: transparent; }
  .partners-grid::-webkit-scrollbar-thumb { background: var(--color-neutral-400); border-radius: 999px; }
  .partner-card { flex: 0 0 auto; width: 82vw; scroll-snap-align: start; }
}

/* --- Gallery -------------------------------------------------------------*/
/* Was the one section missing the .section class (0 top padding of its own),
   so the gap before it was half of every other section-to-section gap on the
   page — including on mobile, where .section's own breakpoint didn't apply
   to it either. Now uses .section for spacing like every other section. */
.gal-strip {
  display: grid; grid-auto-flow: column; grid-auto-columns: 22vw;
  gap: var(--space-4); overflow-x: auto; padding: 0 var(--space-6) var(--space-4);
  scroll-snap-type: x mandatory;
  /* A default system scrollbar reads as an unstyled leftover on an otherwise
     fully-themed page — thin it and tint it with the brand accent instead of
     hiding it outright, since this strip's only affordance that it scrolls
     is the scrollbar itself. */
  scrollbar-width: thin;
  scrollbar-color: var(--color-neutral-400) transparent;
}
.gal-strip::-webkit-scrollbar { height: 6px; }
.gal-strip::-webkit-scrollbar-track { background: transparent; }
.gal-strip::-webkit-scrollbar-thumb { background: var(--color-neutral-400); border-radius: 999px; }
.gal-strip::-webkit-scrollbar-thumb:hover { background: var(--color-accent); }
.gal-strip .ph-gal { aspect-ratio: 1; scroll-snap-align: start; }
.gal-strip .ph-gal:nth-child(odd) { border-radius: 999px; }

/* --- RTL + reduced motion ---------------------------------------------- */
[dir="rtl"] .marquee-track { animation-direction: reverse; }
@media (prefers-reduced-motion: reduce) { .cp-shape { transition: none; transform: rotate(var(--cp-angle, 0deg)); } }
@media (prefers-reduced-motion: reduce) { .hero-slide.is-active { animation: none; } }

/* --- Responsive ---------------------------------------------------------*/
@media (max-width: 1020px) {
  /* Swipeable horizontal slider instead of a stacked column — same
     scroll-snap + peek-of-next-card pattern as .about-grid/.gal-strip
     elsewhere on the page. The journey stays laid out left-to-right here
     too, so the base (desktop) connector-puck rule below needs no mobile
     override anymore — a horizontal arrow between horizontally-scrolling
     cards is already the right visual, unlike the old stacked column,
     which needed it rotated into a "↓". */
  .steps {
    display: flex; grid-template-columns: none;
    max-width: none; margin-inline: 0;
    overflow-x: auto; scroll-snap-type: x mandatory;
    gap: var(--space-4); padding-bottom: var(--space-3);
    scrollbar-width: thin; scrollbar-color: var(--color-neutral-400) transparent;
  }
  .steps::-webkit-scrollbar { height: 6px; }
  .steps::-webkit-scrollbar-track { background: transparent; }
  .steps::-webkit-scrollbar-thumb { background: var(--color-neutral-400); border-radius: 999px; }
  .step { flex: 0 0 auto; width: 82vw; scroll-snap-align: start; }
  /* Same slider treatment, same breakpoint as .steps/.partners-grid above/
     below — all three card-row sections now switch to a swipeable slider
     at the same width instead of About lagging behind at 720px while the
     other two already had (a plain 2-column grid in between read as an
     inconsistent, half-converted layout). */
  .about-grid {
    display: flex; grid-template-columns: none;
    max-width: none; margin-inline: 0;
    overflow-x: auto; scroll-snap-type: x mandatory;
    gap: var(--space-4); padding-bottom: var(--space-3);
    scrollbar-width: thin; scrollbar-color: var(--color-neutral-400) transparent;
  }
  .about-grid::-webkit-scrollbar { height: 6px; }
  .about-grid::-webkit-scrollbar-track { background: transparent; }
  .about-grid::-webkit-scrollbar-thumb { background: var(--color-neutral-400); border-radius: 999px; }
  .about-tile-lg { grid-row: auto; }
  .about-tile { flex: 0 0 auto; width: 82vw; min-height: 260px; scroll-snap-align: start; }
  .story, .why-grid { grid-template-columns: 1fr; }
  /* Stacked layout needs room under the figure for the badge it no longer
     has a second column to overlap into. */
  .story-figure { margin-bottom: var(--space-8); }
  .story-badge { max-width: calc(100% - var(--space-6)); }
  .why-features { grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
  .gal-strip { grid-auto-columns: 44vw; }
}
@media (max-width: 720px) {
  /* story_h2's <br> is a deliberate 2-line break for the wide desktop
     column ("من مصنع واحد، / إلى كل بيت.") — on one line on mobile
     instead, by request. Hiding the <br> alone isn't enough: the shared
     clamp(30px,3.6vw,44px) floor is still too large to fit this phrase in
     one line even at the narrowest widths (tested down to 320px), so it
     needs a smaller size dedicated to this breakpoint too, not just
     nowrap — verified against 320-720px with real margin to spare, not
     tuned to a bare fit (see the #process heading's own comment further
     down for why a zero-margin nowrap fit is worth avoiding). */
  .story-copy h2 br { display: none; }
  .story-copy h2 { font-size: clamp(20px, 7vw, 26px); white-space: nowrap; }
  /* The one-line fit above was measured on the Arabic heading. The English
     one is too long for it: held on one line, it widened the whole column
     past the screen and cut off the paragraphs below on phones. */
  html[lang="en"] .story-copy h2 { white-space: normal; }
  /* Full resize pass for the whole Factory/Brand (#craft) block, mobile
     only, by request: photo capped and centered instead of running the
     full column width, body copy and list down a step from the
     inherited desktop/body sizes, and the two actions scoped to this
     section only (not the shared .btn used everywhere else on the page)
     so they don't carry their full desktop padding into a stacked phone
     layout. */
  /* width, not just max-width: .story-figure is a grid item that normally
     stretches to its column's full width (nothing else here gives it or
     the photo inside it a definite size) — max-width alone paired with
     margin-inline:auto disables that stretch without supplying a width to
     replace it, collapsing the figure to 0 and, via the badge's own
     max-width:calc(100%-...) resolving against that zero, breaking the
     location badge into a near-unreadable single-word-per-line stack. An
     explicit width sidesteps the ambiguity entirely. */
  .story-figure { width: 260px; max-width: 100%; margin-inline: auto; }
  .story-badge { font-size: 11px; padding: var(--space-2) var(--space-3); }
  .story-copy p { font-size: 14px; }
  .story-list { margin: var(--space-4) 0; gap: var(--space-2); }
  .story-list li { font-size: 13px; gap: var(--space-2); }
  .story-list svg { width: 28px; height: 28px; padding: 6px; }
  .story-actions { gap: var(--space-2); }
  .story-actions .btn { padding: 9px 18px; font-size: 13px; }
  /* auto-fit/minmax, not a flat 1fr, by request — lets the 3 cards wrap
     naturally against whatever width is actually available instead of
     being locked to one column at every mobile size: a narrow phone still
     gets 1 per row (240px min doesn't leave room for 2), but a wider phone
     or landscape/phablet width naturally wraps to 2-then-1 instead of 3
     stacked full-width cards. Paired with the card's own scaled-down icon/
     padding/type below so a 1-column fallback still reads as sized for a
     small screen, not a shrunk desktop card. */
  .why-features { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: var(--space-4); }
  .why-feat { padding: var(--space-4); gap: var(--space-2); }
  .why-icon { width: 52px; height: 52px; }
  .why-feat h3 { font-size: 15px; }
  .why-feat p { font-size: 12.5px; }
  .gal-strip { grid-auto-columns: 72vw; }
  .story-badge { inset-inline-start: 0; bottom: calc(var(--space-4) * -1); font-size: 11px; padding: var(--space-2) var(--space-3); }
  /* Card width is min(360px, 76vw) — tuned assuming the track gets the row's
     full width. With both 44px arrows still flanking it at this viewport,
     the track's own visible box shrank to a sliver (the card itself is
     wider than what's left after 2×44px + gap), so at rest only a fraction
     of the first card was actually visible instead of the intended "peek
     of the next card" affordance. Touch scrolling + scroll-snap (already
     wired up) is the natural mobile interaction here anyway — same
     reasoning .hero-arrow already applies at this same breakpoint. */
  .prod-row-slider .prod-slider-nav { display: none; }
  /* Two-per-row peek, by request: 58vw fits the first card in full plus a
     clear (not sliver-thin) partial second card in the remaining width,
     signaling there's more to swipe to — same swipe-only interaction the
     nav-arrow removal above already established, just sized so the "next
     card" affordance actually reads instead of the general min(360px,76vw)
     rule's near-full-width single card at these widths. */
  .prod-slider-track > .prod { width: 58vw; }
  /* Catalog refinement pass, by request — card and type sized down and
     the various gaps tightened so a 58vw card doesn't carry desktop-scale
     padding/type/spacing around content that's now noticeably smaller.
     Scoped to #catalogue's own elements (not the shared .sec-head/.prod
     classes' base rules) so nothing here touches how other sections look
     on mobile. */
  #catalogue .sec-head { margin-bottom: var(--space-4); }
  #catalogue .sec-note { font-size: 13px; }
  .prod-rows { gap: var(--space-4); }
  .prod-slider-track { gap: var(--space-3); }
}
@media (max-width: 400px) {
  /* #process .sec-head h2's own nowrap (see #process, above) is pinned to a fixed
     22px floor below ~367px wide (6vw stops shrinking it further), banking
     on the Arabic heading's rendered width staying under what's left of
     the viewport at that fixed size — margin thin enough by design that
     small cross-engine differences in Arabic text shaping/kerning (Gecko
     vs. Blink render the same Tajawal string at slightly different pixel
     widths) can tip it into overflow on one engine but not another, unlike
     every other nowrap heading on this page, which already has a
     white-space:normal fallback somewhere in the cascade. Last-resort
     wrap, scoped to only the narrowest tier where the floor offers no
     further shrink room — leaves the deliberate one-line look untouched
     everywhere wider. */
  #process .sec-head h2 { white-space: normal; }
}

/* --- Scroll reveal, re-asserted ---------------------------------------------
   The .reveal states live in css/base.css. In the single stylesheet this
   page used to load, they came after every card rule above, so here
   .reveal's transition and .is-in's transform:none have always won over
   the cards' own (.prod in css/components/product-card.css, .why-feat,
   .step, .partner-card): the cards fade in over 0.7s and their :hover lift
   and the process steps' desktop stagger never apply. Repeated last so the
   split keeps that exact behaviour — delete this block to let the cards'
   own transitions and hover lifts take effect. */
.reveal { transition: opacity 0.7s ease, transform 0.7s ease; }
.reveal.is-in { transform: none; }
