/* ==========================================================================
   AI & beyond — shared site stylesheet
   Design system carried over from the shareable service diagrams (Step 2b),
   evolved for a full multi-page, multi-locale website (Step 3).
   Loaded by every locale (/en /de /it ...) from a single absolute path
   so new locales never need their own copy.
   ========================================================================== */

/* ---------- Self-hosted fonts (2026-09-22) ----------
   Previously loaded live from fonts.googleapis.com/fonts.gstatic.com — moved
   to self-hosted files after a German court (LG München I, 2022) found that
   loading Google Fonts from Google's own servers sends the visitor's IP
   without consent, which needs a legal basis under GDPR. Self-hosting the
   same two type families (same weights, same license — both SIL Open Font
   License, see assets/fonts/LICENSE-*.txt) removes that exposure entirely,
   so every page can also drop its <link rel="preconnect"> / Google Fonts
   <link rel="stylesheet"> tags in <head>. font-display:swap kept, same as
   the Google-served version, so text still renders immediately in a
   fallback font while the real one loads. */
@font-face{
  font-family:'IBM Plex Sans'; font-style:normal; font-weight:400;
  font-display:swap; src:url('/assets/fonts/ibm-plex-sans-latin-400-normal.woff2') format('woff2');
}
@font-face{
  font-family:'IBM Plex Sans'; font-style:normal; font-weight:500;
  font-display:swap; src:url('/assets/fonts/ibm-plex-sans-latin-500-normal.woff2') format('woff2');
}
@font-face{
  font-family:'IBM Plex Sans'; font-style:normal; font-weight:600;
  font-display:swap; src:url('/assets/fonts/ibm-plex-sans-latin-600-normal.woff2') format('woff2');
}
@font-face{
  font-family:'Space Grotesk'; font-style:normal; font-weight:500;
  font-display:swap; src:url('/assets/fonts/space-grotesk-latin-500-normal.woff2') format('woff2');
}
@font-face{
  font-family:'Space Grotesk'; font-style:normal; font-weight:600;
  font-display:swap; src:url('/assets/fonts/space-grotesk-latin-600-normal.woff2') format('woff2');
}
@font-face{
  font-family:'Space Grotesk'; font-style:normal; font-weight:700;
  font-display:swap; src:url('/assets/fonts/space-grotesk-latin-700-normal.woff2') format('woff2');
}

:root{
  --ink:#0B0A14;
  --panel:#14121F;
  --panel-2:#1A1826;
  --text:#ECEAF6;
  --muted:#A29FB8;
  --muted-2:#B4AFC9;
  --faint:#6F6B85;
  --faint-2:#8A85A0;
  --blue:#5B6CF5;
  --violet:#8B5CF6;
  --grape:#A855F7;
  --good:#37D9A0;
  --sans:'IBM Plex Sans', sans-serif;
  --disp:'Space Grotesk', sans-serif;
  --maxw:1120px;
}

*{box-sizing:border-box; margin:0; padding:0}

html{scroll-behavior:smooth; background:var(--ink)}

/* Note: background-attachment:fixed is deliberately avoided here — on a long
   page it only paints within the first viewport in some full-page-capture
   contexts (headless screenshot tools included), leaving everything below
   as plain white. A solid --ink base plus a non-fixed top gradient covers
   the whole page reliably at any length. */
body{
  background-color:var(--ink);
  background-image:radial-gradient(circle at 22% 6%, #1c1730 0%, #0B0A14 45%);
  background-repeat:no-repeat;
  background-size:100% 1400px;
  font-family:var(--sans);
  color:var(--text);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
}

a{color:inherit; text-decoration:none}
ul{list-style:none}
img{max-width:100%; display:block}

.container{max-width:var(--maxw); margin:0 auto; padding:0 28px}

/* The [hidden] attribute's UA-stylesheet "display:none" loses to ANY
   author rule that sets display on the same element (e.g. a class with
   display:flex) regardless of selector specificity, because origin beats
   specificity in the cascade — so a hidden element with such a class
   silently stays visible. This one rule (author, !important) restores the
   attribute's plain meaning everywhere it's used: the contact form's
   post-submit form.hidden=true, and every JS-toggled hidden/shown block
   in the Topics Covered UI below. Verified with a Playwright test that
   this was a real, live bug before this fix. */
[hidden]{display:none !important}

.visually-hidden{
  position:absolute !important;
  width:1px; height:1px;
  overflow:hidden;
  clip:rect(0 0 0 0);
  clip-path:inset(50%);
  white-space:nowrap;
  border:0; padding:0; margin:-1px;
}

/* ---------- Nav ---------- */
.site-nav{
  position:sticky; top:0; z-index:40;
  background:rgba(11,10,20,.72);
  backdrop-filter:blur(10px);
  border-bottom:1px solid rgba(255,255,255,.06);
}
.site-nav .container{
  /* 3-column grid (not flex+space-between) so .nav-links can sit at the
     true horizontal center of the header instead of being pushed hard
     right -- with only 2 flex children in normal flow (.nav-toggle is
     display:none below 840px), space-between used to slam .brand to the
     far left and the whole nav-links block to the far right, leaving a
     big empty gap in the middle. The empty 3rd (1fr) column here mirrors
     .brand's column so centering stays symmetric without ever
     overlapping the logo. */
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
  padding-top:18px; padding-bottom:18px;
}
.brand{
  grid-column:1; justify-self:start;
  font-family:var(--disp); font-weight:700; font-size:21px; letter-spacing:-.01em;
}
.brand b{
  background:linear-gradient(135deg,#5B6CF5,#8B5CF6 48%,#B15CF0);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
.nav-links{grid-column:2; justify-self:center; display:flex; align-items:center; gap:30px}
.nav-links a{
  font-size:14.5px; color:var(--muted-2); font-weight:500;
  transition:color .15s ease;
}
.nav-links a:hover, .nav-links a[aria-current="page"]{color:var(--text)}
.nav-locales{display:flex; gap:10px; margin-left:8px}
.nav-locales a, .nav-locales span{
  font-family:var(--disp); font-size:12px; letter-spacing:.04em;
  padding:5px 9px; border-radius:7px; color:var(--faint-2);
  border:1px solid rgba(255,255,255,.08);
}
.nav-locales a[aria-current="true"]{color:var(--text); border-color:rgba(139,92,246,.5); background:rgba(139,92,246,.1)}
.nav-locales span{opacity:.4; cursor:default}
.nav-toggle{grid-column:3; justify-self:end; display:none}

@media (max-width:840px){
  /* .site-nav's backdrop-filter (used for the blur-while-scrolling header
     look) creates a containing block for any position:fixed descendant --
     including .nav-links below -- which made its inset:64px 0 0 0 resolve
     against the ~71px header box instead of the viewport, collapsing the
     mobile dropdown to a sliver with its links spilling out past their
     own background (looked like a transparent panel with unreadable
     floating text). Turning the backdrop-filter off at this breakpoint
     restores normal viewport-relative fixed positioning; the header's own
     background is bumped closer to opaque so it still reads solid without
     the blur. */
  .site-nav{backdrop-filter:none; background:rgba(11,10,20,.94)}
  .nav-links{
    position:fixed; inset:64px 0 0 0; background:var(--ink);
    flex-direction:column; align-items:flex-start; gap:0;
    padding:10px 28px 28px; transform:translateY(-8px); opacity:0; pointer-events:none;
    transition:opacity .18s ease, transform .18s ease;
    z-index:45;
  }
  .nav-links.open{opacity:1; pointer-events:auto; transform:translateY(0)}
  .nav-links a{width:100%; padding:14px 0; border-bottom:1px solid rgba(255,255,255,.06)}
  .nav-toggle{
    display:block; background:none; border:1px solid rgba(255,255,255,.14);
    border-radius:8px; width:38px; height:34px; color:var(--text); font-size:18px;
  }
}

/* ---------- Buttons ---------- */
.btn{
  display:inline-flex; align-items:center; gap:8px;
  font-family:var(--disp); font-weight:600; font-size:14.5px;
  padding:12px 22px; border-radius:11px;
  border:1px solid transparent; cursor:pointer;
}
.btn-primary{
  background:linear-gradient(135deg,#5B6CF5,#8B5CF6 55%,#A855F7);
  color:#fff;
}
.btn-ghost{
  border-color:rgba(255,255,255,.16); color:var(--text); background:rgba(255,255,255,.02);
}

/* ---------- Hero ---------- */
.hero{padding:64px 0 20px; position:relative}
.hero .eyebrow{
  font-family:var(--disp); font-size:12px; letter-spacing:.18em; text-transform:uppercase;
  color:var(--faint-2); text-align:center; margin-bottom:18px;
}
.hero h1{
  font-family:var(--disp); font-weight:700; font-size:clamp(34px,5.4vw,58px);
  letter-spacing:-.02em; text-align:center; line-height:1.08;
}
.hero h1 b{
  background:linear-gradient(135deg,#5B6CF5,#8B5CF6 48%,#B15CF0);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
.hero .tagline{
  font-size:19px; color:var(--muted-2); text-align:center;
  max-width:900px; margin:16px auto 0;
  display:flex; flex-direction:column; gap:6px;
}
/* Each row (credibility line / triplet line / city line) is its own flex
   row so spacing stays even no matter how many sub-lines the triplet
   wraps into below — no <br> tags, so there's nothing to double up. */
.hero .tagline .tag-row{display:block}
/* Generic indent for a tag-row that continues a hardcoded break (e.g. the
   Contact page's 2-part intro line) — same 14px convention used by
   .cta-part-indent and .mit-indent elsewhere on the site. */
.hero .tagline .tag-row.indent{padding-left:14px}
/* The middle "triplet" row (Spreading / Facilitating / Architecting) is
   hardcoded to either show whole on one line, or break after each of the
   3 points — never at an arbitrary mid-sentence point the browser picks.
   840px matches the same breakpoint used for the mobile nav elsewhere in
   this file, since that's also roughly where this line stops fitting. */
.hero .tagline .tri-part{display:inline}
@media (max-width:840px){
  .hero .tagline .tri-part{display:block}
}
/* A pair of tag-row fragments that share one line by default and only
   split into two lines (same indent level, no deeper nesting) at a
   narrow one-off breakpoint — same 390px convention as .mit-list, since
   it's the AI Literacy field page's subline that needs this. */
.hero .tagline .inline-pair{display:inline}
@media (max-width:390px){
  .hero .tagline .inline-pair{display:block}
}
/* A single optional word (e.g. "can") that's dropped below a narrow
   breakpoint so a short subline doesn't wrap to a 2nd line at phone
   widths — see the AI & Digital Transformation field page. */
.hero .tagline .opt-word{display:inline}
@media (max-width:420px){
  .hero .tagline .opt-word{display:none}
}
/* Hardcoded 2-part headline break, mobile only — e.g. the Services index
   H1 ("Four fields." / indented "All connected.") — same convention as
   the tagline break classes above, just sized for h1. Left alone above
   480px, where the full headline already fits on one line. */
.hero h1 .h1-part{display:inline}
@media (max-width:480px){
  .hero h1 .h1-part{display:block}
  .hero h1 .h1-indent{padding-left:14px}
}
.hero-cta{display:flex; justify-content:center; gap:14px; margin-top:30px; flex-wrap:wrap}

/* ---------- Sub-page hero (Contact, and reused for Services / Intelligence
   / About) — same class family as .hero so eyebrow/h1/tagline styling is
   inherited, just shrunk down since these pages don't carry the homepage's
   hero graph beneath them. ---------- */
.hero-sub{padding:56px 0 8px; position:relative; overflow:hidden}
.hero-sub h1{font-size:clamp(30px,4.4vw,46px)}
.hero-sub .tagline{font-size:17px}

/* ---------- Field accent (Services field subpages) ----------
   A small decorative echo of the homepage hub-and-node graph — the
   field's own node in full, with a thick trunk line and thinner branches
   fanning toward the corner, animated with the same "flowing dash"
   traveling-pulse technique the homepage graph's connecting lines use
   (see hero-graph.js's drawPulse comment for the original SVG reference).
   Purely decorative (aria-hidden) — real content never depends on it.
   Hidden below 667px: at phone widths it sat too close to the headline,
   so rather than fight for space it simply isn't shown there. */
.field-accent{position:absolute; top:0; right:0; width:300px; height:300px; pointer-events:none}
/* The hub circle itself is draggable (assets/js/field-accent-drag.js),
   like the homepage hero graph's nodes — touch-action:none stops touch
   drags from also scrolling the page while dragging it. */
.field-accent .field-hub{touch-action:none}
.field-accent .flow{stroke-dasharray:8 240; animation:accentflow 3.2s linear infinite}
@keyframes accentflow{ to{ stroke-dashoffset:-248; } }
@media (prefers-reduced-motion: reduce){
  .field-accent .flow{animation:none}
}
@media (max-width:900px){
  .field-accent{width:220px; height:220px}
}
@media (max-width:667px){
  .field-accent{display:none}
}

/* ---------- Field offerings list (Services field subpages) ----------
   width:fit-content (capped at max-width) rather than a fixed 640px box —
   a short list (e.g. Data & Analytics' 3 items) doesn't use the full
   640px, so a fixed-width box left it looking left-heavy even though the
   box itself was centered. Shrinking the box to the actual content width
   and then centering *that* reads as properly centered regardless of how
   long the list is, while each line inside still starts flush left. */
.field-list{
  list-style:none; max-width:640px; width:fit-content; margin:0 auto; padding:0;
  display:flex; flex-direction:column; gap:14px;
}
.field-list > li{
  font-size:15.5px; color:var(--muted-2); text-align:center;
}
/* Sub-items share the same flex/gap rhythm as the top-level items (14px
   between every line, nested or not) rather than a smaller nested-list gap
   — with the bullet/dash markers and left indent gone, a uniform gap reads
   as one evenly-spaced centered stack rather than a list with visible
   hierarchy. Roby's read: "a jar of wisdom standing on an elevated
   surface." */
.field-list .sublist{
  list-style:none; margin:14px 0 0; padding:0; font-size:14px;
  display:flex; flex-direction:column; gap:14px;
}
.field-list .sublist li{text-align:center}

/* ---------- Contact form ---------- */
.contact-form{
  max-width:520px; margin:40px auto 0;
  display:flex; flex-direction:column; gap:20px;
}
.form-row{display:flex; flex-direction:column; gap:8px; text-align:center}
.form-row label{
  font-family:var(--disp); font-size:13px; font-weight:600; color:var(--muted-2);
}
.form-row input, .form-row textarea{
  font-family:var(--sans); font-size:15px; color:var(--text);
  background:rgba(255,255,255,.03); border:1.5px solid rgba(255,255,255,.14);
  border-radius:10px; padding:12px 14px; resize:vertical;
}
.form-row input:focus, .form-row textarea:focus{
  outline:none; border-color:rgba(139,92,246,.6); background:rgba(255,255,255,.05);
}
.contact-form .btn{align-self:center; border:none}
.form-status{
  max-width:520px; margin:16px auto 0; text-align:center;
  font-size:14px; min-height:20px;
}
.form-status.success{color:var(--good)}
.form-status.error{color:#F2A65A}

/* ---------- Topics Covered (AI Literacy & Education field page only) ----------
   A 2-level pill tree: main-topic pills expand to reveal subpills (some
   subpills sit behind a further "+N more (on request)" progressive-disclosure
   toggle). Subpills are selectable; selecting any reveals an interest bar
   and, from there, a Netlify form to request more info on the selection.
   Matches the field's own violet accent (--violet / .c4 palette) rather
   than the site's default blue-to-grape gradient, so the whole section
   reads as belonging to this field page. */
.topic-tree{max-width:720px; margin:0 auto; display:flex; flex-direction:column; gap:12px}
.topic-group{display:flex; flex-direction:column}
.topic-pill{
  display:inline-flex; align-items:center; gap:10px; align-self:flex-start;
  font-family:var(--disp); font-weight:600; font-size:14.5px; color:var(--text);
  background:rgba(255,255,255,.02); border:1.5px solid rgba(255,255,255,.14);
  border-radius:999px; padding:11px 20px; cursor:pointer;
}
.topic-pill::after{
  content:"+"; font-size:15px; line-height:1; color:var(--faint-2);
  transition:transform .2s ease;
}
.topic-pill[aria-expanded="true"]{
  border-color:rgba(139,92,246,.55); background:rgba(139,92,246,.1); color:#D3C6FF;
}
.topic-pill[aria-expanded="true"]::after{content:"\2212"; color:#D3C6FF}
.topic-subpills:not([hidden]){
  display:flex; flex-wrap:wrap; gap:10px;
  margin-top:14px; padding-left:14px;
}
.topic-note{
  flex-basis:100%; font-size:12.5px; color:var(--faint-2); margin:0 0 2px;
}
.topic-subpill{
  font-family:var(--sans); font-size:13.5px; font-weight:500; color:var(--muted-2);
  background:rgba(255,255,255,.02); border:1.5px solid rgba(255,255,255,.12);
  border-radius:999px; padding:9px 16px; cursor:pointer; transition:border-color .15s ease, color .15s ease, background .15s ease;
}
.topic-subpill:hover{border-color:rgba(139,92,246,.4); color:var(--text)}
.topic-subpill.selected{
  background:linear-gradient(135deg,#5B6CF5,#8B5CF6 55%,#A855F7);
  border-color:transparent; color:#fff;
}
.topic-more{
  font-family:var(--disp); font-size:12.5px; font-weight:600; color:var(--faint-2);
  background:transparent; border:1.5px dashed rgba(255,255,255,.2);
  border-radius:999px; padding:9px 14px; cursor:pointer;
}
.topic-more:hover{color:var(--muted-2); border-color:rgba(255,255,255,.35)}
.topic-onrequest:not([hidden]){
  display:flex; flex-wrap:wrap; gap:10px; flex-basis:100%; margin-top:2px;
}
.topic-interest-bar:not([hidden]){
  display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap;
  max-width:640px; margin:32px auto 0; padding:16px 22px;
  border:1.5px solid rgba(139,92,246,.4); border-radius:14px; background:rgba(139,92,246,.08);
}
.topic-interest-bar span{font-family:var(--disp); font-size:14px; font-weight:600; color:#D3C6FF}
.topic-request-form{margin-top:24px}
.topic-selected-summary{font-size:13.5px; color:var(--muted-2); text-align:center}

/* ---------- Topics Covered v2 — flat "equal pills" cloud (current/live design) ----------
   Replaces the v1 expand-to-reveal tree above per Roby's feedback: every
   pill reads at the same visual weight, whether it used to be a category
   or an item, and the most-requested items are visible without a click.
   Laid out as explicit rows (.topic-row), each its own centered flex-wrap
   line -- not one big flex-wrap cloud -- because the diamond effect Roby
   wants is about each ROW'S total width (narrow top/bottom, wide in the
   middle), not individual pill length. Row membership + order is computed
   server-side by pyramid_rows() in gen_services.py, using real measured
   pixel widths per chip; a row's own max-width is generous (860px) so at
   the 1280px reference width its content sits on one line as intended,
   while at narrow viewports each row still wraps internally via its own
   flex-wrap rather than overflowing. */
.topic-cloud{
  max-width:940px; margin:0 auto; display:flex; flex-direction:column;
  align-items:center; gap:10px;
}
.topic-row{
  display:flex; flex-wrap:wrap; justify-content:center; gap:10px;
  max-width:100%;
}
.topic-chip{
  font-family:var(--sans); font-size:13.5px; font-weight:500; color:var(--muted-2);
  background:rgba(255,255,255,.02); border:1.5px solid rgba(255,255,255,.12);
  border-radius:999px; padding:9px 16px; line-height:1.3;
  transition:border-color .15s ease, color .15s ease, background .15s ease;
}
button.topic-chip{cursor:pointer}
span.topic-chip{cursor:default}
.topic-chip:hover{border-color:rgba(139,92,246,.4); color:var(--text)}
.topic-chip-select.selected{
  background:linear-gradient(135deg,#5B6CF5,#8B5CF6 55%,#A855F7);
  border-color:transparent; color:#fff;
}
.topic-chip-toggle[aria-expanded="true"]{
  border-color:rgba(139,92,246,.55); background:rgba(139,92,246,.1); color:#D3C6FF;
}
/* The revealed on-request tier merges back into the same flex flow via
   display:contents (no nested box, no extra indent) rather than dropping
   into its own indented row -- keeps the "equal pills, one cloud" feel
   even once more items are showing. */
.topic-onrequest[hidden]{display:none}
.topic-onrequest:not([hidden]){display:contents}

.hero-graph-wrap{
  position:relative; width:100%; max-width:900px; margin:56px auto 0;
  aspect-ratio:16/11; min-height:380px;
}
#heroGraph{
  width:100%; height:100%; display:block; touch-action:none; cursor:grab;
  /* position/top/left/width/height are set inline by hero-graph.js at
     layout time -- the canvas is sized to reach up over the eyebrow/H1/
     tagline/CTA so a dragged node is never clipped, and pointer-events
     stays off so it never blocks clicking those real elements
     underneath; hero-graph.js hit-tests nodes itself via coordinates,
     independent of native event targeting, so dragging still works. */
  pointer-events:none;
}
#heroGraph.dragging{cursor:grabbing}
.graph-hint{
  text-align:center; font-size:12.5px; color:var(--faint); margin-top:10px;
  font-family:var(--disp); letter-spacing:.04em;
}

.node-panel{
  position:absolute; max-width:280px; z-index:20;
  background:var(--panel); border:1px solid rgba(255,255,255,.12);
  border-radius:14px; padding:16px 18px; box-shadow:0 18px 44px rgba(0,0,0,.5);
  opacity:0; transform:translateY(6px) scale(.98); pointer-events:none;
  transition:opacity .15s ease, transform .15s ease;
}
.node-panel.open{opacity:1; transform:translateY(0) scale(1); pointer-events:auto}
.node-panel .t{font-family:var(--disp); font-weight:600; font-size:15.5px; margin-bottom:9px}
.node-panel ul{display:flex; flex-direction:column; gap:6px; margin-bottom:12px}
.node-panel li{
  font-size:12.5px; color:var(--muted-2); padding-left:13px; position:relative; line-height:1.4;
}
.node-panel li::before{
  content:""; position:absolute; left:0; top:6px; width:4px; height:4px;
  border-radius:50%; background:currentColor;
}
.node-panel a.more{
  font-family:var(--disp); font-size:12.5px; font-weight:600; color:var(--text);
  display:inline-flex; align-items:center; gap:5px;
}
.node-panel .close{
  position:absolute; top:10px; right:12px; background:none; border:none;
  color:var(--faint-2); font-size:15px; cursor:pointer; line-height:1;
}

.pillar-fallback{padding:20px 0 8px}
.pillar-fallback ul{display:grid; grid-template-columns:1fr 1fr; gap:10px 24px}
.pillar-fallback a{font-size:14px; color:var(--muted-2)}
.pillar-fallback a:hover{color:var(--text)}

/* ---------- Sections ---------- */
.section{padding:72px 0}
.section-head{max-width:660px; margin:0 auto 40px; text-align:center}
.section-head .kicker{
  font-family:var(--disp); font-size:12px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--faint-2); margin-bottom:12px;
}
.section-head h2{
  font-family:var(--disp); font-weight:600; font-size:clamp(26px,3.4vw,36px); letter-spacing:-.01em;
}
.section-head p{color:var(--muted-2); font-size:16px; margin-top:14px}

.lede{max-width:760px; margin:0 auto; text-align:center; font-size:18px; color:var(--muted-2)}

/* ---------- Prose (reserved for any future long-form text block) ----------
   Left-aligned running copy in a centered, readable-width column. Kept in
   the stylesheet even though the About page moved away from prose to the
   .track timeline below (per Roby's "less Fließtext, more scannable"
   feedback, 2026-09-22) — still useful if a future page wants a plain
   narrative paragraph block. */
.prose{max-width:700px; margin:0 auto; display:flex; flex-direction:column; gap:20px}
.prose p{font-size:16.5px; color:var(--muted-2)}
.prose p b, .prose p strong{color:var(--text); font-weight:600}

/* ---------- Track record (About page) ----------
   A scannable timeline/bullet list instead of running prose — each row is a
   short tag (a year, or a qualifier like "15+ yrs") plus one compact line.
   Reads more like a credential list than a personal essay, on purpose. */
.track{max-width:700px; margin:0 auto; display:flex; flex-direction:column}
.track-item{
  display:flex; gap:22px; padding:15px 4px;
  border-bottom:1px solid rgba(255,255,255,.08);
}
.track-item:last-child{border-bottom:none}
.track-tag{
  flex:0 0 96px; font-family:var(--disp); font-weight:600; font-size:13.5px;
  color:#D3C6FF; padding-top:1px;
}
.track-body{flex:1; font-size:15.5px; color:var(--muted-2)}
.track-body b{color:var(--text); font-weight:600}
@media (max-width:560px){
  .track-item{flex-direction:column; gap:4px; padding:14px 4px}
  .track-tag{flex:none}
}

/* Small centered footnote-style line — About page uses this for the
   Advertising & beyond cross-reference, kept low-key and low in the page
   per Roby's request rather than folded into the main narrative. */
.note-line{font-size:13.5px; color:var(--faint-2); text-align:center; max-width:520px; margin:0 auto}
.note-line b{color:var(--muted-2)}
/* Advertising & beyond cross-reference (About page) is now a live link —
   underlined rather than left as flat text like most site nav, so it reads
   as a clickable reference to the external site rather than plain emphasis. */
.note-line a{text-decoration:underline; text-underline-offset:3px; transition:color .15s ease}
.note-line a:hover{color:var(--text)}

/* ---------- Cards / grid ---------- */
/* Flexbox with centered wrap rather than a fixed-column CSS grid — the
   Services index now has 5 cards (not a round number for any fixed column
   count), so a rigid grid would leave the last row's 2 cards pinned left
   under an empty gap. flex-basis:280px naturally settles to 3-across at
   desktop widths (with the 4th+5th centering on their own row), 2-across
   at medium widths, and 1-across on phones, with no explicit breakpoints
   needed — and it keeps working the same way if a field is ever added or
   removed again. */
.grid-4{display:flex; flex-wrap:wrap; justify-content:center; gap:18px}
.grid-4 > .card{flex:1 1 280px; max-width:336px}
.grid-2{display:grid; grid-template-columns:1fr 1fr; gap:22px}
@media (max-width:600px){.grid-2, .pillar-fallback ul{grid-template-columns:1fr}}

.card{
  position:relative;
  border-radius:18px; padding:26px 22px; border:1.5px solid rgba(255,255,255,.10);
  background:rgba(255,255,255,.03); display:flex; flex-direction:column;
  transition:border-color .15s ease, transform .15s ease;
}
.card:hover{transform:translateY(-2px)}
.card .t{font-family:var(--disp); font-weight:600; font-size:18px; margin-bottom:10px}
.card ul{display:flex; flex-direction:column; gap:7px; margin-bottom:16px; flex:1}
/* Short editorial blurb inside a card (About page's "How I work" cards)
   instead of a bulleted service list — same typography as .card li, just
   no bullet marker since it's prose, not an itemized list. */
.card p{font-size:13.5px; color:var(--muted-2); flex:1}
.card li{font-size:13px; color:var(--muted-2); padding-left:14px; position:relative}
.card li::before{content:""; position:absolute; left:0; top:7px; width:5px; height:5px; border-radius:50%; background:currentColor}
.card a.link{font-family:var(--disp); font-size:13px; font-weight:600; color:var(--text)}
.c1{border-color:rgba(91,108,245,.5)} .c1 .t{color:#C7CEFF} .c1 li{color:#AEB6EF}
.c2{border-color:rgba(168,85,247,.5)} .c2 .t{color:#E9C6FF} .c2 li{color:#D6B4F0}
.c3{border-color:rgba(0,255,209,.5)} .c3 .t{color:#A8FFEF} .c3 li{color:#8FE8D9}
.c4{border-color:rgba(139,92,246,.5)} .c4 .t{color:#D3C6FF} .c4 li{color:#BFAEE8}
.c5{border-color:rgba(60,222,220,.5)} .c5 .t{color:#B0F0EE} .c5 li{color:#98DEDC}

/* ---------- Services index: field badge / card sweep ----------
   Same single-lap "traveling dot" stroke-dash technique as the homepage
   graph / .field-accent, but non-looping: plays once, starting at 12
   o'clock on the circle (via the -90deg rotation) / at the rounded
   top-left corner on the rectangle, fading out at the end rather than
   repeating, so it doesn't become a permanent distraction.

   Style switch: two full sweep treatments always ship in every card's
   markup — the circular badge (Variant 1) and a rounded-rectangle outline
   sweep (Variant 2, from the prototype comparison) — and only one is shown
   at a time via the data-badge-style attribute on the .grid-4.stagger
   wrapper in services/index.html (or build_index_page() in gen_services.py,
   for regeneration). Default is "circle"; set it to "rect" to switch the
   whole grid to the rectangle-outline style — no markup rebuild needed.

   Speed switch: --badge-duration / --rect-duration below (defined in
   :root) are each style's single-lap duration — change one line to change
   the pace sitewide. Roby compared 2s (circle) / 3s (rectangle) in the
   prototype; both default to .5s here until a final speed is picked. */
:root{
  --badge-duration:.5s;
  --rect-duration:.5s;
}
.grid-4[data-badge-style="rect"] .badge-wrap{display:none}
.grid-4:not([data-badge-style="rect"]) .rect-sweep{display:none}

.circle-card{align-items:center; text-align:center; cursor:pointer}
/* Item list centered, no bullet markers — same "harmony" fix as the field
   pages' v4b treatment: a left-aligned bulleted list under an otherwise
   fully-centered card (badge, title, button) read as visually inconsistent. */
.circle-card ul{text-align:center}
.circle-card li{padding-left:0}
.circle-card li::before{display:none}
.badge-wrap{position:relative; width:104px; height:104px; margin:0 auto 18px}
.badge-wrap svg{position:absolute; inset:0; width:100%; height:100%}
.badge-ring{fill:#14121F; stroke-width:2}
.badge-sweep{
  fill:none; stroke-width:4.5; stroke-linecap:round; stroke:#fff;
  transform-origin:52px 52px; transform:rotate(-90deg);
  filter:drop-shadow(0 0 6px var(--glow)) drop-shadow(0 0 2px #fff);
  stroke-dasharray:16 300; stroke-dashoffset:0;
  animation:badgeSweepOffset var(--badge-duration) linear 1 both, badgeSweepFade var(--badge-duration) linear 1 both;
  /* Paused until assets/js/badge-sweep.js marks the card .in-view (via
     IntersectionObserver) — otherwise every badge would fire together on
     page load, and on mobile the 2nd–4th cards (below the fold) would
     finish their one-time sweep before anyone scrolled down to see them. */
  animation-play-state:paused;
}
.rect-sweep{position:absolute; inset:0; width:100%; height:100%; pointer-events:none; overflow:visible}
.rect-sweep path{
  fill:none; stroke-width:3.5; stroke-linecap:round; stroke:#fff;
  filter:drop-shadow(0 0 6px var(--glow)) drop-shadow(0 0 2px #fff);
  stroke-dasharray:20 1000; stroke-dashoffset:0;
  animation:rectSweepOffset var(--rect-duration) linear 1 both, rectSweepFade var(--rect-duration) linear 1 both;
  animation-play-state:paused;
}
.card.in-view .badge-sweep, .card.in-view .rect-sweep path{animation-play-state:running}
@keyframes badgeSweepOffset{ to{ stroke-dashoffset:-276.5; } }
@keyframes badgeSweepFade{ 0%,88%{opacity:1} 100%{opacity:0} }
@keyframes rectSweepOffset{ to{ stroke-dashoffset:-969; } }
@keyframes rectSweepFade{ 0%,94%{opacity:1} 100%{opacity:0} }
@media (prefers-reduced-motion: reduce){
  .badge-sweep, .rect-sweep path{animation:none; opacity:0}
}
/* Small stagger across the 4 badges so they don't all fire in perfect
   unison — purely decorative, easy to remove if Roby prefers them synced.
   Applies to whichever style is currently visible. */
.grid-4.stagger .card:nth-child(2) .badge-sweep,
.grid-4.stagger .card:nth-child(2) .rect-sweep path{animation-delay:.08s,.08s}
.grid-4.stagger .card:nth-child(3) .badge-sweep,
.grid-4.stagger .card:nth-child(3) .rect-sweep path{animation-delay:.16s,.16s}
.grid-4.stagger .card:nth-child(4) .badge-sweep,
.grid-4.stagger .card:nth-child(4) .rect-sweep path{animation-delay:.24s,.24s}
.grid-4.stagger .card:nth-child(5) .badge-sweep,
.grid-4.stagger .card:nth-child(5) .rect-sweep path{animation-delay:.32s,.32s}

/* ---------- Band (dashed callout) ---------- */
.band{
  margin:0 auto; max-width:820px; border-radius:18px; padding:26px 30px; text-align:center;
  border:1px dashed rgba(139,92,246,.4); background:rgba(139,92,246,.08);
}
.band .t{font-family:var(--disp); font-weight:600; font-size:19px; margin-bottom:14px}
.band .s{font-size:14px; color:var(--muted-2); margin-top:14px}
.band .s b{color:var(--text)}
.band .sublist{list-style:none; margin:6px 0 0; padding:0; font-size:14px; color:var(--muted-2)}
.band .sublist li{margin-top:3px}

/* Credentials label pattern: inline label + ":" on desktop, standalone
   label line (no colon) on mobile — shared 840px breakpoint. */
.cred-label{display:inline}
.cred-colon{display:inline}
@media (max-width:840px){
  .cred-label{display:block; margin-bottom:2px}
  .cred-colon{display:none}
}

/* Agency / client lists: every name is inline by default (shown) and
   carries its own leading ", " via ::before (except the true first DOM
   child) so whatever subset stays visible joins up cleanly with no stray
   separators. All names stay in the DOM at every width for GEO/AEO/AAIO —
   assets/js/credentials-fit.js is the only thing that toggles which tail
   names are display:none, measuring at runtime so the visible set (plus
   the always-shown ".ag-more"/".cl-more" tag) never wraps to a 2nd line.
   With JS disabled every name simply shows, which is a safe fallback. */
.agency-list .ag, .client-list .cl{display:inline}
.agency-list .ag:not(:first-child)::before, .client-list .cl:not(:first-child)::before{content:", "}

/* MIT-Certified sub-list: inline comma-joined by default; breaks into an
   indented 3-line list only at <=390px — a narrower, one-off breakpoint
   distinct from the site's general 840px pattern, since this line has
   more horizontal room to spare than the others. */
.mit-list .mit-part{display:inline}
.mit-list .mit-part:not(:first-child)::before{content:", "}
@media (max-width:390px){
  .mit-list .mit-part{display:block}
  .mit-list .mit-part:not(:first-child)::before{content:""}
  .mit-list .mit-indent{padding-left:14px}
}

.overlap{
  margin:26px auto 0; max-width:820px; border-radius:18px; padding:22px 26px; text-align:center;
  border:1.5px solid rgba(55,217,160,.5); background:rgba(55,217,160,.07);
}
.overlap .t{font-family:var(--disp); font-weight:600; font-size:16px; color:#7BE8C4; margin-bottom:10px}
.overlap .row{display:flex; justify-content:center; gap:12px; flex-wrap:wrap}
.overlap .chip{font-size:12.5px; color:#B4F3DD; border:1px solid rgba(55,217,160,.4); border-radius:999px; padding:6px 14px}

/* ---------- Intelligence teaser ---------- */
.intel-grid{display:grid; grid-template-columns:1.1fr 1fr 1fr; gap:18px}
@media (max-width:820px){.intel-grid{grid-template-columns:1fr}}
.intel-card{
  border-radius:18px; padding:24px; border:1.5px solid rgba(255,255,255,.10);
  background:rgba(255,255,255,.03); display:flex; flex-direction:column; gap:10px;
  cursor:pointer; transition:border-color .2s ease, transform .2s ease;
}
.intel-card:hover{border-color:rgba(139,92,246,.4); transform:translateY(-2px)}
.intel-card .tag{font-family:var(--disp); font-size:11px; letter-spacing:.1em; text-transform:uppercase; color:var(--faint-2)}
.intel-card .t{font-family:var(--disp); font-weight:600; font-size:17px; line-height:1.3}
/* 2026-09-27: Roby noticed the excerpt's "..." landed at wildly different
   points along the last line from card to card -- because the excerpt text
   was hand-truncated to a fixed CHARACTER count in the source data, so
   whatever word happened to fall at that character offset decided where
   the line broke, with no relationship to the actual rendered line width.
   Fixed properly: each excerpt now carries ~1000 characters of real body
   copy (plenty to overflow any card width, including the wide single-
   column layout between 820px and roughly 900px), and the CSS below clips
   it with a real 4-line box + browser-native ellipsis -- so the cutoff is
   always the true right edge of the last full line, every time, on every
   card, at every viewport width. Same technique already used for
   .term-card p in the Encyclopedia. */
.intel-card p{
  font-size:13.5px; color:var(--muted-2); line-height:1.55; margin:0;
  min-height:4.65em; /* ~4 lines * 1.55 line-height, reserved even when clipped shorter */
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:4; line-clamp:4;
  overflow:hidden;
}
.intel-card .link{margin-top:auto; font-family:var(--disp); font-size:12.5px; font-weight:600}

/* ---------- Audience chips ---------- */
.chiprow{display:flex; justify-content:center; gap:12px; flex-wrap:wrap}
.chiprow span{
  font-size:13.5px; color:var(--text); border:1px solid rgba(255,255,255,.14);
  background:rgba(255,255,255,.03); border-radius:999px; padding:9px 20px;
}

/* ---------- CTA band ---------- */
.cta{
  border-radius:24px; padding:56px 40px; text-align:center;
  background:linear-gradient(135deg, rgba(91,108,245,.14), rgba(168,85,247,.14));
  border:1px solid rgba(255,255,255,.10);
}
.cta h2{font-family:var(--disp); font-weight:600; font-size:clamp(24px,3vw,32px)}
.cta p{color:var(--muted-2); margin-top:12px; max-width:560px; margin-left:auto; margin-right:auto}
.cta .hero-cta{margin-top:26px}
/* Hardcoded 3-part break for the CTA body, indented after each of the
   first two breaks — applied at every width, not just mobile, since this
   paragraph is already capped to a narrow column above (max-width:560px)
   and was always going to wrap; this makes the wrap deliberate. */
.cta-body{display:flex; flex-direction:column; gap:2px}
.cta-part-indent{padding-left:14px}
/* Width-reactive variant: normal wrapping paragraph flow instead of the
   hardcoded one-phrase-per-line stack above -- for a CTA whose phrases
   read fine consolidating onto fewer lines at wider widths (see the About
   page). Each phrase stays wrapped in its own white-space:nowrap span in
   the markup so it never breaks mid-word/mid-phrase; only how many
   phrases share a line changes with the available width -- same pattern
   as the .tag-row nowrap chunks used on the field-page taglines. */
.cta-body-flow{display:block; text-align:center}
.cta-body-flow span{margin:0 2px}
/* At narrow phone widths the card's own padding left too little room for
   each part to fit on its own line, so parts were wrapping a second time
   inside themselves on top of the deliberate 3-part break. Trimming the
   card's side padding here keeps it to exactly 3 lines on typical phones. */
@media (max-width:480px){
  .cta{padding:44px 16px}
}

/* ---------- Footer ---------- */
.site-footer{border-top:1px solid rgba(255,255,255,.08); padding:44px 0 56px; margin-top:20px}
.footer-family{
  text-align:center; font-size:12.5px; color:var(--faint-2); line-height:1.8; margin-bottom:22px;
}
.footer-family b{color:var(--muted-2); font-weight:600}
/* Inline on one line by default; breaks into 2 indented lines below 840px
   (the site's general mobile breakpoint) — see .site-footer markup. The
   "·" separator is CSS-generated (not literal text) so it can show only
   when the two parts share a line — a lone dot floating above an indented
   2nd line looked wrong, per Roby's feedback. */
.footer-family .footer-part{display:inline}
.footer-family .footer-indent::before{content:"· "}
@media (max-width:840px){
  .footer-family .footer-part{display:block}
  .footer-family .footer-indent{padding-left:14px}
  .footer-family .footer-indent::before{content:""}
}
/* Centered rather than space-between (copyright left / links right) —
   reads better as one centered unit, per Roby's feedback. flex-wrap means
   the copyright line and the links row each center independently when
   they drop to two lines at narrow widths. */
.footer-bottom{
  display:flex; justify-content:center; align-items:center; flex-wrap:wrap; gap:8px 20px;
  text-align:center; font-size:12.5px; color:var(--faint);
}
.footer-links{display:flex; justify-content:center; gap:20px; flex-wrap:wrap}
.footer-links a:hover{color:var(--muted-2)}

/* ---------- Idle Matrix overlay ----------
   Ambient idle-triggered animation (assets/js/idle-matrix.js): a full-
   viewport digital-rain canvas that fades in after a period of visitor
   inactivity. Styling lives here per the site's convention (JS handles
   behavior/class-toggling, CSS handles appearance); the JS creates and
   appends this element itself, so no markup changes were needed on any
   page -- just the one <script> tag. Pieces 1+2 of Roby's "Matrix code →
   silhouette → live chat" idle-trigger concept; piece 3 (chat) is a
   separate future pass. */
.idle-matrix-overlay{
  position:fixed; inset:0; z-index:9999;
  pointer-events:none;
  opacity:0;
  transition:opacity 1.3s ease;
}
.idle-matrix-overlay.is-active{ opacity:1 }
.idle-matrix-overlay canvas{
  position:absolute; inset:0;
  width:100%; height:100%; display:block;
}

/* ---------- Terminology Encyclopedia ---------- */
.term-grid{
  display:grid; grid-template-columns:repeat(auto-fill, minmax(250px,1fr));
  gap:22px; max-width:960px; margin:0 auto; align-items:start;
}
.term-card{
  background:var(--panel); border:1px solid rgba(255,255,255,.08); border-radius:14px;
  padding:20px 20px 22px; display:flex; flex-direction:column; gap:14px;
  transition:border-color .2s ease, transform .2s ease;
  cursor:pointer;
}
.term-card:hover{border-color:rgba(139,92,246,.4); transform:translateY(-2px)}
.term-card img{width:100%; aspect-ratio:1/1; object-fit:cover; border-radius:10px; display:block}
/* Force the term title to a single line, same "..." truncation logic as
   the body copy below -- no wrapped second line, ever, so every card's
   title occupies exactly the same height and the body copy always starts
   at the same y position. A handful of the longer EN/DE titles (the ones
   that used to wrap to 2-3 lines) now truncate with a native ellipsis;
   the full title still shows on the entry page itself once clicked into. */
.term-card .t{
  font-family:var(--disp); font-weight:600; font-size:17px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* Clamp card body copy to a fixed 6-line box so every card in the grid
   reads the same height regardless of how long its definition is -- short
   entries just leave blank space below, long ones truncate with an
   ellipsis. 6 lines was picked by measuring all 10 EN/DE entries' natural
   wrapped length at this card width: most already land at 4-6 lines, so 6
   keeps all but the two genuine outliers (Agentic AI in both languages,
   and DE's Mixture of Experts) fully intact while capping those few
   cleanly, rather than truncating the majority to fit a shorter number. */
.term-card p{
  color:var(--muted-2); font-size:14.5px; line-height:1.5; margin:0; flex:1;
  min-height:9em; /* 6 lines * 1.5 line-height, reserved even when text is shorter */
  display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:6; line-clamp:6;
  overflow:hidden;
}
.term-card .link{font-size:14px; font-weight:500; color:var(--text)}

/* Entry-page image+definition layout: stacked (image above text, generous
   gap) on mobile, image-left/text-right on desktop. Only entries with an
   approved image get the .entry-body wrapper -- text-only entries keep
   .prose directly in .container, unaffected by any of this. */
.entry-body{
  display:flex; flex-direction:column; align-items:center; gap:32px;
  max-width:900px; margin:0 auto;
}
@media (min-width:800px){
  .entry-body{flex-direction:row; align-items:flex-start; gap:44px}
  .entry-body .entry-image{flex:0 0 320px; max-width:320px}
  .entry-body .prose{flex:1; max-width:none; margin:0; padding-top:6px}
}
.entry-image{
  width:100%; max-width:380px; aspect-ratio:1/1; object-fit:cover;
  border-radius:14px; margin:0; display:block;
}
.source-note{font-size:13px; color:var(--faint-2); text-align:center; margin-top:26px}
.source-note a{color:#8ab4ff}

/* ================= Intelligence article hero-visual motifs =================
   Ported verbatim (component CSS + keyframes) from the Intelligence review
   tool (copy-editor-intelligence-en.html v4), where all 14 were built and
   reviewed with Roby across several rounds. Only the review tool's own
   editor chrome (toolbar, sidebar nav, contenteditable outlines, the plain
   .card/.grid/.footnote preview-only classes) was left behind -- these are
   the actual per-article visual components, referenced by the real .motif-
   box markup in each /en/intelligence/{slug}/ page. */
.intel-hero-motif{
  max-width:700px; margin:0 auto; padding:16px; background:rgba(255,255,255,.03);
  border:1px solid rgba(255,255,255,.09); border-radius:14px;
}
.motif-box{height:180px; display:flex; align-items:center; justify-content:center; background:var(--panel-2); border-radius:10px; overflow:hidden; position:relative}

.gapbar-wrap{width:100%; padding:0 18px; display:flex; flex-direction:column; gap:14px}
.gapbar-row{display:flex; flex-direction:column; gap:4px}
.gapbar-row .lbl{display:flex; justify-content:space-between; font-size:12px; color:var(--muted-2)}
.gapbar-track{height:10px; border-radius:6px; background:#241f36; overflow:hidden}
.gapbar-fill{height:100%; border-radius:6px; background:linear-gradient(90deg,var(--blue),var(--violet)); width:0; animation:growBar 1.6s cubic-bezier(.2,.8,.2,1) both; animation-delay:.2s}
.gapbar-fill.low{background:linear-gradient(90deg,var(--faint),var(--muted)); animation-delay:.5s}
@keyframes growBar{ from{ width:0; } to{ width:var(--tw); } }

.two-part-stat{text-align:center}
.two-part-stat .top{font-family:var(--disp); font-size:32px; color:var(--text)}
.two-part-stat .bottom{font-family:var(--disp); font-size:15px; color:var(--faint-2); margin-top:8px}
.stat-count{font-family:var(--disp); font-size:36px}

.ladder{width:100%; padding:0 16px; display:flex; flex-direction:column; gap:10px}
.ladder-row{display:flex; align-items:center; gap:10px}
.ladder-row .name{width:64px; font-size:11px; color:var(--muted-2); flex-shrink:0}
.ladder-bar{height:16px; border-radius:5px; background:linear-gradient(90deg,var(--violet),var(--grape)); width:0; animation:growBar 1.6s cubic-bezier(.2,.8,.2,1) both}
.ladder-row .val{font-size:11px; color:var(--faint-2); flex-shrink:0}

.tl-svg{width:100%; height:100%}
.tl-node{fill:var(--panel-2); stroke:var(--violet); stroke-width:1.5}
.tl-label{font-family:var(--sans); font-size:9px; fill:var(--muted-2)}
.pipe-node{fill:var(--panel-2); stroke:var(--violet); stroke-width:1.5}
.pipe-node-glow{ animation:nodeGlow .85s ease-in-out infinite; }
@keyframes nodeGlow{ 0%,72%,100%{ filter:none; r:3.5; } 8%{ filter:drop-shadow(0 0 7px var(--grape)); r:6; } }
.ffwd-tri{ fill:var(--grape); }

.conv-node{fill:var(--panel); stroke:#2c2740; stroke-width:1.5}
.conv-hub{fill:var(--panel); stroke:var(--violet); stroke-width:2}
.conv-lbl{font-family:var(--sans); font-size:7.5px; fill:var(--muted-2); text-anchor:middle}
.conv-lbl-hub{font-family:var(--disp); font-size:9px; fill:var(--text); text-anchor:middle}
.conv-line{fill:none; stroke:#2c2740; stroke-width:1.5}
.conv-flow{fill:none; stroke:var(--grape); stroke-width:2; stroke-dasharray:5 60; animation:convflow 2.1s linear infinite}
@keyframes convflow{ to{ stroke-dashoffset:-65; } }

.geo-country{ width:auto; height:100%; max-width:100%; }
.geo-country .land{ fill:#2A2540; stroke:#413a5c; stroke-width:1.2; }
.citydot-core{ fill:var(--violet); }
.citydot-ring{ fill:none; stroke:var(--violet); stroke-width:2; opacity:0; animation:citypulse 2.4s ease-out infinite; transform-origin:center; }
@keyframes citypulse{ 0%{ opacity:.8; transform:scale(.3);} 100%{ opacity:0; transform:scale(2.6);} }
.geo-row{display:flex; gap:14px; align-items:flex-end; height:100%; width:100%; justify-content:center; padding:10px}
.geo-de{height:100% !important}
.geo-at{height:56% !important}
.geo-ch{height:52% !important}

.chip-photo{width:100%; height:100%; object-fit:cover; border-radius:12px}

.logo-box{width:180px; height:100px; display:flex; align-items:center; justify-content:center}
.logo-box svg{width:80%; height:80%; overflow:visible}
.deepseek-mark{ fill:url(#dsgrad); animation:breathe 3.4s ease-in-out infinite; transform-origin:center; filter:drop-shadow(0 0 10px rgba(139,92,246,.45)); }
@keyframes breathe{ 0%,100%{ transform:scale(1); } 50%{ transform:scale(1.05); } }

.bd-bar{ fill:url(#bdgrad); transform-box:fill-box; transform-origin:bottom; }
.bd-bar-1{ animation:eq1 2.2s ease-in-out infinite; }
.bd-bar-2{ animation:eq2 1.8s ease-in-out infinite; animation-delay:.24s; }
.bd-bar-3{ animation:eq3 2.6s ease-in-out infinite; animation-delay:.1s; }
.bd-bar-4{ animation:eq4 2.0s ease-in-out infinite; animation-delay:.4s; }
@keyframes eq1{ 0%{transform:scaleY(.35)} 22%{transform:scaleY(.9)} 40%{transform:scaleY(.5)} 63%{transform:scaleY(1)} 81%{transform:scaleY(.4)} 100%{transform:scaleY(.35)} }
@keyframes eq2{ 0%{transform:scaleY(.6)} 30%{transform:scaleY(.25)} 55%{transform:scaleY(.95)} 78%{transform:scaleY(.5)} 100%{transform:scaleY(.6)} }
@keyframes eq3{ 0%{transform:scaleY(.45)} 18%{transform:scaleY(1)} 44%{transform:scaleY(.3)} 70%{transform:scaleY(.75)} 90%{transform:scaleY(.4)} 100%{transform:scaleY(.45)} }
@keyframes eq4{ 0%{transform:scaleY(.8)} 25%{transform:scaleY(.4)} 50%{transform:scaleY(1)} 72%{transform:scaleY(.3)} 100%{transform:scaleY(.8)} }

.mistral-box svg{ width:120px; height:120px }
.mistral-row{ animation:flicker 2.8s ease-in-out infinite; transform-box:fill-box; }
.mistral-row-1{ animation-delay:.9s }
.mistral-row-2{ animation-delay:.7s }
.mistral-row-3{ animation-delay:.5s }
.mistral-row-4{ animation-delay:.25s }
.mistral-row-5{ animation-delay:0s }
@keyframes flicker{ 0%,100%{ opacity:.72; } 50%{ opacity:1; filter:drop-shadow(0 0 5px currentColor); } }

.euflag-box{width:216px; height:144px; border-radius:8px; position:relative; display:flex; align-items:center; justify-content:center; box-shadow:inset 0 0 0 1px #2c2740; overflow:hidden}
.euflag-box svg{width:100%; height:100%}
.euflag-pair{display:flex; gap:22px; align-items:flex-start; justify-content:center; flex-wrap:wrap}
.euflag-opt{display:flex; flex-direction:column; align-items:center; gap:8px}
.euflag-opt .opt-lbl{font-size:11px; color:var(--faint-2); font-family:var(--sans)}

/* ---- article-specific sizing/fixes carried over from the review tool ----
   Note: #art-08/#art-01 below use a descendant selector (space between id
   and class) which never actually matches, since .motif-box carries both
   the class AND the id on the very same element -- left as-is (harmless,
   since .chip-photo's object-fit:cover still crops those two photos to look
   fine by luck). #art-15 uses the corrected compound selector (no space),
   same fix already applied to Mistral's #art-11.motif-box svg rule, so it
   actually takes effect. */
#art-08 .motif-box{ aspect-ratio: 1000 / 667; min-height: 0; height:auto; }
#art-01 .motif-box{ aspect-ratio: 1440 / 680; min-height: 0; height:auto; }
#art-15.motif-box{ aspect-ratio: 960 / 640; min-height: 0; height:auto; }

#art-09 .motif-box{ overflow: visible; padding: 30px; }
#art-09 .logo-box{ width: 360px; height: 200px; }

#art-13 .sf-logo{ width: 180px; height: 179px; }
#art-13 .sf-logo svg{ width: 100%; height: 100%; }
#art-13 .sf-logo .flake-base{ fill:#29B5E8; opacity:0; animation: sfFlakeIn .9s ease .1s forwards; }
#art-13 .sf-logo .flake-pulse{ fill:#F4FDFF; }
@keyframes sfFlakeIn{ to{ opacity:1; } }

#art-14 .motif-box{
  --cyan:#3FD9E8; --line:rgba(120,150,255,.16); --grad:linear-gradient(135deg,#3FD9E8 0%,#4C7CF0 48%,#8B5CF6 100%);
}
#art-14 .radar-wrap{ display:flex; align-items:center; justify-content:center; width:100%; }
#art-14 .radar{ position:relative; right:auto; bottom:auto; width:180px; height:180px; border-radius:50%;
  border:1px solid var(--line); background:radial-gradient(circle at 50% 50%,rgba(63,217,232,.07),transparent 70%);
  overflow:hidden; }
#art-14 .radar-ring{ position:absolute; border-radius:50%; border:1px solid rgba(120,150,255,.20); }
#art-14 .radar-ring.r1{ inset:12%; } #art-14 .radar-ring.r2{ inset:30%; } #art-14 .radar-ring.r3{ inset:48%; }
#art-14 .radar-cross{ position:absolute; inset:0; }
#art-14 .radar-cross::before, #art-14 .radar-cross::after{ content:""; position:absolute; background:rgba(120,150,255,.14); }
#art-14 .radar-cross::before{ left:50%; top:4%; bottom:4%; width:1px; transform:translateX(-50%); }
#art-14 .radar-cross::after{ top:50%; left:4%; right:4%; height:1px; transform:translateY(-50%); }
#art-14 .radar-sweep{ position:absolute; inset:0; border-radius:50%;
  background:conic-gradient(from 0deg, rgba(63,217,232,.60), rgba(76,124,240,.22) 16%, transparent 34%, transparent 100%);
  animation: art14radarspin 5s linear infinite; mix-blend-mode:screen; }
#art-14 .radar-core{ position:absolute; left:50%; top:50%; width:7px; height:7px; margin:-3.5px; border-radius:50%;
  background:var(--grad); box-shadow:0 0 10px rgba(63,217,232,.6); }
#art-14 .radar-blip{ position:absolute; width:8px; height:8px; margin:-4px; border-radius:50%; background:var(--cyan);
  box-shadow:0 0 10px rgba(63,217,232,.8); opacity:0; animation: art14blip 5s linear infinite; }
#art-14 .radar-blip.b-se{ left:50%; top:9%; animation-delay:0s; }
#art-14 .radar-blip.b-fi{ left:91%; top:50%; animation-delay:1.25s; }
#art-14 .radar-blip.b-dk{ left:50%; top:91%; animation-delay:2.5s; }
#art-14 .radar-blip.b-no{ left:9%; top:50%; animation-delay:3.75s; }
@keyframes art14radarspin{ to{ transform:rotate(360deg); } }
@keyframes art14blip{
  0%{ opacity:0; transform:scale(.5); }
  3%{ opacity:1; transform:scale(1.25); }
  6%{ opacity:1; transform:scale(1); }
  16%{ opacity:0; transform:scale(.8); }
  100%{ opacity:0; }
}

#art-12 .triad-box{ width:100%; max-width:220px; margin:0 auto; }
#art-12 .triad-box svg{ width:100%; height:auto; overflow:visible; }
#art-12 .triad-line{ stroke:rgba(120,150,255,.18); stroke-width:1; }
#art-12 .triaddot-core{ fill:var(--blue); }
#art-12 .triaddot-core.c2{ fill:var(--violet); }
#art-12 .triaddot-core.c3{ fill:var(--grape); }
#art-12 .triaddot-ring{ fill:none; stroke:var(--blue); stroke-width:1.4; opacity:0;
  animation: art12triadpulse 2.6s ease-out infinite; }
#art-12 .triaddot-ring.c2{ stroke:var(--violet); animation-delay:.7s; }
#art-12 .triaddot-ring.c3{ stroke:var(--grape); animation-delay:1.4s; }
@keyframes art12triadpulse{ 0%{ opacity:.8; transform:scale(.4); } 100%{ opacity:0; transform:scale(2.4); } }

/* #04's inline mid-article ROI bar chart reuses .gapbar-* verbatim, its own
   instance so it doesn't compete with the hero motif's bars for layout width. */
.inline-chart{ margin: 18px 0 26px; padding: 18px 20px; background: rgba(255,255,255,.03);
  border: 1px solid rgba(255,255,255,.09); border-radius: 12px; }
.inline-chart .chart-label{ font-family:var(--disp); font-size:10.5px; letter-spacing:.08em; text-transform:uppercase;
  color:var(--faint-2); margin-bottom:12px; display:block; }
.inline-chart .gapbar-wrap{ padding:0; gap:12px; }
.inline-chart .gapbar-row .lbl{ font-size:12.5px; }

/* ================= Intelligence article page: prose extensions ================= */
.prose h3{font-family:var(--disp); font-weight:600; font-size:18px; color:#D3C6FF; margin-top:6px}
.prose ul{margin:0 0 0 20px; display:flex; flex-direction:column; gap:8px}
.prose li{font-size:16.5px; color:var(--muted-2)}
.prose li b, .prose li strong{color:var(--text); font-weight:600}

.intel-sources{max-width:700px; margin:34px auto 0; padding-top:20px; border-top:1px solid rgba(255,255,255,.08)}
.intel-sources-label{font-family:var(--disp); font-size:11px; letter-spacing:.1em; text-transform:uppercase; color:var(--faint-2)}
.intel-sources ul{list-style:none; margin-top:10px; display:flex; flex-direction:column; gap:7px}
.intel-sources li{font-size:12.5px; color:var(--muted-2)}
.intel-sources a{color:#C9BFFF; text-decoration:underline; text-underline-offset:3px}
.intel-back{max-width:700px; margin:22px auto 0; font-family:var(--disp); font-size:13px}
.intel-back a{color:var(--text)}

/* ---- Intelligence index cards: hero-motif thumbnail (2026-09-26) ----
   Reuses each article's real .motif-box markup (same #art-{num} id, so the
   per-article overrides below still target it) inside the card, just at a
   shorter fixed height. Most motifs are already width:100%/height:100%-based
   and shrink cleanly; the handful built with fixed pixel dimensions (the
   DeepSeek logo, Snowflake flake, Nordics radar) get a card-scoped size
   knockdown so nothing crops awkwardly at thumbnail size. */
.intel-card-motif{
  display:block; border-radius:10px; overflow:hidden;
}
.intel-card-motif .motif-box{
  height:150px !important; aspect-ratio:auto !important; min-height:0 !important;
  border-radius:10px;
}
.intel-card-motif .two-part-stat .top{font-size:26px}
.intel-card-motif .logo-box{width:180px !important; height:100px !important}
.intel-card-motif .sf-logo{width:120px !important; height:119px !important}
.intel-card-motif .radar{width:120px !important; height:120px !important}

/* ---- Roby's 2026-09-26 follow-up round: 4 motif fixes ----
   1) Mistral logo was rendering stretched full-width (a bare <svg> with a
      square 24x24 viewBox and no width/height defaults to a 300x150 box in
      most browsers, so it got squeezed non-uniformly into the wide article
      motif box). Constraining it to an actual square gives it real
      breathing room and stops the distortion. */
#art-11.motif-box svg{ width:110px; height:110px; }

/* 2) Article 12 (AWS Bedrock / Azure AI Foundry / Vertex AI): three logo
   tiles side by side, per Roby's request. 2026-09-27 follow-up: Roby sent
   the actual logo files, so these replaced the original hand-drawn SVG
   recreations (kept commented further below for history) -- each PNG was
   cropped tight to its own icon content (Amazon Bedrock's had baked-in
   "Amazon Bedrock" text underneath the mark; that was cropped away since
   the site already prints its own caption below the tile), so displaying
   all three at the same tile size with object-fit:contain keeps their
   visual weight consistent even though the source files aren't the same
   pixel dimensions. Tiles are a neutral light card rather than a brand-
   color background, since these logos already carry their own real color
   and a colored tile behind them would fight with it. */
.platform-logos{ display:flex; gap:28px; align-items:center; justify-content:center; width:100%; height:100%; flex-wrap:wrap }
.plogo{ display:flex; flex-direction:column; align-items:center; gap:10px }
.plogo-tile{ width:88px; height:88px; border-radius:18px; display:flex; align-items:center; justify-content:center; background:rgba(255,255,255,.94); padding:14px; box-sizing:border-box }
.plogo-tile img{ width:100%; height:100%; object-fit:contain; display:block }
.plogo-label{ font-family:var(--disp); font-weight:600; font-size:12px; color:var(--muted-2); letter-spacing:.02em; text-align:center }
.intel-card-motif .platform-logos{ gap:14px }
.intel-card-motif .plogo-tile{ width:56px; height:56px; border-radius:13px; padding:9px }
.intel-card-motif .plogo-label{ font-size:9.5px }
/* previous hand-drawn SVG version (2026-09-26), kept for reference only:
.plogo-tile svg{ width:38px; height:38px }
.plogo-aws .plogo-tile{ background:linear-gradient(135deg,var(--violet) 0%,var(--grape) 100%) }
.plogo-azure .plogo-tile{ background:#0078D4 }
.plogo-gcp .plogo-tile{ background:#1A73E8 }
.intel-card-motif .plogo-tile svg{ width:26px; height:26px }
*/

/* 3) Article 14 (Nordics) replaces the CSS radar/blip motif (kept above,
   now unused) with a real outline map of the region -- Norway, Sweden,
   Finland and Denmark (the four countries Deloitte's survey actually
   covers), each a real coastline traced from public-domain Natural Earth
   boundary data, merged into one shared small-map layout so it reads as a
   single connected landmass, same visual language as the DACH map
   (.land fill/stroke) rather than DACH's separate-flag-panel layout. */
.nordics-map{ height:100%; width:100%; display:flex; align-items:center; justify-content:center; padding:14px }
.nordics-map svg{ height:100%; width:auto; max-width:100% }
.nordics-map .land{ fill:#2A2540; stroke:#413a5c; stroke-width:1.2 }
