/* ==========================================================================
   SSI — component styles

   Tokens live in theme.json and arrive as --wp--preset--* custom properties.
   NOTHING in this file may contain a literal hex value. The pre-launch check
   in tools/verify.py fails the build if one appears.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Box model

   Without this, any element combining width:100% with padding overflows its
   parent by the padding. The header and topbar wrappers do exactly that, which
   pushed the language switcher and email address off the right edge of the
   viewport and produced a horizontal scrollbar on every page. WordPress does
   not ship a border-box reset for block themes; the theme must.
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* Deliberately NOT overflow-x: hidden on html/body. It would mask any future
   overflow instead of surfacing it, and setting overflow on either element
   makes it the scroll container — which breaks position:sticky on the header. */

/* --------------------------------------------------------------------------
   Per-script typography — build prompt §4
   Lao diacritics stack above and below the baseline and clip at normal
   leading, so line height is set per language, not globally.
   -------------------------------------------------------------------------- */
body.ssi-lang-lo {
  font-family: var(--wp--preset--font-family--lao);
  font-size: 18px;
  line-height: var(--wp--custom--line-height--lao);
}
body.ssi-lang-zh {
  font-family: var(--wp--preset--font-family--sc);
  line-height: var(--wp--custom--line-height--sc);
}
body.ssi-lang-lo :is(h1, h2, h3, h4, .wp-block-button__link) {
  line-height: var(--wp--custom--line-height--heading-lao);
  letter-spacing: 0;
}
body.ssi-lang-zh :is(h1, h2, h3, h4) { letter-spacing: 0; }

/* --- Line breaking per script --------------------------------------------
   Lao is written without spaces between words, so the browser sees a single
   40-50 character token and has nowhere legal to break it. Unbroken, one meta
   description put a horizontal scrollbar on the whole page at 360px.

   overflow-wrap: anywhere, NOT word-break: break-all — break-all would also
   split Latin and Chinese runs mid-word; anywhere only breaks a token that
   genuinely cannot fit. Neither is a substitute for a Lao word-segmentation
   dictionary; it is the correct behaviour available in CSS today. */
body.ssi-lang-lo,
body.ssi-lang-lo :is(p, li, dd, dt, td, th, h1, h2, h3, h4, h5, h6,
                     figcaption, label, .ssi-stat__label, .ssi-card__body) {
  overflow-wrap: anywhere;
  word-break: normal;
  hyphens: none;
}
/* Chinese may break between characters, but not before closing punctuation
   or after opening punctuation — strict enforces that. */
body.ssi-lang-zh,
body.ssi-lang-zh :is(p, li, dd, dt, td, th, h1, h2, h3, h4, h5, h6,
                     figcaption, label) {
  line-break: strict;
  overflow-wrap: break-word;
}

/* §4 — no all-caps, no letter-spacing, anywhere, in any language. */
:is(h1, h2, h3, h4, h5, h6, .wp-block-button__link, .wp-block-navigation__responsive-container-content a, label, legend, caption, th) {
  text-transform: none;
}

/* --------------------------------------------------------------------------
   Focus — §4 requires a visible state on every interactive element
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 3px solid var(--wp--preset--color--ssi-red-deep);
  outline-offset: 2px;
}

.ssi-skip-link {
  position: absolute;
  inset-inline-start: -9999px;
  top: 0;
  z-index: 100;
  background: var(--wp--preset--color--ssi-red-deep);
  color: var(--wp--preset--color--surface);
  padding: 12px 18px;
  font-weight: 700;
  text-decoration: none;
}
.ssi-skip-link:focus { inset-inline-start: 0; }

/* --------------------------------------------------------------------------
   Hazard-stripe motif — the eight white bars on the logo's vertical stroke.
   A signature, not a texture: default is a 72px mark, not a full-width rule.
   -------------------------------------------------------------------------- */
.ssi-stripe {
  width: 72px;
  height: 5px;
  background-image: repeating-linear-gradient(
    var(--wp--custom--stripe--angle),
    var(--wp--preset--color--ssi-red) 0 var(--wp--custom--stripe--pitch),
    transparent var(--wp--custom--stripe--pitch) var(--wp--custom--stripe--gap)
  );
}
/* .ssi-stripe--full removed. A full-width diagonal band reads as construction
   tape, not a brand signature — the design brief's own warning. The motif now
   appears only at mark scale: beside the brand, and under section numerals. */

/* --------------------------------------------------------------------------
   Image placeholder — §10
   -------------------------------------------------------------------------- */
.ssi-placeholder {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--wp--preset--spacing--20);
  width: 100%;
  padding: var(--wp--preset--spacing--50);
  text-align: center;
  overflow: hidden;
  background: var(--wp--preset--color--surface-alt);
  border: 1px dashed var(--wp--preset--color--border-dash);
  border-radius: var(--wp--custom--radius);
  color: var(--wp--preset--color--text-ph);
}
.ssi-placeholder::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    var(--wp--custom--stripe--angle),
    rgb(127 127 127 / 5.5%) 0 12px,
    transparent 12px 24px
  );
  pointer-events: none;
}
.ssi-placeholder[data-ratio="21:9"] { aspect-ratio: 21 / 9; }
.ssi-placeholder[data-ratio="16:9"] { aspect-ratio: 16 / 9; }
.ssi-placeholder[data-ratio="3:2"]  { aspect-ratio: 3 / 2; }
.ssi-placeholder[data-ratio="4:3"]  { aspect-ratio: 4 / 3; }
.ssi-placeholder[data-ratio="1:1"]  { aspect-ratio: 1 / 1; }

.ssi-placeholder__icon {
  position: relative;
  width: 30px;
  height: 30px;
  color: var(--wp--preset--color--icon-muted);
}
.ssi-placeholder__label {
  position: relative;
  max-width: 46ch;
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--wp--preset--color--text-ph);
}
.ssi-placeholder__spec {
  position: relative;
  font-size: 0.76rem;
  color: var(--wp--preset--color--text-ph-2);
}

/* Commissioned slots read differently from ones nobody has actioned. */
.ssi-placeholder--commissioned { border-style: solid; }

/* --------------------------------------------------------------------------
   Unresolved content tokens — §12
   Renders visibly rather than vanishing, so an unconfirmed fact cannot ship
   by accident.
   -------------------------------------------------------------------------- */
.ssi-token--unresolved {
  display: inline-block;
  padding: 2px 8px;
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--wp--preset--color--text-ph);
  background: var(--wp--preset--color--surface-alt);
  border: 1px dashed var(--wp--preset--color--border-dash);
  border-radius: var(--wp--custom--radius);
}

/* --------------------------------------------------------------------------
   Certification independence firewall note — §11
   Wording is pending SSI confirmation; the component is built so the copy can
   drop straight in.
   -------------------------------------------------------------------------- */
.ssi-firewall-note {
  padding: var(--wp--preset--spacing--40) var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--note-bg);
  border: 1px solid var(--wp--preset--color--note-border);
  border-radius: var(--wp--custom--radius);
  color: var(--wp--preset--color--note-text);
  font-size: 0.94rem;
}
.ssi-firewall-note > :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Motion — §13: restrained, and off entirely on request
   -------------------------------------------------------------------------- */
/* Progressive enhancement, not decoration-first. The hidden state applies
   only once JavaScript has confirmed it is running and has set the flag on
   <html>. Written the other way round — opacity:0 by default — a script error,
   a blocked file or a slow connection would have left the proof band, the
   credentials and the case figures permanently invisible. Content first,
   motion second; the same rule already governs the marquee pause control and
   the language menu. */
html.ssi-js .ssi-reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
html.ssi-js .ssi-reveal.is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html.ssi-js .ssi-reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   Phase 2 — service, practice and cross-type components
   ========================================================================== */

.ssi-breadcrumb {
  font-size: 0.85rem;
  color: var(--wp--preset--color--ssi-grey-text);
  margin-bottom: var(--wp--preset--spacing--30);
}
.ssi-breadcrumb a { color: var(--wp--preset--color--ssi-grey-text); }

.ssi-lead { color: var(--wp--preset--color--ssi-grey-text); max-width: 54ch; }

/* Deliverables read as ticks; triggers read as questions the buyer recognises. */
.ssi-ticks, .ssi-triggers { list-style: none; padding: 0; margin: 0; }
.ssi-ticks li, .ssi-triggers li {
  position: relative;
  padding-inline-start: 30px;
  margin-bottom: var(--wp--preset--spacing--30);
}
.ssi-ticks li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.55em;
  width: 12px;
  height: 7px;
  border-inline-start: 2.5px solid var(--wp--preset--color--ssi-red-text);
  border-bottom: 2.5px solid var(--wp--preset--color--ssi-red-text);
  transform: rotate(-45deg);
}
.ssi-triggers li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.62em;
  width: 8px;
  height: 8px;
  background: var(--wp--preset--color--ssi-grey);
}

.ssi-proof {
  border-inline-start: 4px solid var(--wp--preset--color--ssi-red);
  padding-inline-start: var(--wp--preset--spacing--40);
  margin-block: var(--wp--preset--spacing--50);
}
.ssi-proof p { margin: 0; font-weight: 700; }

/* .ssi-stat__num / __label are defined once, in the Proof band section. */

/* Practice cards carry their number — the editorial device from the homepage. */
.ssi-practice-grid, .ssi-service-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--wp--preset--spacing--50);
  margin-block: var(--wp--preset--spacing--60);
}
.ssi-practice-card, .ssi-service-card {
  display: block;
  padding: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  text-decoration: none;
  color: var(--wp--preset--color--ink);
  transition: border-color 0.16s ease, box-shadow 0.16s ease;
}
.ssi-practice-card:hover, .ssi-service-card:hover {
  border-color: var(--wp--preset--color--ssi-red-text);
  box-shadow: var(--wp--preset--shadow--card-hover);
}
.ssi-practice-card__num { font-size: 0.86rem; font-weight: 800; color: var(--wp--preset--color--ssi-red-text); }
.ssi-practice-card .ssi-stripe { margin-block: var(--wp--preset--spacing--20) var(--wp--preset--spacing--40); }
.ssi-practice-card__title { margin: 0 0 var(--wp--preset--spacing--20); font-size: 1.15rem; }
.ssi-practice-card__desc { font-size: 0.92rem; color: var(--wp--preset--color--ssi-grey-text); margin: 0; }
.ssi-practice-card__icon {
  width: 48px; height: 48px; border-radius: var(--wp--custom--radius);
  background: var(--wp--preset--color--tint-red); color: var(--wp--preset--color--ssi-red-text);
  display: grid; place-items: center; margin-bottom: var(--wp--preset--spacing--40);
}
.ssi-practice-card__icon svg { width: 24px; height: 24px; stroke: currentColor; fill: none; stroke-width: 1.7; }
.ssi-practice-card { position: relative; padding-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--60); }
.ssi-practice-card__go { display: block; margin-top: var(--wp--preset--spacing--40);
  font-size: 0.86rem; font-weight: 700; color: var(--wp--preset--color--ssi-red-text); }
.ssi-practice-card__go::after { content: " →"; display: inline-block; transition: transform 0.16s ease; }
.ssi-practice-card:hover .ssi-practice-card__go::after { transform: translateX(3px); }

/* A catalogue entry with no post yet is shown, not hidden — see render.php. */
.ssi-service-card--missing {
  border-style: dashed;
  border-color: var(--wp--preset--color--border-dash);
  color: var(--wp--preset--color--text-ph);
}

.ssi-cross__group { margin-block: var(--wp--preset--spacing--60); }
.ssi-cross__label {
  font-size: 0.86rem;
  font-weight: 800;
  color: var(--wp--preset--color--ssi-red-text);
  padding-bottom: var(--wp--preset--spacing--20);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.ssi-cross ul { list-style: none; padding: 0; margin: var(--wp--preset--spacing--30) 0 0; }
.ssi-cross li { padding-block: var(--wp--preset--spacing--20); border-bottom: 1px solid var(--wp--preset--color--border); }

/* Copy still awaiting confirmation renders visibly, never silently. */
.ssi-pending {
  font-size: 0.86rem;
  font-weight: 700;
  padding: var(--wp--preset--spacing--30);
  background: var(--wp--preset--color--surface);
  border: 1px dashed var(--wp--preset--color--border-dash);
  border-radius: var(--wp--custom--radius);
}

/* ==========================================================================
   Navigation and landing-page design

   Two problems this solves. The nav had no way to reach 32 services without
   three clicks, and the services index listed five practices with no way to
   search across them. Both are navigability failures at this catalogue size.
   ========================================================================== */

/* --- Mega menu ---------------------------------------------------------- */
.ssi-nav { display: flex; align-items: center; gap: 2px; }

/* --- Mobile navigation ---------------------------------------------------
   Below lg the six nav items, the brand lockup and the CTA cannot share a
   line. Rather than shrink them all past legibility, the nav becomes a
   disclosure panel behind a labelled button.

   Three things this is NOT:
   · not a hover menu — iPadOS reports a 1024px viewport with touch and no
     hover, so pointer type cannot be inferred from width;
   · not an icon-only "hamburger" — the button carries the word Menu, in the
     current language, because an unlabelled glyph tests badly with users who
     are not habitual web users, which describes much of SSI's Lao audience;
   · not position:fixed — a fixed panel fights iOS Safari's collapsing address
     bar and the on-screen keyboard. It is absolutely positioned under the
     sticky header and scrolls with its own content. */
.ssi-nav__toggle { display: none; }

@media (max-width: 1024px) {
  .ssi-nav__toggle {
    display: inline-flex; align-items: center; gap: 9px;
    min-height: 44px; padding: 10px 14px;
    border: 1px solid var(--wp--preset--color--border);
    border-radius: var(--wp--custom--radius);
    background: var(--wp--preset--color--surface);
    font: inherit; font-size: 0.92rem; font-weight: 700;
    color: var(--wp--preset--color--ink);
    cursor: pointer;
  }
  .ssi-nav__toggle-bars { display: block; width: 18px; height: 12px; position: relative; flex: none; }
  .ssi-nav__toggle-bars::before,
  .ssi-nav__toggle-bars::after,
  .ssi-nav__toggle-bars span {
    content: ""; position: absolute; inset-inline: 0; height: 2px;
    background: var(--wp--preset--color--ssi-red-text); border-radius: 2px;
    transition: transform 0.18s ease, opacity 0.18s ease;
  }
  .ssi-nav__toggle-bars::before { top: 0; }
  .ssi-nav__toggle-bars span { top: 5px; }
  .ssi-nav__toggle-bars::after { top: 10px; }
  .ssi-nav__toggle[aria-expanded="true"] .ssi-nav__toggle-bars::before { transform: translateY(5px) rotate(45deg); }
  .ssi-nav__toggle[aria-expanded="true"] .ssi-nav__toggle-bars span { opacity: 0; }
  .ssi-nav__toggle[aria-expanded="true"] .ssi-nav__toggle-bars::after { transform: translateY(-5px) rotate(-45deg); }

  .ssi-nav {
    position: absolute; inset-inline: 0; top: 100%;
    display: none; flex-direction: column; align-items: stretch; gap: 0;
    padding: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40)
             var(--wp--preset--spacing--50);
    background: var(--wp--preset--color--surface);
    border-top: 1px solid var(--wp--preset--color--border);
    box-shadow: 0 12px 24px rgb(15 18 22 / 10%);
    /* Never taller than the viewport below the header, and scrollable inside
       itself, so a long practice list cannot trap the user. */
    max-height: calc(100dvh - var(--ssi-header-h, 66px));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .ssi-header.is-nav-open .ssi-nav { display: flex; }

  .ssi-nav__link,
  .ssi-nav__trigger {
    min-height: 48px; width: 100%;
    padding-inline: var(--wp--preset--spacing--20);
    justify-content: space-between;
    border-bottom: 1px solid var(--wp--preset--color--border);
    border-radius: 0; font-size: 1rem;
  }
  /* The desktop underline indicator is meaningless in a stacked list. */
  .ssi-nav__link::after, .ssi-nav__trigger::after { display: none; }
  .ssi-nav__item { position: static; width: 100%; }
}

/* The header is sticky, so the panel must know how tall it is. */
.ssi-header__inner { --ssi-header-h: 72px; }
@media (max-width: 1024px) { .ssi-header__inner { --ssi-header-h: 66px; } }

.ssi-nav__item { position: relative; }
.ssi-nav__link,
.ssi-nav__trigger {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 10px 12px; border: 0; background: none; border-radius: var(--wp--custom--radius);
  font: inherit; font-size: 0.92rem; font-weight: 600; line-height: 1.2;
  color: var(--wp--preset--color--ink); text-decoration: none;
  white-space: nowrap; cursor: pointer; position: relative;
}
.ssi-nav__link::after,
.ssi-nav__trigger::after {
  content: ""; position: absolute; inset-inline: 12px; bottom: 3px; height: 2px;
  background: var(--wp--preset--color--ssi-red);
  transform: scaleX(0); transform-origin: left; transition: transform 0.18s ease;
}
.ssi-nav__link:hover::after,
.ssi-nav__trigger:hover::after,
.ssi-nav__trigger[aria-expanded="true"]::after { transform: scaleX(1); }
.ssi-nav__link[aria-current="page"] { color: var(--wp--preset--color--ssi-red-text); }
.ssi-nav__link[aria-current="page"]::after { transform: scaleX(1); }
.ssi-nav__chev { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2.4;
  transition: transform 0.18s ease; }
.ssi-nav__trigger[aria-expanded="true"] .ssi-nav__chev { transform: rotate(180deg); }

.ssi-mega {
  /* Anchored to the header, not to the trigger. Centring a 1100px panel on a
     nav item that sits left of centre pushes it off both edges — the clipping
     seen in review. Spanning the header and constraining the inner grid to the
     content width cannot overflow, at any viewport size. */
  position: absolute;
  inset-inline: 0;
  top: 100%;
  background: var(--wp--preset--color--surface);
  border-block: 1px solid var(--wp--preset--color--border);
  box-shadow: 0 12px 28px rgb(15 18 22 / 8%);
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-6px);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s;
  z-index: 80;
}
.ssi-nav__trigger[aria-expanded="true"] + .ssi-mega,
.ssi-nav__item.is-open .ssi-mega {
  opacity: 1; visibility: visible; pointer-events: auto; transform: translateY(0);
}
.ssi-mega__inner {
  max-width: var(--wp--style--global--wide-size, 1200px);
  margin-inline: auto;
  padding: var(--wp--preset--spacing--60) var(--wp--preset--spacing--50);
}
.ssi-mega__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: var(--wp--preset--spacing--50);
}
.ssi-mega__col { min-width: 0; }
.ssi-mega__num {
  font-size: 0.78rem; font-weight: 800; color: var(--wp--preset--color--ssi-red-text);
  display: block; margin-bottom: 6px;
}
.ssi-mega__head {
  display: block; font-size: 0.98rem; font-weight: 700; line-height: 1.25;
  color: var(--wp--preset--color--ink); text-decoration: none; margin-bottom: 6px;
}
.ssi-mega__head:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-mega__col .ssi-stripe { width: 40px; height: 4px; margin-bottom: 14px; }
.ssi-mega__list { list-style: none; margin: 0; padding: 0; }
.ssi-mega__list a {
  display: block; padding: 5px 0; font-size: 0.88rem; line-height: 1.35;
  color: var(--wp--preset--color--ssi-grey-text); text-decoration: none;
}
.ssi-mega__list a:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-mega__all {
  display: inline-block; margin-top: 10px; font-size: 0.85rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-red-text); text-decoration: none;
}
.ssi-mega__all::after { content: " →"; }
.ssi-mega__foot {
  margin-top: var(--wp--preset--spacing--50); padding-top: var(--wp--preset--spacing--40);
  border-top: 1px solid var(--wp--preset--color--border);
  display: flex; gap: var(--wp--preset--spacing--50); flex-wrap: wrap;
  font-size: 0.86rem; color: var(--wp--preset--color--ssi-grey-text);
}
@media (max-width: 1024px) {
  .ssi-mega { position: static; transform: none; box-shadow: none; border: 0;
    opacity: 1; visibility: visible; }
  .ssi-nav__trigger[aria-expanded="false"] + .ssi-mega { display: none; }
  .ssi-mega__inner { padding: var(--wp--preset--spacing--40) 0 0 var(--wp--preset--spacing--40); }
  .ssi-mega__grid { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--40); }
}

/* --- Service finder ----------------------------------------------------- */
.ssi-finder { margin-block: var(--wp--preset--spacing--60); }
.ssi-finder__bar { display: flex; gap: var(--wp--preset--spacing--30); flex-wrap: wrap; align-items: center; }
.ssi-finder__search {
  flex: 1 1 260px; min-width: 0; padding: 12px 14px; font: inherit; font-size: 1rem;
  border: 1px solid var(--wp--preset--color--border); border-radius: var(--wp--custom--radius);
  background: var(--wp--preset--color--surface); color: var(--wp--preset--color--ink);
}
.ssi-finder__search:focus { border-color: var(--wp--preset--color--ssi-red-text); }
.ssi-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.ssi-chip {
  padding: 8px 14px; border: 1px solid var(--wp--preset--color--border);
  border-radius: 999px; background: var(--wp--preset--color--surface);
  font: inherit; font-size: 0.86rem; font-weight: 600;
  color: var(--wp--preset--color--ssi-grey-text); cursor: pointer;
  transition: border-color 0.14s ease, color 0.14s ease, background-color 0.14s ease;
}
.ssi-chip:hover { border-color: var(--wp--preset--color--ssi-red-text); color: var(--wp--preset--color--ssi-red-text); }
.ssi-chip[aria-pressed="true"] {
  background: var(--wp--preset--color--ssi-red-text);
  border-color: var(--wp--preset--color--ssi-red-text);
  color: var(--wp--preset--color--surface);
}
.ssi-finder__count {
  font-size: 0.88rem; color: var(--wp--preset--color--ssi-grey-text);
  margin-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--30);
}
.ssi-finder__empty {
  padding: var(--wp--preset--spacing--70) var(--wp--preset--spacing--50); text-align: center;
  border: 1px dashed var(--wp--preset--color--border-dash); border-radius: var(--wp--custom--radius);
  color: var(--wp--preset--color--ssi-grey-text);
}

/* Service card gains a practice tag and a directional affordance. */
.ssi-service-card { display: flex; flex-direction: column; position: relative; }
.ssi-service-card__practice {
  font-size: 0.74rem; font-weight: 800; color: var(--wp--preset--color--ssi-red-text);
  margin-bottom: 8px;
}
.ssi-service-card h3 { font-size: 1.05rem; line-height: 1.3; }
.ssi-service-card p { font-size: 0.9rem; color: var(--wp--preset--color--ssi-grey-text); margin-bottom: 16px; }
.ssi-service-card__go {
  margin-top: auto; font-size: 0.85rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-red-text);
}
.ssi-service-card__go::after { content: " →"; display: inline-block; transition: transform 0.16s ease; }
.ssi-service-card:hover .ssi-service-card__go::after { transform: translateX(3px); }

/* --- Landing hero ------------------------------------------------------- */
.ssi-landing-hero {
  display: grid; grid-template-columns: auto 1fr; gap: var(--wp--preset--spacing--50);
  align-items: start; padding-block: var(--wp--preset--spacing--40) var(--wp--preset--spacing--60);
  border-bottom: 1px solid var(--wp--preset--color--border);
  margin-bottom: var(--wp--preset--spacing--60);
}
.ssi-landing-hero__num {
  font-size: clamp(3rem, 8vw, 5rem); font-weight: 800; line-height: 0.85;
  color: var(--wp--preset--color--ssi-red); font-variant-numeric: tabular-nums;
}
.ssi-landing-hero h1 { margin-top: -0.1em; }
@media (max-width: 768px) { .ssi-landing-hero { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--30); } }

/* --- Previous / next within a practice ---------------------------------- */
.ssi-pager {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--wp--preset--spacing--40);
  margin-top: var(--wp--preset--spacing--70); padding-top: var(--wp--preset--spacing--50);
  border-top: 1px solid var(--wp--preset--color--border);
}
.ssi-pager a {
  text-decoration: none; color: var(--wp--preset--color--ink);
  padding: var(--wp--preset--spacing--40); border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius); transition: border-color 0.16s ease;
}
.ssi-pager a:hover { border-color: var(--wp--preset--color--ssi-red-text); }
.ssi-pager span { display: block; font-size: 0.78rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-grey-text); margin-bottom: 4px; }
.ssi-pager .next { text-align: end; }
.ssi-pager .empty { border: 0; }
@media (max-width: 560px) { .ssi-pager { grid-template-columns: 1fr; } }


/* ==========================================================================
   Header — v2

   Replaces the full-bleed hazard band with: a hairline rule, a short stripe
   at mark scale beside the brand, and elevation that appears on scroll. The
   motif survives as a signature; the texture does not.
   ========================================================================== */

.ssi-topbar {
  background: var(--wp--preset--color--surface-alt);
  color: var(--wp--preset--color--ssi-grey-text);
  font-size: 0.78rem;
  line-height: 1.5;
}
.ssi-topbar__inner {
  display: flex; align-items: center; gap: var(--wp--preset--spacing--50);
  flex-wrap: wrap; min-height: 36px;
}
.ssi-topbar__cred { display: inline-flex; align-items: center; gap: 7px; }
.ssi-topbar__cred svg {
  width: 13px; height: 13px; flex: none;
  stroke: var(--wp--preset--color--ssi-red-text); fill: none; stroke-width: 2.2;
}
.ssi-topbar a { color: var(--wp--preset--color--ssi-grey-text); text-decoration: none; }
.ssi-topbar a:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-topbar__right {
  margin-inline-start: auto;
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px var(--wp--preset--spacing--40);
  min-width: 0; /* Without this a flex item refuses to shrink and overflows. */
}

/* --- Topbar: phones, Facebook, language menu -----------------------------
   The topbar carries four things now and the space did not grow, so the
   three-item language row became one control. Order of sacrifice as the
   viewport narrows: second credential, first credential, second phone, email.
   The primary phone, Facebook and the language control never drop — those are
   what a visitor on a phone actually reaches for. */
.ssi-topbar__tel {
  font-weight: 700;
  color: var(--wp--preset--color--ink);
  text-decoration: none;
  white-space: nowrap;
}
.ssi-topbar__tel:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-topbar__fb,
.ssi-lang__toggle {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 4px 8px;
  border: 0; border-radius: 3px;
  background: none; font: inherit;
  color: var(--wp--preset--color--ssi-grey-text);
  text-decoration: none; cursor: pointer;
  white-space: nowrap; flex: none;
}
.ssi-topbar__fb svg { width: 17px; height: 17px; }
.ssi-topbar__fb:hover,
.ssi-lang__toggle:hover { color: var(--wp--preset--color--ssi-red-text); }

/* --- Language menu -------------------------------------------------------
   One control instead of three links. It stays a disclosure, not a <select>:
   a select cannot show a flag, cannot be styled to match, and needs script to
   navigate. Without JavaScript the <ul> is simply a visible list of links, so
   nobody is trapped in a language they cannot read.

   Flags are inline SVG, never emoji — Windows has no flag glyphs, so the Lao
   flag would render as the letters "LA" on most of SSI's clients' desktops.
   Every option carries its name beside the flag, because a flag names a
   country and the reader is choosing a language. */
.ssi-lang { position: relative; flex: none; }
.ssi-lang__cur { font-weight: 700; }
.ssi-lang__chev { width: 13px; height: 13px; transition: transform 0.15s ease; }
.ssi-lang__toggle[aria-expanded="true"] .ssi-lang__chev { transform: rotate(180deg); }

.ssi-flag {
  width: 20px; height: 14px; flex: none;
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 22%);
}
.ssi-flag--globe { width: 17px; height: 17px; box-shadow: none; }

.ssi-lang__menu {
  position: absolute; inset-inline-end: 0; top: calc(100% + 6px);
  z-index: 90; min-width: 190px;
  margin: 0; padding: 5px;
  list-style: none;
  background: var(--wp--preset--color--surface);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  box-shadow: 0 10px 28px rgb(15 18 22 / 16%);
}
/* Script-off default: the list is simply visible, inline, as three links. */
.ssi-lang:not([data-ssi-lang-ready]) .ssi-lang__toggle { display: none; }
.ssi-lang:not([data-ssi-lang-ready]) .ssi-lang__menu {
  position: static; display: flex; gap: 2px;
  min-width: 0; background: none; border: 0; box-shadow: none; padding: 0;
}
.ssi-lang[data-ssi-lang-ready] .ssi-lang__menu { display: none; }
.ssi-lang[data-ssi-lang-ready].is-open .ssi-lang__menu { display: block; }

.ssi-lang__opt {
  display: flex; align-items: center; gap: 10px;
  min-height: 44px; padding: 8px 12px;
  border-radius: 3px;
  color: var(--wp--preset--color--ink);
  text-decoration: none; white-space: nowrap;
}
.ssi-lang:not([data-ssi-lang-ready]) .ssi-lang__opt {
  color: var(--wp--preset--color--ssi-grey-text);
  min-height: 32px; padding: 4px 8px;
}
.ssi-lang__opt:hover { background: var(--wp--preset--color--surface-alt); }
.ssi-lang__opt.is-current { font-weight: 700; }
.ssi-lang__opt.is-current::after {
  content: "";
  width: 7px; height: 7px; margin-inline-start: auto;
  border-radius: 50%;
  background: var(--wp--preset--color--ssi-red-text);
}
.ssi-lang__opt:lang(lo) { font-family: var(--wp--preset--font-family--lao); }
.ssi-lang__opt:lang(zh-Hans), .ssi-lang__opt:lang(zh) {
  font-family: var(--wp--preset--font-family--sc);
}

.ssi-header {
  position: sticky; top: 0; z-index: 70;
  background: color-mix(in srgb, var(--wp--preset--color--surface) 97%, transparent);
  backdrop-filter: saturate(180%) blur(10px);
  border-bottom: 1px solid var(--wp--preset--color--border);
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
}
/* Elevation appears only once the page has moved — a shadow at rest is noise. */
.ssi-header.is-stuck {
  box-shadow: 0 1px 0 var(--wp--preset--color--border), 0 6px 20px rgb(15 18 22 / 6%);
  border-bottom-color: transparent;
}
.ssi-header__inner {
  display: flex; align-items: center; gap: var(--wp--preset--spacing--60);
  min-height: 78px;
}
/* Positioning context for the full-width mega panel. */
.ssi-header { position: sticky; }
.ssi-header > .wp-block-group, .ssi-header > .wrap { position: static; }
.ssi-nav__item { position: static; }

/* --- Call to action ------------------------------------------------------
   Found missing 2026-08-09. There were rules for .ssi-cta--header, --ghost,
   --invert and --external, but never one for .ssi-cta itself — so every
   primary call to action on the site, including the one in the header,
   rendered as a bare underlined link. The modifiers all assumed a base that
   was not there. Nothing caught it because the harness tested that the class
   was USED, not that it was STYLED.

   This is the one place red is allowed to be a fill. ssi-red-text (#D40000)
   not ssi-red (#FF0000): white on pure red is 4.0:1 and fails AA, white on
   this is 5.53:1 and passes. Pure red stays for display type and marks. */
.ssi-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 48px;            /* comfortably past the 24x24 AA floor */
  padding: 12px 22px;
  border: 1px solid transparent;
  border-radius: var(--wp--custom--radius);
  background: var(--wp--preset--color--ssi-red-text);
  color: var(--wp--preset--color--surface);
  font: inherit;
  font-weight: 700;
  line-height: 1.3;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.ssi-cta:hover,
.ssi-cta:focus-visible {
  background: var(--wp--preset--color--ssi-red-deep);
  color: var(--wp--preset--color--surface);
}
.ssi-cta:active { background: var(--wp--preset--color--ssi-red-deep); }

/* Secondary. Outlined, never a second red fill — two red buttons side by side
   is exactly the red saturation this rebuild exists to correct. */
.ssi-cta--ghost {
  background: none;
  border-color: var(--wp--preset--color--border);
  color: var(--wp--preset--color--ink);
}
.ssi-cta--ghost:hover,
.ssi-cta--ghost:focus-visible {
  background: var(--wp--preset--color--surface-alt);
  border-color: var(--wp--preset--color--ssi-red-text);
  color: var(--wp--preset--color--ssi-red-text);
}

/* In the header the control has to fit a 76px bar beside the nav. */
.ssi-cta--header { min-height: 40px; padding: 8px 16px; font-size: 0.92rem; }

/* Lao needs the taller line, and a button is where clipped diacritics are
   most visible because the background stops at the box edge. */
body.ssi-lang-lo .ssi-cta { line-height: var(--wp--custom--line-height--heading-lao); }

/* --- Header flex discipline ----------------------------------------------
   Both of these shipped broken and were caught by eye, not by the harness.
   The toggle rendered as three stacked glyphs — M / e / n — and the CTA
   became a vertical red bar down the right edge. Same cause both times: a
   flex item with no shrink protection in a row too narrow to hold it, so
   flexbox shrank the box and the text wrapped one character per line.

   Controls are fixed-size and never wrap. The brand above is the only item
   permitted to give ground, because it is the only one that can lose
   characters and still be understood. */
.ssi-nav__toggle,
.ssi-cta--header {
  flex: none;
  white-space: nowrap;
  min-width: 0;
}

/* Brand lockup v3.
   The dotted divider read as a stray artefact and the legal suffix trailing on
   the same line read as an afterthought. Two lines, no divider: the name holds
   the top line, the legal form sits beneath it as a caption. The tagline moves
   out of the header — it competes with navigation and already appears in the
   hero and footer. */
/* The brand is the ONLY header item allowed to shrink — it is the only one
   that can lose characters without losing meaning. Controls never do. */
.ssi-brand { display: flex; align-items: center; gap: 13px; text-decoration: none;
  color: inherit; flex: 0 1 auto; min-width: 0; }
.ssi-brand__mark { display: block; flex: none; }
.ssi-brand__mark img,
.ssi-brand__mark svg { height: 46px; width: auto; display: block; }
.ssi-brand__txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ssi-brand__name {
  font-size: 1.28rem; font-weight: 800; line-height: 1.1; letter-spacing: -0.022em;
  white-space: nowrap; color: var(--wp--preset--color--ink);
}
.ssi-brand__legal {
  font-size: 0.72rem; font-weight: 500; line-height: 1.2;
  color: var(--wp--preset--color--ssi-grey-text); white-space: nowrap;
}

@media (max-width: 1280px) {
  .ssi-header__inner { gap: var(--wp--preset--spacing--50); }
  .ssi-brand__name { font-size: 1.14rem; }
}
@media (max-width: 1024px) {
  .ssi-header__inner { min-height: 66px; }
  .ssi-brand__mark img, .ssi-brand__mark svg { height: 38px; }
  .ssi-brand__name { font-size: 1.06rem; }
}
/* Most Lao traffic is mobile (§5), so the topbar has to give things up as the
   viewport narrows. Order of sacrifice: second credential, first credential,
   second phone, email. The primary phone, Facebook and the language control
   never drop. Everything shed here appears in full in the footer and on
   Contact. */
@media (max-width: 1024px) {
  .ssi-topbar__cred:nth-of-type(n+2) { display: none; }
}
@media (max-width: 768px) {
  .ssi-topbar__cred { display: none; }
  .ssi-topbar__tel--second { display: none; }
  .ssi-topbar__inner { justify-content: flex-end; }
}
@media (max-width: 560px) {
  .ssi-brand__legal { display: none; }  /* Full legal form is in the footer. */
  .ssi-topbar__email { display: none; }
}
@media (max-width: 400px) {
  /* The number stays; it is the single most useful thing on a phone. */
  .ssi-lang__cur { display: none; }
  .ssi-topbar__right { gap: 4px; }
}

/* --- Partners ------------------------------------------------------------
   Visually distinct from SSI's own services, because the distinction is the
   point: these are delivered by a partner, and conflating them is what
   ISO/IEC 17021 objects to. */
.ssi-partner { margin-block: var(--wp--preset--spacing--70); }
.ssi-partner__head { max-width: 62ch; margin-bottom: var(--wp--preset--spacing--60); }
.ssi-partner__eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 0.8rem; font-weight: 700; color: var(--wp--preset--color--ssi-grey-text);
  margin-bottom: var(--wp--preset--spacing--30);
}
.ssi-partner__eyebrow::before {
  content: ""; width: 22px; height: 3px; background: var(--wp--preset--color--ssi-grey);
}
.ssi-partner__grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--wp--preset--spacing--40);
}
.ssi-partner__service {
  padding: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface-alt);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
}
.ssi-partner__service h3 { font-size: 1.02rem; margin-bottom: var(--wp--preset--spacing--20); }
.ssi-partner__service p { font-size: 0.92rem; color: var(--wp--preset--color--ssi-grey-text); margin: 0; }
.ssi-partner__pending {
  display: inline-block; margin-inline-start: 8px; padding: 2px 8px;
  font-size: 0.7rem; font-weight: 700; vertical-align: middle;
  background: var(--wp--preset--color--note-bg); color: var(--wp--preset--color--note-text);
  border: 1px solid var(--wp--preset--color--note-border); border-radius: var(--wp--custom--radius);
}


/* --- Services index intro ------------------------------------------------
   The intro opts into the wide measure (alignwide), so it runs to wideSize
   rather than the 760px contentSize the rest of the page reads at. That is
   deliberate: a positioning statement broken across three short lines reads
   as a paragraph, not a statement.

   The headline carries no measure of its own — the container sets the width
   and the type sizes to fill it. text-wrap: balance keeps the break even on
   the widths where it does wrap, instead of leaving a one-word last line. */
.ssi-index-intro {
  margin-bottom: var(--wp--preset--spacing--60);
}
.ssi-index-headline {
  font-size: clamp(1.9rem, 4.2vw, 3rem);
  line-height: 1.15;
  letter-spacing: -0.022em;
  text-wrap: balance;
  max-width: none;
  margin: 0 0 var(--wp--preset--spacing--40);
}
/* Lead measure is wider than the 54ch body default but still short of the
   headline — a paragraph needs a measure, a headline does not. */
.ssi-index-lead {
  font-size: 1.15rem;
  max-width: 68ch;
  margin: 0;
  color: var(--wp--preset--color--ssi-grey-text);
}
/* Lao sets a 1.85 line-height on body and Lao counts characters differently
   from Latin, so a ch-based measure runs long. Both are corrected here rather
   than in the shared rule, which English also uses. */
:lang(lo) .ssi-index-lead { max-width: 58ch; }
:lang(zh) .ssi-index-lead { max-width: 44ch; }


/* ==========================================================================
   Pointer, not width

   iPadOS Safari requests desktop sites by default: it reports a 1024px-wide
   viewport with touch input and no hover. Any affordance that only appears on
   :hover is therefore invisible on the device most likely to be used by the
   procurement manager reading this site at a desk.

   So every hover state is paired with :focus-visible, and on coarse pointers
   the resting state carries the affordance outright rather than revealing it.
   ========================================================================== */
@media (hover: none) {
  /* Cards: the "read more" cue is always visible, never revealed. */
  .ssi-practice-card__go,
  .ssi-service-card__go { opacity: 1; transform: none; }

  /* Nothing should shift under a finger — a transform on tap reads as a
     mis-tap, and on iOS the :hover state sticks after the tap ends. */
  .ssi-practice-card:hover,
  .ssi-service-card:hover,
  .ssi-partner__service:hover { transform: none; }
}

/* Touch targets, applied at every width because pointer type is independent
   of it. 44px is the comfortable size; 24px is the AA floor. */
@media (pointer: coarse) {
  .ssi-nav__link,
  .ssi-nav__trigger,
  .ssi-chip,
  .ssi-lang__opt,
  .ssi-pager a { min-height: 44px; }

  .ssi-lang__opt { padding-inline: 12px; }

  /* Adjacent targets need clearance as well as size (WCAG 2.5.8). */
  .ssi-lang__opt + .ssi-lang__opt { margin-inline-start: 4px; }
}

/* Keyboard parity: anything that changes on hover changes on focus too. */
.ssi-practice-card:focus-visible .ssi-practice-card__go,
.ssi-service-card:focus-visible .ssi-service-card__go { opacity: 1; transform: none; }


/* --- Proof band ----------------------------------------------------------
   Redesigned 2026-08-09. The previous version was wp:columns with full-height
   vertical rules, which produced two faults: the footnote fell inside the
   first column, and the rules read as table dividers rather than as design.

   Now it is a definition list — each figure is a value, each caption is what
   it counts, which is what <dl> is for. Separation comes from the logo's own
   hazard stripe under each numeral rather than from borders: the brief calls
   that motif "a recurring signature", and four small marks do the work four
   heavy rules were doing badly.

   The footnote sits below a full-width rule, outside the grid, so it can no
   longer be mistaken for part of the first figure. */
.ssi-proof-band {
  background: var(--wp--preset--color--surface-alt);
  color: var(--wp--preset--color--ink);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  padding: var(--wp--preset--spacing--60);
  margin-block: var(--wp--preset--spacing--60);
}
.ssi-proof-band__eyebrow {
  display: flex; align-items: center; gap: 13px;
  margin: 0 0 var(--wp--preset--spacing--50);
  font-size: 0.88rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-proof-band__eyebrow .ssi-stripe { width: 40px; height: 4px; flex: none; }

.ssi-proof-band__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--wp--preset--spacing--50);
  margin: 0;
}
.ssi-stat { display: flex; flex-direction: column; }
.ssi-stat__num {
  font-size: clamp(2rem, 4.4vw, 2.9rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  /* Tabular figures, so the comma in 2,000+ does not push it off the rhythm
     the other three sit on. */
  font-variant-numeric: tabular-nums;
  color: var(--wp--preset--color--ssi-red);
  margin: 0;
}
/* The signature at mark scale, under each figure. This IS the separator. */
.ssi-stat__num::after {
  content: "";
  display: block;
  width: 34px; height: 4px;
  margin-top: var(--wp--preset--spacing--30);
  background-image: repeating-linear-gradient(
    var(--wp--custom--stripe--angle),
    var(--wp--preset--color--ssi-red) 0 var(--wp--custom--stripe--pitch),
    transparent var(--wp--custom--stripe--pitch) var(--wp--custom--stripe--gap)
  );
}
.ssi-stat__label {
  margin: var(--wp--preset--spacing--30) 0 0;
  font-size: 0.92rem;
  line-height: 1.45;
  color: var(--wp--preset--color--ssi-grey-text);
  max-width: 24ch;
}
.ssi-proof-band__note {
  margin: var(--wp--preset--spacing--50) 0 0;
  padding-top: var(--wp--preset--spacing--40);
  border-top: 1px solid var(--wp--preset--color--border);
  font-size: 0.84rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
@media (max-width: 1024px) {
  .ssi-proof-band__grid { grid-template-columns: repeat(2, 1fr); }
  .ssi-proof-band { padding: var(--wp--preset--spacing--50); }
}
@media (max-width: 400px) {
  .ssi-proof-band__grid { grid-template-columns: 1fr; }
  .ssi-stat__label { max-width: none; }
}
body.ssi-lang-lo .ssi-stat__label { line-height: var(--wp--custom--line-height--lao); }


/* --- Values, contact routes, office -------------------------------------- */
.ssi-values { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--wp--preset--spacing--40); }
@media (max-width: 1024px) { .ssi-values { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 400px)  { .ssi-values { grid-template-columns: 1fr; } }
.ssi-value h3 { font-size: 1.02rem; margin: 0 0 6px; }
.ssi-value p  { font-size: 0.92rem; color: var(--wp--preset--color--ssi-grey-text); margin: 0; }
.ssi-vision { font-size: 1.14rem; max-width: 60ch; }

.ssi-routes { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--wp--preset--spacing--50); }
@media (max-width: 768px) { .ssi-routes { grid-template-columns: 1fr; } }
.ssi-route {
  padding: var(--wp--preset--spacing--50);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--wp--preset--spacing--30);
}
.ssi-route h2 { font-size: 1.14rem; margin: 0; }
.ssi-route p  { color: var(--wp--preset--color--ssi-grey-text); margin: 0; flex: 1; }
.ssi-office { margin-top: var(--wp--preset--spacing--70); }

/* --- Labelled content gap ------------------------------------------------
   Same discipline as the image placeholder: an absence with a name on it,
   never a silent omission. */
.ssi-gap {
  margin-top: var(--wp--preset--spacing--70);
  padding: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface-alt);
  border: 1px dashed var(--wp--preset--color--border-dash);
  border-radius: var(--wp--custom--radius);
}
.ssi-gap h2 { margin-top: 0; }
.ssi-gap__body { color: var(--wp--preset--color--text-ph); max-width: 62ch; }
.ssi-gap__spec { font-size: 0.82rem; color: var(--wp--preset--color--text-ph-2); margin-bottom: 0; }

/* ==========================================================================
   Brand intro overlay

   Re-creates the supplied logo animation in SVG and CSS. The mp4 is 4.12 MB
   against a 1.5 MB locked homepage budget and 6.0s against a 2.5s LCP budget;
   this is a few kilobytes and 1.6 seconds.

   It sits OVER the homepage, which has already rendered underneath. There is
   no splash URL, no redirect and no interstitial: search engines and the
   performance metrics both see the real page from the first frame.

   The overlay ships with [hidden] and is revealed by script. With JavaScript
   off, or before the script runs, nothing covers the page — a splash that
   cannot be dismissed because its script failed is a locked door.
   ========================================================================== */
.ssi-intro {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  place-items: center;
  background: var(--wp--preset--color--surface);
  /* The mark is red and grey with white bars cut from the red stroke, so it
     reads on white exactly as it does in the header. The overlay fades
     out rather than cutting, so the change of ground is a transition and not
     a flash — which also keeps it clear of WCAG 2.3.1. */
  animation: ssi-intro-out 0.42s ease 1.32s forwards;
}
.ssi-intro[hidden] { display: none; }

.ssi-intro__stage {
  width: min(38vw, 260px);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
}
.ssi-intro__mark { width: 100%; height: auto; overflow: visible; }

/* The two S forms arrive from opposite sides and settle, then the hazard bar
   wipes in — the same beats as the supplied animation, at a quarter of its
   duration. Groups are targeted by fill so this survives the logo being
   re-exported, rather than depending on path order. */
.ssi-intro__red {
  animation: ssi-intro-red 0.72s cubic-bezier(0.16, 0.84, 0.32, 1) both;
}
.ssi-intro__grey {
  animation: ssi-intro-grey 0.72s cubic-bezier(0.16, 0.84, 0.32, 1) 0.08s both;
}
.ssi-intro__bar {
  animation: ssi-intro-bar 0.44s ease 0.62s both;
  transform-origin: center bottom;
}

@keyframes ssi-intro-red  { from { opacity: 0; transform: translateX(-22%) skewX(-8deg); } }
@keyframes ssi-intro-grey { from { opacity: 0; transform: translateX(22%) skewX(8deg); } }
@keyframes ssi-intro-bar  { from { opacity: 0; transform: scaleY(0); } }
@keyframes ssi-intro-out  { to { opacity: 0; visibility: hidden; } }

.ssi-intro__skip {
  position: absolute;
  inset-block-end: max(24px, env(safe-area-inset-bottom));
  inset-inline-end: max(24px, env(safe-area-inset-right));
  min-height: 44px;
  padding: 10px 18px;
  /* Set once, here. The overlay ground is white — see .ssi-intro above — so
     these are the light-ground values. They used to be the dark-ground ones
     with a second block further down overriding them, which meant #B9BEC6
     text on white at 1.9:1 was one deleted rule away from shipping. */
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  background: none;
  color: var(--wp--preset--color--ssi-grey-text);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 700;
  cursor: pointer;
}
.ssi-intro__skip:hover { color: var(--wp--preset--color--ssi-red-text); }

/* §13 and WCAG 2.3.3. Reduced motion removes the intro entirely rather than
   slowing it — a brand flourish is decorative, and the correct amount of
   decorative motion for someone who asked for none is none. */
@media (prefers-reduced-motion: reduce) {
  .ssi-intro { display: none !important; }
}

/* --- Brand film ----------------------------------------------------------
   Click-to-play only. The poster is the final frame of the animation, so the
   resting state is the finished logo rather than a black rectangle. */
.ssi-film { margin: var(--wp--preset--spacing--60) 0 0; }
.ssi-film__video {
  width: 100%; height: auto; display: block;
  /* Only visible in the letterbox gaps; the poster covers the rest. */
  background: var(--wp--preset--color--surface-alt);
  border-radius: var(--wp--custom--radius);
}
.ssi-film__caption {
  font-size: 0.84rem;
  color: var(--wp--preset--color--ssi-grey-text);
  margin-top: var(--wp--preset--spacing--20);
}

/* --- Product card --------------------------------------------------------
   The platform is a hand-off, not a page to dwell on: identity, what it does,
   what it covers, and a way in. */
.ssi-product {
  padding: var(--wp--preset--spacing--60);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  margin-block: var(--wp--preset--spacing--60);
}
.ssi-product__head {
  display: flex; align-items: center; gap: var(--wp--preset--spacing--40);
  margin-bottom: var(--wp--preset--spacing--40);
}
.ssi-product__icon { width: 72px; height: 72px; flex: none; border-radius: 14px; }
.ssi-product__eyebrow {
  font-size: 0.82rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-grey-text); margin: 0 0 2px;
}
.ssi-product__name { font-size: 1.6rem; margin: 0; }

.ssi-modules { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: 8px;
  margin-block: var(--wp--preset--spacing--40); }
.ssi-module {
  padding: 6px 14px; font-size: 0.88rem; font-weight: 600;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: 999px;
  background: var(--wp--preset--color--surface-alt);
}
.ssi-outcomes { columns: 2; column-gap: var(--wp--preset--spacing--50); }
@media (max-width: 560px) { .ssi-outcomes { columns: 1; } }

.ssi-product__actions { display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--30);
  margin-block: var(--wp--preset--spacing--50) var(--wp--preset--spacing--30); }
@media (max-width: 560px) { .ssi-product__actions .ssi-cta { width: 100%; } }

/* The new-tab affordance is shown, not sprung — WCAG 3.2.5. */
.ssi-cta--external { gap: 9px; }
.ssi-cta__ext { width: 15px; height: 15px; flex: none; }
.ssi-sr {
  position: absolute; width: 1px; height: 1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.ssi-product__source { font-size: 0.8rem; color: var(--wp--preset--color--ssi-grey-text);
  border-top: 1px solid var(--wp--preset--color--border);
  padding-top: var(--wp--preset--spacing--30); margin-bottom: 0; }
.ssi-gap__list { color: var(--wp--preset--color--text-ph); max-width: 68ch; }

/* --- Product body: copy beside the banner --------------------------------
   Two columns above 768px, stacked below. The copy leads because it is the
   part that translates, resizes and gets read by a screen reader; the banner
   is a fixed marketing composition with its wording baked in.

   The outcomes list drops to one column inside the narrower half-width
   column — two columns of ticks at 46vw is four words per line. */
.ssi-product__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--wp--preset--spacing--60);
  align-items: start;
}
.ssi-product__copy { min-width: 0; }
.ssi-product__body .ssi-outcomes { columns: 1; }
.ssi-product__body .ssi-lead { max-width: none; }

.ssi-product__figure { margin: 0; min-width: 0; }
.ssi-product__figure img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--wp--custom--radius);
  border: 1px solid var(--wp--preset--color--border);
}

@media (max-width: 768px) {
  .ssi-product__body { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--50); }
  /* Below the fold once stacked, so the copy is not held up by the image. */
  .ssi-product__figure { order: 2; }
}

/* --- Contact ------------------------------------------------------------
   Each number is two targets: call it, or open WhatsApp. WhatsApp is how
   business is actually done in Laos, and a wa.me link opens a conversation
   rather than asking someone to copy a number into another app. */
.ssi-phones { list-style: none; padding: 0; margin: 0 0 var(--wp--preset--spacing--20); }
.ssi-phone {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--wp--preset--spacing--30);
  margin-bottom: var(--wp--preset--spacing--20);
}
.ssi-phone__num {
  font-size: 1.08rem; font-weight: 700;
  color: var(--wp--preset--color--ink);
  text-decoration: none;
  min-height: 44px; display: inline-flex; align-items: center;
}
.ssi-phone__num:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-phone__wa {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 44px; padding: 6px 12px;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  font-size: 0.86rem; font-weight: 600;
  color: var(--wp--preset--color--ssi-grey-text);
  text-decoration: none;
}
.ssi-phone__wa svg { width: 16px; height: 16px; flex: none; }
.ssi-phone__wa:hover {
  color: var(--wp--preset--color--ssi-red-text);
  border-color: var(--wp--preset--color--ssi-red-text);
}
.ssi-phones__note { font-size: 0.84rem; color: var(--wp--preset--color--ssi-grey-text); }
.ssi-office__addr { font-size: 1.04rem; }
.ssi-office__reg { font-size: 0.84rem; color: var(--wp--preset--color--ssi-grey-text); }

.ssi-foot-contact { display: flex; flex-wrap: wrap; gap: 6px var(--wp--preset--spacing--40); }
.ssi-foot-contact a { min-height: 44px; display: inline-flex; align-items: center; }

/* ==========================================================================
   Client logo marquee

   §13 asks for restrained motion, and an infinite marquee is the opposite of
   restrained — it never stops. So it earns its place by being useful (it is
   the evidence for "200+ client organisations" directly above it) and by
   obeying three rules absolutely:

   1. WCAG 2.2.2. Moving content that starts automatically and runs longer
      than five seconds MUST have a mechanism to pause it. A marquee runs
      forever, so the pause button is not a nicety — without it this component
      cannot ship. It also pauses on hover and on keyboard focus.
   2. prefers-reduced-motion replaces the marquee with a static wrapped grid.
      Not slower — stopped. Every mark stays reachable.
   3. Greyscale, so a wall of competing brand colours does not out-shout the
      page. Colour returns on hover and focus, which also confirms the row is
      interactive.

   The track holds the marks twice. The second copy is aria-hidden, or a
   screen reader would read the entire client list through twice.
   ========================================================================== */
/* --- Client logo marquee -------------------------------------------------
   Greyscale by default: 33 competing brand palettes would otherwise fight
   the page and SSI's own red. Colour returns on hover and keyboard focus. */
.ssi-logos { position: relative; }
.ssi-logos__head {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--wp--preset--spacing--30) var(--wp--preset--spacing--40);
  margin-bottom: var(--wp--preset--spacing--40);
}
.ssi-logos__title { font-size: 0.88rem; font-weight: 700; margin: 0;
  color: var(--wp--preset--color--ssi-grey-text); }
.ssi-logos__note { font-size: 0.82rem; margin: 0;
  color: var(--wp--preset--color--ssi-grey-text); }

.ssi-logos__viewport {
  overflow: hidden;
  /* Fade the ends so marks enter and leave rather than being sliced. */
  /* A mask stencil, not a colour: these stops mean 'opaque here', which is
     why they are keywords and not palette tokens. */
  -webkit-mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
}
.ssi-logos__track {
  display: flex;
  align-items: center;
  gap: clamp(28px, 5vw, 64px);
  width: max-content;
  animation: ssi-marquee var(--ssi-marquee-duration, 60s) linear infinite;
}
@keyframes ssi-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Pause on hover, on keyboard focus anywhere inside, and on demand. */
.ssi-logos:hover .ssi-logos__track,
.ssi-logos:focus-within .ssi-logos__track,
.ssi-logos.is-paused .ssi-logos__track { animation-play-state: paused; }

.ssi-logo {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  height: 44px;
}
.ssi-logo img {
  max-height: 44px; width: auto; display: block;
  filter: grayscale(1);
  opacity: 0.62;
  transition: filter 0.2s ease, opacity 0.2s ease;
}
.ssi-logo:hover img,
.ssi-logo:focus-visible img { filter: grayscale(0); opacity: 1; }

.ssi-logos__pause {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 32px; padding: 5px 11px;
  margin-inline-start: auto;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  background: none; font: inherit; font-size: 0.8rem; font-weight: 600;
  color: var(--wp--preset--color--ssi-grey-text);
  cursor: pointer;
}
.ssi-logos__pause:hover { color: var(--wp--preset--color--ink); }
.ssi-logos__pause svg { width: 13px; height: 13px; }
.ssi-logos__pause .ssi-logos__ico--play { display: none; }
.ssi-logos.is-paused .ssi-logos__pause .ssi-logos__ico--play { display: block; }
.ssi-logos.is-paused .ssi-logos__pause .ssi-logos__ico--pause { display: none; }

/* Script off: the control cannot work, so do not show it. Hover still pauses. */
.ssi-logos:not([data-ssi-logos-ready]) .ssi-logos__pause { display: none; }

/* --- Reduced motion: a static grid, not a slower marquee -----------------
   Someone who asked for no motion gets none. The second, aria-hidden copy is
   removed so the grid does not show every client twice. */
@media (prefers-reduced-motion: reduce) {
  .ssi-logos__viewport { -webkit-mask-image: none; mask-image: none; overflow: visible; }
  .ssi-logos__track {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--wp--preset--spacing--40) var(--wp--preset--spacing--50);
  }
  .ssi-logos__track [aria-hidden="true"] { display: none; }
  .ssi-logos__pause { display: none !important; }
}

/* Institutions read differently from clients: fewer, named, and not a claim
   of custom. A quiet static row rather than a second marquee. */
.ssi-inst {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  /* Nine marks measure about 1045px at 38px tall. The gap is sized so they
     hold ONE line across the wide measure and wrap only when the viewport
     genuinely cannot take them — a single stray mark on a second line reads
     as an afterthought rather than as a group. */
  gap: var(--wp--preset--spacing--40) clamp(20px, 3vw, 44px);
  margin-top: var(--wp--preset--spacing--50);
  padding-top: var(--wp--preset--spacing--50);
  border-top: 1px solid var(--wp--preset--color--border);
}
.ssi-inst img { max-height: 38px; width: auto; filter: grayscale(1); opacity: 0.55; }
/* Below the wide measure the marks shrink before the row breaks. */
@media (max-width: 1024px) { .ssi-inst img { max-height: 32px; } }
@media (max-width: 768px)  { .ssi-inst img { max-height: 28px; } }
.ssi-inst a:hover img, .ssi-inst a:focus-visible img { filter: grayscale(0); opacity: 1; }

/* ==========================================================================
   Brand grounds — replacing the near-black bands

   SSI confirmed on 2026-08-09 that a large field of near-black reads as
   inauspicious to a Lao audience, and asked that the design stay inside the
   logo's own colours. It does now: white and light grey grounds, red for the
   statistics, grey for the rules between them. No fourth colour is introduced.

   §3 already permitted this. Red is listed there as belonging on "key
   statistics", and #FF0000 is reserved for "logo, large display type, solid
   fills, accents" — a 33px bold numeral is large display type. It clears the
   3.0 WCAG needs at that size with 3.70:1.

   These rules come last in the file so they win on order without !important.
   The dark tokens stay defined in theme.json; they are simply no longer used
   for large areas.
   ========================================================================== */

/* --- Topbar ground -------------------------------------------------------
   Reconciled 2026-08-09. The topbar was styled twice: once for the retired
   near-black ground (white text, red-on-dark) and once for the light ground
   that replaced it. The cascade happened to resolve correctly, so nothing
   looked wrong — but the file held two answers and the dead one would have
   been read as intent by whoever maintains this next. The colours are now
   set once, above, and only what is genuinely additional lives here. */
.ssi-topbar { border-bottom: 1px solid var(--wp--preset--color--border); }
/* The Lao and PRC flags are both largely red; without a hairline they bleed
   into a red hover state. Inset, not a border, so the 20x14 box does not grow. */
.ssi-flag { box-shadow: inset 0 0 0 1px rgb(31 35 40 / 14%); }


/* --- Training catalogue --------------------------------------------------
   /training/ was in the primary nav from the beginning with nothing behind
   it. Cards link out to the service page that already carries the detail, so
   a programme is never described in two places that can drift apart. */
.ssi-training-intro .ssi-lead { font-size: 1.14rem; max-width: 62ch; }
.ssi-endorse {
  display: flex; align-items: flex-start; gap: 13px;
  margin: var(--wp--preset--spacing--40) 0 0;
  font-weight: 700; max-width: 60ch;
}
.ssi-endorse .ssi-stripe { width: 40px; height: 4px; flex: none; margin-top: 0.62em; }

/* Three figures, not four — the training record is a subset of the sitewide
   one, and padding it to four columns would mean inventing a fourth. */
.ssi-proof-band__grid--3 { grid-template-columns: repeat(3, 1fr); }

.ssi-how__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: var(--wp--preset--spacing--40) var(--wp--preset--spacing--60);
}
.ssi-how__list li {
  padding-inline-start: var(--wp--preset--spacing--40);
  border-inline-start: 3px solid var(--wp--preset--color--ssi-red);
  color: var(--wp--preset--color--ssi-grey-text);
}

.ssi-courses__lead { color: var(--wp--preset--color--ssi-grey-text); max-width: 62ch; }
.ssi-course-grid {
  list-style: none; margin: var(--wp--preset--spacing--50) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--wp--preset--spacing--40);
}
.ssi-course {
  display: flex; flex-direction: column;
  padding: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
}
.ssi-course__ref {
  margin: 0 0 var(--wp--preset--spacing--30);
  font-size: 0.8rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--wp--preset--color--ssi-red);
}
.ssi-course__title { margin: 0 0 var(--wp--preset--spacing--30); font-size: 1.08rem; }
.ssi-course__title a { color: var(--wp--preset--color--ink); text-decoration: none; }
.ssi-course__title a:hover { color: var(--wp--preset--color--ssi-red-text); }
/* The whole card is the target, not just the four words of the link. */
.ssi-course__title a::after { content: ""; position: absolute; inset: 0; }
.ssi-course { position: relative; }
.ssi-course:hover { border-color: var(--wp--preset--color--ssi-red-text); }
.ssi-course__summary {
  margin: 0; font-size: 0.94rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-course__flag {
  margin: var(--wp--preset--spacing--40) 0 0;
  padding-top: var(--wp--preset--spacing--30);
  border-top: 1px solid var(--wp--preset--color--border);
  font-size: 0.84rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-red-text);
}

.ssi-scale__body { max-width: 66ch; }
.ssi-scale__link a { color: var(--wp--preset--color--ssi-red-text); font-weight: 700; }

@media (max-width: 768px) {
  .ssi-how__list { grid-template-columns: 1fr; }
  .ssi-proof-band__grid--3 { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 400px) {
  .ssi-proof-band__grid--3 { grid-template-columns: 1fr; }
}
body.ssi-lang-lo .ssi-course__summary,
body.ssi-lang-lo .ssi-how__list li { line-height: var(--wp--custom--line-height--lao); }


/* --- Case study index ----------------------------------------------------
   The second dead nav link, found by the §7 guard. One case study is
   published, so this is an index of one — which is why the note explaining
   the short list is part of the design rather than an apology bolted on. */
.ssi-case-list { list-style: none; margin: var(--wp--preset--spacing--60) 0 0; padding: 0;
  display: grid; gap: var(--wp--preset--spacing--50); }
.ssi-case-card {
  position: relative;
  padding: var(--wp--preset--spacing--60);
  background: var(--wp--preset--color--surface);
  border: 1px solid var(--wp--preset--color--border);
  border-inline-start: 4px solid var(--wp--preset--color--ssi-red);
  border-radius: 0;   /* single-sided accent — square, or the corners fight it */
}
.ssi-case-card:hover { border-color: var(--wp--preset--color--ssi-red-text);
  border-inline-start-color: var(--wp--preset--color--ssi-red); }
.ssi-case-card__title { margin: 0 0 var(--wp--preset--spacing--30); font-size: 1.35rem; }
.ssi-case-card__title a { color: var(--wp--preset--color--ink); text-decoration: none; }
.ssi-case-card__title a:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-case-card__title a::after { content: ""; position: absolute; inset: 0; }
.ssi-case-card__meta {
  display: flex; flex-wrap: wrap; gap: 6px var(--wp--preset--spacing--50);
  margin: 0 0 var(--wp--preset--spacing--40);
  font-size: 0.86rem; color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-case-card__meta strong { color: var(--wp--preset--color--ink); font-weight: 700; }
.ssi-case-card__lead { margin: 0; max-width: 68ch; color: var(--wp--preset--color--ssi-grey-text); }
.ssi-case-note {
  margin-top: var(--wp--preset--spacing--50);
  font-size: 0.86rem; color: var(--wp--preset--color--text-ph-2);
}
body.ssi-lang-lo .ssi-case-card__lead { line-height: var(--wp--custom--line-height--lao); }


/* --- Homepage hero -------------------------------------------------------
   Found unstyled 2026-08-09 in the same sweep as .ssi-cta: the markup, the
   copy and the translations all existed, and not one rule had been written
   for any of it. The hero was rendering on the browser's h1 and p defaults. */
.ssi-hero {
  margin-block: var(--wp--preset--spacing--60) var(--wp--preset--spacing--80);
  /* Two columns since 2026-08-10. The text held the full width with the right
     half empty; the aside puts the company profile and the QR where a first-
     time visitor from a ministry or an investor actually looks for them.
     minmax(0,...) on the text column, or a long unbroken Lao run pushes the
     grid past the container. */
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 0.8fr);
  gap: var(--wp--preset--spacing--80);
  align-items: start;
}
.ssi-hero__body { min-width: 0; }

.ssi-hero__aside {
  display: flex; flex-direction: column;
  gap: var(--wp--preset--spacing--50);
  padding: var(--wp--preset--spacing--50);
  background: var(--wp--preset--color--surface-alt);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  /* The eyebrow sits on the baseline grid; drop the aside to meet the
     headline rather than the line above it. */
  margin-top: var(--wp--preset--spacing--60);
}
.ssi-aside__dl { width: 100%; justify-content: flex-start; gap: 10px; }
.ssi-aside__dl svg { width: 17px; height: 17px; flex: none; }
/* Pending is a span, not a link — there is nothing to download yet, and a
   disabled-looking button that still navigates is worse than one that does
   not pretend. Cursor and contrast say so without colour alone. */
.ssi-aside__dl.is-pending {
  cursor: not-allowed;
  border-style: dashed;
  border-color: var(--wp--preset--color--border-dash);
  color: var(--wp--preset--color--text-ph);
}
.ssi-aside__dl.is-pending:hover {
  background: none;
  border-color: var(--wp--preset--color--border-dash);
  color: var(--wp--preset--color--text-ph);
}
.ssi-aside__meta {
  margin: -8px 0 0;
  font-size: 0.8rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-aside__meta--pending { color: var(--wp--preset--color--text-ph-2); }

.ssi-aside__qrwrap {
  margin: 0; padding-top: var(--wp--preset--spacing--40);
  border-top: 1px solid var(--wp--preset--color--border);
  display: flex; flex-direction: column; align-items: center;
  gap: var(--wp--preset--spacing--30); text-align: center;
}
.ssi-aside__qr {
  display: block; width: 160px; height: 160px;
  background: var(--wp--preset--color--surface);
  border-radius: var(--wp--custom--radius);
}
/* Reserve the exact final size so generating the code later shifts nothing. */
.ssi-aside__qr--pending {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 12px;
  border: 1px dashed var(--wp--preset--color--border-dash);
  color: var(--wp--preset--color--text-ph-2);
  font-size: 0.74rem; line-height: 1.35;
}
.ssi-aside__qr--pending svg { width: 30px; height: 30px; color: var(--wp--preset--color--icon-muted); }
.ssi-aside__qrwrap figcaption {
  font-size: 0.8rem; color: var(--wp--preset--color--ssi-grey-text); max-width: 24ch;
}

/* The aside is supporting material. Below the tablet breakpoint it goes under
   the headline rather than squeezing it, and it never precedes the H1 in the
   reading order at any width — it is second in the DOM. */
@media (max-width: 1024px) {
  .ssi-hero { grid-template-columns: 1fr; gap: var(--wp--preset--spacing--60); }
  .ssi-hero__aside { margin-top: 0; max-width: 420px; }
}
body.ssi-lang-lo .ssi-aside__qrwrap figcaption { line-height: var(--wp--custom--line-height--lao); max-width: 28ch; }
.ssi-hero__eyebrow {
  display: flex; align-items: center; gap: 13px;
  margin: 0 0 var(--wp--preset--spacing--40);
  font-size: 0.9rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-hero__eyebrow .ssi-stripe { width: 40px; height: 4px; flex: none; }
.ssi-hero__h1 {
  margin: 0 0 var(--wp--preset--spacing--40);
  font-size: clamp(2.1rem, 5.2vw, 3.5rem);
  line-height: 1.1;
  letter-spacing: -0.025em;
  /* Wide enough to use the page, short enough that the eye can track a line.
     The brief's complaint was text wrapping too narrow; this is the ceiling. */
  max-width: 20ch;
}
.ssi-hero__lead {
  margin: 0 0 var(--wp--preset--spacing--50);
  font-size: clamp(1.05rem, 1.6vw, 1.22rem);
  color: var(--wp--preset--color--ssi-grey-text);
  max-width: 62ch;
}
.ssi-hero__actions {
  display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--30);
  margin: 0;
}
@media (max-width: 560px) {
  .ssi-hero__h1 { max-width: none; }
  .ssi-hero__actions .ssi-cta { flex: 1 1 100%; }
}
body.ssi-lang-lo .ssi-hero__h1 { max-width: 24ch; line-height: var(--wp--custom--line-height--heading-lao); }
body.ssi-lang-zh .ssi-hero__h1 { max-width: 18ch; }

/* --- Credentials ---------------------------------------------------------
   "Who you are dealing with". Three spans per item were rendering as one
   unbroken string — "Operating LicenceNo. 4754 / MoLSWGranted 20 November
   2019" — because no rule made them blocks.

   Deliberately NOT shaped like the proof band, which sits a few hundred
   pixels below it on the same page. Two filled grey boxes with red numerals
   would read as the same component twice. These are documents you can check,
   not achievements we counted: white ground, a red hairline at the start of
   each item, and the value carrying the weight. The proof band celebrates;
   this one simply states. */
.ssi-creds { margin-block: var(--wp--preset--spacing--70); }
.ssi-creds__head {
  margin: 0 0 var(--wp--preset--spacing--50);
  padding-bottom: var(--wp--preset--spacing--30);
  border-bottom: 1px solid var(--wp--preset--color--border);
  font-size: 1.4rem;
}
.ssi-creds__grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--wp--preset--spacing--50);
}
.ssi-cred {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding-inline-start: var(--wp--preset--spacing--40);
  border-inline-start: 3px solid var(--wp--preset--color--ssi-red);
  min-width: 0;
}
.ssi-cred__label {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-cred__value {
  font-size: 1.12rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--wp--preset--color--ink);
  /* Licence and registration numbers must line up, and must not be broken
     across lines by the browser looking for somewhere to wrap. */
  font-variant-numeric: tabular-nums;
  overflow-wrap: break-word;
}
.ssi-cred__note {
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--wp--preset--color--ssi-grey-text);
}
/* An empty note must not leave a phantom gap. */
.ssi-cred__note:empty { display: none; }

@media (max-width: 1024px) { .ssi-creds__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px)  { .ssi-creds__grid { grid-template-columns: 1fr; } }
body.ssi-lang-lo .ssi-cred__note,
body.ssi-lang-lo .ssi-cred__value { line-height: var(--wp--custom--line-height--lao); }

/* --- Case study figures --------------------------------------------------
   These ARE counted results, so unlike the credentials they take the proof
   band's shape: red numeral, stripe mark, caption. Same device, same meaning. */
.ssi-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--wp--preset--spacing--50);
  margin-block: var(--wp--preset--spacing--60);
  padding: var(--wp--preset--spacing--60);
  background: var(--wp--preset--color--surface-alt);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
}
.ssi-figure { display: flex; flex-direction: column; }
.ssi-figure__value {
  font-size: clamp(1.9rem, 4vw, 2.6rem);
  font-weight: 800; line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
  color: var(--wp--preset--color--ssi-red);
}
.ssi-figure__value::after {
  content: ""; display: block;
  width: 34px; height: 4px;
  margin-top: var(--wp--preset--spacing--30);
  background-image: repeating-linear-gradient(
    var(--wp--custom--stripe--angle),
    var(--wp--preset--color--ssi-red) 0 var(--wp--custom--stripe--pitch),
    transparent var(--wp--custom--stripe--pitch) var(--wp--custom--stripe--gap)
  );
}
.ssi-figure__label {
  margin-top: var(--wp--preset--spacing--30);
  font-size: 0.92rem; font-weight: 700;
  color: var(--wp--preset--color--ink);
}
.ssi-figure__note {
  margin-top: 3px;
  font-size: 0.84rem; line-height: 1.45;
  color: var(--wp--preset--color--ssi-grey-text);
}
body.ssi-lang-lo :is(.ssi-figure__label, .ssi-figure__note) {
  line-height: var(--wp--custom--line-height--lao);
}

/* --- Case study body ----------------------------------------------------- */
.ssi-case__meta {
  margin: 0 0 var(--wp--preset--spacing--50);
  font-size: 0.9rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-case__inclusion {
  margin: var(--wp--preset--spacing--50) 0 0;
  padding-inline-start: var(--wp--preset--spacing--40);
  border-inline-start: 3px solid var(--wp--preset--color--ssi-red);
  max-width: 62ch;
}
.ssi-case__inclusion strong {
  font-size: 1.28rem; font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--wp--preset--color--ssi-red);
}
.ssi-case__inclusion span {
  display: block; margin-top: 3px;
  font-size: 0.86rem; color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-case__note {
  margin-top: var(--wp--preset--spacing--40);
  font-size: 0.84rem;
  color: var(--wp--preset--color--text-ph-2);
}

/* --- Section rhythm ------------------------------------------------------
   Containers whose only job is vertical spacing. Named so that the §7 guard
   sees a decision rather than an omission. */
.ssi-how,
.ssi-courses,
.ssi-scale { margin-block: var(--wp--preset--spacing--70); }
.ssi-cta-band__body { max-width: 62ch; color: var(--wp--preset--color--ssi-grey-text); }
.ssi-cta-band__act { margin-bottom: 0; }
.ssi-inst__mark { display: inline-flex; align-items: center; }
/* Nested inside the finder panel, so it must not repeat the outer top margin. */
.ssi-service-grid--nested { margin-top: 0; }


/* --- Contact routes ------------------------------------------------------
   Four routes, one per conversion goal. Each previously ended in an identical
   empty mailto with no subject, so SSI received enquiries with no site, no
   headcount and no language and had to ask for all of it before quoting. The
   checklist is questions only — it asserts nothing about SSI. */
.ssi-route__ask-intro {
  margin: 0;
  font-size: 0.86rem; font-weight: 700;
  color: var(--wp--preset--color--ink);
}
.ssi-route__ask {
  margin: 0; padding: 0; list-style: none;
  display: flex; flex-direction: column; gap: 4px;
  font-size: 0.88rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-route__ask li { padding-inline-start: 15px; position: relative; }
.ssi-route__ask li::before {
  content: ""; position: absolute;
  inset-inline-start: 0; top: 0.62em;
  width: 7px; height: 2px;
  background: var(--wp--preset--color--ssi-red);
}
.ssi-route__acts {
  display: flex; flex-wrap: wrap; gap: var(--wp--preset--spacing--30);
  margin: var(--wp--preset--spacing--20) 0 0;
}

/* Arrived from a service page's "Enquire about training" link. A ring, not a
   fill: the route must be findable without becoming the only thing on screen,
   because the other three are still valid choices. */
.ssi-route.is-asked {
  border-color: var(--wp--preset--color--ssi-red);
  box-shadow: 0 0 0 3px var(--wp--preset--color--tint-red);
}
.ssi-route.is-asked:focus { outline: none; }
@media (max-width: 560px) { .ssi-route__acts .ssi-cta { flex: 1 1 100%; } }
body.ssi-lang-lo .ssi-route__ask { line-height: var(--wp--custom--line-height--lao); }


/* --- Photograph in a placeholder slot ------------------------------------
   The filled state of ssi/placeholder. Same box, same ratio, so replacing a
   brief with a photograph moves nothing on the page — which is the whole
   reason the slot holds a ratio in the first place. */
.ssi-photo {
  margin: 0;
  position: relative;
  border-radius: var(--wp--custom--radius);
  overflow: hidden;
  background: var(--wp--preset--color--surface-alt);
}
.ssi-photo__img {
  display: block;
  width: 100%;
  height: 100%;
  /* The box owns the shape; the photograph is cropped into it. object-position
     is set per block from the focal point an editor dragged, so the crop keeps
     faces rather than centring blindly. */
  object-fit: cover;
}
.ssi-photo[data-ratio="21:9"] .ssi-photo__img { aspect-ratio: 21 / 9; }
.ssi-photo[data-ratio="16:9"] .ssi-photo__img { aspect-ratio: 16 / 9; }
.ssi-photo[data-ratio="3:2"]  .ssi-photo__img { aspect-ratio: 3 / 2; }
.ssi-photo[data-ratio="4:3"]  .ssi-photo__img { aspect-ratio: 4 / 3; }
.ssi-photo[data-ratio="1:1"]  .ssi-photo__img { aspect-ratio: 1 / 1; }

.ssi-photo__credit {
  margin: var(--wp--preset--spacing--20) 0 0;
  padding-inline: var(--wp--preset--spacing--30);
  font-size: 0.8rem;
  color: var(--wp--preset--color--ssi-grey-text);
}

/* Editor only. An editor who can see their photograph on screen would have no
   way of knowing visitors cannot, so the reason is stated over the image. */
.ssi-photo__held {
  position: absolute;
  inset-block-start: 0; inset-inline: 0;
  padding: 8px 12px;
  background: var(--wp--preset--color--note-bg);
  border-bottom: 1px solid var(--wp--preset--color--note-border);
  color: var(--wp--preset--color--note-text);
  font-size: 0.82rem; font-weight: 700;
}

/* A slot holding a file that cannot yet publish is not the same as a slot
   with nothing in it, and the shot list should not read them as equal. */
.ssi-placeholder--awaiting-consent,
.ssi-placeholder--awaiting-alt {
  border-style: solid;
  border-color: var(--wp--preset--color--note-border);
  background: var(--wp--preset--color--note-bg);
}
.ssi-placeholder__hold {
  margin-top: var(--wp--preset--spacing--20);
  font-size: 0.8rem; font-weight: 700;
  color: var(--wp--preset--color--note-text);
}
.ssi-placeholder--commissioned { border-style: solid; }


/* --- Previous work list --------------------------------------------------
   Our Experience, which replaced the Training tab on 2026-08-10. Deliberately
   a list rather than a card grid: these entries are of very different weights
   — a national programme beside a document translation — and equal-sized
   cards would claim they were comparable. A list lets each be as long as it
   actually is. */
.ssi-works { margin-block: var(--wp--preset--spacing--70); }

.ssi-work__controls {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--wp--preset--spacing--30);
  margin-block: var(--wp--preset--spacing--50);
  padding-bottom: var(--wp--preset--spacing--40);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.ssi-work__search {
  flex: 1 1 260px; min-width: 0;
  min-height: 44px; padding: 10px 14px;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  background: var(--wp--preset--color--surface);
  font: inherit; color: var(--wp--preset--color--ink);
}
.ssi-work__search:focus-visible {
  outline: 2px solid var(--wp--preset--color--ssi-red-deep);
  outline-offset: 1px;
}
.ssi-work__filters { display: flex; flex-wrap: wrap; gap: 6px; }
.ssi-work__filter {
  min-height: 36px; padding: 7px 13px;
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
  background: none; font: inherit; font-size: 0.86rem; font-weight: 600;
  color: var(--wp--preset--color--ssi-grey-text);
  cursor: pointer;
}
.ssi-work__filter:hover { color: var(--wp--preset--color--ink); }
/* Active state carries a border AND a weight change, not colour alone —
   WCAG 1.4.1. */
.ssi-work__filter.is-on {
  border-color: var(--wp--preset--color--ssi-red-text);
  color: var(--wp--preset--color--ssi-red-text);
  font-weight: 700;
}
.ssi-work__count {
  flex: 1 1 100%; margin: 0;
  font-size: 0.84rem; color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-work__empty {
  margin: var(--wp--preset--spacing--50) 0 0;
  color: var(--wp--preset--color--ssi-grey-text);
}

.ssi-work-list { list-style: none; margin: 0; padding: 0; }
.ssi-work {
  padding-block: var(--wp--preset--spacing--50);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.ssi-work:first-child { padding-top: 0; }
.ssi-work[hidden] { display: none; }
.ssi-work__title { margin: 0 0 6px; font-size: 1.14rem; }
.ssi-work__meta {
  margin: 0 0 var(--wp--preset--spacing--30);
  font-size: 0.86rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-red-text);
}
.ssi-work__scope {
  margin: 0; max-width: 74ch;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-work__outcome {
  margin: var(--wp--preset--spacing--30) 0 0;
  padding-inline-start: var(--wp--preset--spacing--40);
  border-inline-start: 3px solid var(--wp--preset--color--ssi-red);
  max-width: 74ch;
  font-size: 0.94rem;
}
.ssi-work__link { margin: var(--wp--preset--spacing--30) 0 0; }
.ssi-work__link a { color: var(--wp--preset--color--ssi-red-text); font-weight: 700; }

.ssi-cases { margin-block: var(--wp--preset--spacing--70); }

@media (max-width: 560px) {
  .ssi-work__search { flex: 1 1 100%; }
}
body.ssi-lang-lo :is(.ssi-work__scope, .ssi-work__outcome) {
  line-height: var(--wp--custom--line-height--lao);
}


/* --- Company profile landing ---------------------------------------------
   Where the printed QR lands. Almost every visitor arrives on a phone, having
   just scanned a code at an event or off a business card, so the download is
   large and first and the phone numbers are one tap away. */
.ssi-profile__act {
  margin-block: var(--wp--preset--spacing--60);
  padding: var(--wp--preset--spacing--60);
  background: var(--wp--preset--color--surface-alt);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
}
.ssi-profile__act h2 { margin-top: 0; }
.ssi-profile__dl {
  gap: 10px;
  /* Bigger than a normal CTA. It is the only reason most people are here, and
     a thumb on a phone at a trade fair is not a mouse on a desk. */
  min-height: 56px;
  font-size: 1.02rem;
}
.ssi-profile__dl svg { width: 19px; height: 19px; flex: none; }
.ssi-profile__note {
  margin: var(--wp--preset--spacing--40) 0 0;
  font-size: 0.88rem;
  color: var(--wp--preset--color--ssi-grey-text);
}

.ssi-profile__contact { margin-top: var(--wp--preset--spacing--70); }
.ssi-profile__phones { list-style: none; margin: 0 0 var(--wp--preset--spacing--40); padding: 0; }
.ssi-profile__phones li {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--wp--preset--spacing--40);
  padding-block: var(--wp--preset--spacing--30);
  border-bottom: 1px solid var(--wp--preset--color--border);
}
.ssi-profile__tel {
  font-size: 1.08rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--wp--preset--color--ink); text-decoration: none;
  /* A phone number is a tap target before it is a piece of text. */
  min-height: 44px; display: inline-flex; align-items: center;
}
.ssi-profile__tel:hover { color: var(--wp--preset--color--ssi-red-text); }
.ssi-profile__wa {
  margin-inline-start: auto;
  min-height: 44px; display: inline-flex; align-items: center;
  padding-inline: 12px;
  font-size: 0.88rem; font-weight: 700;
  color: var(--wp--preset--color--ssi-red-text); text-decoration: none;
}
.ssi-profile__explore { margin-top: var(--wp--preset--spacing--50); }
.ssi-profile__explore a { color: var(--wp--preset--color--ssi-red-text); font-weight: 700; }

@media (max-width: 560px) {
  .ssi-profile__act { padding: var(--wp--preset--spacing--50); }
  .ssi-profile__dl { width: 100%; justify-content: center; }
  .ssi-profile__wa { margin-inline-start: 0; padding-inline: 0; }
}


/* --- Legal pages and 404 -------------------------------------------------
   Both added in the 2026-08-10 launch audit: there was no privacy policy at
   all, and the 404 was two English-only lines that offered nobody a way out. */
.ssi-legal__updated {
  margin: -8px 0 var(--wp--preset--spacing--50);
  font-size: 0.86rem;
  color: var(--wp--preset--color--ssi-grey-text);
}
.ssi-legal__s { margin-top: var(--wp--preset--spacing--60); max-width: 68ch; }
.ssi-legal__s h2 { font-size: 1.2rem; margin-bottom: var(--wp--preset--spacing--20); }
.ssi-legal__s p { margin: 0; color: var(--wp--preset--color--ssi-grey-text); }

.ssi-404 { margin-top: var(--wp--preset--spacing--60); }
.ssi-404__links {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--wp--preset--spacing--30);
}
.ssi-404__links li { margin: 0; }
.ssi-404__links a {
  display: flex; align-items: center;
  min-height: 52px; padding: 12px 16px;
  border: 1px solid var(--wp--preset--color--border);
  border-inline-start: 3px solid var(--wp--preset--color--ssi-red);
  border-radius: 0;
  color: var(--wp--preset--color--ink); text-decoration: none; font-weight: 700;
}
.ssi-404__links a:hover {
  border-color: var(--wp--preset--color--ssi-red-text);
  border-inline-start-color: var(--wp--preset--color--ssi-red);
  color: var(--wp--preset--color--ssi-red-text);
}
.ssi-404__search { margin-top: var(--wp--preset--spacing--50); }
.ssi-404__search a { color: var(--wp--preset--color--ssi-red-text); font-weight: 700; }

.ssi-footer__legal { margin: var(--wp--preset--spacing--40) 0 0; font-size: 0.86rem; }
body.ssi-lang-lo .ssi-legal__s p { line-height: var(--wp--custom--line-height--lao); }

/* --- Footer -------------------------------------------------------------- */
.pv-foot,
.ssi-footer {
  background: var(--wp--preset--color--surface-alt);
  color: var(--wp--preset--color--ssi-grey-text);
  border-top: 1px solid var(--wp--preset--color--border);
}
.pv-foot a,
.ssi-footer a { color: var(--wp--preset--color--ssi-grey-text); }
.pv-foot a:hover,
.ssi-footer a:hover { color: var(--wp--preset--color--ssi-red-text); }

/* --- Service CTA band ----------------------------------------------------- */
.ssi-cta-band {
  background: var(--wp--preset--color--surface-alt);
  color: var(--wp--preset--color--ink);
  border: 1px solid var(--wp--preset--color--border);
  border-radius: var(--wp--custom--radius);
}
.ssi-cta-band a:not(.ssi-cta) { color: var(--wp--preset--color--ssi-red-text); }
.ssi-cta--invert {
  background: var(--wp--preset--color--ssi-red-text);
  color: var(--wp--preset--color--surface);
}
.ssi-cta--invert:hover {
  background: var(--wp--preset--color--ssi-red-deep);
  color: var(--wp--preset--color--surface);
}
/* The service CTA band was a solid red field with white text. §3 forbids red
   as a large fill — "never a page background", "under a tenth of any screen".
   Light ground, red button: the same call to action, inside the rule. */
.ssi-cta-band { padding: var(--wp--preset--spacing--50); margin-top: var(--wp--preset--spacing--70); }
.ssi-cta-band h2, .ssi-cta-band h3 { color: var(--wp--preset--color--ink); margin-top: 0; }
.ssi-cta-band p { color: var(--wp--preset--color--ssi-grey-text); }
