/* * The visual design of the service. * * Colour carries meaning in the graph and in the tables, so the variables * are named by role rather than by hue. The dark palette is the default and * the light one overrides the same variables, so every rule is written once. * A reader's stored choice sets `data-theme` on the document element. */ :root { color-scheme: dark; --background: #0b0f17; --surface: #121826; --surface-raised: #18202f; --border: #243044; --border-strong: #35455f; --text: #dfe7f1; --text-muted: #93a2b8; --text-faint: #64748b; --accent: #7fd1ff; --accent-strong: #4cb4e8; --accent-soft: rgba(127, 209, 255, 0.12); --positive: #5fd39a; --warning: #e8c26a; --negative: #f28b82; --shadow: rgba(0, 0, 0, 0.35); --graph-surface: #161d2b; --graph-grid: #1e2738; --topbar: rgba(11, 15, 23, 0.9); --scrim: rgba(0, 0, 0, 0.45); --warning-soft: rgba(232, 194, 106, 0.1); --negative-soft: rgba(242, 139, 130, 0.1); --accent-faint: rgba(127, 209, 255, 0.04); --on-accent: #04121c; --syntax-keyword: #c792ea; --syntax-type: #82aaff; --syntax-function: #7fd1ff; /* One hue per layer, so that a legend with ten entries is readable. The concept graph uses these directly; the flow graphs reuse the earlier stage names. */ /* Layout. Both are fixed proportions: the sidebar is a share of the window and the table of contents is a share of the reading column, so neither changes size with the length of a title. */ --sidebar-width: 19rem; --toc-width: 15rem; /* Graph group colours. */ --group-overview: #7fd1ff; --group-foundation: #a3e635; --group-physics: #4ade80; --group-geometry: #34d399; --group-elements: #fbbf24; --group-orbit: #fb923c; --group-propagation: #f87171; --group-rf: #e879f9; --group-network: #c084fc; --group-ops: #818cf8; --group-sim: #38bdf8; --group-methods: #94a3b8; /* Flow stages */ --group-input: #94a3b8; --group-parse: #7dd3fc; --group-prepare: #a78bfa; --group-propagate: #38bdf8; --group-verify: #4ade80; --group-fields: #4ade80; --group-crosscheck: #fbbf24; --group-branch: #fbbf24; --group-error: #f87171; --group-output: #4ade80; --group-validate: #a78bfa; --group-convert: #38bdf8; --group-store: #a78bfa; --group-frames: #4ade80; --group-rotate: #38bdf8; --group-geodetic: #fbbf24; --group-time: #a78bfa; --group-general: #94a3b8; --group-damping: #4ade80; --group-frameset: #4ade80; } /* * The light palette. It is a full override rather than a filter, because a * filter inverts the graph colours and makes the legend meaningless. The * background is tinted rather than pure white, because a long page of * pure white is harder to read than a page that is not. */ :root[data-theme='light'] { color-scheme: light; --background: #f4f6fa; --surface: #ffffff; --surface-raised: #eef1f6; --border: #d3dae4; --border-strong: #b3bfcd; --text: #16202e; --text-muted: #4a5769; --text-faint: #6b7787; --accent: #0b6fa4; --accent-strong: #08587f; --accent-soft: rgba(11, 111, 164, 0.1); --positive: #147d54; --warning: #9a6510; --negative: #b3261e; --shadow: rgba(15, 30, 50, 0.12); --graph-surface: #ffffff; --graph-grid: #dce3ec; --topbar: rgba(255, 255, 255, 0.9); --scrim: rgba(15, 30, 50, 0.35); --warning-soft: rgba(154, 101, 16, 0.1); --negative-soft: rgba(179, 38, 30, 0.09); --accent-faint: rgba(11, 111, 164, 0.05); --on-accent: #ffffff; --syntax-keyword: #7c3aed; --syntax-type: #1d4ed8; --syntax-function: #0b6fa4; /* The graph hues are darkened so that a thin stroke stays legible on a light background; the dark values are too pale to read here. */ --group-overview: #0b6fa4; --group-foundation: #4d7c0f; --group-physics: #15803d; --group-geometry: #0f766e; --group-elements: #a16207; --group-orbit: #c2410c; --group-propagation: #b91c1c; --group-rf: #86198f; --group-network: #6d28d9; --group-ops: #3730a3; --group-sim: #0369a1; --group-methods: #475569; --group-input: #475569; --group-parse: #0369a1; --group-prepare: #5b21b6; --group-propagate: #0369a1; --group-verify: #15803d; --group-fields: #15803d; --group-crosscheck: #a16207; --group-branch: #a16207; --group-error: #b91c1c; --group-output: #15803d; --group-validate: #5b21b6; --group-convert: #0369a1; --group-store: #5b21b6; --group-frames: #0f766e; --group-rotate: #0369a1; --group-geodetic: #a16207; --group-time: #5b21b6; --group-general: #475569; --group-damping: #15803d; --group-frameset: #0f766e; } * { box-sizing: border-box; } html, body { margin: 0; padding: 0; height: 100%; } body { background: var(--background); color: var(--text); font-family: var(--sans); font-size: 15px; line-height: 1.65; -webkit-font-smoothing: antialiased; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } h1, h2, h3, h4 { line-height: 1.25; font-weight: 600; } h1 { font-size: 1.9rem; margin: 0 0 0.5rem; } h2 { font-size: 1.35rem; margin: 2.25rem 0 0.75rem; padding-bottom: 0.4rem; border-bottom: 1px solid var(--border); } h3 { font-size: 1.1rem; margin: 1.75rem 0 0.5rem; } h4 { font-size: 1rem; margin: 1.25rem 0 0.4rem; color: var(--text-muted); } p { margin: 0.75rem 0; } ul, ol { margin: 0.75rem 0; padding-left: 1.5rem; } li { margin: 0.3rem 0; } code { font-family: var(--mono); font-size: 0.88em; background: var(--surface-raised); padding: 0.12em 0.35em; border-radius: var(--radius-small); } pre { margin: 0; overflow-x: auto; } pre code { background: none; padding: 0; font-size: 0.85rem; line-height: 1.6; } blockquote { margin: 1rem 0; padding: 0.25rem 1rem; border-left: 3px solid var(--border-strong); color: var(--text-muted); } hr { border: none; border-top: 1px solid var(--border); margin: 2rem 0; } /* Layout */ .app { display: flex; min-height: 100vh; } /* The frame carries the surface and the rule, so the column is drawn for the whole page. The sidebar itself is sticky and scrolls; without the frame the column would end one viewport down and look cut off on a long page. */ .sidebar-frame { flex: 0 0 var(--sidebar-width); background: var(--surface); border-right: 1px solid var(--border); } .sidebar { position: sticky; top: 0; height: 100vh; overflow-y: auto; padding: 1.25rem 0 2rem; } .sidebar-brand { display: block; padding: 0 1.25rem 1rem; border-bottom: 1px solid var(--border); margin-bottom: 1rem; } .sidebar-brand a { color: var(--text); font-size: 1.05rem; font-weight: 600; letter-spacing: 0.01em; } .sidebar-brand p { margin: 0.15rem 0 0; color: var(--text-faint); font-size: 0.78rem; } .sidebar nav { padding: 0 0.75rem; } .sidebar-section { margin-bottom: 1.5rem; } /* The group separates the concepts from the implementation, so it is written differently from the layers inside it. */ .sidebar-group-title { color: var(--accent); font-size: 0.72rem; line-height: 1.35; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; padding-bottom: 0.35rem; margin: 0 0 0.55rem 0.4rem; border-bottom: 1px solid var(--border-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .sidebar-section h2 { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-faint); margin: 0 0 0.5rem 0.5rem; border: none; padding: 0; } /* A layer in the sidebar. It is a two column grid: the title and the fold control, so that every layer occupies the same box whatever the length of its name. A row that grew with its text would give the sidebar an irregular rhythm down its length. */ .sidebar-chapter { /* One box per layer, whatever the length of its name, so the sidebar has a regular rhythm down its length rather than one that follows the text. */ display: grid; grid-template-columns: minmax(0, 1fr) 1.1rem; align-items: center; column-gap: 0.15rem; margin-bottom: 0.15rem; padding-left: 0.4rem; border-left: 2px solid var(--border-strong); min-height: 1.35rem; margin-bottom: 0.15rem; } /* The document links belong to the layer but are not part of its header row, so they take the whole width below it. Without this they are placed in the header's first column and a link with no space to break is set one character per line. */ .sidebar-chapter > a.item { grid-column: 1 / -1; } .sidebar-chapter-title { min-width: 0; font-size: 0.78rem; line-height: 1.35; color: var(--text); margin: 0; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .sidebar-chapter-title:hover { color: var(--accent); text-decoration: none; } .sidebar-chapter-toggle { width: 1.1rem; height: 1.1rem; padding: 0; border: none; background: none; color: var(--text-faint); font-size: 0.65rem; line-height: 1; cursor: pointer; transition: transform 0.12s ease; } .sidebar-chapter.collapsed .sidebar-chapter-toggle { transform: rotate(-90deg); } .sidebar-chapter-toggle:hover { color: var(--accent); background: none; } .sidebar a.item { display: block; padding: 0.3rem 0.5rem; border-radius: var(--radius-small); color: var(--text-muted); font-size: 0.88rem; } .sidebar a.item:hover { background: var(--surface-raised); color: var(--text); text-decoration: none; } .sidebar a.item.active { background: var(--accent-soft); color: var(--accent); } .main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; } .topbar { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem 1.5rem; border-bottom: 1px solid var(--border); background: var(--topbar); position: sticky; top: 0; z-index: 20; backdrop-filter: blur(6px); } .search-box { flex: 0 1 auto; max-width: 34rem; display: flex; gap: 0.5rem; align-items: stretch; } .search-box input { flex: 1 1 auto; /* Without this the input keeps its intrinsic width as its minimum and squeezes the button until the label wraps. */ min-width: 0; } /* A button label is a single line; a narrow flex item must not break it. */ button { white-space: nowrap; flex-shrink: 0; } .content { padding: 1.75rem 2.5rem 4rem; width: 100%; flex: 1 1 auto; } /* A document is a measure of text plus the table of contents beside it, so the reading column keeps a comfortable line length while the table of contents sits at its right. The width is capped so that the pair stays centred on a very wide screen rather than drifting apart. */ .content.doc { max-width: calc(62rem + var(--toc-width) + 2.5rem); margin: 0 auto; } /* The graph and the crate grid use the whole width; a wide screen shows more of them rather than more margin. */ .content.wide { max-width: none; } /* Controls */ input, select, textarea, button { font-family: inherit; font-size: 0.9rem; } input[type='text'], input[type='number'], input[type='search'], select, textarea { width: 100%; background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius-small); padding: 0.45rem 0.6rem; transition: border-color 0.12s ease; } input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent-strong); } textarea { font-family: var(--mono); font-size: 0.82rem; line-height: 1.5; resize: vertical; min-height: 6rem; } button { background: var(--accent-strong); color: var(--on-accent); border: none; border-radius: var(--radius-small); padding: 0.45rem 0.9rem; font-weight: 600; cursor: pointer; } button:hover:not(:disabled) { background: var(--accent); } button:disabled { opacity: 0.55; cursor: not-allowed; } button.secondary { background: var(--surface-raised); color: var(--text); border: 1px solid var(--border-strong); } button.secondary:hover:not(:disabled) { background: var(--border); } button.ghost { background: none; color: var(--text-muted); border: 1px solid var(--border); } button.ghost:hover:not(:disabled) { background: var(--surface-raised); color: var(--text); } /* Cards and panels */ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.1rem 1.25rem; } /* Cards stacked outside a grid. Inside a grid the `gap` spaces them, and a sibling margin would add a second gap, pushing every card after the first in a row down by one row gap while the first stays flush. */ .card + .card { margin-top: 1rem; } .grid.cards > .card + .card { margin-top: 0; } /* A filter field is a control, not a paragraph, so it is given a measure rather than the whole width of the page. */ .filter-field { max-width: 34rem; } .card h3 { margin-top: 0; } /* The root of a page that renders cards. It carries no style of its own; it exists so that the cards under it can be measured together. */ .page-root { display: block; } /* A grid of cards stretches every card in a row to the height of the tallest. The card is a column so that its action sits at the bottom of every card rather than floating at the height of the text above it. */ .grid.cards > .card { display: flex; flex-direction: column; height: 100%; } .card-action { margin-top: auto; padding-top: 0.75rem; align-self: flex-start; } /* A card is a column, so its button would otherwise stretch across the whole card rather than sit under the text. */ .grid.cards > .card > button, .grid.cards > .card > a { align-self: flex-start; } /* The tag row follows the summary; only the action is pushed to the bottom, so the actions of a row line up regardless of how many tags a card has. */ .card-tags { padding-top: 0.15rem; } /* A summary is clamped so that one long paragraph does not stretch its card far past the cards beside it. */ .card-clamp { /* The text is not truncated: the card height is uniform across the page, so a card with a longer summary simply carries more lines and the action stays on the bottom line of every card. */ overflow-wrap: anywhere; } /* * A grid whose track is wider than a narrow screen. * * `minmax(28rem, 1fr)` makes a track that is at least 28rem wide, so on a * phone the column stays 448 pixels wide and the page scrolls sideways. The * `min(…, 100%)` lets the track shrink to the width it actually has, so a * narrow screen gets one narrow column rather than one wide one. */ /* * A card on the interface index shows what a crate is, in one or two lines. * * The cards there are in a grid with three columns on a wide screen, so a * single long summary made every card in its row as tall as the longest one. * The text is cut rather than the card stretched, because the rest of the * summary is on the page the card leads to. */ .crate-grid .card-clamp { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; margin: 0.4rem 0 0; color: var(--text-muted); font-size: 0.9rem; } /* A layer heading on the interface index. */ .api-layer-heading { display: flex; align-items: baseline; gap: 0.5rem; margin: 1.75rem 0 0; font-size: 1.05rem; } .api-layer-heading a { color: var(--text); } .api-layer-summary { margin: 0.25rem 0 0; color: var(--text-muted); font-size: 0.9rem; } .grid { display: grid; gap: 1rem; } /* * A card grid has a fixed number of columns rather than as many as fit. * * `auto-fill` sizes the columns to the width available *and* to the number * of cards in the row, so a section of three cards and a section of four end * up with different card widths on the same page. A fixed column count keeps * one rhythm down the page: every card is the same width, and a section that * does not fill a row leaves the gap at the end rather than widening the * cards. The columns collapse on a narrow screen, where a fluid width is the * right answer. */ .grid.cards { grid-template-columns: repeat(3, minmax(0, 1fr)); } .grid.metrics { grid-template-columns: repeat(auto-fill, minmax(min(11rem, 100%), 1fr)); } .metric { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.85rem 1rem; } .metric .value { font-size: 1.45rem; font-weight: 600; color: var(--accent); font-family: var(--mono); } .metric .label { color: var(--text-faint); font-size: 0.78rem; letter-spacing: 0.03em; } .page-header { margin-bottom: 1.5rem; } .page-header p { color: var(--text-muted); max-width: 60rem; } .breadcrumb { color: var(--text-faint); font-size: 0.82rem; margin-bottom: 0.4rem; } .breadcrumb a { color: var(--text-faint); } .tag { display: inline-block; white-space: nowrap; background: var(--surface-raised); border: 1px solid var(--border); color: var(--text-muted); border-radius: 999px; padding: 0.05rem 0.6rem; font-size: 0.75rem; margin: 0 0.3rem 0.3rem 0; } .badge { display: inline-block; white-space: nowrap; border-radius: var(--radius-small); padding: 0.05rem 0.4rem; font-size: 0.72rem; font-family: var(--mono); border: 1px solid var(--border-strong); color: var(--text-muted); } .notice { border-left: 3px solid var(--accent-strong); background: var(--accent-soft); padding: 0.6rem 0.9rem; border-radius: var(--radius-small); margin: 1rem 0; font-size: 0.88rem; } .notice.warning { border-color: var(--warning); background: var(--warning-soft); } .notice.error { border-color: var(--negative); background: var(--negative-soft); } .notice.muted { border-color: var(--border-strong); background: var(--surface); color: var(--text-muted); } .empty { color: var(--text-faint); font-style: italic; padding: 1rem 0; } /* Search results */ .result-list { list-style: none; padding: 0; margin: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .result { padding: 0.85rem 1.1rem; border-bottom: 1px solid var(--border); background: var(--surface); } .result:last-child { border-bottom: none; } .result:hover { background: var(--surface-raised); } .result-title { font-size: 1rem; font-weight: 600; } .result-summary { margin: 0.35rem 0 0.55rem; color: var(--text-muted); font-size: 0.88rem; } .result-meta { display: flex; gap: 0.35rem; flex-wrap: wrap; } /* Documentation */ .doc-body { min-width: 0; /* * A doc comment names items by their full path, as in * `NoiseTemperature::from_noise_figure_db`, and a path has no space in it * to break at. Left alone it makes the page scroll sideways on a phone, * because the column can shrink but the word cannot. */ overflow-wrap: anywhere; } .doc-body h1, .doc-body h2, .doc-body h3 { position: relative; scroll-margin-top: 4.5rem; } .doc-body .anchor { opacity: 0; margin-left: 0.4rem; color: var(--text-faint); font-weight: 400; text-decoration: none; } .doc-body h1:hover .anchor, .doc-body h2:hover .anchor, .doc-body h3:hover .anchor { opacity: 1; } .doc-body figure.code { margin: 1rem 0; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .doc-body figure.code figcaption { padding: 0.3rem 0.8rem; background: var(--surface-raised); border-bottom: 1px solid var(--border); color: var(--text-faint); font-size: 0.72rem; font-family: var(--mono); text-transform: uppercase; letter-spacing: 0.06em; } .doc-body figure.code pre { padding: 0.9rem 1rem; } .doc-body a.concept-link { border-bottom: 1px dotted var(--accent-strong); } .doc-layout { display: grid; grid-template-columns: minmax(0, 1fr) var(--toc-width); gap: 2.5rem; align-items: start; } /* A document with no headings gets the full width rather than an empty column at its right. */ .doc-layout.no-toc { grid-template-columns: minmax(0, 1fr); max-width: 46rem; } .doc-toc { position: sticky; top: 4.5rem; align-self: start; font-size: 0.85rem; max-height: calc(100vh - 6rem); overflow-y: auto; padding-left: 1rem; border-left: 1px solid var(--border); } /* Tables */ .table-wrap { overflow-x: auto; margin: 1rem 0; border: 1px solid var(--border); border-radius: var(--radius); } table { width: 100%; border-collapse: collapse; font-size: 0.88rem; } th, td { text-align: left; padding: 0.5rem 0.8rem; border-bottom: 1px solid var(--border); vertical-align: top; } /* A table cell that holds an identifier is kept on one line so that the column does not break a name across two lines. */ td:first-child { white-space: nowrap; } th { background: var(--surface-raised); color: var(--text-muted); font-weight: 600; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; } th.numeric, td.numeric { text-align: right; font-family: var(--mono); font-size: 0.82rem; } tbody tr:last-child td { border-bottom: none; } tbody tr:hover { background: var(--accent-faint); } /* Scalars */ .scalars { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); gap: 0.6rem 1.5rem; margin: 1rem 0; } .scalar { display: flex; justify-content: space-between; gap: 0.75rem; padding: 0.35rem 0; border-bottom: 1px dashed var(--border); } .scalar .label { color: var(--text-muted); font-size: 0.85rem; } .scalar .value { font-family: var(--mono); font-size: 0.85rem; text-align: right; white-space: nowrap; } .scalar .unit { color: var(--text-faint); margin-left: 0.3rem; } /* Graph and charts */ .graph-canvas { width: 100%; /* The aspect of the layout space, so the drawing fills the canvas instead of being letterboxed inside it. */ aspect-ratio: 2 / 1; min-height: 20rem; max-height: 46rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); display: block; cursor: grab; touch-action: none; } .graph-canvas:active { cursor: grabbing; } .graph-controls { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin: 0.75rem 0; } .legend { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.5rem; font-size: 0.78rem; color: var(--text-muted); } .legend span { display: inline-flex; align-items: center; gap: 0.35rem; } .legend i { width: 0.75rem; height: 0.75rem; border-radius: 50%; display: inline-block; } .chart { width: 100%; height: 14rem; display: block; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); margin: 0.75rem 0; } /* Signature and code blocks */ .signature { font-family: var(--mono); font-size: 0.82rem; background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-small); padding: 0.5rem 0.7rem; overflow-x: auto; white-space: pre-wrap; word-break: break-word; } .signature .kw { color: var(--syntax-keyword); } .signature .ty { color: var(--syntax-type); } .signature .fn { color: var(--syntax-function); } .api-item { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 0.85rem 1rem; margin-bottom: 0.6rem; } .api-item header { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; } /* The items of one crate, two to a row, so that a crate with dozens of items stays a reasonable height. */ .item-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(28rem, 100%), 1fr)); gap: 0.6rem; align-items: start; } .item-grid .api-item { margin-bottom: 0; } .api-item .name { font-family: var(--mono); font-size: 0.95rem; color: var(--accent); font-weight: 600; } .api-item .doc { color: var(--text-muted); font-size: 0.85rem; margin: 0.5rem 0 0; white-space: pre-wrap; } /* A doc comment rendered as Markdown. The block spacing is tightened because the comment is a fragment rather than a document. */ .api-doc { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.5rem; } .api-doc p { margin: 0.4rem 0; } .api-doc ul, .api-doc ol { margin: 0.4rem 0; padding-left: 1.2rem; } .api-doc h1, .api-doc h2, .api-doc h3, .api-doc h4 { font-size: 0.9rem; margin: 0.75rem 0 0.3rem; color: var(--text); } .api-doc figure.code { margin: 0.5rem 0; } .api-doc figure.code figcaption { display: none; } .api-doc .table-wrap { margin: 0.5rem 0; } /* A crate summary on the crate page */ .crate-summary { color: var(--text-muted); font-size: 0.9rem; max-width: 60rem; margin-bottom: 0.5rem; } .crate-summary p { margin: 0.5rem 0; } /* A link to another interface item, rendered from a doc comment. */ .api-doc a.api-link, .crate-summary a.api-link { color: var(--accent); border-bottom: 1px dotted var(--accent-strong); } /* The interface items a tool calls */ .calls { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.9rem; padding-top: 0.75rem; border-top: 1px solid var(--border); } .calls-label { color: var(--text-faint); font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.05em; margin-right: 0.2rem; } /* The interface index */ .crate-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr)); gap: 0.75rem; margin-top: 1rem; } .crate-card { display: block; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.85rem 1rem; color: var(--text); } .crate-card:hover { border-color: var(--accent-strong); background: var(--surface-raised); text-decoration: none; } .crate-card header { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; } .crate-card .name { font-family: var(--mono); font-size: 0.95rem; color: var(--accent); font-weight: 600; } .crate-card p { margin: 0.5rem 0 0; color: var(--text-muted); font-size: 0.85rem; } /* The group heading on the document index, which separates the concepts from the implementation. */ .docs-group-title { padding-bottom: 0.4rem; margin-bottom: 1rem; border-bottom: 1px solid var(--border-strong); color: var(--accent); } /* A subject within a layer, shown above the documents of that subject. */ .docs-subject { margin: 1.5rem 0 0.5rem; color: var(--text); font-size: 0.95rem; border-left: 2px solid var(--border-strong); padding-left: 0.6rem; } .sidebar-subject { font-size: 0.72rem; color: var(--text-muted); margin: 0.35rem 0 0.15rem 0.55rem; padding-left: 0.45rem; border-left: 2px solid var(--border); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* A tool in the tool index. The card is a link, so the whole area is the target and the hover is the affordance. */ .tool-card { color: var(--text); } .tool-card:hover { border-color: var(--accent-strong); background: var(--surface-raised); text-decoration: none; } .tool-card h4 { display: flex; align-items: baseline; gap: 0.35rem; margin: 0 0 0.35rem; font-size: 0.98rem; color: var(--accent); } /* The chevron marks the card as an entry point to a page of its own, which is what a reader cannot otherwise tell from a heading. */ .tool-go { color: var(--text-faint); font-size: 1.05rem; line-height: 1; transition: transform 0.12s ease, color 0.12s ease; } .tool-card:hover .tool-go { color: var(--accent); transform: translateX(2px); } /* The form a tool opens, given room to breathe under the index. */ .tool-form { scroll-margin-top: 4.5rem; } .tool-card p { margin: 0; color: var(--text-muted); font-size: 0.86rem; } .tool-card.selected { border-color: var(--accent); background: var(--accent-soft); } /* The table of contents beside a document. */ .toc-list { list-style: none; padding: 0; margin: 0; } /* A deeper heading is indented, so the levels of the document are visible without reading the text. */ .toc-item { display: flex; align-items: baseline; gap: 0.15rem; margin: 0.1rem 0; } .toc-item.level-3 { padding-left: 0.85rem; } .toc-item.level-4 { padding-left: 1.7rem; } .toc-item.level-5, .toc-item.level-6 { padding-left: 2.55rem; } .toc-toggle { flex: 0 0 auto; width: 1rem; height: 1rem; padding: 0; border: none; background: none; color: var(--text-faint); font-size: 0.7rem; line-height: 1; cursor: pointer; transition: transform 0.12s ease; } .toc-toggle.collapsed { transform: rotate(-90deg); } .toc-toggle:hover { color: var(--accent); background: none; } .toc-link { color: var(--text-muted); font-size: 0.85rem; line-height: 1.4; } /* A row of links that wrap, used for the item filter results. */ .chip-row { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.85rem; } .chip { display: inline-flex; align-items: baseline; gap: 0.4rem; background: var(--surface-raised); border: 1px solid var(--border); border-radius: 999px; padding: 0.2rem 0.7rem; font-family: var(--mono); font-size: 0.78rem; color: var(--accent); } .chip:hover { border-color: var(--accent-strong); text-decoration: none; } .chip-note { color: var(--text-faint); font-size: 0.72rem; } .kind { font-family: var(--mono); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.05rem 0.4rem; border-radius: var(--radius-small); background: var(--surface-raised); border: 1px solid var(--border-strong); color: var(--text-muted); } /* Tool forms */ .form-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr)); gap: 0.9rem 1.25rem; } .field { display: flex; flex-direction: column; gap: 0.25rem; } .field.wide { grid-column: 1 / -1; } .field label { font-size: 0.82rem; color: var(--text-muted); font-weight: 600; } .field .help { font-size: 0.75rem; color: var(--text-faint); } .field .unit { font-size: 0.72rem; color: var(--text-faint); font-family: var(--mono); } .field.invalid input, .field.invalid textarea, .field.invalid select { border-color: var(--negative); } .spinner { display: inline-block; width: 0.85rem; height: 0.85rem; border: 2px solid var(--border-strong); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.7s linear infinite; vertical-align: -0.1rem; margin-right: 0.4rem; } @keyframes spin { to { transform: rotate(360deg); } } /* * Formulas. * * The output is MathML, which the browser sets itself, so only the container * needs styling: the family to read in, and the room a display formula takes. * `math` inherits the surrounding colour, so a formula follows the theme * without a second set of variables. */ .math { /* The browser's default math font is fine on a dark background and thin on a light one, so the family is only suggested and the browser may use its own. */ font-size: 1.02em; line-height: 1.5; } .math-display { display: block; margin: 1.1rem 0; /* A formula is centred on its own, as a formula rather than as a line of prose. Overflow scrolls rather than being clipped, because a long formula stays readable when it can be scrolled and disappears when it cannot. */ overflow-x: auto; overflow-y: hidden; text-align: center; } :root[data-theme='light'] .math { /* On a light background the browser's math font is thin, so it is asked for a slightly heavier weight to match the text around it. */ font-weight: 500; } /* The drawer toggle. It is only useful while the sidebar is a drawer, so it is hidden by default and the narrow-screen rule below reveals it. */ .menu-button { display: none; } /* Responsive */ @media (max-width: 1180px) { .grid.cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } } @media (max-width: 760px) { .grid.cards { grid-template-columns: minmax(0, 1fr); } } @media (max-width: 700px) { /* The counters are a summary, not navigation, so they are the first thing to go when the bar is tight. */ .topbar .badge { display: none; } .topbar { padding: 0.6rem 1rem; } } /* * Below this width there is no room for a sidebar beside the page, so the * sidebar becomes a drawer that covers the page and is opened from the top * bar. It is a drawer rather than a stacked block because a block would push * the page down by half the screen, and the navigation is long. * * The drawer is a normal element rather than a dialog because it holds no * state to trap and no action to confirm; what it does need is to be * dismissible, which the overlay and the close button both do. */ @media (max-width: 960px) { .app { flex-direction: column; } /* The sidebar is a drawer here, so the button that opens it is needed. */ .menu-button { display: inline-flex; } /* The frame holds the width of the sidebar beside the page. Once the sidebar is a drawer the frame has nothing to hold, and left in the flow it would push the page down by its own width. */ .sidebar-frame { flex: 0 0 auto; width: 0; height: 0; border-right: none; background: none; overflow: visible; } .sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: min(20rem, 84vw); /* The frame that carried the background is empty in this layout, so the drawer carries it itself; without this the page shows through it. */ background: var(--surface); border-right: 1px solid var(--border); max-width: none; height: 100dvh; z-index: 40; /* The drawer sits over the page, so it needs its own edge to lift it off the content behind it. */ box-shadow: var(--shadow); /* Closed by default on a narrow screen; the open class is set by the top bar button. */ transform: translateX(-100%); transition: transform 160ms ease; } .sidebar.open { transform: translateX(0); } /* Tapping outside the drawer dismisses it. It covers the page, so it is placed below the drawer and above the page. */ .sidebar-scrim { position: fixed; inset: 0; z-index: 30; background: var(--scrim); border: none; padding: 0; cursor: pointer; } .content { padding: 1.25rem 1rem 3rem; } .doc-layout { grid-template-columns: minmax(0, 1fr); } .doc-layout.no-toc { max-width: none; } .doc-toc { position: static; max-height: none; width: 100%; border-left: none; border-top: 1px solid var(--border); padding-left: 0; padding-top: 0.75rem; } }