1346 lines
37 KiB
CSS
1346 lines
37 KiB
CSS
body {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
:root {
|
|
color-scheme: light dark;
|
|
--bg-base: light-dark(#FFFFFF, #0a0a0a);
|
|
--bg-surface: light-dark(#F9F9F9, #0f0f0f);
|
|
--bg-elevated: light-dark(#F1F1F1, #151515);
|
|
--bg-hover: light-dark(#E5E5E5, #1f1f1f);
|
|
|
|
--fg: light-dark(var(--fg-dark), var(--fg-light));
|
|
--fg-mute: light-dark(var(--fg-mute-dark), var(--fg-mute-light));
|
|
--bg-accent: light-dark(#0977d7, #0977d7);
|
|
--bg-accent-hover: color-mix(in srgb, var(--bg-accent), black 25%);
|
|
--bg-accent-click: color-mix(in srgb, var(--bg-accent), black 45%);
|
|
--fg-inv: light-dark(var(--fg-light), var(--fg-dark));
|
|
--fg-mute-inv: light-dark(var(--fg-mute-light), var(--fg-mute-dark));
|
|
--fg-light: #EAEAEA;
|
|
--fg-dark: #1A1A1A;
|
|
--fg-mute-light: #5A5A5A;
|
|
--fg-mute-dark: #7A7A7A;
|
|
|
|
--nav-button-padding: 1rem;
|
|
--nav-button-inside-gap: .5rem;
|
|
--content-pad: 3.5rem;
|
|
--padded-gap: 3rem;
|
|
--card-container-gap: 1rem;
|
|
--card-bottom-padding: .5rem;
|
|
--card-border-radius: .5rem;
|
|
--button-padding: 1rem;
|
|
--button-border-radius: .6rem;
|
|
--nav-height: 4rem; /* corrected at runtime by nav.js to the real value */
|
|
}
|
|
:root[data-theme="light"] {
|
|
color-scheme: light;
|
|
}
|
|
:root[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
}
|
|
|
|
* {
|
|
transition: background-color 0.2s ease, color 0.2s ease;
|
|
}
|
|
|
|
.padded {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: var(--content-pad);
|
|
gap: var(--padded-gap);
|
|
}
|
|
body {
|
|
background-color: var(--bg-base);
|
|
color: var(--fg);
|
|
font-family: "Roboto", sans-serif;
|
|
font-optical-sizing: auto;
|
|
font-weight: normal;
|
|
font-style: normal;
|
|
font-variation-settings: "wdth" 100;
|
|
}
|
|
/* Reusable <nexvane-nav>/<nexvane-footer> wrapper tags (see
|
|
scripts/components/nav-footer.js) should be invisible to layout —
|
|
the real <nav> and <footer> they render stay direct visual children
|
|
of <body>. */
|
|
nexvane-nav,
|
|
nexvane-footer {
|
|
display: contents;
|
|
}
|
|
|
|
nav {
|
|
top: 0;
|
|
z-index: 10000;
|
|
position: sticky;
|
|
background-color: var(--bg-surface);
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
user-select: none;
|
|
}
|
|
/* .nav-topbar, .nav-links and .nav-right are structural wrappers only.
|
|
On desktop they stay display:contents so their children behave exactly
|
|
like direct flex children of <nav>, reproducing the original layout
|
|
pixel-for-pixel. They only become real boxes on mobile (see media query). */
|
|
.nav-topbar,
|
|
.nav-menu,
|
|
.nav-links,
|
|
.nav-right {
|
|
display: contents;
|
|
}
|
|
.nav-right > :first-child {
|
|
margin-left: auto;
|
|
}
|
|
nav .nav-hamburger {
|
|
display: none;
|
|
}
|
|
|
|
/* --- LOGO ---
|
|
One SVG, colored via CSS mask + background-color (same technique as
|
|
the download/manual card icons in index.css) instead of two separate
|
|
light/dark PNGs — background-color inherits the already
|
|
theme-aware --fg variable, so light/dark mode "just works" without
|
|
swapping image files or crossfading between them. */
|
|
.nav-logo-mark {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
flex-shrink: 0;
|
|
background-color: var(--fg);
|
|
-webkit-mask: url("../img/logo/nexvane-logo.svg") center / contain no-repeat;
|
|
mask: url("../img/logo/nexvane-logo.svg") center / contain no-repeat;
|
|
}
|
|
|
|
.nav-button {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: var(--nav-button-inside-gap);
|
|
padding: .85rem var(--nav-button-padding);
|
|
position: relative;
|
|
color: inherit;
|
|
text-decoration: none;
|
|
}
|
|
/* <p> keeps its default user-agent margin otherwise — invisible on
|
|
desktop where the nav's own fixed height already clips it, but on
|
|
mobile each stacked row sizes to its content, so that margin turns
|
|
into a large, obviously-unintended gap between rows. */
|
|
.nav-button p {
|
|
margin: 0;
|
|
}
|
|
.nav-button:hover {
|
|
background-color: var(--bg-hover);
|
|
cursor: pointer;
|
|
}
|
|
.nav-button-icon {
|
|
margin-right: .5rem;
|
|
}
|
|
/* --- DROPDOWN CSS --- */
|
|
.dropdown-content {
|
|
display: none;
|
|
flex-direction: column;
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
background-color: var(--bg-elevated);
|
|
min-width: 200px;
|
|
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.5);
|
|
z-index: 100;
|
|
}
|
|
.dropdown-right {
|
|
left: auto;
|
|
right: 0;
|
|
}
|
|
.nav-button:hover .dropdown-content {
|
|
display: flex;
|
|
}
|
|
.dropdown-content a {
|
|
color: var(--fg);
|
|
padding: 1rem var(--nav-button-padding);
|
|
text-decoration: none;
|
|
font-size: 0.95rem;
|
|
}
|
|
.dropdown-content a:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
/* --- BANNER --- */
|
|
.banner {
|
|
height: 70vh;
|
|
background-position: center;
|
|
background-size: cover;
|
|
background-repeat: no-repeat;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
.banner > p {
|
|
padding: 0;
|
|
margin: 0;
|
|
user-select: none;
|
|
color: var(--fg-light);
|
|
}
|
|
.darkened-banner {
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
background-blend-mode: darken;
|
|
}
|
|
.banner-title {
|
|
font-weight: bolder;
|
|
font-size: 5rem;
|
|
}
|
|
.banner-subheading {
|
|
font-weight: bold;
|
|
font-size: 2rem;
|
|
}
|
|
/*--- CARDS ---*/
|
|
.card-container {
|
|
display: grid;
|
|
gap: var(--card-container-gap);
|
|
justify-content: center;
|
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), calc(33.333% - 0.7rem)));
|
|
}
|
|
.card {
|
|
padding: .5rem;
|
|
display: grid;
|
|
grid-template-rows: repeat(2, 1fr);
|
|
grid-template-columns: 1fr;
|
|
height: 75vh;
|
|
background-color: var(--bg-elevated);
|
|
border-radius: var(--card-border-radius);
|
|
transition: transform 0.2s ease, background-color 0.2s ease;
|
|
}
|
|
.card:hover {
|
|
background-color: var(--bg-hover);
|
|
cursor: pointer;
|
|
user-select: none;
|
|
transform: scale(1.01);
|
|
}
|
|
.card-top {
|
|
background-position: center;
|
|
background-size: cover;
|
|
background-repeat: no-repeat;
|
|
}
|
|
.card-bottom {
|
|
padding: var(--card-bottom-padding);
|
|
gap: var(--card-bottom-padding);
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.card-text {
|
|
margin: 0;
|
|
line-height: 1.5;
|
|
}
|
|
.card-title {
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
margin: 1rem 0 1rem 0;
|
|
}
|
|
/*--- BUTTONS ---*/
|
|
.button {
|
|
color: var(--fg-inv);
|
|
font-weight: bold;
|
|
background-color: var(--bg-accent);
|
|
text-decoration: none;
|
|
padding: var(--button-padding);
|
|
border-radius: var(--button-border-radius);
|
|
text-align: center;
|
|
margin-top: auto;
|
|
}
|
|
.button:hover {
|
|
background-color: var(--bg-accent-hover);
|
|
color: var(--fg);
|
|
}
|
|
.button:active {
|
|
background-color: var(--bg-accent-click);
|
|
}
|
|
h1 {
|
|
font-size: 2.5rem;
|
|
align-self: center;
|
|
}
|
|
|
|
/*--- FOOTER ---*/
|
|
footer
|
|
{
|
|
background-color: var(--bg-surface);
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: center;
|
|
}
|
|
|
|
.dashed-text-div
|
|
{
|
|
color: var(--fg-mute);
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.dashed-text-div > * + *::before
|
|
{
|
|
content: "-";
|
|
margin-right: 1rem;
|
|
}
|
|
|
|
/* =========================================================
|
|
DOC LAYOUT (sidebar + content + footer)
|
|
A reusable two-column layout for docs-style pages: a
|
|
navbar-styled sidebar (search box + collapsible node/leaf
|
|
tree — see the SIDEBAR block below, it's a separate,
|
|
generic component, not docs-specific) sticks below the
|
|
navbar and scrolls independently, next to a content column
|
|
that ends in the footer — the footer sits under the content
|
|
only, not full-bleed, and is in normal document flow (not
|
|
sticky).
|
|
========================================================= */
|
|
|
|
.doc-layout {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
/* =========================================================
|
|
LANGUAGE MODAL (generic — see scripts/components/lang-modal.js)
|
|
========================================================= */
|
|
|
|
.lang-modal-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 20000;
|
|
/* display:none by default; JS toggles the hidden attribute to show
|
|
it. This can't just be one unconditional `display: flex` — an
|
|
author-level display declaration always overrides the browser's
|
|
own [hidden]{display:none} rule (author CSS beats UA stylesheet
|
|
regardless of specificity), which would make `hidden = true` a
|
|
no-op and the modal impossible to close. */
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
padding: 1rem;
|
|
}
|
|
.lang-modal-overlay:not([hidden]) {
|
|
display: flex;
|
|
}
|
|
.lang-modal {
|
|
width: 100%;
|
|
max-width: 320px;
|
|
background-color: var(--bg-elevated);
|
|
border-radius: var(--card-border-radius);
|
|
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.5);
|
|
overflow: hidden;
|
|
}
|
|
.lang-modal-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 1rem 1.25rem;
|
|
}
|
|
.lang-modal-header p {
|
|
font-weight: bold;
|
|
margin: 0;
|
|
}
|
|
.lang-modal-close {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: none;
|
|
border: none;
|
|
color: var(--fg-mute);
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
padding: .25rem;
|
|
border-radius: var(--button-border-radius);
|
|
}
|
|
.lang-modal-close:hover {
|
|
background-color: var(--bg-hover);
|
|
color: var(--fg);
|
|
}
|
|
.lang-modal-options {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 0 0 .5rem 0;
|
|
}
|
|
.lang-modal-option {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
padding: .75rem 1.25rem;
|
|
background: none;
|
|
border: none;
|
|
font: inherit;
|
|
font-size: .95rem;
|
|
color: var(--fg);
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
.lang-modal-option:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
.lang-modal-check {
|
|
color: var(--bg-accent);
|
|
opacity: 0;
|
|
}
|
|
.lang-modal-option.active .lang-modal-check {
|
|
opacity: 1;
|
|
}
|
|
|
|
|
|
|
|
.sidebar {
|
|
position: sticky;
|
|
top: var(--nav-height);
|
|
flex-shrink: 0;
|
|
width: 280px;
|
|
height: calc(100vh - var(--nav-height));
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: var(--bg-surface);
|
|
}
|
|
|
|
.sidebar-search {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
padding: 0 .85rem;
|
|
height: 2.85rem;
|
|
flex-shrink: 0;
|
|
color: var(--fg-mute);
|
|
}
|
|
.sidebar-search input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
background: none;
|
|
border: none;
|
|
outline: none;
|
|
color: var(--fg);
|
|
font: inherit;
|
|
font-size: .9rem;
|
|
}
|
|
.sidebar-search input::placeholder {
|
|
color: var(--fg-mute);
|
|
}
|
|
/* Suggestions float below the search box instead of pushing the tree
|
|
down, so results can come from any registered SiteSearch entries —
|
|
not just this sidebar's own tree. */
|
|
.sidebar-search-results {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 20;
|
|
background-color: var(--bg-elevated);
|
|
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
|
|
max-height: 60vh;
|
|
overflow-y: auto;
|
|
}
|
|
.sidebar-search-result {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .1rem;
|
|
padding: .5rem .85rem;
|
|
color: var(--fg);
|
|
text-decoration: none;
|
|
}
|
|
.sidebar-search-result:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
.sidebar-search-result-title {
|
|
font-size: .85rem;
|
|
}
|
|
.sidebar-search-result-desc {
|
|
font-size: .75rem;
|
|
color: var(--fg-mute);
|
|
}
|
|
|
|
.sidebar-tree {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: .25rem 0;
|
|
/* Reserve the scrollbar's own space up front (rather than only once
|
|
the tree is tall enough to need it) so rows never have to resize
|
|
or sit flush against a track that just popped in. */
|
|
scrollbar-gutter: stable;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--bg-hover) transparent;
|
|
}
|
|
.sidebar-tree::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
.sidebar-tree::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
.sidebar-tree::-webkit-scrollbar-thumb {
|
|
background-color: var(--bg-hover);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.sidebar-tree-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
/* Only nested lists indent — each level of nesting shifts its own
|
|
children further right. Rows without a chevron reserve the same
|
|
width via .sidebar-tree-spacer (below), so this indentation is the
|
|
ONLY thing that changes with depth — a child's icon always sits to
|
|
the right of its parent's, never level with or before it. */
|
|
.sidebar-tree-list .sidebar-tree-list {
|
|
padding-left: .85rem;
|
|
}
|
|
|
|
/* Each row is chevron-hit (toggle) + link (navigate), side by side, so
|
|
a node can both expand/collapse AND go to its own page — the chevron
|
|
is a small dedicated hit target, the label is the rest of the row.
|
|
Hover is on the row so either half lights up the whole row. */
|
|
.sidebar-tree-row {
|
|
display: flex;
|
|
align-items: stretch;
|
|
width: 100%;
|
|
}
|
|
.sidebar-tree-row:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
.sidebar-tree-chevron-hit,
|
|
.sidebar-tree-spacer {
|
|
flex-shrink: 0;
|
|
width: 1.5rem;
|
|
}
|
|
.sidebar-tree-chevron-hit {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: none;
|
|
border: none;
|
|
color: var(--fg-mute);
|
|
cursor: pointer;
|
|
}
|
|
.sidebar-tree-chevron {
|
|
font-size: .65rem;
|
|
transition: transform 0.15s ease;
|
|
}
|
|
.sidebar-tree-chevron-hit[aria-expanded="true"] .sidebar-tree-chevron {
|
|
transform: rotate(90deg);
|
|
}
|
|
.sidebar-tree-link {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .4rem;
|
|
padding: .4rem .75rem .4rem .1rem;
|
|
color: var(--fg);
|
|
text-decoration: none;
|
|
background: none;
|
|
border: none;
|
|
font: inherit;
|
|
font-size: .88rem;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
.sidebar-tree-icon {
|
|
flex-shrink: 0;
|
|
font-size: .85rem;
|
|
color: var(--fg-mute);
|
|
}
|
|
/* The exact page you're on: full row highlight (covers the chevron too,
|
|
when there is one — a leaf's link is the row's only child anyway, so
|
|
this still just colors that one element for leafs). */
|
|
.sidebar-tree-row.active {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
.sidebar-tree-row.active .sidebar-tree-link,
|
|
.sidebar-tree-row.active .sidebar-tree-icon {
|
|
color: var(--bg-accent);
|
|
}
|
|
/* A section that CONTAINS the active page: a lighter touch (just the
|
|
icon), so you can see which part of the tree you're in without it
|
|
competing with the one row that's actually "you are here". */
|
|
.sidebar-tree-row.ancestor-active > .sidebar-tree-link > .sidebar-tree-icon {
|
|
color: var(--bg-accent);
|
|
}
|
|
|
|
.doc-main {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.doc-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: var(--content-pad);
|
|
}
|
|
|
|
/* =========================================================
|
|
DOC CONTENT (headings, prose, callouts, tables, TOC — the
|
|
visual language for writing documentation pages, all scoped
|
|
to .doc-content so it never touches the marketing page's own
|
|
h1 usage elsewhere on the site).
|
|
========================================================= */
|
|
|
|
/* Spacing: a flat `gap` on .doc-content gave every pair of
|
|
elements the same large space, so a heading looked just as
|
|
far from ITS OWN paragraph as from the next section entirely
|
|
— nothing read as "grouped". Margins let that differ: tight
|
|
right under a heading, roomy before the next h2/h3. */
|
|
.doc-content > * {
|
|
margin: 0;
|
|
}
|
|
.doc-content > * + * {
|
|
margin-top: 1.5rem;
|
|
}
|
|
.doc-content > h2 {
|
|
margin-top: 3rem;
|
|
}
|
|
.doc-content > h3 {
|
|
margin-top: 2rem;
|
|
}
|
|
.doc-content > h1 + * {
|
|
margin-top: 1rem;
|
|
}
|
|
.doc-content > h2 + *,
|
|
.doc-content > h3 + * {
|
|
margin-top: .6rem;
|
|
}
|
|
|
|
.doc-content h1,
|
|
.doc-content h2,
|
|
.doc-content h3 {
|
|
scroll-margin-top: calc(var(--nav-height) + 1rem);
|
|
}
|
|
.doc-content h1 {
|
|
font-size: 2.5rem;
|
|
align-self: flex-start;
|
|
}
|
|
.doc-content h2 {
|
|
font-size: 1.65rem;
|
|
}
|
|
.doc-content h3 {
|
|
font-size: 1.2rem;
|
|
}
|
|
.doc-content p {
|
|
line-height: 1.6;
|
|
}
|
|
.doc-content ul,
|
|
.doc-content ol {
|
|
padding-left: 1.4rem;
|
|
line-height: 1.6;
|
|
}
|
|
.doc-content li + li {
|
|
margin-top: .35rem;
|
|
}
|
|
.doc-content a {
|
|
color: var(--bg-accent);
|
|
text-decoration: none;
|
|
}
|
|
.doc-content a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* --- Callout / admonition boxes: scripts/components/doc-callout.js ---
|
|
Blended against --bg-elevated (not "transparent") so the tint stays
|
|
clearly visible even against a near-black page background — a
|
|
low-opacity color mixed toward transparent barely shifts anything
|
|
when the base it's sitting on is already almost black. */
|
|
.doc-callout {
|
|
--callout-color: var(--bg-accent);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: .35rem;
|
|
padding: 1rem 1.25rem;
|
|
border-radius: var(--card-border-radius);
|
|
border: 1px solid color-mix(in srgb, var(--callout-color) 50%, transparent);
|
|
background-color: color-mix(in srgb, var(--callout-color) 18%, var(--bg-elevated));
|
|
}
|
|
.doc-callout[type="tip"] {
|
|
--callout-color: #1f9d55;
|
|
}
|
|
.doc-callout[type="warning"] {
|
|
--callout-color: #c88719;
|
|
}
|
|
.doc-callout[type="error"] {
|
|
--callout-color: #d64545;
|
|
}
|
|
.doc-callout-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: .5rem;
|
|
font-weight: bold;
|
|
color: var(--callout-color);
|
|
}
|
|
.doc-callout-body {
|
|
color: var(--fg);
|
|
line-height: 1.6;
|
|
}
|
|
.doc-callout-body > :first-child {
|
|
margin-top: 0;
|
|
}
|
|
.doc-callout-body > :last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
/* --- Tabs: scripts/components/doc-tabs.js ---
|
|
The tab strip and panels are deliberately plain: a bottom-border
|
|
indicator on the active tab and a hairline box around the whole
|
|
thing, so it reads as "a variant switch for the content beneath it"
|
|
rather than competing for attention with the callouts above. */
|
|
.doc-tabs {
|
|
display: block;
|
|
margin: 1.5rem 0;
|
|
border: 1px solid var(--bg-hover);
|
|
border-radius: var(--card-border-radius);
|
|
overflow: hidden;
|
|
}
|
|
.doc-tabs-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
background-color: var(--bg-elevated);
|
|
border-bottom: 1px solid var(--bg-hover);
|
|
}
|
|
.doc-tabs-tab {
|
|
appearance: none;
|
|
background: none;
|
|
border: none;
|
|
/* Sits flush against .doc-tabs-list's own border-bottom so the
|
|
active indicator can sit exactly on top of it without a seam. */
|
|
border-bottom: 2px solid transparent;
|
|
margin-bottom: -1px;
|
|
padding: .65rem 1.1rem;
|
|
font: inherit;
|
|
font-size: .9rem;
|
|
font-weight: 600;
|
|
color: var(--fg-mute);
|
|
cursor: pointer;
|
|
}
|
|
.doc-tabs-tab:hover {
|
|
color: var(--fg);
|
|
background-color: var(--bg-hover);
|
|
}
|
|
.doc-tabs-tab.active {
|
|
color: var(--bg-accent);
|
|
border-bottom-color: var(--bg-accent);
|
|
}
|
|
.doc-tabs-panel {
|
|
/* doc-tab is never registered as a custom element (it's a pure
|
|
content container, styled and toggled entirely from here), so
|
|
without this it defaults to the browser's display:inline for
|
|
unknown elements — which doesn't establish a proper block
|
|
context for the <p>/<pre> inside it, so the padding below ends
|
|
up not actually insetting them (that's what was clipping the
|
|
first couple characters of every panel's text). */
|
|
display: block;
|
|
padding: 1.25rem;
|
|
/* Matches .doc-tabs-list's own background rather than staying
|
|
transparent — otherwise the only thing distinguishing the panel
|
|
from the plain page behind it is a 1px border, so the padding
|
|
around plain-text content reads as if it isn't there at all. */
|
|
background-color: var(--bg-elevated);
|
|
}
|
|
/* The display:block above ties with the browser's own [hidden] rule
|
|
for specificity, and an author rule wins that tie — so without
|
|
this, setting display here would quietly un-hide every inactive
|
|
panel at once. */
|
|
.doc-tabs-panel[hidden] {
|
|
display: none;
|
|
}
|
|
.doc-tabs-panel > :first-child {
|
|
margin-top: 0;
|
|
}
|
|
.doc-tabs-panel > :last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
/* A <pre> normally has its own background/border/padding (see below)
|
|
so it reads as a distinct block in ordinary prose. Inside a tab
|
|
panel that's already that same card, `pre` inheriting its own box
|
|
on top doubles up: a visible seam plus a second ring of padding
|
|
stacked on the panel's, which is what actually produced the odd
|
|
dead-space gap under the tab strip. Flatten it back to plain text
|
|
in a box so the panel's own padding is the only spacing at play. */
|
|
.doc-tabs-panel pre {
|
|
margin: 0;
|
|
padding: 0;
|
|
border: none;
|
|
background: none;
|
|
border-radius: 0;
|
|
}
|
|
|
|
/* --- Inline code & code blocks ---
|
|
Nothing styled these before now — harmless while content was plain
|
|
prose, but the tabs above exist specifically to hold code samples,
|
|
so an unstyled <pre> would undercut the very feature it's meant to
|
|
demo. */
|
|
code {
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
font-size: .9em;
|
|
}
|
|
:not(pre) > code {
|
|
background-color: var(--bg-hover);
|
|
padding: .15em .4em;
|
|
border-radius: .3em;
|
|
}
|
|
pre {
|
|
background-color: var(--bg-elevated);
|
|
border: 1px solid var(--bg-hover);
|
|
border-radius: var(--card-border-radius);
|
|
padding: 1rem 1.25rem;
|
|
overflow-x: auto;
|
|
}
|
|
pre code {
|
|
font-size: .85rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* --- Syntax-highlighted code blocks: scripts/components/doc-code.js ---
|
|
Shiki (real TextMate-grammar highlighting, VS Code's own Dark+/
|
|
Light+ themes) generates a fully self-styled <pre class="shiki">
|
|
with each token's colors set as inline styles/CSS variables — it
|
|
still lands on a plain `pre` element, though, so the sizing/border
|
|
rules above apply to it same as any other code block automatically.
|
|
All that's left is wiring Shiki's dual-theme CSS variables to this
|
|
site's own [data-theme] toggle (scripts/theme.js) instead of the
|
|
prefers-color-scheme media query Shiki defaults to, since here the
|
|
dark/light switch is a manual click, not just an OS setting.
|
|
|
|
The :not(...) exclusion matters once Twoslash is involved (see
|
|
below): its hover popups are themselves <span>s nested inside this
|
|
same <pre>, and since this rule needs !important to win over
|
|
Shiki's own inline per-token colors, it would otherwise force the
|
|
popup's background/text to match the surrounding code instead of
|
|
rendering as a distinct floating tooltip. */
|
|
[data-theme="light"] .shiki,
|
|
[data-theme="light"] .shiki span:not(.twoslash-popup-container, .twoslash-popup-container *) {
|
|
color: var(--shiki-light) !important;
|
|
background-color: var(--shiki-light-bg) !important;
|
|
font-style: var(--shiki-light-font-style) !important;
|
|
font-weight: var(--shiki-light-font-weight) !important;
|
|
text-decoration: var(--shiki-light-text-decoration) !important;
|
|
}
|
|
[data-theme="dark"] .shiki,
|
|
[data-theme="dark"] .shiki span:not(.twoslash-popup-container, .twoslash-popup-container *) {
|
|
color: var(--shiki-dark) !important;
|
|
background-color: var(--shiki-dark-bg) !important;
|
|
font-style: var(--shiki-dark-font-style) !important;
|
|
font-weight: var(--shiki-dark-font-weight) !important;
|
|
text-decoration: var(--shiki-dark-text-decoration) !important;
|
|
}
|
|
|
|
/* --- Tree-sitter output: scripts/components/doc-code.js ---
|
|
A real parser's output (currently just C++ — see TREE_SITTER_LANGS
|
|
in that file), not a TextMate grammar's, so there's no dual-theme
|
|
CSS-variable dance to do here: this is our own plain `tok-*` classes
|
|
with no inline styles competing for the cascade, so a normal (no
|
|
!important needed) color per class per side of the [data-theme]
|
|
toggle is all it takes. Colors match Dark+/Light+ token-for-token
|
|
where the concepts line up (type/function/variable/etc.), so a
|
|
tree-sitter block and a Shiki block sitting near each other on the
|
|
same page read as one consistent palette. */
|
|
[data-theme="dark"] .tree-sitter .tok-keyword { color: #569CD6; }
|
|
[data-theme="dark"] .tree-sitter .tok-type { color: #4EC9B0; }
|
|
[data-theme="dark"] .tree-sitter .tok-function,
|
|
[data-theme="dark"] .tree-sitter .tok-function-special { color: #DCDCAA; }
|
|
[data-theme="dark"] .tree-sitter .tok-variable,
|
|
[data-theme="dark"] .tree-sitter .tok-property { color: #9CDCFE; }
|
|
[data-theme="dark"] .tree-sitter .tok-variable-builtin { color: #569CD6; }
|
|
[data-theme="dark"] .tree-sitter .tok-string { color: #CE9178; }
|
|
[data-theme="dark"] .tree-sitter .tok-number { color: #B5CEA8; }
|
|
[data-theme="dark"] .tree-sitter .tok-comment { color: #6A9955; }
|
|
[data-theme="dark"] .tree-sitter .tok-constant { color: #4FC1FF; }
|
|
[data-theme="dark"] .tree-sitter .tok-label { color: #C8C8C8; }
|
|
|
|
[data-theme="light"] .tree-sitter .tok-keyword { color: #0000FF; }
|
|
[data-theme="light"] .tree-sitter .tok-type { color: #267F99; }
|
|
[data-theme="light"] .tree-sitter .tok-function,
|
|
[data-theme="light"] .tree-sitter .tok-function-special { color: #795E26; }
|
|
[data-theme="light"] .tree-sitter .tok-variable,
|
|
[data-theme="light"] .tree-sitter .tok-property { color: #001080; }
|
|
[data-theme="light"] .tree-sitter .tok-variable-builtin { color: #0000FF; }
|
|
[data-theme="light"] .tree-sitter .tok-string { color: #A31515; }
|
|
[data-theme="light"] .tree-sitter .tok-number { color: #098658; }
|
|
[data-theme="light"] .tree-sitter .tok-comment { color: #008000; }
|
|
[data-theme="light"] .tree-sitter .tok-constant { color: #0070C1; }
|
|
[data-theme="light"] .tree-sitter .tok-label { color: #383838; }
|
|
|
|
/* --- Twoslash hover popups: @shikijs/twoslash + twoslash-cdn, loaded
|
|
by scripts/components/doc-code.js only for a `twoslash`-marked code
|
|
block. The popup's own look (border, shadow, layout) comes from the
|
|
library's own style-rich.css, loaded alongside it — these just hand
|
|
its color variables this site's own palette for each half of the
|
|
[data-theme] toggle instead of the library's fixed light-grey
|
|
default, so the popup doesn't look out of place against a dark
|
|
page. */
|
|
[data-theme="dark"] {
|
|
--twoslash-border-color: var(--bg-hover);
|
|
--twoslash-popup-bg: var(--bg-elevated);
|
|
--twoslash-popup-color: var(--fg);
|
|
--twoslash-code-font: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
}
|
|
[data-theme="light"] {
|
|
--twoslash-border-color: var(--bg-hover);
|
|
--twoslash-popup-bg: var(--bg-elevated);
|
|
--twoslash-popup-color: var(--fg);
|
|
--twoslash-code-font: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
}
|
|
|
|
/* --- Tables ---
|
|
border-collapse:collapse fights border-radius (collapsed borders
|
|
render on a layer the table's own corner-clipping doesn't reliably
|
|
catch, especially on the last row) — border-collapse:separate with
|
|
zero spacing gets the same visual seam between rows while keeping
|
|
the rounded corners intact. Row background uses --bg-elevated, not
|
|
--bg-surface — the latter is only 5/255 brighter than the page
|
|
background, which read as no card at all. */
|
|
.doc-content table {
|
|
width: 100%;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
border-radius: var(--card-border-radius);
|
|
overflow: hidden;
|
|
}
|
|
.doc-content th {
|
|
text-align: left;
|
|
background-color: var(--bg-elevated);
|
|
color: var(--fg-mute);
|
|
font-weight: normal;
|
|
font-size: .85rem;
|
|
padding: .9rem 1.1rem;
|
|
}
|
|
.doc-content td {
|
|
padding: 1.1rem;
|
|
border-top: 1px solid var(--bg-hover);
|
|
background-color: var(--bg-elevated);
|
|
vertical-align: top;
|
|
}
|
|
.doc-content td:first-child {
|
|
font-weight: bold;
|
|
}
|
|
|
|
.doc-content-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
gap: var(--padded-gap);
|
|
}
|
|
.doc-content-row > .doc-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: var(--content-pad) 0 var(--content-pad) var(--content-pad);
|
|
}
|
|
|
|
/* --- "On this page" TOC: scripts/components/doc-toc.js ---
|
|
Mirrors the left <sidebar>'s own layout language (sticky, flush
|
|
under the nav, full viewport height, internal scroll) rather than
|
|
floating as a separate rounded card — the two read as one family
|
|
instead of the sidebar being "real" and the TOC being a tooltip
|
|
that happens to sit still. The one deliberate difference: the
|
|
background stays semi-transparent (see .doc-toc-panel below) since
|
|
an opaque panel this size, this close to the sidebar, reads as a
|
|
second competing nav rather than a lightweight companion to the
|
|
content.
|
|
|
|
<doc-toc> itself only ever handles POSITION (sticky column, full
|
|
height — see the responsive overrides below for how that collapses
|
|
once there's no room for a third column). .doc-toc-panel only ever
|
|
handles APPEARANCE (background, borders). Keeping those separate is
|
|
what makes the narrow-viewport version a one-property-at-a-time
|
|
change instead of a second copy of the whole component. */
|
|
doc-toc {
|
|
display: block;
|
|
position: sticky;
|
|
top: var(--nav-height);
|
|
/* Defensively low: sticky elements with no z-index of their own can
|
|
behave inconsistently across browsers as they scroll past other
|
|
positioned content — this guarantees the nav (z-index 10000)
|
|
always wins regardless. */
|
|
z-index: 1;
|
|
flex-shrink: 0;
|
|
width: 220px;
|
|
box-sizing: border-box;
|
|
height: calc(100vh - var(--nav-height));
|
|
}
|
|
.doc-toc-panel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
border-left: 1px solid var(--bg-hover);
|
|
/* Semi-transparent rather than a solid fill like the sidebar's —
|
|
this column is a secondary, at-a-glance aid, not a second place
|
|
to navigate from, so it shouldn't compete at the same visual
|
|
weight. Blurred so scrolling content behind it reads as tinted
|
|
rather than as raw visual noise through a hard-edged cutout. */
|
|
background-color: color-mix(in srgb, var(--bg-surface) 40%, transparent);
|
|
backdrop-filter: blur(6px);
|
|
-webkit-backdrop-filter: blur(6px);
|
|
overflow: hidden;
|
|
}
|
|
.doc-toc-summary {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: .5rem;
|
|
height: 2.85rem;
|
|
padding: 0 .85rem;
|
|
cursor: pointer;
|
|
user-select: none;
|
|
flex-shrink: 0;
|
|
}
|
|
.doc-toc-summary:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
/* Hide the browser's own disclosure triangle — the chevron icon
|
|
below is the one and only toggle indicator. */
|
|
.doc-toc-summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
.doc-toc-summary::marker {
|
|
content: "";
|
|
}
|
|
.doc-toc-heading {
|
|
font-size: .75rem;
|
|
font-weight: bold;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
color: var(--fg-mute);
|
|
}
|
|
.doc-toc-chevron {
|
|
font-size: .65rem;
|
|
color: var(--fg-mute);
|
|
flex-shrink: 0;
|
|
transition: transform 0.15s ease;
|
|
}
|
|
.doc-toc-panel[open] > .doc-toc-summary .doc-toc-chevron {
|
|
transform: rotate(90deg);
|
|
}
|
|
.doc-toc-nav {
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
padding: .25rem 0;
|
|
scrollbar-gutter: stable;
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--bg-hover) transparent;
|
|
}
|
|
.doc-toc-nav::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
.doc-toc-nav::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
.doc-toc-nav::-webkit-scrollbar-thumb {
|
|
background-color: var(--bg-hover);
|
|
border-radius: 4px;
|
|
}
|
|
.doc-toc-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
.doc-toc-list .doc-toc-list {
|
|
padding-left: .85rem;
|
|
}
|
|
.doc-toc-link {
|
|
display: block;
|
|
padding: .4rem .85rem;
|
|
border-left: 2px solid transparent;
|
|
color: var(--fg-mute);
|
|
text-decoration: none;
|
|
font-size: .85rem;
|
|
line-height: 1.4;
|
|
}
|
|
.doc-toc-link:hover {
|
|
background-color: var(--bg-hover);
|
|
color: var(--fg);
|
|
}
|
|
.doc-toc-link.active {
|
|
color: var(--bg-accent);
|
|
border-left-color: var(--bg-accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* =========================================================
|
|
RESPONSIVE / MOBILE OVERRIDES
|
|
Everything above this point defines the desktop (and base)
|
|
appearance and is left untouched. Everything below only
|
|
applies at smaller viewport widths and adapts layout,
|
|
spacing and the nav into a collapsible mobile menu.
|
|
========================================================= */
|
|
|
|
@media (max-width: 1200px) {
|
|
/* The right-side "on this page" column needs real room alongside
|
|
BOTH the left sidebar and the main content — sidebar (280px)
|
|
plus TOC (260px) plus padding/gaps eats well over half of an
|
|
860px-wide viewport, leaving the content column squeezed down
|
|
to almost nothing. Rather than just dropping the TOC (which
|
|
loses it entirely on every phone and tablet), it moves out of
|
|
the row and becomes an ordinary block ahead of the content —
|
|
same <details> panel, just no longer pinned to a column. */
|
|
.doc-content-row {
|
|
flex-direction: column;
|
|
/* The row's default align-items:flex-start (needed on desktop so
|
|
the TOC column doesn't stretch to the tallest sibling) means a
|
|
stacked child sizes to its own content width instead of
|
|
filling the line — without this override the TOC panel here
|
|
would end up only as wide as its text, not the full column. */
|
|
align-items: stretch;
|
|
}
|
|
.doc-content-row > .doc-content {
|
|
padding-right: var(--content-pad);
|
|
}
|
|
doc-toc {
|
|
display: block;
|
|
position: static;
|
|
width: auto;
|
|
height: auto;
|
|
/* Comes first in the DOM (after <main> — see manual.html) so
|
|
it reads as part of the page's normal top-to-bottom flow;
|
|
flexbox is what actually puts it before the content
|
|
visually without needing to touch the markup. */
|
|
order: -1;
|
|
}
|
|
.doc-toc-panel {
|
|
height: auto;
|
|
max-height: none;
|
|
border: 1px solid var(--bg-hover);
|
|
background-color: var(--bg-elevated);
|
|
backdrop-filter: none;
|
|
-webkit-backdrop-filter: none;
|
|
}
|
|
.doc-toc-nav {
|
|
max-height: 40vh;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 860px) {
|
|
:root {
|
|
--content-pad: 1.25rem;
|
|
--padded-gap: 2rem;
|
|
--nav-button-padding: 1.25rem;
|
|
}
|
|
|
|
/* --- NAV --- */
|
|
nav {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
.nav-topbar,
|
|
.nav-menu,
|
|
.nav-links,
|
|
.nav-right {
|
|
display: flex;
|
|
}
|
|
.nav-topbar {
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: stretch;
|
|
width: 100%;
|
|
flex-shrink: 0;
|
|
}
|
|
/* .nav-menu is the single collapsible/scrollable region below the
|
|
always-visible topbar, so the hamburger stays reachable and no
|
|
nav content can ever end up taller than the viewport with no way
|
|
to reach it. */
|
|
.nav-menu {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
max-height: 0;
|
|
overflow-y: hidden;
|
|
transition: max-height 0.25s ease;
|
|
}
|
|
nav.nav-open .nav-menu {
|
|
max-height: calc(100vh - 4.5rem);
|
|
overflow-y: auto;
|
|
}
|
|
.nav-links {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
}
|
|
/* Unlike .nav-links (Download/Documentation/About, which stay
|
|
full-width stacked rows), .nav-right pairs the user menu with the
|
|
settings gear side by side on their own row — the gear reads as
|
|
"goes with Username", not as a fourth unrelated stacked item. */
|
|
.nav-right {
|
|
flex-direction: row;
|
|
align-items: stretch;
|
|
justify-content: space-between;
|
|
width: 100%;
|
|
}
|
|
.nav-right > :first-child {
|
|
margin-left: 0;
|
|
}
|
|
nav .nav-hamburger {
|
|
display: flex;
|
|
background: none;
|
|
border: none;
|
|
margin: 0;
|
|
color: inherit;
|
|
font: inherit;
|
|
font-size: 1.4rem;
|
|
appearance: none;
|
|
}
|
|
nav .nav-hamburger::-moz-focus-inner {
|
|
border: 0;
|
|
padding: 0;
|
|
}
|
|
/* Only the items inside the collapsible menu should become
|
|
full-width stacked rows — the topbar's logo and hamburger stay
|
|
their natural inline size. */
|
|
.nav-menu .nav-button {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
/* Let the dropdown-content (width 100%) drop onto its own line
|
|
below the label instead of sitting beside it. */
|
|
flex-wrap: wrap;
|
|
}
|
|
/* .nav-right's two buttons sit side by side (see above), so while
|
|
closed they size to their own content instead of each claiming
|
|
the full row — otherwise "Username" and the gear would each
|
|
still stack as their own full-width line. Once opened, though,
|
|
a button goes back to full width (flex-wrap: wrap on .nav-right
|
|
above then pushes its now-full-width sibling onto the next
|
|
line) so its dropdown-content has the same full row to expand
|
|
into that .nav-links items get, instead of being squeezed into
|
|
whatever width the closed pill happened to be. */
|
|
.nav-right {
|
|
flex-wrap: wrap;
|
|
}
|
|
.nav-right > .nav-button {
|
|
width: auto;
|
|
flex-wrap: nowrap;
|
|
}
|
|
.nav-right > .nav-button.open {
|
|
width: 100%;
|
|
flex-wrap: wrap;
|
|
}
|
|
/* Hover-triggered dropdowns don't work well on touch; disable hover
|
|
opening on mobile and rely on the .open class toggled by nav.js */
|
|
.nav-button:hover .dropdown-content {
|
|
display: none;
|
|
}
|
|
.nav-button.dropdown.open .dropdown-content {
|
|
display: flex;
|
|
}
|
|
/* Every dropdown in the mobile menu — .nav-links (Download,
|
|
Documentation, About) and .nav-right (Username, the gear) alike
|
|
— drops its contents inline, full-width, right under the label
|
|
it belongs to, rather than floating as a small anchored panel
|
|
that can run off the edge of a narrow screen. */
|
|
.nav-menu .dropdown-content {
|
|
position: static;
|
|
box-shadow: none;
|
|
min-width: 0;
|
|
width: 100%;
|
|
background-color: var(--bg-hover);
|
|
margin-top: .5rem;
|
|
}
|
|
.nav-menu .dropdown-content a {
|
|
padding-left: calc(var(--nav-button-padding) * 2);
|
|
}
|
|
|
|
/* --- BANNER --- */
|
|
.banner {
|
|
height: 45vh;
|
|
}
|
|
.banner-title {
|
|
font-size: 2.75rem;
|
|
}
|
|
.banner-subheading {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
/* --- HEADINGS --- */
|
|
h1 {
|
|
font-size: 1.9rem;
|
|
}
|
|
|
|
/* --- CARDS ---
|
|
CSS Grid's auto-fit centers the whole track group, but a leftover
|
|
card in an incomplete last row still sits in the first track
|
|
(flush left) rather than being centered itself. Flexbox with
|
|
wrap + justify-content:center centers each row's items
|
|
individually, so a lone leftover card sits centered instead of
|
|
left-aligned, while still keeping 2-3 cards per row when they fit. */
|
|
.card-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
}
|
|
.card {
|
|
flex: 0 1 min(100%, 320px);
|
|
box-sizing: border-box;
|
|
/* A fixed-height image row + an auto text row means the image
|
|
is always the same size no matter how long the card's text
|
|
is, and the card itself grows to fit the text instead of
|
|
squeezing the image to make room for it. */
|
|
height: auto;
|
|
grid-template-rows: 190px auto;
|
|
}
|
|
.card-title {
|
|
font-size: 1.25rem;
|
|
margin: .75rem 0;
|
|
}
|
|
|
|
/* --- FOOTER ---
|
|
Stays a horizontal row; it only wraps onto a second line if it
|
|
truly can't fit, it never stacks fully vertical. */
|
|
.dashed-text-div {
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
text-align: center;
|
|
}
|
|
footer {
|
|
padding: 1.5rem 0;
|
|
}
|
|
|
|
/* --- DOC LAYOUT ---
|
|
The sticky/independently-scrolling sidebar only makes sense next
|
|
to a wide content column. On mobile it stacks above the content
|
|
as a normal (non-sticky) block instead, capped in height with
|
|
its own scroll so a long tree doesn't push the content way down. */
|
|
.doc-layout {
|
|
flex-direction: column;
|
|
}
|
|
.sidebar {
|
|
position: static;
|
|
width: 100%;
|
|
height: auto;
|
|
max-height: 50vh;
|
|
}
|
|
.doc-content {
|
|
padding: 1.25rem;
|
|
}
|
|
/* The right-side "on this page" TOC only makes sense next to a wide
|
|
content column too — there's no room for a third column here. */
|
|
.doc-content-row {
|
|
flex-direction: column;
|
|
}
|
|
.doc-content-row > .doc-content {
|
|
padding: 1.25rem;
|
|
}
|
|
.doc-toc-nav {
|
|
max-height: 35vh;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
:root {
|
|
--content-pad: 1rem;
|
|
}
|
|
.banner-title {
|
|
font-size: 2.1rem;
|
|
}
|
|
.banner-subheading {
|
|
font-size: 1.05rem;
|
|
}
|
|
.card {
|
|
grid-template-rows: 160px auto;
|
|
}
|
|
h1 {
|
|
font-size: 1.6rem;
|
|
}
|
|
}
|