/* BONO FOOD — layout and components. One file, both directions.

   Direction-dependent values multiply by var(--dir); everything else is
   direction-neutral and must stay that way, so /ar/ needs no second sheet.

   Colour comes only from tokens.css. Two rules are load-bearing:
     - `color: var(--grass)` is FORBIDDEN anywhere (check_structure enforces
       it). --grass clears AA on one ground by 0.01 and fails the other two.
       --leaf is the green that may carry text on light.
     - --zest is text on --forest and --canopy ONLY. On white it is 1.59:1.
       No scanner can see the ground, so that one is on the author.

   Reveal contract is [data-reveal] + .is-lit, driven by motion.js. Targets
   ship VISIBLE; the hidden pre-reveal state lives behind html.js, so a
   crawler or a failed script still sees every word. */

/* ---------------------------------------------------------- base ---- */
*, *::before, *::after { box-sizing: border-box; }
html, body, h1, h2, h3, p, figure, dl, dd, ul, ol { margin: 0; padding: 0; }
ul, ol { list-style: none; }
html { scroll-behavior: smooth; }

body {
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.65 var(--face-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; }

:focus-visible { outline: 3px solid var(--leaf); outline-offset: 3px; border-radius: 2px; }
/* --leaf vanishes on the dark grounds; --zest is 9.32:1 there. */
.hd:not(.is-small) :focus-visible, .hero :focus-visible, .stats :focus-visible,
.cta :focus-visible, .ft :focus-visible, .band :focus-visible {
  outline-color: var(--zest);
}
/* The condensed header is 94% white, so --zest there is 1.59:1 — a focus
   ring nobody can see, failing SC 1.4.11 and 2.4.11. The hover colour was
   already guarded for .is-small further down; the focus ring was not. */
.hd.is-small :focus-visible { outline-color: var(--leaf); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.skip {
  position: absolute; inset-inline-start: 12px; top: -60px; z-index: 100;
  background: var(--canopy); color: var(--paper);
  padding: 12px 18px; font-weight: 600;
  transition: top .2s var(--ease);
}
.skip:focus { top: 12px; }

h1, h2, h3 {
  font-family: var(--face-display);
  font-weight: 800; line-height: .98;
  letter-spacing: var(--track-display);
  text-wrap: balance;
}
.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: var(--pad); }
.mono {
  font: 500 11px/1 var(--face-mono);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}

.js [data-reveal] {
  opacity: 0;
  transform: translate(calc(10px * var(--dir)), 30px);
  transition: opacity var(--reveal-dur) var(--ease),
              transform var(--reveal-dur) var(--ease);
}
.js [data-reveal].is-lit { opacity: 1; transform: none; }

/* #main, not <html>: it is the common ancestor of every [data-reveal] and a
   SIBLING of .hd, so clipping here cannot reach the header's position:sticky.
   `clip`, not `hidden` — clip never promotes the other axis to auto and never
   creates a scroll container. */
#main { overflow-x: clip; }

/* -------------------------------------------------------- buttons ---- */
.btn {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 15px 26px; border: 0; border-radius: 2px;
  font: 600 15px/1 var(--face-body); cursor: pointer;
  transition: transform .3s var(--ease), background .3s, color .3s, border-color .3s;
}
/* --canopy, NOT --grass: white on --grass is 3.29:1 and fails AA. The
   approved mockup shipped exactly that bug in its header button. */
.btn-solid { background: var(--canopy); color: var(--paper); }
.btn-solid:hover { background: var(--forest); transform: translateY(-3px); }
.btn-zest { background: var(--zest); color: var(--forest); }
.btn-zest:hover { background: var(--paper); transform: translateY(-3px); }
.btn-ghost { border: 1.5px solid rgba(255,255,255,.42); color: var(--paper); }
.btn-ghost:hover { border-color: var(--zest); color: var(--zest); transform: translateY(-3px); }

/* --------------------------------------------------------- header ---- */
.hd {
  position: fixed; inset-block-start: 0; inset-inline: 0; z-index: 60;
  display: flex; align-items: center; gap: clamp(16px, 3vw, 40px);
  padding: 18px var(--pad);
  transition: padding .45s var(--ease), background .45s var(--ease), box-shadow .45s var(--ease);
}
.hd.is-small {
  padding: 9px var(--pad);
  background: rgba(255,255,255,.94);
  backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 var(--line);
}
/* Product and contact pages begin on a light surface. Keeping their header
   transparent made the white primary navigation disappear until the visitor
   scrolled, which is an especially poor first impression on a catalogue. */
.hd--solid {
  background: rgba(255,255,255,.97);
  box-shadow: 0 1px 0 var(--line);
}
.hd-mark { display: flex; align-items: center; line-height: 0; flex: 0 0 auto; }
.hd-mark img {
  height: 52px; width: auto; border-radius: 50%;
  background: var(--paper); padding: 3px;
  box-shadow: 0 2px 14px rgba(0,0,0,.18);
  transition: height .45s var(--ease), box-shadow .45s var(--ease);
}
.hd.is-small .hd-mark img { height: 40px; box-shadow: none; }

.hd-nav, .hd-lang { display: flex; gap: clamp(12px, 2vw, 26px); align-items: center; }
.hd-nav { margin-inline-start: auto; }
.hd-nav a, .hd-lang a {
  font: 600 12.5px/1 var(--face-body);
  letter-spacing: .05em; text-transform: uppercase;
  color: rgba(255,255,255,.86);
  transition: color .45s var(--ease);
}
.hd.is-small .hd-nav a, .hd.is-small .hd-lang a { color: var(--ink-soft); }
.hd--solid .hd-nav a, .hd--solid .hd-lang a { color: var(--ink-soft); }
.hd-nav a:hover, .hd-lang a:hover { color: var(--zest); }
.hd.is-small .hd-nav a:hover, .hd.is-small .hd-lang a:hover { color: var(--leaf); }
.hd--solid .hd-nav a:hover, .hd--solid .hd-lang a:hover { color: var(--leaf); }
.hd-lang a[aria-current="true"] { color: var(--paper); font-weight: 700; }
.hd.is-small .hd-lang a[aria-current="true"] { color: var(--ink); }
.hd--solid .hd-lang a[aria-current="true"] { color: var(--ink); }

/* Arabic header type. Two separate corrections, and the first is the bug:
   `line-height: 1` makes the line box exactly the font size, which is enough
   for Figtree and not for IBM Plex Sans Arabic — the ج of المنتجات and the ص
   of اتصل both drop well below the baseline and were being cut off. Leading,
   not a smaller size, is what fixes clipping; shrinking alone just clips
   proportionally.
   The size step-down is the second, independent correction: Arabic sets
   optically larger than Latin at an identical px value, so 12.5px reads
   heavier here than it does on /en/ and /tr/.
   Scoped to [dir="rtl"] so the Latin trees keep the setting the design was
   drawn at. The header's height comes from the 52px logo, so the taller line
   box changes no layout. */
[dir="rtl"] .hd-nav a,
[dir="rtl"] .hd-lang a { font-size: 11.5px; line-height: 1.7; }
.hd-lang {
  padding-inline-start: clamp(12px, 2vw, 22px);
  border-inline-start: 1px solid rgba(255,255,255,.28);
}
.hd.is-small .hd-lang { border-inline-start-color: var(--line); }
.hd--solid .hd-lang { border-inline-start-color: var(--line); }
.hd .btn { padding: 11px 20px; font-size: 13px; }

/* Below 700px the header WRAPS to a second row rather than hiding anything.
   No hamburger: a scripted disclosure would regress the rule this site holds
   everywhere else — a crawler or a failed script still reaches every word.
   The first build hid .hd-nav outright here and shipped six pages with no
   route at all to Products or Contact. */
@media (max-width: 700px) {
  .hd { flex-wrap: wrap; row-gap: 8px; padding-block: 10px; }
  .hd-nav { margin-inline-start: 0; order: 3; width: 100%; flex-wrap: wrap; gap: 14px; }
  .hd-lang { margin-inline-start: auto; }
  .hd-nav a { font-size: 11.5px; }
  /* An explicit RTL rule is required, not optional: `[dir="rtl"] .hd-nav a`
     above outranks a bare `.hd-nav a` on specificity, so the Latin
     step-down would never have reached the Arabic tree. This is also where
     clipping bit hardest — the nav wraps to its own row here, so a clipped
     descender lands against the row below it. */
  [dir="rtl"] .hd-nav a { font-size: 10.5px; }
  .hd-mark img { height: 42px; }
}

/* ----------------------------------------------------------- hero ---- */
.hero {
  position: relative; min-height: 100svh; display: grid;
  background: var(--forest); overflow: hidden; isolation: isolate;
}
.hero-slides { position: absolute; inset: 0; }
.hero-slides [data-slide] { position: absolute; inset: 0; opacity: 0; transition: opacity var(--slide-fade) var(--ease); }
.hero-slides [data-slide].is-on { opacity: 1; }
.hero-slides img {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.06);
  transition: transform var(--kenburns) linear;
}
.hero-slides [data-slide].is-on img { transform: scale(1.16); }

.hero::before, .hero::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.hero::before {
  background: linear-gradient(calc(100deg * var(--dir)),
    rgba(6,26,13,.94) 0%, rgba(6,26,13,.72) 38%, rgba(6,26,13,.18) 72%, rgba(6,26,13,.42) 100%);
}
.hero::after {
  inset-block-start: auto; height: 42%;
  background: linear-gradient(transparent, rgba(6,26,13,.82));
}
/* No photography yet: the scrim has nothing to sit on, so the ground carries
   it. This is the state the site ships in until the client sends images, and
   it has to read as deliberate rather than broken. */
.hero[data-no-photography]::before {
  background: radial-gradient(120% 90% at calc(50% + 22% * var(--dir)) 0%, var(--canopy), var(--forest) 62%);
}

.hero-inner {
  position: relative; z-index: 2; align-self: end; width: 100%;
  padding-block: calc(var(--header-h) + 40px) clamp(64px, 12vh, 132px);
}
.hero-kicker { color: var(--zest); display: flex; align-items: center; gap: 12px; margin-bottom: 20px; min-height: 11px; }
.hero-kicker::before { content: ""; width: 34px; height: 2px; background: var(--zest); flex: 0 0 auto; }
/* `.hero-h`, NOT `.hero h1`. The visible headline is a <p> (the page's real
   <h1> is .sr-only, because the headline text changes per slide and a
   document outline must not). The mockup's selector was `.hero h1` and was
   lifted verbatim onto restructured markup, so it matched NOTHING: the
   headline rendered at inherited 17px in --ink on --forest, 1.12:1, on all
   three homepages. Font-family/weight/tracking come from the h1,h2,h3 rule
   above, which the fallback <h2> still matches; the <p> needs them stated. */
.hero-h {
  font-family: var(--face-display);
  font-weight: 800; line-height: .98;
  letter-spacing: var(--track-display);
  text-wrap: balance;
  font-size: clamp(44px, 8.6vw, 124px);
  color: var(--paper);
  max-width: 15ch;
  margin-bottom: 22px;
}
/* Exactly one headline is ever visible. hero.js hides the authored fallback
   the moment it writes a real one, and shows it again if it cannot. Doing
   this in CSS behind html.js was rejected: it would blank the headline
   whenever the script fails, which is the failure Ruling 17 exists to stop. */
.hero-h[hidden] { display: none; }
.hero-sub { color: rgba(255,255,255,.84); font-size: clamp(16px, 1.5vw, 20px); max-width: 52ch; margin-bottom: 36px; }
.hero-btns { display: flex; gap: 14px; flex-wrap: wrap; }

/* Per-word mask reveal, built by hero.js as .rv > span.
   A CSS ANIMATION, not a class the script has to remember to add. The first
   version started each word at translateY(105%) and revealed it when JS set
   .is-lit on the next animation frame — which meant a dropped frame, a
   throttled background tab, or any script failure left the entire headline
   invisible with no error. `animation ... both` plays once on insertion and
   ends at the visible state, so the worst case is a word that appears
   without moving. Fail visible, never fail blank. */
.rv { display: inline-block; overflow: hidden; vertical-align: bottom; }
.rv > span { display: inline-block; animation: word-rise .78s var(--ease) both; }
@keyframes word-rise { from { transform: translateY(105%); } to { transform: translateY(0); } }

.hero-ghost {
  position: absolute; inset-inline-end: var(--pad); inset-block-end: clamp(48px, 9vh, 104px);
  z-index: 2; font-family: var(--face-display); font-weight: 800;
  font-size: clamp(88px, 16vw, 232px); line-height: .8;
  color: transparent; -webkit-text-stroke: 1.5px rgba(255,255,255,.22);
  pointer-events: none; user-select: none; font-variant-numeric: tabular-nums;
}
.hero-dots {
  position: absolute; inset-inline-start: var(--pad); inset-block-end: clamp(24px, 4vh, 46px);
  z-index: 3; display: flex; gap: 10px;
}
.hero-dots button {
  width: 52px; height: 3px; padding: 0; border: 0; cursor: pointer;
  background: rgba(255,255,255,.28); position: relative;
}
/* The bar is 3px tall; this gives it a real 27px tap target without moving
   anything visually. */
.hero-dots button::after { content: ""; position: absolute; inset: -12px -3px; }
.hero-dots button i { position: absolute; inset: 0; overflow: hidden; background: var(--zest); transform: scaleX(0); transform-origin: calc(50% - 50% * var(--dir)) 0; }
.hero-dots button.is-on i { animation: dot-fill var(--slide-hold) linear forwards; }
@keyframes dot-fill { to { transform: scaleX(1); } }

/* ---------------------------------------------------------- stats ---- */
.stats { background: var(--forest); color: var(--paper); padding-block: clamp(48px, 7vw, 88px); }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: clamp(24px, 4vw, 56px); }
.stats .n {
  font-family: var(--face-display); font-weight: 800;
  font-size: clamp(42px, 5.6vw, 78px); line-height: .9;
  color: var(--zest); letter-spacing: -.04em; font-variant-numeric: tabular-nums;
}
.stats .n.is-text { font-size: clamp(24px, 3vw, 42px); }
.stats .l { margin-top: 10px; color: rgba(255,255,255,.66); }

/* -------------------------------------------------- section frame ---- */
.sec { padding-block: clamp(68px, 10vw, 136px); }
.sec-stone { background: var(--stone); }
.sec-head { display: grid; gap: 18px; margin-bottom: clamp(32px, 5vw, 62px); }
@media (min-width: 900px) { .sec-head { grid-template-columns: 1.15fr .85fr; align-items: end; } }
.sec-head .k { color: var(--leaf); }
.sec-head h2 { font-size: clamp(32px, 5.2vw, 72px); }
.sec-head p { color: var(--ink-soft); max-width: 46ch; }
.lede { color: var(--ink-soft); max-width: 58ch; }

/* ----------------------------------------------------- categories ---- */
.cats { display: grid; gap: 14px; grid-template-columns: 1fr; }
/* Five categories are a fixed part of the range. A deliberate five-up grid
   at desktop removes the accidental four-plus-one orphan card, while the
   intermediate three-up composition keeps the cards comfortably readable. */
@media (min-width: 700px) { .cats { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .cats { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.cat { position: relative; aspect-ratio: 4/5; overflow: hidden; background: var(--forest); display: block; }
.cat img { width: 100%; height: 100%; object-fit: cover; transition: transform 1.1s var(--ease); }
.cat:hover img, .cat:focus-visible img { transform: scale(1.09); }
/* MEASURED, not guessed. The blurb added below pushed the text block to 65%
   of the card, so its first line landed at 57-79% down — where the old
   `transparent 34%` ramp was still only 31-51% opaque. Over the pale oils
   photograph that measured 4.43:1, under the 4.5:1 AA floor for 14px text.
   A photo overlay cannot be checked by check_contrast.py, which compares
   static token pairs; this was caught by sampling the composited pixels in
   the browser, and must be re-measured there if either ramp or copy changes. */
.cat::after { content: ""; position: absolute; inset: 0; background: linear-gradient(transparent 16%, rgba(6,26,13,.5) 42%, rgba(6,26,13,.94)); }
.cat-tx { position: absolute; inset-block-end: 0; inset-inline: 0; z-index: 2; padding: 22px; color: var(--paper); transition: transform .55s var(--ease); }
.cat:hover .cat-tx, .cat:focus-visible .cat-tx { transform: translateY(-8px); }
.cat h3 { font-size: 25px; margin-bottom: 6px; }
.cat .c { color: var(--zest); font: 500 11px/1 var(--face-mono); letter-spacing: .14em; text-transform: uppercase; }
/* The blurb sits over the same dark scrim as the heading, so it takes --paper
   at reduced opacity rather than --ink-soft, which is a light-ground colour
   and would be all but invisible here. */
.cat-blurb {
  margin: 0; font-size: 14px; line-height: 1.45;
  color: rgba(255,255,255,.93);
  /* Bounded so a longer blurb at handover cannot push the text block back up
     the card into the transparent part of the ramp. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
}
.cat-blurb[data-pending] { color: var(--zest); }

/* ----------------------------------------------------------- band ---- */
.band { position: relative; min-height: clamp(400px, 56vh, 600px); display: grid; place-items: center; overflow: hidden; background: var(--forest); }
.band img { position: absolute; inset-block: -14%; inset-inline: 0; width: 100%; height: 128%; object-fit: cover; will-change: transform; }
.band::after { content: ""; position: absolute; inset: 0; background: linear-gradient(rgba(6,26,13,.78), rgba(6,26,13,.86)); }
.band-c { position: relative; z-index: 2; text-align: center; color: var(--paper); padding-inline: var(--pad); }
.band .k { color: var(--zest); margin-bottom: 18px; }
.band h2 { font-size: clamp(28px, 4.4vw, 60px); max-width: 19ch; margin-inline: auto; }

/* ----------------------------------------------------------- rail ---- */
.rail { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 18px; scroll-snap-type: x mandatory; }
.rail::-webkit-scrollbar { height: 4px; }
.rail::-webkit-scrollbar-thumb { background: var(--grass); }
/* Category filter chips. --leaf, not --grass: --grass is 3.29:1 on white and
   is decoration-only in this palette (tokens.css says so, check_contrast.py
   enforces it). The active chip inverts to --forest/--paper, which measures
   14.84:1 — the same pair the hero uses. */
.filters {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-block-end: 22px;
}
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 15px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); color: var(--ink-soft);
  font: 500 14px/1 var(--face-body); cursor: pointer;
  transition: background .25s var(--ease), color .25s var(--ease), border-color .25s var(--ease);
}
.chip:hover { border-color: var(--leaf); color: var(--leaf); }
.chip:focus-visible { outline: 2px solid var(--leaf); outline-offset: 2px; }
.chip.is-on { background: var(--forest); border-color: var(--forest); color: var(--paper); }
.chip-n {
  font: 500 12px/1 var(--face-mono); font-variant-numeric: tabular-nums;
  color: var(--ink-soft); opacity: .7;
}
.chip.is-on .chip-n { color: var(--zest); opacity: 1; }

/* The products page runs the same .prod card as the home rail, but as a grid
   rather than a horizontal scroller: a catalogue is for comparing lines
   side by side, and a rail hides two thirds of them off-screen. `flex` on
   .prod is simply ignored on a grid item, so the card needs no variant. */
.pgrid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 620px)  { .pgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1000px) { .pgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.prod { flex: 0 0 296px; scroll-snap-align: start; background: var(--stone); overflow: hidden; }
.prod .ph { aspect-ratio: 3/2; overflow: hidden; background: var(--forest); }
.prod img { width: 100%; height: 100%; object-fit: cover; transition: transform 1s var(--ease); }
.prod:hover img { transform: scale(1.07); }
.prod .b { padding: 20px; }
.prod h3 { font-size: 20px; margin-bottom: 8px; }
/* --ink-soft on --stone measures 6.52:1 — an already-checked pair, so the
   description needs no new colour. Sized down from body copy because a card
   is scanned, not read. */
.prod-desc {
  margin: 0 0 14px; font-size: 14.5px; line-height: 1.5;
  color: var(--ink-soft);
}

/* ---------------------------------------------------- spec tables ---- */
.spec-table { display: grid; }
.spec-row { display: grid; grid-template-columns: 1fr auto; gap: 8px 16px; padding: 9px 0; border-block-start: 1px solid var(--line); font-size: 14px; }
.spec-row > span:first-child, .spec-row > dt { color: var(--ink-soft); }
.spec-val { font-variant-numeric: tabular-nums; text-align: end; }
.tgrid { display: grid; gap: 1px; background: var(--line); grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.tgrid > div { background: var(--stone); padding: 26px; }
.tgrid dt { color: var(--leaf); margin-bottom: 8px; }
.tgrid dd { font-family: var(--face-display); font-weight: 600; font-size: 19px; letter-spacing: -.02em; }

/* -------------------------------------------------------- partners ---- */
.mq { overflow: hidden; padding-block: 32px; border-block: 1px solid var(--line); --gap: clamp(40px, 6vw, 88px); }
.mq-track { display: flex; gap: var(--gap); width: max-content; animation: mq-slide 34s linear infinite; align-items: center; }
/* The -50% formula only loops seamlessly when the set is rendered TWICE;
   app.js renders it once, so the track shuttled and snapped. It now emits a
   duplicate marked aria-hidden, and this guards the geometry. */
.mq:hover .mq-track, .mq:focus-within .mq-track { animation-play-state: paused; }
@keyframes mq-slide { to { transform: translateX(calc(-1 * (50% + var(--gap) / 2) * var(--dir))); } }
.mq img { height: 42px; width: auto; opacity: .62; filter: grayscale(1); transition: opacity .4s, filter .4s; }
.mq img:hover { opacity: 1; filter: none; }

/* ------------------------------------------------------------ cta ---- */
.cta { background: var(--forest); color: var(--paper); padding-block: clamp(68px, 10vw, 136px); position: relative; overflow: hidden; }
.cta::after {
  content: ""; position: absolute; width: 60vw; height: 60vw;
  inset-inline-end: -18vw; inset-block-start: -22vw; border-radius: 50%;
  background: radial-gradient(circle, rgba(106,156,21,.34), transparent 66%);
  pointer-events: none;
}
.cta-in { position: relative; z-index: 2; display: grid; gap: 34px; }
@media (min-width: 920px) { .cta-in { grid-template-columns: 1.1fr .9fr; align-items: center; } }
.cta h2 { font-size: clamp(32px, 5.2vw, 70px); }
.cta .k { color: var(--zest); margin-bottom: 16px; }
.cta p { color: rgba(255,255,255,.72); max-width: 44ch; margin-top: 16px; }
.cta .spec-row { border-block-start-color: rgba(255,255,255,.16); }
.cta .spec-row > span:first-child { color: rgba(255,255,255,.6); }

/* --------------------------------------------------------- footer ---- */
.ft { background: #071F0E; color: rgba(255,255,255,.6); padding-block: clamp(48px, 7vw, 82px) 32px; font-size: 14px; }
.ft-top { display: grid; gap: 32px; grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)); margin-bottom: 42px; }
.ft h3 { font: 600 12px/1 var(--face-body); letter-spacing: .1em; text-transform: uppercase; color: var(--paper); margin-bottom: 14px; }
.ft ul { display: grid; gap: 9px; }
.ft a:hover { color: var(--zest); }
.ft-base { border-block-start: 1px solid rgba(255,255,255,.12); padding-top: 20px; display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between; font-size: 12.5px; }

/* --------------------------------------------------- PENDING flags ----
   HAZARD: any rule that sets its own `color` and can ALSO land on a
   [data-pending] element wins at equal (0,1,0) specificity by source order.
   Before adding one, check whether it can co-occur; if so, add a matching
   `<selector>[data-pending]` restating this block. Four separate bugs of
   exactly this shape shipped in the first build. */
[data-pending] {
  background: var(--amber-bg);
  color: var(--amber-ink);
  border: 1px dashed var(--amber-line);
  padding: 2px 7px;
  font: 500 13px/1.35 var(--face-mono);
  border-radius: 2px;
  display: inline-block;
}
.spec-val[data-pending] { color: var(--amber-ink); text-align: start; }
.lede[data-pending] { color: var(--amber-ink); }
.certs li[data-pending] { background: var(--amber-bg); border: 1px dashed var(--amber-line); padding: 2px 7px; font-size: 13px; }

/* ------------------------------------------------------ direction ---- */
/* Latin runs inside Arabic must not reorder the Arabic around them. A CLASS
   selector, so it applies to any element — check_structure's bidi scan is
   tag-agnostic for exactly that reason. */
.lat { unicode-bidi: isolate; }
.lat[data-pending] { font-family: var(--face-mono); }
[dir="rtl"] .spec-val.lat { direction: ltr; text-align: start; }

/* ------------------------------------------------- reduced motion ---- */
/* Reduced motion stops the marquee — inside overflow:hidden that left the
   later logos permanently out of reach at 375px. Scroll it instead: the
   animation is gone, the content is not.
   `overflow-x: auto` is the WHOLE fix. An earlier version of this block also
   set `.mq-track { width: auto }`, which erased all six partner logos on
   every homepage at every width: `width:auto` makes the flex track fill .mq,
   and `img { max-width: 100% }` above then becomes a cyclic percentage that
   zeroes each item's min-content floor. Measured figure widths [0,0,0] while
   naturalWidth stayed 250 — the images loaded and were crushed. The track
   keeps `width: max-content`. */
@media (prefers-reduced-motion: reduce) {
  .mq { overflow-x: auto; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  html { scroll-behavior: auto; }
  .js [data-reveal] { opacity: 1; transform: none; }
  .hero-slides img, .hero-slides [data-slide].is-on img { transform: none; }
  .rv > span { transform: none; }
  .mq-track { animation: none; }
}
