Card faces and the expanded panel now show only title + subtitle. Setup keeps its title; Wave 1/2/3+ become Knowledge, Tools, Agents with updated subtitles. The .rm-mono meta line (e.g. "Once · 1-2 weeks") is removed from all four cards, along with the now-orphan .rm-mono base rule and its is-expanded order override. Card min-height drops to 132px and the column flex now centres the title+subtitle pair so the leaner content sits balanced inside the walnut surface; grid stretch keeps the row equal-height. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1474 lines
44 KiB
CSS
1474 lines
44 KiB
CSS
/* =============================================================
|
||
Fenja AI — "Product Deepdive" page
|
||
A self-contained top-level page (#page-product-deepdive) reached
|
||
via its own dot. Hosts:
|
||
"The Question" — full-viewport framing statement (fade-in)
|
||
"The Layers" — pinned scrubbed five-beat architecture build
|
||
"Choose your Capability" — 4 product cards (final section)
|
||
============================================================= */
|
||
|
||
/* ─── Page scaffold ─────────────────────────────────────────
|
||
#page-product-deepdive is `position: fixed; inset: 0;` via the
|
||
shared `.page` rule. Its child #product-deepdive-scroll is the
|
||
internal scroller — Lenis + ScrollTrigger.scrollerProxy attach
|
||
here so the scrub on Section B works without touching the
|
||
window scroll. Mirrors #overview-scroll's shape. */
|
||
#product-deepdive-scroll {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
background: var(--background);
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(56,56,49,0.18) transparent;
|
||
}
|
||
#product-deepdive-scroll::-webkit-scrollbar { width: 6px; }
|
||
#product-deepdive-scroll::-webkit-scrollbar-thumb {
|
||
background: rgba(56,56,49,0.18);
|
||
border-radius: 3px;
|
||
}
|
||
#product-deepdive-scroll > section { position: relative; z-index: 2; }
|
||
|
||
/* The dot-nav-tray paper-fade reads as a soft footer on the timeline
|
||
page but would interrupt the architecture pin's scrub on the
|
||
deepdive page. Hide it while this page is active, mirroring the
|
||
#page-overview rule. */
|
||
body:has(#page-product-deepdive.is-active) .dot-nav-tray { opacity: 0; }
|
||
|
||
/* ─── Section A: Choose your Capability — final section ───────
|
||
This is the last section of the Deepdive page. min-height + flex
|
||
centring make the cards land vertically centred in the viewport
|
||
when the user scrolls to the page end. */
|
||
#platform-cards {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background: var(--background);
|
||
color: var(--on-surface);
|
||
padding: clamp(2rem, 6vh, 5rem) clamp(2rem, 5vw, 7rem);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: clamp(2.5rem, 6vh, 4rem);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.platform-cards-head {
|
||
text-align: center;
|
||
max-width: var(--content-max);
|
||
}
|
||
|
||
.platform-eyebrow {
|
||
font-family: var(--font-sans);
|
||
font-weight: 500;
|
||
font-size: var(--text-label-md);
|
||
letter-spacing: var(--tracking-wider);
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
margin: 0 0 var(--space-5) 0;
|
||
}
|
||
|
||
.platform-title {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: clamp(2.5rem, 5vw, 3.75rem);
|
||
letter-spacing: -0.022em;
|
||
line-height: 1.1;
|
||
color: var(--on-surface);
|
||
margin: 0;
|
||
}
|
||
.platform-title em { font-style: italic; font-weight: 400; }
|
||
|
||
.platform-card-grid {
|
||
width: 100%;
|
||
max-width: var(--content-max);
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: var(--space-5);
|
||
align-items: stretch;
|
||
}
|
||
|
||
.platform-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--surface-container-lowest);
|
||
color: var(--on-surface);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-ambient);
|
||
padding: var(--space-7) var(--space-6);
|
||
min-height: 320px;
|
||
}
|
||
.platform-card.is-dark {
|
||
background: var(--secondary);
|
||
color: #fffcf7;
|
||
}
|
||
|
||
.platform-card-name {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: clamp(1.75rem, 2.4vw, 2.125rem);
|
||
line-height: 1.05;
|
||
margin: 0 0 var(--space-3) 0;
|
||
color: inherit;
|
||
}
|
||
.platform-card-name em {
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
display: block;
|
||
}
|
||
|
||
.platform-card-tier {
|
||
font-family: var(--font-sans);
|
||
font-weight: 600;
|
||
font-size: var(--text-label-sm);
|
||
letter-spacing: var(--tracking-wider);
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
margin: 0 0 var(--space-5) 0;
|
||
}
|
||
.platform-card.is-dark .platform-card-tier {
|
||
color: rgba(255, 252, 247, 0.65);
|
||
}
|
||
|
||
.platform-card-body {
|
||
font-family: var(--font-sans);
|
||
font-size: var(--text-body-md);
|
||
line-height: var(--leading-relaxed);
|
||
color: inherit;
|
||
margin: 0 0 var(--space-5) 0;
|
||
}
|
||
|
||
.platform-card-includes {
|
||
margin: auto 0 0 0;
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-size: var(--text-body-md);
|
||
color: var(--on-surface-variant);
|
||
}
|
||
.platform-card.is-dark .platform-card-includes {
|
||
color: rgba(255, 252, 247, 0.78);
|
||
}
|
||
|
||
/* Narrow desktop fallback — 4 cards become a 2×2 grid before content
|
||
gets too cramped. Mobile UAs are routed to a separate page entirely
|
||
(see PROJECT.md), so this is the only narrow case to handle. */
|
||
@media (max-width: 960px) {
|
||
.platform-card-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||
}
|
||
|
||
|
||
/* =============================================================
|
||
IMPLEMENTATION ROADMAP — #platform-roadmap
|
||
Four stage cards in a horizontal row, with chevrons between
|
||
each pair, then a continuous cross-cutting band below.
|
||
Mirrors #platform-cards section framing (.platform-cards-head,
|
||
.platform-eyebrow, .platform-title) and pl-card type density
|
||
(sans name + serif italic subtitle + mono meta).
|
||
|
||
Cards use --secondary (walnut) — the deck's primary brand
|
||
accent. The cross-cutting band uses --surface-container so it
|
||
reads as neutral infrastructure, not a fifth card.
|
||
============================================================= */
|
||
#platform-roadmap {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
background: var(--background);
|
||
color: var(--on-surface);
|
||
padding: clamp(2rem, 6vh, 5rem) clamp(2rem, 5vw, 7rem);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: clamp(2rem, 5vh, 3.5rem);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Stage row — four equal cards with a chevron between each pair.
|
||
The chevron lives on .rm-card::after; the last card suppresses
|
||
it so we never trail off with an arrow pointing into the void. */
|
||
.rm-row {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
width: 100%;
|
||
max-width: var(--content-max);
|
||
display: grid;
|
||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||
gap: var(--space-5);
|
||
align-items: stretch;
|
||
}
|
||
|
||
.rm-card {
|
||
position: relative;
|
||
background: var(--secondary);
|
||
color: var(--on-secondary);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-ambient);
|
||
padding: var(--space-6) var(--space-5);
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: var(--space-2);
|
||
min-height: 132px;
|
||
/* Clickable. Hover and focus-visible lift the card so it's
|
||
obvious the surface is interactive; the focus outline uses
|
||
the brand accent and the same offset the rest of the deck
|
||
uses for focus rings. */
|
||
cursor: pointer;
|
||
user-select: none;
|
||
transition: transform 200ms cubic-bezier(0.2, 0, 0, 1),
|
||
box-shadow 200ms cubic-bezier(0.2, 0, 0, 1);
|
||
}
|
||
.rm-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: var(--shadow-float);
|
||
}
|
||
.rm-card:focus-visible {
|
||
outline: 2px solid var(--secondary);
|
||
outline-offset: 4px;
|
||
transform: translateY(-3px);
|
||
box-shadow: var(--shadow-float);
|
||
}
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.rm-card { transition: none; }
|
||
.rm-card:hover,
|
||
.rm-card:focus-visible { transform: none; }
|
||
}
|
||
|
||
/* Chevron between cards. SVG-as-background lives in the gutter
|
||
between this card's right edge and the next card's left edge,
|
||
centered horizontally in that gap. */
|
||
.rm-card::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 100%;
|
||
width: 14px;
|
||
height: 24px;
|
||
/* Translate right by half the gutter so the chevron's centerline
|
||
lands at the gutter midpoint; vertical translate -50% centers
|
||
it on the card's vertical midline. */
|
||
transform: translate(calc((var(--space-5) - 14px) * 0.5), -50%);
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 24' fill='none' stroke='%238a887f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M3 4 L11 12 L3 20'/></svg>");
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 14px 24px;
|
||
pointer-events: none;
|
||
}
|
||
.rm-card:last-child::after { content: none; }
|
||
|
||
.rm-name {
|
||
font-family: var(--font-sans);
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.005em;
|
||
color: inherit;
|
||
margin: 0;
|
||
}
|
||
|
||
.rm-italic {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
font-size: var(--text-body-md);
|
||
line-height: 1.35;
|
||
color: rgba(255, 252, 247, 0.86);
|
||
margin: 0;
|
||
}
|
||
|
||
/* Cross-cutting band — sits underneath the row, spans the same
|
||
width. Paper surface (not walnut) so the eye reads "continuous
|
||
infrastructure" rather than "fifth step". */
|
||
.rm-band {
|
||
width: 100%;
|
||
max-width: var(--content-max);
|
||
background: var(--surface-container);
|
||
color: var(--on-surface);
|
||
border-radius: var(--radius-lg);
|
||
padding: var(--space-5) var(--space-6);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-2);
|
||
box-shadow: var(--shadow-ambient);
|
||
}
|
||
|
||
.rm-band-name {
|
||
font-family: var(--font-sans);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
margin: 0;
|
||
}
|
||
|
||
.rm-band-italic {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
font-size: var(--text-body-md);
|
||
line-height: 1.4;
|
||
color: var(--on-surface);
|
||
margin: 0;
|
||
}
|
||
|
||
.rm-foot {
|
||
width: 100%;
|
||
max-width: var(--content-max);
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
font-size: var(--text-body-sm);
|
||
line-height: 1.4;
|
||
color: var(--on-surface-muted);
|
||
text-align: center;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Narrow desktop — drop to a 2×2 grid of stage cards, same
|
||
breakpoint .platform-card-grid uses. The horizontal chevron
|
||
between columns no longer makes sense in 2×2, so suppress it;
|
||
the band + footer keep their full-width layout. */
|
||
@media (max-width: 960px) {
|
||
.rm-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||
.rm-card::after { content: none; }
|
||
}
|
||
|
||
/* =============================================================
|
||
ROADMAP CARD MORPH — same-element expansion (no modal)
|
||
The clicked card IS the expanded panel — same DOM element,
|
||
same walnut surface, same border-radius. On expand the row
|
||
grid flips from 4-up × 1 row to 6-up × 2 rows: the clicked
|
||
card spans columns 2–5 of row 1 (≈66% of the row's content
|
||
width); the other three cards drop to row 2, each spanning
|
||
2 columns, so they remain visible below. The transition is
|
||
FLIP-driven from initRoadmap in platform.js.
|
||
============================================================= */
|
||
|
||
/* Expanded row layout — six logical columns, two rows.
|
||
Active when one card has .is-expanded; off otherwise. */
|
||
.rm-row.has-expanded {
|
||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||
grid-template-rows: auto auto;
|
||
row-gap: var(--space-6);
|
||
}
|
||
.rm-row.has-expanded .rm-card.is-expanded {
|
||
grid-column: 2 / span 4;
|
||
grid-row: 1;
|
||
}
|
||
.rm-row.has-expanded .rm-card:not(.is-expanded) {
|
||
grid-column: span 2;
|
||
grid-row: 2;
|
||
/* Subtle recede — readable but visually secondary. */
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* Default body / close-button states (collapsed). The body sits
|
||
in the DOM but is removed from layout via display:none so the
|
||
card's collapsed face stays compact. */
|
||
.rm-card-body { display: none; }
|
||
.rm-card-close { display: none; }
|
||
|
||
/* Expanded card — visual treatment. Same brown surface, same
|
||
radius. The walnut accent does not change; only the size, the
|
||
content arrangement, and a stronger drop-shadow change. */
|
||
.rm-card.is-expanded {
|
||
/* Walnut surface stays. Stronger shadow reinforces "floating
|
||
above the row of cards below". */
|
||
box-shadow:
|
||
0 24px 48px -16px rgba(56, 56, 49, 0.20),
|
||
0 6px 16px -6px rgba(56, 56, 49, 0.10);
|
||
padding: var(--space-7) clamp(2rem, 4vw, 3rem);
|
||
cursor: default;
|
||
z-index: 2;
|
||
}
|
||
|
||
/* Suppress the inter-card chevron on the expanded card and any
|
||
neighbour in row 2 — the visual sequence is interrupted while
|
||
one card is featured. */
|
||
.rm-row.has-expanded .rm-card::after { content: none; }
|
||
|
||
/* Expanded body — visible, normal flow inside the card. */
|
||
.rm-card.is-expanded .rm-card-body {
|
||
display: block;
|
||
margin-top: var(--space-5);
|
||
}
|
||
|
||
/* Title + subtitle sit at the top of the expanded panel. Subtitle
|
||
gets a small bottom margin to separate it from the intro. */
|
||
.rm-card.is-expanded .rm-name {
|
||
font-size: clamp(1.5rem, 2.4vw, 2rem);
|
||
margin: 0;
|
||
}
|
||
.rm-card.is-expanded .rm-italic {
|
||
font-size: var(--text-body-lg);
|
||
margin: 0 0 var(--space-2) 0;
|
||
color: rgba(255, 252, 247, 0.86);
|
||
}
|
||
|
||
/* Intro paragraph — serif, lightly literary. Inside the walnut
|
||
card the colour is the card's on-secondary tone, slightly
|
||
softened. */
|
||
.rm-card-intro {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: var(--text-body-lg);
|
||
line-height: var(--leading-relaxed);
|
||
color: rgba(255, 252, 247, 0.92);
|
||
margin: 0 0 var(--space-5) 0;
|
||
}
|
||
|
||
.rm-card-section-label {
|
||
font-family: var(--font-sans);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: rgba(255, 252, 247, 0.62);
|
||
margin: 0 0 var(--space-3) 0;
|
||
}
|
||
|
||
.rm-card-list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
.rm-card-list li {
|
||
font-family: var(--font-sans);
|
||
font-size: var(--text-body-md);
|
||
line-height: 1.45;
|
||
color: rgba(255, 252, 247, 0.94);
|
||
padding-left: 18px;
|
||
position: relative;
|
||
}
|
||
/* Cream bullet against the walnut background. */
|
||
.rm-card-list li::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 4px;
|
||
top: 0.55em;
|
||
width: 5px;
|
||
height: 5px;
|
||
border-radius: 50%;
|
||
background: rgba(255, 252, 247, 0.78);
|
||
}
|
||
|
||
/* Close (×) button — top-right of the expanded card. Same
|
||
visual idiom as the rest of the deck's circular hit targets. */
|
||
.rm-card.is-expanded .rm-card-close {
|
||
display: inline-flex;
|
||
position: absolute;
|
||
top: var(--space-4);
|
||
right: var(--space-4);
|
||
width: 28px;
|
||
height: 28px;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: transparent;
|
||
border: 0;
|
||
border-radius: 999px;
|
||
color: rgba(255, 252, 247, 0.70);
|
||
cursor: pointer;
|
||
transition: background 180ms ease, color 180ms ease;
|
||
}
|
||
.rm-card.is-expanded .rm-card-close:hover,
|
||
.rm-card.is-expanded .rm-card-close:focus-visible {
|
||
background: rgba(255, 252, 247, 0.10);
|
||
color: rgba(255, 252, 247, 0.95);
|
||
}
|
||
.rm-card.is-expanded .rm-card-close:focus-visible {
|
||
outline: 2px solid rgba(255, 252, 247, 0.55);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Reduced motion — let the layout change happen without a FLIP
|
||
transform animation. Cards still toggle states; just no
|
||
translate/scale tweening between them. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.rm-card { transition: opacity 200ms ease !important; }
|
||
.rm-card.is-expanded,
|
||
.rm-row.has-expanded .rm-card { transform: none !important; }
|
||
}
|
||
|
||
/* =============================================================
|
||
"The Question" intro section — first section of the Deepdive
|
||
page. A full-viewport framing statement; fades in on scroll
|
||
like the cards stagger. Plain (not pinned, not scrubbed).
|
||
============================================================= */
|
||
#platform-question {
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: clamp(4rem, 8vh, 8rem) clamp(2rem, 5vw, 7rem);
|
||
background: var(--background);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.pq-wrap {
|
||
width: 100%;
|
||
max-width: var(--reading-max);
|
||
}
|
||
|
||
/* Title — italic serif for the parallel "isn't enough" rejection.
|
||
Sized below the previous hero scale so the longer flowing text
|
||
reads as a sustained statement rather than a billboard. */
|
||
.pq-title {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
font-size: clamp(1.75rem, 3.2vw, 2.5rem);
|
||
letter-spacing: var(--tracking-snug);
|
||
line-height: 1.25;
|
||
color: var(--on-surface);
|
||
margin: 0 0 var(--space-6) 0;
|
||
opacity: 0; /* GSAP animates this; CSS prevents pre-init flash */
|
||
}
|
||
.pq-title em { font-style: italic; font-weight: 400; }
|
||
|
||
/* Body — upright serif for the affirmative resolution. Same size
|
||
as the title so the two parts read as paired voices, not as
|
||
title-and-fineprint. Italic <em> emphasises the key claim
|
||
("platform you control"). */
|
||
.pq-body {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: clamp(1.5rem, 2.6vw, 2rem);
|
||
letter-spacing: var(--tracking-snug);
|
||
line-height: 1.35;
|
||
color: var(--on-surface);
|
||
margin: 0;
|
||
opacity: 0; /* GSAP animates this; CSS prevents pre-init flash */
|
||
}
|
||
.pq-body em { font-style: italic; font-weight: 400; }
|
||
|
||
/* =============================================================
|
||
"The Layers" entry section — pinned scrubbed five-beat build.
|
||
A .pl-pin two-row layout: a static title header and a
|
||
two-column body (copy stage + diagram canvas). Pinned for
|
||
+=500% so five beats can play out at 100%-viewport-per-beat
|
||
(Beats 1–4 assemble the diagram; Beat 5 is the closing
|
||
summary copy panel against the fully assembled diagram). The
|
||
card and layer-wrapper visuals are recreated 1:1 from the
|
||
design handoff (architecture boxes/.../README.md) — pixel
|
||
sizes, letter-spacing, gaps, and radii are taken verbatim.
|
||
============================================================= */
|
||
|
||
/* No fixed height: ScrollTrigger inserts a pin-spacer (~600vh for
|
||
the +=500% pin) inside this section. */
|
||
#platform-layers {
|
||
position: relative;
|
||
width: 100%;
|
||
background: var(--background);
|
||
color: var(--on-surface);
|
||
}
|
||
|
||
/* The pin: a vertical flex stack — header (title), body
|
||
(two-column copy + canvas), footer (closing caption). The body
|
||
takes remaining height and centres its two columns. */
|
||
.pl-pin {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
/* Top padding sets where the header sits. The body below fills
|
||
the remaining height and centres the diagram, so a larger
|
||
top padding both lowers the header and tightens the gap to
|
||
the boxes (gap shrinks by ~half the padding bump). */
|
||
padding: clamp(5rem, 12vh, 9rem) clamp(2rem, 5vw, 7rem) clamp(1rem, 2vh, 2rem);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.pl-pin-header {
|
||
flex: 0 0 auto;
|
||
text-align: center;
|
||
margin-bottom: clamp(1rem, 2.5vh, 2rem);
|
||
}
|
||
|
||
.pl-pin-title {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: clamp(1.625rem, 2.6vw, 2.125rem);
|
||
letter-spacing: var(--tracking-snug);
|
||
line-height: 1.15;
|
||
color: var(--on-surface);
|
||
margin: 0 0 var(--space-2) 0;
|
||
}
|
||
|
||
.pl-pin-subtitle {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
font-size: clamp(0.95rem, 1.4vw, 1.125rem);
|
||
color: var(--on-surface-variant);
|
||
margin: 0;
|
||
}
|
||
|
||
.pl-pin-body {
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: clamp(2rem, 5vw, 6rem);
|
||
}
|
||
|
||
/* Copy stage — flex item that takes remaining width up to the
|
||
reading max. Each .pl-copy-step is absolutely positioned within
|
||
the stage; vertical centring keeps shorter beats (Beats 1, 3)
|
||
reading as deliberate panels rather than top-anchored fragments. */
|
||
.pl-copy-stage {
|
||
position: relative;
|
||
flex: 1 1 0;
|
||
min-width: 0;
|
||
max-width: var(--reading-max);
|
||
min-height: 320px;
|
||
}
|
||
|
||
.pl-copy-step {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
opacity: 0; /* GSAP animates this; CSS prevents pre-init flash */
|
||
}
|
||
|
||
/* Diagram canvas — three layer slots in their final vertical
|
||
positions from the start, regardless of which beats have fired.
|
||
This is the "settled-elements-never-move" guarantee: each layer
|
||
reveals in place rather than growing from zero, so neither
|
||
Foundation nor Tools is ever pushed to make room. */
|
||
.pl-canvas-wrap {
|
||
flex: 0 1 auto;
|
||
width: clamp(440px, 50vw, 700px);
|
||
min-width: 0;
|
||
}
|
||
|
||
.pl-canvas {
|
||
position: relative; /* anchors .pl-canvas-frame */
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px; /* layer-to-layer gap, per design spec */
|
||
}
|
||
|
||
/* Beat-5 frame — a thin outline around the three settled layer
|
||
groups with a label sitting above the top-right corner. Initial
|
||
opacity 0 so it's invisible during Beats 1–4; platform.js fades
|
||
it in on Beat 5. The negative inset gives breathing room between
|
||
the existing card-group surfaces and the outline. */
|
||
.pl-canvas-frame {
|
||
position: absolute;
|
||
inset: -18px;
|
||
border: 1px solid var(--outline-variant, rgba(56,56,49,0.22));
|
||
border-radius: calc(var(--radius-lg) + 6px);
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
}
|
||
|
||
.pl-canvas-frame-label {
|
||
position: absolute;
|
||
bottom: 100%; /* sits above the top edge */
|
||
right: 0;
|
||
margin-bottom: 8px;
|
||
font-family: var(--font-sans);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.pl-eyebrow {
|
||
font-family: var(--font-sans);
|
||
font-weight: 500;
|
||
font-size: var(--text-label-md);
|
||
letter-spacing: var(--tracking-wider);
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
margin: 0 0 var(--space-5) 0;
|
||
}
|
||
|
||
.pl-headline {
|
||
font-family: var(--font-serif);
|
||
font-weight: 400;
|
||
font-size: clamp(1.875rem, 3.4vw, 2.75rem);
|
||
letter-spacing: -0.02em;
|
||
line-height: 1.1;
|
||
color: var(--on-surface);
|
||
margin: 0 0 var(--space-6) 0;
|
||
}
|
||
.pl-headline em { font-style: italic; font-weight: 400; }
|
||
|
||
.pl-body {
|
||
font-family: var(--font-sans);
|
||
font-size: var(--text-body-md);
|
||
line-height: var(--leading-relaxed);
|
||
color: var(--on-surface);
|
||
margin: 0;
|
||
}
|
||
.pl-body em { font-style: italic; }
|
||
|
||
/* ─── Architecture-layer visual (right column) ──────────────
|
||
Pixel values below come straight from the design handoff
|
||
README. Do not relax them to tokens — the spec is explicit. */
|
||
.pl-group {
|
||
background: var(--surface-container);
|
||
border-radius: var(--radius-lg); /* 20px */
|
||
padding: 24px 24px 28px;
|
||
}
|
||
|
||
.pl-group-head {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
margin-bottom: 18px;
|
||
padding-left: 4px;
|
||
gap: var(--space-4);
|
||
}
|
||
|
||
.pl-group-label {
|
||
font-family: var(--font-sans);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
}
|
||
|
||
.pl-group-caption {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-size: 14px;
|
||
color: var(--on-surface-muted);
|
||
}
|
||
|
||
.pl-cards {
|
||
display: grid;
|
||
gap: 14px;
|
||
}
|
||
.pl-cards--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||
.pl-cards--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||
.pl-cards--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
||
.pl-cards--start { align-items: start; }
|
||
.pl-cards--stretch { align-items: stretch; }
|
||
|
||
.pl-card {
|
||
background: var(--surface-container-lowest);
|
||
border-radius: var(--radius-md); /* 12px */
|
||
padding: 18px 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
min-height: 76px;
|
||
box-shadow: 0 1px 0 rgba(56,56,49,0.04), 0 8px 20px -16px rgba(56,56,49,0.18);
|
||
}
|
||
|
||
.pl-card-name {
|
||
font-family: var(--font-sans);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.005em;
|
||
color: var(--on-surface);
|
||
margin: 0 0 2px 0;
|
||
}
|
||
|
||
.pl-card-italic {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-size: 13px;
|
||
line-height: 1.35;
|
||
color: var(--on-surface-variant);
|
||
margin: 0;
|
||
}
|
||
|
||
.pl-card-mono {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.04em;
|
||
line-height: 1.4;
|
||
color: var(--on-surface-muted);
|
||
margin: 0;
|
||
}
|
||
|
||
/* Narrow desktop: drop Tools/Agents to a 2-col grid before content
|
||
gets too cramped. Same breakpoint .platform-card-grid uses.
|
||
Mobile UAs go to a separate page entirely (PROJECT.md), so this
|
||
is the only narrow case to handle. */
|
||
@media (max-width: 960px) {
|
||
.pl-cards--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||
}
|
||
|
||
/* =============================================================
|
||
WIKI DEEP-DIVE — #wiki-deepdive
|
||
Pinned scrubbed five-beat section, mirroring #platform-layers's
|
||
structure: a .pl-pin-style shell (.wd-pin) with the reused
|
||
.pl-pin-header for title/subtitle, and a three-column .wd-body
|
||
below. platform.js (initWiki) drives the beats.
|
||
|
||
Visual contract:
|
||
• LEFT (scatter) — muted, jittered, "messy"
|
||
• MIDDLE (compiler) — walnut/secondary accent (only "active"
|
||
element on the slide; reads as configurable engine)
|
||
• RIGHT (wiki mock) — calm, neutral; inline <sup> citations
|
||
use the same walnut accent so the trust thread (Beat 4)
|
||
has a colour to follow back to the source
|
||
============================================================= */
|
||
#wiki-deepdive {
|
||
position: relative;
|
||
width: 100%;
|
||
background: var(--background);
|
||
color: var(--on-surface);
|
||
}
|
||
|
||
/* Pin shell — vertical stack: header at top, action band centred
|
||
vertically below. The band has explicit height (58vh) so all
|
||
three zones share a top edge, a bottom edge, and a centreline.
|
||
Header gets its own row via grid; body row takes the rest and
|
||
centres the action band inside it. */
|
||
.wd-pin {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100vh;
|
||
display: grid;
|
||
grid-template-rows: auto 1fr;
|
||
align-items: start;
|
||
padding: clamp(5rem, 12vh, 9rem) clamp(2rem, 5vw, 7rem) clamp(1.5rem, 3vh, 2.5rem);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.wd-pin .pl-pin-header { grid-row: 1; justify-self: center; }
|
||
.wd-pin .pl-pin-title em { font-style: italic; font-weight: 400; }
|
||
|
||
/* Beat-0 anchor — the architecture grid's Wiki card, scaled up
|
||
and centered over the action band. platform.js holds it at
|
||
opacity 1 / scale 1.6 during Beat 0 and fades it down as Beat 1
|
||
reveals the band. The pl-card visual is identical to the one
|
||
in #platform-layers; same DOM class, same tokens. */
|
||
.wd-anchor {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: clamp(220px, 22vw, 320px);
|
||
transform: translate(-50%, -50%) scale(1);
|
||
pointer-events: none;
|
||
z-index: 4;
|
||
opacity: 0; /* GSAP fades this in for Beat 0 */
|
||
}
|
||
.wd-anchor .pl-card { box-shadow: var(--shadow-float); }
|
||
|
||
/* Action band — the shared 55–60vh strip. Five-column grid:
|
||
three zones with two small chevron columns sitting in the
|
||
gaps. The chevrons take their natural width; outer gap is
|
||
wider than before so the compiler reads as an equal-weight
|
||
peer of the cluster and stack, with real whitespace on both
|
||
sides instead of crowding either neighbour. */
|
||
.wd-body {
|
||
grid-row: 2;
|
||
align-self: center;
|
||
justify-self: center;
|
||
width: 100%;
|
||
max-width: var(--content-max);
|
||
height: clamp(360px, 58vh, 620px);
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(0, 1.15fr) auto
|
||
minmax(0, 0.78fr) auto
|
||
minmax(0, 1.15fr);
|
||
/* +20% horizontal whitespace between zones from the previous
|
||
`clamp(2.25rem, 5vw, 5rem)`. Wider gutters reinforce the
|
||
three zones as distinct movements rather than a continuous
|
||
band, and give the chevrons more room to breathe between
|
||
them. */
|
||
gap: clamp(2.7rem, 6vw, 6rem);
|
||
align-items: stretch;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
/* Chevron between zones — same geometric mark used in
|
||
#platform-roadmap. Vertically centred via align-self; sized
|
||
below the body text so it reads as a hint, not a sign. */
|
||
.wd-chevron {
|
||
align-self: center;
|
||
width: 14px;
|
||
height: 24px;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 24' fill='none' stroke='%238a887f' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M3 4 L11 12 L3 20'/></svg>");
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 14px 24px;
|
||
opacity: 0.55;
|
||
}
|
||
|
||
/* Zone column. Top section (labels) is auto-height; the visual
|
||
below fills remaining height. A consistent .wd-zone-head wrapper
|
||
keeps the eyebrow/name lines aligned across all three columns. */
|
||
.wd-zone {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
/* Shared top-label block. Reserves enough height for an optional
|
||
sub-caption line (used by the Fenja Wiki zone) so all three
|
||
columns hand off to their visual at the same Y baseline. */
|
||
.wd-zone-eyebrow {
|
||
font-family: var(--font-sans);
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
margin: 0;
|
||
}
|
||
|
||
.wd-zone-name {
|
||
font-family: var(--font-sans);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.005em;
|
||
color: var(--on-surface);
|
||
margin: 2px 0 0 0;
|
||
}
|
||
|
||
/* Sub-caption directly under the zone name (used by Fenja Wiki
|
||
and Compiler). Italic serif, muted — matches the rest of the
|
||
deck's secondary captions. Reserved baseline-height block so
|
||
zones without a sub-caption keep the same handoff Y. */
|
||
.wd-zone-sub {
|
||
font-family: var(--font-serif);
|
||
font-style: italic;
|
||
font-size: 12.5px;
|
||
line-height: 1.4;
|
||
color: var(--on-surface-muted);
|
||
margin: 4px 0 0 0;
|
||
min-height: 1em;
|
||
}
|
||
|
||
/* ─── LEFT: Scattered knowledge ───────────────────────────── */
|
||
.wd-scatter {
|
||
position: relative;
|
||
flex: 1 1 auto;
|
||
margin-top: var(--space-3);
|
||
overflow: hidden; /* bound the cluster strictly inside */
|
||
}
|
||
|
||
/* Icons absolutely positioned via inline --tx / --ty / --r / --s.
|
||
--tx/--ty are top-left offsets in % of the scatter zone — the
|
||
cluster stays inside the action band rather than spreading
|
||
floor-to-ceiling like the previous arrangement. */
|
||
.wd-doc {
|
||
position: absolute;
|
||
left: var(--tx, 0);
|
||
top: var(--ty, 0);
|
||
transform: rotate(var(--r, 0deg)) scale(var(--s, 1));
|
||
transform-origin: top left;
|
||
color: var(--on-surface-muted);
|
||
display: block;
|
||
width: clamp(46px, 4.6vw, 66px);
|
||
/* --o is the per-icon opacity target (1 = foreground, ~0.45
|
||
= background pile). Read by initWiki at reveal-time so the
|
||
stagger fades each icon to its own opacity, not all to 1. */
|
||
opacity: var(--o, 1);
|
||
filter: drop-shadow(0 4px 6px rgba(56, 56, 49, 0.05));
|
||
transition: color 280ms ease, filter 280ms ease;
|
||
}
|
||
.wd-doc svg { display: block; width: 100%; height: auto; }
|
||
.wd-doc--slide { width: clamp(60px, 6vw, 86px); }
|
||
.wd-doc--tacit { width: clamp(58px, 5.8vw, 84px); }
|
||
.wd-doc--note { width: clamp(42px, 4.2vw, 58px); }
|
||
.wd-doc--mail { width: clamp(54px, 5.4vw, 76px); }
|
||
|
||
/* Trust-beat source-tint: the originating document on the left
|
||
subtly lifts toward charcoal + a tiny upscale. platform.js
|
||
toggles .is-source on the matching .wd-doc at Beat 4. */
|
||
.wd-doc.is-source {
|
||
color: var(--on-surface);
|
||
filter: drop-shadow(0 6px 10px rgba(56, 56, 49, 0.10));
|
||
}
|
||
|
||
/* ─── MIDDLE: Fenja AI Compiler ───────────────────────────── */
|
||
/* The compiler zone overrides the default flex layout used by
|
||
the other zones so the card can be locked to the zone's exact
|
||
vertical centre (matching the chevron midline on either side).
|
||
The labels are grouped in .wd-compiler-head and anchored just
|
||
above the card so their spacing follows it. */
|
||
.wd-zone--compiler {
|
||
position: relative;
|
||
display: block;
|
||
text-align: center;
|
||
}
|
||
.wd-zone--compiler .wd-zone-eyebrow,
|
||
.wd-zone--compiler .wd-zone-name,
|
||
.wd-zone--compiler .wd-zone-sub {
|
||
text-align: center;
|
||
}
|
||
|
||
/* Label group anchored at the TOP of the zone so the title sits
|
||
on the same horizontal baseline as the eyebrows in the other
|
||
two zones (Scattered knowledge / Structured output). The card
|
||
below is still locked to zone-centre via absolute positioning
|
||
— the resulting gap between the title block and the card is
|
||
intentional.
|
||
Flex + gap match the other zones' .wd-zone container so the
|
||
internal rhythm between eyebrow → name → sub is the same as
|
||
the left and right columns. */
|
||
.wd-compiler-head {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 100%;
|
||
max-width: 280px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3);
|
||
}
|
||
.wd-compiler-head .wd-zone-eyebrow { margin: 0; }
|
||
.wd-compiler-head .wd-zone-name { margin: 2px 0 0 0; }
|
||
.wd-compiler-head .wd-zone-sub { margin: 4px 0 0 0; min-height: 1em; }
|
||
|
||
/* Rules card — outline + paper fill. Locked to the zone's
|
||
vertical centre via absolute positioning so its midline
|
||
aligns exactly with the chevrons in the gutters on either
|
||
side (both centred in the same grid row). */
|
||
.wd-compiler {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
transform: translate(-50%, -50%);
|
||
width: 100%;
|
||
max-width: 240px;
|
||
margin: 0;
|
||
padding: var(--space-4) var(--space-5);
|
||
background: var(--surface-container-lowest);
|
||
color: var(--on-surface);
|
||
border: 1px solid var(--outline-variant);
|
||
border-radius: var(--radius-lg);
|
||
box-shadow: var(--shadow-ambient);
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: var(--space-3);
|
||
}
|
||
|
||
.wd-compiler-label {
|
||
font-family: var(--font-sans);
|
||
font-size: 10px;
|
||
font-weight: 600;
|
||
letter-spacing: 0.18em;
|
||
text-transform: uppercase;
|
||
color: var(--on-surface-variant);
|
||
}
|
||
|
||
.wd-compiler-rules {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 11px;
|
||
width: 100%;
|
||
flex: 0 0 auto;
|
||
}
|
||
.wd-compiler-rules li {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.wd-rule-toggle {
|
||
width: 22px;
|
||
height: 12px;
|
||
border-radius: 999px;
|
||
background: var(--surface-container);
|
||
border: 1px solid var(--outline-variant);
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
}
|
||
.wd-rule-toggle::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 1px;
|
||
left: 1px;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--on-surface-muted);
|
||
transition: left 200ms ease, background 200ms ease;
|
||
}
|
||
.wd-rule-toggle.is-on {
|
||
background: var(--surface-container-high);
|
||
}
|
||
.wd-rule-toggle.is-on::after {
|
||
left: 12px;
|
||
background: var(--on-surface);
|
||
}
|
||
.wd-rule-line {
|
||
flex: 1 1 auto;
|
||
height: 1px;
|
||
background: var(--outline-variant);
|
||
}
|
||
|
||
/* ─── RIGHT: Abstract layered page stack ───────────────────
|
||
Three page-shaped cards stacked with depth. Each subsequent
|
||
card is offset toward the TOP-RIGHT of the one behind (≈60%
|
||
overlap), so the eye reads back → mid → front. The frontmost
|
||
card is sharp; cards behind are progressively blurred — as if
|
||
each layer in front fogs the cards beneath. */
|
||
.wd-stack {
|
||
position: relative;
|
||
flex: 1 1 auto;
|
||
margin-top: var(--space-3);
|
||
/* Card sizing reflects two reductions from the previous
|
||
pass:
|
||
Step A — internal whitespace between body and source list
|
||
is halved (no `margin-top: auto` on the source
|
||
list; tighter divider margins), giving a ~20%
|
||
height reduction without changing block sizes.
|
||
Step B — the whole stack is then reduced by a further
|
||
~30% in both dimensions.
|
||
Combined: roughly 0.8 × 0.7 = 0.56× the previous size. */
|
||
--wd-card-w: clamp(140px, 12vw, 200px);
|
||
--wd-card-h: clamp(160px, 20vh, 225px);
|
||
/* Per-card shift tightened to ~22% (≈78% overlap) so the
|
||
three cards read as one thing with depth rather than three
|
||
drifting pages. Same top-right offset direction; the back-
|
||
blur depth effect is unchanged. */
|
||
--wd-card-step-x: 22%;
|
||
--wd-card-step-y: 22%;
|
||
}
|
||
|
||
.wd-stack-card {
|
||
position: absolute;
|
||
width: var(--wd-card-w);
|
||
height: var(--wd-card-h);
|
||
background: var(--surface-container-lowest);
|
||
border: 1px solid var(--outline-variant);
|
||
border-radius: var(--radius-md);
|
||
box-shadow:
|
||
0 6px 14px -8px rgba(56, 56, 49, 0.10),
|
||
0 2px 6px -3px rgba(56, 56, 49, 0.06);
|
||
padding: 11px 11px 11px 22px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.wd-stack-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 5px;
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
}
|
||
|
||
/* Per-depth offsets. The back card sits in the bottom-left, the
|
||
middle card shifts ~36% right & up, the front card another
|
||
~36% right & up — total stack runs upper-right. */
|
||
.wd-stack-card[data-depth="back"] {
|
||
left: 0;
|
||
top: calc(var(--wd-card-step-y) * 2);
|
||
z-index: 1;
|
||
}
|
||
.wd-stack-card[data-depth="mid"] {
|
||
left: var(--wd-card-step-x);
|
||
top: var(--wd-card-step-y);
|
||
z-index: 2;
|
||
}
|
||
.wd-stack-card[data-depth="front"] {
|
||
left: calc(var(--wd-card-step-x) * 2);
|
||
top: 0;
|
||
z-index: 3;
|
||
}
|
||
|
||
/* Thin vertical rail on the left edge — abstract sidebar/nav,
|
||
not labelled tabs. Three short ticks sit on the rail. */
|
||
.wd-stack-rail {
|
||
position: absolute;
|
||
top: 13px;
|
||
bottom: 13px;
|
||
left: 11px;
|
||
width: 1px;
|
||
background: var(--outline-variant);
|
||
}
|
||
.wd-stack-rail::before,
|
||
.wd-stack-rail::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: -3px;
|
||
width: 7px;
|
||
height: 1.2px;
|
||
background: var(--on-surface-muted);
|
||
opacity: 0.55;
|
||
}
|
||
.wd-stack-rail::before { top: 12px; }
|
||
.wd-stack-rail::after { top: 30px; }
|
||
|
||
/* Title bar — short underline-style block at the top of each
|
||
page card. Thicker and darker than body blocks below. */
|
||
.wd-stack-title-bar {
|
||
display: block;
|
||
height: 5px;
|
||
width: 58%;
|
||
background: var(--on-surface);
|
||
border-radius: 2px;
|
||
opacity: 0.55;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
/* Subtitle — shorter darker block between the two body
|
||
sections. Smaller than the title bar; reads as an H2. */
|
||
.wd-stack-subhead {
|
||
display: block;
|
||
height: 4px;
|
||
width: 34%;
|
||
background: var(--on-surface);
|
||
border-radius: 2px;
|
||
opacity: 0.42;
|
||
margin-top: 2px;
|
||
margin-bottom: 1px;
|
||
}
|
||
|
||
/* Body line — a flex row of inline pieces (blocks, brackets,
|
||
citations). Keeps everything on a baseline; spacing between
|
||
pieces is the gap. */
|
||
.wd-stack-line {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
position: relative;
|
||
}
|
||
|
||
/* Body block — one inline run of "text". Width comes from a
|
||
per-instance --w custom prop so each line reads as varying
|
||
prose. The default keeps a fallback if --w isn't set. */
|
||
.wd-stack-block {
|
||
display: block;
|
||
height: 3.5px;
|
||
width: var(--w, 100%);
|
||
background: var(--surface-container-high);
|
||
border-radius: 999px;
|
||
flex: 0 0 auto;
|
||
}
|
||
.wd-stack-block--short { width: 64%; }
|
||
|
||
/* Wiki-style internal-link marker — a proper square-bracket
|
||
pair enclosing a short horizontal block. Renders as `[ ── ]`,
|
||
reading as an inline link to another subject. The bracket
|
||
characters come from ::before / ::after; the inner block is
|
||
a real child element so per-instance --bw varies its width. */
|
||
.wd-stack-bracket {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 2px;
|
||
flex: 0 0 auto;
|
||
font-family: var(--font-mono);
|
||
font-size: 10px;
|
||
line-height: 1;
|
||
color: var(--on-surface-muted);
|
||
padding: 0 1px;
|
||
}
|
||
.wd-stack-bracket::before { content: "["; opacity: 0.75; }
|
||
.wd-stack-bracket::after { content: "]"; opacity: 0.75; }
|
||
.wd-stack-bracket > span {
|
||
display: inline-block;
|
||
height: 3.5px;
|
||
width: var(--bw, 14px);
|
||
background: var(--surface-container-high);
|
||
border-radius: 999px;
|
||
}
|
||
|
||
/* Citation markers sit inline at the trailing edge of the
|
||
block/segment they cite. Walnut so they thread the trust
|
||
beat. */
|
||
.wd-cite {
|
||
font-family: var(--font-mono);
|
||
font-size: 7.5px;
|
||
font-weight: 600;
|
||
color: var(--secondary);
|
||
line-height: 1;
|
||
padding: 0 1px;
|
||
border-radius: 3px;
|
||
transition: color 220ms ease, transform 220ms ease;
|
||
display: inline-block;
|
||
margin-left: 1px;
|
||
position: relative;
|
||
top: -2px;
|
||
}
|
||
.wd-cite.is-lit {
|
||
/* Soft walnut pill behind the lit marker. Subtle by design —
|
||
pairs with the scale pulse + source highlight + arc draw
|
||
to register without dominating. */
|
||
background: rgba(120, 95, 83, 0.16);
|
||
padding: 1px 3px;
|
||
}
|
||
|
||
/* Thin divider — separates the body from the source list at
|
||
the bottom of each card. Step A from the size-reduction pass
|
||
halved the whitespace here, bringing the body and source
|
||
list into close contact (modest gap on either side of the
|
||
divider, no auto-margin pushing the sources to the bottom). */
|
||
.wd-stack-divider {
|
||
border: 0;
|
||
height: 1px;
|
||
background: var(--outline-variant);
|
||
margin: 3px 0 2px 0;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Source list — three numbered horizontal lines below the
|
||
divider. Sits directly under the divider (no auto top
|
||
margin) so the bottom of the card is tight rather than
|
||
reserving empty space. data-source on each entry pairs it
|
||
with an in-text citation so Beat 4 highlights the matching
|
||
bottom entry alongside the lit inline marker. */
|
||
.wd-stack-sources {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
}
|
||
.wd-stack-source {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
transition: opacity 220ms ease;
|
||
}
|
||
.wd-stack-source sup {
|
||
font-family: var(--font-mono);
|
||
font-size: 7px;
|
||
font-weight: 600;
|
||
color: var(--on-surface-muted);
|
||
line-height: 1;
|
||
min-width: 5px;
|
||
}
|
||
.wd-stack-source-line {
|
||
flex: 1 1 auto;
|
||
height: 1px;
|
||
background: var(--surface-container-high);
|
||
border-radius: 999px;
|
||
max-width: 70%;
|
||
}
|
||
/* Beat-4 highlight on the source entry that pairs with the
|
||
lit citation. Subtle tint — kept low-key so the lit marker
|
||
above the divider stays the focus. */
|
||
.wd-stack-source.is-paired sup {
|
||
color: var(--secondary);
|
||
}
|
||
.wd-stack-source.is-paired .wd-stack-source-line {
|
||
background: var(--secondary);
|
||
opacity: 0.55;
|
||
}
|
||
|
||
/* Narrow desktop — single-column stack so the left → middle →
|
||
right narrative direction is preserved. Same breakpoint other
|
||
sections use. Action band height + anchor are disabled in
|
||
this mode, and the compiler's absolute positioning (used to
|
||
lock to the chevron midline on wider screens) collapses back
|
||
to natural flow. */
|
||
@media (max-width: 960px) {
|
||
.wd-body {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: var(--space-8);
|
||
height: auto;
|
||
}
|
||
.wd-chevron { display: none; }
|
||
.wd-anchor { width: 200px; }
|
||
.wd-scatter { min-height: 220px; }
|
||
.wd-stack { min-height: 280px; }
|
||
.wd-zone--compiler { display: flex; flex-direction: column; }
|
||
.wd-compiler-head {
|
||
position: static;
|
||
transform: none;
|
||
max-width: none;
|
||
}
|
||
.wd-compiler {
|
||
position: static;
|
||
transform: none;
|
||
margin: var(--space-4) auto 0;
|
||
}
|
||
}
|
||
|
||
|
||
/* Reduced motion — release the pin entirely, stack header, all
|
||
five text panels, and the fully assembled diagram vertically.
|
||
platform.js mirrors this gate. The .pq-* opacity:1 lines belong
|
||
to #platform-question (above) but the GSAP fade-in init also
|
||
applies there, so they're paired here for the same reason. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.pq-title, .pq-body { opacity: 1; }
|
||
|
||
#platform-layers .pl-pin {
|
||
height: auto;
|
||
gap: var(--space-10);
|
||
padding: var(--space-12) clamp(2rem, 5vw, 7rem);
|
||
}
|
||
#platform-layers .pl-pin-body {
|
||
flex-direction: column;
|
||
gap: var(--space-10);
|
||
}
|
||
#platform-layers .pl-copy-stage {
|
||
position: relative;
|
||
min-height: 0;
|
||
max-width: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-7);
|
||
}
|
||
#platform-layers .pl-copy-step {
|
||
position: relative;
|
||
opacity: 1;
|
||
}
|
||
#platform-layers .pl-canvas-wrap { width: 100%; }
|
||
#platform-layers .pl-group { opacity: 1; }
|
||
#platform-layers .pl-card { opacity: 1; transform: none; }
|
||
#platform-layers .pl-canvas-frame { opacity: 1; }
|
||
|
||
/* Wiki deep-dive: release the pin, stack columns, show all
|
||
content + flow lines in their final composed state. Back-
|
||
blur on the stack is also disabled here per the brief:
|
||
reduced motion gets the final composed state, no staged
|
||
blur animation. */
|
||
#wiki-deepdive .wd-pin {
|
||
height: auto;
|
||
padding: var(--space-12) clamp(2rem, 5vw, 7rem);
|
||
gap: var(--space-8);
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
#wiki-deepdive .wd-anchor { display: none; }
|
||
#wiki-deepdive .wd-body {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: var(--space-8);
|
||
height: auto;
|
||
}
|
||
#wiki-deepdive .wd-cite { color: var(--secondary); }
|
||
#wiki-deepdive .wd-stack-card { filter: none !important; }
|
||
/* Release the compiler's absolute positioning so labels and
|
||
the card stack naturally below each other when the pin is
|
||
released. */
|
||
#wiki-deepdive .wd-zone--compiler {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
#wiki-deepdive .wd-compiler-head {
|
||
position: static;
|
||
transform: none;
|
||
max-width: none;
|
||
}
|
||
#wiki-deepdive .wd-compiler {
|
||
position: static;
|
||
transform: none;
|
||
margin: var(--space-4) auto 0;
|
||
}
|
||
}
|