/* TITECH v3 · expertise.css: owned by WO-20 (the Featured Expertise hub page, work-orders/WO-20-expertise-page.md). */
/* One page that sells the four featured services together: hero, a jump nav, four deep dives (RUCKUS and Network
   Security dark, as on the home tiles), a four-layer stack, a proof strip and the home CTA band with the form. Every class
   is .xp-* on top of the existing page-hero, section-title, section-intro, .fp-chips, .fp-num, .featured-tile__label,
   button and text-link markup, so WO-05's rhythm, WO-02's cards and buttons, and the dark --ctx-* values apply as they
   are. Text, surfaces and lines come only from --ctx-*. Also here: the link under the featured tiles (07 blocks). */

:root {
  --xp-arrow-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14' fill='none' stroke='%23000' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 1.75v10.5M3 8.25l4 4 4-4'/%3E%3C/svg%3E");
  --xp-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --xp-arabic: "IBM Plex Sans Arabic", "IBM Plex Sans", Tahoma, Arial, sans-serif;
}

/* ---------------------------------------------------------------- 0. Under the featured tiles (home and /solutions/) */
.featured-more {
  margin: clamp(32px, 4vw, 48px) 0 0;
  text-align: center;
}

/* ---------------------------------------------------------------- 1. Hero */
.xp-hero .actions { column-gap: 28px; }
.xp-hero .hero-lead { max-width: 52ch; }

/* The line art: four linked nodes on an orbit (access network, network security, XDR, cybersecurity) around a small
   core, in --c-signal-soft. .fp-hero-visual (pages.css) gives the size, the 1.25px strokes, the signal glow, the RTL
   mirror and the reduced-motion guard for its one motion, the .fp-flow dashes along the orbit. The nodes are filled, so
   the orbit passes behind them. */
.xp-hero-art .xp-a-orbit { stroke: var(--c-signal-soft); stroke-opacity: .28; }
.xp-hero-art .xp-a-orbit2 { stroke: var(--c-signal-soft); stroke-opacity: .8; stroke-dasharray: 3 9; }
.xp-hero-art .xp-a-halo { stroke: var(--c-signal-soft); stroke-opacity: .22; stroke-dasharray: 2 6; }
.xp-hero-art .xp-a-spoke { stroke: var(--c-signal-soft); stroke-opacity: .5; stroke-dasharray: 2 5; }
.xp-hero-art .xp-a-node { fill: var(--c-banner); fill-opacity: .9; stroke: var(--c-signal-soft); stroke-opacity: .9; } /* WO-19 integration: the banner's navy, so a node reads hollow on the page hero (as .c-node, WO-19c), not as an ink disc */
.xp-hero-art .xp-a-line { stroke: var(--c-signal-soft); stroke-opacity: .95; }
.xp-hero-art .xp-a-faint { stroke-opacity: .5; }
.xp-hero-art .xp-a-core-ring { stroke: var(--c-signal-soft); stroke-opacity: .6; }
.xp-hero-art .xp-a-dot { fill: var(--c-signal-soft); stroke: none; }
.xp-hero-art .xp-a-blip { fill: var(--c-spec-1); stroke: none; }

/* ---------------------------------------------------------------- Shared card behaviour (jump cards, strengths) */
/* WO-02's card: a bordered surface, a spotlight behind the content (--mx and --my come from motion.js through
   data-spotlight; without it the light falls at the top centre), and on hover a 2px lift, the soft glow and a lit border. */
.xp-jump-card, .xp-strength {
  position: relative;
  isolation: isolate;
  box-sizing: border-box;
  border: 1px solid var(--ctx-line);
  border-radius: var(--r-lg);
  background: var(--ctx-surface);
  transition: translate var(--dur-base) var(--ease-out), border-color var(--dur-hover) var(--ease-hover),
    box-shadow var(--dur-hover) var(--ease-hover), background-color var(--dur-hover) var(--ease-hover);
}
.xp-jump-card::after, .xp-strength::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 0%), rgb(76 141 255 / .10), transparent 60%);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-hover) var(--ease-hover);
}
@media (hover: hover) {
  .xp-jump-card:hover, .xp-strength:hover {
    border-color: rgb(76 141 255 / .45);
    box-shadow: var(--shadow-card), var(--glow-soft);
    translate: 0 -2px;
  }
  .xp-jump-card:hover::after, .xp-strength:hover::after { opacity: 1; }
}
.xp-jump-card:focus-visible {
  border-color: rgb(76 141 255 / .45);
  box-shadow: var(--shadow-card), var(--glow-soft);
}
.xp-jump-card:focus-visible::after { opacity: 1; }

/* ---------------------------------------------------------------- 2. Jump nav: four compact anchor cards */
.xp-jump { padding-block: var(--space-block); }
.xp-jump-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.xp-jump-item { min-width: 0; margin: 0; }
.xp-jump-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "num arrow" "name name" "line line";
  align-content: start;
  gap: 0 16px;
  height: 100%;
  padding: var(--cmp-card-pad);
  color: var(--ctx-heading);
  text-decoration: none;
}
.xp-jump-num {
  grid-area: num;
  color: var(--ctx-muted);
  font-family: var(--xp-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: .14em;
}
/* A 14px down arrow: these links go further down this page. It follows the link colour and drops 3px on hover. */
.xp-jump-arrow {
  grid-area: arrow;
  width: 14px;
  height: 14px;
  background-color: var(--ctx-link);
  -webkit-mask: var(--xp-arrow-down) center / contain no-repeat;
  mask: var(--xp-arrow-down) center / contain no-repeat;
  transition: translate var(--dur-fast) var(--ease-out);
}
.xp-jump-card:is(:hover, :focus-visible) .xp-jump-arrow { translate: 0 3px; }
.xp-jump-name {
  grid-area: name;
  margin-block-start: 20px;
  color: var(--ctx-heading);
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -.01em;
}
.xp-jump-line {
  grid-area: line;
  margin-block-start: 8px;
  color: var(--ctx-body);
  font-size: .9375rem;
  line-height: 1.55;
}

/* ---------------------------------------------------------------- 3. Deep dives */
/* Head: the label and the logo row across the top, then the title with the two-sentence summary beside it. The logo row
   takes the full width, so a partner row stays on one line wherever it can. */
.xp-dive-head {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  grid-template-areas: "label label" "logos logos" "title summary";
  align-items: start;
  gap: 20px clamp(32px, 5vw, 80px);
  margin-block-end: var(--space-block);
}
.xp-dive-head > * { min-width: 0; margin: 0; }
.xp-dive-head > .xp-label { grid-area: label; justify-self: start; }
.xp-dive-head > .xp-logos { grid-area: logos; }
.xp-dive-head > .section-title { grid-area: title; max-width: 16em; margin-block-start: 4px; text-wrap: balance; }
/* The summary starts level with the title's first line, which sits a little below its box top (as in .fp-why). */
.xp-dive-head > .xp-dive-summary { grid-area: summary; max-width: 56ch; padding-block-start: .5em; }

/* Logo rows: the tile's own shortcode, mono. One partner row at 22px, a single brand logo at 26px (DESIGN-SPEC §10.1).
   The height is reserved, so nothing moves when the masks load. Where a row can't fit on one line it becomes an even
   grid of three (3 + 3, 3 + 2) at 20px, and of two on the narrowest phones, instead of leaving one logo on its own.
   660px is the one-line width of the longest row (Cybersecurity, six logos). */
.xp-logos { min-height: 22px; container-type: inline-size; }
.xp-logos .logo-inline { --logo-h: 22px; --logo-color: var(--ctx-body); gap: 14px 28px; }
.xp-logos--brand { min-height: 26px; }
.xp-logos--brand .logo-inline { --logo-h: 26px; --logo-color: var(--ctx-heading); }
@container (max-width: 659px) {
  .xp-logos:not(.xp-logos--brand) .logo-inline {
    --logo-h: 20px;
    display: grid;
    grid-template-columns: repeat(3, max-content);
    gap: 16px 20px;
  }
}
@container (max-width: 339px) {
  .xp-logos:not(.xp-logos--brand) .logo-inline { grid-template-columns: repeat(2, max-content); }
}

/* Strengths: four numbered cards. The numeral is the spectrum as text (.fp-num, pages.css). */
.xp-strengths {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.xp-strength {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin: 0;
  padding: var(--cmp-card-pad);
}
/* The accent line on the top edge: 2px, 48px wide, clear of the rounded corner, in the spectrum (as .fp-fit). */
.xp-strength::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline-start: var(--r-lg);
  width: 48px;
  height: 2px;
  background: var(--g-spectrum);
}
.xp-strength h3 {
  margin: 20px 0 10px;
  color: var(--ctx-heading);
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -.01em;
}
.xp-strength p { margin: 0; color: var(--ctx-body); font-size: .9375rem; line-height: 1.6; }
/* The numeral keeps its own width, so the spectrum runs across its two digits rather than across the whole card. */
.xp-strength .fp-num { align-self: flex-start; }

/* Foot: the technology chips on the inline-start, the enquiry and the detail-page link on the inline-end. */
.xp-dive-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 28px 48px;
  margin-block-start: clamp(32px, 4vw, 48px);
  padding-block-start: clamp(24px, 3vw, 32px);
  border-block-start: 1px solid var(--ctx-line);
}
.xp-chips { flex: 1 1 420px; min-width: 0; }
.xp-dive-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
}

/* ---------------------------------------------------------------- 4. How it fits together: a four-layer stack */
/* An ordered list, first item first: Connect is layer 01. It is drawn as a stack with its base at the bottom, so the
   list runs bottom to top (column-reverse). Nothing in it can be focused, so the visual order never fights the keyboard.
   Each layer's slab (::before) is a step narrower than the one below it, while the text of all four keeps one set of
   columns inside the narrowest slab, so the stack reads as a pyramid and the words still line up. The inline-start bar
   climbs the spectrum from teal at the base to rose at the top. */
.xp-connect .section-head { max-width: 62rem; }
.xp-stack {
  display: flex;
  flex-direction: column-reverse;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.xp-layer {
  --xp-accent: var(--c-spec-4);
  --xp-step: clamp(0px, 3.5vw, 56px);
  --xp-pad: clamp(20px, 2.6vw, 32px);
  position: relative;
  isolation: isolate;
  display: grid;
  grid-template-columns: minmax(150px, 13rem) minmax(0, 15rem) minmax(0, 1fr) auto;
  grid-template-areas: "role name line icon";
  align-items: center;
  gap: 8px clamp(20px, 3vw, 40px);
  margin: 0;
  padding: 22px calc(var(--xp-step) * 3 + var(--xp-pad));
}
.xp-layer::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline: calc(var(--i, 0) * var(--xp-step));
  z-index: -1;
  border: 1px solid var(--ctx-line);
  border-inline-start: 3px solid var(--xp-accent);
  border-radius: var(--r-lg);
  background: var(--ctx-surface);
  box-shadow: var(--shadow-card);
  pointer-events: none;
}
.xp-layer:nth-child(2) { --xp-accent: var(--c-spec-3); }
.xp-layer:nth-child(3) { --xp-accent: var(--c-spec-2); }
.xp-layer:nth-child(4) { --xp-accent: var(--c-spec-1); }
.xp-layer > * { margin: 0; }
.xp-layer-role {
  grid-area: role;
  display: flex;
  align-items: baseline;
  gap: 12px;
  color: var(--ctx-heading);
  font-family: var(--xp-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: .14em;
  text-transform: uppercase;
}
/* The layer number sits in front of its role. It is hidden from screen readers, which already hear the list's order. */
.xp-layer-no { color: var(--ctx-muted); }
.xp-layer-body { display: contents; }
.xp-layer-name {
  grid-area: name;
  margin: 0;
  color: var(--ctx-heading);
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -.01em;
}
.xp-layer-line {
  grid-area: line;
  margin: 0;
  color: var(--ctx-body);
  font-size: .9375rem;
  line-height: 1.6;
}
.xp-layer .xp-layer-icon { grid-area: icon; }

/* The stack builds from its base on reveal: motion.js sets .m-pending on the list (data-reveal) while it is below the
   fold, then .m-in, and removes that again a moment later. So the finished stack is the default state and only the
   waiting state is hidden; the transitions sit on the default state, so removing .m-in leaves them running. With no JS,
   after a JS error and under reduced motion the class never appears and the stack is simply there. */
.xp-layer {
  transition: opacity var(--dur-reveal) var(--ease-out) calc(var(--i, 0) * var(--stagger) * 2 + 120ms),
    translate var(--dur-reveal) var(--ease-out) calc(var(--i, 0) * var(--stagger) * 2 + 120ms);
}
@media screen and (prefers-reduced-motion: no-preference) {
  .xp-stack.m-pending .xp-layer { opacity: 0; translate: 0 var(--rise); transition: none; }
}

/* ---------------------------------------------------------------- 5. Proof strip (dark): the four approved numbers */
.xp-proof { padding-block: calc(var(--space-block) * 1.5); }
.xp-proof .xp-proof-kicker { margin: 0 0 clamp(28px, 3vw, 40px); }
.xp-proof-list {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
  border-block-start: 1px solid var(--ctx-line-strong);
}
.xp-stat {
  min-width: 0;
  margin: 0;
  padding: 28px clamp(20px, 2.5vw, 36px) 0;
  border-inline-start: 1px solid var(--ctx-line);
}
.xp-stat:first-child { padding-inline-start: 0; border-inline-start: 0; }
.xp-stat > * { margin: 0; }
.xp-stat-num {
  display: flex;
  flex-direction: column;
  margin-block-end: 16px;
  color: var(--ctx-heading);
  font-size: clamp(2.5rem, 1.7rem + 2.2vw, 3.75rem);
  font-weight: 300;
  line-height: 1;
  letter-spacing: -.03em;
  font-variant-numeric: tabular-nums;
}
/* "Since" rides above 2012; the other three keep the same empty line, so the four numbers share one baseline. */
.xp-stat-num span {
  min-height: 1.5em;
  color: var(--ctx-muted);
  font-size: .9375rem;
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0;
}
.xp-stat-label {
  margin-block-end: 10px;
  color: var(--ctx-heading);
  font-family: var(--xp-mono);
  font-size: var(--fs-label);
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.xp-stat-text { max-width: 32ch; color: var(--ctx-body); font-size: .9375rem; line-height: 1.55; }

/* ---------------------------------------------------------------- Responsive */
@media (max-width: 1100px) {
  .xp-strengths { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .xp-layer {
    grid-template-columns: minmax(150px, 12rem) minmax(0, 1fr) auto;
    grid-template-areas: "role name icon" "role line icon";
  }
}
@media (max-width: 1000px) {
  .xp-jump-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .xp-layer { --xp-step: 2vw; } /* a shallower pyramid, so the layer text keeps its room on tablets */
  .xp-dive-head { grid-template-columns: minmax(0, 1fr); grid-template-areas: "label" "logos" "title" "summary"; }
  .xp-dive-head > .xp-dive-summary { padding-block-start: 0; }
  .xp-proof-list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .xp-stat { padding-block-end: 28px; }
  .xp-stat:nth-child(odd) { padding-inline-start: 0; border-inline-start: 0; }
  .xp-stat:nth-child(n+3) { border-block-start: 1px solid var(--ctx-line); }
}
@media (max-width: 700px) {
  .xp-layer {
    --xp-step: 0px;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "role icon" "name icon" "line line";
    gap: 6px 16px;
  }
  .xp-layer-line { margin-block-start: 6px; }
  .xp-dive-foot { align-items: flex-start; flex-direction: column; }
  .xp-chips { flex: none; }
}
@media (max-width: 600px) {
  /* Balanced lines left one word alone on a phone ("Respond"); pretty wrapping only avoids a lone last word. */
  .xp-dive-head > .section-title { text-wrap: pretty; }
  .xp-jump-list, .xp-strengths { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .xp-jump-card {
    grid-template-columns: 2.25rem minmax(0, 1fr) auto;
    grid-template-areas: "num name arrow" "num line arrow";
    align-items: baseline;
    padding-block: 18px;
  }
  .xp-jump-name { margin-block-start: 0; font-size: 1.125rem; }
  .xp-jump-line { margin-block-start: 4px; }
  .xp-jump-arrow { align-self: center; }
  .xp-proof-list { grid-template-columns: minmax(0, 1fr); }
  .xp-stat, .xp-stat:nth-child(odd) { padding: 22px 0; border-inline-start: 0; }
  .xp-stat + .xp-stat { border-block-start: 1px solid var(--ctx-line); }
  /* One row per number: the figure on the inline-start, its label and line beside it. */
  .xp-stat {
    display: grid;
    grid-template-columns: 6.5rem minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    column-gap: 16px;
  }
  .xp-stat-num { grid-row: 1 / span 2; margin: 0; }
  .xp-stat-num span { min-height: 0; }
  .xp-stat-label { padding-block-start: 6px; }
}

/* ---------------------------------------------------------------- Arabic */
/* Plex Mono has no Arabic glyphs and tracking breaks joining (§3): labels take the Arabic label rules. Western digits
   stay, and the numbers keep their own direction. */
:is([dir="rtl"], html[lang^="ar"]) :is(.xp-jump-num, .xp-layer-role, .xp-stat-label) {
  font-family: var(--xp-arabic);
  font-size: .8125rem;
  font-weight: 500;
  line-height: 1.6;
  letter-spacing: 0;
  text-transform: none;
}
:is([dir="rtl"], html[lang^="ar"]) :is(.xp-jump-name, .xp-strength h3, .xp-layer-name) { line-height: 1.35; letter-spacing: 0; }
:is([dir="rtl"], html[lang^="ar"]) :is(.xp-jump-line, .xp-strength p, .xp-layer-line, .xp-stat-text) { line-height: 1.8; }
:is([dir="rtl"], html[lang^="ar"]) .xp-stat-num { letter-spacing: 0; }
[dir="rtl"] .xp-stat-num bdi { direction: ltr; unicode-bidi: isolate; text-align: right; }

/* ---------------------------------------------------------------- Reduced motion, forced colours, print */
/* Lifts and nudges are movement and go. Colour and glow fades are not, so they stay: the legacy theme cuts every
   transition to .01ms with !important under reduced motion, and only another !important restores them (as WO-02 does). */
@media (prefers-reduced-motion: reduce) {
  .xp-jump-card:hover, .xp-strength:hover,
  .xp-jump-card:is(:hover, :focus-visible) .xp-jump-arrow { translate: none; }
  :is(.xp-jump-card, .xp-strength), :is(.xp-jump-card, .xp-strength)::after {
    transition-property: color, background-color, border-color, box-shadow, opacity !important;
    transition-duration: var(--dur-hover) !important;
    transition-timing-function: var(--ease-hover) !important;
  }
}
@media (forced-colors: active) {
  .xp-jump-arrow { forced-color-adjust: none; background-color: CanvasText; }
  .xp-layer::before { border-color: CanvasText; }
}
@media print {
  .xp-layer::before { box-shadow: none; }
}

/* ---------------------------------------------------------------- Arriving from the jump nav (owner, 2026-09-24)
   Picking a service used to drop you at it with no transition. motion.js now scrolls smoothly and adds .is-arriving
   when the scroll settles; the section's blocks slide down into place and fade in, staggered, then the class comes
   off and the section is back to its own styles. Keyframes only — nothing is hidden if the script never runs. */
.xp-dive, .xp-connect, #connect {
  scroll-margin-block-start: calc(var(--header-height, 88px) + 20px);
}
.xp-dive:focus-visible, .xp-connect:focus-visible { outline: none; }   /* the section takes focus for screen readers */

@media (prefers-reduced-motion: no-preference) {
  .xp-dive.is-arriving > .shell > *,
  .xp-connect.is-arriving > .shell > * {
    animation: xp-arrive 620ms var(--ease-out) both;
  }
  .xp-dive.is-arriving > .shell > *:nth-child(2),
  .xp-connect.is-arriving > .shell > *:nth-child(2) { animation-delay: 90ms; }
  .xp-dive.is-arriving > .shell > *:nth-child(3),
  .xp-connect.is-arriving > .shell > *:nth-child(3) { animation-delay: 160ms; }
  .xp-dive.is-arriving > .shell > *:nth-child(n + 4),
  .xp-connect.is-arriving > .shell > *:nth-child(n + 4) { animation-delay: 220ms; }
}

@keyframes xp-arrive {
  from { opacity: 0; transform: translateY(-14px) scaleY(.985); transform-origin: top center; }
  to   { opacity: 1; transform: none; }
}

/* Reduce-motion arrival: no travel, no scale — just a cross-fade, so choosing a service still reads as a change
   of scene for people who turn motion down. */
@media (prefers-reduced-motion: reduce) {
  /* site.css line 599 flattens every animation to .01ms under this setting, which is right for movement; a
     cross-fade carries none, so this one opts back in explicitly. */
  .xp-dive.is-arriving > .shell > *,
  .xp-connect.is-arriving > .shell > * {
    animation: xp-arrive-fade 420ms linear both !important;
    animation-duration: 420ms !important;
  }
}

@keyframes xp-arrive-fade {
  from { opacity: .28; }
  to   { opacity: 1; }
}
