/* Systems & Software. One world sits behind the page and the chapters are content passing through it.
   Loads after site.css (fonts, header, search) and galaxy-pages.css (menu). Type follows the home hero:
   Helvetica Neue for display, Instrument Serif italic for the accent, Archivo for reading. */
:root {
  --sys-ice: #EDF1F4; --sys-graphite: #0D1117; --sys-text: #efeee9;
  --sys-display: "Helvetica Neue", Inter, var(--sans);
  --sys-accent: "Instrument Serif", var(--serif);
  --sys-body: Archivo, var(--sans);
  --sys-ease: cubic-bezier(.22, 1, .36, 1);
}
html { background: #080a0a; transition: background-color .6s var(--sys-ease); }
html.is-light { background: var(--sys-ice); }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
body.systems-page { background: transparent; color: var(--sys-text); font-family: var(--sys-body); overflow-x: hidden; }
.film :where(h1, h2, h3, p) { margin: 0; }
.accent { font-family: var(--sys-accent); font-style: italic; font-weight: 400; letter-spacing: 0; }
/* words rise from behind their own masks. The mask is a clip-path, not overflow, so every word keeps the line's
   baseline (an overflow-hidden inline-block sits on its bottom edge instead); it reaches past the box for descenders
   and italic overhang */
.w { display: inline-block; clip-path: inset(-.2em -.2em -.32em); }
.wi { display: inline-block; }
.is-set .w { clip-path: none; }
/* text that animates in stays hidden from the first paint until the script has set it up, so it never flashes in
   place first; if the script never runs, it shows anyway after a few seconds */
.js:not(.motion-ready) body:not(.topic-page) :is(.r-line, .r-lede, .caps h2, .panels, .close-h, .close-main p, .close-actions, .base), .js:not(.motion-ready):not(.carried) body:not(.topic-page) .bar { visibility: hidden; animation: sys-show 0s 4.5s forwards; }
@keyframes sys-show { to { visibility: visible; } }

/* the header from the home page, switching to its dark logo once the page turns light */
.bar__mark { position: relative; }
.bar__mark img { transition: opacity .45s var(--sys-ease); }
.bar__mark img + img { position: absolute; left: 0; top: 0; opacity: 0; }
.is-light .bar__mark img:first-child { opacity: 0; }
.is-light .bar__mark img + img { opacity: 1; }
.search-trigger, .menu-trigger { transition: color .45s var(--sys-ease), border-color .45s var(--sys-ease), background .45s var(--sys-ease), box-shadow .45s var(--sys-ease); }
/* over a painting the search field is smoked glass: a dark base under the sheen, so it reads the same over a lit sky
   or a dark wall, and a picture passing behind it in a page transition does not change what it looks like */
html:not(.is-light) body.systems-page .search-trigger { background: linear-gradient(180deg, #ffffff17, #ffffff0d), rgba(8, 10, 13, .55); }
.is-light .search-trigger { color: #0d11178c; border-color: #0d11171f; background: linear-gradient(180deg, #ffffffcc, #ffffff99); box-shadow: inset 0 1px 0 #ffffff, 0 1px 2px #0d11170d, 0 10px 28px -16px #0d111733; }
.is-light .search-trigger svg { color: #0d11179e; }
.is-light .search-trigger kbd { color: #0d111780; border-color: #0d11171a; background: #0d111708; }
.is-light .menu-trigger { color: var(--sys-graphite); }
/* in the light chapters content scrolls under the header; a short fade of the page colour keeps it off the logo */
.bar::before { content: ""; position: absolute; inset: 0 0 -30px; z-index: -1; pointer-events: none; opacity: 0; transition: opacity .45s var(--sys-ease); background: linear-gradient(180deg, var(--sys-ice) 0%, var(--sys-ice) 52%, rgba(237, 241, 244, 0) 100%); }
.is-light .bar::before { opacity: 1; }

.world { position: fixed; inset: 0; width: 100%; height: 100%; display: block; z-index: 0; opacity: 0; }
.film { position: relative; z-index: 1; }
.no-gl .world { display: none; }

/* 1. the ridge: the line holds while the world moves */
.ridge { position: relative; height: 100vh; }
.no-gl .ridge::before { content: ""; position: absolute; inset: 0; background: url(systems/ridge.webp) 50% 52% / cover; filter: saturate(.7) brightness(.7) contrast(1.15); }
.r-line { position: absolute; left: 0; right: 0; top: 15vh; text-align: center; font: 200 clamp(52px, 7.4vw, 140px)/1 var(--sys-display); letter-spacing: -.005em; text-wrap: balance; }
.r-line .accent { font-size: .86em; }
.r-lede { position: absolute; left: 0; right: 0; bottom: 9vh; width: min(44ch, 86vw); margin: 0 auto; text-align: center; font: 400 clamp(16px, 1.3vw, 21px)/1.45 var(--sys-body); letter-spacing: -.015em; color: #d5d9dbe8; text-shadow: 0 1px 18px rgba(5, 6, 10, .55); }

/* 2. in the light: what we build, one capability at a time */
.caps { position: relative; height: 100vh; display: flex; flex-direction: column; padding: 14vh var(--gutter) 7vh; color: var(--sys-graphite); }
.caps h2 { margin-bottom: 5vh; font: 200 clamp(40px, 4.4vw, 84px)/1 var(--sys-display); letter-spacing: -.005em; }
.panels { flex: 1; display: flex; gap: 10px; min-height: 0; }
.panel { position: relative; flex: 1 1 0; min-width: 0; min-height: 0; overflow: hidden; border-radius: 3px; background: #0b0d10; color: var(--sys-text); }
.p-img { position: absolute; left: 50%; top: 50%; width: var(--open-w, 900px); height: var(--open-h, 100%); margin-left: calc(var(--open-w, 900px) / -2); margin-top: calc(var(--open-h, 600px) / -2); background-size: cover; background-position: center; transform: scale(1.12); will-change: transform; }
.panel::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(8, 10, 10, .08) 35%, rgba(8, 10, 10, .82) 100%); pointer-events: none; }
/* a closed card shows its painting under a light shade, with no name written in it: it reads as a picture, not a dark bar
   with a label in it (the open card carries the title) */
.p-dim { position: absolute; inset: 0; z-index: 1; background: rgba(8, 10, 10, .22); }
.p-tab { display: none; }
.p-in { position: absolute; z-index: 2; left: 34px; bottom: 34px; width: min(460px, calc(100vw - 2 * var(--gutter) - 68px)); opacity: 0; visibility: hidden; }
.p-in h3 { font: 200 clamp(38px, 3.5vw, 64px)/1 var(--sys-display); letter-spacing: -.005em; }
.p-in p { margin-top: 14px; max-width: 36ch; font: 400 16px/1.5 var(--sys-body); letter-spacing: -.01em; color: #d5d9dbe8; }
.p-in a { display: inline-block; margin-top: 22px; padding-bottom: 4px; background: linear-gradient(rgba(239, 238, 233, .45), rgba(239, 238, 233, .45)) 0 100% / 100% 1px no-repeat; font: 500 14px/1.2 var(--sys-body); }
.p-in a:hover { background-image: linear-gradient(var(--sys-text), var(--sys-text)); }

/* 3. the close: one screen, one action, and the way on */
/* A close-up of the page's own painting set beside text that would otherwise stand in an empty field. One treatment
   for every page: cut from the painting by tools/art-details.py, no frame, revealed by a wipe and drifting slightly with
   the scroll (the page's script sets that up; without it the picture simply shows). A plate holding several pictures
   shows the one marked .is-on; a scene changes it as it steps. */
.b-plate { position: relative; margin: 0; overflow: hidden; border-radius: 3px; background: #0b0d10; aspect-ratio: var(--plate-ratio, 3 / 2); }
.b-plate img { position: absolute; left: 0; top: -6%; width: 100%; height: 112%; object-fit: cover; object-position: var(--plate-pos, 50% 50%); }
.b-plate.is-stack img { opacity: 0; } .b-plate.is-stack img.is-on { opacity: 1; }
/* text on one side, its picture on the other */
.b-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); align-items: center; column-gap: clamp(40px, 6vw, 110px); }
@media (max-width: 760px) { .b-split { grid-template-columns: 1fr; row-gap: 34px; } .t-end .b-plate { display: none; } }
/* The close is the ending: one screen, the footer at its foot. The statement runs the width on one line, and the reply
   hangs beneath it from a little before the middle: what to bring, then the way to start. It stands in the room left
   above the footer, clear of the header */
.close { position: relative; min-height: 100svh; display: flex; flex-direction: column; padding: 0 var(--gutter); color: var(--sys-graphite); }
.close-main { flex: 1 0 auto; display: flex; flex-direction: column; justify-content: center; padding: calc(84px + 4vh) 0 6vh; }   /* (a set clearance: --bar-h is written while a page is left) */
.close-h { font: 200 clamp(52px, 7vw, 134px)/1 var(--sys-display); letter-spacing: -.01em; text-wrap: balance; }
.close-h .accent { font-size: .86em; }
.close-main p { max-width: 44ch; margin: 5.5vh 0 0 40%; font: 400 clamp(17px, 1.3vw, 21px)/1.5 var(--sys-body); letter-spacing: -.015em; color: #333b46; }
.close-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 18px 30px; margin-top: 42px; }
.close-main .close-actions { margin: 4.5vh 0 0 40%; }
.close .base { margin-top: 0; }
@media (max-width: 760px) {
  .close-main { padding: 84px 0 4vh; }
  .close-h { font-size: clamp(44px, 12.4vw, 56px); }
  .close-main p { margin: 3.2vh 0 0; }
  .close-main .close-actions { margin: 3.4vh 0 0; gap: 14px 26px; }
}
.btn { display: inline-flex; align-items: center; height: 50px; padding: 0 26px; border-radius: 999px; background: var(--sys-graphite); color: var(--sys-ice); font: 500 15px/1 var(--sys-body); letter-spacing: -.005em; transition: background-color .18s var(--sys-ease), transform .12s var(--sys-ease); }
.btn:active { transform: scale(.97); }
.btn:hover { background: #262d38; }
.mail { padding-bottom: 4px; background: linear-gradient(rgba(13, 17, 23, .3), rgba(13, 17, 23, .3)) 0 100% / 100% 1px no-repeat; font: 400 15px/1.3 var(--sys-body); color: #333b46; transition: color .18s var(--sys-ease); }
.mail:hover { color: var(--sys-graphite); background-image: linear-gradient(var(--sys-graphite), var(--sys-graphite)); }
.base { display: flex; flex-wrap: wrap; align-items: center; gap: 14px 30px; margin-top: 16vh; padding: 26px 0 30px; border-top: 1px solid rgba(13, 17, 23, .12); font: 400 14px/1.4 var(--sys-body); color: #4a5461; }
.base img { display: block; width: 118px; height: auto; margin-right: auto; }
.base nav { display: flex; flex-wrap: wrap; gap: 8px 24px; }
.base a { transition: color .18s var(--sys-ease); }
.base a:hover { color: var(--sys-graphite); }
.close :focus-visible, .caps :focus-visible { outline-color: #2f6f9a; }

@media (max-width: 760px) {
  .r-line { top: 17vh; padding: 0 20px; }
  .panels { flex-direction: column; gap: 8px; }
  .p-in { left: 22px; bottom: 22px; }
  .p-in h3 { font-size: 34px; }
  .p-in p { font-size: 15px; }
}
html.is-light { color-scheme: light; }

/* moving between Systems and its capability pages: the card and the hero are one element across the page load */
.panel { cursor: pointer; }
@view-transition { navigation: auto; }
.bar { view-transition-name: site-bar; }
::view-transition-group(site-bar) { z-index: 3; }
/* the header cross-fades at the page's pace, so its words and the ground behind them change together */
::view-transition-old(site-bar), ::view-transition-new(site-bar) { animation-duration: .5s; animation-timing-function: var(--sys-ease); }
/* the size animation starts a frame before the first frame reaches the screen; a short hold keeps its start on screen */
::view-transition-group(topic-hero) { z-index: 2; animation-duration: .8s; animation-delay: 60ms; animation-fill-mode: both; animation-timing-function: var(--sys-ease); }
/* the carried picture stays inside the box that grows: without the clip a covering image spills past it, and the old and
   new pictures show twice at two sizes with the page through them */
::view-transition-old(topic-hero), ::view-transition-new(topic-hero) { width: 100%; height: 100%; object-fit: cover; overflow: clip; }
::view-transition-new(topic-hero) { object-position: var(--vt-pos, 50% 50%); }
::view-transition-old(topic-hero), ::view-transition-new(topic-hero) { animation-duration: .32s; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .5s; animation-timing-function: var(--sys-ease); }
/* leaving a light header (the overview's light chapter, a light ending) for a dark one: the page and the header's two
   states cross in a quarter second, before any carried picture reaches the top, so the header is never half of each
   for long (a fifth of a second scored worse: the grey middle then landed on three frames) */
html.vt-quick::view-transition-old(root), html.vt-quick::view-transition-new(root), html.vt-quick::view-transition-old(site-bar), html.vt-quick::view-transition-new(site-bar) { animation-duration: .26s; }
@media (prefers-reduced-motion: reduce) { ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation-duration: 0s !important; } }

/* leaving for the rest of the site (assets/page-leave.js): the page dims at
   once under the header, then the page transition carries it out */
body::after { content: ""; position: fixed; inset: 0; z-index: 29; pointer-events: none; background: #020409; opacity: 0; }
html.is-leaving body::after { opacity: .42; transition: opacity .26s cubic-bezier(.2, .7, .3, 1); }
@media (prefers-reduced-motion: reduce) { body::after { display: none; } }

/* leaving with a lit header: its ground moves off the header and onto the page for the transition's snapshot, so the
   header cross-fades on its own (above the page and the growing picture) without showing what was under it */
html.vt-ground .bar::before { opacity: 0 !important; transition: none !important; }
html.vt-restore .bar::before { transition: none !important; }
html.vt-ground body::before { content: ""; position: fixed; left: 0; right: 0; top: 0; height: calc(var(--bar-h, 72px) + 30px); z-index: 28; pointer-events: none; background: linear-gradient(180deg, var(--sys-ice) 0%, var(--sys-ice) 52%, rgba(237, 241, 244, 0) 100%); }
html.vt-ground.is-deep body::before { background: linear-gradient(180deg, var(--b-bg) 0%, var(--b-bg) 52%, rgba(0, 0, 0, 0) 100%); }
