Nexoniarz is the sole author and SchOS was never published under Apache-2.0, so this is a straight relicence. LICENSE is the FSF GPLv2 text, every first-party SPDX header moves to GPL-2.0-or-later, and NOTICE, README.md, docs/licensing.md and the site say so. projects/schfs-linux follows the rest of the tree. The GPL gives copyleft: a fork has to ship its source. It also lifts the old "Linux drivers, never" rule, since GPL-2.0-only code combines with GPL-2.0-or-later. "Or later" leaves room for GPLv3, but one binary still has to pick one version, so docs/licensing.md now keeps Apache-2.0 and GPLv3 code out of the kernel to keep Linux code possible there. BSD 4-clause code goes from "avoid" to "no", because the advertising clause is GPL-incompatible. LDLS still ships no .ko. That boundary is now project policy rather than a legal requirement, and ldls.md, devices.md and licensing.md section 6 say so. refmod declares license=GPL, and ldls-check passes (27 checks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
542 lines
14 KiB
CSS
542 lines
14 KiB
CSS
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
/* ==========================================================================
|
|
The SchOS site — layout only
|
|
--------------------------------------------------------------------------
|
|
Everything here is *arrangement*: where things sit, how wide they get, when
|
|
they stack. Not one colour, shadow, radius, type size or duration is stated
|
|
in this file — those come from Niveus, which is the whole point of the
|
|
split. A hex value in here means the system is missing a token, and the
|
|
answer is to add one rather than to work around it.
|
|
|
|
The one exception is `--nv-brand`, which is declared in the pages: it is
|
|
SCH_ACCENT from include/theme.h, and it is an input to the system rather
|
|
than a restatement of it.
|
|
========================================================================== */
|
|
|
|
/* --- The page shell -------------------------------------------------------- */
|
|
|
|
.shell {
|
|
max-inline-size: var(--nv-layout-max-width);
|
|
margin-inline: auto;
|
|
padding-inline: var(--nv-layout-gutter);
|
|
}
|
|
|
|
.section {
|
|
padding-block: var(--nv-space-20);
|
|
}
|
|
|
|
.section__head {
|
|
max-inline-size: var(--nv-measure);
|
|
margin-block-end: var(--nv-space-8);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-3);
|
|
}
|
|
|
|
/* A closing remark under a figure. Narrower than the figure, so it reads as
|
|
commentary on it rather than as the next section. */
|
|
.section__note {
|
|
max-inline-size: var(--nv-measure);
|
|
margin-block-start: var(--nv-space-5);
|
|
}
|
|
|
|
/* The flag in the bar. Deliberately small: the site is about an operating
|
|
system, and the flag is a statement, not a subject. */
|
|
.pride img {
|
|
display: block;
|
|
block-size: var(--nv-space-4);
|
|
inline-size: auto;
|
|
border-radius: var(--nv-radius-xs);
|
|
}
|
|
|
|
@media (max-width: 60rem) {
|
|
.appearance { display: none; }
|
|
}
|
|
|
|
/* --- Hero ------------------------------------------------------------------ */
|
|
|
|
.hero {
|
|
display: grid;
|
|
gap: var(--nv-space-12);
|
|
padding-block: var(--nv-space-16) var(--nv-space-8);
|
|
align-items: center;
|
|
}
|
|
|
|
@media (min-width: 62rem) {
|
|
.hero {
|
|
grid-template-columns: 5fr 7fr;
|
|
gap: var(--nv-space-14);
|
|
}
|
|
}
|
|
|
|
.hero__copy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-5);
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.hero__title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-4);
|
|
font-size: clamp(var(--nv-font-size-4xl), 7vw, var(--nv-font-size-5xl));
|
|
}
|
|
|
|
.hero__mark {
|
|
inline-size: 1.15em;
|
|
block-size: 1.15em;
|
|
}
|
|
|
|
/* --- The console ----------------------------------------------------------- */
|
|
/*
|
|
A terminal is a recess, not a card that happens to hold monospace: the
|
|
material says "this is a window into the machine" before the text does.
|
|
*/
|
|
|
|
.console {
|
|
overflow: hidden;
|
|
}
|
|
|
|
.console__bar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-3);
|
|
padding: var(--nv-space-2) var(--nv-space-3);
|
|
border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
}
|
|
|
|
.console__log {
|
|
margin: 0;
|
|
padding: var(--nv-space-4);
|
|
min-block-size: 18rem;
|
|
overflow-x: auto;
|
|
white-space: pre;
|
|
line-height: var(--nv-line-height-relaxed);
|
|
background: var(--nv-color-surface-inset);
|
|
box-shadow: var(--nv-elevation-inset-sm);
|
|
}
|
|
|
|
/* The cursor the boot log types behind. */
|
|
.console__log::after {
|
|
content: "";
|
|
display: inline-block;
|
|
inline-size: 0.6em;
|
|
block-size: 1em;
|
|
vertical-align: text-bottom;
|
|
background: var(--nv-color-accent);
|
|
animation: console-blink 1.1s steps(2, start) infinite;
|
|
}
|
|
|
|
@keyframes console-blink { 50% { opacity: 0; } }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.console__log::after { animation: none; }
|
|
}
|
|
|
|
/* --- Origin, as a timeline -------------------------------------------------- */
|
|
/*
|
|
The rail is not decoration: these steps happened in this order, and the
|
|
order is the claim the section makes. A grid would lose it.
|
|
*/
|
|
|
|
.timeline {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-8);
|
|
border-inline-start: var(--nv-border-width) solid var(--nv-color-border);
|
|
padding-inline-start: var(--nv-space-6);
|
|
}
|
|
|
|
.timeline__step {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-2);
|
|
max-inline-size: var(--nv-measure);
|
|
position: relative;
|
|
}
|
|
|
|
.timeline__step::before {
|
|
content: "";
|
|
position: absolute;
|
|
inline-size: var(--nv-space-2);
|
|
block-size: var(--nv-space-2);
|
|
border-radius: var(--nv-radius-full);
|
|
background: var(--nv-color-accent);
|
|
inset-inline-start: calc(var(--nv-space-6) * -1 - var(--nv-space-1));
|
|
inset-block-start: var(--nv-space-1);
|
|
}
|
|
|
|
.timeline__when {
|
|
color: var(--nv-color-text-tertiary);
|
|
}
|
|
|
|
/* --- The stack -------------------------------------------------------------- */
|
|
|
|
.stack {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.stack__layer {
|
|
display: grid;
|
|
grid-template-columns: 12rem 1fr;
|
|
gap: var(--nv-space-4);
|
|
align-items: baseline;
|
|
padding: var(--nv-space-3) var(--nv-space-5);
|
|
border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
}
|
|
|
|
.stack__layer:last-child { border-block-end: 0; }
|
|
|
|
.stack__name {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-2);
|
|
font-family: var(--nv-font-family-mono);
|
|
color: var(--nv-color-text-primary);
|
|
}
|
|
|
|
/* The three seams the section is about. They are the argument, so they carry
|
|
the accent ground; everything else is the arrangement between them. */
|
|
.stack__layer--seam {
|
|
background: var(--nv-color-accent-subtle);
|
|
box-shadow: inset var(--nv-border-width-strong) 0 0 var(--nv-color-accent);
|
|
}
|
|
|
|
@media (max-width: 48rem) {
|
|
.stack__layer {
|
|
grid-template-columns: 1fr;
|
|
gap: var(--nv-space-1);
|
|
}
|
|
}
|
|
|
|
/* --- Hybrid: what a driver needs -------------------------------------------- */
|
|
|
|
.needs {
|
|
display: grid;
|
|
gap: var(--nv-space-4);
|
|
margin-block-end: var(--nv-space-8);
|
|
}
|
|
|
|
@media (min-width: 48rem) {
|
|
.needs { grid-template-columns: repeat(2, 1fr); }
|
|
}
|
|
|
|
/* --- Numbers ---------------------------------------------------------------- */
|
|
|
|
.figures {
|
|
display: grid;
|
|
gap: var(--nv-space-4);
|
|
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
|
}
|
|
|
|
/* A card carries no padding of its own -- its parts do -- and a stat tile has
|
|
no parts. */
|
|
.figure {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-1);
|
|
align-items: flex-start;
|
|
padding: var(--nv-space-5);
|
|
}
|
|
|
|
.figure__value {
|
|
font-variant-numeric: tabular-nums;
|
|
letter-spacing: var(--nv-letter-spacing-tight);
|
|
}
|
|
|
|
/* --- PSOGM ------------------------------------------------------------------ */
|
|
|
|
.crash {
|
|
display: grid;
|
|
gap: var(--nv-space-8);
|
|
align-items: center;
|
|
padding: var(--nv-space-12);
|
|
}
|
|
|
|
@media (min-width: 48rem) {
|
|
.crash { grid-template-columns: auto 1fr; }
|
|
}
|
|
|
|
/* The bed's label is a caption for the whole region, not the first cell of
|
|
its grid. */
|
|
.crash .nv-bed__label {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.crash__mark {
|
|
inline-size: var(--nv-space-20);
|
|
block-size: auto;
|
|
}
|
|
|
|
.crash__copy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-4);
|
|
}
|
|
|
|
/* --- The closing band -------------------------------------------------------- */
|
|
|
|
/* Padded here for the same reason .figure is: a card's padding lives on its
|
|
parts, and this band has none -- it is a mark, some copy and two buttons. */
|
|
.getband {
|
|
display: grid;
|
|
gap: var(--nv-space-8);
|
|
align-items: center;
|
|
padding: var(--nv-space-8);
|
|
}
|
|
|
|
@media (min-width: 48rem) {
|
|
.getband { grid-template-columns: auto 1fr; }
|
|
}
|
|
|
|
.getband__mark {
|
|
inline-size: var(--nv-space-20);
|
|
block-size: auto;
|
|
justify-self: center;
|
|
}
|
|
|
|
.getband__copy {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-4);
|
|
align-items: flex-start;
|
|
}
|
|
|
|
/* --- Cards ------------------------------------------------------------------- */
|
|
|
|
/* Niveus's reset makes images block-level, which is right nearly everywhere
|
|
and wrong for a mark that belongs beside a title. A flex row rather than
|
|
`display: inline`, so the two stay aligned whatever the title does. */
|
|
.nv-card__title:has(.cardmark) {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-2);
|
|
}
|
|
|
|
.cardmark {
|
|
inline-size: 1.2em;
|
|
block-size: 1.2em;
|
|
flex: none;
|
|
}
|
|
|
|
/* --- Footer ------------------------------------------------------------------ */
|
|
|
|
.site-footer {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--nv-space-5);
|
|
padding-block: var(--nv-space-8) var(--nv-space-16);
|
|
}
|
|
|
|
.site-footer__row {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--nv-space-2) var(--nv-space-6);
|
|
align-items: baseline;
|
|
justify-content: space-between;
|
|
padding-block-start: var(--nv-space-6);
|
|
}
|
|
|
|
.pride-note {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--nv-space-3);
|
|
}
|
|
|
|
.pride-note img {
|
|
block-size: var(--nv-space-4);
|
|
inline-size: auto;
|
|
border-radius: var(--nv-radius-xs);
|
|
}
|
|
|
|
/* --- Reveal on scroll -------------------------------------------------------- */
|
|
/*
|
|
app.js adds .is-revealed. Without the script every section is simply
|
|
visible, which is the only acceptable failure mode for decoration.
|
|
*/
|
|
|
|
.section[data-reveal] {
|
|
opacity: 0;
|
|
translate: 0 var(--nv-travel-md);
|
|
transition:
|
|
opacity var(--nv-duration-slow) var(--nv-ease-entrance),
|
|
translate var(--nv-duration-slow) var(--nv-ease-entrance);
|
|
}
|
|
|
|
.section[data-reveal].is-revealed {
|
|
opacity: 1;
|
|
translate: 0 0;
|
|
}
|
|
|
|
/* --- Cards that are entirely a link ------------------------------------------ */
|
|
/*
|
|
The whole card is the target -- `.nv-card--interactive` says so by lifting
|
|
under the pointer -- so an underline on the title is a second affordance for
|
|
the same thing, and it reads as a link *inside* a link.
|
|
*/
|
|
|
|
.nv-card__link {
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* --- Command blocks ----------------------------------------------------------- */
|
|
/*
|
|
A command is the one thing on these pages that must be copied exactly, so it
|
|
gets a recess of its own rather than sitting in the card's own material.
|
|
`white-space: pre` and not `pre-wrap`: a wrapped shell command is a command
|
|
that can be mistyped.
|
|
*/
|
|
|
|
.cmd {
|
|
margin: 0;
|
|
padding: var(--nv-space-3) var(--nv-space-4);
|
|
overflow-x: auto;
|
|
white-space: pre;
|
|
line-height: var(--nv-line-height-relaxed);
|
|
background: var(--nv-color-surface-inset);
|
|
border-radius: var(--nv-radius-nested-md);
|
|
box-shadow: var(--nv-elevation-inset-sm);
|
|
}
|
|
|
|
/* --- The documentation reader ------------------------------------------------ */
|
|
/*
|
|
Index, document, contents. Three columns that become one on a narrow screen,
|
|
in that order of importance: the document is the page, the index is how you
|
|
got there, and the contents panel is a convenience.
|
|
*/
|
|
|
|
.docshell {
|
|
max-inline-size: var(--nv-layout-max-width);
|
|
margin-inline: auto;
|
|
padding-inline: var(--nv-layout-gutter);
|
|
padding-block: var(--nv-space-8) var(--nv-space-16);
|
|
display: grid;
|
|
gap: var(--nv-space-8);
|
|
align-items: start;
|
|
}
|
|
|
|
@media (min-width: 64rem) {
|
|
.docshell {
|
|
grid-template-columns: 15rem minmax(0, 1fr) 14rem;
|
|
gap: var(--nv-space-10);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 64rem) {
|
|
.docshell__rail { display: none; }
|
|
}
|
|
|
|
/* Both panels follow the reader down the page, under the bar rather than
|
|
behind it. */
|
|
.docshell__aside,
|
|
.docshell__rail {
|
|
position: sticky;
|
|
inset-block-start: var(--nv-space-16);
|
|
max-block-size: calc(100vh - var(--nv-space-24));
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* A vertical nav that is a panel in a page, not the chrome of an app: no
|
|
surface of its own, and no full-height rule down its edge. */
|
|
.docnav {
|
|
position: static; /* the column is what sticks, not the nav */
|
|
background: none;
|
|
border: 0;
|
|
box-shadow: none;
|
|
padding: 0;
|
|
inline-size: 100%; /* the sidebar component is a fixed 15rem */
|
|
block-size: auto;
|
|
}
|
|
|
|
.docnav .nv-nav__list {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
gap: var(--nv-space-px);
|
|
margin-block-end: var(--nv-space-5);
|
|
}
|
|
|
|
/* A nav item in a bar is one line by definition; a document title is not. The
|
|
contents panel is 14rem wide and some headings are longer than that, so they
|
|
wrap rather than being cut off mid-word -- a truncated entry is a
|
|
destination you cannot recognise. */
|
|
.docnav .nv-nav__item {
|
|
justify-content: flex-start;
|
|
inline-size: 100%;
|
|
white-space: normal;
|
|
block-size: auto;
|
|
min-block-size: var(--nv-control-height-sm);
|
|
padding-block: var(--nv-space-1-5);
|
|
text-align: start;
|
|
}
|
|
|
|
/* A contents panel is a shape: depth reads as indentation. */
|
|
.toc-lvl3 .nv-nav__item { padding-inline-start: var(--nv-space-5); }
|
|
.toc-lvl4 .nv-nav__item { padding-inline-start: var(--nv-space-8); }
|
|
|
|
/* --- The document itself ----------------------------------------------------- */
|
|
|
|
.doc {
|
|
max-inline-size: none; /* the column already sets the measure */
|
|
}
|
|
|
|
.doc > * + * {
|
|
margin-block-start: var(--nv-space-4);
|
|
}
|
|
|
|
.doc h1,
|
|
.doc h2,
|
|
.doc h3,
|
|
.doc h4 {
|
|
margin-block-start: var(--nv-space-8);
|
|
scroll-margin-block-start: var(--nv-space-20);
|
|
}
|
|
|
|
.doc h2 {
|
|
padding-block-end: var(--nv-space-2);
|
|
border-block-end: var(--nv-border-width) solid var(--nv-color-border-subtle);
|
|
}
|
|
|
|
.doc__pre {
|
|
margin: 0;
|
|
padding: var(--nv-space-4);
|
|
overflow-x: auto;
|
|
white-space: pre;
|
|
line-height: var(--nv-line-height-relaxed);
|
|
background: var(--nv-color-surface-inset);
|
|
border-radius: var(--nv-radius-md);
|
|
box-shadow: var(--nv-elevation-inset-sm);
|
|
}
|
|
|
|
.doc blockquote {
|
|
margin-inline: 0;
|
|
padding: var(--nv-space-1) var(--nv-space-5);
|
|
border-inline-start: var(--nv-border-width-strong) solid var(--nv-color-accent-border);
|
|
color: var(--nv-color-text-secondary);
|
|
}
|
|
|
|
.doc ul,
|
|
.doc ol {
|
|
padding-inline-start: var(--nv-space-6);
|
|
}
|
|
|
|
.doc li + li {
|
|
margin-block-start: var(--nv-space-2);
|
|
}
|
|
|
|
/* --- The feature rail --------------------------------------------------------- */
|
|
/*
|
|
One declaration, because the component already does the rest: the equal
|
|
heights are the flex default, the snapping, the hidden scrollbar and the
|
|
fade that marks which end has more behind it are all .nv-scroller's. The
|
|
only thing it cannot know is how wide a card of *this* content wants to be,
|
|
and 18rem is a line where these carry a paragraph.
|
|
*/
|
|
|
|
.features {
|
|
--nv-scroller-item: 21rem;
|
|
}
|