/* ============================================================================
   STRATYLIX — COMPONENTS
   One primitive per concept with modifiers. The archived site had 8 unrelated
   card classes and 8 unrelated button classes; this file replaces both.
   ========================================================================== */

/* ---- buttons ------------------------------------------------------------- */
.sx-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;                  /* touch target */
  padding: 0.75rem 1.35rem;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease);
}
.sx-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.sx-btn--primary {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--text-inverse);
}
.sx-btn--primary:hover { background: var(--brand-strong); border-color: var(--brand-strong); color: var(--text-inverse); }

.sx-btn--secondary {
  background: color-mix(in srgb, var(--brand) 10%, transparent);
  border-color: var(--border-brand);
  color: var(--brand-ink);
}
.sx-btn--secondary:hover { background: color-mix(in srgb, var(--brand) 18%, transparent); border-color: var(--brand); color: var(--text); }

.sx-btn--ghost { border-color: var(--border); color: var(--text-muted); }
.sx-btn--ghost:hover { border-color: var(--border-strong); color: var(--text); background: var(--surface-2); }

.sx-btn--lg { padding: 0.95rem 1.7rem; font-size: var(--fs-base); }
.sx-btn--block { width: 100%; }

/* ---- cards --------------------------------------------------------------- */
.sx-card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  background-image: var(--grad-surface);
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.sx-card--interactive:hover { border-color: var(--border-brand); transform: translateY(-2px); }
.sx-card--flat { background: none; background-image: none; }
.sx-card__title { font-size: var(--fs-md); font-weight: 650; color: var(--text); }
.sx-card__text  { font-size: var(--fs-sm); color: var(--text-muted); margin: 0; }
.sx-card__eyebrow {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand);
}

/* ---- badges & pills ------------------------------------------------------ */
.sx-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.22rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  background: var(--surface-2);
}
.sx-badge--brand { border-color: var(--border-brand); color: var(--brand); background: color-mix(in srgb, var(--brand) 8%, transparent); }

.sx-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  background: var(--surface);
}

/* ---- section header ------------------------------------------------------ */
.sx-section-head { max-width: 62ch; margin-bottom: var(--sp-6); }
.sx-section-head--center { margin-inline: auto; text-align: center; }
.sx-eyebrow {
  display: inline-block;
  margin-bottom: var(--sp-3);
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  /* --text-muted, not --text-subtle: at 13px uppercase on the light palette's
     --surface-3 the subtle ramp is 4.1:1, below AA. The pill border and the
     caps keep it reading as a label, so the extra contrast costs no hierarchy. */
  color: var(--text-muted);
}
/* :not(.sx-eyebrow) — an eyebrow is a <p>, so this rule used to win over
   .sx-eyebrow on specificity and repaint it at --fs-md/--text-muted. The result
   was the same component rendering at 19px inside a section head and 13px
   outside one, on the same page. The eyebrow owns its own type now. */
.sx-section-head p:not(.sx-eyebrow) { margin-top: var(--sp-4); color: var(--text-muted); font-size: var(--fs-md); }

/* ---- icon wrapper -------------------------------------------------------- */
.sx-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 8%, transparent);
}
.sx-icon svg { width: 1.15rem; height: 1.15rem; }
.sx-icon--lg { width: 3rem; height: 3rem; }
.sx-icon--lg svg { width: 1.4rem; height: 1.4rem; }

/* ---- stat ---------------------------------------------------------------- */
.sx-stat__value {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--brand);
  font-variant-numeric: tabular-nums;
}
.sx-stat__label { margin-top: var(--sp-2); font-size: var(--fs-xs); color: var(--text-subtle); }

/* ---- CTA panel ----------------------------------------------------------- */
.sx-cta {
  padding: clamp(2rem, 4vw, 3.25rem);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  background: var(--surface);
  background-image: var(--grad-surface);
  text-align: center;
}
.sx-cta h2 { font-size: var(--fs-xl); }
.sx-cta p { margin: var(--sp-4) auto 0; max-width: 56ch; color: var(--text-muted); }
.sx-cta .sx-cluster { margin-top: var(--sp-5); justify-content: center; }

/* ---- disclosure (FAQ / accordion) --------------------------------------- */
.sx-disclosure { border-bottom: 1px solid var(--hairline); }
.sx-disclosure__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  min-height: 56px;
  padding: var(--sp-4) 0;
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--text);
  text-align: start;
}
.sx-disclosure__trigger:hover { color: var(--brand); }
.sx-disclosure__trigger:focus-visible { outline: none; box-shadow: var(--focus-ring-inset); }
.sx-disclosure__icon {
  flex: 0 0 auto;
  width: 1rem; height: 1rem;
  color: var(--text-subtle);
  transition: transform var(--dur) var(--ease);
}
.sx-disclosure__trigger[aria-expanded="true"] .sx-disclosure__icon { transform: rotate(45deg); }
.sx-disclosure__panel { padding-bottom: var(--sp-5); color: var(--text-muted); max-width: 74ch; }
.sx-disclosure__panel[hidden] { display: none; }

/* ---- forms --------------------------------------------------------------- */
.sx-field { display: flex; flex-direction: column; gap: var(--sp-2); }
.sx-field__label { font-size: var(--fs-sm); font-weight: 600; color: var(--text); }
.sx-field__req { color: var(--brand); }
.sx-field__hint { font-size: var(--fs-xs); color: var(--text-subtle); }

.sx-input, .sx-textarea, .sx-select {
  width: 100%;
  min-height: 44px;
  padding: 0.7rem 0.9rem;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-inset);
  color: var(--text);
  font-size: var(--fs-base);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.sx-textarea { min-height: 8rem; line-height: 1.6; }
.sx-input::placeholder, .sx-textarea::placeholder { color: var(--text-subtle); }
.sx-input:focus-visible, .sx-textarea:focus-visible, .sx-select:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 22%, transparent);
}
.sx-input[aria-invalid="true"], .sx-textarea[aria-invalid="true"], .sx-select[aria-invalid="true"] {
  border-color: var(--danger);
}
.sx-field__error { font-size: var(--fs-xs); color: var(--danger); min-height: 1em; }
.sx-field__error:empty { display: none; }

.sx-file {
  width: 100%;
  padding: var(--sp-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface-inset);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.sx-file::file-selector-button {
  margin-inline-end: var(--sp-3);
  padding: 0.45rem 0.85rem;
  border: 1px solid var(--border-brand);
  border-radius: var(--r-xs);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
  color: var(--brand);
  font-weight: 600;
  cursor: pointer;
}

/* Live region for submit status. Visible — never display:none, or screen
   readers may not announce updates. */
.sx-form__status {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}
.sx-form__status:empty { display: none; }
.sx-form__status[data-state="ok"]   { border-color: var(--success); color: var(--success); }
.sx-form__status[data-state="err"]  { border-color: var(--danger);  color: var(--danger); }
.sx-form__status[data-state="busy"] { border-color: var(--border-brand); color: var(--brand); }

/* Honeypot: off-screen, not display:none, never announced, never tabbable. */
.sx-hp {
  position: absolute !important;
  left: -9999px;
  width: 1px; height: 1px;
  overflow: hidden;
}

/* ---- modal foundation ---------------------------------------------------- */
/* Uses <dialog>; JS only calls showModal()/close(). */
.sx-modal {
  width: min(92vw, 640px);
  max-height: 88vh;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--text);
}
.sx-modal::backdrop { background: rgba(3, 6, 12, 0.66); }
.sx-modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-5);
  border-bottom: 1px solid var(--hairline);
}
.sx-modal__body { padding: var(--sp-5); overflow-y: auto; }
.sx-modal__close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 auto;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text);
}
.sx-modal__close:hover { background: var(--surface-2); }

/* ---- footer -------------------------------------------------------------- */
.sx-footer {
  border-top: 1px solid var(--border);
  background: var(--surface);
  /* The chat launcher is position:fixed at the viewport's bottom-right, 52px
     tall on a --sp-5 offset, so it occupies the last ~84px of the viewport at
     every scroll position. With the bottom bar flush to the end of the page it
     landed on top of the social icons — they were behind the button, not just
     near it. Reserve the launcher's band so nothing in the footer sits under
     it when the page is scrolled to the bottom. */
  padding-block: var(--sp-7) calc(var(--sp-5) + 60px);
}
.sx-footer__grid {
  display: grid;
  gap: var(--sp-5) var(--sp-4);
  /* Two columns even on a phone. `auto-fit` with a 1.4fr brand column used to
     let the grid keep six tracks there — the brand column collapsed to ~16px
     and the footer logo rendered 0-3px wide — so tracks are explicit and
     opt-in by width. But one column stacked all four link lists end to end and
     ran the footer to 1,893px, most of it a single file of links. The brand
     block spans both; the link lists pair up. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
.sx-footer__grid > :first-child { grid-column: 1 / -1; }
@media (min-width: 640px) {
  .sx-footer__grid { gap: var(--sp-6); }
}
/* Tablet: the brand block already spans the full width, so the four link lists
   fit one row here rather than stacking two-by-two down the page. */
@media (min-width: 700px) and (max-width: 1023.98px) {
  .sx-footer__grid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-5); }
}
@media (min-width: 1024px) {
  /* Five tracks for five blocks: brand, What We Do, Industries, Company,
     Contact. It was four link tracks for five link blocks, so the last one
     wrapped to a second row and left the rest of that row empty. */
  .sx-footer__grid {
    grid-template-columns: minmax(240px, 1.5fr) repeat(4, minmax(130px, 1fr));
    gap: var(--sp-5) var(--sp-6);
  }
  /* The brand block spans the full first column; the link columns follow. */
  .sx-footer__grid > :first-child { grid-column: 1; }
}
.sx-footer h2 {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-subtle);
  margin-bottom: var(--sp-3);
}
.sx-footer ul { list-style: none; margin: 0; padding: 0; }
.sx-footer li + li { margin-top: var(--sp-2); }
.sx-footer a {
  display: inline-block;
  /* An inline-block is sized to max-content, so a single long unbreakable word
     widened the grid column and pushed the page past the viewport at 375px
     (German "Unternehmensanwendungen"). max-width clamps the box; `anywhere`
     (not `break-word`) is required because only `anywhere` reduces min-content,
     which is what the auto-min of the grid column is sized from. */
  max-width: 100%;
  overflow-wrap: anywhere;
  padding-block: 0.25rem;          /* keeps the tap area sane */
  font-size: var(--fs-sm);
  color: var(--text-muted);
  text-decoration: none;
}
.sx-footer a:hover { color: var(--text); text-decoration: underline; }
/* The brand lockup is an <a>, so the rule above was turning it into an
   inline-block. Its two children are <img>, which base.css resets to
   display:block, so they stacked: mark on one line, wordmark under it. The
   `flex-wrap: nowrap` in header.css could never prevent that, because in this
   context the element was not a flex container at all — `.sx-footer a` is
   (0,1,1) and beats `.sx-logo` (0,1,0).
   Restated at (0,2,1) so the lockup stays one horizontal unit. overflow-wrap
   and the text padding are also undone: there is no text here to wrap, and the
   padding was adding height to an element whose size comes from --logo-h. */
.sx-footer a.sx-logo {
  display: inline-flex;
  overflow-wrap: normal;
  padding-block: var(--sp-2);
}
/* ---- assistant starter prompts -------------------------------------------
   Shown once with the greeting and hidden on first send. Quiet chips: they are
   a shortcut, not a call to action competing with the page CTA.
   ------------------------------------------------------------------------ */
.sx-chat__starters {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  padding: 0 var(--sp-4) var(--sp-3);
}
.sx-chat__starters[hidden] { display: none; }
.sx-chat__starter {
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  font: inherit;
  font-size: .8125rem;
  line-height: 1.2;
  color: var(--text-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.sx-chat__starter:hover { color: var(--text); border-color: var(--border-brand); }
.sx-chat__starter:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* ---- E-Verify participation notice ---------------------------------------
   Federal programme statement. Kept visually quiet: it is a trust signal, not
   a marketing claim, and it must not compete with the CTA. The logo slot only
   renders once the official USCIS artwork is supplied.
   ------------------------------------------------------------------------ */
.sx-everify {
  display: flex;
  gap: var(--sp-4);
  align-items: flex-start;
  margin-block: var(--sp-5);
  padding: var(--sp-4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
}
.sx-everify__h { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); }
.sx-everify p { margin: 0 0 var(--sp-2); font-size: var(--fs-sm); color: var(--text-muted); }
.sx-everify p:last-child { margin-bottom: 0; }
.sx-everify__id { font-size: var(--fs-xs) !important; color: var(--text-muted) !important; }
.sx-everify__logo { flex: 0 0 auto; height: auto; max-width: 112px; }

.sx-everify--compact {
  margin-block: var(--sp-4) 0;
  padding: 0;
  border: 0;
  background: none;
  flex-direction: column;
  gap: var(--sp-2);
}
.sx-everify--compact .sx-everify__id { line-height: 1.5; }

/* Offices and the E-Verify notice are prose, not link lists, and as grid
   children they made the Contact column twice the height of its neighbours.
   They sit in their own band between the columns and the bottom bar, running
   across the footer instead of down one column of it. */
.sx-footer__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-5) var(--sp-6);
  margin-top: var(--sp-6);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hairline);
}
.sx-footer__meta > * { margin: 0; }
.sx-footer__officesblock { min-width: 0; }
.sx-footer__offices {
  list-style: none;
  margin: var(--sp-2) 0 0;
  padding: 0;
  /* Side by side while they fit, stacked when they do not — so a second
     office widens the band rather than lengthening the footer. */
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-6);
}
.sx-footer__offices li + li { margin-top: 0; }
.sx-footer__offices a { display: block; font-size: var(--fs-sm); line-height: 1.5; }
.sx-footer__offices-h { margin-top: 0; }

.sx-footer__desc { margin-top: var(--sp-4); max-width: 40ch; font-size: var(--fs-sm); color: var(--text-muted); }
/* Moved out of the bottom bar, which is now carrying the legal strip. A tagline
   belongs with the brand block anyway, not stranded opposite a copyright line. */
.sx-footer__tagline {
  margin-top: var(--sp-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text);
}
.sx-footer__acronym { margin-top: var(--sp-3); font-size: var(--fs-xs); color: var(--text-subtle); }
.sx-footer__bottom {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-7);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--hairline);
  font-size: var(--fs-xs);
  color: var(--text-subtle);
}
/* Boilerplate, so it reads as a strip rather than as a fifth peer column of
   the site's real navigation. */
.sx-footer__legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  list-style: none;
  margin: 0;
  padding: 0;
}
.sx-footer__legal li + li { margin-top: 0; }
.sx-footer__legal a { font-size: var(--fs-xs); }
.sx-footer__social { display: flex; gap: var(--sp-2); }
.sx-footer__social a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;                    /* touch target */
  border: 1px solid var(--border); border-radius: var(--r-sm);
  color: var(--text-muted);
}
.sx-footer__social a:hover { color: var(--brand); border-color: var(--border-brand); }
.sx-footer__social svg { width: 1.05rem; height: 1.05rem; }

/* ============================================================================
   GLOBAL INTERFACE CHROME
   Carried across from the archived site: the two-layer cursor and the scroll
   progress bar. Both are decorative, pointer-events:none, and both disable
   themselves on touch devices and under prefers-reduced-motion.
   ========================================================================== */

/* ---- scroll progress ------------------------------------------------------
   A thin brand-gradient bar that fills left to right as the page scrolls.

   It sits on the HEADER'S BOTTOM EDGE, not at the top of the viewport: it is
   absolutely positioned inside .sx-header and overlays that element's 1px
   border-bottom, so the header's last line *is* the progress line. Because it
   is a child of the header it follows the resting (76px), scrolled (60px) and
   mobile (64px) heights with no extra rules.
   -------------------------------------------------------------------------- */
.sx-progress {
  position: absolute;
  inset-block-end: -1px;        /* cover the header's own border-bottom */
  inset-inline-start: 0;
  z-index: 1;
  height: 2px;
  width: 0;
  pointer-events: none;
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--accent), var(--brand) 55%, var(--brand-strong));
  box-shadow: 0 0 8px color-mix(in srgb, var(--brand) 70%, transparent);
  transition: width 80ms linear;
}
/* Progress is an indicator, not decoration — keep it, drop the easing. */
@media (prefers-reduced-motion: reduce) {
  .sx-progress { transition: none; }
}

/* ---- progressive disclosure ("Show all") ----------------------------------
   Long card grids are trimmed to their first few items ON SMALL SCREENS ONLY.
   The homepage measured 24,375px on a 390px phone — roughly 36 screens — and
   the card grids account for ~10,300px of that.

   Everything stays in the DOM: the trim is a class applied by main.js, so a
   crawler and a JS-less visitor both get the full list, and the desktop
   composition is untouched. Above the breakpoint the button never appears and
   no item is ever hidden.
   -------------------------------------------------------------------------- */
.sx-more { display: none; margin-block-start: var(--sp-5); }
.sx-more--on { display: block; }
.sx-more__btn { width: 100%; justify-content: center; }

/* Items past the keep-count, while collapsed. */
[data-sx-more].is-trimmed > .sx-more-hidden { display: none; }

/* Desktop: never trim, never offer the button. */
@media (min-width: 900px) {
  .sx-more { display: none !important; }
  [data-sx-more].is-trimmed > .sx-more-hidden { display: revert; }
}

/* ---- AI assistant ---------------------------------------------------------
   A launcher and a panel, both authored in markup so they exist for assistive
   tech before chat.js runs. Until a visitor opens it the only cost is one
   button — no transcript, no request, nothing stored.
   -------------------------------------------------------------------------- */
.sx-chat__launch {
  position: fixed;
  inset-block-end: var(--sp-5);
  inset-inline-end: var(--sp-5);
  z-index: var(--z-toast);
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 52px;
  padding: 0 var(--sp-5);
  border: 1px solid var(--border-brand);
  border-radius: var(--r-pill);
  background: var(--surface-2);
  background-image: var(--grad-surface);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--elev-3);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
              transform var(--dur) var(--ease-out);
}
.sx-chat__launch:hover { background: var(--surface-3); border-color: var(--brand); }
.sx-chat__launch:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.sx-chat__launch svg { width: 18px; height: 18px; color: var(--brand); }

.sx-chat {
  position: fixed;
  inset-block-end: calc(var(--sp-5) + 64px);
  inset-inline-end: var(--sp-5);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  width: min(400px, calc(100vw - var(--sp-6)));
  max-height: min(620px, calc(100svh - 140px));
  /* Brand-tinted edge and radius, like every other panel on the site. */
  border: 1px solid var(--border-brand);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--elev-4);
  overflow: hidden;
}
/* A cyan hairline across the top edge — the same mark the CTA band and the
   capability field use, so the assistant reads as part of the site rather than
   as a widget bolted onto it. */
.sx-chat::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--brand) 22%, var(--brand) 78%, transparent);
  pointer-events: none;
  z-index: 1;
}
.sx-chat[hidden] { display: none; }

.sx-chat__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4);
  border-bottom: 1px solid var(--border);
  background: linear-gradient(160deg, color-mix(in srgb, var(--brand) 9%, transparent), transparent 70%);
}
.sx-chat__mark {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  border: 1px solid var(--border-brand); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--brand) 10%, transparent);
}
.sx-chat__markimg { width: 18px; height: auto; display: block; }
.sx-chat__headtext { min-width: 0; }
.sx-chat__head h2 {
  margin: 0; font-size: var(--fs-sm); font-weight: 650; letter-spacing: 0;
  display: flex; align-items: center; gap: .45em;
}
/* Online dot. Static — a pulsing one would animate forever in the corner of
   every page, which is exactly the kind of thing prefers-reduced-motion exists
   to stop. */
.sx-chat__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent);
  flex: 0 0 auto;
}
.sx-chat__head p {
  margin: 0; font-size: var(--fs-xs); color: var(--text-subtle);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The close control was a 34px low-contrast glyph that read as decoration.
   It is a bordered, brand-edged 40px target now — the size a thumb expects and
   the contrast a control needs, so nobody has to hunt for the way out. */
.sx-chat__close {
  margin-inline-start: auto;
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; padding: 0;
  border: 1px solid var(--border-brand); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--brand) 7%, transparent);
  color: var(--text); cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.sx-chat__close svg { width: 16px; height: 16px; }
.sx-chat__close:hover { background: color-mix(in srgb, var(--brand) 16%, transparent); border-color: var(--brand); }
.sx-chat__close:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.sx-chat__log {
  flex: 1 1 auto;
  overflow-y: auto;
  list-style: none;
  margin: 0;
  padding: var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.sx-chat__msg { display: flex; }
.sx-chat__msg p {
  margin: 0;
  max-width: 85%;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  font-size: var(--fs-sm);
  line-height: 1.55;
  white-space: pre-wrap;          /* the endpoint replies in plain text */
  overflow-wrap: anywhere;
}
.sx-chat__msg--bot p {
  color: var(--text);
  border: 1px solid var(--border-brand);
  border-top-left-radius: var(--r-xs);
  background:
    linear-gradient(160deg, color-mix(in srgb, var(--brand) 8%, transparent), transparent 60%),
    var(--surface-2);
}
.sx-chat__msg--user { justify-content: flex-end; }
.sx-chat__msg--user p {
  background: color-mix(in srgb, var(--brand) 16%, var(--surface-2));
  color: var(--text);
  border-top-right-radius: var(--r-xs);
}
.sx-chat__msg.is-pending p { color: var(--text-subtle); letter-spacing: .2em; }

.sx-chat form {
  display: flex; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-top: 1px solid var(--hairline);
}
.sx-chat textarea {
  flex: 1 1 auto;
  /* 44px fitted one line of text and clipped the placeholder mid-word — the
     first thing a visitor read was a broken sentence. Two lines of the actual
     line-height, so the placeholder lands whole. */
  min-height: calc(2.9em + var(--sp-4));
  max-height: 132px;
  padding: var(--sp-3);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-inset); color: var(--text);
  font: inherit; font-size: var(--fs-sm); line-height: 1.45;
  resize: vertical;
}
.sx-chat textarea::placeholder { color: var(--text-subtle); }
/* On a phone the panel is narrower and the send button takes a fixed slice, so
   the same placeholder needs a third line. Two lines there clipped it mid-word
   — the first thing a visitor read was a broken sentence. */
@media (max-width: 640px) {
  .sx-chat textarea { min-height: calc(4.35em + var(--sp-4)); }
}
.sx-chat textarea:focus-visible { outline: none; border-color: var(--brand); box-shadow: var(--focus-ring-inset); }
.sx-chat form button {
  flex: 0 0 auto;
  min-height: 44px; padding-inline: var(--sp-4);
  border: 1px solid var(--brand); border-radius: var(--r-sm);
  background: var(--brand); color: var(--text-inverse);
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
}
.sx-chat form button:disabled { opacity: .55; cursor: not-allowed; }
/* Reset control for the returning-visitor greeting. Quiet on purpose: it is an
   escape hatch, not a feature to advertise. */
.sx-chat__msg--meta { display: block; margin-top: calc(var(--sp-2) * -1); }
.sx-chat__forget {
  font: inherit;
  font-size: var(--fs-xs);
  color: var(--text-subtle);
  background: none;
  border: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}
.sx-chat__forget:hover { color: var(--text); }
.sx-chat__forget:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--r-xs); }

.sx-chat__note {
  margin: 0; padding: 0 var(--sp-4) var(--sp-3);
  font-size: var(--fs-xs); color: var(--text-subtle);
}

/* Phone: the panel takes the width and sits above the launcher. */
@media (max-width: 520px) {
  .sx-chat { inset-inline: var(--sp-3); width: auto; }
  .sx-chat__launch { inset-inline-end: var(--sp-3); inset-block-end: var(--sp-3); }
}

/* ---- updates signup -------------------------------------------------------
   Lives in the footer and rides the same data-sx-form pipeline as the contact
   and careers forms, so validation, the busy state and the status region are
   already handled.
   -------------------------------------------------------------------------- */
.sx-signup { margin-top: var(--sp-6); }
.sx-signup h2 {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
}
.sx-signup__note {
  margin: 0 0 var(--sp-4);
  max-width: 46ch;
  font-size: var(--fs-xs);
  color: var(--text-subtle);
}
.sx-signup__row { display: flex; flex-wrap: wrap; gap: var(--sp-2); max-width: 420px; }
.sx-signup__input {
  flex: 1 1 200px;
  min-height: 44px;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface-inset);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
}
.sx-signup__input::placeholder { color: var(--text-subtle); }
.sx-signup__input:focus-visible {
  outline: none;
  border-color: var(--brand);
  box-shadow: var(--focus-ring-inset);
}
.sx-signup__row .sx-btn { flex: 0 0 auto; min-height: 44px; }
.sx-signup .sx-form__status { margin-top: var(--sp-3); }

/* ---- magnetic cursor ------------------------------------------------------
   Two layers over the native pointer, which is deliberately left visible: the
   arrow and the hand pointer keep working exactly as the OS draws them, and
   these sit on top.

   The field's box IS animated (width/height/border-radius) because it has to
   morph to arbitrary target shapes, but it is fixed-position, pointer-events
   none and out of flow, so it has nothing to reflow against.
   -------------------------------------------------------------------------- */
.sx-cur-dot,
.sx-cur-field {
  position: fixed;
  inset-block-start: 0;
  inset-inline-start: 0;
  pointer-events: none;
  opacity: 0;
  will-change: transform;
  transition: opacity 220ms var(--ease);
}
.has-cursor .sx-cur-dot { opacity: 1; }
.has-cursor .sx-cur-field { opacity: .55; }

.sx-cur-dot {
  z-index: calc(var(--z-toast) + 3);
  width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--brand);
  box-shadow: 0 0 10px color-mix(in srgb, var(--brand) 65%, transparent);
  transition: opacity 220ms var(--ease), transform 140ms var(--ease-out),
              width 200ms var(--ease), height 200ms var(--ease),
              margin 200ms var(--ease);
}

.sx-cur-field {
  z-index: calc(var(--z-toast) + 2);
  width: 34px; height: 34px;
  border: 1px solid color-mix(in srgb, var(--brand) 70%, transparent);
  border-radius: 999px;
  background: color-mix(in srgb, var(--brand) 6%, transparent);
  transition: opacity 220ms var(--ease), background 260ms var(--ease),
              border-color 260ms var(--ease);
}

/* Snapped onto a link or button: brighten, and shrink the dot so the ring
   reads as the primary indicator rather than competing with it. */
.has-cursor.is-locked .sx-cur-field {
  opacity: .95;
  border-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 12%, transparent);
}
.has-cursor.is-locked .sx-cur-dot {
  width: 4px; height: 4px; margin: -2px 0 0 -2px;
}

/* Press: the ring tightens. transform only, so it composites. */
.has-cursor.is-pressing .sx-cur-field { opacity: 1; }
.has-cursor.is-pressing .sx-cur-dot   { transform-origin: 50% 50%; }

/* Never on touch, never with reduced motion. */
@media (hover: none), (pointer: coarse) {
  .sx-cur-dot, .sx-cur-field { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .sx-cur-dot, .sx-cur-field { display: none !important; }
}

/* ---- inline arrow --------------------------------------------------------- */
/* Was a literal "→" (U+2192). Neither shipped font carries it: inter-latin and
   manrope-latin are latin subsets of 230 and 218 glyphs, and while they do
   contain the em dash, en dash, middot, ellipsis, curly apostrophe and ©
   this site uses, U+2192 is in the Arrows block and is absent from both. Every
   arrow therefore fell back to a system font — silent on macOS, but on Android,
   where the fallback stack is thin, it rendered as tofu after the link text.
   Drawn as a path instead, so it cannot depend on font coverage at all and it
   inherits colour like the nav caret already does. */
.sx-arrow {
  /* base.css resets `svg { display: block }` for the icon/media case, which
     would drop this onto its own line mid-sentence — it sits inside a run of
     text, so it has to be inline. */
  display: inline-block;
  width: .875em;
  height: .875em;
  flex: 0 0 auto;
  vertical-align: -.11em;   /* optical baseline match against cap-height text */
}


/* ============================================================================
   MEDIA SLOTS
   Reserved space for imagery that has not been chosen yet.

   aspect-ratio does the reserving, so the box occupies its final height from
   the first paint — before any file exists, and later before any file loads.
   That is the whole point: a slot that sizes itself from the image would shift
   the page on every load and put CLS straight into the red.

   The placeholder is bound to :empty. Drop a <picture> or <img> in and the
   selector stops matching, so the treatment removes itself — there is no flag
   to unset and no way to ship a placeholder behind a real image by accident.
   Nothing here renders text, so nothing can leak into production output.
   ========================================================================== */
.sx-media {
  position: relative;
  aspect-ratio: var(--media-ratio, 4 / 3);
  overflow: hidden;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.sx-media--landscape { --media-ratio: 4 / 3; }
.sx-media--wide      { --media-ratio: 16 / 10; }
.sx-media--editorial { --media-ratio: 3 / 2; }
.sx-media--portrait  { --media-ratio: 4 / 5; }
.sx-media--square    { --media-ratio: 1 / 1; }
/* Full-bleed supporting band. A container-width 16:10 is 775px tall at 1440 —
   a supporting visual should not be taller than most of the sections it
   supports. Cinematic ratio keeps it a band. */
.sx-media--band      { --media-ratio: 21 / 9; }
@media (max-width: 899.98px) { .sx-media--band { --media-ratio: 16 / 9; } }

.sx-media--16x9     { --media-ratio: 16 / 9; }

/* A plate is shown WHOLE. Where --band is a crop of a larger picture and can
   take any ratio the layout wants, a plate carries composited artwork — both
   of ours have the lockup inside the frame — so it holds the source's own
   ratio at every width and is never cropped by object-fit. The ratio comes
   from the kind class beside it; this modifier is the behaviour, which is why
   it can sit on a 16:9 plate and a 3:2 one alike.

   It also opts out of the phone height cap below: capping the height of a box
   whose ratio is the point would crop exactly what the ratio exists to
   protect. */

/* Whatever eventually lands inside fills the reserved box without distorting.
   Applies to <picture>, <img> and <video> so the slot is format-agnostic. */
.sx-media > picture,
.sx-media > img,
.sx-media > video,
/* The <img> inside a <picture> is the replaced element — object-fit on the
   <picture> itself does nothing, so without this the image overflowed the
   reserved box instead of filling it. The slot anticipated <picture> but only
   ever styled the wrapper. */
.sx-media > picture > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- photography treatment -----------------------------------------------
   The site is a near-black enterprise palette with one cyan; photography
   arrives lit for print. Left raw it reads as a stock plate dropped into the
   page. Desaturated and darkened it becomes texture the section can carry, and
   the scrim grounds it into the surface instead of ending on a hard edge.

   Tokens, not fixed colours: the scrim mixes from --bg and the wash from
   --brand, so the same treatment lightens on the light surface rather than
   muddying it.
   -------------------------------------------------------------------------- */
/* Same highlight as the featured capability card (.sx-solcard--feature): the
   brand-tinted border and the 160deg cyan wash off the top-left corner, at the
   same 9%. A filled media block is a peer of that card, so it should carry the
   same accent rather than a weaker one of its own. */
.sx-media--photo { border-color: var(--border-brand); }
.sx-media--photo > picture > img {
  filter: saturate(0.5) contrast(1.04) brightness(0.72);
}

/* A plate is artwork, not raw stock, and this one carries the lockup. The
   treatment above exists to stop a print-lit photograph reading as a plate
   dropped into the page — applied to something that IS a considered plate it
   greys the wordmark and fights the artwork's own cyan. Shown as supplied;
   only the extreme edges are eased so it does not butt against the section on
   a hard line. The fades stop well short of the centre, where the lockup is. */
.sx-media--photo.sx-media--plate > picture > img { filter: none; }
.sx-media--photo.sx-media--plate::after {
  background:
    /* The feature card's wash, laid over the plate rather than under it — the
       image covers the box, so a background gradient would never be seen. */
    linear-gradient(160deg,
      color-mix(in srgb, var(--brand) 9%, transparent), transparent 60%),
    linear-gradient(to top,
      color-mix(in srgb, var(--bg) 62%, transparent), transparent 22%),
    linear-gradient(to bottom,
      color-mix(in srgb, var(--bg) 42%, transparent), transparent 16%);
}
.sx-media--photo::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    /* Both edges, not just the foot. Scrimmed at the bottom only, the band
       still met the section with a hard bright line along its top. */
    linear-gradient(to top,
      color-mix(in srgb, var(--bg) 88%, transparent), transparent 45%),
    linear-gradient(to bottom,
      color-mix(in srgb, var(--bg) 60%, transparent), transparent 32%),
    linear-gradient(115deg,
      color-mix(in srgb, var(--brand) 20%, transparent), transparent 58%);
}
/* The values above are tuned for the near-black palette: dim the plate hard,
   then scrim both edges into it. Applied unchanged on a light ground they do
   the opposite of the job — a light scrim over an already-dimmed photo bleaches
   it to a grey wash. On light the photo barely needs dimming at all, and one
   soft foot scrim is enough to seat it. */
[data-surface="light"] .sx-media--photo > picture > img,
.sx-section--light .sx-media--photo > picture > img {
  filter: saturate(0.74) contrast(1.02) brightness(0.97);
}
[data-surface="light"] .sx-media--photo::after,
.sx-section--light .sx-media--photo::after {
  background:
    linear-gradient(to top,
      color-mix(in srgb, var(--bg) 58%, transparent), transparent 38%),
    linear-gradient(115deg,
      color-mix(in srgb, var(--brand) 12%, transparent), transparent 58%);
}

/* Reduced-motion users are not the audience here, but a viewer who has asked
   for less visual noise gets the quieter end of the treatment. */
@media (prefers-contrast: more) {
  .sx-media--photo > picture > img { filter: none; }
  .sx-media--photo::after { background: none; }
}

/* Empty state: a brand surface, a faint technical grid and one soft cyan wash.
   Deliberately quiet — it should read as considered space, not as a missing
   asset. No icon, no label, no border dashes. */
.sx-media:empty {
  background:
    radial-gradient(90% 70% at 22% 18%,
      color-mix(in srgb, var(--brand) 9%, transparent), transparent 62%),
    var(--surface-inset);
  border-color: color-mix(in srgb, var(--brand) 18%, var(--border));
}
.sx-media:empty::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, color-mix(in srgb, var(--brand) 7%, transparent) 1px, transparent 1px),
    linear-gradient(to bottom, color-mix(in srgb, var(--brand) 7%, transparent) 1px, transparent 1px);
  background-size: 44px 44px;
  /* Fades the grid out toward the lower right so it reads as texture rather
     than as graph paper. */
  -webkit-mask-image: radial-gradient(120% 100% at 15% 10%, #000 15%, transparent 78%);
          mask-image: radial-gradient(120% 100% at 15% 10%, #000 15%, transparent 78%);
}

/* ---- media + copy rows ---------------------------------------------------
   Stacked by default. The split and the side the media takes are per-section,
   so the page alternates instead of running text-left/image-right eight times.
   -------------------------------------------------------------------------- */
.sx-mediarow {
  display: grid;
  gap: var(--sp-6);
  align-items: center;
  grid-template-columns: minmax(0, 1fr);
  /* Clearance below the row. It had none, so whatever followed sat flush
     against the media box — the Supply Chain plate met the lifecycle card on a
     shared edge, and the AI row does the same to its card grid. Matched to
     .sx-solmedia's own separation so every media block on the page keeps the
     same distance from the block after it. */
  margin-block-end: var(--sp-6);
}
@media (min-width: 900px) {
  .sx-mediarow { gap: var(--sp-8); grid-template-columns: var(--row-split, 1fr 1fr); }
  .sx-mediarow--55 { --row-split: 55fr 45fr; }
  .sx-mediarow--60 { --row-split: 60fr 40fr; }
  /* Media first in the source order would put a decorative box ahead of the
     heading for a screen reader. It stays second in the DOM and is moved by
     the grid instead. */
  .sx-mediarow--mediafirst > .sx-media { order: -1; }
}

/* The plate and the card beside it should read as one pair, and at 1fr 1fr
   they did not: a 16:9 image in half the row came out 40px shorter than the
   card, so the card overhung it. 57/43 makes the image 14% larger and, more
   usefully, makes it the TALLER of the two — so the card stretches to meet it
   rather than the image being chased upward.

   57 is the ceiling, not a preference. At 58 the card falls under ~396px,
   .sx-layers loses its second column, and the card jumps from 348px to 508px —
   the opposite of matched. Held to the 1280 breakpoint because below it the
   card is naturally far taller than any uncropped 16:9 can reach. */
@media (min-width: 1280px) {
  .sx-mediarow--mediafirst { --row-split: 57fr 43fr; align-items: stretch; }
  /* Only the card stretches. The plate keeps its own ratio — stretching it
     would either distort the artwork or crop into the pipeline labels. */
  .sx-mediarow--mediafirst > .sx-media { align-self: start; }
}

/* Mobile: a 4:3 box on a 390px screen is ~290px tall, and four of them add a
   screen and a half to the page. Capped, and the crop absorbs it. */
@media (max-width: 899.98px) {
  .sx-media { max-height: 240px; }
  .sx-media--portrait { max-height: 320px; }
  /* See .sx-media--plate: the cap would crop the artwork it exists to show. At
     412px the full frame is 209px tall, so it costs nothing here anyway. */
  .sx-media--plate { max-height: none; }
}
