/* ============================================================================
   base.css — reset, document defaults, typography, grid, layout primitives.
   Reads tokens.css. Declares no component. Logical properties only.
   ========================================================================= */

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Clear the sticky header on every in-page anchor. */
  scroll-padding-block-start: calc(var(--header-h-condensed) + var(--sp-5));
  /* Prevent the layout shift when a modal locks scroll. */
  scrollbar-gutter: stable;
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: var(--weight-regular);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;   /* never fake-bold, and never in Arabic */
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ol, ul { margin: 0; }
ul[class], ol[class] { list-style: none; padding: 0; }
/* Navigation and component lists are structural, not enumerations. Only .prose
   and .article-prose keep markers, because there a list IS a list of items. */
nav ul, nav ol,
.site-footer ul, .contact__details, .contact__routing ul,
.blog-empty__suggest ul, .scored-list, .index { list-style: none; padding: 0; margin: 0; }

img, picture, video, canvas, svg { display: block; max-inline-size: 100%; }
img, video { block-size: auto; }

input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }

table { border-collapse: collapse; inline-size: 100%; }
:where(th, td) { text-align: start; }

hr {
  border: 0; block-size: 1px; background: var(--border);
  margin-block: var(--sp-6);
}

::selection { background: var(--ember); color: #fff; }

/* --------------------------------------------------------------------------
   Type scale. Sentence case only — no ALL-CAPS headings anywhere.
   ----------------------------------------------------------------------- */
.t-display, .t-h1, .t-h2, .t-h3,
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-semi);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
  color: var(--fg);
}

.t-display { font-size: var(--fs-display); line-height: var(--lh-display); font-weight: var(--weight-bold); }
h1, .t-h1   { font-size: var(--fs-h1);   line-height: var(--lh-h1);   font-weight: var(--weight-bold); }
h2, .t-h2   { font-size: var(--fs-h2);   line-height: var(--lh-h2); }
h3, .t-h3   { font-size: var(--fs-h3);   line-height: var(--lh-h3); }
h4, .t-h4   { font-family: var(--font-body); font-size: var(--fs-body);
              font-weight: var(--weight-semi); line-height: var(--lh-h3);
              letter-spacing: 0; text-wrap: balance; }

.t-lead {
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--fg-muted);
  text-wrap: pretty;
  max-inline-size: var(--measure);
}
.t-body  { font-size: var(--fs-body);  line-height: var(--lh-body); }
.t-small { font-size: var(--fs-small); line-height: var(--lh-small); }

/* The field-note voice. Mono, uppercase, tracked, tabular. This is the only
   place 12px is permitted, and never for reading copy. */
.t-meta {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted);
}
/* Arabic has no uppercase and must never be letter-spaced. */
[lang="ar"] .t-meta, :lang(ar) .t-meta {
  text-transform: none;
  letter-spacing: 0;
  font-family: var(--font-arabic);
}

.t-stat {
  font-family: var(--font-display);
  font-size: var(--fs-stat);
  line-height: var(--lh-stat);
  font-weight: var(--weight-bold);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.t-muted   { color: var(--fg-muted); }
.t-ember   { color: var(--ember-deep); }
.t-slate   { color: var(--slate); }
.t-measure { max-inline-size: var(--measure); }
.t-nowrap  { white-space: nowrap; }
/* Phone numbers and mixed-direction strings must not reorder under RTL. */
.bidi-isolate { unicode-bidi: isolate; direction: ltr; display: inline-block; }
/* A bare number inside a sentence. The sentence follows the paragraph
   direction; the number does not, so "34% pass rate" cannot become
   "pass rate 34%" with the digits on the wrong side. */
.num { unicode-bidi: isolate; direction: ltr; }

/* --------------------------------------------------------------------------
   Links. Inline links are ink with an ember underline — never orange words.
   ----------------------------------------------------------------------- */
a { color: inherit; }

a.link,
.prose a:not([class]) {
  color: var(--fg);
  text-decoration: none;
  border-block-end: 1px solid var(--ember);
  padding-block-end: 1px;
  transition: background-color var(--dur-hover) var(--ease-out),
              border-color var(--dur-hover) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  a.link:hover, .prose a:not([class]):hover { background: var(--ember-wash); }
}

/* --------------------------------------------------------------------------
   Focus. Visible on everything interactive, in one consistent form.
   ----------------------------------------------------------------------- */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip-link {
  position: absolute;
  inset-block-start: var(--sp-2);
  inset-inline-start: var(--sp-2);
  z-index: var(--z-skiplink);
  padding: var(--sp-3) var(--sp-5);
  background: var(--ink);
  color: var(--fg-on-dark);
  border-radius: var(--r-btn);
  font-size: var(--fs-small);
  font-weight: var(--weight-semi);
  transform: translateY(calc(-100% - var(--sp-4)));
  transition: transform var(--dur-dropdown) var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

.visually-hidden {
  position: absolute !important;
  inline-size: 1px; block-size: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%);
  white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   Layout: container, 12-column editorial grid, sections.
   Content default is columns 3–11; marginalia sits in 1–2.
   ----------------------------------------------------------------------- */
.container {
  inline-size: 100%;
  max-inline-size: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-inline-size: 860px; }
.container--wide   { max-inline-size: 1440px; }

.grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
  gap: var(--grid-gap);
}
/* Below 1024 the editorial offset collapses to one column — but an inline-start
   rule survives on marked blocks so the documentary feel does not. */
@media (max-width: 1023px) {
  .grid { grid-template-columns: minmax(0, 1fr); }
  .grid > * { grid-column: 1 !important; }
}

.col-content   { grid-column: 3 / 12; }
.col-content-w { grid-column: 3 / 13; }
.col-margin    { grid-column: 1 / 3; }
.col-full      { grid-column: 1 / -1; }
.col-half-a    { grid-column: 1 / 7; }
.col-half-b    { grid-column: 7 / -1; }
.col-7         { grid-column: 1 / 8; }
.col-5-end     { grid-column: 8 / -1; }

.section        { padding-block: var(--section-y); }
.section--tight { padding-block: var(--section-y-tight); }
.section--loose { padding-block: var(--section-y-loose); }
.section--flush-top { padding-block-start: 0; }
.section--flush-bottom { padding-block-end: 0; }

/* Full-bleed bands escape the container entirely. */
.band          { background: var(--bg-alt); }
.band--mist    { background: var(--mist); }
.band--wash    { background: var(--ember-wash); }
/* Uses --band-ink-bg, not --ink: in dark the two grounds are four values apart
   and the band disappeared into the page. See tokens.css. */
.band--ink     { background: var(--band-ink-bg); color: var(--fg-on-dark); }
.band--ink h1, .band--ink h2, .band--ink h3, .band--ink .t-h2, .band--ink .t-h3 { color: var(--fg-on-dark); }
.band--ink .t-lead, .band--ink .t-meta, .band--ink .t-muted { color: var(--fg-on-dark-muted); }
.band--ink { --border: var(--border-on-dark); --fg: var(--fg-on-dark); --fg-muted: var(--fg-on-dark-muted); }

.rule       { border: 0; border-block-start: var(--rule); margin: 0; }
.rule--strong { border: 0; border-block-start: var(--rule-strong); margin: 0; }

/* Marginalia: mono metadata in columns 1–2 on desktop; on mobile it moves to a
   line ABOVE its content — a designed decision, not a collapse. */
.marginalia {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-label);
}
@media (max-width: 1023px) {
  .marginalia { margin-block-end: var(--sp-3); }
}

/* --------------------------------------------------------------------------
   Flow + simple utilities. Kept deliberately small — this is not a framework.
   ----------------------------------------------------------------------- */
.flow > * + * { margin-block-start: var(--flow, var(--sp-4)); }
.flow-2 > * + * { margin-block-start: var(--sp-2); }
.flow-3 > * + * { margin-block-start: var(--sp-3); }
.flow-5 > * + * { margin-block-start: var(--sp-5); }
.flow-6 > * + * { margin-block-start: var(--sp-6); }
.flow-7 > * + * { margin-block-start: var(--sp-7); }

.cluster { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; }
.cluster--tight { gap: var(--sp-2); }
.cluster--loose { gap: var(--sp-5); }
.stack { display: flex; flex-direction: column; }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }

.cols-2 { display: grid; gap: var(--sp-5) var(--sp-8); }
@media (min-width: 768px) { .cols-2 { grid-template-columns: 1fr 1fr; } }
.cols-3 { display: grid; gap: var(--sp-5); }
@media (min-width: 640px)  { .cols-3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .cols-3 { grid-template-columns: repeat(3, 1fr); } }

/* --------------------------------------------------------------------------
   prose — running text. 68ch measure, real rhythm, no orange body copy.
   ----------------------------------------------------------------------- */
.prose {
  max-inline-size: var(--measure);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--fg);
}
.prose > * + * { margin-block-start: var(--sp-5); }
.prose h2 { margin-block-start: var(--sp-8); }
.prose h3 { margin-block-start: var(--sp-7); }
.prose h2 + *, .prose h3 + * { margin-block-start: var(--sp-4); }
.prose p { text-wrap: pretty; }
.prose ul, .prose ol { padding-inline-start: var(--sp-5); }
.prose li + li { margin-block-start: var(--sp-2); }
.prose strong { font-weight: var(--weight-semi); }
.prose figure { margin-block: var(--sp-7); }
.prose figcaption {
  font-family: var(--font-mono); font-size: var(--fs-label);
  letter-spacing: var(--tracking-label); text-transform: uppercase;
  color: var(--fg-muted); margin-block-start: var(--sp-3);
}
.prose--legal { max-inline-size: var(--measure-legal); }

/* --------------------------------------------------------------------------
   Scroll reveal. ONE pattern, applied to figures, cards and data bands only —
   never a heading, never a paragraph, never the hero. Content is visible by
   default; JS adds .reveal-armed, so a fast scroller never sees an empty page.
   ----------------------------------------------------------------------- */
.reveal-armed { opacity: 0; transform: translate3d(0, 12px, 0); }
.reveal-armed.is-revealed {
  opacity: 1; transform: translate3d(0, 0, 0);
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .reveal-armed { transform: none; }
  .reveal-armed.is-revealed { transform: none; transition: opacity var(--dur-reveal) linear; }
}

/* Global reduced-motion contract: every transform and translation removed;
   opacity and colour survive. "Fewer and gentler", not "none". */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, background-color, border-color, color, fill, stroke, outline-color !important;
    scroll-behavior: auto !important;
  }
}

/* Print: the scorecard and the article should survive a printer. */
@media print {
  .site-header, .site-footer, .cta-band, .sticky-quote, .cookie-banner { display: none !important; }
  body { background: #fff; color: #000; }
  a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 10pt; }
}

/* --------------------------------------------------------------------------
   RTL: the few things that are NOT handled by logical properties.
   Everything else mirrors for free because no physical property is used.
   ----------------------------------------------------------------------- */

/* MIRROR: arrows, chevrons and back/next controls mean "forward", not "east". */
[dir="rtl"] .index__arrow,
[dir="rtl"] .link-arrow__icon { transform: scaleX(-1); }

/* DO NOT MIRROR: the logo, client logos, media controls, the map, numerals,
   and chart axes where the data reads left-to-right by convention. */
[dir="rtl"] .logo,
[dir="rtl"] .coverage__map,
[dir="rtl"] .chart,
[dir="rtl"] .sparkline,
[dir="rtl"] .client-wall img { direction: ltr; }
[dir="rtl"] .chart svg text { direction: ltr; }

/* Numerals stay Latin sitewide — decided once, see _data/i18n/ar.json. */
[lang="ar"] { font-variant-numeric: tabular-nums; }

/* Mixed-direction strings — phone numbers, codes, English source text inside an
   Arabic page — must not reorder. */
[dir="rtl"] .t-meta,
[dir="rtl"] .index__code,
[dir="rtl"] .mega__code,
[dir="rtl"] .drawer__code,
[dir="rtl"] .marginalia { unicode-bidi: isolate; }

/* Arabic is NEVER letter-spaced, uppercased or fake-bolded. The mono metadata
   voice keeps its font and size but drops the tracking and the transform —
   both are meaningless in Arabic and the tracking actively breaks the script's
   joining behaviour. */
[lang="ar"] .t-meta,
[lang="ar"] .marginalia,
[lang="ar"] .needs-input,
[lang="ar"] .badge,
[lang="ar"] .index__code,
[lang="ar"] .mega__code,
[lang="ar"] .drawer__code,
[lang="ar"] .field__req,
[lang="ar"] .field__opt,
[lang="ar"] .stepper__n,
[lang="ar"] .stepper__duration,
[lang="ar"] .city__n,
[lang="ar"] .section-opener__code,
[lang="ar"] .section-opener__kicker,
[lang="ar"] .article__toc-title,
[lang="ar"] .kpi__label,
[lang="ar"] .panel__title,
[lang="ar"] .scored__flag,
[lang="ar"] figcaption,
[lang="ar"] .prose figcaption {
  letter-spacing: 0;
  text-transform: none;
}
/* Latin runs inside an Arabic page — codes, references, English source text —
   keep their own treatment, because they ARE Latin. */
[lang="ar"] [lang="en"] { letter-spacing: revert; }
[lang="ar"] .t-meta:lang(en) { text-transform: uppercase; letter-spacing: var(--tracking-label); }

/* No horizontal overflow at any width from 320px up. Long unbroken strings —
   an email address, a URL in an English source block, a reference code — are
   the usual cause, so they wrap rather than push the page wide. */
body { overflow-x: clip; }
.prose, .pending-prose__source, .index__desc, .card__excerpt,
.field__hint, .state__body, address { overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   [data-js-only] — a control that only means something once JS has taken over.
   Hidden by default and revealed by scripts/main.js, so the page never shows a
   button that does nothing. The CONTENT such a control governs is always
   visible in the markup; JS introduces the hiding, never the reverse.
   ----------------------------------------------------------------------- */
/* Doubled attribute selector so this always beats a component rule that sets
   `display` on the same element. A plain [data-js-only] ties with .quote__nav
   on specificity and loses on source order, which shipped a "Next" button that
   did nothing when scripting was off. */
[data-js-only][data-js-only] { display: none !important; }

/* --------------------------------------------------------------------------
   Bidi isolation for anything numeric.

   A string like "82 / 100" contains digits and a neutral slash. In an RTL
   context the neutral takes the paragraph direction and the whole run reorders
   to "100 / 82" — a wrong number, silently. Scores, ratios, ranges, dates,
   references, file sizes and phone numbers are all Latin-ordered runs and must
   be isolated, not merely left in place.

   This is the same class of bug as an unisolated phone number, and it is worse:
   a reordered phone number looks wrong, a reordered score looks plausible.
   ----------------------------------------------------------------------- */
.scored__value,
.stat-band__figure,
.score-badge,
.kpi__figure,
.kpi__delta,
.table__num,
.index__code,
.mega__code,
.drawer__code,
.city__n,
.stepper__n,
.service-lines__n,
.scenario__n,
.method__n,
.file-chip__size,
.pagination__link,
.badge,
time {
  unicode-bidi: isolate;
}
/* Runs that are unambiguously Latin-ordered — a ratio, a code, a date, a phone
   number — also need the direction pinned, not only isolation. */
.scored__value,
.score-badge,
.table__num,
.index__code,
.mega__code,
.drawer__code,
.file-chip__size {
  direction: ltr;
}
/* …but in an RTL layout they still sit on the correct side of their cell. */
[dir="rtl"] .scored__value,
[dir="rtl"] .table__num { text-align: end; }
