/* PI Probaligence, shared site chrome (header, footer, marquee, consent, lightbox).
   Static, progressive enhancement. Tokens match the design system: warm-black
   canvas, single amber accent, Space Grotesk display, IBM Plex Sans body,
   IBM Plex Mono labels. No external resources. */

:root{
  --bg:#060607; --bg-warm:#0A0908; --surface:#101012;
  --hairline:rgba(255,255,255,.09); --text:#F4F3EF; --muted:#9C9C96;
  --faint:#82827C; --amber:#FFB006; --amber-lt:#FFCA4D;
  --ease-out:cubic-bezier(.16,.84,.32,1);
}

/* ---------- header ---------- */
.site-header{position:fixed;top:0;left:0;right:0;z-index:40;
  background:transparent;border-bottom:1px solid transparent;
  transition:background .3s, border-color .3s, backdrop-filter .3s}
.site-header.scrolled{background:rgba(6,6,7,.78);
  border-bottom:1px solid rgba(255,255,255,.08);
  -webkit-backdrop-filter:saturate(140%) blur(16px);backdrop-filter:saturate(140%) blur(16px)}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .site-header.scrolled{background:rgba(6,6,7,.97)}
}
.nav-inner{max-width:1280px;margin:0 auto;padding:0 24px;height:68px;
  display:flex;align-items:center;justify-content:space-between;gap:24px}
.skip-link{position:absolute;left:16px;top:-60px;background:var(--amber);color:#000;
  padding:10px 16px;font:600 14px/1 'IBM Plex Sans',sans-serif;z-index:60;border-radius:2px;transition:top .2s}
.skip-link:focus{top:12px}
main:focus{outline:none}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none;flex:none}
.brand img{height:34px;width:auto;display:block}
.brand .brand-word{height:15px}
.brand span{font:600 17px/1 'Space Grotesk',sans-serif;color:var(--text);letter-spacing:-.01em}

.nav{display:flex;align-items:center;gap:4px}
.nav-link{font:500 14.5px/1 'IBM Plex Sans',sans-serif;color:var(--muted);
  text-decoration:none;padding:9px 13px;border-radius:2px;white-space:nowrap;transition:color .15s;background:none;border:0;cursor:pointer}
.nav-link:hover{color:var(--text)}
.nav-link.active{color:var(--amber)}
.has-menu{position:relative}
.nav-trigger{display:flex;align-items:center;gap:5px;color:var(--muted)}
.nav-trigger:hover,.nav-trigger[aria-expanded="true"]{color:var(--text)}
.nav-trigger .caret{font-size:9px;color:var(--faint);transition:transform .2s;display:inline-block}
.has-menu:hover .nav-trigger .caret,.nav-trigger[aria-expanded="true"] .caret{transform:rotate(180deg)}
.submenu{position:absolute;top:calc(100% + 6px);left:0;min-width:258px;
  background:rgba(14,14,16,.97);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,.1);border-radius:3px;padding:7px;
  display:flex;flex-direction:column;box-shadow:0 24px 60px rgba(0,0,0,.6);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .18s, transform .18s, visibility .18s;z-index:50}
.has-menu:hover .submenu,.has-menu:focus-within .submenu,
.nav-trigger[aria-expanded="true"] + .submenu{opacity:1;visibility:visible;transform:translateY(0)}
.submenu a{display:block;padding:10px 13px;border-radius:2px;text-decoration:none;
  font:400 14px/1.3 'IBM Plex Sans',sans-serif;color:var(--muted);transition:background .15s}
.submenu a:hover{background:rgba(255,176,6,.08)}
.submenu a b{display:block;color:var(--text);font-weight:600}
.submenu a span{display:block;color:var(--faint);font-size:12.5px;margin-top:2px}
.submenu .sep{height:1px;background:rgba(255,255,255,.07);margin:6px 12px}
.nav-cta{margin-left:12px;background:var(--amber);color:#08070A;
  font:600 14.5px/1 'IBM Plex Sans',sans-serif;padding:11px 18px;border-radius:2px;
  text-decoration:none;white-space:nowrap;transition:filter .15s, transform .15s}
.nav-cta:hover{filter:brightness(1.08);transform:translateY(-1px)}
.nav-cta-ghost{margin-left:12px;border:1px solid rgba(255,176,6,.5);color:var(--amber-lt);
  font:600 14.5px/1 'IBM Plex Sans',sans-serif;padding:10px 16px;border-radius:2px;
  text-decoration:none;white-space:nowrap;transition:border-color .15s, background .15s}
.nav-cta-ghost:hover{border-color:var(--amber);background:rgba(255,176,6,.08)}
/* was 1220px: between 1221 and 1280 the nav already overflowed its 1280px
   container by 18px with both CTAs and the full menu shown. Dropping the ghost
   CTA 60px earlier fixes that and leaves room for the search button. */
@media (max-width:1280px){.nav .nav-cta-ghost{display:none}}

.nav-toggle{display:none;flex:none;width:44px;height:44px;flex-direction:column;
  align-items:center;justify-content:center;gap:5px;background:none;border:0;cursor:pointer}
.nav-toggle span{width:22px;height:1.6px;background:var(--text);display:block;
  transition:transform .25s, opacity .2s}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(6.6px) rotate(45deg)}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-6.6px) rotate(-45deg)}

.mobile-panel{overflow:hidden;max-height:0;opacity:0;visibility:hidden;background:rgba(6,6,7,.98);
  -webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);border-bottom:0;
  transition:max-height .34s ease, opacity .26s, visibility .26s}
.mobile-panel.open{max-height:92vh;opacity:1;visibility:visible;overflow-y:auto;border-bottom:1px solid rgba(255,255,255,.08)}
.mobile-panel nav{display:flex;flex-direction:column;padding:14px 24px 28px;gap:2px}
.mobile-panel a{text-decoration:none}
.mobile-panel .m-item{font:500 19px/1 'Space Grotesk',sans-serif;color:var(--text);padding:11px 0}
.mobile-panel .m-sub{font:400 16px/1 'IBM Plex Sans',sans-serif;color:var(--muted);padding:8px 0}
.mobile-panel .m-label{font:500 12px/1 'IBM Plex Mono',monospace;color:var(--faint);
  letter-spacing:.12em;text-transform:uppercase;padding:18px 0 6px}
.mobile-panel .m-sep{height:1px;background:rgba(255,255,255,.07);margin:14px 0}
.mobile-panel .nav-cta{margin:18px 0 0;text-align:center;padding:15px 18px;font-size:15px;display:block}
.mobile-panel .nav-cta-ghost{margin:12px 0 0;text-align:center;padding:14px 18px;font-size:15px;display:block}

/* Between the mobile breakpoint and 1100px the nav row runs out of width: it
   already overflowed its container here before search was added, and the
   search button made it worse. Tighten the spacing rather than drop an item. */
@media (min-width:961px) and (max-width:1100px){
  .nav{gap:1px}
  .nav-link{padding:9px 8px;font-size:14px}
  .nav-cta{margin-left:8px;padding:11px 14px;font-size:14px}
  .nav-search{margin-left:2px;padding:0 8px}
}

@media (max-width:960px){
  .nav{display:none}
  .nav-toggle{display:flex}
}

/* ---------- logo marquee ---------- */
.logo-marquee{background:var(--bg-warm);border-top:1px solid rgba(255,255,255,.07);
  border-bottom:1px solid rgba(255,255,255,.07);padding:46px 0;overflow:hidden}
.logo-marquee .label{text-align:center;margin-bottom:30px;padding:0 24px;
  font:500 11.5px/1.5 'IBM Plex Mono',monospace;letter-spacing:.18em;text-transform:uppercase;color:var(--faint)}
.marquee-mask{-webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.marquee-track{display:flex;align-items:center;gap:24px;width:max-content;
  animation:marqueeScroll 30s linear infinite;will-change:transform}
.marquee-track .row{display:flex;align-items:center;gap:24px;flex:none}
.marquee-track .logo-cell{flex:none;display:flex;align-items:center;justify-content:center;
  width:172px;height:74px;background:#F6F5F2;border:1px solid rgba(0,0,0,.06);border-radius:4px;box-sizing:border-box}
.marquee-track .logo-cell img{height:36px;width:auto;max-width:124px;object-fit:contain;display:block}
@keyframes marqueeScroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.marquee-track{animation:none}}

/* ---------- footer ---------- */
.site-footer{background:var(--bg);border-top:1px solid rgba(255,255,255,.08);
  padding:80px 24px 40px;font-family:'IBM Plex Sans',sans-serif}
.footer-inner{max-width:1280px;margin:0 auto}
.footer-grid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr 1fr;gap:48px 32px;align-items:start}
.footer-brand{min-width:220px}
.footer-brand .brand{margin-bottom:18px}
.footer-brand .brand img{height:32px}
.footer-brand .brand .brand-word{height:14px}
.footer-brand .brand span{font-size:16px}
.footer-brand p{color:var(--muted);font-size:14px;line-height:1.6;max-width:300px;margin:0 0 18px}
.footer-cred{display:flex;flex-direction:column;gap:14px;align-items:flex-start}
.footer-cred img{display:block;width:auto}
.footer-cred .cred-ansys{height:40px}
.footer-cred .cred-cadfem{height:19px}
.footer-grid .f-h{font:500 11.5px/1 'IBM Plex Mono',monospace;color:var(--faint);
  letter-spacing:.16em;text-transform:uppercase;margin:0 0 18px}
.footer-grid ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:11px}
.footer-grid li a{color:var(--muted);text-decoration:none;font-size:14px}
.footer-grid li a:hover{color:var(--text)}
.footer-grid li a.amber{color:var(--amber)}
.footer-bottom{display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;
  align-items:center;margin-top:64px;padding-top:24px;border-top:1px solid rgba(255,255,255,.07)}
.footer-bottom .copy{font:400 13px/1 'IBM Plex Mono',monospace;color:var(--faint)}
.footer-bottom .legal{display:flex;gap:18px;flex-wrap:wrap}
.footer-bottom .legal a{color:var(--faint);text-decoration:none;font-size:13px}
.footer-bottom .legal a:hover{color:var(--text)}
.footer-social{display:flex;gap:10px;margin-top:22px}
.social-btn{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;
  border:1px solid var(--hairline);border-radius:9px;color:var(--muted);
  background:rgba(255,255,255,.02);transition:color .18s var(--ease-out),border-color .18s,background .18s,transform .18s}
.social-btn svg{height:18px;width:auto;fill:currentColor;display:block}
.social-btn:hover{color:var(--amber);border-color:rgba(255,176,6,.5);background:rgba(255,176,6,.09);transform:translateY(-2px)}
.social-btn:focus-visible{outline:2px solid var(--amber);outline-offset:2px}
.social-btn--pending{opacity:.4;cursor:default}
.social-btn--pending:hover{color:var(--muted);border-color:var(--hairline);background:rgba(255,255,255,.02);transform:none}
@media (max-width:860px){.footer-grid{grid-template-columns:1fr 1fr}}
@media (max-width:520px){.footer-grid{grid-template-columns:1fr}}

/* ---------- consent banner ---------- */
.consent-banner{position:fixed;left:16px;right:16px;bottom:16px;z-index:80;max-width:760px;
  margin:0 auto;background:rgba(16,16,18,.98);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:18px 20px;
  display:flex;flex-wrap:wrap;gap:14px;align-items:center;justify-content:space-between;
  box-shadow:0 24px 60px rgba(0,0,0,.6)}
.consent-banner p{margin:0;color:var(--muted);font:400 13.5px/1.55 'IBM Plex Sans',sans-serif;flex:1 1 320px}
.consent-banner a{color:var(--amber);text-decoration:underline}
.consent-banner a:hover{text-decoration:none}
.consent-actions{display:flex;gap:10px;flex:none}
.consent-btn{font:600 13.5px/1 'IBM Plex Sans',sans-serif;padding:11px 18px;border-radius:2px;
  cursor:pointer;border:1px solid transparent}
.consent-btn.primary{background:var(--amber);color:#08070A;border-color:var(--amber)}
.consent-btn.primary:hover{filter:brightness(1.08)}
.consent-btn.secondary{background:transparent;color:var(--text);border-color:rgba(255,255,255,.2)}
.consent-btn.secondary:hover{border-color:rgba(255,255,255,.4)}

/* ---------- figure lightbox ---------- */
.zoomable{cursor:zoom-in}
.lightbox{position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;
  background:rgba(6,6,7,.92);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);padding:32px}
.lightbox[hidden]{display:none}
.lightbox img{max-width:92vw;max-height:88vh;width:auto;height:auto;border-radius:4px;
  box-shadow:0 30px 80px rgba(0,0,0,.7)}
.lightbox-close{position:absolute;top:20px;right:24px;width:44px;height:44px;border:0;border-radius:50%;
  background:rgba(255,255,255,.1);color:#fff;font-size:26px;line-height:1;cursor:pointer}
.lightbox-close:hover{background:rgba(255,255,255,.2)}

/* ---------- hero entrance (FOUC-free) ----------
   The head script adds .anim before first paint, so the hero starts hidden and
   its content staggers in via site.js. Without JS / under reduced-motion the
   .anim class is never added, so the hero stays visible. The .anim-done safety
   reveals it even if site.js fails to run. !important beats the baked inline opacity. */
html.anim [data-hero-in]{opacity:0!important}
html.anim [data-hero-in].in,
html.anim.anim-done [data-hero-in]{opacity:1!important;transition:opacity .5s cubic-bezier(.2,.7,.2,1)}
/* hero content entrance: smooth CSS keyframe (transform/opacity only), staggered by JS via animation-delay */
@keyframes pi-hero-in{from{opacity:0;transform:translateY(38px)}to{opacity:1;transform:none}}
html.anim [data-hero-in].in .pi-hero-item{animation:pi-hero-in .85s var(--ease-out) both;will-change:transform,opacity}

/* ---------- scroll reveals: CSS scroll-driven animation ----------
   Where supported (Chromium 115+), sections reveal by scrubbing this animation to their
   scroll position on the compositor thread, which is what makes it buttery. Only transform
   and opacity animate (no blur, no layout). The JS in site.js covers other browsers. Gated
   by html.anim, so reduced-motion (no .anim class) leaves content static and visible. */
@keyframes pi-reveal{from{opacity:0;transform:translateY(54px)}to{opacity:1;transform:none}}
@supports (animation-timeline:view()){
  /* applied by site.js to content BLOCKS inside sections (small elements reveal as each
     scrolls in; a whole tall section would finish revealing before it is in view). */
  html.anim .pi-rv{
    animation:pi-reveal both linear;
    animation-timeline:view();
    animation-range:entry 4% cover 40%;
    will-change:transform,opacity;
  }
}

/* shared focus visibility */
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible,
[tabindex]:focus-visible{outline:2px solid var(--amber-lt);outline-offset:2px;border-radius:2px}

/* reduced motion: smooth scrolling off too (animations are handled per page) */
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

/* belt and braces against sub-pixel horizontal pan on very narrow viewports (WCAG 1.4.10);
   clip does not create a scroll container, so sticky and scroll-driven timelines are unaffected */
html{overflow-x:clip}

/* reduced motion: the frozen marquee strip would show only its first logos, so wrap
   the first row into a centered grid instead and hide the aria-hidden duplicate row.
   !important beats the inline styles of the homepage [data-marquee] variant. */
@media (prefers-reduced-motion:reduce){
  .marquee-track,[data-marquee]{width:auto!important}
  .marquee-track .row,[data-marquee]>div{flex:1 1 100%!important;flex-wrap:wrap!important;justify-content:center;row-gap:12px}
  .marquee-track .row[aria-hidden="true"],[data-marquee]>div[aria-hidden="true"]{display:none!important}
  .marquee-mask,[data-marquee-mask]{-webkit-mask-image:none!important;mask-image:none!important}
  /* compact cells so the wrapped grid stays a short band, not a wall */
  .marquee-track .logo-cell,[data-marquee]>div>span{width:128px!important;height:56px!important}
  .marquee-track .logo-cell img,[data-marquee]>div>span img{height:26px!important;max-width:100px!important}
}

/* Insight-page episode figures: the source slides are fixed 720px social-carousel crops, so a
   full-width figure renders them near 1:1 and they look soft. Cap the display width and centre the
   frame so the 720px source downsamples (~1.5x) and reads sharper. Scoped to the insights images,
   so nothing else on the site is touched; grid figures are already narrower than the cap so they
   are unaffected. margin !important beats the inline left/right:0 while keeping the inline top gap. */
figure:has(> img[src*="/assets/media/insights/"]){max-width:500px;margin-left:auto!important;margin-right:auto!important}

/* marquee pause control (WCAG 2.2.2: moving content must be pausable, hover alone is not enough) */
.marquee-pause{position:absolute;right:14px;bottom:10px;z-index:2;width:34px;height:34px;border:1px solid var(--hairline);
  border-radius:50%;background:rgba(16,16,18,.8);color:var(--muted);font-size:11px;line-height:1;cursor:pointer}
.marquee-pause:hover{color:var(--text);border-color:rgba(255,255,255,.3)}
@media (prefers-reduced-motion:reduce){.marquee-pause{display:none}}

/* ---------- mobile figures fit the screen ----------
   The technical figures are desktop-authored rasters with their labels baked in. The first answer
   to that was a sideways pan: --fig-min held the image at the width its labels stayed legible and
   the wrapper scrolled. Reviewed on a real phone (2026-07-29) it reads as a broken layout, not as
   an invitation: the reader sees two of four boxes and a wire running off the edge, and nothing
   says the rest is one swipe away. A cropped figure is worse than a small one, because the reader
   cannot tell there is more.

   So the figure now fits the column. Detail is not lost: the viewport meta allows pinch-zoom, so a
   reader who wants the axis labels can zoom the page. Where a figure genuinely cannot survive the
   scale (dense charts, the four-box loop), the fix is a phone-authored version of the artwork, not
   a scrollport. Those are listed in the mobile pass report.

   class="fig-bleed" on the <figure> still reclaims the 24px gutters, and --fig-min is now inert,
   kept only so the per-figure inline styles do not need touching. */
.fig-pan>img,.fig-pan>video{display:block;width:100%;height:auto;max-width:100%}
/* the hint said "swipe to see the full figure". Nothing pans now, so it would be a lie. */
.fig-hint{display:none}
@media (max-width:560px){
  /* !important throughout: these pages are generated output and every <figure> carries an inline
     margin / padding / border / border-radius shorthand, which outranks any author stylesheet.
     Without it the class is inert, the figure keeps its 24px gutters and nothing bleeds. */
  figure.fig-bleed{margin-left:-24px!important;margin-right:-24px!important;border-radius:0!important;
    border-left:0!important;border-right:0!important;padding-left:0!important;padding-right:0!important}
  figure.fig-bleed>figcaption{padding-left:24px!important;padding-right:24px!important}
  /* min-width:0 undoes any per-figure --fig-min still sitting in a page's inline style */
  .fig-pan>img,.fig-pan>video{width:100%!important;min-width:0!important;max-width:100%!important}
}

/* ---------- hero figures sit above the hero backdrop ----------
   The hero sections layer a background image and its scrims as absolutely positioned children,
   then give the text column position:relative;z-index:1 to clear them. A <figure data-hero> added
   later as a sibling kept the default static position, and CSS paints non-positioned content
   before positioned siblings, so on 8 guide pages the figure and its caption render underneath
   the scrims: the caption drops to about 1.2:1 contrast and is effectively invisible. This is not
   width-dependent, it was simply easiest to see on a phone. */
section[data-hero-in]>figure[data-hero]{position:relative;z-index:1}

/* ---------- touch ergonomics ----------
   Measured in a mobile Chromium at 390px and 360px. These controls came out 13 to 34 CSS px tall.
   Most clear the 24px WCAG 2.5.8 minimum only through the spacing exception, and all of them are
   fiddly under a thumb. The breadcrumb and legal links grow their hit area with padding cancelled
   by an equal negative margin, so the target grows and the layout does not move at all. Gated on
   (pointer:coarse) so the mouse layout, built on a tight 11px rhythm, is untouched. */
@media (pointer:coarse){
  nav[aria-label="Breadcrumb"] a{display:inline-block;padding:15px 6px;margin:-15px -6px}
  .footer-bottom .legal a{display:inline-block;padding:13px 4px;margin:-13px -4px}
  .social-btn{width:44px;height:44px}
}
@media (max-width:560px){
  .marquee-pause{width:44px;height:44px;font-size:13px;right:10px;bottom:6px}
  /* the current crumb is a full article title and wrapped to three lines on a phone */
  nav[aria-label="Breadcrumb"]>span:last-child{display:inline-block;max-width:100%;
    white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom}
}

/* ---------- hero CTA pairs stack instead of wrapping ragged ----------
   The hero button rows are flex-wrap, so on a phone the second button wrapped to its own line at
   its own intrinsic width and the pair read as a mistake. Scoped to div[data-hero] because
   /guides/predict-a-full-field/ also carries data-hero on a <figure>, and :has(> a) keeps it to
   button rows: /sensitivity-analysis/ uses the same div[data-hero] wrapper for a row of <span>
   method chips (Sobol indices, SHAP, DGSM) that are meant to wrap as chips, not stack. */
@media (max-width:560px){
  section[data-hero-in] div[data-hero]:has(> a){flex-direction:column;align-items:stretch}
  section[data-hero-in] div[data-hero]:has(> a)>a{display:flex;justify-content:center;text-align:center}
}

/* The mobile menu is capped at 92vh, which on a phone means the large viewport, so its last item
   sits under the browser chrome while the URL bar is showing. dvh tracks the viewport actually
   visible; the vh line stays first as the fallback for engines without dvh. */
@media (max-width:960px){
  .mobile-panel.open{max-height:calc(100vh - 68px);max-height:calc(100dvh - 68px)}
}

/* ---------- article body patterns ----------
   News and guide bodies were pure paragraph stacks, which read as a wall of text. These give an
   article three ways to break a run of prose without changing the page template: a point list, a
   key-figure strip, and a pull quote. Static CSS only, no motion, no JS. */

/* point list: replaces a paragraph that enumerates three or four things */
.na-points{list-style:none;margin:26px 0 0;padding:0;max-width:68ch}
.na-points > li{position:relative;padding:14px 0 14px 26px;border-top:1px solid var(--hairline);
  color:#B8B8B2;font-size:16px;line-height:1.65}
.na-points > li:last-child{border-bottom:1px solid var(--hairline)}
.na-points > li::before{content:"";position:absolute;left:2px;top:23px;width:7px;height:1px;background:var(--amber)}
.na-points > li b{color:var(--text);font-weight:600}

/* key figures: 2 to 4 real numbers lifted out of the prose, each with its unit and what it counts */
.na-figs{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(200px, 100%), 1fr));gap:1px;margin:38px 0 0;
  background:var(--hairline);border:1px solid var(--hairline);border-radius:4px;overflow:hidden}
.na-figs > div{background:#0A0908;padding:24px 22px}
.na-figs .na-n{font:700 clamp(26px,3.4vw,34px)/1.05 'Space Grotesk',sans-serif;letter-spacing:-.02em;color:var(--amber)}
.na-figs .na-u{font:500 11.5px/1 'IBM Plex Mono',monospace;letter-spacing:.14em;text-transform:uppercase;
  color:var(--faint);margin-top:12px}
.na-figs .na-c{color:#9C9C96;font-size:14px;line-height:1.6;margin-top:10px}

/* pull quote: one attributable quote per article, lifted out of the paragraph flow */
.na-quote{margin:40px 0 0;padding:4px 0 4px 26px;border-left:2px solid var(--amber);max-width:64ch}
.na-quote p{font:400 clamp(18px,2.1vw,22px)/1.5 'Space Grotesk',sans-serif;color:var(--text);letter-spacing:-.01em;margin:0}
.na-quote cite{display:block;font:500 12px/1.5 'IBM Plex Mono',monospace;letter-spacing:.12em;text-transform:uppercase;
  color:var(--faint);font-style:normal;margin-top:16px}

/* aside: a short bordered note for context that would otherwise pad a paragraph */
.na-note{border:1px solid rgba(255,176,6,.28);border-radius:4px;padding:22px 26px;margin:38px 0 0;
  background:#000;max-width:68ch}
.na-note .na-k{font:500 12px/1 'IBM Plex Mono',monospace;letter-spacing:.14em;text-transform:uppercase;
  color:var(--amber);margin-bottom:12px}
.na-note p{color:#C8C8C2;font-size:15px;line-height:1.65;margin:0}

/* News hub lean previews: collapse insights/news to a few items with a Show all toggle (JS-injected). */
.xpd-collapsed > .xpd-extra{display:none!important}
.xpd-more-wrap{margin-top:28px;text-align:center}
.xpd-more{font:500 12.5px/1 'IBM Plex Mono',monospace;letter-spacing:.04em;color:#FFB006;background:transparent;border:1px solid rgba(255,176,6,.4);border-radius:2px;padding:12px 20px;cursor:pointer;transition:border-color .15s,background .15s}
.xpd-more:hover{background:rgba(255,176,6,.1);border-color:#FFB006}

/* Demonstration ticker: a running warning strip attached to every interactive
   piece that imitates the product or its math (the Flow builder mock, the
   Pareto explorer, the browser Gaussian process). Same component as the one in
   the challenge app. The track is duplicated exactly once; the -50% loop
   depends on it. The visible copy is hidden from assistive tech, which reads
   the aria-label on the note instead. Reduced motion stops the scroll and
   leaves a readable, scrollable row. */
/* the warning never fades in with the scroll reveal: site.js marks the children
   of a block with .pi-rv, and a disclaimer that is still at 10% opacity while the
   demo above it is usable defeats its purpose. Depending on how a page nests it,
   the mark lands on the strip itself (the Flow mock) or on .demo-ticker-view
   inside it (the science plot), which reads as a strip with no text, so both the
   strip and anything inside it opt out. */
html.anim .demo-ticker.pi-rv,
html.anim .demo-ticker .pi-rv{animation:none;opacity:1;transform:none}
/* overflow:hidden here as well as on the view: without it the max-content width of
   the track (~4000px) becomes this box's contribution and blows out the grid column
   of the Flow mock it sits in. The mask stays on the view so it never fades the button. */
.demo-ticker{position:relative;height:20px;min-width:0;overflow:hidden;background:rgba(255,255,255,.045);
  border-top:1px solid rgba(255,255,255,.1);border-bottom:1px solid rgba(255,255,255,.1)}
.demo-ticker-view{height:100%;margin-right:20px;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 4%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 4%,#000 92%,transparent)}
.demo-ticker-track{display:flex;width:max-content;will-change:transform;
  animation:demoTickerScroll 46s linear infinite}
.demo-ticker-set{display:flex;flex:0 0 auto}
/* pause control, same WCAG 2.2.2 reasoning as .marquee-pause above: the text
   moves on its own, so it must be stoppable without a pointer hover */
.demo-ticker-pause{position:absolute;right:0;top:0;z-index:2;width:20px;height:18px;
  border:0;border-left:1px solid rgba(255,255,255,.1);background:rgba(10,9,8,.72);
  color:var(--muted);font-size:7.5px;line-height:1;cursor:pointer;padding:0}
.demo-ticker-pause:hover{background:rgba(255,255,255,.1);color:var(--text)}
.demo-ticker-set b{padding:0 18px;white-space:nowrap;
  font:500 9.5px/18px 'IBM Plex Mono',monospace;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted)}
.demo-ticker-set b::before{content:'\25C6';margin-right:18px;opacity:.5}
@keyframes demoTickerScroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){
  .demo-ticker-view{margin-right:0;overflow-x:auto;scrollbar-width:none;-webkit-mask-image:none;mask-image:none}
  .demo-ticker-view::-webkit-scrollbar{display:none}
  .demo-ticker-track{animation:none}
  .demo-ticker-set + .demo-ticker-set{display:none}
  .demo-ticker-set b:nth-child(n+4){display:none}   /* one pass of the message, not the looped copies */
  .demo-ticker-pause{display:none}
}
@media (max-width:560px){
  .demo-ticker-set b{padding:0 12px;font-size:9px;letter-spacing:.1em}
  .demo-ticker-set b::before{margin-right:12px}
}

/* ---------- site search ----------
   The trigger buttons are injected by site.js, not written into the 84 page
   files: search needs JS, so a button that exists without it would be dead
   chrome. Panel styling follows the toolbox command-palette element. */
.nav-search{flex:none;display:inline-flex;align-items:center;gap:8px;margin-left:4px;
  height:34px;padding:0 10px;background:none;border:1px solid var(--hairline);border-radius:2px;
  color:var(--muted);cursor:pointer;transition:color .15s, border-color .15s, background .15s}
.nav-search:hover{color:var(--text);border-color:rgba(255,255,255,.22);background:rgba(255,255,255,.03)}
.nav-search svg{width:15px;height:15px;flex:none;stroke:currentColor;fill:none;stroke-width:1.8}
.mobile-panel .m-search{display:flex;align-items:center;gap:10px;width:100%;margin:4px 0 10px;
  padding:13px 14px;background:rgba(255,255,255,.04);border:1px solid var(--hairline);border-radius:2px;
  color:var(--muted);font:400 15px/1 'IBM Plex Sans',sans-serif;text-align:left;cursor:pointer}
.mobile-panel .m-search svg{width:16px;height:16px;flex:none;stroke:currentColor;fill:none;stroke-width:1.8}

.pis-overlay{position:fixed;inset:0;z-index:80;background:rgba(0,0,0,.74);
  display:flex;justify-content:center;align-items:flex-start;padding:10vh 18px 18px}
@supports ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .pis-overlay{-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
}
.pis-overlay[hidden]{display:none}
.pis-panel{width:min(640px,100%);max-height:80vh;display:flex;flex-direction:column;
  background:var(--bg-warm);border:1px solid rgba(255,176,6,.32);border-radius:4px;overflow:hidden;
  box-shadow:0 24px 70px rgba(0,0,0,.6);opacity:0;transform:translateY(8px) scale(.985)}
.pis-anim .pis-panel{transition:opacity .18s ease, transform .18s cubic-bezier(.2,.9,.3,1)}
.pis-overlay.pis-in .pis-panel{opacity:1;transform:none}
.pis-inputrow{display:flex;align-items:center;gap:12px;padding:15px 16px;flex:none;
  border-bottom:1px solid var(--hairline)}
.pis-inputrow svg{width:17px;height:17px;flex:none;stroke:var(--amber);fill:none;stroke-width:1.8}
.pis-input{flex:1;min-width:0;background:transparent;border:0;outline:none;color:var(--text);
  font:400 15px/1.2 'IBM Plex Sans',sans-serif}
.pis-input::placeholder{color:var(--faint)}
.pis-input::-webkit-search-cancel-button,.pis-input::-webkit-search-decoration{-webkit-appearance:none;appearance:none}
.pis-esc{font:500 10.5px/1 'IBM Plex Mono',monospace;color:var(--faint);
  border:1px solid var(--hairline);border-radius:2px;padding:4px 6px;flex:none}
/* ---------- search overlay on a phone ----------
   Three mobile-specific corrections to the rules above.

   1. iOS Safari zooms the page in whenever a focused field is under 16px and never zooms back
      out. The search box is the single most-tapped field on the site, so 15px is the worst place
      to have it. Same defect and same fix as the contact form.
   2. The "Esc" chip names a key the device does not have. Tapping the backdrop already closes the
      overlay (site.js closes on a backdrop mousedown, which touch synthesises), so the chip is
      just a misleading 30px of the input row. A visible close button would be better still, but
      that needs markup, not CSS.

   The panel geometry needs nothing from here: the 560px block further down already takes the
   overlay full screen on a phone, which is the right idiom and leaves the results list scrolling
   inside a fixed frame. */
@media (pointer:coarse){
  .pis-input{font-size:16px}
}
@media (max-width:560px){
  .pis-esc{display:none}
}
.pis-list{flex:1;overflow-y:auto;padding:6px 0;overscroll-behavior:contain}
.pis-list::-webkit-scrollbar{width:8px}
.pis-list::-webkit-scrollbar-thumb{background:rgba(255,176,6,.25);border-radius:4px}
.pis-group{padding:12px 18px 6px;font:500 10px/1 'IBM Plex Mono',monospace;
  letter-spacing:.18em;text-transform:uppercase;color:var(--faint)}
.pis-item{display:block;padding:10px 18px;border-left:2px solid transparent;
  text-decoration:none;cursor:pointer}
.pis-item .pis-t{display:block;font:500 14.5px/1.35 'IBM Plex Sans',sans-serif;color:var(--text)}
.pis-item .pis-x{display:block;margin-top:3px;font:400 12.5px/1.5 'IBM Plex Sans',sans-serif;
  color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the default UA mark is a yellow block: keep the highlight on brand */
.pis-item mark{background:none;color:var(--amber);font-weight:600}
.pis-item .pis-x mark{color:var(--amber-lt);font-weight:500}
.pis-item .pis-u{display:block;margin-top:4px;font:400 11px/1.4 'IBM Plex Mono',monospace;
  color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pis-item.pis-active{background:rgba(255,176,6,.09);border-left-color:var(--amber)}
.pis-item.pis-active .pis-t{color:var(--amber-lt)}
.pis-empty{padding:26px 18px 22px;font:400 13px/1.6 'IBM Plex Sans',sans-serif;color:var(--muted)}
.pis-empty b{display:block;color:var(--text);font-weight:500;margin-bottom:6px}
.pis-foot{flex:none;display:flex;gap:16px;padding:10px 18px;border-top:1px solid var(--hairline);
  font:400 10.5px/1.4 'IBM Plex Mono',monospace;color:var(--faint)}
.pis-foot .pis-count{margin-left:auto}
@media (max-width:560px){
  .pis-overlay{padding:0}
  .pis-panel{width:100%;max-height:100%;height:100%;border:0;border-radius:0}
  .pis-foot{display:none}
}
@media (prefers-reduced-motion:reduce){
  .pis-anim .pis-panel{transition:none}
}

/* ---------- search: the extras the cascade needs ----------
   The overlay answers a query in three ways now (exact, spelling-corrected,
   closest), caps its list at 12 and hands the rest to /search/. These are the
   three pieces of chrome that carries. */
.pis-item.pis-more{font:500 12px/1.4 'IBM Plex Mono',monospace;color:var(--amber);
  padding:12px 18px 14px;letter-spacing:.04em}
.pis-item.pis-more.pis-active{color:var(--amber-lt)}
.pis-sugg{display:inline-block;margin:10px 8px 0 0;padding:6px 11px;cursor:pointer;
  background:transparent;border:1px solid var(--hairline);border-radius:2px;
  font:400 12.5px/1 'IBM Plex Sans',sans-serif;color:var(--muted)}
.pis-sugg:hover,.pis-sugg:focus-visible{color:var(--amber-lt);border-color:rgba(255,176,6,.42)}

/* ---------- the /search/ page ----------
   Same result rows as the overlay, given the room a full page has: the snippet
   wraps to two lines instead of being cut at one, and every result is listed
   rather than the first twelve. */
.sp-field{display:flex;align-items:center;gap:12px;padding:14px 16px;
  background:var(--bg-warm);border:1px solid rgba(255,176,6,.32);border-radius:4px}
.sp-field svg{width:18px;height:18px;flex:none;stroke:var(--amber);fill:none;stroke-width:1.8}
.sp-field input{flex:1;min-width:0;background:transparent;border:0;outline:none;color:var(--text);
  font:400 16px/1.2 'IBM Plex Sans',sans-serif}
.sp-field input::placeholder{color:var(--faint)}
.sp-field input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
.sp-results .pis-item{padding:16px 0 17px;border-left:0;border-top:1px solid var(--hairline)}
.sp-results .pis-item:hover{background:rgba(255,176,6,.05)}
.sp-results .pis-item .pis-t{font-size:17px;line-height:1.3}
.sp-results .pis-item .pis-x{margin-top:6px;font-size:13.5px;white-space:normal;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.sp-results .pis-item .pis-u{margin-top:7px}
.sp-results .pis-group{padding:0 0 14px}
.sp-results .pis-empty{padding:22px 0}

/* A deep link from search targets a heading, and the header is sticky, so
   without this the heading lands underneath it. Covers in-page anchors too. */
:target,main h2[id],main h3[id]{scroll-margin-top:96px}

/* ---------- form validation ----------
   The demo request form runs on novalidate, so the browser shows nothing and
   site.js writes these elements itself: one .field-msg under each field that
   fails, one .form-summary above the button. Error red is #FF6E5C, the value
   the toolbox uses (design-toolbox/elements/premium-form-fields.html).

   The contact form carries its border and background in inline style
   attributes, so a stylesheet cannot mark a field red without !important.
   Same reason as the 16px font-size rule on that page. */
.field-msg{margin:7px 0 0;font:400 12.5px/1.45 'IBM Plex Sans',sans-serif;color:#FF6E5C;
  display:flex;gap:6px;align-items:flex-start}
.field-msg:empty{display:none}
.field-msg::before{content:'!';flex:none;width:14px;height:14px;margin-top:1px;
  border:1px solid currentColor;border-radius:50%;
  font:600 10px/12px 'IBM Plex Sans',sans-serif;text-align:center}
.cf-form [aria-invalid="true"],form [aria-invalid="true"]{
  border-color:rgba(255,110,92,.62)!important;background:rgba(255,110,92,.05)!important}
form [aria-invalid="true"]:focus-visible{outline-color:#FF6E5C}
.form-summary{margin:0;padding:11px 13px;border:1px solid rgba(255,110,92,.28);
  border-left:2px solid #FF6E5C;background:rgba(255,110,92,.06);
  font:400 13.5px/1.6 'IBM Plex Sans',sans-serif;color:#EEC7C1}
/* Scroll a flagged field clear of the sticky header when site.js reveals it. */
.cf-form input,.cf-form select,.cf-form textarea{scroll-margin-top:110px;scroll-margin-bottom:40px}

/* ---------- .no-phone: content a phone must not be offered ----------
   Added 2026-07-29 for the Flow builder. The builder at /challenge-app/#/studio is
   a drag-and-wire node editor, so a phone gets no route into it: the app hides its
   own entry points and guards the route (challenge-app/js/phone.js), and the two
   links to it from these pages carry this class. Query matches phone.js: portrait
   phone width, or the same phone sideways (wide but short, and touch, which keeps a
   short desktop window out of it). Also used to drop the sentence clause that
   promises the build. !important because a page's own <style> block loads after
   this file, so an equal-specificity rule there (.fb-cta{display:flex}) would win. */
@media (max-width:480px), (max-height:480px) and (pointer:coarse) {
  .no-phone { display: none !important; }
}
