/* A capability page under Systems & Software. The card it was opened from has become this hero, and each of the six
   pages composes its hero differently (data-topic on <body>); its body below is laid out by topic-bodies.css. Loads
   after systems-page.css (tokens, header states, buttons, type masks, page transitions). */
.topic-page .t-hero { position: sticky; top: 0; height: 100vh; min-height: 560px; overflow: hidden; --hg: var(--hero-ground, #080a0a, #080a0a); background: linear-gradient(180deg, rgba(8, 10, 10, 0) 10%, #080a0a 26%), linear-gradient(90deg, var(--hg)) #080a0a; color: var(--sys-text); }
/* (the ground's top is the painting's own top, blurred: eight colours across the band under the header, measured by
   tools/hero-grounds.mjs. While a card's picture grows into the hero, the header stands over that instead of a dark
   band the picture then slides into) */
@media (max-width: 760px) { .topic-page .t-hero { --hg: var(--hero-ground-m, var(--hero-ground, #080a0a, #080a0a)); } }
.t-media { position: absolute; inset: 0; overflow: hidden; view-transition-name: topic-hero; scale: var(--t-push); }
/* (the push into the picture while it dissolves into the page: systems-topic.js scrubs it from 1 to a few percent more.
   It is the frame's own scale, apart from the transform a scene may give the frame, and it scales about the frame's
   middle, so no edge of the picture can come into view. Registered, so changing it restyles nothing inside) */
@property --t-push { syntax: "<number>"; inherits: false; initial-value: 1; }
/* room around the picture for its camera move and the scroll drift, so no edge ever shows */
.t-img { position: absolute; inset: -7% -4%; background-size: cover; background-position: var(--vt-pos, 50% 50%); will-change: transform; }
/* CØ turns about the planet's centre, far below the frame: its picture box is wider so the turn has room on a phone */
[data-topic="constellation-zero"] .t-img { left: -12%; right: -12%; }
html:not(.carried) .t-img { animation: t-in 1.1s var(--sys-ease) both; }
/* each page's picture, and the next page's picture on its card */
[data-topic="applications"] .t-img, [data-topic="operations"] .t-next-media { background-image: url(/assets/systems/private-ai.webp); }
[data-topic="software"] .t-img, [data-topic="applications"] .t-next-media { background-image: url(/assets/systems/custom-software.webp); }
[data-topic="workflows"] .t-img, [data-topic="software"] .t-next-media { background-image: url(/assets/systems/ai-workflows.webp); }
[data-topic="infrastructure"] .t-img, [data-topic="workflows"] .t-next-media { background-image: url(/assets/systems/architecture.webp); }
[data-topic="deployment"] .t-img, [data-topic="infrastructure"] .t-next-media { background-image: url(/assets/systems/deployment.webp); }
[data-topic="operations"] .t-img, [data-topic="deployment"] .t-next-media { background-image: url(/assets/systems/managed-operations.webp); }   /* a direct visit fades the picture in; a card arrival already has it */
@keyframes t-in { from { opacity: 0; } }
/* On a direct visit, and on a page reached by a link whose picture was not in hand as it was shown (.t-wait, set by the
   page's head script), the picture comes in by systems-topic.js: held while it is decoded (.t-hold), then drawn at 1%
   for a few frames (.t-pre: its texture goes up and the GPU builds what drawing it needs while it cannot be seen), and
   only then faded in (.t-pic), on frames that come on time. So the first frame of it a reader sees is the start of its
   fade: a fade timed from the first paint ran on while the screen was held, and the picture first showed half faded
   in. The fade starts softly, so a frame held early in it changes next to nothing. (If the script never comes, the
   picture fades in after 3 s) */
.js:not(.carried):not(.t-pic) .t-img, .js.t-wait:not(.t-pic) .t-img { animation: t-show 1.2s cubic-bezier(.45, 0, .2, 1) 3s both; }
.js.t-hold:not(.carried):not(.t-pic) .t-img, .js.t-hold.t-wait:not(.t-pic) .t-img { animation: none; opacity: 0; }
.js.t-pre:not(.carried):not(.t-pic) .t-img, .js.t-pre.t-wait:not(.t-pic) .t-img { animation: none; opacity: .01; }
.js.t-pic:not(.carried) .t-img, .js.t-wait.t-pic .t-img { animation: t-on 1.2s cubic-bezier(.45, 0, .2, 1) both; }
/* a page reached by a transition holds its painting out of the first frames until it is decoded off the main thread
   (.t-decode, set and cleared by the head script; a few dozen ms at most): drawn in the transition's first frame, the
   compositor stopped to decode the 2880 px picture there and then, and the frame was held for it. It appears at once,
   with no fade: a card's picture growing into the hero is still mostly the card's at that moment */
.js.t-decode .t-img { visibility: hidden; }
@keyframes t-on { from { opacity: .01; } }
@keyframes t-show { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .js:not(.carried):not(.t-pic) .t-img, .js.t-wait:not(.t-pic) .t-img { animation: t-show 0s 3s both; }
  .js.t-pic:not(.carried) .t-img, .js.t-wait.t-pic .t-img { animation: none; }
}
/* the rehearsal before the hero's words come in (systems-topic.js begin), unseen: mid-arrival they are drawn behind a
   veil of almost no opacity (a fading word is drawn on a layer of its own and laid over, as the veil is), and whole in a
   colour of almost no alpha, straight onto the picture's layer as they will be; what paints a ground of its own (a
   redaction bar, a horizon line, the caret) is veiled then too */
html.t-veil .t-hero-text { opacity: .004 !important; }
html.t-ink .t-hero-text, html.t-ink .t-hero-text * { color: rgb(239 238 233 / .004) !important; text-shadow: none !important; }
html.t-ink .t-hero-text :is(.w.rd, .t-horizon, .t-caret) { opacity: .004 !important; }
.t-fx { position: absolute; inset: 0; width: 100%; height: 100%; display: block; mix-blend-mode: screen; pointer-events: none; }
.t-media::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(180deg, rgba(8, 10, 10, .42) 0%, rgba(8, 10, 10, 0) 24%, rgba(8, 10, 10, 0) 42%, rgba(8, 10, 10, .86) 100%), linear-gradient(90deg, rgba(8, 10, 10, .5) 0%, rgba(8, 10, 10, 0) 58%); }
/* the words over a painting carry a soft dark shadow of their own: the camera moves the picture under them, and a lit
   window or lantern passing behind a small line must not take it away (a pale hero has no painting behind it) */
.t-hero:not(.is-pale) :is(.t-title, .t-statement, .t-line) { text-shadow: 0 1px 2px rgba(4, 6, 9, 0), 0 0 8px rgba(4, 6, 9, 0), 0 0 22px rgba(4, 6, 9, 0); transition: text-shadow .9s var(--sys-ease); }
.t-hero.is-settled:not(.is-pale) :is(.t-title, .t-statement) { text-shadow: 0 1px 2px rgba(4, 6, 9, .5), 0 0 8px rgba(4, 6, 9, 0), 0 0 14px rgba(4, 6, 9, .55); }
.t-hero.is-settled:not(.is-pale) .t-line { text-shadow: 0 1px 2px rgba(4, 6, 9, .8), 0 0 8px rgba(4, 6, 9, .85), 0 0 22px rgba(4, 6, 9, .7); }   /* small type needs more */
@media (prefers-reduced-motion: reduce) { .t-hero:not(.is-pale) :is(.t-title, .t-statement, .t-line) { transition: none; } }
/* the soft darks some heroes lay behind their words sit above the picture, so while a card's picture grows into the hero
   it covers them, and they would appear all at once when the hand-over ends (a darkening of half the hero in one frame).
   They come in with the hero's own start instead */
.t-hero-text::before, .t-hero-text::after, .t-hero-text :is(.t-title, .t-statement, .t-line)::before { transition: opacity .8s cubic-bezier(.45, 0, .25, 1); will-change: opacity; }   /* (a soft start: they can cover half the hero; kept on layers of their own, so the end of the fade draws nothing new: a first visit built the soft dark's drawing there and then, a held frame among the arriving words) */
.js:not(.motion-ready) .t-hero-text::before, .js:not(.motion-ready) .t-hero-text::after, .js:not(.motion-ready) .t-hero-text :is(.t-title, .t-statement, .t-line)::before { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .t-hero-text::before, .t-hero-text::after, .t-hero-text :is(.t-title, .t-statement, .t-line)::before { transition: none; } }
.t-shade { position: absolute; inset: 0; z-index: 1; background: var(--t-top, #080a0a); opacity: 0; pointer-events: none; }
/* the shade is the ground the body comes up on, so the picture dissolves into the very colour that follows it */
.topic-page .t-hero:not(.is-sheet) .t-shade { background: var(--t-top); }
/* a hero that is itself the stage (topic-bodies.js heroStage): the scene's words sit over the painting, which a shade of
   the page's own picks up once the steps begin */
.t-stage-layer { position: absolute; inset: 0; z-index: 2; pointer-events: none; color: var(--sys-text); }
.t-stage-layer a, .t-stage-layer button { pointer-events: auto; }
.t-stage-layer::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; opacity: 0; transition: opacity .9s var(--sys-ease); background: var(--stage-shade, linear-gradient(180deg, rgba(8, 10, 10, .6) 0%, rgba(8, 10, 10, 0) 32%, rgba(8, 10, 10, 0) 58%, rgba(8, 10, 10, .72) 100%)); }
.t-hero.is-staging .t-stage-layer::before { opacity: 1; }
@media (max-width: 760px) { .t-stage-layer::before { background: var(--stage-shade-m, linear-gradient(180deg, rgba(8, 10, 10, .8) 0%, rgba(8, 10, 10, .3) 28%, rgba(8, 10, 10, 0) 48%, rgba(8, 10, 10, .8) 100%)); } }

/* hero type: the name, the promise in the company's own words, and the card's line */
.t-hero-text { position: absolute; z-index: 2; left: var(--gutter); bottom: 11vh; width: min(860px, calc(100vw - 2 * var(--gutter))); }
.t-title { font: 200 clamp(56px, 7.6vw, 144px)/.98 var(--sys-display); letter-spacing: -.005em; text-wrap: balance; }
.t-statement { margin-top: .45em; font: 200 clamp(26px, 2.9vw, 54px)/1.08 var(--sys-display); letter-spacing: -.005em; color: #ecedee; text-wrap: balance; }
.t-statement .accent { font-size: 1.08em; }
.t-line { margin-top: 26px; max-width: 42ch; font: 400 clamp(16px, 1.25vw, 20px)/1.5 var(--sys-body); letter-spacing: -.015em; color: #c9cfd3e6; }
.is-soft .w { clip-path: none; }
[data-topic="operations"] .t-media::after { background: linear-gradient(180deg, rgba(8, 10, 10, .42) 0%, rgba(8, 10, 10, 0) 24%, rgba(8, 10, 10, .1) 40%, rgba(8, 10, 10, .9) 100%), linear-gradient(90deg, rgba(8, 10, 10, .78) 0%, rgba(8, 10, 10, .35) 42%, rgba(8, 10, 10, 0) 64%); }   /* type that focuses in must not be clipped while it is still soft */

/* Private AI: the name leads, low on the left beneath the archive */
[data-topic="applications"] .t-hero-text { bottom: 10vh; }
/* Custom software: set right, high over the red sky */
[data-topic="software"] .t-hero-text { top: 16vh; bottom: auto; left: auto; right: var(--gutter); width: min(760px, 52vw); text-align: right; }
[data-topic="software"] .t-hero-text > * { width: fit-content; margin-left: auto; }
[data-topic="software"] .t-line { max-width: 34ch; }
[data-topic="software"] .t-media::after { background: linear-gradient(180deg, rgba(8, 10, 10, .62) 0%, rgba(8, 10, 10, .2) 38%, rgba(8, 10, 10, 0) 58%, rgba(8, 10, 10, .55) 100%), linear-gradient(270deg, rgba(8, 10, 10, .45) 0%, rgba(8, 10, 10, 0) 55%); }
/* AI workflows: low, starting where the window opens, read left to right like the chain outside */
[data-topic="workflows"] .t-hero-text { left: 50vw; bottom: 8vh; width: min(660px, 45vw); }
[data-topic="workflows"] .t-title { font-size: clamp(48px, 5.6vw, 108px); }
/* Architecture: a narrow caption column on the rock beside the building */
[data-topic="infrastructure"] .t-hero-text { bottom: 12vh; width: min(380px, 24vw); }
[data-topic="infrastructure"] .t-title { font-size: clamp(36px, 3.5vw, 66px); }
[data-topic="infrastructure"] .t-statement { font-size: clamp(22px, 2.1vw, 40px); }
[data-topic="infrastructure"] .t-line { font-size: 15px; }
/* Deployment: set right, low, each line opening from its middle */
[data-topic="deployment"] .t-hero-text { left: auto; right: var(--gutter); bottom: 10vh; width: min(720px, 50vw); text-align: right; }
[data-topic="deployment"] .t-hero-text > * { width: fit-content; margin-left: auto; }
[data-topic="deployment"] .t-media::after { background: linear-gradient(180deg, rgba(8, 10, 10, .42) 0%, rgba(8, 10, 10, 0) 24%, rgba(8, 10, 10, 0) 45%, rgba(8, 10, 10, .85) 100%), linear-gradient(270deg, rgba(8, 10, 10, .5) 0%, rgba(8, 10, 10, 0) 55%); }
/* Managed operations: the promise leads, the name sits small above it */
[data-topic="operations"] .t-title { font-size: clamp(22px, 1.9vw, 34px); font-weight: 300; }
[data-topic="operations"] .t-statement { margin-top: .35em; font-size: clamp(44px, 5.4vw, 104px); line-height: 1; }

/* below the hero: the page's own body (topic-bodies.css lays each one out) */
.t-body { position: relative; z-index: 2; background: var(--sys-ice); color: var(--sys-graphite); overflow-x: clip; }   /* nothing a scene parks off to the side may widen the page */
/* The hand-off. The body never comes up as a panel: it has no top edge. Its ground starts clear and becomes the page's
   ground over half a screen, eased at both ends, while the hero's shade, the same ground, deepens to whole by the time
   the body's top is a quarter of the way down the screen (systems-topic.js). Wherever the body is still clear it shows
   the shade, so there is no seam of colour either: the picture dissolves into the page. The page's ground is defined
   once here: the paper on a light page, the page's own --b-bg on a dark one. (A hero brought up close into its paper,
   the glossary's and the Terms', has given way to the paper already: its body keeps its plain ground.)
   --t-top is the ground at the top of the body, where the dissolve lands: the page's ground, or, when the body opens on
   a dark section of its own (data-header="deep"), that section's (systems-topic.js sets it). A page that hands over in
   its own way marks its body's root element data-handoff="own" (Contact): none of this is drawn there, and the shade
   keeps its old dark */
.topic-page { --t-ground: var(--sys-ice); --t-top: var(--t-ground); --t-soft: 50vh;
  --t-edge: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, .043) 12.5%, rgba(0, 0, 0, .156) 25%, rgba(0, 0, 0, .316) 37.5%, rgba(0, 0, 0, .5) 50%, rgba(0, 0, 0, .684) 62.5%, rgba(0, 0, 0, .844) 75%, rgba(0, 0, 0, .957) 87.5%, #000 100%); }
.topic-page:has(.t-body[data-tone="dark"]) { --t-ground: var(--b-bg, #080a0a); }
.topic-page:has(.t-body > [data-handoff="own"]) { --t-top: #080a0a; }
.topic-page .t-hero:not(.is-sheet) ~ .t-body:not(:has(> [data-handoff="own"])) { background: linear-gradient(var(--t-ground), var(--t-ground)) 0 calc(var(--t-soft) - 1px) / 100% 100% no-repeat; }
.topic-page .t-hero:not(.is-sheet) ~ .t-body:not(:has(> [data-handoff="own"]))::before { content: ""; position: absolute; z-index: -1; left: 0; right: 0; top: 0; height: var(--t-soft); pointer-events: none; background: var(--t-top); -webkit-mask-image: var(--t-edge); mask-image: var(--t-edge); }
/* without motion a hero is not brought up close into its paper (the glossary's and the Terms'): its body comes up the
   way every other does, over the picture dissolving into that paper (systems-topic.js) */
@media (prefers-reduced-motion: reduce) {
  .topic-page .t-hero.is-sheet ~ .t-body:not(:has(> [data-handoff="own"])) { background: linear-gradient(var(--t-ground), var(--t-ground)) 0 calc(var(--t-soft) - 1px) / 100% 100% no-repeat; }
  .topic-page .t-hero.is-sheet ~ .t-body:not(:has(> [data-handoff="own"]))::before { content: ""; position: absolute; z-index: -1; left: 0; right: 0; top: 0; height: var(--t-soft); pointer-events: none; background: var(--t-ground); -webkit-mask-image: var(--t-edge); mask-image: var(--t-edge); }
  /* (the glossary's letter rail opens its body on a band of paper of its own, for the list to pass under once the rail
     is held at the top; coming up over the picture it was a box with straight edges. It lays no paper until the paper
     has reached the header (bodies-glossary.js turns the header light there), when the dissolve under it is whole and
     the band is the same paper) */
  html:not(.is-light) .t-hero.is-sheet ~ .t-body .b-gl-az { background: none; }
  html:not(.is-light) .t-hero.is-sheet ~ .t-body .b-gl-az::after { content: none; }
}
/* a light page's header steps aside while the picture under it washes to paper, and comes back light once the paper
   covers the screen (a light logo over paper, or a dark one over the picture, is a half state); systems-topic.js sets
   .t-aside for the stretch between */
html.t-handed .bar { transition: opacity .45s var(--sys-ease), visibility 0s; }
html.t-aside .bar { opacity: 0; visibility: hidden; transition: opacity .3s var(--sys-ease), visibility 0s linear .3s; }

/* the way on: the next capability's picture, which becomes that page's hero */
.t-next { padding: 0 var(--gutter); }
.t-next-card { position: relative; display: block; height: 52vh; min-height: 320px; overflow: hidden; border-radius: 3px; background: #0b0d10; color: var(--sys-text); }
.t-next-media { position: absolute; inset: 0; overflow: hidden; background-size: cover; background-position: 50% 50%; }
/* the hover zoom happens inside the picture, which clips it, so the page transition captures exactly what is on screen */
.t-next-media::before { content: ""; position: absolute; inset: 0; background: inherit; transition: transform .9s var(--sys-ease); }
[data-topic="software"] .t-next-media { background-position: 64% 50%; }
.t-next-card::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(180deg, rgba(8, 10, 10, .05) 30%, rgba(8, 10, 10, .8) 100%); }
/* the way on reads as one line of type, "Next" in the italic accent and then the page's name, never a small label
   stacked over a title (the size is set on the line, so the two always match) */
.t-next-text { position: absolute; z-index: 2; left: 34px; right: 34px; bottom: 32px; font-size: clamp(40px, 4.6vw, 88px); line-height: 1.04; text-wrap: balance; }
.t-next-label { margin-right: .3em; font: italic 400 1em/1.04 var(--sys-accent); }   /* no space between: a long name wraps after its first word, never after Next */
.t-next-title { font: 200 1em/1.04 var(--sys-display); letter-spacing: -.005em; }
@media (hover: hover) and (pointer: fine) { .t-next-card:hover .t-next-media::before { transform: scale(1.03); } }
.topic-page .base { margin: 12vh var(--gutter) 0; }

/* animated text stays hidden until the script has set it up; if the script never runs it shows anyway */
.js:not(.motion-ready) :is(.t-hero-text, .t-next-text) { visibility: hidden; animation: sys-show 0s 4.5s forwards; }

@media (prefers-reduced-motion: reduce) { html:not(.carried) .t-img { animation: none; } }
@media (max-width: 760px) {
  .t-img { background-position: var(--vt-pos-m, var(--vt-pos, 50% 50%)); }   /* a phone shows a narrow slice: keep the subject in it */
  [data-topic="contact"] .t-img { --vt-pos-m: 84% 50%; }   /* the dish */
  [data-topic="intelligence"] .t-img { --vt-pos-m: 72% 50%; }   /* the telescope and the reading lamp */
  .t-hero-text, [data-topic] .t-hero-text { left: var(--gutter); right: var(--gutter); top: auto; bottom: 9vh; width: auto; text-align: left; }
  [data-topic] .t-hero-text > * { margin-left: 0; }
  .t-title, [data-topic] .t-title { font-size: clamp(44px, 12vw, 64px); }
  [data-topic="operations"] .t-title { font-size: 20px; }
  .t-statement, [data-topic] .t-statement { font-size: clamp(24px, 7vw, 36px); }
  .t-next-text { left: 22px; right: 22px; bottom: 24px; }
}
