/* ============================================================= 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; gap: var(--space-2); min-height: 168px; /* 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,"); 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; } .rm-mono { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em; line-height: 1.4; color: rgba(255, 252, 247, 0.62); margin: auto 0 0 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); } /* Meta moves to the TOP of the expanded panel via flex order; the natural margin-auto that pushed it to the bottom in the collapsed face is reset to zero. */ .rm-card.is-expanded .rm-mono { order: -1; margin: 0 0 var(--space-2) 0; } /* Title + subtitle stay in DOM order below the meta. 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 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 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,"); 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; } }