mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-11 01:51:34 -10:00
refactor(web): split globals.css into per-surface partials
globals.css (3,828 lines) becomes an import hub over app/styles/*.css, cut at existing section boundaries in the original order: tailwind, tokens-roles, base, shell, utilities, portal, home, docs, states, docs-help, changelog, overrides. Pure move; no rule edited. Tailwind expands each CSS file separately and appends variant utilities (hover:, md:, ...) at the end of whichever file holds `@tailwind utilities`, which moved them ahead of every site rule. postcss.config now runs postcss-import (already a tailwindcss@3 dependency, hoisted in package-lock) for ./styles/ imports so Tailwind sees one stylesheet again. tokens.css stays a separate module as before. The contract tests and whale-tokens.ts read the stylesheet through a new lib/site-css.ts, which inlines the partials in cascade order. Checks: - Built CSS, back-to-back A/B/A builds (original / split / original): split output byte-identical to original (same md5 and chunk hash). - next build: exit 0 - npm run lint: 0 errors, 2 pre-existing warnings - npx tsc --noEmit: exit 0 - vitest (docs-theme-contract, blue-stage-contract, docs-ia, nav-hit-target, public-surface-contract): 5 files, 43/43 passed - npm run check:tokens: up to date (122 tokens) Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
1c4edf6839
commit
28754c4c2b
21 files changed
+3870
-3833
No files matched your search
+17
-3824
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,326 @@
|
||||
/* ---------- base ---------- */
|
||||
html {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
max-width: 100%;
|
||||
overflow-x: clip;
|
||||
}
|
||||
|
||||
body {
|
||||
/* The sheet: one opaque paper field. The descent into the water is drawn
|
||||
by the waterline band and the ocean column, never by a page gradient. */
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
font-family: var(--font-body), "Noto Sans SC", system-ui, sans-serif;
|
||||
font-feature-settings: "ss01", "cv11", "tnum";
|
||||
max-width: 100%;
|
||||
overflow-x: clip;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.codewhale-mark-primary { fill: var(--mark-ink); }
|
||||
|
||||
main, header, footer, nav { position: relative; z-index: 1; }
|
||||
|
||||
/* ---------- type ---------- */
|
||||
/* The folio voice: Newsreader for the big headings, set at book weight with
|
||||
a little negative tracking so it reads as a title page rather than a
|
||||
blog. Small headings use the shared Shannon Sans label face. */
|
||||
.font-display { font-family: var(--font-serif), "Newsreader", Georgia, "Times New Roman", serif; font-weight: 500; }
|
||||
.font-serif { font-family: var(--font-serif), "Newsreader", Georgia, "Times New Roman", serif; }
|
||||
.font-condensed { font-family: var(--font-display), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; font-weight: 600; }
|
||||
.font-cjk { font-family: var(--font-cjk), "PingFang SC", "Source Han Serif SC", serif; }
|
||||
|
||||
/* CJK paragraph rhythm — looser leading, wider tracking for body; tighter for headings */
|
||||
.cjk-body {
|
||||
line-height: 1.9;
|
||||
letter-spacing: 0.02em;
|
||||
word-break: break-all;
|
||||
}
|
||||
.cjk-heading {
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.cjk-prose p {
|
||||
line-height: 1.9;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
/* Full-width punctuation should use CJK spacing */
|
||||
.cjk-prose {
|
||||
font-feature-settings: "halt", "pwid";
|
||||
}
|
||||
.font-mono { font-family: var(--font-mono), "IBM Plex Mono", ui-monospace, monospace; }
|
||||
|
||||
/* ---------- the ` · ` chain ---------- */
|
||||
/* The single most recognisable thing about the TUI — its header, empty state
|
||||
and footer all speak in dot chains — and the grammar the product's voice is
|
||||
carried by on this site, on both sides of the waterline. The separator is
|
||||
punctuation emitted by CSS, never copy: no locale translates it, and no
|
||||
string is ever concatenated around one. No uppercase and no wide tracking:
|
||||
the TUI header has neither, and both are what break Han. */
|
||||
.dotline {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
font-family: var(--font-mono), "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/* The separator hangs off the END of the preceding item, not the start of the
|
||||
next one. When a long chain wraps, a line ending in `·` reads as
|
||||
continuation; a line beginning with one reads as a bullet list. */
|
||||
.dotline > *:not(:last-child)::after {
|
||||
content: "·";
|
||||
margin-inline: 0.5em;
|
||||
color: var(--stage-dim);
|
||||
}
|
||||
|
||||
html[lang="zh"] .dotline,
|
||||
html[lang="ja"] .dotline,
|
||||
html[lang="ko"] .dotline {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, "Times New Roman", serif;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.022em;
|
||||
color: var(--ink);
|
||||
text-wrap: balance;
|
||||
}
|
||||
h3, h4 {
|
||||
font-family: var(--font-display), ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
h1 { font-size: clamp(2.5rem, 5.6vw, 5rem); line-height: 1.02; word-break: keep-all; overflow-wrap: anywhere; }
|
||||
h2 { font-size: clamp(1.6rem, 2.9vw, 2.6rem); line-height: 1.1; word-break: keep-all; overflow-wrap: anywhere; }
|
||||
h3, h4 { font-size: 1.12rem; line-height: 1.25; font-weight: 600; }
|
||||
h1.font-display, h2.font-display { font-weight: 500; }
|
||||
/* Han has no serif/sans distinction to spend; the CJK stack already leads
|
||||
with a serif. */
|
||||
html[lang="zh"] h1, html[lang="ja"] h1, html[lang="ko"] h1,
|
||||
html[lang="zh"] h2, html[lang="ja"] h2, html[lang="ko"] h2 { letter-spacing: 0; font-weight: 600; }
|
||||
|
||||
/* `overflow-wrap: anywhere` keeps long English headings safe, but can strand
|
||||
CJK punctuation — or a lone numeral — on a line of its own. Strict CJK
|
||||
line-breaking keeps punctuation attached while still allowing breaks between
|
||||
Han characters. ja and ko want the same rule: ja for the identical reason as
|
||||
zh, ko because it has real word boundaries and should break on them. */
|
||||
html[lang="zh"] h1,
|
||||
html[lang="zh"] h2,
|
||||
html[lang="zh"] h3,
|
||||
html[lang="ja"] h1,
|
||||
html[lang="ja"] h2,
|
||||
html[lang="ja"] h3,
|
||||
html[lang="ko"] h1,
|
||||
html[lang="ko"] h2,
|
||||
html[lang="ko"] h3 {
|
||||
line-break: strict;
|
||||
word-break: normal;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
h1 .font-cjk {
|
||||
display: inline-block;
|
||||
font-size: clamp(1.6rem, 7.5vw, 2.2rem);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
/* Keep headings inside the viewport without inserting awkward English hyphens. */
|
||||
h1, h2 { hyphens: auto; }
|
||||
html[lang="en"] h1,
|
||||
html[lang="en"] h2 { hyphens: none; }
|
||||
}
|
||||
|
||||
/* ---------- structural primitives ---------- */
|
||||
/* One hairline everywhere: action blue at low alpha, visible on the deep
|
||||
field without ever becoming a border wall. */
|
||||
.hairline { border-color: var(--hairline); }
|
||||
.hairline-t { border-top: 1px solid var(--hairline); }
|
||||
.hairline-b { border-bottom: 1px solid var(--hairline); }
|
||||
.hairline-l { border-left: 1px solid var(--hairline); }
|
||||
.hairline-r { border-right: 1px solid var(--hairline); }
|
||||
|
||||
/* The single site container: width + auto margins so every page's gutters
|
||||
line up with the nav. */
|
||||
.site-container {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
/* The single hero band shared by home, docs, and the community portals: a
|
||||
quiet, flat stage surface against the deep field. */
|
||||
.hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
background: transparent;
|
||||
color: var(--ink);
|
||||
padding-block: var(--hero-pad);
|
||||
}
|
||||
|
||||
/* The single section rhythm for per-route hero/section blocks. */
|
||||
.section {
|
||||
padding-block: var(--section-pad);
|
||||
}
|
||||
|
||||
.double-rule {
|
||||
background-image:
|
||||
linear-gradient(var(--hairline), var(--hairline)),
|
||||
linear-gradient(var(--hairline), var(--hairline));
|
||||
background-size: 100% 1px, 100% 1px;
|
||||
background-position: top, bottom;
|
||||
background-repeat: no-repeat;
|
||||
padding: 0.45rem 0;
|
||||
}
|
||||
|
||||
.col-rule > * + * {
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
/* Single-column phones: drop the column rules so cards stack flush. */
|
||||
@media (max-width: 767px) {
|
||||
.col-rule > * + * { border-left: 0; border-top: 1px solid var(--hairline); }
|
||||
}
|
||||
|
||||
/* small-caps eyebrow — muted mono chrome for compact labels */
|
||||
.eyebrow {
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
/* ---------- compact section marker ---------- */
|
||||
/* The seal keeps its Han glyph but speaks as Tideline chrome now: a bordered
|
||||
stage plate with muted ink, not an ink block on paper. */
|
||||
.seal {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--paper-deep);
|
||||
border: 1px solid var(--stage-line);
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-cjk), "PingFang SC", serif;
|
||||
font-weight: 700;
|
||||
width: 2.6rem;
|
||||
height: 2.6rem;
|
||||
border-radius: 6px;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
/* Action-blue variant for the few routes that need a stronger section marker. */
|
||||
.seal-indigo {
|
||||
color: var(--indigo);
|
||||
border-color: color-mix(in srgb, var(--indigo) 55%, transparent);
|
||||
}
|
||||
|
||||
/* Below the waterline the seal stays the same plate — the whole page is water
|
||||
now, so there is nothing left to invert. */
|
||||
.ocean-column .seal {
|
||||
background: var(--stage-elevated);
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
/* ---------- pills / status ---------- */
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.12rem 0.45rem;
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
border: 1px solid var(--paper-line);
|
||||
background: var(--paper-deep);
|
||||
color: var(--ink);
|
||||
}
|
||||
.pill-hot { background: var(--indigo); color: var(--paper); border-color: var(--indigo); }
|
||||
.pill-new { background: var(--paper-deep); color: var(--ink); border-color: var(--ink-mute); }
|
||||
.pill-jade { background: var(--jade); color: var(--paper); border-color: var(--jade); }
|
||||
.pill-ochre { background: var(--ochre); color: var(--paper); border-color: var(--ochre); }
|
||||
.pill-ghost { background: transparent; color: var(--ink-mute); border-color: var(--ink-mute); }
|
||||
|
||||
/* ---------- numbers ---------- */
|
||||
.tabular { font-variant-numeric: tabular-nums; }
|
||||
.bignum {
|
||||
font-family: var(--font-body), system-ui, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 2.2rem;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ---------- code blocks ---------- */
|
||||
pre.code-block {
|
||||
background: var(--code-bg);
|
||||
color: var(--code-fg);
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
padding: 1rem 1.1rem;
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.55;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
position: relative;
|
||||
white-space: pre;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
pre.code-block::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 1px;
|
||||
background: var(--hairline);
|
||||
}
|
||||
pre.code-block .prompt { color: var(--action-on-dark); }
|
||||
pre.code-block .comment { color: var(--gpui-stage-ink-dim); }
|
||||
pre.code-block .key { color: var(--signal-gold); }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
pre.code-block { font-size: 0.76rem; padding: 0.85rem 0.95rem; }
|
||||
}
|
||||
|
||||
code.inline {
|
||||
background: var(--paper-deep);
|
||||
border: 1px solid var(--hairline);
|
||||
padding: 0.05rem 0.32rem;
|
||||
font-family: var(--font-mono), monospace;
|
||||
font-size: 0.85em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- search ---------- */
|
||||
.search-input {
|
||||
font-family: var(--font-body), system-ui, sans-serif;
|
||||
font-size: 1rem;
|
||||
padding: 0.75rem 2.5rem 0.75rem 1rem;
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
border: 1px solid var(--hairline);
|
||||
transition: border-color 180ms ease, box-shadow 180ms ease;
|
||||
}
|
||||
.search-input::placeholder { color: var(--ink-mute); }
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--indigo);
|
||||
box-shadow: 0 0 0 3px var(--indigo-pale);
|
||||
}
|
||||
|
||||
mark.search-highlight {
|
||||
background: var(--indigo-pale);
|
||||
color: var(--indigo-deep);
|
||||
padding: 0.02em 0.08em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Changelog */
|
||||
/* ------------------------------------------------------------------ */
|
||||
.changelog-facts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
|
||||
.changelog-facts > div {
|
||||
padding: 1.1rem 1.25rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
background: var(--paper-card);
|
||||
}
|
||||
|
||||
.changelog-facts span {
|
||||
display: block;
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.changelog-facts > div { min-width: 0; }
|
||||
|
||||
.changelog-facts strong {
|
||||
display: block;
|
||||
overflow-wrap: anywhere;
|
||||
margin-top: 0.45rem;
|
||||
color: var(--ink);
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.changelog-facts a {
|
||||
display: inline-block;
|
||||
margin-top: 0.55rem;
|
||||
color: var(--indigo);
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.changelog-facts a:hover { text-decoration: underline; text-underline-offset: 0.2rem; }
|
||||
|
||||
.changelog-facts [data-published="true"] strong { color: var(--jade); }
|
||||
.changelog-facts [data-candidate="unreleased"] strong { color: var(--ochre); }
|
||||
|
||||
.changelog-release {
|
||||
padding-block: 2.25rem;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.changelog-release-head {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem 1.5rem;
|
||||
}
|
||||
|
||||
.changelog-release-head h2 {
|
||||
font-size: clamp(1.4rem, 2.6vw, 1.9rem);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.changelog-release-head .dotline {
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.changelog-release-head .dotline a { color: var(--indigo); }
|
||||
.changelog-release-head .dotline a:hover { text-decoration: underline; text-underline-offset: 0.2rem; }
|
||||
|
||||
.changelog-unreleased-note {
|
||||
max-width: 44rem;
|
||||
margin-top: 0.6rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.changelog-sections {
|
||||
display: grid;
|
||||
gap: 1.35rem;
|
||||
margin-top: 1.4rem;
|
||||
}
|
||||
|
||||
.changelog-sections h3 {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 0.6rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.changelog-sections h3 a {
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.changelog-sections h3 a:hover {
|
||||
color: var(--indigo);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.2rem;
|
||||
}
|
||||
|
||||
.changelog-sections ul {
|
||||
margin-top: 0.55rem;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.changelog-sections li {
|
||||
min-width: 0;
|
||||
padding: 0.6rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.6;
|
||||
/* Release notes quote commands, env names, and URLs verbatim; a long
|
||||
unbroken token must wrap inside the column, never widen the page. */
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.changelog-sections li::before {
|
||||
content: "·";
|
||||
margin-right: 0.6rem;
|
||||
color: var(--stage-dim);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.changelog-facts { grid-template-columns: 1fr; }
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Docs: release truth, task index, contextual help */
|
||||
/* ------------------------------------------------------------------ */
|
||||
.release-truth {
|
||||
margin-top: 1.4rem;
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.release-truth-label {
|
||||
color: var(--ink-mute);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.release-truth a {
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.release-truth a:hover {
|
||||
color: var(--indigo-deep);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.2rem;
|
||||
}
|
||||
|
||||
.release-truth[data-release-state="published"] > span:not(.release-truth-label) {
|
||||
color: var(--jade);
|
||||
}
|
||||
|
||||
.docs-result-lead {
|
||||
margin: -0.2rem 0 0.9rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.docs-result-heading h3 {
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.docs-result-heading-topics {
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-result-topics {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.docs-task-group .docs-topic-list {
|
||||
border-top: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.docs-task-row .docs-topic-source {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-help {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.3fr) minmax(12rem, 0.7fr);
|
||||
gap: clamp(1.5rem, 4vw, 3rem);
|
||||
margin-top: 3rem;
|
||||
padding: clamp(1.4rem, 3vw, 2rem);
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
background: var(--paper-card);
|
||||
}
|
||||
|
||||
.docs-help-copy h2 {
|
||||
font-size: 1.15rem;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
|
||||
.docs-help-copy p {
|
||||
margin-top: 0.55rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.docs-help-source {
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem !important;
|
||||
color: var(--ink-mute) !important;
|
||||
}
|
||||
|
||||
.docs-help-source a {
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.docs-help-source a:hover { text-decoration: underline; text-underline-offset: 0.2rem; }
|
||||
|
||||
.docs-help-links {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
align-content: start;
|
||||
border-top: 1px solid var(--stage-line);
|
||||
padding-top: 0.6rem;
|
||||
}
|
||||
|
||||
.docs-help-links a {
|
||||
display: inline-flex;
|
||||
min-height: 2.5rem;
|
||||
align-items: center;
|
||||
color: var(--indigo);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-help-links a:hover { color: var(--indigo-deep); text-decoration: underline; text-underline-offset: 0.2rem; }
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.docs-help { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ---------- reference tables on docs pages ---------- */
|
||||
.docs-ref-rows {
|
||||
display: grid;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-ref-rows > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(10rem, 0.42fr) minmax(0, 1fr);
|
||||
gap: 1.25rem;
|
||||
padding: 0.9rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-ref-rows dt {
|
||||
color: var(--ink);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-ref-rows dd {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-ref-rows code.inline { white-space: nowrap; }
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.docs-ref-rows > div { grid-template-columns: 1fr; gap: 0.3rem; }
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Documentation portal */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
.docs-portal {
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.community-welcome-inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.community-welcome-inner h1 {
|
||||
max-width: 50rem;
|
||||
font-size: clamp(2rem, 4.2vw, 3.6rem);
|
||||
line-height: 1.03;
|
||||
}
|
||||
|
||||
.community-welcome-inner h1 {
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.community-welcome-inner p {
|
||||
max-width: 47rem;
|
||||
margin-top: 1rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.docs-portal .hero {
|
||||
padding-block: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-portal-band {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.docs-portal-band .portal-mark,
|
||||
.docs-portal-band .release-truth {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.docs-hub-lede {
|
||||
margin: 0.8rem 0 1.5rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.docs-shell {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(13rem, 0.28fr) minmax(0, 0.72fr);
|
||||
grid-template-areas: "sidebar content";
|
||||
gap: clamp(2rem, 5vw, 4.5rem);
|
||||
align-items: start;
|
||||
padding-block: clamp(2.25rem, 5vw, 4rem);
|
||||
}
|
||||
|
||||
.docs-sidebar {
|
||||
grid-area: sidebar;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-sidebar-heading {
|
||||
padding-block: 0.9rem;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
color: var(--ink);
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-sidebar-heading a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.docs-sidebar-heading a > span:last-child {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-sidebar-group {
|
||||
padding-block: 0.9rem;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-sidebar-category {
|
||||
margin-bottom: 0.35rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.docs-sidebar-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.8rem;
|
||||
padding-block: 0.32rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.4;
|
||||
transition: color 150ms ease;
|
||||
}
|
||||
|
||||
.docs-sidebar-link:hover,
|
||||
.docs-sidebar-link-current {
|
||||
color: var(--docs-accent);
|
||||
}
|
||||
|
||||
.docs-sidebar-link-current {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-sidebar-link > span:last-child:not(:first-child) {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-content {
|
||||
grid-area: content;
|
||||
width: 100%;
|
||||
max-width: 52rem;
|
||||
}
|
||||
|
||||
.docs-content p {
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
.docs-breadcrumb {
|
||||
margin-bottom: 1.15rem;
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-breadcrumb ol {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.docs-breadcrumb li:not(:last-child)::after {
|
||||
content: "/";
|
||||
margin-inline: 0.5em;
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.docs-breadcrumb a {
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.docs-breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.docs-breadcrumb [aria-current="page"] {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.docs-search-block {
|
||||
margin-bottom: 2.25rem;
|
||||
}
|
||||
|
||||
.docs-search-label {
|
||||
display: block;
|
||||
margin-bottom: 0.55rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-search-input {
|
||||
min-height: 3rem;
|
||||
padding-right: 3rem;
|
||||
border-color: var(--paper-line-soft);
|
||||
border-radius: 5px;
|
||||
background: color-mix(in srgb, var(--paper) 88%, transparent);
|
||||
}
|
||||
|
||||
.docs-search-clear {
|
||||
position: absolute;
|
||||
right: 0.9rem;
|
||||
top: 50%;
|
||||
translate: 0 -50%;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.docs-search-clear:hover {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.docs-search-count {
|
||||
margin-top: 0.55rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-result-groups {
|
||||
display: grid;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.docs-result-heading {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.55rem;
|
||||
}
|
||||
|
||||
.docs-result-heading h2 {
|
||||
font-size: 1.28rem;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.docs-result-heading > span {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.docs-topic-list {
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-topic-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(8rem, 0.34fr) auto;
|
||||
gap: 1.25rem;
|
||||
align-items: center;
|
||||
padding-block: 1rem;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
transition: background-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.docs-topic-row:hover {
|
||||
color: var(--docs-accent);
|
||||
background: color-mix(in srgb, var(--paper-deep) 68%, transparent);
|
||||
}
|
||||
|
||||
.docs-topic-title {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: baseline;
|
||||
color: var(--ink);
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-topic-title > span:last-child {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.docs-topic-main p {
|
||||
margin-top: 0.28rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.docs-topic-source {
|
||||
overflow: hidden;
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.docs-topic-source span {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.docs-topic-arrow {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.docs-empty {
|
||||
padding-block: 3.5rem;
|
||||
border-block: 1px solid var(--hairline);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.docs-empty > p:first-child {
|
||||
color: var(--ink);
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-empty > p:nth-child(2) {
|
||||
margin: 0.5rem auto 1.25rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.docs-source-note {
|
||||
margin-top: 2.5rem;
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.docs-source-note p {
|
||||
max-width: 70ch;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.docs-shell {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas:
|
||||
"content"
|
||||
"sidebar";
|
||||
}
|
||||
|
||||
/* The one docs nav stays reachable below the article: the groups reflow
|
||||
into columns so 23 topics read as an index, not a scroll of links. */
|
||||
.docs-sidebar nav {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
|
||||
column-gap: 2rem;
|
||||
}
|
||||
|
||||
.docs-sidebar-group:last-child { border-bottom: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.docs-topic-row {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 0.6rem 1rem;
|
||||
}
|
||||
|
||||
.docs-topic-source {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.docs-topic-arrow {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- community release record ---------- */
|
||||
.community-credit-section {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.community-record-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem 1.25rem;
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.community-record-links a {
|
||||
color: var(--indigo-deep);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.community-record-links a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.22rem;
|
||||
}
|
||||
|
||||
.community-credit-groups {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.community-credit-groups section {
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.community-credit-groups h3 {
|
||||
margin-bottom: 0.9rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.community-credit-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.community-credit-list a {
|
||||
padding: 0.3rem 0.45rem;
|
||||
border: 1px solid var(--hairline);
|
||||
color: var(--ink-soft);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.community-credit-list a:hover {
|
||||
border-color: var(--indigo);
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Docs theme — paper is the site default */
|
||||
/* */
|
||||
/* The docs sheet reads on the same paper as the rest of the folio. */
|
||||
/* The theme toggle (docs routes only) offers the whale's dark stage */
|
||||
/* as an opt-in, exactly the way the TUI keeps Blue Stage beside its */
|
||||
/* light preset. The dark override is set on */
|
||||
/* `html[data-theme="dark"] .docs-theme` through the shared */
|
||||
/* below-the-waterline rule beside :root; the attribute is applied */
|
||||
/* before paint by the inline script in the locale layout, so there */
|
||||
/* is no theme flash. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
.docs-theme {
|
||||
--docs-accent: var(--gpui-primary);
|
||||
--docs-button-bg: var(--gpui-paper-raised);
|
||||
--docs-button-border: var(--gpui-edge);
|
||||
--docs-button-text: var(--gpui-ink);
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.docs-theme .portal-button-secondary {
|
||||
border-color: var(--docs-button-border);
|
||||
background: var(--docs-button-bg);
|
||||
color: var(--docs-button-text);
|
||||
}
|
||||
|
||||
.docs-theme .portal-button-secondary:hover {
|
||||
border-color: var(--docs-accent);
|
||||
background: var(--docs-button-bg);
|
||||
color: var(--docs-accent);
|
||||
}
|
||||
|
||||
.docs-theme .portal-button-secondary:focus-visible,
|
||||
.docs-sidebar-link:focus-visible {
|
||||
outline: 2px solid var(--docs-accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* The opt-in dark sheet: the whale's stage tokens, scoped to the docs
|
||||
subtree so the shared nav keeps the paper. Surface/ink tokens come from
|
||||
the shared below-the-waterline rule; only the docs-specific inks are
|
||||
restated here. */
|
||||
html[data-theme="dark"] .docs-theme {
|
||||
--docs-accent: var(--gpui-primary-dark);
|
||||
--docs-button-bg: var(--gpui-stage-muted);
|
||||
--docs-button-border: var(--gpui-stage-edge);
|
||||
--docs-button-text: var(--gpui-stage-ink);
|
||||
--code-bg: var(--gpui-stage-deep);
|
||||
--code-fg: var(--gpui-stage-ink);
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Product home — the Tidal Folio: a sheet read under the sea. */
|
||||
/* */
|
||||
/* The hero is one illustrated plate: paper at the top left, the */
|
||||
/* whale's water rising from the bottom right, and the real terminal */
|
||||
/* capture floating at the waterline. The reading sections that */
|
||||
/* follow are plain paper; the page descends for good at the */
|
||||
/* waterline band, and the footer is the seabed. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
.product-home {
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.product-container {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
/* ---------- the hero plate ---------- */
|
||||
|
||||
.folio-hero {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
overflow: hidden;
|
||||
padding-block: clamp(3rem, 7vh, 5.5rem) 0;
|
||||
/* The plate ends on the deep field so the drawing can run to the edge. */
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
/* The strata: geometry, not a picture. Anchored to the bottom edge so the
|
||||
water always fills the plate's floor whatever the copy height. */
|
||||
.folio-strata {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Copy on the paper at the left; the terminal and its running head on the
|
||||
water at the right, so the plate reads as a page with a marginal figure
|
||||
rather than a screenshot with a caption. */
|
||||
.folio-hero-grid {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.15fr) minmax(18rem, 0.85fr);
|
||||
grid-template-rows: auto auto;
|
||||
column-gap: clamp(2rem, 5vw, 5rem);
|
||||
row-gap: clamp(1.25rem, 2.5vw, 2rem);
|
||||
align-items: end;
|
||||
padding-bottom: clamp(3rem, 7vw, 6rem);
|
||||
}
|
||||
|
||||
.folio-hero-copy {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
grid-column: 1;
|
||||
grid-row: 1 / 3;
|
||||
align-self: start;
|
||||
max-width: 40rem;
|
||||
padding-bottom: clamp(0.5rem, 2vw, 1.5rem);
|
||||
}
|
||||
.folio-hero-copy::before { content: ""; position: absolute; inset: -2rem -3rem; z-index: -1; background: var(--paper); filter: blur(24px); pointer-events: none; }
|
||||
|
||||
.folio-hero-copy h1 {
|
||||
max-width: 14ch;
|
||||
font-size: clamp(2.75rem, 6.4vw, 5.75rem);
|
||||
line-height: 0.98;
|
||||
}
|
||||
|
||||
.folio-lede {
|
||||
max-width: 34rem;
|
||||
margin: 1.6rem 0 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: clamp(1.02rem, 1.5vw, 1.2rem);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.folio-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 1.9rem;
|
||||
}
|
||||
|
||||
/* Buttons on paper: brand navy fills the primary; the secondary is an
|
||||
outlined sheet. Sentence case in the body face — this is a title page,
|
||||
not a status bar. */
|
||||
.folio-button {
|
||||
display: inline-flex;
|
||||
min-height: 3rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.7rem 1.35rem;
|
||||
border: 1px solid var(--indigo-deep);
|
||||
border-radius: 5px;
|
||||
color: var(--indigo-deep);
|
||||
font-family: var(--font-body), system-ui, sans-serif;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.folio-button:hover {
|
||||
background: var(--indigo-pale);
|
||||
}
|
||||
|
||||
.folio-button-primary {
|
||||
background: var(--indigo-deep);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
.folio-button-primary:hover {
|
||||
background: var(--indigo);
|
||||
border-color: var(--indigo);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
/* ---------- the terminal at the waterline ---------- */
|
||||
|
||||
.folio-shot {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
align-self: end;
|
||||
position: relative;
|
||||
width: min(100%, 40rem);
|
||||
margin-top: clamp(4rem, 12vw, 11rem);
|
||||
margin: 0;
|
||||
border: 1px solid rgb(var(--c-stage-soft) / 0.2);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: var(--gpui-stage-deep);
|
||||
/* The one shadow on the site: the plate is lifted off the water, so the
|
||||
shadow is soft and offset downward the way light through water would
|
||||
cast it. */
|
||||
box-shadow: 0 30px 60px -24px rgb(var(--c-stage-deep) / 0.75);
|
||||
}
|
||||
|
||||
.folio-shot img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: var(--gpui-stage-deep);
|
||||
}
|
||||
|
||||
.folio-shot figcaption {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
padding: 0.7rem 0.95rem 0.8rem;
|
||||
border-top: 1px solid rgb(var(--c-stage-soft) / 0.14);
|
||||
background: var(--gpui-stage);
|
||||
color: var(--stage-soft);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* The status line under the capture is the TUI header, typeset as facts:
|
||||
a `cw` chip and a dot chain. Spacing comes from `.dotline`; the
|
||||
separators are CSS punctuation. */
|
||||
.folio-shot .paper-facts {
|
||||
color: var(--stage-muted);
|
||||
}
|
||||
|
||||
.folio-shot .paper-facts .dotline-chip {
|
||||
padding: 0.08rem 0.36rem;
|
||||
border-radius: 3px;
|
||||
background: var(--gpui-stage-muted);
|
||||
color: var(--stage-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.folio-shot .paper-facts > *:not(:last-child)::after {
|
||||
color: var(--stage-hint);
|
||||
}
|
||||
|
||||
/* The chapter marker on the water: the plate's running head. */
|
||||
.folio-chapter {
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
align-self: start;
|
||||
justify-self: start;
|
||||
width: min(100%, 22rem);
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
.folio-chapter-num {
|
||||
display: block;
|
||||
padding-bottom: 0.7rem;
|
||||
border-bottom: 1px solid rgb(var(--c-stage-soft) / 0.3);
|
||||
color: var(--action-on-dark);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.folio-chapter-title {
|
||||
margin: 0.9rem 0 0;
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-size: clamp(1.35rem, 1.9vw, 1.7rem);
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
/* ---------- reading sections on paper ---------- */
|
||||
|
||||
.folio-section {
|
||||
padding-block: clamp(3.5rem, 7vw, 6rem);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-section h2 {
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.folio-section-lede {
|
||||
max-width: 40rem;
|
||||
margin: 1.1rem 0 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* What you gain: three ruled columns, no cards, no icons. */
|
||||
.folio-gain-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
margin-top: clamp(2rem, 4vw, 3rem);
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-gain-grid > div {
|
||||
padding: 1.5rem 1.75rem 1.75rem 0;
|
||||
}
|
||||
|
||||
.folio-gain-grid > div + div {
|
||||
padding-left: 1.75rem;
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-gain-grid h3 {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-size: 1.45rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.015em;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.folio-gain-grid p {
|
||||
margin-top: 0.75rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
/* A two-column chapter: the argument on the left, the facts on the right. */
|
||||
.folio-chapter-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(16rem, 0.8fr);
|
||||
gap: clamp(2rem, 6vw, 6rem);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.folio-running-head {
|
||||
display: block;
|
||||
margin-bottom: 1.1rem;
|
||||
color: var(--indigo);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.folio-fact-list {
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-fact-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 0.5fr) minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
padding: 0.95rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-fact-list dt {
|
||||
color: var(--ink);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.folio-fact-list dd {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.folio-link {
|
||||
display: inline-block;
|
||||
margin-top: 1.4rem;
|
||||
color: var(--indigo);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: rgb(var(--c-indigo) / 0.35);
|
||||
text-underline-offset: 0.25em;
|
||||
transition: text-decoration-color 150ms ease;
|
||||
}
|
||||
|
||||
.folio-link:hover {
|
||||
text-decoration-color: currentColor;
|
||||
}
|
||||
|
||||
/* ---------- the waterline band ---------- */
|
||||
|
||||
.folio-waterline {
|
||||
position: relative;
|
||||
height: clamp(11rem, 24vw, 20rem);
|
||||
overflow: hidden;
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.folio-waterline > svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- product: start-here band + shared getting-started steps ---------- */
|
||||
|
||||
.product-start {
|
||||
padding-block: clamp(3.5rem, 7vw, 6rem);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.product-start h2 {
|
||||
max-width: 24ch;
|
||||
}
|
||||
|
||||
.product-start-lede {
|
||||
margin-top: 1.1rem;
|
||||
max-width: 40rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.product-start-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.product-start-links a {
|
||||
color: var(--indigo);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.product-start-links a:hover {
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
/* Shared step list rendered by <GettingStartedSteps> (homepage band and the
|
||||
/docs/guide page). Light tokens; the docs dark theme re-themes it through
|
||||
the same --hairline/--ink-* vars as the rest of the docs subtree. */
|
||||
.gs-steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
margin-top: clamp(2rem, 4vw, 3rem);
|
||||
padding: 0;
|
||||
border-top: 1px solid var(--hairline);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.gs-steps li {
|
||||
padding: 1.35rem 1.5rem 1.5rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.gs-steps li + li {
|
||||
padding-left: 1.5rem;
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
.docs-content .gs-steps { grid-template-columns: minmax(0, 1fr); }
|
||||
.docs-content .gs-steps li + li { padding-left: 0; border-left: 0; }
|
||||
|
||||
.gs-step-index {
|
||||
color: var(--indigo);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.gs-steps h3 {
|
||||
margin-top: 1.1rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.gs-steps p {
|
||||
margin-top: 0.6rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.gs-step-commands {
|
||||
margin-top: 0.9rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
pre.gs-step-commands {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.gs-step-link {
|
||||
display: inline-block;
|
||||
margin-top: 0.9rem;
|
||||
color: var(--indigo);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.gs-step-link:hover {
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
/* ---------- a11y: skip link ---------- */
|
||||
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: -3.5rem;
|
||||
z-index: 60;
|
||||
padding: 0.55rem 0.9rem;
|
||||
background: var(--ink);
|
||||
color: var(--paper);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.skip-link:focus-visible {
|
||||
top: 0.5rem;
|
||||
outline: 2px solid var(--indigo);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ---------- honest status badges ---------- */
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.2rem 0.55rem;
|
||||
border: 1px solid var(--hairline);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.status-badge-dot {
|
||||
width: 0.45rem;
|
||||
height: 0.45rem;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.status-badge-experimental .status-badge-dot { background: var(--ochre); }
|
||||
.status-badge-preview .status-badge-dot { background: var(--indigo); }
|
||||
.status-badge-pending .status-badge-dot { background: var(--ochre); }
|
||||
.status-badge-unavailable .status-badge-dot { background: var(--ink-mute); }
|
||||
|
||||
/* ---------- session media (real-session surface) ---------- */
|
||||
|
||||
.session-media {
|
||||
border: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.session-media video {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
background: var(--ocean-deep);
|
||||
}
|
||||
|
||||
.session-media-stage {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.9rem;
|
||||
padding: clamp(1.5rem, 4vw, 2.5rem);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
background: var(--paper-deep);
|
||||
}
|
||||
|
||||
.session-media-pending-note {
|
||||
max-width: 44rem;
|
||||
margin: 0;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.session-media-caption {
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem 1.25rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.session-media-caption strong {
|
||||
color: var(--ink);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.session-media-caption a {
|
||||
color: var(--indigo);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.session-media-caption a:hover {
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
/* The boundaries band carries no field of its own — the water is the page. */
|
||||
.product-boundaries {
|
||||
padding-block: clamp(4rem, 8vw, 7rem);
|
||||
}
|
||||
|
||||
.product-boundaries-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(16rem, 0.8fr) minmax(0, 1.2fr);
|
||||
gap: clamp(3rem, 8vw, 8rem);
|
||||
}
|
||||
|
||||
.product-boundaries h2 {
|
||||
font-size: clamp(2.5rem, 5vw, 4.7rem);
|
||||
}
|
||||
|
||||
.product-boundaries h2 span {
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.product-boundaries-grid > div > p {
|
||||
max-width: 31rem;
|
||||
margin-top: 1.25rem;
|
||||
color: var(--ink-soft);
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.product-boundary-list {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.product-boundary-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(12rem, 0.85fr) minmax(0, 1.15fr);
|
||||
align-items: baseline;
|
||||
gap: 1.5rem;
|
||||
padding: 1.25rem 1.4rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
background: var(--paper-card);
|
||||
}
|
||||
|
||||
.product-boundary-list dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-boundary-list dd {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
/* ---------- below the waterline ---------- */
|
||||
/* */
|
||||
/* The water column. Its tokens are the dark whale palette (see the shared */
|
||||
/* rule beside :root), so every component inside reads correctly without */
|
||||
/* knowing it is under water. The field starts at the panel surface just */
|
||||
/* under the waterline band and sinks to the deep stop the footer continues, */
|
||||
/* so the seabed arrives without a seam. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.ocean-column {
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
background: linear-gradient(180deg, var(--gpui-stage) 0%, var(--gpui-stage-deep) 38rem);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Bands inside the column carry no field of their own. */
|
||||
.ocean-column > section {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* One line weight on the field. */
|
||||
.ocean-column .hairline,
|
||||
.ocean-column .hairline-t,
|
||||
.ocean-column .hairline-b,
|
||||
.ocean-column .hairline-l {
|
||||
border-color: var(--stage-line);
|
||||
}
|
||||
|
||||
.ocean-column h2 {
|
||||
max-width: 24ch;
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
/* Where Codewhale runs today: one honest table, no badges. */
|
||||
.folio-availability {
|
||||
padding-block: clamp(3.5rem, 7vw, 6rem);
|
||||
border-bottom: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.folio-availability-list {
|
||||
margin-top: clamp(2rem, 4vw, 3rem);
|
||||
border-top: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.folio-availability-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(9rem, 0.35fr) minmax(0, 1fr);
|
||||
gap: 1.25rem;
|
||||
padding: 1.15rem 0;
|
||||
border-bottom: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.folio-availability-list dt {
|
||||
color: var(--stage-text);
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.folio-availability-list dd {
|
||||
color: var(--stage-soft);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.folio-availability-list dd strong {
|
||||
display: block;
|
||||
margin-bottom: 0.2rem;
|
||||
color: var(--stage-text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.folio-availability-note {
|
||||
max-width: 40rem;
|
||||
margin-top: 1.4rem;
|
||||
color: var(--stage-muted);
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.product-surfaces {
|
||||
padding-block: clamp(3.5rem, 7vw, 6rem);
|
||||
border-bottom: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.product-surface-list {
|
||||
margin-top: 2.5rem;
|
||||
border-top: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.product-surface-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(13rem, 0.7fr) minmax(0, 1.3fr);
|
||||
gap: 2rem;
|
||||
padding: 1.2rem 0;
|
||||
border-bottom: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.product-surface-list strong {
|
||||
color: var(--stage-text);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.product-surface-list span {
|
||||
color: var(--stage-soft);
|
||||
}
|
||||
|
||||
.product-surfaces > .product-container > a {
|
||||
display: inline-block;
|
||||
margin-top: 1.5rem;
|
||||
color: var(--action-on-dark);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
/* The install band is the TUI composer: a raised plate on the column,
|
||||
bracketed above by Signal Gold and below by Operate violet — the
|
||||
product's exact composer framing, and the one place gold is legitimate
|
||||
here, because what it brackets is the point of human intent. The
|
||||
descendant selector out-ranks `.ocean-column > section { transparent }`. */
|
||||
.ocean-column .product-install-band {
|
||||
padding-block: clamp(3rem, 6vw, 5rem);
|
||||
border-top: 1px solid color-mix(in srgb, var(--signal-gold) 40%, transparent);
|
||||
border-bottom: 1px solid color-mix(in srgb, var(--violet) 40%, transparent);
|
||||
background: var(--stage-composer);
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
.product-install-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(15rem, 0.65fr) minmax(0, 1.35fr);
|
||||
gap: clamp(2rem, 7vw, 7rem);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
/* `❯` sits inside the plate, where the TUI puts it — a code-owned literal in
|
||||
the same class as `Codewhale` and `npm install -g codewhale`. */
|
||||
.product-composer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.product-composer pre.code-block {
|
||||
padding-left: 2.6rem;
|
||||
}
|
||||
|
||||
.product-composer button {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.product-composer-prompt {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1.1rem;
|
||||
z-index: 1;
|
||||
color: var(--cyan);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", ui-monospace, monospace;
|
||||
font-size: 0.78rem;
|
||||
line-height: 1.55;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.product-composer pre.code-block {
|
||||
padding-left: 2.3rem;
|
||||
}
|
||||
|
||||
.product-composer-prompt {
|
||||
top: 0.85rem;
|
||||
left: 0.95rem;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
}
|
||||
|
||||
.product-install-band pre.code-block {
|
||||
margin: 0;
|
||||
border-color: var(--stage-line);
|
||||
}
|
||||
|
||||
.product-install-band .dotline {
|
||||
margin-top: 1rem;
|
||||
color: var(--stage-hint);
|
||||
}
|
||||
|
||||
.product-install-band a {
|
||||
display: inline-block;
|
||||
margin-top: 1.2rem;
|
||||
padding-bottom: 0.2rem;
|
||||
border-bottom: 1px solid var(--stage-line);
|
||||
color: var(--action-on-dark);
|
||||
font-size: 0.95rem;
|
||||
transition: border-color 150ms ease;
|
||||
}
|
||||
|
||||
.product-install-band a:hover {
|
||||
border-color: var(--action-on-dark);
|
||||
}
|
||||
|
||||
.product-community {
|
||||
padding-block: clamp(3.5rem, 7vw, 6rem);
|
||||
}
|
||||
|
||||
.product-community-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.1fr) minmax(16rem, 0.75fr);
|
||||
gap: clamp(2rem, 5vw, 5rem);
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.product-community p {
|
||||
max-width: 42rem;
|
||||
margin-top: 1rem;
|
||||
color: var(--stage-soft);
|
||||
font-size: 1rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.product-community nav {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 1rem;
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid var(--stage-line);
|
||||
}
|
||||
|
||||
.product-community nav a {
|
||||
color: var(--ocean-current);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.product-community nav a:hover {
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
.product-home a:focus-visible {
|
||||
outline: 3px solid var(--signal-gold);
|
||||
outline-offset: 4px;
|
||||
}
|
||||
|
||||
@media (max-width: 1050px) {
|
||||
.folio-hero-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
}
|
||||
|
||||
.folio-hero-copy {
|
||||
max-width: 44rem;
|
||||
}
|
||||
|
||||
.folio-hero-copy {
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.folio-shot {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
width: min(100%, 44rem);
|
||||
margin-top: clamp(1.5rem, 6vw, 3rem);
|
||||
}
|
||||
|
||||
.folio-chapter {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
justify-self: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.folio-gain-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.folio-gain-grid > div,
|
||||
.folio-gain-grid > div + div {
|
||||
padding: 1.35rem 0;
|
||||
border-left: 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.folio-chapter-grid,
|
||||
.product-install-grid,
|
||||
.product-community-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gs-steps {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.gs-steps li:nth-child(3) {
|
||||
padding-left: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.folio-availability-list > div,
|
||||
.product-surface-list > div {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
.site-nav-inner {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.site-nav-actions {
|
||||
min-width: 0;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.site-nav-actions select {
|
||||
width: 6.75rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.paper-wordmark {
|
||||
max-width: 9.75rem;
|
||||
}
|
||||
|
||||
.folio-hero-copy h1 {
|
||||
max-width: 100%;
|
||||
font-size: clamp(2.3rem, 11vw, 3.2rem);
|
||||
word-break: normal;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.paper-install-cta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.folio-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.folio-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gs-steps {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.gs-steps li,
|
||||
.gs-steps li + li,
|
||||
.gs-steps li:nth-child(3) {
|
||||
padding: 1.25rem 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.product-community nav {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/* ---------- /product: the availability table on paper ---------- */
|
||||
/* The same list the homepage sets in the water, re-inked for the sheet. */
|
||||
.product-availability-paper,
|
||||
.product-availability-paper > div {
|
||||
border-color: var(--hairline);
|
||||
}
|
||||
.product-availability-paper dt,
|
||||
.product-availability-paper dd strong {
|
||||
color: var(--ink);
|
||||
}
|
||||
.product-availability-paper dd {
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
/* ---------- usage counting: the consent sheet and its footer control ---------- */
|
||||
/* A quiet sheet at the foot of the viewport, on paper, with the site's own
|
||||
inks. It is not a modal: the page stays usable, and the choice is one
|
||||
click either way. */
|
||||
.usage-counting {
|
||||
display: grid;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.usage-counting-status {
|
||||
color: var(--ink);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.usage-counting-elsewhere {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.usage-counting-button {
|
||||
display: inline-flex;
|
||||
justify-self: start;
|
||||
min-height: 2.75rem;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1.1rem;
|
||||
border: 1px solid var(--indigo);
|
||||
border-radius: 5px;
|
||||
color: var(--indigo);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.usage-counting-button:hover {
|
||||
background: rgb(var(--c-indigo) / 0.1);
|
||||
}
|
||||
|
||||
.usage-counting-button:focus-visible {
|
||||
outline: 2px solid var(--indigo);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ---------- docs: the reading sheet ---------- */
|
||||
/* Read mode: comprehension first. The article gets a book measure, the serif
|
||||
title voice on its headings, generous leading, and a contents rail that
|
||||
stays put while the sheet scrolls. */
|
||||
.docs-content {
|
||||
max-width: 46rem;
|
||||
}
|
||||
|
||||
.docs-content h1 {
|
||||
font-size: clamp(2.2rem, 3.6vw, 3rem);
|
||||
line-height: 1.05;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.docs-content h2 {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-weight: 500;
|
||||
font-size: clamp(1.5rem, 2.2vw, 1.9rem);
|
||||
line-height: 1.15;
|
||||
letter-spacing: -0.015em;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.docs-content h3 {
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.docs-content p,
|
||||
.docs-content li {
|
||||
max-width: 66ch;
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.docs-content > section + section {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
|
||||
.docs-content code.inline {
|
||||
background: var(--paper-card);
|
||||
border-color: var(--hairline);
|
||||
}
|
||||
|
||||
.docs-content pre.code-block {
|
||||
margin-block: 1rem;
|
||||
}
|
||||
|
||||
/* The contents rail: a running index in the margin, sticky on tall screens. */
|
||||
@media (min-width: 901px) {
|
||||
.docs-sidebar {
|
||||
position: sticky;
|
||||
top: 5.25rem;
|
||||
max-height: calc(100vh - 6rem);
|
||||
overflow-y: auto;
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-sidebar-heading {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.docs-sidebar-category {
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
|
||||
.docs-sidebar-link {
|
||||
font-size: 0.86rem;
|
||||
padding-block: 0.36rem;
|
||||
}
|
||||
|
||||
.docs-sidebar-link-current {
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.docs-sidebar-link-current::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 0.4rem;
|
||||
height: 1px;
|
||||
margin-right: 0.45rem;
|
||||
vertical-align: middle;
|
||||
background: var(--indigo);
|
||||
}
|
||||
|
||||
/* Topic rows on the hub: a title with room to breathe, the source path quiet. */
|
||||
.docs-topic-title {
|
||||
font-size: 0.98rem;
|
||||
}
|
||||
|
||||
.docs-topic-main p {
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.docs-result-heading h2 {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-weight: 500;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
/* The docs hero band: the mark and the release line as one running head. */
|
||||
.docs-portal .hero {
|
||||
padding-block: 1rem;
|
||||
background: var(--paper-deep);
|
||||
}
|
||||
|
||||
.docs-portal .portal-mark {
|
||||
color: var(--ink);
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.release-truth {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Contextual help plate at the foot of every page, on the sheet's own card. */
|
||||
.docs-help {
|
||||
background: var(--paper-card);
|
||||
border-color: var(--hairline);
|
||||
}
|
||||
|
||||
.docs-help-copy h2 {
|
||||
font-family: var(--font-serif), "Newsreader", Georgia, serif;
|
||||
font-weight: 500;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
/* Keep the terminal centered and compact beneath the introductory copy. */
|
||||
.folio-hero-grid { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto auto; row-gap: 2rem; }
|
||||
.folio-hero-copy { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: clamp(2rem, 5vw, 5rem); max-width: none; grid-column: 1; grid-row: 1; padding-bottom: 0; }
|
||||
.folio-hero-copy h1 { grid-column: 1; grid-row: 1 / 3; max-width: none; font-size: clamp(2.75rem, 5vw, 4.5rem); line-height: 1.06; text-wrap: balance; }
|
||||
.folio-hero-copy .folio-lede { grid-column: 2; grid-row: 1; margin-top: 0; align-self: end; }
|
||||
.folio-hero-copy .folio-actions { grid-column: 2; grid-row: 2; align-self: start; margin-top: 1.25rem; }
|
||||
.folio-shot { grid-column: 1; grid-row: 2; width: min(100%, 56rem); justify-self: center; margin-top: 0; }
|
||||
.folio-chapter { grid-column: 1; grid-row: 3; justify-self: start; }
|
||||
.product-install-grid > div, .product-composer, .product-composer > div { min-width: 0; }
|
||||
@media (max-width: 760px) {
|
||||
.folio-hero-copy { display: block; }
|
||||
.folio-hero-copy .folio-lede { margin-top: 1.25rem; }
|
||||
.folio-shot { width: 100%; }
|
||||
}
|
||||
@@ -0,0 +1,630 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Documentation-led public home */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
.portal-home {
|
||||
background: var(--paper);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.portal-container {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.portal-current {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.22;
|
||||
pointer-events: none;
|
||||
background-image: repeating-radial-gradient(
|
||||
ellipse 82% 44% at 24% -22%,
|
||||
transparent 0,
|
||||
transparent 2.7rem,
|
||||
rgb(var(--c-stage-soft) / 0.18) 2.76rem,
|
||||
transparent 2.83rem
|
||||
);
|
||||
mask-image: linear-gradient(to bottom, transparent, black 24%, black 72%, transparent);
|
||||
}
|
||||
|
||||
.portal-hero-grid {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.12fr) minmax(20rem, 0.88fr);
|
||||
gap: clamp(2.5rem, 6vw, 5rem);
|
||||
align-items: center;
|
||||
padding-block: clamp(3.25rem, 6vw, 4.75rem);
|
||||
}
|
||||
|
||||
.portal-hero-copy {
|
||||
max-width: 42rem;
|
||||
}
|
||||
|
||||
.portal-mark {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
margin-bottom: 1.25rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.portal-hero h1 {
|
||||
max-width: 41rem;
|
||||
font-size: clamp(2.3rem, 4.8vw, 3.75rem);
|
||||
line-height: 1.01;
|
||||
}
|
||||
|
||||
.portal-lede {
|
||||
max-width: 40rem;
|
||||
margin-top: 1.35rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: clamp(1rem, 1.7vw, 1.18rem);
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.portal-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.7rem;
|
||||
margin-top: 1.7rem;
|
||||
}
|
||||
|
||||
.portal-button {
|
||||
display: inline-flex;
|
||||
min-height: 2.75rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.65rem 1rem;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 5px;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.portal-button-primary {
|
||||
border-color: var(--indigo);
|
||||
background: var(--indigo);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
.portal-button-primary:hover {
|
||||
background: var(--indigo-deep);
|
||||
border-color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
.portal-button-secondary {
|
||||
border-color: var(--paper-edge);
|
||||
background: var(--paper-deep);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.portal-button-secondary:hover {
|
||||
border-color: var(--indigo);
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.portal-meta {
|
||||
max-width: 38rem;
|
||||
margin-top: 0.9rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.76rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.portal-quickstart {
|
||||
padding: clamp(1.35rem, 3vw, 2rem);
|
||||
border: 1px solid rgb(var(--c-stage-soft) / 0.2);
|
||||
border-radius: 8px;
|
||||
background: var(--ocean-deep);
|
||||
box-shadow: 0 1.25rem 3rem rgba(0, 0, 0, 0.45);
|
||||
color: var(--stage-soft);
|
||||
}
|
||||
|
||||
.portal-quickstart > span {
|
||||
color: var(--stage-muted);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.portal-quickstart h2 {
|
||||
margin-top: 0.65rem;
|
||||
color: var(--stage-text);
|
||||
font-size: clamp(1.2rem, 2vw, 1.55rem);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.portal-quickstart p {
|
||||
margin-top: 0.7rem;
|
||||
color: rgb(var(--c-stage-soft) / 0.7);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.portal-quickstart pre.code-block {
|
||||
margin-top: 1.15rem;
|
||||
border-color: rgb(var(--c-stage-soft) / 0.2);
|
||||
background: rgb(var(--c-stage-deep) / 0.6);
|
||||
}
|
||||
|
||||
.portal-quickstart > a {
|
||||
display: inline-block;
|
||||
margin-top: 1rem;
|
||||
color: var(--ocean-current);
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.portal-quickstart > a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.25rem;
|
||||
}
|
||||
|
||||
.portal-section {
|
||||
padding-block: clamp(3.25rem, 6vw, 4.75rem);
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.portal-section-muted {
|
||||
background: var(--paper-deep);
|
||||
}
|
||||
|
||||
.legal-doc {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(3.25rem, 6vw, 4.75rem);
|
||||
max-width: 42rem;
|
||||
}
|
||||
|
||||
.legal-doc-kicker {
|
||||
margin: 0 0 0.75rem;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-muted, color-mix(in srgb, var(--ink) 62%, var(--paper)));
|
||||
}
|
||||
|
||||
.legal-doc h1 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-family: var(--font-display), serif;
|
||||
font-size: clamp(1.85rem, 3.6vw, 2.7rem);
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.legal-doc-updated,
|
||||
.legal-doc p,
|
||||
.legal-doc section p {
|
||||
margin: 0 0 1.15rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.legal-doc section h2 {
|
||||
margin: 1.75rem 0 0.5rem;
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.legal-doc-nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.legal-doc-nav a {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.2rem;
|
||||
}
|
||||
|
||||
.public-account-entry {
|
||||
max-width: 32rem;
|
||||
}
|
||||
|
||||
.public-account-mark {
|
||||
display: block;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 0 1.15rem;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.portal-section-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(14rem, 0.62fr) minmax(0, 1.38fr);
|
||||
gap: clamp(2.5rem, 7vw, 6.5rem);
|
||||
}
|
||||
|
||||
.portal-section-copy > span,
|
||||
.portal-docs-heading span,
|
||||
.portal-community-grid > div:first-child > span {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.portal-section-copy h2,
|
||||
.portal-docs-heading h2,
|
||||
.portal-community h2 {
|
||||
margin-top: 0.65rem;
|
||||
font-size: clamp(1.55rem, 3vw, 2.4rem);
|
||||
}
|
||||
|
||||
.portal-section-copy p {
|
||||
margin-top: 0.9rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.92rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.portal-topic-list {
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.portal-topic-list > a,
|
||||
.portal-topic-list > div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8rem, 0.42fr) minmax(0, 1fr) auto;
|
||||
gap: 1.25rem;
|
||||
align-items: start;
|
||||
padding-block: 1.15rem;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
transition: background-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.portal-topic-list > a:hover {
|
||||
color: var(--indigo);
|
||||
background: color-mix(in srgb, var(--indigo) 7%, transparent);
|
||||
}
|
||||
|
||||
.portal-topic-list strong {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.portal-topic-list span {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.portal-topic-list span:last-child {
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.portal-docs-heading {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.portal-docs-heading a {
|
||||
flex: none;
|
||||
color: var(--indigo-deep);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.portal-docs-heading a:hover,
|
||||
.portal-community-links a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.25rem;
|
||||
}
|
||||
|
||||
/* Sortable model table — quiet column-header buttons; the active column
|
||||
carries a direction mark instead of a color change. */
|
||||
.models-sort {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.35rem;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
color: var(--ink-mute);
|
||||
font: inherit;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.models-sort:hover {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.models-sort.is-active {
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
.models-sort-mark {
|
||||
font-size: 0.72em;
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.models-sort.is-active .models-sort-mark {
|
||||
color: var(--indigo-deep);
|
||||
}
|
||||
|
||||
.models-sort:focus-visible {
|
||||
outline: 2px solid var(--indigo);
|
||||
outline-offset: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.models-reasoning {
|
||||
display: inline-block;
|
||||
margin-left: 0.5rem;
|
||||
padding: 0.05rem 0.4rem;
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: 999px;
|
||||
color: var(--ink-mute);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.04em;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.portal-doc-groups {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: clamp(2.5rem, 6vw, 5rem);
|
||||
}
|
||||
|
||||
.portal-doc-groups h3 {
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.portal-doc-groups section > p {
|
||||
min-height: 4.5rem;
|
||||
margin-top: 0.55rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.portal-doc-groups .portal-topic-list {
|
||||
margin-top: 1.25rem;
|
||||
}
|
||||
|
||||
.portal-doc-groups .portal-topic-list > a {
|
||||
grid-template-columns: minmax(7rem, 0.42fr) minmax(0, 1fr) auto;
|
||||
gap: 0.9rem;
|
||||
}
|
||||
|
||||
.portal-community {
|
||||
padding-block: clamp(3.25rem, 6vw, 4.75rem);
|
||||
background:
|
||||
radial-gradient(circle at 84% -30%, rgb(var(--c-primary-dark) / 0.1), transparent 30rem),
|
||||
var(--ocean-deep);
|
||||
color: var(--stage-soft);
|
||||
}
|
||||
|
||||
.portal-community-grid {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(16rem, 0.85fr) minmax(0, 1.15fr);
|
||||
gap: clamp(2.5rem, 7vw, 7rem);
|
||||
}
|
||||
|
||||
.portal-community-grid > div:first-child > span {
|
||||
color: var(--stage-muted);
|
||||
}
|
||||
|
||||
.portal-community h2 {
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
.portal-community p {
|
||||
color: rgb(var(--c-stage-soft) / 0.76);
|
||||
font-size: 0.96rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.portal-community-links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.8rem 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.25rem;
|
||||
border-top: 1px solid rgb(var(--c-stage-soft) / 0.18);
|
||||
}
|
||||
|
||||
.portal-community-links a {
|
||||
color: var(--ocean-current);
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.contribute-path-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-path-grid article {
|
||||
padding: 1.5rem 1.5rem 1.5rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-path-grid article:nth-child(even) {
|
||||
padding-inline: 1.5rem 0;
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-path-grid h3 {
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.contribute-path-grid p {
|
||||
margin-top: 0.55rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.contribute-path-grid a,
|
||||
.contribute-steps a {
|
||||
display: inline-block;
|
||||
margin-top: 0.9rem;
|
||||
color: var(--indigo-deep);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.contribute-path-grid a:hover,
|
||||
.contribute-steps a:hover {
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.22rem;
|
||||
}
|
||||
|
||||
.contribute-steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-steps li {
|
||||
display: grid;
|
||||
grid-template-columns: 2.25rem minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
padding: 1.5rem 1.5rem 1.5rem 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-steps li:nth-child(even) {
|
||||
padding-inline: 1.5rem 0;
|
||||
border-left: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-steps li > span {
|
||||
color: var(--indigo-deep);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.contribute-steps h3 {
|
||||
font-size: 1rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.contribute-steps p {
|
||||
margin-top: 0.5rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.contribute-review-list {
|
||||
border-top: 1px solid var(--hairline);
|
||||
}
|
||||
|
||||
.contribute-review-list li {
|
||||
padding-block: 1rem;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.84rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contribute-dev-loop {
|
||||
padding-block: clamp(3.25rem, 6vw, 4.75rem);
|
||||
background: var(--ocean-deep);
|
||||
}
|
||||
|
||||
.contribute-dev-loop .portal-section-copy > span {
|
||||
color: var(--stage-muted);
|
||||
}
|
||||
|
||||
.contribute-dev-loop .portal-section-copy h2 {
|
||||
color: var(--stage-text);
|
||||
}
|
||||
|
||||
.contribute-dev-loop .portal-section-copy p {
|
||||
color: rgb(var(--c-stage-soft) / 0.76);
|
||||
}
|
||||
|
||||
.contribute-dev-loop pre.code-block {
|
||||
border-color: rgb(var(--c-stage-soft) / 0.2);
|
||||
background: rgb(var(--c-stage-deep) / 0.62);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.portal-hero-grid,
|
||||
.portal-section-grid,
|
||||
.portal-community-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.portal-hero-copy {
|
||||
max-width: 48rem;
|
||||
}
|
||||
|
||||
.portal-quickstart {
|
||||
max-width: 42rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.portal-doc-groups {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.portal-doc-groups section > p {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.contribute-path-grid,
|
||||
.contribute-steps {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.contribute-path-grid article,
|
||||
.contribute-path-grid article:nth-child(even),
|
||||
.contribute-steps li,
|
||||
.contribute-steps li:nth-child(even) {
|
||||
padding-inline: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.portal-hero-grid {
|
||||
gap: 2.25rem;
|
||||
padding-block: 2.75rem;
|
||||
}
|
||||
|
||||
.portal-actions,
|
||||
.portal-docs-heading {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.portal-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.portal-topic-list > a,
|
||||
.portal-topic-list > div,
|
||||
.portal-doc-groups .portal-topic-list > a,
|
||||
.portal-doc-groups .portal-topic-list > div {
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 0.45rem 1rem;
|
||||
}
|
||||
|
||||
.portal-topic-list > a span:nth-child(2),
|
||||
.portal-topic-list > div span:nth-child(2) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.portal-topic-list > a span:last-child,
|
||||
.portal-topic-list > div span:last-child {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.portal-community-links {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/* ---------- nav link ---------- */
|
||||
.site-nav {
|
||||
position: sticky;
|
||||
z-index: 30;
|
||||
top: 0;
|
||||
border-bottom: 1px solid var(--hairline);
|
||||
background: rgb(var(--c-paper) / 0.94);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.site-nav-inner {
|
||||
display: flex;
|
||||
width: var(--container);
|
||||
min-height: 4.25rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.site-wordmark {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
color: var(--ink);
|
||||
font-family: var(--font-display), ui-sans-serif, system-ui, sans-serif;
|
||||
font-size: 1.15rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
|
||||
.paper-nav-inner {
|
||||
min-height: 3.85rem;
|
||||
padding-block: 0.55rem;
|
||||
}
|
||||
|
||||
.paper-wordmark {
|
||||
flex: 0 1 auto;
|
||||
gap: 0.75rem;
|
||||
max-width: 15rem;
|
||||
min-width: 8.75rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-wordmark-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
line-height: 1.1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.paper-wordmark-mark {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
/* The traced wordmark is ~7.1:1; when the compact nav clamps its width the
|
||||
glyphs scale down inside the box instead of squashing. */
|
||||
.paper-wordmark-logo {
|
||||
display: block;
|
||||
flex: 0 1 auto;
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
height: 20px;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
.paper-star-badge {
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.paper-install-cta {
|
||||
align-items: center;
|
||||
min-height: 2.25rem;
|
||||
padding: 0.35rem 0.85rem;
|
||||
background: var(--indigo-deep);
|
||||
color: var(--paper);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
border-radius: 5px;
|
||||
transition: background-color 150ms ease;
|
||||
}
|
||||
|
||||
.paper-install-cta:hover {
|
||||
background: var(--indigo);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
.paper-auth {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.paper-auth-signin,
|
||||
.paper-auth-register {
|
||||
align-items: center;
|
||||
min-height: 2.25rem;
|
||||
padding: 0.35rem 0.75rem;
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease;
|
||||
}
|
||||
|
||||
.paper-auth-signin,
|
||||
.paper-auth-register {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.paper-auth-signin:hover,
|
||||
.paper-auth-register:hover {
|
||||
color: var(--indigo);
|
||||
}
|
||||
|
||||
.site-nav-actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
gap: 0.65rem;
|
||||
}
|
||||
|
||||
.site-nav-actions > * { flex-shrink: 0; }
|
||||
|
||||
/* Native <select> sizes to the selected option (Safari) or the longest
|
||||
option (Chrome). Unbounded, "Bahasa Indonesia (sebagian)" / "Deutsch
|
||||
(teilweise)" push the compact controls past overflow-x: clip. */
|
||||
.site-nav-actions select {
|
||||
width: 6.75rem;
|
||||
max-width: 6.75rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.site-github-link {
|
||||
display: inline-flex;
|
||||
min-height: 2.25rem;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.75rem;
|
||||
border: 1px solid var(--hairline);
|
||||
border-radius: 6px;
|
||||
font-family: var(--font-body), system-ui, sans-serif;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.site-github-link:hover { border-color: var(--ink); }
|
||||
|
||||
.brand-mark { width: 1rem; height: 1rem; display: block; }
|
||||
|
||||
.paper-star-badge .brand-mark { margin-right: 0.3rem; }
|
||||
|
||||
.nav-link {
|
||||
font-family: var(--font-body), system-ui, sans-serif;
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0;
|
||||
color: var(--ink);
|
||||
position: relative;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
.nav-link::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0; right: 0; bottom: -2px;
|
||||
height: 2px;
|
||||
background: var(--indigo);
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
.nav-link:hover::after, .nav-link[aria-current="page"]::after { transform: scaleX(1); }
|
||||
|
||||
/* ---------- footer ---------- */
|
||||
.site-footer {
|
||||
position: relative;
|
||||
background: var(--ocean-deep);
|
||||
color: var(--stage-soft);
|
||||
}
|
||||
|
||||
/* The waterline before the seabed. Every page that is still on paper when
|
||||
it reaches the footer descends here; the homepage is already under water
|
||||
by then, so its column hides the band and the seabed simply continues. */
|
||||
.site-footer-waterline {
|
||||
position: relative;
|
||||
height: clamp(9rem, 18vw, 15rem);
|
||||
overflow: hidden;
|
||||
background: var(--paper);
|
||||
}
|
||||
.site-footer-waterline > svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
main:has(.ocean-column) + .site-footer .site-footer-waterline { display: none; }
|
||||
.site-footer-main { border-top: 1px solid rgb(var(--c-stage-soft) / 0.13); }
|
||||
main:has(.ocean-column) + .site-footer .site-footer-main { border-top-color: transparent; }
|
||||
|
||||
.site-footer-main,
|
||||
.site-footer-meta {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.site-footer-main {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(16rem, 1.25fr) minmax(18rem, 0.75fr);
|
||||
gap: 4rem;
|
||||
padding-block: 2.8rem;
|
||||
}
|
||||
|
||||
.site-wordmark-footer { color: var(--stage-text); }
|
||||
.site-wordmark-footer img { display: block; width: auto; height: 18px; }
|
||||
.site-footer-brand p { max-width: 30rem; margin-top: 0.8rem; color: rgb(var(--c-stage-soft) / 0.62); font-size: 0.86rem; line-height: 1.6; }
|
||||
|
||||
.site-footer-links {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.site-footer-links > div { display: flex; flex-direction: column; gap: 0.55rem; }
|
||||
.site-footer-links span { margin-bottom: 0.25rem; color: var(--ocean-current); font-family: var(--font-mono), "IBM Plex Mono", monospace; font-size: 0.75rem; letter-spacing: 0.1em; text-transform: uppercase; }
|
||||
.site-footer-links a { color: rgb(var(--c-stage-soft) / 0.76); font-size: 0.82rem; }
|
||||
.site-footer-links a:hover { color: var(--stage-text); }
|
||||
|
||||
.site-footer-meta {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
padding-block: 1rem;
|
||||
border-top: 1px solid rgb(var(--c-stage-soft) / 0.13);
|
||||
color: rgb(var(--c-stage-soft) / 0.5);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.site-footer-meta a:hover { color: var(--ocean-current); }
|
||||
.site-footer-meta > div { display: flex; gap: 1rem; }
|
||||
.site-footer-meta > p { min-width: 0; max-width: 70ch; overflow-wrap: anywhere; }
|
||||
|
||||
/* ---------- ticker ---------- */
|
||||
.ticker-viewport {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ticker-track {
|
||||
display: inline-flex;
|
||||
gap: 3rem;
|
||||
white-space: nowrap;
|
||||
animation: ticker 80s linear infinite;
|
||||
padding-right: 3rem;
|
||||
}
|
||||
@keyframes ticker {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
}
|
||||
|
||||
/* One wire entry: verb, number or tag, title, by-line, age. Baseline-aligned
|
||||
so the mono verb sits on the same line as the title it qualifies. */
|
||||
.ticker-item {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* The verb carries the state. Merges are the headline case, so they get the
|
||||
one warm-neutral ink on the sheet; releases take ochre; anything closed
|
||||
recedes to mute. Everything else is the action blue. */
|
||||
.ticker-verb {
|
||||
color: var(--indigo);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ticker-verb[data-event="merged"] { color: var(--jade); }
|
||||
.ticker-verb[data-event="published"] { color: var(--ochre); }
|
||||
.ticker-verb[data-event="closed"] { color: var(--ink-mute); }
|
||||
|
||||
.ticker-num,
|
||||
.ticker-tag,
|
||||
.ticker-by,
|
||||
.ticker-age {
|
||||
color: var(--ink-mute);
|
||||
}
|
||||
|
||||
.ticker-title {
|
||||
color: var(--ink);
|
||||
max-width: 24rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* The contributor's handle is the point of the line, not a footnote. */
|
||||
.ticker-handle {
|
||||
color: var(--ink);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ticker-first {
|
||||
padding: 0 0.35rem;
|
||||
border: 1px solid var(--ochre);
|
||||
color: var(--ochre);
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ticker-sep { color: var(--paper-line-soft); }
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.ticker-track { animation: none; }
|
||||
/* A frozen track would clip every entry past the fold with no way to reach
|
||||
them. Stopping the motion must not also remove the content. */
|
||||
.ticker-viewport { overflow-x: auto; }
|
||||
}
|
||||
|
||||
/* ---------- ambient ---------- */
|
||||
/*
|
||||
* Two effects, both OPT-IN under `no-preference` rather than opted out under
|
||||
* `reduce`. A `reduce` override can be defeated by specificity; a gate cannot.
|
||||
* Reduced motion freezes the field — it never removes it, so the column's
|
||||
* gradient and the whale below are unconditional and only the movement is not.
|
||||
*
|
||||
* The TUI's ambient life — fish, jellyfish, bubbles, the whale cameo — stays
|
||||
* out of scope. This page quotes the product's restraint doctrine in its own
|
||||
* copy. The standing loops on the whole site are four — ticker, caret, and
|
||||
* these two. Entrances are transitions, and the one-shot settles live in the
|
||||
* Motion section below under the same no-preference gate.
|
||||
*/
|
||||
|
||||
/* M1 — the column breath. Opacity only, on a 90s cycle: the TUI's authored
|
||||
0.018–0.055 phase bias expressed as the same fraction of a mix. Opacity is
|
||||
the one property that cannot cause layout work on an element four bands
|
||||
tall. No position change, no hue shift, no filter. */
|
||||
@keyframes cw-breath {
|
||||
0%, 100% { opacity: 0; }
|
||||
50% { opacity: 0.045; }
|
||||
}
|
||||
|
||||
/* M2 — the caustic. `ambient_life.rs`'s literal behaviour: ~1.3s crossing the
|
||||
mark, parked off-canvas for the remaining ~2.7s, peak 0.33. The easing is a
|
||||
sine in-out, i.e. the raised cosine it is meant to be. */
|
||||
@keyframes cw-caustic {
|
||||
0% { transform: translateX(0); }
|
||||
32.5% { transform: translateX(1560px); }
|
||||
100% { transform: translateX(1560px); }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.ocean-column::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
background: var(--stage-ambient);
|
||||
opacity: 0;
|
||||
animation: cw-breath 90s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.codewhale-caustic {
|
||||
animation: cw-caustic 4s cubic-bezier(0.37, 0, 0.63, 1) infinite;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Shared surface states — empty / loading / error (surface-state.tsx) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* One plate for every data-bearing page's non-data moments. The mark on the
|
||||
* left carries the state in colour AND the copy carries it in words, so no
|
||||
* state is conveyed by colour alone: mute for empty, action blue for loading,
|
||||
* coral for error — the status-bar grammar the product already speaks.
|
||||
*/
|
||||
.state-block {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
padding: clamp(1.5rem, 4vw, 2.25rem) clamp(1.25rem, 3vw, 1.75rem);
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
background: var(--paper-card);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.state-block-compact {
|
||||
padding: 1rem 1.15rem;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.state-mark {
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
margin-top: 0.42rem;
|
||||
border-radius: 999px;
|
||||
background: var(--ink-mute);
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.state-block-loading .state-mark { background: var(--indigo); }
|
||||
.state-block-error .state-mark { background: var(--ocean-coral); }
|
||||
.state-block-error { border-color: color-mix(in srgb, var(--ocean-coral) 45%, var(--stage-line)); }
|
||||
|
||||
.state-copy { min-width: 0; }
|
||||
|
||||
.state-title {
|
||||
color: var(--ink);
|
||||
font-size: 0.98rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.state-body {
|
||||
max-width: 44rem;
|
||||
margin-top: 0.4rem;
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.86rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.state-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.state-retry[aria-busy="true"] { opacity: 0.7; cursor: progress; }
|
||||
|
||||
/* Skeleton lines: neutral bars, never fake words. The shimmer is opt-in
|
||||
under no-preference; reduced motion sees still bars. */
|
||||
.state-skeleton {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
margin-top: 0.85rem;
|
||||
}
|
||||
|
||||
.state-skeleton > span {
|
||||
display: block;
|
||||
height: 0.7rem;
|
||||
border-radius: 3px;
|
||||
background: color-mix(in srgb, var(--ink-mute) 22%, transparent);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.state-skeleton > span {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
color-mix(in srgb, var(--ink-mute) 18%, transparent) 0%,
|
||||
color-mix(in srgb, var(--ink-mute) 34%, transparent) 50%,
|
||||
color-mix(in srgb, var(--ink-mute) 18%, transparent) 100%
|
||||
);
|
||||
background-size: 200% 100%;
|
||||
animation: state-shimmer 1.6s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes state-shimmer {
|
||||
from { background-position: 200% 0; }
|
||||
to { background-position: -200% 0; }
|
||||
}
|
||||
|
||||
/* Route-level boundaries (loading.tsx / error.tsx / not-found.tsx) sit in
|
||||
the site container with the page rhythm so a boundary never looks like a
|
||||
different site. */
|
||||
.route-state {
|
||||
width: var(--container);
|
||||
margin-inline: auto;
|
||||
padding-block: clamp(3rem, 6vw, 4.75rem);
|
||||
max-width: 52rem;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Connection banner — offline / reconnecting / degraded / restored */
|
||||
/* ------------------------------------------------------------------ */
|
||||
.connection-banner {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 35;
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||
gap: 0.9rem;
|
||||
align-items: center;
|
||||
width: var(--container);
|
||||
margin: 0.75rem auto 0;
|
||||
padding: 0.7rem 1rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 6px;
|
||||
background: color-mix(in srgb, var(--paper-card) 92%, transparent);
|
||||
backdrop-filter: blur(8px);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.connection-mark {
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
border-radius: 999px;
|
||||
background: var(--ink-mute);
|
||||
}
|
||||
|
||||
.connection-banner-offline { border-color: color-mix(in srgb, var(--ocean-coral) 50%, var(--stage-line)); }
|
||||
.connection-banner-offline .connection-mark { background: var(--ocean-coral); }
|
||||
.connection-banner-reconnecting .connection-mark { background: var(--indigo); }
|
||||
.connection-banner-degraded .connection-mark { background: var(--ochre); }
|
||||
.connection-banner-restored { border-color: color-mix(in srgb, var(--jade) 50%, var(--stage-line)); }
|
||||
.connection-banner-restored .connection-mark { background: var(--jade); }
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.connection-banner-reconnecting .connection-mark {
|
||||
animation: connection-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
.connection-banner {
|
||||
animation: mm-in 200ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes connection-pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.35; }
|
||||
}
|
||||
|
||||
.connection-copy { min-width: 0; }
|
||||
.connection-title { font-size: 0.86rem; font-weight: 600; }
|
||||
.connection-body { margin-top: 0.15rem; color: var(--ink-soft); font-size: 0.78rem; line-height: 1.5; }
|
||||
.connection-checked { color: var(--ink-mute); font-family: var(--font-mono), "IBM Plex Mono", monospace; font-size: 0.75rem; }
|
||||
|
||||
.connection-actions { display: flex; gap: 0.5rem; }
|
||||
|
||||
.connection-button {
|
||||
display: inline-flex;
|
||||
min-height: 2.25rem;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.75rem;
|
||||
border: 1px solid var(--stage-line);
|
||||
border-radius: 5px;
|
||||
color: var(--ink);
|
||||
font-family: var(--font-mono), "IBM Plex Mono", monospace;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
|
||||
}
|
||||
|
||||
.connection-button:hover { border-color: var(--indigo); color: var(--indigo); }
|
||||
.connection-button-primary { border-color: var(--indigo); background: var(--indigo); color: var(--paper); }
|
||||
.connection-button-primary:hover { background: var(--indigo-deep); border-color: var(--indigo-deep); color: var(--paper); }
|
||||
.connection-button:disabled { opacity: 0.55; cursor: not-allowed; }
|
||||
.connection-button[aria-busy="true"] { cursor: progress; }
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.connection-banner {
|
||||
grid-template-columns: auto minmax(0, 1fr);
|
||||
}
|
||||
.connection-actions {
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -0,0 +1,176 @@
|
||||
/* ---------- root tokens — GPUI theme ---------- */
|
||||
/*
|
||||
* The site is a folio read in the product client's light: warm paper at the
|
||||
* surface, warm charcoal at depth. Above the waterline the field is the GPUI
|
||||
* light theme's paper and the ink is its plum-charcoal foreground. Below the
|
||||
* waterline (`.ocean-column`, the footer, every terminal plate) the same
|
||||
* names resolve to the dark charcoal tokens, so a component written once
|
||||
* reads correctly on either side.
|
||||
*
|
||||
* The token NAMES keep their historical shape (`--paper`, `--ink`,
|
||||
* `--indigo`) so every component rule stays diffable; the VALUES resolve
|
||||
* through the `--gpui-*` block below, which now references the generated
|
||||
* Shoreline tokens in ./tokens.css (crates/palette/src/tokens.rs is the
|
||||
* single source — the same constants the GPUI client and the TUI open on).
|
||||
* Only values with no Shoreline slot stay literal: the hover tones, the
|
||||
* accent washes, and the AA-adapted state hues. `--whale-*`/`--light-*`
|
||||
* remain available to anything that genuinely wants the terminal palette.
|
||||
*/
|
||||
:root {
|
||||
/* --- The GPUI source palette (generated Shoreline slots) --- */
|
||||
--gpui-paper: var(--shoreline-light-surface); /* light background — warm paper */
|
||||
--gpui-paper-raised: var(--shoreline-light-elevated); /* light card/popover */
|
||||
--gpui-paper-muted: var(--shoreline-light-panel); /* light muted surface */
|
||||
--gpui-ink: var(--shoreline-light-text-body); /* light foreground */
|
||||
--gpui-ink-soft: var(--shoreline-light-text-soft);
|
||||
--gpui-ink-mute: var(--shoreline-light-text-muted); /* light muted_foreground */
|
||||
--gpui-edge: var(--shoreline-light-border); /* light border */
|
||||
--gpui-primary: var(--shoreline-light-action); /* light primary */
|
||||
--gpui-primary-hover: #00536d; /* light button_primary_hover — no Shoreline slot */
|
||||
--gpui-accent: #dcebee; /* light accent — no Shoreline slot */
|
||||
--gpui-selection: var(--shoreline-light-selection); /* light selection */
|
||||
|
||||
--gpui-stage: var(--shoreline-surface); /* dark background — warm charcoal */
|
||||
--gpui-stage-deep: var(--shoreline-chrome); /* deepest dark */
|
||||
--gpui-stage-muted: var(--shoreline-panel); /* dark muted surface */
|
||||
--gpui-stage-raised: var(--shoreline-elevated); /* raised dark plate */
|
||||
--gpui-stage-ink: var(--shoreline-text-body); /* dark foreground */
|
||||
--gpui-stage-ink-soft: var(--shoreline-text-muted); /* dark muted_foreground */
|
||||
--gpui-stage-ink-dim: var(--shoreline-text-dim);
|
||||
--gpui-stage-edge: var(--shoreline-border); /* dark border */
|
||||
--gpui-primary-dark: var(--shoreline-action); /* dark primary */
|
||||
--gpui-primary-hover-dark: #93cde1; /* dark button_primary_hover — no Shoreline slot */
|
||||
--gpui-on-primary: #102d38; /* dark primary_foreground — no Shoreline slot */
|
||||
--gpui-accent-dark: #303f48; /* dark accent — no Shoreline slot */
|
||||
--gpui-selection-dark: var(--shoreline-selection); /* dark selection */
|
||||
|
||||
/* Warm-muted state hues from the GPUI mockups, darkened or lifted per
|
||||
side of the waterline so text uses of them still clear AA. No Shoreline
|
||||
slots: these are site-specific adaptations. */
|
||||
--gpui-tan: #b1a17a; /* human mark */
|
||||
--gpui-moss: #739686; /* outcome */
|
||||
--gpui-rust: #aa6f6c; /* attention */
|
||||
|
||||
/* RGB channel triples backing the Tailwind surface/ink tokens (see
|
||||
tailwind.config.ts). `.ocean-column` and the opt-in docs dark sheet
|
||||
override them for their dark subtree. */
|
||||
--c-paper: var(--shoreline-light-surface-rgb);
|
||||
--c-paper-deep: var(--shoreline-light-panel-rgb);
|
||||
--c-paper-edge: var(--shoreline-light-border-rgb);
|
||||
--c-ink: var(--shoreline-light-text-body-rgb);
|
||||
--c-ink-soft: var(--shoreline-light-text-soft-rgb);
|
||||
--c-ink-mute: var(--shoreline-light-text-muted-rgb);
|
||||
--c-indigo: var(--shoreline-light-action-rgb);
|
||||
--c-indigo-deep: 0 83 109; /* light primary hover — no Shoreline slot */
|
||||
--c-stage-soft: var(--shoreline-text-muted-rgb);
|
||||
--c-stage-deep: var(--shoreline-chrome-rgb);
|
||||
--c-primary-dark: var(--shoreline-action-rgb);
|
||||
|
||||
/* The sheet. */
|
||||
--paper: var(--gpui-paper);
|
||||
--paper-deep: var(--gpui-paper-muted);
|
||||
--paper-edge: var(--gpui-edge);
|
||||
--paper-card: var(--gpui-paper-raised);
|
||||
--paper-line: var(--gpui-edge);
|
||||
--paper-line-soft: var(--gpui-edge);
|
||||
--ink: var(--gpui-ink);
|
||||
--ink-soft: var(--gpui-ink-soft);
|
||||
--ink-mute: var(--gpui-ink-mute);
|
||||
/* Action on paper is the GPUI light primary; hover sinks to its hover. */
|
||||
--indigo: var(--gpui-primary);
|
||||
--indigo-deep: var(--gpui-primary-hover);
|
||||
--indigo-pale: rgb(var(--c-indigo) / 0.1);
|
||||
--action-on-dark: var(--gpui-primary-dark);
|
||||
/* State inks darkened from the mockup hues so they clear AA on paper:
|
||||
moss is outcome, rust is attention, tan is the human mark. */
|
||||
--ochre: #7d6a3f;
|
||||
--jade: #4e6f61;
|
||||
--seafoam: var(--gpui-moss);
|
||||
--cobalt: var(--gpui-primary);
|
||||
--cyan: var(--gpui-primary); /* reserved for the composer prompt glyph and the release line */
|
||||
--ocean-deep: var(--gpui-stage-deep);
|
||||
--ocean-mid: var(--gpui-stage-muted);
|
||||
--ocean-current: var(--gpui-primary-dark);
|
||||
--ocean-mist: var(--gpui-stage-ink-soft);
|
||||
--ocean-coral: #8f5a56;
|
||||
--signal-gold: #7d6a3f;
|
||||
/* The whale mark is ink on paper; below the waterline it is the light
|
||||
stage foreground. The warm tan stays reserved for human moments. */
|
||||
--mark-ink: var(--gpui-ink);
|
||||
--stage-text: var(--gpui-stage-ink);
|
||||
--stage-soft: var(--gpui-stage-ink-soft);
|
||||
--stage-muted: var(--gpui-stage-ink-dim);
|
||||
|
||||
/* The water column, re-inked in the GPUI dark ramp. */
|
||||
--stage-field-top: var(--gpui-stage-muted); /* surface */
|
||||
--stage-field-mid: var(--gpui-stage); /* the authored 42% break */
|
||||
--stage-field-deep: var(--gpui-stage-deep); /* deep field, and the footer seabed */
|
||||
--stage-ambient: var(--gpui-accent-dark);
|
||||
--stage-composer: var(--gpui-stage-muted); /* the raised input plate */
|
||||
--stage-elevated: var(--gpui-stage-raised);
|
||||
--stage-line: var(--gpui-stage-edge); /* the charcoal border on stage */
|
||||
--stage-hint: var(--gpui-stage-ink-dim);
|
||||
--stage-dim: var(--gpui-stage-ink-soft);
|
||||
--violet: var(--gpui-selection-dark); /* 1px rules only, never text */
|
||||
|
||||
/* Newsreader carries h1/h2; Shannon Sans supplies body and small headings.
|
||||
The historic condensed role keeps its scale and weight, not a second face.
|
||||
IBM Plex Mono and the Unicode fallback stacks retain their own roles. */
|
||||
--font-body: var(--font-shannon-sans);
|
||||
--font-display: var(--font-shannon-sans);
|
||||
--font-cjk: "PingFang SC", "Hiragino Sans GB", "Source Han Serif SC",
|
||||
"Noto Serif CJK SC", serif;
|
||||
|
||||
/* Hairline + code surfaces routed through vars so the dark subtrees can
|
||||
re-ink them without touching every rule. Code plates are always the
|
||||
stage's own deepest field, on either side of the waterline. */
|
||||
--hairline: rgb(var(--c-ink) / 0.16);
|
||||
--code-bg: var(--gpui-stage-deep);
|
||||
--code-fg: var(--gpui-stage-ink);
|
||||
|
||||
/* One site container and one vertical rhythm. Every page gutter aligns
|
||||
with the nav; every hero and section shares a single padding token. */
|
||||
--container: min(100% - 2rem, 76rem);
|
||||
--hero-pad: clamp(3rem, 6vw, 4.75rem);
|
||||
--section-pad: clamp(2.75rem, 5vw, 4rem);
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
/*
|
||||
* Below the waterline. One rule, applied to every dark subtree — the water
|
||||
* column on the homepage, the footer seabed, the opt-in docs dark sheet
|
||||
* (`.docs-portal` is the docs layout root, which also carries `.docs-theme`)
|
||||
* — so a component never needs to know which side of the surface it is on.
|
||||
*/
|
||||
.ocean-column,
|
||||
.site-footer,
|
||||
html[data-theme="dark"] .docs-portal {
|
||||
--c-paper: var(--shoreline-surface-rgb);
|
||||
--c-paper-deep: var(--shoreline-panel-rgb);
|
||||
--c-paper-edge: var(--shoreline-border-rgb);
|
||||
--c-ink: var(--shoreline-text-body-rgb);
|
||||
--c-ink-soft: var(--shoreline-text-muted-rgb);
|
||||
--c-ink-mute: var(--shoreline-text-dim-rgb);
|
||||
--c-indigo: var(--shoreline-action-rgb);
|
||||
--c-indigo-deep: 147 205 225; /* lifted primary — hover on dark, no Shoreline slot */
|
||||
--paper: var(--gpui-stage);
|
||||
--paper-deep: var(--gpui-stage-muted);
|
||||
--paper-edge: var(--gpui-stage-edge);
|
||||
--paper-card: var(--gpui-stage-muted);
|
||||
--paper-line: var(--gpui-stage-edge);
|
||||
--paper-line-soft: var(--gpui-stage-edge);
|
||||
--ink: var(--gpui-stage-ink);
|
||||
--ink-soft: var(--gpui-stage-ink-soft);
|
||||
--ink-mute: var(--gpui-stage-ink-dim);
|
||||
--indigo: var(--gpui-primary-dark);
|
||||
--indigo-deep: var(--gpui-primary-hover-dark);
|
||||
--indigo-pale: rgb(var(--c-primary-dark) / 0.14);
|
||||
--ochre: #d6c78f;
|
||||
--jade: #9ec7b2;
|
||||
--cyan: var(--gpui-primary-dark);
|
||||
--ocean-coral: #d08a80;
|
||||
--signal-gold: #d6c78f;
|
||||
--mark-ink: var(--gpui-stage-ink);
|
||||
--hairline: rgb(var(--c-stage-soft) / 0.2);
|
||||
color-scheme: dark;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Motion — entrances, considered hovers, and one-shot settles */
|
||||
/* ------------------------------------------------------------------ */
|
||||
/*
|
||||
* Grammar: the page is complete and static; motion answers a person's action
|
||||
* (a hover draws a rule, a press stamps a copy). The one authored ambient
|
||||
* moment is the column breath above. Everything here animates transform
|
||||
* and/or opacity only and is gated on `no-preference`, so reduced motion is
|
||||
* the *absence* of motion rather than an override fighting it.
|
||||
*/
|
||||
/* The nav-link underline draw, extended to the other mono-voice links. Hover
|
||||
draws the rule; the transition is the whole effect, so reduced motion keeps
|
||||
the existing colour change and simply skips the draw. */
|
||||
.product-start-links a,
|
||||
.gs-step-link,
|
||||
.product-surfaces > .product-container > a,
|
||||
.product-community nav a {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.product-start-links a::after,
|
||||
.gs-step-link::after,
|
||||
.product-surfaces > .product-container > a::after,
|
||||
.product-community nav a::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: -2px;
|
||||
height: 1px;
|
||||
background: currentColor;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 180ms ease;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.product-start-links a:hover::after,
|
||||
.gs-step-link:hover::after,
|
||||
.product-surfaces > .product-container > a:hover::after,
|
||||
.product-community nav a:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* The primary-button arrow leans into the direction it points. */
|
||||
.product-button > span[aria-hidden="true"] {
|
||||
display: inline-block;
|
||||
transition: transform 150ms ease;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.product-button:hover > span[aria-hidden="true"],
|
||||
.product-button:focus-visible > span[aria-hidden="true"] {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy-to-clipboard: the press is a stamp, the confirmation a jade seal —
|
||||
the wire's merge verb colour, borrowed for one beat. The colour change is
|
||||
unconditional (it is the confirmation); only the motion is gated. */
|
||||
.copy-btn[data-copied="true"] {
|
||||
background: var(--jade);
|
||||
color: var(--paper);
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.copy-btn {
|
||||
transition: background-color 150ms ease, color 150ms ease, transform 120ms ease;
|
||||
}
|
||||
|
||||
.copy-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.copy-btn[data-copied="true"] {
|
||||
animation: copy-settle 300ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes copy-settle {
|
||||
0% { transform: scale(1); }
|
||||
35% { transform: scale(0.93); }
|
||||
70% { transform: scale(1.04); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Mobile menu: the sheet drops a few points while its items settle in
|
||||
sequence; exit is a short fade. The exit is driven by a delayed unmount in
|
||||
components/mobile-menu.tsx — with reduced motion the menu mounts and
|
||||
unmounts instantly, exactly as before. */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.mm-panel {
|
||||
animation: mm-in 240ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
|
||||
.mm-panel.mm-closing {
|
||||
animation: mm-out 170ms cubic-bezier(0.4, 0, 0.8, 0.4) both;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mm-panel li,
|
||||
.mm-panel nav > a {
|
||||
animation: mm-rise 280ms cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
|
||||
.mm-panel li:nth-child(1) { animation-delay: 34ms; }
|
||||
.mm-panel li:nth-child(2) { animation-delay: 58ms; }
|
||||
.mm-panel li:nth-child(3) { animation-delay: 82ms; }
|
||||
.mm-panel li:nth-child(4) { animation-delay: 106ms; }
|
||||
.mm-panel li:nth-child(5) { animation-delay: 130ms; }
|
||||
.mm-panel li:nth-child(6) { animation-delay: 154ms; }
|
||||
.mm-panel li:nth-child(7) { animation-delay: 178ms; }
|
||||
.mm-panel li:nth-child(8) { animation-delay: 202ms; }
|
||||
.mm-panel nav > a { animation-delay: 226ms; }
|
||||
}
|
||||
|
||||
@keyframes mm-in {
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes mm-out {
|
||||
from { opacity: 1; transform: translateY(0); }
|
||||
to { opacity: 0; transform: translateY(-6px); }
|
||||
}
|
||||
|
||||
@keyframes mm-rise {
|
||||
from { opacity: 0; transform: translateY(6px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ---------- decorative big CJK in margin ---------- */
|
||||
.margin-glyph {
|
||||
font-family: var(--font-cjk), "PingFang SC", "Source Han Serif SC", serif;
|
||||
font-weight: 700;
|
||||
color: var(--ink);
|
||||
opacity: 0.04;
|
||||
font-size: 18rem;
|
||||
line-height: 0.9;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* ---------- focus + selection ---------- */
|
||||
::selection { background: var(--indigo); color: var(--paper); }
|
||||
:focus-visible { outline: 2px solid var(--indigo); outline-offset: 2px; }
|
||||
|
||||
/* ---------- link reset ---------- */
|
||||
a { color: inherit; text-decoration: none; }
|
||||
a.body-link {
|
||||
color: var(--ink);
|
||||
background-image: linear-gradient(var(--indigo), var(--indigo));
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 100%;
|
||||
background-size: 100% 1px;
|
||||
transition: background-size 180ms ease;
|
||||
}
|
||||
a.body-link:hover { background-size: 100% 6px; color: var(--ink); }
|
||||
|
||||
/* ---------- mobile-only adjustments ---------- */
|
||||
@media (max-width: 640px) {
|
||||
/* Big CJK margin glyph already hidden via tailwind's `hidden lg:block`,
|
||||
but re-assert that nothing hits the viewport edge. */
|
||||
.margin-glyph { display: none; }
|
||||
|
||||
/* Ticker text gets cramped on phones — shrink + tighten gaps */
|
||||
.ticker-track { gap: 1.5rem; padding-right: 1.5rem; }
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.site-nav-inner { gap: 1rem; }
|
||||
.site-github-link { display: none; }
|
||||
.site-footer-main { grid-template-columns: 1fr; gap: 2.5rem; }
|
||||
.site-footer-meta { grid-template-columns: minmax(0, 1fr); gap: 0.7rem; }
|
||||
.site-footer-meta > div { min-width: 0; flex-wrap: wrap; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.site-wordmark { font-size: 1.05rem; }
|
||||
}
|
||||
|
||||
/* Anchor fallback; page-level scroll-mt utilities should be able to override it. */
|
||||
:where([id]) { scroll-margin-top: 5rem; }
|
||||
@@ -1,8 +1,9 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveWhale } from "./whale-tokens";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
const CSS = readFileSync(new URL("../app/globals.css", import.meta.url), "utf8");
|
||||
const CSS = siteCss();
|
||||
|
||||
function selectorBlock(selector: string): string {
|
||||
const match = CSS.match(
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
secondaryNavLinks as buildSecondaryNavLinks,
|
||||
} from "./i18n/links";
|
||||
import { SITE_URL } from "./page-meta";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
const webRoot = new URL("../", import.meta.url);
|
||||
const repoRoot = new URL("../../", import.meta.url);
|
||||
@@ -38,7 +39,7 @@ const navLinks = webText("components/nav-links.tsx");
|
||||
const mobileMenu = webText("components/mobile-menu.tsx");
|
||||
const footer = webText("components/footer.tsx");
|
||||
const localeLayout = webText("app/[locale]/layout.tsx");
|
||||
const css = webText("app/globals.css");
|
||||
const css = siteCss();
|
||||
|
||||
describe("docs-map registration", () => {
|
||||
it("registers the guide and vocabulary topics as first-party pages", () => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveWhale } from "./whale-tokens";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
const CSS = readFileSync(new URL("../app/globals.css", import.meta.url), "utf8");
|
||||
const CSS = siteCss();
|
||||
|
||||
function selectorBlock(selector: string): string {
|
||||
const match = CSS.match(new RegExp(`(?:^|\n)${selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*\\{([^}]*)\\}`, "s"));
|
||||
|
||||
@@ -16,6 +16,7 @@ import { locales } from "./config";
|
||||
import { getChrome } from "./dictionaries";
|
||||
import { navLinks } from "./links";
|
||||
import { isDocsPath, replacePathLocale } from "./path";
|
||||
import { siteCss } from "../site-css";
|
||||
|
||||
const webRoot = new URL("../../", import.meta.url);
|
||||
|
||||
@@ -35,7 +36,7 @@ function advance(text: string, fontRem: number): number {
|
||||
}
|
||||
|
||||
describe("localized chrome keeps a clickable home control", () => {
|
||||
const css = webText("app/globals.css");
|
||||
const css = siteCss();
|
||||
const theme = webText("components/theme-toggle.tsx");
|
||||
|
||||
it("keeps the wordmark and locale switcher from shrinking to zero", () => {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { SNIPPETS, VERIFY } from "./install-binary-snippets";
|
||||
import { getChrome, getHome } from "./i18n/dictionaries";
|
||||
import { footerProjectLinks } from "./i18n/links";
|
||||
import { TERMINAL_SCREENSHOT } from "./media-manifest";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
const root = new URL("../../", import.meta.url);
|
||||
|
||||
@@ -643,7 +644,7 @@ done
|
||||
});
|
||||
|
||||
it("keeps reduced motion static without hiding the reasoning trace", () => {
|
||||
const css = text("web/app/globals.css");
|
||||
const css = siteCss();
|
||||
|
||||
expect(css).toMatch(
|
||||
/@media \(prefers-reduced-motion: reduce\)\s*\{[\s\S]*?\.ticker-track\s*\{\s*animation:\s*none;\s*\}[\s\S]*?\}/,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
/**
|
||||
* The hand-written site stylesheet as one string, in cascade order.
|
||||
*
|
||||
* `app/globals.css` is an import hub; its rules live in `app/styles/*.css`.
|
||||
* This inlines each `./styles/` import in place so the contract tests can read
|
||||
* the stylesheet the way the browser resolves it. The generated `tokens.css`
|
||||
* is left out, as it was before the split.
|
||||
*
|
||||
* Node-only (`node:fs`): imported by the contract tests, never by a component.
|
||||
*/
|
||||
export function siteCss(): string {
|
||||
const appDir = new URL("../app/", import.meta.url);
|
||||
const hub = readFileSync(new URL("globals.css", appDir), "utf8");
|
||||
return hub.replace(/^@import\s+"\.\/(styles\/[\w-]+\.css)";$/gm, (_, path: string) =>
|
||||
readFileSync(new URL(path, appDir), "utf8"),
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { siteCss } from "./site-css";
|
||||
|
||||
/**
|
||||
* The design palette as the site actually resolves it.
|
||||
*
|
||||
* `app/tokens.css` is generated from `crates/palette/src/tokens.rs` by
|
||||
* `scripts/export-design-tokens.py`, and `globals.css` carries the hand-kept
|
||||
* `scripts/export-design-tokens.py`, and `app/styles/tokens-roles.css` carries the hand-kept
|
||||
* `--gpui-*` block that mirrors the GPUI client's theme. Site variables state
|
||||
* which token each uses (`--paper: var(--gpui-paper)`) instead of repeating
|
||||
* the hex. The contract tests still need the literal color to check parity
|
||||
@@ -19,7 +20,7 @@ import { readFileSync } from "node:fs";
|
||||
*/
|
||||
const RAW: Record<string, string> = (() => {
|
||||
const generated = readFileSync(new URL("../app/tokens.css", import.meta.url), "utf8");
|
||||
const globals = readFileSync(new URL("../app/globals.css", import.meta.url), "utf8");
|
||||
const globals = siteCss();
|
||||
const raw: Record<string, string> = {};
|
||||
for (const match of generated.matchAll(/--((?:whale|light|shoreline-light|shoreline)-[\w-]+):\s*([^;]+);/g)) {
|
||||
raw[match[1]] = match[2].trim();
|
||||
@@ -28,7 +29,7 @@ const RAW: Record<string, string> = (() => {
|
||||
raw[match[1]] = match[2].trim();
|
||||
}
|
||||
if (Object.keys(raw).length === 0) {
|
||||
throw new Error("no palette properties found in tokens.css/globals.css");
|
||||
throw new Error("no palette properties found in tokens.css/styles");
|
||||
}
|
||||
return raw;
|
||||
})();
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
// Inline the app/styles/* partials into globals.css before Tailwind runs.
|
||||
// Tailwind expands each file on its own and appends variant utilities
|
||||
// (hover:, md:, ...) at the end of the file that holds `@tailwind
|
||||
// utilities`; inlining keeps them after every partial, as they were when
|
||||
// globals.css was one file. tokens.css stays a separate module.
|
||||
"postcss-import": { filter: (path) => path.startsWith("./styles/") },
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user