/* ============================================================================
   ranco.css — RANCO's own stylesheet. Loads LAST, after base/tokens/fonts/
   type-scale/layout-primitives/motion-kit/micro-interactions/sticky-cta.

   Direction: DIR-2 LEDGER, executed at the owner's "clean and simple"
   restraint level (design-brief.md §0). One signature move: the Register Row
   (§5). No cards, no stat band, no dark hero, no blueprint grid.
   ========================================================================= */

/* --- 0 · UTILITIES --------------------------------------------------------- */
.visually-hidden {
  position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0; padding: 0; margin: -1px;
}
.skip-link {
  position: absolute; inset-inline-start: var(--space-3); inset-block-start: -80px;
  z-index: 100; background: var(--accent); color: var(--accent-ink);
  padding: var(--space-2) var(--space-4); border-radius: var(--radius-sm);
  transition: inset-block-start .18s;
}
.skip-link:focus { inset-block-start: var(--space-3); }

/* An inline Arabic fragment (client's own verbatim wording) sitting inside an
   EN (dir=ltr) page. IBM Plex Sans (Latin) carries no Arabic glyphs, so this
   fragment needs the Arabic face explicitly rather than falling back silently
   (B8.7/B8.8 logic, applied to the EN mirror). */
.ar-inline { font-family: var(--font-body-ar, inherit); unicode-bidi: isolate; }

/* Mirror utility for the /ar/ pages: an inline LATIN fragment sitting inside
   an Arabic (dir=rtl) page — the English service name under its Arabic
   register-row heading, an ISO designation, a brand/software name.
   Deliberately does NOT force the Latin webfont stack (font-family:
   var(--font-body-latin)) the way .ar-inline forces the Arabic stack on the
   EN mirror: forcing it here would make the browser fetch
   ibm-plex-sans-latin-var.woff2 on an /ar/ route, which breaks the "at most
   two Arabic font files, never the Latin one" rule in FONTS-NOTES.md and
   was caught by a network-request check during the bilingual build. Instead
   this relies on the inherited --font-body-ar stack's own fallback chain —
   'IBM Plex Sans Arabic' (already loaded) covers Basic Latin, and
   'IBM Plex Sans Arabic Fallback' behind it is `local('Arial')`, so any
   missing glyph resolves locally with zero extra network fetch. Pair with
   dir="ltr" (and lang="en" for a whole phrase, not just a code/number) for
   correct bidi ordering — unicode-bidi:isolate is what this class adds. */
.latin-inline { unicode-bidi: isolate; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.panel :focus-visible, .site-header :focus-visible { outline-color: var(--panel-signal); }

body { background: var(--bg); color: var(--ink); }

/* Scoped to `main`, not a bare `img, picture` selector — qa.py's bare-element
   scan flags an unscoped img/picture selector on sight (it is usually a real
   bug), and this one is a legitimate loading-state background, not a bug.
   Scoping it silences the false positive honestly instead of leaving a
   checker result that has to be manually waved through on every future run.
   `main` is also the right scope on its own merits: it excludes the header
   and footer logo marks, which are small transparent SVGs that load
   instantly and would otherwise sit in an odd tinted box for no reason. */
main img, main picture { background: var(--surface); }

/* micro-interactions.css's .link-draw is `display:inline-block` with no
   min-height, which renders at ~21px against a WCAG 2.2 (2.5.8) 24px floor —
   160 instances site-wide (every nav link, every register-row link, every
   header action). Site-specific override, not a snippet edit: same pattern
   already correct on .ibix-credit and .lang-switch below. inline-flex keeps
   the underline pseudo-element's positioning context intact. */
.link-draw { min-height: 24px; display: inline-flex; align-items: center; }

/* --- 1 · BUTTONS ------------------------------------------------------------ */
.btn--primary {
  background: var(--accent); color: var(--accent-ink);
  border-color: var(--accent); border-radius: var(--radius-sm);
  padding-inline: var(--space-4); min-height: 48px; font-weight: 700;
}
.btn--primary:hover, .btn--primary:focus-visible { background: var(--ink); border-color: var(--ink); }
.btn--ghost {
  background: transparent; color: var(--accent); border-color: var(--border-interactive);
  border-radius: var(--radius-sm); padding-inline: var(--space-4); min-height: 48px;
}
.btn--ghost:hover, .btn--ghost:focus-visible { border-color: var(--accent); background: var(--surface); }
.btn--small { min-height: 40px; padding-inline: var(--space-3); font-size: var(--step--1); }
.btn--on-panel.btn--primary { background: var(--panel-text); color: var(--panel); border-color: var(--panel-text); }
.btn--on-panel.btn--primary:hover, .btn--on-panel.btn--primary:focus-visible { background: var(--panel-signal); border-color: var(--panel-signal); color: var(--panel-text); }
.btn--on-panel.btn--ghost { color: var(--panel-text); border-color: var(--panel-border); }
.btn--on-panel.btn--ghost:hover, .btn--on-panel.btn--ghost:focus-visible { border-color: var(--panel-text); }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* --- 2 · HEADER -------------------------------------------------------------- */
.site-header {
  position: sticky; inset-block-start: 0; z-index: var(--z-header, 50);
  background: var(--bg); border-block-end: 1px solid var(--border-subtle);
  block-size: var(--header-h);
}
.site-header__inner {
  max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4);
  block-size: 100%; display: flex; align-items: center; gap: var(--space-5);
}
.site-header__logo { display: flex; align-items: center; flex-shrink: 0; }
.site-header__logo img { block-size: 34px; inline-size: auto; }

.site-nav { flex: 1; }
.site-nav__list { list-style: none; display: flex; gap: var(--space-5); align-items: center; }
.site-nav__link { font-size: var(--step--1); font-weight: 500; color: var(--ink); }
.site-nav__link[aria-current="page"] { color: var(--accent); font-weight: 700; }

.site-header__actions { display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }
.header-action {
  display: inline-flex; align-items: center; gap: 6px; font-size: var(--step--1);
  color: var(--ink); white-space: nowrap;
}
.header-action--wa { color: var(--muted); font-size: var(--step--2); }
.header-action svg { inline-size: 16px; block-size: 16px; flex-shrink: 0; }

.lang-switch {
  font-size: var(--step--1); font-weight: 700; color: var(--accent);
  border: 1.5px solid var(--border-interactive); border-radius: var(--radius-sm);
  padding: 4px 10px; min-height: 32px; display: inline-flex; align-items: center;
}
.lang-switch:hover, .lang-switch:focus-visible { background: var(--surface); }

.menu-toggle {
  display: none; background: none; border: 0; padding: 8px; min-height: 44px; min-width: 44px;
  align-items: center; justify-content: center;
}
.menu-toggle__bars, .menu-toggle__bars::before, .menu-toggle__bars::after {
  content: ""; display: block; inline-size: 22px; block-size: 2px; background: var(--ink);
  transition: transform .2s, opacity .2s;
}
.menu-toggle__bars::before { position: relative; inset-block-start: -6px; }
.menu-toggle__bars::after { position: relative; inset-block-end: 4px; }
.menu-toggle[aria-expanded="true"] .menu-toggle__bars { background: transparent; }
.menu-toggle[aria-expanded="true"] .menu-toggle__bars::before { inset-block-start: 0; transform: rotate(45deg); }
.menu-toggle[aria-expanded="true"] .menu-toggle__bars::after { inset-block-end: 0; transform: rotate(-45deg); }

.mobile-nav {
  position: fixed; inset: var(--header-h) 0 0 0; z-index: var(--z-overlay, 60);
  background: var(--bg); overflow-y: auto; padding: var(--space-6) var(--space-4);
  transform: translateY(-8px); opacity: 0; pointer-events: none; transition: opacity .22s, transform .22s;
}
.mobile-nav.is-open { transform: none; opacity: 1; pointer-events: auto; }
.mobile-nav__list { list-style: none; display: flex; flex-direction: column; gap: var(--space-4); }
.mobile-nav__list a { font-size: var(--step-2); font-weight: 700; color: var(--ink); }
.mobile-nav__actions { margin-block-start: var(--space-6); display: flex; flex-direction: column; gap: var(--space-3); }

/* Header burger breakpoint, re-measured 2026-08-04 (Playwright, 8 nav items
   incl. the de-generic "ما نقوم به" label) for BOTH language routes. The RTL
   row was already known not to fit until ~1360px («خدمات النقل واللوجستيات»
   is the long pole); the same sweep found the LTR row *also* wraps — 4 lines
   at 861px, 3 at 1100px, 2 at 1200–1280px, one line only from ~1400px. The
   two routes were previously split ([dir="rtl"] got the wide range, EN kept
   the plain 860px cutoff) which left EN broken across the whole 861–1400px
   band, including 1366×768. One shared threshold for both directions — the
   English row needs exactly the same room the Arabic row does, so there is
   no reason to carry two breakpoints and two rule sets. */
@media (min-width: 1401px) {
  .menu-toggle, .mobile-nav { display: none !important; }
}
@media (max-width: 1400px) {
  .site-nav, .header-action--wa { display: none; }
  .site-header__actions .header-action--call { display: none; }
  .menu-toggle { display: inline-flex; }
  .site-header__actions { margin-inline-start: auto; }
}

/* --- 3 · HERO — split asymmetric grid (design-brief.md §11.1 pattern) ------- */
.hero-split {
  padding-block-start: clamp(var(--space-7), 8vh, var(--space-9));
  padding-block-end: clamp(var(--space-6), 6vh, var(--space-8));
  background: var(--bg);
}
/* Short-viewport fit, the house pattern (ibix-base.css .hero-fit-pad) applied
   directly rather than via the class: measured 1024x700, 1366x700 and
   1536x730 pushed "Request a quote" up to 76px below the fold because the
   vh-based clamp()s above still resolve to a large top pad on a SHORT-but-
   not-narrow laptop screen. This block loads after ibix-base.css so it wins
   outright at max-height:820px without needing a second class in the markup. */
@media (max-height: 820px) {
  .hero-split {
    padding-block-start: clamp(56px, 9vh, 96px);
    padding-block-end: clamp(24px, 5vh, 48px);
  }
}
.hero-split__inner {
  max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4);
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  column-gap: clamp(1.5rem, 4vw, 4rem); row-gap: var(--space-5); align-items: center;
}
/* grid-column: 1/2, NOT 1/-1 — the real cause of the hero-CTA-below-fold bug
   (2026-08-04 fix pass). An eyebrow spanning both columns collides with
   .hero-split__media's explicit "grid-column:2/3; grid-row:1/span 4" — sparse
   auto-placement cannot seat a full-width item in a row whose column 2 is
   already occupied, so it gets pushed to row 5, and every row after it (title,
   lead, cta) cascades down behind it. Measured: 4 phantom empty rows, ~690px
   of grid height at 1366x700 where content only needs ~434px. Column 1 only
   matches every other child in this grid and removes the collision outright. */
.hero-split__eyebrow { grid-column: 1 / 2; }
.hero-split__title { grid-column: 1 / 2; margin-block-end: var(--space-3); }
.hero-split__lead { grid-column: 1 / 2; max-width: 46ch; font-size: var(--step-1); line-height: var(--lh-long); color: var(--muted); }
.hero-split__cta { grid-column: 1 / 2; margin-block-start: var(--space-4); }
.hero-split__media {
  grid-column: 2 / 3; grid-row: 1 / span 4; position: relative; align-self: stretch;
  min-block-size: clamp(220px, 32vh, 420px); border-radius: var(--radius);
  overflow: hidden; overflow: clip;
}
.hero-split__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* No hero on this site is full-viewport or carries text over the photo (§4.1) —
   the media column is a real grid cell beside the copy, never a background. */

@media (max-width: 860px) {
  .hero-split__inner { grid-template-columns: minmax(0, 1fr); }
  .hero-split__title, .hero-split__lead, .hero-split__cta { grid-column: 1 / -1; }
  .hero-split__media { grid-column: 1 / -1; grid-row: auto; align-self: auto; min-block-size: 0; aspect-ratio: 16 / 10; }
}
/* Home hero copy that is not shown on very small phones stays in the DOM and
   crawlable — it is only visually collapsed, never removed or hidden from AT. */
@media (max-width: 480px) {
  .hero-split__lead, .hero-split__credrow { display: none; }
}

/* Typographic hero (no photo) — /about and interior pages that open on a
   statement rather than a split image. */
.hero-plain {
  padding-block-start: clamp(var(--space-7), 8vh, var(--space-9));
  padding-block-end: var(--space-7); background: var(--bg);
}
.hero-plain__inner { max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4); max-width: 70ch; }
.hero-plain__lead { font-size: var(--step-1); line-height: var(--lh-long); color: var(--muted); max-width: 60ch; margin-block-start: var(--space-3); }

/* --- 4 · REGISTER ROW — the one signature move (design-brief.md §5) -------- */
.register-group { display: flex; flex-direction: column; }
.register-row {
  position: relative; display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  column-gap: var(--gap-col, var(--space-2));
  align-items: baseline;
  padding-block: calc(var(--gap-row, var(--space-7)) / 2);
}
.register-row:first-child { padding-block-start: 0; }
.register-row:last-child { padding-block-end: calc(var(--gap-row, var(--space-7)) / 2); }

.register-row__index {
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-variant-numeric: tabular-nums; color: var(--signal-ink); font-size: var(--step--1);
  align-self: start; padding-block-start: .2em;
}
/* --signal-ink, not --signal: tokens.css is explicit that --signal is decor/
   large-display only (3.00:1 on --surface, on the AA failure line at 14px).
   --signal-ink is the token built for exactly this — small text carrying the
   brand blue — at 5.07–5.81:1. Measured WCAG AA failure fixed 2026-08-04. */
.register-row__body { min-width: 0; }
.register-row__name { font-size: var(--step-2); font-weight: 700; color: var(--ink); margin: 0; }
.register-row__ar { color: var(--muted); font-size: var(--step-0); margin-block-start: 2px; }
/* Mirror of .register-row__ar for the /ar/ pages — the English product name
   shown under its Arabic register-row heading (design-brief.md §8, "hand-
   mirrored items"). Pair with .latin-inline. */
.register-row__en { color: var(--muted); font-size: var(--step-0); margin-block-start: 2px; }
.register-row__desc { color: var(--muted); font-size: var(--step-0); line-height: var(--lh-long); margin-block-start: var(--space-2); max-width: 62ch; }
.register-row__value {
  font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 700;
  text-align: end; white-space: nowrap; align-self: start; font-size: var(--step-0);
}
.register-row__link { display: inline-flex; align-items: center; gap: 6px; margin-block-start: var(--space-2); color: var(--accent); font-weight: 700; font-size: var(--step--1); }

.register-row__rule {
  position: absolute; inset-inline: 0; inset-block-end: 0; block-size: 1px;
  background: var(--border-subtle); transform-origin: left center;
}
[dir="rtl"] .register-row__rule { transform-origin: right center; }
.register-row:first-child .register-row__rule { inset-block-start: 0; inset-block-end: auto; }

/* Content is fully visible regardless of JS/motion state — only the hairline
   ever animates, and it starts at full opacity/visible colour; scaleX(0) hides
   only the decorative rule, never any text (D-16 principle applied to motion). */
html.js .register-group[data-stagger] > .register-row .register-row__rule {
  transform: scaleX(0);
  transition: transform 320ms var(--ease-emphasized, cubic-bezier(.2,0,0,1));
  transition-delay: calc(var(--i, 0) * var(--stagger-step, 40ms));
}
html.js .register-group[data-stagger] > .register-row.in .register-row__rule,
html.js .register-group[data-stagger].in > .register-row .register-row__rule {
  transform: scaleX(1);
}
/* Cancel motion-kit's generic [data-stagger] > * opacity/translateY fade for
   register rows specifically — the row TEXT is never hidden, only the rule
   draws in (design-brief.md §5 motion beat). More specific selector wins. */
.register-group[data-stagger] > .register-row { opacity: 1; transform: none; }

@keyframes ranco-rule-draw { to { transform: scaleX(1); } }
html.js:not(.mk) .register-row__rule { animation: ranco-rule-draw 1ms linear 4s forwards; }

@media (max-width: 640px) {
  .register-row { grid-template-columns: auto 1fr; }
  .register-row__value { grid-column: 1 / -1; text-align: start; margin-block-start: var(--space-2); }
}

/* --- 5 · TWO-UP SPLIT, FULL-BLEED BAND, FILMSTRIP --------------------------- */
.two-up {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
}
.two-up figure { margin: 0; border-radius: var(--radius); overflow: hidden; overflow: clip; }
.two-up img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 640px) { .two-up { grid-template-columns: 1fr; } }

.full-bleed-band { position: relative; block-size: clamp(220px, 26vh, 340px); overflow: hidden; overflow: clip; }
.full-bleed-band img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.filmstrip {
  display: flex; gap: var(--space-3); overflow-x: auto; -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity; padding-block: var(--space-2);
}
.filmstrip figure {
  flex: 0 0 clamp(160px, 22vw, 240px); scroll-snap-align: start; margin: 0;
  aspect-ratio: 4 / 3; border-radius: var(--radius); overflow: hidden; overflow: clip;
}
.filmstrip img { width: 100%; height: 100%; object-fit: cover; }
[dir="rtl"] .filmstrip { direction: ltr; } /* carousel/track geometry stays LTR with RTL content, per compat standard */

/* --- 6 · QUOTE / STATEMENT --------------------------------------------------- */
.quote {
  border-inline-start: 3px solid var(--accent); padding-inline-start: var(--space-4);
  font-size: var(--step-1); line-height: var(--lh-long); color: var(--ink); max-width: 60ch;
}
.quote cite { display: block; margin-block-start: var(--space-3); font-style: normal; font-size: var(--step--1); color: var(--muted); font-weight: 700; }
.objective-quote {
  border: 1px solid var(--border-subtle); border-radius: var(--radius);
  padding: var(--space-5); background: var(--surface); max-width: 68ch;
}
.objective-quote__label { text-transform: uppercase; letter-spacing: var(--tracking-eyebrow); font-size: var(--step--2); color: var(--signal-ink); font-weight: 700; }
.objective-quote p { font-size: var(--step-1); line-height: var(--lh-long); margin-block-start: var(--space-2); }

/* --- 7 · LOGO WALL -----------------------------------------------------------
   The wall used to force every logo into a uniform ~129px-wide tile and let
   height fall where it may — measured, Halliburton rendered 129×10px against
   Sinopec's 42×44px on the same wall. A wordmark at 13.61:1 (Halliburton)
   can never exceed 9.5px tall in a 129px-wide box: the TILE was the
   constraint, not a per-logo max-height. Fix: tier by mark aspect ratio (an
   emblem legitimately sits taller than a wordmark) and widen the grid so the
   widest tier (Halliburton, ~300px natural width at its target height) has
   room. Column count is fixed per breakpoint — 4 desktop / 3 tablet /
   2 mobile — because the target pixel widths in the spec only hold at a
   deterministic column count; a fluid auto-fill minmax() would drift off
   those numbers at in-between viewport widths. Removing any one tile still
   reflows with no CSS edit — column count comes from the viewport, never
   the item count. */
.logo-wall {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}
@media (min-width: 640px)  { .logo-wall { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .logo-wall { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.logo-tile {
  background: var(--elevated); border: 1px solid var(--border-subtle); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center; padding: var(--space-4);
  block-size: 96px; transition: border-color .18s;
}
.logo-tile:hover, .logo-tile:focus-within { border-color: var(--border-interactive); }
/* max-inline-size:100% is the safety net below --grid-max / on narrow tiles:
   a replaced element with a definite height and max-width both set shrinks
   width-first and recomputes height off the intrinsic ratio, so the mark
   never overflows its tile — it just reads a little shorter than its tier
   target on a cramped viewport, never cropped or stretched. */
.logo-tile img { max-inline-size: 100%; width: auto; height: auto; object-fit: contain; }

/* Tier is a property of the MARK, driven by a class on the <img> — never a
   src*= filename selector, which silently stops matching the moment a file
   is renamed or re-exported. */
.logo-tile .logo--emblem   { block-size: 46px; }  /* aspect <1.5  — Sinopec */
.logo-tile .logo--wordmark { block-size: 38px; }  /* aspect 1.5–3.5 — Arabian Drilling, Weatherford, SANAD, ADES, Saipem */
.logo-tile .logo--wide     { block-size: 30px; }  /* aspect 3.5–7 — Saudi Aramco, Schlumberger, KCA Deutag, Baker Hughes */
.logo-tile .logo--xwide    { block-size: 22px; }  /* aspect >7 — Halliburton (13.61:1) */

/* Extra-wide tier spans two mobile columns so it is not crushed to ~11px on
   a phone (spec's explicit mobile rule). :has() ships in every major engine
   since Dec 2023 (~93%); unsupported browsers just keep the single-column
   width, which is the pre-fix (still readable, just not optically balanced)
   state — a real but harmless degradation. */
@media (max-width: 639px) {
  .logo-tile:has(.logo--xwide) { grid-column: span 2; }
}
.logo-wall__note { font-size: var(--step--2); color: var(--muted); margin-block-start: var(--space-3); }

.text-list { color: var(--ink); font-size: var(--step-0); line-height: var(--lh-long); max-width: 70ch; }

/* Wrapping inline link row (404 quick links, capability-statement's "what it
   contains" list). Was a `style="display:flex;..."` inline attribute on both
   404 pages and both capability-statement pages — an inline style always
   beats a media query, so nothing could ever override it. Genuinely static
   across every viewport here (flex-wrap already handles narrow widths), so a
   class costs nothing and removes the inline-style qa.py flag honestly. */
.link-list { display: flex; flex-wrap: wrap; gap: var(--space-4); list-style: none; margin-block-start: var(--space-4); }
.link-list--lg { margin-block-start: var(--space-5); }

/* --- 8 · CONTACT TILES (call / whatsapp / email) ---------------------------- */
.contact-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--space-3); }
.contact-tile {
  background: var(--elevated); border: 1px solid var(--border-subtle); border-radius: var(--radius);
  padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2);
}
.contact-tile__k { font-size: var(--step--1); text-transform: uppercase; letter-spacing: var(--tracking-eyebrow); color: var(--muted); }
.contact-tile__v { font-size: var(--step-1); font-weight: 700; font-variant-numeric: tabular-nums; }
.contact-tile .btn--primary, .contact-tile .btn--ghost { margin-block-start: var(--space-2); align-self: flex-start; }

/* --- 9 · CREDENTIALS / GATED CONTENT ---------------------------------------- */
/* Marks a block as structurally separable so it can be pulled without a rebuild. */
.gated { }

/* --- 10 · FORMS -------------------------------------------------------------- */
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-4); }
.form-grid .field--full { grid-column: 1 / -1; }
@media (max-width: 640px) { .form-grid { grid-template-columns: 1fr; } }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: var(--step--1); font-weight: 700; color: var(--ink); }
.field .opt { font-weight: 400; color: var(--muted); }
.field input, .field select, .field textarea {
  border: 1.5px solid var(--border-interactive); border-radius: var(--radius-sm);
  background: var(--elevated); color: var(--ink); padding: 10px 12px; min-height: 44px;
  font-size: var(--step-0); font-family: inherit;
}
.field textarea { min-height: 110px; resize: vertical; }
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.field:has(input:invalid[data-touched]) input,
.field:has(textarea:invalid[data-touched]) textarea { border-color: #b3372e; }
.field__err { font-size: var(--step--2); color: #b3372e; min-height: 1em; }

/* Self-managed honeypot — visually hidden but present in the DOM and to
   screen readers a bot might not filter; never a Formspree _gotcha.
   Uses the same clip pattern as .visually-hidden (no offset at all) rather
   than a physical `left:-9999px`: that offset is harmless in LTR but in
   [dir="rtl"] it becomes an effective right-edge overflow and balloons the
   scrollable canvas to ~10,000px wide — measured live on /ar/contact and
   /ar/capability-statement (2026-08-04 fix pass). A clip-path with zero
   offset cannot expand the canvas in either writing direction. */
.hp-field {
  position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; opacity: 0; pointer-events: none;
}

.form-actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-4); }
.form-note { font-size: var(--step--1); color: var(--muted); margin-block-start: var(--space-3); max-width: 60ch; }
.form-status { font-size: var(--step-0); font-weight: 700; margin-block-start: var(--space-3); }
.form-status[data-state="success"] { color: #1a7a4c; }
.form-status[data-state="fail"] { color: #b3372e; }
.form-status:empty { display: none; }

/* --- 11 · SECTIONS / SURFACES ------------------------------------------------ */
.section-head { max-width: 60ch; margin-block-end: var(--space-5); }
.section-head p { color: var(--muted); font-size: var(--step-0); margin-block-start: var(--space-2); }
.on-surface { background: var(--surface); }
.on-panel { background: var(--panel); color: var(--panel-text); }
.on-panel .eyebrow, .on-panel h2, .on-panel h3 { color: var(--panel-text); }
.on-panel p { color: var(--panel-muted); }

/* Panel CTA band — the one dark moment per page (§10.5). Never the footer,
   never carries the logo. */
.panel-cta { text-align: start; }
.panel-cta__inner { max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4); max-width: 60ch; }

/* --- 12 · FLEET REGISTER TABLE HEADER --------------------------------------- */
.register-legend { display: flex; gap: var(--space-4); font-size: var(--step--1); color: var(--muted); text-transform: uppercase; letter-spacing: var(--tracking-eyebrow); margin-block-end: var(--space-3); }

/* --- 13 · FOOTER -------------------------------------------------------------- */
#site-footer {
  background: var(--bg); border-block-start: 1px solid var(--border-subtle);
  padding-block: var(--space-8) var(--space-6);
}
.footer-grid {
  max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4);
  display: grid; grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: var(--space-6);
}
.footer-brand img { block-size: 32px; width: auto; margin-block-end: var(--space-3); }
.footer-brand p { color: var(--muted); font-size: var(--step-0); max-width: 34ch; }
.footer-col h3 { font-size: var(--step--1); text-transform: uppercase; letter-spacing: var(--tracking-eyebrow); color: var(--muted); margin-block-end: var(--space-3); }
.footer-col ul { list-style: none; display: flex; flex-direction: column; gap: var(--space-2); }
.footer-col a { color: var(--ink); font-size: var(--step-0); }
.footer-col address { font-style: normal; color: var(--ink); font-size: var(--step-0); line-height: var(--lh-long); }

.footer-bottom {
  max-inline-size: var(--grid-max); margin-inline: auto; padding-inline: var(--space-4);
  margin-block-start: var(--space-7); padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--border-subtle);
  display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: space-between; align-items: center;
  font-size: var(--step--1); color: var(--muted);
}
.footer-bottom__legal { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); align-items: center; }
/* Privacy Policy / Terms of Use — same min-height:24px pattern as .ibix-credit
   and .lang-switch below; measured 82-86 x 21px without it (WCAG 2.2 min 24x24). */
.footer-bottom__legal a { min-height: 24px; display: inline-flex; align-items: center; }

/* Mandatory footer credit — whole phrase is the link, RANCO's own theme,
   underlined at rest + hover, min-height 24px on the anchor (qa.py FAILs
   without every one of these). */
.ibix-credit {
  color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
  min-height: 24px; display: inline-flex; align-items: center; font-weight: 600;
}
.ibix-credit:hover, .ibix-credit:focus-visible { color: var(--accent); }

@media (max-width: 900px) {
  .footer-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 560px) {
  .footer-grid { grid-template-columns: 1fr; }
}

/* --- 14 · UTILITY CLASSES ----------------------------------------------------
   Replaces 448 inline style="" occurrences (43 distinct patterns) removed in
   the 2026-08-04 CSP fix pass. The pages shipped with `style-src 'self'
   'unsafe-inline'` as a stopgap so the pre-existing inline styles kept
   working; that stopgap is now reversed — CSP is `style-src 'self'` with NO
   'unsafe-inline' — because the house rule is that nothing sets a responsive
   PROPERTY inline: no media query can ever beat an inline style, which is a
   latent bug generator, not just a CSP weakness. These are single-purpose and
   composable (e.g. `class="mt-3 text-muted"`), not 43 bespoke classes, so the
   set stays small even though there are more than "a dozen" of them — each
   maps to one real value already in use on this site; merging e.g. the 60ch
   and 68ch measures into one class would have been a visible content-width
   regression, not a cleanup. Every one of these composes ATOP the component
   rules above, per this file's own load-order rule, and every value here is
   a design token already defined in tokens.css/type-scale.css/layout-
   primitives.css — no new magic numbers were introduced by this pass. */

/* -- image frame reset, for a standalone <figure> outside .two-up (which
   already carries this exact reset via its own descendant selector, §5).
   Declared BEFORE the spacing utilities below on purpose: `margin: 0` is a
   shorthand, and margin-block-start / margin-inline (the logical longhands
   .mt-* and .mx-auto set) target the SAME underlying physical edges as that
   shorthand. At equal specificity the cascade resolves per edge by SOURCE
   ORDER, not by which selector "looks" more specific — so if this rule came
   after .mt-5 in the file, `margin: 0` would silently win and cancel the
   margin-block-start .mt-5 was there to set. Caught by a mechanical CSS-
   declaration diff against the original inline styles (2026-08-04): every
   .figure-frame.mt-5 instance was measured rendering marginBlockStart:0
   instead of 32px until this file was reordered. */
.figure-frame { margin: 0; border-radius: var(--radius); overflow: hidden; overflow: clip; }

/* -- spacing -- */
.mt-0 { margin-block-start: 0; }
.mt-2 { margin-block-start: var(--space-2); }
.mt-3 { margin-block-start: var(--space-3); }
.mt-4 { margin-block-start: var(--space-4); }
.mt-5 { margin-block-start: var(--space-5); }
.mt-6 { margin-block-start: var(--space-6); }
.mt-7 { margin-block-start: var(--space-7); }
.mx-auto { margin-inline: auto; }

/* -- section padding (the --section-pad custom property .section consumes —
   see layout-primitives.css §3) -- */
.pad-6 { --section-pad: var(--space-6); }
.pad-7 { --section-pad: var(--space-7); }
.pad-8 { --section-pad: var(--space-8); }
.pad-9 { --section-pad: var(--space-9); }

/* -- direct padding-block, for the few non-.section elements that need it
   (a plain .shell div, never a second value on top of .section) -- */
.py-6 { padding-block: var(--space-6); }
.py-7 { padding-block: var(--space-7); }

/* -- register-row stagger delay (ranco.css §4) -- */
.stagger-40 { --stagger-step: 40ms; }

/* -- text -- */
.text-muted { color: var(--muted); }
.text-signal { color: var(--signal-ink); }
.text-neg1 { font-size: var(--step--1); }
.text-0 { font-size: var(--step-0); }
.text-1 { font-size: var(--step-1); }
.fw-700 { font-weight: 700; }
.not-italic { font-style: normal; }
/* .text-neg1/.text-0/.text-1 set ONLY font-size, deliberately not reusing
   type-scale.css's .text-ui/.text-lead — those also carry a line-height
   override (1.3 / 1.65) the original inline styles never set, which would
   have nudged single-line elements a few px taller than the original. */

/* -- measure (prose max-width). .measure maps to the SAME --measure token
   type-scale.css already narrows for /ar/ (65ch EN / 58ch RTL) — replacing a
   hardcoded "max-width:65ch" with this class is a genuine RTL improvement,
   not just a mechanical swap, because the literal value never shrank for
   Arabic and the token already does. .measure-50 maps to --measure-narrow,
   which IS 50ch verbatim in both directions (type-scale.css only narrows
   --measure itself for RTL, not --measure-narrow/-wide) — byte-identical,
   not an approximation. The other steps (60/68/74ch) are real, distinct
   content widths in use across the build; forcing them onto one number
   would visibly change measured line length. -- */
.measure { max-width: var(--measure); }
.measure-50 { max-width: var(--measure-narrow); }
.measure-60 { max-width: 60ch; }
.measure-68 { max-width: 68ch; }
.measure-74 { max-width: 74ch; }

/* -- fixed component widths (image frames, form containers) -- */
.w-340 { max-width: 340px; }
.w-560 { max-width: 560px; }
.w-840 { max-width: 840px; }
.w-900 { max-width: 900px; }

/* -- alignment -- */
.items-center { align-items: center; }
.items-start { align-items: start; }

/* -- CLS-guard aspect ratio. 1600/1200 reduces to the same ratio as 4/3 —
   aspect-ratio is computed as a ratio, not compared as a string, so this is
   arithmetically identical to the original inline value, not an approximation. -- */
.ar-4x3 { aspect-ratio: 4 / 3; }

/* -- legal-page "Arabic is authoritative" callout (privacy + terms, both
   languages — 4 identical instances, hence a named component, not a utility
   soup composition) -- */
.legal-callout {
  margin-block-start: var(--space-5); padding: var(--space-4);
  background: var(--surface); border-radius: var(--radius);
  border: 1px solid var(--border-subtle);
}

/* --- 14b · REDUCED MOTION — MUST BE THE LAST BLOCK IN THIS FILE -------------- */
/* Repeated per motion-kit.css's own rule: this file loads last, so this is
   also the block that has to win against every rule above it. */
@media (prefers-reduced-motion: reduce) {
  html.js .register-group[data-stagger] > .register-row .register-row__rule,
  html.js .register-group[data-stagger] > .register-row.in .register-row__rule,
  html.js .register-group[data-stagger].in > .register-row .register-row__rule {
    transform: scaleX(1); transition: none;
  }
  html.js:not(.mk) .register-row__rule { animation: none; }
  .menu-toggle__bars, .menu-toggle__bars::before, .menu-toggle__bars::after,
  .mobile-nav, .skip-link, .logo-tile { transition: none; }
}
